Forum: Compiler & IDEs Peak-Hold Algorithmus


von Lord Z. (lordziu)


Lesenswert?

Hallo Gemeinde.

Ich taste auf einem mega644P ein Audiosignal ab und schicke es durch die 
ElmChan-FFT. Ausgewählte Frequenzen zeige ich über LEDs an. Nun möchte 
ich ein Peak-Hold realisieren, damit die Anzeige nicht so "zuckt".

Ich habe dazu folgenden Algorithmus geschrieben, der als Parameter den 
aktuell berechneten Frequenzwert bekommt. Alle alten Werte werden um 3dB 
abgesenkt (halbiert). Den größten Wert in meinem Buffer gebe ich dann 
zurück und zeige ihn an.
1
#define PEAKHOLD_N  5
2
uint16_t peaks[PEAKHOLD_N];
3
4
/*
5
  Wird alle 10ms mit einem neuen FFT-Wert für
6
  die entsprechende Frequenz aufgerufen.
7
  
8
  Return-Wert wird über LEDs angezeigt
9
*/
10
uint16_t getNextPeak(uint16_t new_value)
11
{
12
  // shift old samples and fade them away
13
  for(uint8_t n=PEAKHOLD_N-1; n>0; n--)
14
  {
15
    peaks[n] = (peaks[n-1] >> 2);
16
  }
17
  
18
  // insert the new value
19
  peaks[0] = new_value;
20
  
21
  // search for max to display
22
  uint16_t max = 0;
23
  for(uint8_t n=PEAKHOLD_N-1; n>=0; n--)
24
  {
25
    if(peaks[n] > max)
26
      max = peaks[n];
27
  }
28
29
  return max;
30
}

Mir geht's darum, ob der Algorithmus so funktionieren kann oder ob ich 
völlig auf dem Holzweg bin?
Syntaxfehler im Code interessieren mich jetzt erstmal nicht.

Grüße

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:

> uint16_t getNextPeak(uint16_t new_value)
> {
>   // shift old samples and fade them away
>   for(uint8_t n=PEAKHOLD_N-1; n>0; n--)
>   {
>     peaks[n] = (peaks[n-1] >> 2);

Das ist eine Division durch 4 und nicht durch 2
Wenn du durch 2 dividieren willst, dann schreib auch Division durch 2! 
Dein Compiler weiß schon, wann er das mit Schieben erledigen kann und 
wann er tatsächlich dividieren muss.


Ich würds so machen:
Wenn der neue Werte größer ist als der alte, dann übernimm ihn direkt. 
Ist er kleiner, dann wird der alte Wert einfach nur um 1 verringert. 
Wenn das je nach Wertebereich zu langsam wieder runter fällt, dann eben 
2 abziehen (oder halbieren oder ...)
1
static uint16_t oldPeak;
2
3
uint16_t getNextPeak(uint16_t new_value)
4
{
5
  if( new_value > oldPeak )
6
    oldPeak = new_value;
7
8
  else if( oldPeak > 0 )
9
    oldPeak--;
10
11
  return oldPeak;
12
}

Ziel: ansteigende Werte werden sofort übernommen. Fällt der Wert, dann 
tut er das langsam.

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:

>   // search for max to display
>   uint16_t max = 0;
>   for(uint8_t n=PEAKHOLD_N-1; n>=0; n--)
>   {
>     if(peaks[n] > max)
>       max = peaks[n];
>   }
>
>   return max;
> }
> [/c]
>
> Mir geht's darum, ob der Algorithmus so funktionieren kann oder ob ich
> völlig auf dem Holzweg bin?

Probiers aus.
Schreib ein Programm, welches eine repräsentatives Samplemenge durch die 
Funktion durchjagt und sieh dir die Ergebnisse an. Letztendlich hängt es 
vom Effekt ab, den du erzielen willst, ob der Code das macht, was du dir 
vorstellst.

von Lord Z. (lordziu)


Lesenswert?

Karl heinz Buchegger schrieb:
> Das ist eine Division durch 4 und nicht durch 2
>
> Wenn du durch 2 dividieren willst, dann schreib auch Division durch 2!

Ich meinte eine Division durch 2, also >>1.

>
> Ich würds so machen:
> Wenn der neue Werte größer ist als der alte, dann übernimm ihn direkt.
> Ist er kleiner, dann wird der alte Wert einfch nur um 1 verringert. Wenn
> das je nach Wertebereich zu langsam wieder runter fällt, dann eben 2
> abziehen

Das ist natürlich die einfachere Methode, da hab ich wieder zu 
kompliziert gedacht ^^.

Ich kanns leider erst morgen testen, weil ich die HW nicht hier hab. 
Hatte mir das nur theoretisch überlegt und wollte mal Meinungen dazu 
hören.

Danke

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:
> Karl heinz Buchegger schrieb:
>> Das ist eine Division durch 4 und nicht durch 2
>>
>> Wenn du durch 2 dividieren willst, dann schreib auch Division durch 2!
>
> Ich meinte eine Division durch 2, also >>1.

Dann schreibs auch als Division durch 2

    peaks[n] = peaks[n-1] / 2;

und alle sind glücklich. Da gibt es kein Vertun und man sieht was Sache 
ist.

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:

> Das ist natürlich die einfachere Methode, da hab ich wieder zu
> kompliziert gedacht ^^.

Muss nicht sein.

Das ergibt einen anderen Effekt. Vielleicht willst du den ja gar nicht 
haben.

Hier gibt es kein richtig und kein falsch. Was funktioniert und was dir 
optisch gefällt ist per Definition richtig :-)

von Besserwisser (Gast)


Lesenswert?

Dieses Shiften im Quellcode kannst du dir sparen, ob du >> 2 oder / 4 
schreibst ist das gleiche. Vertraue deinem Compiler, das er intelligent 
genug ist, das effektivste daraus zu machen.

von Lord Z. (lordziu)


Lesenswert?

Besserwisser schrieb:
> Dieses Shiften im Quellcode kannst du dir sparen, ob du >> 2 oder / 4
> schreibst ist das gleiche. Vertraue deinem Compiler, das er intelligent
> genug ist, das effektivste daraus zu machen.

Wahrscheinlich werden gleich alle über mich herfallen, aber ich hatte 
das vor ein paar Wochen mit der neuesten Kombination WinAVR / AVR-Studio 
getestet. Und da wurde meines Erachtens nicht vom Compiler optimiert.

Als ich da ne Division durch z.B. 64 durch shiften ersetzt hatte, habe 
ich deutlich schnelleren Code erhalten. Steht ja auch so in diversen 
Optimierungs-Guides. Ich war auch der Meinung, dass der Compiler sowas 
machen müsste. Vielleicht liegts ja auch an Einstellungen, ich 
compiliere immer mit -Os.

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:

> Als ich da ne Division durch z.B. 64 durch shiften ersetzt hatte, habe
> ich deutlich schnelleren Code erhalten. Steht ja auch so in diversen
> Optimierungs-Guides. Ich war auch der Meinung, dass der Compiler sowas
> machen müsste. Vielleicht liegts ja auch an Einstellungen, ich
> compiliere immer mit -Os.

Das kann dann nur ein Fehler im Optimizer des Compilers gewesen sein.
Diese Optimierung ist eine der leichtesten Übungen für einen Compiler 
die sie seit mehr als 40 Jahren anstandslos beherrschen.

Wenn dem tatsächlich so war, dann ist das ein Fall für den Bug Report.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.