Durchschnitt dynamisch berechnen

Gast #6058609
Lesenswert?

Hallo,
ich möchte die die Anzahl von Impulsen/Minute dynamisch bestimmen.
Ich habe einen Impulsgeber der Werte zwischen 0 und 120 Impulse pro 
Minute ausgibt (Durchschnitt bei etwa 60).
Für den Durchschnittswert möchte ich jede Sekunde auslesen können, 
wieviele Impulse in den letzten 60 Sekunden aufgezeichnet wurden. Ich 
hätte dies, mit einem Stack gelöst, der die millis() der Impulse 
speichert, sowie seine Größe. Immer bei einer Abfrage würden dann alle 
Elemente, die älter als 60 Sekunden sind gelöscht und die große 
zurückgeben.

Leider habe ich keine Erfahrung mit ADT auf Mikrocontrollern, sowie mit 
OOP auf Mikrocontrollern
Gast #6058651
Lesenswert?

Das geht nur wenn Du wirklich 60 verschiedene Werte speicherst,
sonst weisst Du nach n paar sekunden nichtmehr was zu löschen wäre..

wenn Du sicher bsit, dass kein Aktueller Wert grösser sein kann als 127.
DANN (und nur dann) kannst Du mit nur 4bit pro Wert auskommen,
also 30byte 240bit

bei jedem speichervorgang überschreibst du schlicht die folgenden 4 bit 
mit deinem gelesenen Wert (mod 240 quasi wie ein endlos magnet band)
und merkst Dir nur die position.
(bei 240bit reicht dann ein byte [255] für die zeigerposition)

Dann machst Du eine volle rotation (liest 60 mal 4bit)
und errechnest den Durchschnitt

und sobald ein neuer Wert eintrudelt, überschreibst Du die nächsten vier 
bit,
der Zeiger rückt um vier vor (immer mod 240 natürlich)
usw usf
Gast #6058814
Lesenswert?

Der naive Ansatz: Array von 60 Zahlen, element 0 kommt jede Sekunde der 
aktuelle Wert rein, und davor wird Elemente 58 nach 59 verschoben, 57 
nach 58, ... 0 nach 1.

Danach alle 60 aufsummieren.

Zweite Verbesserung: memmove.

Dritte Verbesserung: ringbuffer

Vierte: nicht immer neu aufsummieren, sondern neuen wert zum 
Summenzähler addieren, ältesten subtrahieren.

Und dann, ... ja dann stellst Du fest, dass so ein Mittelwert meist 
wenig hilfreich ist.
Gast #6058920
Lesenswert?

1
volatile uint8_t pulses;
2

3
void
4
pulse_irq() // interrupt context
5
{
6
    pulses++;
7
}
8

9
static uint8_t
10
get_pulses() // get number of pulses since last call
11
{
12
    // Instead of resetting pulses (needs atomic read-and-clear)
13
    // remember the last value and take the difference to now.
14
    static uint8_t last;
15

16
    uint8_t x = pulses;
17
    x -= last;
18
    last += x;
19
    return x;
20
}
21

22
static uint8_t
23
get_pulse_avg() // returns pulse average over last 60 calls
24
{
25
    static uint8_t history[60];
26
    static uint8_t pos;
27
    static uint16_t avg;
28

29
    uint8_t x = get_pulses();
30

31
    avg -= history[pos];
32
    avg += x;
33
    histoy[pos++] = x;
34
    if (pos == 60)
35
        pos = 0;
36

37
    return avg / 60;
38
}
39

40
void
41
every_second()
42
{
43
    printf("pulse average: %d\n", get_pulse_avg());
44
}
Gast #6058969
Lesenswert?

Der gleitende Mittelwert über 60s ist halt ein extremer (FIR-)Tiefpass, 
der nur langfristige Änderungen durchlässt.  Mit einem exponentiellen 
(IIR-)Tiefpass wie unter werden eher die Flanken "runder".  Was man 
braucht ist Anwendungssache.

Zum Vergleichen/Rumexperimentieren:
1
volatile uint8_t pulses;
2

3
void
4
pulse_irq() // interrupt context
5
{
6
    pulses++;
7
}
8

9
static uint8_t
10
get_pulses() // get number of pulses since last call
11
{
12
    // Instead of resetting pulses (needs atomic read-and-clear)
13
    // remember last value and take the difference to now
14
    static uint8_t last;
15

16
    uint8_t x = pulses;
17
    x -= last;
18
    last += x;
19
    return x;
20
}
21

22
static uint8_t
23
avg_60(uint8_t x) // returns average of x over last 60 calls
24
{
25
    static uint8_t history[60];
26
    static uint8_t pos;
27
    static uint16_t avg;
28

29
    avg -= history[pos];
30
    avg += x;
31
    history[pos++] = x;
32
    if (pos == 60)
33
        pos = 0;
34
    return avg / 60;
35
}
36

37
static uint8_t
38
avg_exp(uint8_t x) // returns exponential average of x
39
{
40
    static int avg;
41

42
    avg += ((int)x * 4 - avg + 1) / 2;
43
    return avg / 4;
44
}
45

46
void
47
every_second()
48
{
49
    uint8_t p = get_pulses();
50

51
    printf("pulses: %d, 60s-average: %d, exp-average: %d\n", p, avg_60(p), avg_exp(p));
52
}
53

54
// test
55
int
56
main(int argc, char **argv)
57
{
58
    int x = 0;
59

60
    for (int i=0; i < 70; ++i)
61
    {
62
        pulses += 50 + 50*sin(x++ * .2);
63
        //pulses += 50 + 3*(x++%8 < 2);
64
        every_second();
65
     }
66

67
     return 0;
68
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren