Forum: Mikrocontroller und Digitale Elektronik Implementierung FIR in C


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe ein FIR-System implementiert (Cortex M4F), das die Ableitung 
einer Zahlenreihe aus 64-Bit-Integer bilden soll. Quick & Dirty (siehe 
untenstehender Quelltext) funktioniert das auch.

Allerdings habe ich das Gefühl, dass es auch in C "schöner" geht, als 
z.B. die Koeffizienten bei jedem Aufruf neu zu berechnen.

Da die Ordnung N auf jeden Fall recht klein bleiben wird, könnte ich die 
lookup-table für die Koeffizienten auch explizit hinschreiben und per 
switch-case auswählen. Oder in der Status-Variablen hinterlegen. Oder 
die Schleife komplett ausschreiben und die Filterordnung per switch-case 
auswählen. Ich könnte mir das Umspeichern bei jedem Aufruf irgendwie 
sparen. Oder.

Wie macht man soetwas in C "richtig" und "schön" ?

1
#define DERIVATIVE_N_VALUES 5
2
3
typedef struct
4
{
5
    int64_t lastValues[DERIVATIVE_N_VALUES];
6
}
7
Diff64state_t;
8
9
10
/* Differenzierer fuer grosse Zahlen
11
 * Steigung der Ausgleichsgerade durch die Zahlenreihe u(z), u(z^-1), .... u(z^-n) */
12
float differentiate64(int64_t u, Diff64state_t *State)
13
{
14
    assert( State != NULL );
15
16
    // Letzte n Werte speichern, um die Ableitung zu bilden
17
    memmove( &(State->lastValues[1]), &(State->lastValues[0]), (DERIVATIVE_N_VALUES-1)*sizeof(int64_t));
18
    State->lastValues[0] = u;
19
20
21
    // FIR-Koeffizienten-Array bestuecken (erste Haelfte)
22
    // Das Koeffizienten-Array ist antisymmetrisch, deswegen wird nur ueber die Haelfte iteriert
23
    int n = DERIVATIVE_N_VALUES;
24
    float b[n];
25
    float c = 6.f/((float) ( n*(n*n-1) ) );
26
    for( int i = 0; i<n/2; i++ )
27
    {
28
        b[i] = c * ((float) (2*i - n + 1) );
29
    }
30
31
32
    // FIR
33
    // Die paarweise Differenz der Eingangswerte verhindert einen Ueberlauf, da fuer alle n
34
    // aufeinanderfolgende Werte sicher gilt: max(u(z^i)) - min(u(z^i)) < 2^16
35
    float d = 0.f;
36
    int64_t *v = State->lastValues;
37
    int64_t *w = State->lastValues + (DERIVATIVE_N_VALUES -1);
38
    for( int i = 0; i<n/2; i++ )
39
    {
40
        int32_t diff = *w-- - *v++;
41
        d += b[i] * ( (float) diff );
42
    }
43
44
    return d;
45
}

von Nop (Gast)


Lesenswert?

Also das memcpy mit den vergangenen Werten kann man sich sparen, wenn 
man einen Ringbuffer nutzt.

Und die Werte für den Filter kann man in eine dedizierte Init-Funktion 
verschieben, wenn sie sich zwischen den Filter-Aufrufen normalerweise 
nicht ändern.

Man kann dann z.B. eine eigene Struktur haben für Ringbuffer und Filter, 
die man an die Filterfunktion als Zeiger übergibt.

von c-hater (Gast)


Lesenswert?

Nop schrieb:

> Also das memcpy mit den vergangenen Werten kann man sich sparen, wenn
> man einen Ringbuffer nutzt.

Das ist nicht notwendigerweise effizienter. Hängt sehr stark von der 
Zielarchitektur ab, aber auch davon, wie großzügig man mit dem RAM 
umgehen kann. Und natürlich davon, wie groß die History des Filters sein 
muss.

Tendenziell wird der Ringpuffer umso interessanter, ja umfangreicher die 
History ist. Bei FIR (mit im Vergleich zu IIR tendenziell 
umfangreicherer History) dürfte aber wohl tatsächlich der Ringpuffer 
i.d.R. das Mittel der Wahl sein.

von Nop (Gast)


Lesenswert?

c-hater schrieb:

> Das ist nicht notwendigerweise effizienter.

Bei einem Ringbuffer hat man natürlich den wrap-around, spart sich dafür 
aber eine Kopierschleife in selber Länge. Solange man als Buffergröße 
eine Zweierpotenz nimmt, ist der wrap-around lediglich ein &, was nur 
eine Instruktion ist.

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.