Amateurfunk verbindet die Welt

Gleitender Mittelwert - moving average

Erstellt: DL6GL, 04.10.2017, letzte Änderung


Um streuende Messwerte zu glätten, verwendet man üblicherweise eine Mittelwertbildung über eine Anzahl von Einzelmessungen. Eine bestimmte Anzahl von Messwerten wird dabei aufsummiert und zum Schluss wird die Summe durch die Anzahl dividiert. Wenn die Abfolge der Messwerte flott genug ist, fällt das Warten auf Summe und Mittelwert nicht ins Gewicht. Bei geringerer Messrate ist eine gleitende Mittelwertbildung ("Running / moving average") ein Ausweg, schneller Ergebnisse zu sehen.

Beim HF-Powermeter ergab sich infolge der geringen Konversionsrate des ADC MCP3421 von 3,75 Samples pro Sekunde (SPS) bei 18Bit Auflösung, also ca. 300msec pro Messung, der Wunsch, zeitnah Messwerte zu sehen. Nachfolgend soll das Prinzip mit diesen Codeschnipseln gezeigt werden, siehe auch hier.

Deklarationen

Dim bytTmp0 As Byte                                  'Temp variable
Dim bytADCdata As Byte '=1: ADC conversion complete
Dim bytADCResult As Byte '=1: Mean value available
Dim bytADCRep As Byte 'Repetitive ADC runs 1...10
Dim bytADCRun As Byte 'Current ADC run for mean value
Const bytADCRepMax = 10 'Max ADC repetitions
Dim dwdADCVal As DWord 'ADC conversion result
Dim dwdADCArr(bytADCRepMax) As DWord 'ADC results of repetitive runs
Dim dwdADCSum As DWord 'Sum of ADC repetitive runs
Dim dwdADCMean As DWord 'Mean ADC conversion result

'Initialize data
dwdADCMean = 0 'ADC mean value
dwdADCSum = 0 'ADC conversion sum
bytADCRun = 1 'ADC result array index
For bytTmp0 = 1 To bytADCRepMax 'Clear data array
dwdADCArr(bytTmp0) = 0
Next bytTmp0

Hier wird unterstellt, dass der ADC positive Werte liefert. Je nachdem, ob der ADC nur positive oder auch negative Werte liefern kann, ist die Typenerklärung zu beachten:

Word 16bit unsigned, 0…65.535 nur positive Werte
DWord 32bit unsigned, 0…4.294.967.295 nur positive Werte
Integer 16bit signed, −32.768…+32.767  
Long 32bit signed, −2.147.483.648…+2.147.483.647  

Insbesondere muss die Variable für die Summe, hier "dwdADCSum", die aus den Einzelergebnissen gebildete Summe darstellen können. In diesem Beispiel kann aus bis zu 10 Einzelmessungen der Mittelwert ermittelt werden.

Running average

Von außen ist "bytADCRep" (Anzahl Einzelmessungen für die Mittelwertbildung) vorgegeben.
Rückgabe an das aufrufende Programm:

  • "bytADCResult = 1": Es steht ein Mittelwert zur Verfügung
  • "dwdADCMean" = Mittelwert
If bytADCdata = 1 Then                              'ADC conversion complete

'Read ADC conversion result dwdADCVal, depends on ADC

If bytADCRep = 1 Then 'One shot
dwdADCMean = dwdADCVal
Else
'Running average
'Current ADC value is summed in dwdADCSum and stored in array lngADCArr
'With a new ADC value the last value is substracted from dwdADCSum
'and then the new value is added to dwdADCSum
'If repetitions reached end value bytADCRep the array index begins with 1 again
'The average is calculated with every new ADC value
dwdADCSum = dwdADCSum - dwdADCArr(bytADCRun) 'Substract last ADC value
dwdADCSum = dwdADCSum + dwdADCVal 'Add new ADC value
dwdADCArr(bytADCRun) = dwdADCVal 'New value into array
Incr bytADCRun 'Next array index
If bytADCRun >= bytADCRep Then 'Repetitions complete
bytADCRun = 1
End If
bytTmp0 = bytADCRep - 1
dwdADCMean = dwdADCSum / bytTmp0 'Mean value
End If
bytADCResult = 1
End If

Der ADC meldet mit "bytADCdata = 1", dass eine Konversion abgeschlossen ist und ein Wert im ADC-Register zur Verfügung steht. Dies und das Auslesen des Wertes sind jeweils ADC-spezifisch.

Je nach gewähltem "bytADCRep" (=1: keine Mittelwertbildung oder 2…10 Einzelmessungen für die Mittelwertbildung) verzweigt die Auswertung. Mit "bytADCRep = 1" sind wir auch schon fertig. Der ADC-Wert "dwdADCVal" wird auf "dwdADCMean" übergeben und mit "bytADCResult = 1" an das aufrufende Programm gemeldet, dass ein Messwert vorliegt.

Bei der Mittelwertberechnung ist "bytADCRun" der Laufindex der Einzelmessungen. Mit jeder Messung wird wie gewohnt die Summe "dwdADCSum" gebildet, sogleich aber der im Array "dwdADCArr" gespeicherte vorherige ADC-Wert subtrahiert. In der Summe "dwdADCSum" erscheint also nur die Differenz zwischen dem vorherigen und dem aktuellen ADC-Wert. Nachdem im Array "dwdADCArr" der vorherige ADC-Wert durch den aktuellen ersetzt wurde, erhöht sich der Laufindex "bytADCRun". Der aktuelle Mittelwert berechnet sich nun aus der Summe "dwdADCSum" geteilt durch die Anzahl Wiederholungsmessungen minus 1, da der vorherige ADC-Messwert von der Summe abgezogen wurde.

Für die Folgemessung wird der Laufindex "bytADCRun" für das Array "dwdADCArr" um 1 erhöht, bis die Anzahl der Wiederholungsmessungen erreicht ist. Dann beginnt die Zählung wieder von vorne mit 1. Mit "bytADCResult = 1" wird nach jeder Einzelmessung dem aufrufenden Programm signalisiert, dass ein neuer Mittelwert "dwdADCMean" vorliegt.

Im Ergebnis nähert sich mit jeder ADC-Messung der aktuelle Mittelwert einem stationären Wert, wie er sich bei einer "üblichen" Mittelwertbildung nach einer möglicherweise lähmenden Wartezeit zeigen würde. Man bekommt also schneller einen "vorläufigen" Mittelwert zu sehen. Ändert sich das am ADC liegende Signal nicht, ist die Anzeige auch schon nach der ersten Einzelmessung (bis auf ein evtl. wackelndes ADC-Bit) stabil.