Implementierung FIR in C

#5905383
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
}
Gast #5905498
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.
Gast #5905514
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.

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