Mam postęp w zakresie modułu fft, który posłuży do budowy "wodospadu" lub innej formy obrazowania zawartości otoczenia odbieranego sygnału.
Piszę o otoczeniu bo zakres analizy jest ograniczony twierdzeniem Nyquista-Shannon-a o próbkowaniu co w skrócie oznacza, że uda się "zauważyć" jedynie te składniki w sygnale, których częstotliwość jest dwukrotnie mniejsza niż częstotliwość próbkowania.
W przypadku kodu Waleta, który działa z częstotliwością próbkowania 24kHz, fft dostarczy informacji o sygnałach co najwyżej 12kHz.
Nie wchodząc w szczegóły należy wstępnie zapewnić aby do członu cyfrowego przetwarzania nie docierały sygnały o częstotliwościach większych niż 12kHz co powinny zapewnić wzmacniacze w torze analogowym między mieszaczem Tayloe a procesorem lub należy użyć w części cyfrowej na wejściu odpowiedni, cyfrowy filtr dolnoprzepustowy. W przypadku Waleta zastosowany jest pierwszy z tych sposobów za pomocą R8/C8 i R9/C10 (płytka ha).
Dotychczasowe przetwarzanie sygnały działa według schematu:
gdzie:
i2s z lewej to w uproszczeniu wejście a/d
FilteredStream to ciąg filtrów cyfrowych (Hilbert + lpf/bpf)
VolumeMeter to moduł pomiaru siły sygnału
i2s z prawej to w uproszczeniu wyjście d/a
a odpowiednie polecenia w kodzie programu są następujące:
Wprowadzenie fft wymaga rozdzielenia sygnału do czego w Arduino Audio Tools można wykorzystać klasę MultiOutput a odpowiedni schemat przetwarzania danych będzie wyglądał następująco:
Tak więc sygnał analogowy "wczytany" do procesora przez konwerter a/d (moduł i2s) zostaje podzielony na dwie kopie z których jedna służy wyłącznie do zasilania modułu fft a druga to opisany wcześniej przebieg związany z filtrowaniem (Hilbert + lpf/bpf), pomiarem siły sygnału (volumeMeter) oraz konwersji d/a do postaci analogowej (i2s).
Odpowiednie polecenia wraz z ich logicznym następstwem pokazuje poniżej:
Okazało się, że krytyczna jest kolejność poleceń szczególnie dla inFiltered. Aby filtrowanie działało od startu tego modułu należy go najpierw uruchomić poleceniem inFiltered.begin(info); a kolejno trzeba zdefiniować sposób filtrowania poleceniami inFiltered.setFilter(...);
cdn lj.
Github
Piszę o otoczeniu bo zakres analizy jest ograniczony twierdzeniem Nyquista-Shannon-a o próbkowaniu co w skrócie oznacza, że uda się "zauważyć" jedynie te składniki w sygnale, których częstotliwość jest dwukrotnie mniejsza niż częstotliwość próbkowania.
W przypadku kodu Waleta, który działa z częstotliwością próbkowania 24kHz, fft dostarczy informacji o sygnałach co najwyżej 12kHz.
Nie wchodząc w szczegóły należy wstępnie zapewnić aby do członu cyfrowego przetwarzania nie docierały sygnały o częstotliwościach większych niż 12kHz co powinny zapewnić wzmacniacze w torze analogowym między mieszaczem Tayloe a procesorem lub należy użyć w części cyfrowej na wejściu odpowiedni, cyfrowy filtr dolnoprzepustowy. W przypadku Waleta zastosowany jest pierwszy z tych sposobów za pomocą R8/C8 i R9/C10 (płytka ha).
Dotychczasowe przetwarzanie sygnały działa według schematu:
Kod:
i2s -> FilteredStream -> VolumeMeter -> i2sgdzie:
i2s z lewej to w uproszczeniu wejście a/d
FilteredStream to ciąg filtrów cyfrowych (Hilbert + lpf/bpf)
VolumeMeter to moduł pomiaru siły sygnału
i2s z prawej to w uproszczeniu wyjście d/a
a odpowiednie polecenia w kodzie programu są następujące:
Kod:
uint16_t sample_rate = 24000;
uint16_t channels = 2;
uint16_t bits_per_sample = 16; // or try with 24 or 32
AudioInfo info(sample_rate, channels, bits_per_sample); //ogólne definicje przetwarzania cyfrowego
...............
I2SStream i2s;
FilteredStream<int16_t, float> inFiltered(i2s, channels); //uruchomienie filtrowania z danymi od konwertera a/d
VolumeMeter volumeMeter(inFiltered); //pomiar siły sygnału przefiltrowanych danych
StreamCopy copier(volumeMeter, i2s); //uruchomienie procesu transferu danych z wejścia a/d do sekcji filtrów cyfrowych
................
void setup(void) {
.......................
inFiltered.setFilter(0, new FilterChain<float, 2>({ new BiQuadDF2<float>(b_lpf24_36, a_lpf24_36), new FIR<float>(hilbert_24_81) })); //definicja filtrowania dla kanału 0
inFiltered.setFilter(1, new FilterChain<float, 2>({ new BiQuadDF2<float>(b_lpf24_36, a_lpf24_36), new FIR<float>(coeffs_delay_81) })); //definicja filtrowania dla kanału 1
volumeMeter.begin(info); //uruchomienie procesu pomiaru siły sygnału
.....................
}Wprowadzenie fft wymaga rozdzielenia sygnału do czego w Arduino Audio Tools można wykorzystać klasę MultiOutput a odpowiedni schemat przetwarzania danych będzie wyglądał następująco:
Kod:
fft
.
i2s -> multiOutput
.
inFiltered -> volumeMeter -> i2sTak więc sygnał analogowy "wczytany" do procesora przez konwerter a/d (moduł i2s) zostaje podzielony na dwie kopie z których jedna służy wyłącznie do zasilania modułu fft a druga to opisany wcześniej przebieg związany z filtrowaniem (Hilbert + lpf/bpf), pomiarem siły sygnału (volumeMeter) oraz konwersji d/a do postaci analogowej (i2s).
Odpowiednie polecenia wraz z ich logicznym następstwem pokazuje poniżej:
Kod:
AudioRealFFT fft; //utworzenie instancji fft
I2SStream i2s;
MultiOutput multiOutput; //utworzenie instancji MultiOutput o nazwie multiOutput
StreamCopy copier; //utworzenie instancji kopiowania danych strumienia i2s
VolumeMeter volumeMeter; utworzenie instancji VolumeMeter o nazwie volumeMeter
FilteredStream<int16_t, float> inFiltered; //utworzenie instancji filtrów o nazwie inFiltered
void setup(void) {
.........
inFiltered.begin(info); //uruchomienie inFiltered
inFiltered.setFilter(0, new FilterChain<float, 2>({ new BiQuadDF2<float>(b_lpf24_36, a_lpf24_36), new FIR<float>(hilbert_24_81) })); //definicja filtrowania dla kanału 0
inFiltered.setFilter(1, new FilterChain<float, 2>({ new BiQuadDF2<float>(b_lpf24_36, a_lpf24_36), new FIR<float>(coeffs_delay_81) })); //definicja filtrowania dla kanału 1
inFiltered.setOutput(volumeMeter); //skierowanie danych z inFiltered do volumeMeter
volumeMeter.begin(info); //uruchomienie volumeMeter
volumeMeter.setOutput(i2s); //skierowanie danych z pomiaru do wyjścia d/a
multiOutput.add(fft); //połączenie pierwszego wyjścia multiOutput z modułem fft
multiOutput.add(inFiltered); //połączenie drugiego wyjście multiOutput z modułem filtrowania
multiOutput.begin(info); //uruchomienie podziału sygnału
copier.begin(multiOutput,i2s); /start transferu danych z wejścia i2s do dzielnika multiOutput
.......
}Okazało się, że krytyczna jest kolejność poleceń szczególnie dla inFiltered. Aby filtrowanie działało od startu tego modułu należy go najpierw uruchomić poleceniem inFiltered.begin(info); a kolejno trzeba zdefiniować sposób filtrowania poleceniami inFiltered.setFilter(...);
cdn lj.
Github

