Kazalo:

1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284: 9 korakov
1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284: 9 korakov

Video: 1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284: 9 korakov

Video: 1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284: 9 korakov
Video: Интеграция бессознательного и уравновешивание полярностей. 2024, Julij
Anonim
1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284
1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284
1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284
1024 vzorcev FFT analizatorja spektra z uporabo Atmega1284

Ta razmeroma enostaven vodič (glede na kompleksnost te teme) vam bo pokazal, kako lahko naredite zelo preprost analizator spektra 1024 vzorcev z uporabo plošče tipa Arduino (1284 ozko) in serijskega ploterja. Vsaka vrsta združljive plošče Arduino bo delovala, toda več RAM -a ima, najboljša frekvenčna ločljivost, ki jo boste dobili. Za izračun FFT z 1024 vzorci bo potreboval več kot 8 KB RAM -a.

Analiza spektra se uporablja za določanje glavnih frekvenčnih komponent signala. Mnogi zvoki (na primer tisti, ki jih proizvaja glasbeni inštrument) so sestavljeni iz osnovne frekvence in nekaterih harmonikov, ki imajo frekvenco, ki je celoštevilčen večkratnik osnovne frekvence. Analizator spektra vam bo pokazal vse te spektralne komponente.

To nastavitev boste morda želeli uporabiti kot števec frekvenc ali preveriti kakršne koli signale, za katere sumite, da povzročajo nekaj hrupa v vašem elektronskem vezju.

Tu se bomo osredotočili na programsko opremo. Če želite narediti trajno vezje za določeno aplikacijo, boste morali ojačati in filtrirati signal. Ta predkondicioniranje je popolnoma odvisno od signala, ki ga želite preučiti, odvisno od njegove amplitude, impedance, največje frekvence itd … Lahko preverite

1. korak: Namestitev knjižnice

Uporabljali bomo knjižnico ArduinoFFT, ki jo je napisal Enrique Condes. Ker želimo čim bolj prihraniti RAM, bomo uporabili razvojno vejo tega skladišča, ki omogoča uporabo podatkovnega tipa float (namesto dvojnega) za shranjevanje vzorčenih in izračunanih podatkov. Zato ga moramo namestiti ročno. Brez skrbi, samo prenesite arhiv in ga raztegnite v mapo knjižnice Arduino (na primer v privzeti konfiguraciji sistema Windows 10: C: / Users / _vaše_ime_ uporabnika_ / Dokumenti / Arduino / knjižnice)

Ali je knjižnica pravilno nameščena, lahko preverite tako, da sestavite enega od navedenih primerov, na primer "FFT_01.ino."

2. korak: Fourierjeva transformacija in koncepti FFT

Opozorilo: če ne prenesete nobenega matematičnega zapisa, boste morda želeli preskočiti na 3. korak. Če pa ne dobite vsega, razmislite o zaključku na koncu razdelka.

Frekvenčni spekter dobimo z algoritmom Fast Fourier Transform. FFT je digitalna izvedba, ki približuje matematični koncept Fourierjeve transformacije. Po tem konceptu, ko dobite evolucijo signala po časovni osi, lahko poznate njegovo predstavitev v frekvenčni domeni, sestavljeni iz kompleksnih (realnih + namišljenih) vrednosti. Koncept je obojestranski, zato ga, ko poznate predstavitev frekvenčne domene, lahko pretvorite nazaj v časovno domeno in dobite signal nazaj tako kot pred preoblikovanjem.

Toda kaj bomo storili s tem nizom izračunanih kompleksnih vrednosti v časovni domeni? No, večina bo prepuščena inženirjem. Za nas bomo poklicali drug algoritem, ki bo te kompleksne vrednosti spremenil v podatke o spektralni gostoti: to je vrednost (= intenzivnost), povezana z vsakim frekvenčnim pasom. Število frekvenčnih pasov bo enako številu vzorcev.

Zagotovo ste seznanjeni s konceptom izenačevalnika, kot je ta Nazaj v osemdeseta z grafičnim eQ. No, dobili bomo enake rezultate, vendar z 1024 pasovi namesto 16 in veliko večjo ločljivostjo intenzivnosti. Ko izenačevalnik daje globalni pogled na glasbo, natančna spektralna analiza omogoča natančno izračun intenzivnosti vsakega od 1024 pasov.

Popoln koncept, vendar:

  1. Ker je FFT digitalizirana različica Fourierjeve transformacije, približa digitalni signal in izgubi nekaj informacij. Torej, strogo gledano, rezultat FFT, če se preoblikuje nazaj z obrnjenim algoritmom FFT, ne bi dal natančno izvirnega signala.
  2. Tudi teorija upošteva signal, ki ni končen, vendar je to vedno trajen konstanten signal. Ker ga bomo digitalizirali le za določeno časovno obdobje (tj. Vzorci), bo vnesenih še nekaj napak.
  3. Končno bo ločljivost analogno -digitalne pretvorbe vplivala na kakovost izračunanih vrednosti.

V praksi

1) Frekvenca vzorčenja (zabeleženo fs)

Vzorec bomo vzorčili, torej merili njegovo amplitudo, vsakih 1/fs sekund. fs je frekvenca vzorčenja. Na primer, če vzorčimo pri 8 KHz, bo ADC (analogno -digitalni pretvornik), ki je na krovu čipa, meril vsakih 1/8000 sekund.

2) Število vzorcev (zabeleženih N ali vzorcev v kodi)

Ker moramo pred zagonom FFT pridobiti vse vrednosti, jih bomo morali shraniti, zato bomo omejili število vzorcev. Algoritem FFT potrebuje več vzorcev z močjo 2. Bolj ko imamo vzorcev, boljši smo, vendar potrebuje veliko pomnilnika, toliko bolj bomo potrebovali tudi shranjevanje preoblikovanih podatkov, ki so kompleksne vrednosti. Knjižnica Arduino FFT z uporabo prihrani nekaj prostora

  • En niz z imenom "vReal" za shranjevanje vzorčenih podatkov in nato dejanskega dela preoblikovanih podatkov
  • En niz z imenom "vImag" za shranjevanje namišljenega dela preoblikovanih podatkov

Potrebna količina RAM -a je 2 (nizi) * 32 (bitovi) * N (vzorci).

Tako bomo v našem Atmega1284, ki ima lepih 16 KB RAM -a, shranili največ N = 16000*8 /64 = 2000 vrednosti. Ker mora biti število vrednosti 2, bomo shranili največ 1024 vrednosti.

3) Ločljivost frekvence

FFT bo izračunal vrednosti za toliko frekvenčnih pasov, kot je število vzorcev. Ti pasovi bodo obsegali od 0 HZ do frekvence vzorčenja (fs). Zato je frekvenčna ločljivost:

Fresolucija = fs / N

Ločljivost je boljša, če je nižja. Zato za boljšo ločljivost (nižjo) želimo:

  • več vzorcev in/ali
  • nižji fs

Ampak…

4) Najmanjši fs

Ker želimo videti veliko frekvenc, od katerih so nekatere veliko višje od "osnovne frekvence", ne moremo nastaviti fs prenizko. Pravzaprav obstaja izrek o vzorčenju Nyquist – Shannon, ki nas prisili, da imamo frekvenco vzorčenja precej nad dvakratno največjo frekvenco, ki bi jo radi preizkusili.

Na primer, če želimo analizirati ves spekter od 0 Hz do recimo 15 KHz, kar je približno največja frekvenca, ki jo večina ljudi lahko razločno sliši, moramo frekvenco vzorčenja nastaviti na 30 KHz. Pravzaprav ga elektroniki pogosto nastavijo na 2,5 (ali celo 2,52) * največjo frekvenco. V tem primeru bi to bilo 2,5 * 15 KHz = 37,5 KHz. Običajne frekvence vzorčenja v profesionalnem zvoku so 44,1 KHz (snemanje zvočnega CD -ja), 48 KHz in več.

Zaključek:

Točke 1 do 4 vodijo do: želimo uporabiti čim več vzorcev. V našem primeru s 16 KB RAM -a bomo upoštevali 1024 vzorcev. Vzorčiti želimo na čim nižji frekvenci vzorčenja, če je dovolj visoka za analizo najvišje frekvence, ki jo pričakujemo v našem signalu (vsaj 2,5 * ta frekvenca).

3. korak: Simulacija signala

Simulacija signala
Simulacija signala

Za prvi poskus bomo nekoliko spremenili primer TFT_01.ino, ki je naveden v knjižnici, za analizo signala, sestavljenega iz

  • Temeljna frekvenca, nastavljena na 440 Hz (glasbeni A)
  • 3. harmonik pri polovični moči osnovnega ("-3 dB")
  • 5. harmonik pri 1/4 moči osnovne ("-6 dB)

Nastali signal lahko vidite na sliki nad sliko. Resnično je zelo podoben resničnemu signalu, ki ga včasih vidimo na osciloskopu (temu bi rekel "Batman"), ko pride do izrezovanja sinusnega signala.

4. korak: Analiza simuliranega signala - kodiranje

0) Vključi knjižnico

#include "arduinoFFT.h"

1) Opredelitve pojmov

V razdelkih z izjavami imamo

const bajt adcPin = 0; // A0

const uint16_t vzorcev = 1024; // Ta vrednost MORA VEDNO biti moč 2 const uint16_t samplingFrequency = 8000; // Vpliva na največjo vrednost časovnika v timer_setup () SYSCLOCK/8/samplingFrekvenca mora biti celo število

Ker ima signal 5. harmoniko (frekvenca tega harmonika = 5 * 440 = 2200 Hz), moramo frekvenco vzorčenja nastaviti nad 2,5 * 2200 = 5500 Hz. Tu sem izbral 8000 Hz.

Izjavimo tudi matrike, v katere bomo shranili surove in izračunane podatke

float vReal [vzorci];

float vImag [vzorci];

2) Namernost

Ustvarjamo objekt ArduinoFFT. Razvojna različica ArduinoFFT uporablja predlogo, zato lahko uporabimo bodisi plavajoči ali dvojni tip podatkov. Float (32 bitov) je dovolj za splošno natančnost našega programa.

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, vzorci, vzorčenjeFrequency);

3) Simulacija signala z izpolnjevanjem niza vReal, namesto da bi ga napolnili z vrednostmi ADC.

Na začetku zanke zapolnimo matriko vReal z:

plavajoči cikli = (((vzorci) * signalFrequency) / vzorčenjeFrequency); // Število ciklov signalov, ki jih bo vzorčenje prebralo

za (uint16_t i = 0; i <vzorci; i ++) {vReal = float ((amplituda * (sin ((i * (TWO_PI * cikli))) / vzorci))))); / * Zgradite podatke s pozitivnimi in negativne vrednosti */ vReal += float ((amplituda * (sin ((3 * i * (TWO_PI * cikla)))/ vzorci)))/ 2.0);/ * Zgradite podatke s pozitivnimi in negativnimi vrednostmi */ vReal += float ((amplituda * (sin ((5 * i * (TWO_PI * ciklov))) / vzorci)))) / 4,0); / * Zgradite podatke s pozitivnimi in negativnimi vrednostmi * / vImag = 0,0; // Imaginarni del je treba v primeru zanke ničeliti, da se izognemo napačnim izračunom in prelivom}

Dodamo digitalizacijo temeljnega vala in dveh harmonikov z manjšo amplitudo. Nato zamišljeno matriko inicializiramo z ničlami. Ker je ta niz poseljen z algoritmom FFT, ga moramo pred vsakim novim izračunom znova počistiti.

4) FFT računalništvo

Nato izračunamo FFT in spektralno gostoto

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward);

FFT.compute (FFTDirection:: Forward); / * Izračunaj FFT */ FFT.complexToMagnitude (); / * Izračunajte velikosti */

Operacija FFT.windowing (…) spreminja neobdelane podatke, ker izvajamo FFT na omejenem številu vzorcev. Prvi in zadnji vzorec predstavljata diskontinuiteto (na eni od njiju ni "nič"). To je vir napak. Operacija "okno" ponavadi zmanjša to napako.

FFT.compute (…) s smerjo "naprej" izračuna transformacijo iz časovne domene v frekvenčno področje.

Nato izračunamo vrednosti velikosti (tj. Intenzivnosti) za vsak frekvenčni pas. Polje vReal je zdaj napolnjeno z vrednostmi velikosti.

5) Risba serijskega ploterja

Natisnimo vrednosti na serijski ploter s klicem funkcije printVector (…)

PrintVector (vReal, (vzorci >> 1), SCL_FREQUENCY);

To je splošna funkcija, ki omogoča tiskanje podatkov s časovno osjo ali frekvenčno osjo.

Natisnemo tudi frekvenco pasu, ki ima najvišjo vrednost

float x = FFT.majorPeak ();

Serial.print ("f0 ="); Serijski.tisk (x, 6); Serial.println ("Hz");

5. korak: Analiza simuliranega signala - rezultati

Analiza simuliranega signala - rezultati
Analiza simuliranega signala - rezultati

Vidimo 3 konice, ki ustrezajo osnovni frekvenci (f0), 3. in 5. harmoniko, s polovico in 1/4 velikosti f0, kot je bilo pričakovano. Na vrhu okna lahko beremo f0 = 440.430114 Hz. Ta vrednost ni ravno 440 Hz zaradi vseh zgoraj navedenih razlogov, vendar je zelo blizu dejanske vrednosti. Toliko nepomembnih decimalk ni bilo treba prikazati.

6. korak: Analiza resničnega signala - ožičenje ADC -ja

Analiza resničnega signala - ožičenje ADC -ja
Analiza resničnega signala - ožičenje ADC -ja

Ker vemo, kako naprej v teoriji, bi radi analizirali pravi signal.

Ožičenje je zelo preprosto. Ozemljitve in signalni vod povežite z A0 pinom vaše plošče skozi zaporedni upor z vrednostjo od 1 KOhm do 10 KOhm.

Ta serijski upor bo zaščitil analogni vhod in se izognil zvonjenju. Mora biti čim višji, da se izogne zvonjenju, in čim nižji, da zagotovi dovolj toka za hitro polnjenje ADC -ja. Če želite izvedeti pričakovano impedanco signala, priključenega na vhodu ADC, si oglejte podatkovni list MCU.

Za to predstavitev sem uporabil funkcijski generator za napajanje sinusoidnega signala s frekvenco 440 Hz in amplitudo okoli 5 voltov (najbolje je, če je amplituda med 3 in 5 voltov, tako da se ADC uporablja skoraj v celoti), skozi upor 1,2 KOhm.

7. korak: Analiza pravega signala - kodiranje

0) Vključi knjižnico

#include "arduinoFFT.h"

1) Izjave in primeri

V razdelku z deklaracijo definiramo vhod ADC (A0), število vzorcev in frekvenco vzorčenja, tako kot v prejšnjem primeru.

const bajt adcPin = 0; // A0

const uint16_t vzorcev = 1024; // Ta vrednost MORA VEDNO biti moč 2 const uint16_t samplingFrequency = 8000; // Vpliva na največjo vrednost časovnika v timer_setup () SYSCLOCK/8/samplingFrekvenca mora biti celo število

Ustvarjamo objekt ArduinoFFT

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, vzorci, vzorčenjeFrequency);

2) Nastavitev časovnika in ADC

Časovnik 1 nastavimo tako, da ciklira pri frekvenci vzorčenja (8 KHz) in poveča prekinitev pri primerjavi izhoda.

void timer_setup () {

// ponastavitev časovnika 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = bit (CS11) | bit (WGM12); // CTC, predkaler 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000/8) / frekvenca vzorčenja) -1; }

In nastavite ADC tako

  • Kot vhod uporablja A0
  • Sproži samodejno na vsakem izhodu časovnika 1 primerjava ujemanja B
  • Ustvari prekinitev, ko je pretvorba končana

Ura ADC je nastavljena na 1 MHz, tako da sistemsko uro (16 MHz) vnaprej umerite za 16. Ker vsaka pretvorba traja približno 13 ur v polnem obsegu, je mogoče pretvorbe doseči pri frekvenci 1/13 = 0,076 MHz = 76 KHz. Frekvenca vzorčenja mora biti bistveno nižja od 76 KHz, da ima ADC čas za vzorčenje podatkov. (izbrali smo fs = 8 KHz).

void adc_setup () {

ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // vklopimo ADC, želimo prekinitev po zaključku ADCSRA | = bit (ADPS2); // Predkaler 16 ADMUX = bit (REFS0) | (adcPin & 7); // nastavitev vhoda ADC ADCSRB = bit (ADTS0) | bit (ADTS2); // Časovnik/števec1 Primerjaj sprožilec ujemanja B ADCSRA | = bit (ADATE); // vklop samodejnega sproženja}

Izjavimo, da bo upravljavec prekinitev, ki bo poklican po vsaki pretvorbi ADC, shranil pretvorjene podatke v matriko vReal in počistil prekinitev

// ADC popoln ISR

ISR (ADC_vect) {vReal [resultNumber ++] = ADC; if (rezultat števila == vzorci) {ADCSRA = 0; // izklop ADC}} EMPTY_INTERRUPT (TIMER1_COMPB_vect);

Izčrpno razlago o pretvorbi ADC lahko dobite na Arduinu (analogRead).

3) Nastavitev

V nastavitveni funkciji počistimo namišljeno podatkovno tabelo in pokličemo funkcije za nastavitev časovnika in ADC

ničI (); // funkcija, ki nastavi na 0 vse namišljene podatke - razloženo v prejšnjem razdelku

timer_setup (); adc_setup ();

3) Zanka

FFT.dcRemoval (); // Odstranite DC komponento tega signala, ker je ADC sklican na maso

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward); // Tehtanje podatkov FFT.compute (FFTDirection:: Forward); // Izračunaj FFT FFT.complexToMagnitude (); // Izračunamo velikosti // natisnemo spekter in osnovno frekvenco f0 PrintVector (vReal, (vzorci >> 1), SCL_FREQUENCY); float x = FFT.majorPeak (); Serial.print ("f0 ="); Serijski.tisk (x, 6); Serial.println ("Hz");

Komponento enosmernega toka odstranimo, ker je ADC sklican na ozemljitev in je signal centriran približno okoli 2,5 voltov.

Nato izračunamo podatke, kot je razloženo v prejšnjem primeru.

8. korak: Analiza resničnega signala - rezultati

Analiza resničnega signala - rezultati
Analiza resničnega signala - rezultati

V tem preprostem signalu vidimo le eno frekvenco. Izračunana osnovna frekvenca je 440.118194 Hz. Tu je vrednost spet zelo blizu dejanske frekvence.

9. korak: Kaj pa obrezan sinusni signal?

Kaj pa odrezan sinusni signal?
Kaj pa odrezan sinusni signal?

Zdaj pa malo preobremenimo ADC s povečanjem amplitude signala nad 5 voltov, tako da je odrezan. Ne pritiskajte preveč, da ne uničite vhoda ADC!

Vidimo lahko, da se pojavljajo nekateri harmoniki. Odsekanje signala ustvari visokofrekvenčne komponente.

Videli ste osnove analize FFT na plošči Arduino. Zdaj lahko poskusite spremeniti frekvenco vzorčenja, število vzorcev in parametre okna. Knjižnica dodaja tudi nekaj parametrov za hitrejše izračunavanje FFT z manj natančnosti. Opazili boste, da če se frekvenca vzorčenja nastavi prenizko, bodo izračunane velikosti zaradi spektralnega zlaganja popolnoma napačne.

Priporočena: