Forum: Mikrocontroller und Digitale Elektronik 8 Bit FFT übernehmen


von Josef (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade eine FFT auf einem 8 Bit Controller zu 
implementieren. Da ich das Rad ja nicht neu erfinden muss, versuche ich 
bereits vorhanden Code zu übernehmen: 
http://forum.arduino.cc/index.php/topic,38153.0.html

Als ersten Schritt probiere ich das ganze erstmal auf PC zum laufen zu 
bekommen. Am Code habe ich folgendes angepasst:
-Das Array "normal" als char angelegt statt im Flash
-"Normales" Auslesen des Arrays statt mit pgm_read_word_near()
-Funktionsaufruf hinzugefügt
-Ausgabe der Ergebnisse per printf

Inzwischen lässt sich das Projekt kompilieren aber das Ergebnis scheint 
nicht zu stimmen. Wenn ich es richtig verstehe, müsste das Ergebnis doch 
ins Array geschrieben werden, dann sollte doch bei einem Sinus ein 
bestimmter Wert im Array stark ausschlagen? Stattdessen sind aber alle 
denkbaren Werte im Array vertreten (inclusive negativer Werte). Falls 
sich jemand mit der FFT auskennt und mir einen Tipp geben könnte, wäre 
ich wirklich sehr dankbar, ich komme hier einfach nicht weiter.

Die Kommentare habe ich herausgenommen, weil der Code hier schon so lang 
war. Im Original (siehe Link) sind die ausführlichen Kommentare ja noch 
zu lesen.
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
5
int fix_fft(char fr[], char fi[], int m, int inverse);
6
int fix_fftr(char f[], int m, int inverse);
7
8
int main(int argc, char** argv) 
9
{
10
  #define N_WAVE      256    /* full length of Sinewave[] */
11
  #define LOG2_N_WAVE 8      /* log2(N_WAVE) */
12
13
  int x;
14
15
char Sinewave[N_WAVE-N_WAVE/4] = {
16
0, 3, 6, 9, 12, 15, 18, 21, 
17
24, 28, 31, 34, 37, 40, 43, 46, 
18
48, 51, 54, 57, 60, 63, 65, 68, 
19
71, 73, 76, 78, 81, 83, 85, 88, 
20
90, 92, 94, 96, 98, 100, 102, 104, 
21
106, 108, 109, 111, 112, 114, 115, 117, 
22
118, 119, 120, 121, 122, 123, 124, 124, 
23
125, 126, 126, 127, 127, 127, 127, 127, 
24
25
127, 127, 127, 127, 127, 127, 126, 126, 
26
125, 124, 124, 123, 122, 121, 120, 119, 
27
118, 117, 115, 114, 112, 111, 109, 108, 
28
106, 104, 102, 100, 98, 96, 94, 92, 
29
90, 88, 85, 83, 81, 78, 76, 73, 
30
71, 68, 65, 63, 60, 57, 54, 51, 
31
48, 46, 43, 40, 37, 34, 31, 28, 
32
24, 21, 18, 15, 12, 9, 6, 3, 
33
34
0, -3, -6, -9, -12, -15, -18, -21, 
35
-24, -28, -31, -34, -37, -40, -43, -46, 
36
-48, -51, -54, -57, -60, -63, -65, -68, 
37
-71, -73, -76, -78, -81, -83, -85, -88, 
38
-90, -92, -94, -96, -98, -100, -102, -104, 
39
-106, -108, -109, -111, -112, -114, -115, -117, 
40
-118, -119, -120, -121, -122, -123, -124, -124, 
41
-125, -126, -126, -127, -127, -127, -127, -127, 
42
43
/*-127, -127, -127, -127, -127, -127, -126, -126, 
44
-125, -124, -124, -123, -122, -121, -120, -119, 
45
-118, -117, -115, -114, -112, -111, -109, -108, 
46
-106, -104, -102, -100, -98, -96, -94, -92, 
47
-90, -88, -85, -83, -81, -78, -76, -73, 
48
-71, -68, -65, -63, -60, -57, -54, -51, 
49
-48, -46, -43, -40, -37, -34, -31, -28, 
50
-24, -21, -18, -15, -12, -9, -6, -3, */
51
};
52
53
54
inline char FIX_MPY(char a, char b)
55
{
56
    /* shift right one less bit (i.e. 15-1) */
57
    int c = ((int)a * (int)b) >> 6;
58
    /* last bit shifted out = rounding-bit */
59
    b = c & 0x01;
60
    /* last shift + rounding bit */
61
    a = (c >> 1) + b;
62
    return a;
63
}
64
65
66
int fix_fft(char fr[], char fi[], int m, int inverse)
67
{
68
    int mr, nn, i, j, l, k, istep, n, scale, shift;
69
    char qr, qi, tr, ti, wr, wi;
70
71
    n = 1 << m;
72
73
    /* max FFT size = N_WAVE */
74
    if (n > N_WAVE)
75
        return -1;
76
77
    mr = 0;
78
    nn = n - 1;
79
    scale = 0;
80
81
    /* decimation in time - re-order data */
82
   for (m=1; m<=nn; ++m) {
83
        l = n;
84
        do {
85
            l >>= 1;
86
        } while (mr+l > nn);
87
        mr = (mr & (l-1)) + l;
88
89
        if (mr <= m)
90
            continue;
91
        tr = fr[m];
92
        fr[m] = fr[mr];
93
        fr[mr] = tr;
94
        ti = fi[m];
95
        fi[m] = fi[mr];
96
        fi[mr] = ti;
97
    }
98
99
    l = 1;
100
    k = LOG2_N_WAVE-1;
101
    while (l < n) 
102
  {
103
        if (inverse) 
104
    {
105
          //          variable scaling, depending upon data 
106
            shift = 0;
107
            for (i=0; i<n; ++i) 
108
      {
109
                j = fr[i];
110
                if (j < 0)
111
                    j = -j;
112
                m = fi[i];
113
                if (m < 0)
114
                    m = -m;
115
                if (j > 16383 || m > 16383) 
116
        {
117
                    shift = 1;
118
                    break;
119
                }
120
            }
121
            if (shift)
122
                ++scale;
123
        } 
124
    else 
125
    {
126
            shift = 1;
127
        }    
128
        istep = l << 1;
129
        for (m=0; m<l; ++m) 
130
    {
131
        j = m << k;
132
   //   wr =  pgm_read_word_near(Sinewave + j+N_WAVE/4);
133
        wr =  Sinewave [j+N_WAVE/4];
134
        wi = Sinewave[j];
135
        if (inverse)
136
            wi = -wi;
137
        if (shift) 
138
      {
139
                wr >>= 1;
140
                wi >>= 1;
141
            }
142
            for (i=m; i<n; i+=istep) 
143
      {
144
                j = i + l;
145
                tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]);
146
                ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]);
147
                qr = fr[i];
148
                qi = fi[i];
149
                if (shift) 
150
        {
151
                    qr >>= 1;
152
                    qi >>= 1;
153
                }
154
                fr[j] = qr - tr;
155
                fi[j] = qi - ti;
156
                fr[i] = qr + tr;
157
                fi[i] = qi + ti;
158
            }
159
        }
160
        --k;
161
        l = istep;
162
    }
163
    return scale;
164
}
165
166
int fix_fftr(char f[], int m, int inverse)
167
{
168
    int i, N = 1<<(m-1), scale = 0;
169
    char tt, *fr=f, *fi=&f[N];
170
171
    if (inverse)
172
        scale = fix_fft(fi, fr, m-1, inverse);
173
    for (i=1; i<N; i+=2) {
174
        tt = f[N+i-1];
175
        f[N+i-1] = f[i];
176
        f[i] = tt;
177
    }
178
    if (! inverse)
179
        scale = fix_fft(fi, fr, m-1, inverse);
180
    return scale;
181
}
182
183
  x = fix_fftr(&Sinewave[0], 6, 0);
184
  
185
    for(x=0;x<(N_WAVE-N_WAVE/4);x++)
186
    {
187
      printf(" %d",Sinewave[x]);  
188
    }
189
}

von Georg G. (df2au)


Lesenswert?

Beim Lesen des Artikels hast du einen der letzten Sätze übersehen:

Unfortunately, there is a flow in the code, no FFT performed
if you copy/paste as it is.

von Thosch (Gast)


Lesenswert?

Falls du es auf einem Atmel AVR implementieren möchtest, schau dir mal 
die Routinen von Chan an: http://elm-chan.org/works/akilcd/report_e.html

Der hat 'ne FFT-Routine in AVR-Assembler geschrieben, aber mit Interface 
zur Verwendung aus C-Code heraus.

Die Library kannst du auf der verlinkten Seite unten runterladen, die 
Sourcen für den Audio-Analyser (als Beispiel für die Verwendung der Lib) 
ebenso.

von Josef (Gast)


Lesenswert?

Georg G. schrieb:
> Beim Lesen des Artikels hast du einen der letzten Sätze übersehen:
>
> Unfortunately, there is a flow in the code, no FFT performed
> if you copy/paste as it is.

Verdammt, das habe ich wirklich übersehen. Danke für den Hinweis. Aber 
es stellt sich die Frage, was man tun muss, um den Code zu übernehmen. 
Der User focalist hat es ja scheinbar hinbekommen.

Wie auch immer, solange ich damit nicht weiterkomme, sehe ich mir mal 
die Routinen von Chan an. Fragen dazu folgen ;)

von Josef (Gast)


Lesenswert?

Achso, der Controller steht noch nicht ganz fest, aber es soll 
wahrscheinlich irgendein PIC werden. Aber das müsste sich ja recht leid 
adaptieren lassen, oder?

von Karl H. (kbuchegg)


Lesenswert?

Vor allen Dingen mal aufräumen im Code.
Wenn ich for Schleifen sehe, die bei 1 anfangen, fangen bei mir die 
Alarmglocken zu schrillen an.
Der Datentyp char ist schlecht gewählt. Kein Mensch weiß´, ob 'char' auf 
diesem Compiler ein signed oder ein unsigned Datentyp ist. Immer 
explizit sein! Es gibt 3(!) kleine Datentypen

* signed char
  Für kleine Integer mit Vorzeichen

* unsigned char
  Für kleine Integer ohne Vorzeichen. Das ist der Datentyp der Wahl, 
wenn man es mit Bytes zu tun hat

* char
  Für alles was mit Textverarbeitung zu tun hat.

Hier hat man es nicht mit Textverarbeitung zu tun. Daher ist 
plain/vanilla char nicht angebracht, sondern ein 'ask for trouble'.

von Josef (Gast)


Lesenswert?

Josef schrieb:
> Achso, der Controller steht noch nicht ganz fest, aber es soll
> wahrscheinlich irgendein PIC werden. Aber das müsste sich ja recht leid
> adaptieren lassen, oder?
Moment, da habe ich nicht nachgedacht. Die Libarys gehen ja nur für AVR. 
Dann hilft mir das wohl nicht so einfach weiter, oder?

Karl Heinz Buchegger schrieb:
> Immer explizit sein!
Wird gemacht :)

Karl Heinz Buchegger schrieb:
> ist ziemlich offensichtlicher Blödsinn. Das sollte offenbar ein
> Swap-Code sein, so wie das hier steht, ist es eine 0-Operation.
Ich verstehe nicht ganz, was du meinst, fr[m] und fr[mr] werden doch 
getauscht, oder nicht? Wie müsste es denn anders heißen?

von Karl H. (kbuchegg)


Lesenswert?

Josef schrieb:

> Karl Heinz Buchegger schrieb:
>> ist ziemlich offensichtlicher Blödsinn. Das sollte offenbar ein
>> Swap-Code sein, so wie das hier steht, ist es eine 0-Operation.
> Ich verstehe nicht ganz, was du meinst, fr[m] und fr[mr] werden doch
> getauscht, oder nicht? Wie müsste es denn anders heißen?


Ich hab diesen Einwand wieder zurück gezogen, weil ich nach dem 5-ten 
mal Code lesen gemerkt habe, dass das eine mal die Indexvariable m 
heißt, während sie das andere mal mr heißt. So einfach sieht man das 
nicht und das fällt für mich genauso unter Code-aufräumen: 
Variablennamen so wählen, dass man aus 2 Meter Entfernung mit 
Sonnenbrille erkennen kann, dass es sich um unterschiedliche Variablen 
handelt. Persönlich mache ich seit langem einen großen Bogen um 
veröffentlichten Code, bei dem 90% aller Variablen einen ein- oder 
zweibuchstabigen Namen tragen.


Aus dem hier werde ich nicht schlau
1
            shift = 0;
2
            for (i=0; i<n; ++i) 
3
      {
4
                j = fr[i];
5
                if (j < 0)
6
                    j = -j;
7
                m = fi[i];
8
                if (m < 0)
9
                    m = -m;
10
                if (j > 16383 || m > 16383)

mal angenommen, dass ein char tatsächlich ein signed char ist. Dann 
ändert sich der Zahlenwert bei der Zuweisung an den int j nicht. Auch 
nicht bei negativen Zahlen. Aber wie soll ja dann größer als 16383 
werden?
Angenommen char ist in Wirklichkeit in unsigned char. Dann werden 
'negtive Werte' aus dem Original im Bereich 128 bis 255 abgebildet. 
Wieder gibt es keine Chance jemals größer als 16383 zu werden.

So richtig hab ich noch nicht durchschaut, was hier eigentlich passieren 
soll.

von Josef (Gast)


Lesenswert?

OK, bei dem Code kann man sich wirklich schnell man vertun. Aufräumen 
würde ich trotzdem nur ungern, weil mir das Risiko hoch erscheint, dass 
ich dabei nur weitere Fehler einprogrammiere. Das ist jetzt schon das 
dritte Code-Beispiel, dass ich erfolglos versuche zum Laufen zu bringen 
- Wirklich frustrierend! Falls jemand noch etwas für PIC Controller 
kennt, wäre das auch super.

Bei dem Codestück muss ich dir Recht geben, die if-Abfrage müsste 
tatsächlich nie erfüllt werden ... Ratlosigkeit

von Karl H. (kbuchegg)


Lesenswert?

Ich müsste mich jetzt auch wieder in die FFT und den Butterfly 
Algorithmus einlesen, wie der konkret funktioniert.
Wenn du das ganze sowieso am PC laufen hast, hindert dich ja nichts 
daran, den Code mit printf zu spicken, erst mal mit einer kleineren 
Datenmenge zu operieren und dann anhand der Ausgaben kontrollieren, ob 
die richtigen Werte (sowohl Index als auch Zahlenwerte) miteinander 
verrechnet werden.

von Georg G. (df2au)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Angenommen char ist in Wirklichkeit in unsigned char. Dann werden
> 'negtive Werte' aus dem Original im Bereich 128 bis 255 abgebildet.
> Wieder gibt es keine Chance jemals größer als 16383 zu werden.

Im Kopf der fft.cpp schreibt der Autor, dass alle Werte short int sind 
und von -32768 bis +32767 laufen. Float war ihm zu langsam und zu 
unbequem. Vermutlich ist einer der Fehler im Code, dass fr[] und fi[] 
als char definiert sind. In seinem Beispiel Code weist er auch int Werte 
zu und nicht char.

von Josef (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich müsste mich jetzt auch wieder in die FFT und den Butterfly
> Algorithmus einlesen, wie der konkret funktioniert.
Geht mir auch so, nur dass ich nicht sicher bin, ob ich überhaupt in der 
Lage bin den Stoff wirklich zu verstehen.

Karl Heinz Buchegger schrieb:
> Wenn du das ganze sowieso am PC laufen hast, hindert dich ja nichts
> daran, den Code mit printf zu spicken, erst mal mit einer kleineren
> Datenmenge zu operieren und dann anhand der Ausgaben kontrollieren, ob
> die richtigen Werte (sowohl Index als auch Zahlenwerte) miteinander
> verrechnet werden.
Dafür mache ich es erstmal am PC. Bisher ist der FFT-Code aber eine 
Black Box für mich. So kann ich natürlich auch nichts debuggen.

Georg G. schrieb:
> Im Kopf der fft.cpp schreibt der Autor, dass alle Werte short int sind
> und von -32768 bis +32767 laufen. Float war ihm zu langsam und zu
> unbequem. Vermutlich ist einer der Fehler im Code, dass fr[] und fi[]
> als char definiert sind. In seinem Beispiel Code weist er auch int Werte
> zu und nicht char.
Stimmt. Gerade habe ich einfach mal blauäugig alles zu unsigned short 
int gemacht, aber natürlich ohne Erfolg. Ich frage mich nur, wie es sein 
kann, dass der Code beim Autor und auch anderen funktioniert hat. Dann 
kann doch eigentlich nichts so grundsätzliches falsch sein.

von Georg G. (df2au)


Lesenswert?

Als Zwischenergebnis:
In dem Paket sind zwei FFT Funktionen, für real/imaginär Daten und für 
nur-real-Daten.

Die nur-real-Daten Funktion hat in der Theorie den Charme, das sie 
drastisch schneller ist. Leider funktioniert sie in der angegebenen Form 
nicht.

Die real/imaginär Daten Funktion funktioniert gut, wenn man nur real 
Daten vorgibt und das imaginäre Feld mit Nullen füllt. Mit imaginär 
Daten ungleich Null habe ich es nicht versucht.

Es sind einige verbesserungsfähige Stellen in dem Programm. Als 
Beispiel: Man muss kein int16_t aus einer Tabelle von int8_t Werten 
lesen. Die Kommentare sind teilweise - wohl aus historischen Gründen - 
irreführend.

Vielleicht findet sich ja noch jemand, der die Macken sucht und hier 
publiziert.

von Frank K. (fchk)


Lesenswert?

Josef schrieb:
> Achso, der Controller steht noch nicht ganz fest, aber es soll
> wahrscheinlich irgendein PIC werden. Aber das müsste sich ja recht leid
> adaptieren lassen, oder?

Wenn es ein dsPIC wird (und das wäre bei einer FFT vielleicht sinnig), 
hat sich Microchip schon darum gekümmert:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en023598

Die Bibliothek ist beim C30 Compiler dabei. Du darfst sie einfach 
benutzen.

fchk

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.