FFT - waterfall
Do wizualizacji danych z fft wykorzystałem jeden z elementów biblioteki TFT dla ESP32 o nazwie eSprite lub Sprite. W skrócie mówiąc ta funkcjonalność polega na utworzeniu grafiki w pamięci procesora a następnie przesłaniu jej, z dużą prędkością, do wyświetlacza. Formowanie grafiki w pamięci zapobiega efektom migotania obrazu a duża prędkość przesyłania danych z pamięci do wyświetlacza za pomocą SPI umożliwia animację obrazów. Grafika wodospadu nie potrzebuje dużych prędkości animacji ale za to wykorzystuje kolejną funkcjonalność Sprite, którą pokazałem na rysunku:
[Obrazek: https://lx-net.pl/walet/image/esprite.jpg]
Sprite to prostokątny obszar w pamięci zdefiniowany przez szerokość (width) oraz wysokość (height) - wyrażone w pixel-ach gdzie zmienne X,Y to miejsce na ekranie gdzie należy umieścić utworzona grafikę.
Waterfall to linie na ekranie pokazujące stan widma odbieranego sygnału (biny) w momencie analizy w taki sposób, że amplituda konkretnego bin-a opisana jest kolorem. "Agresywność" koloru związana jest z wielkością amplitudy bin-a tak, że słabszym binom odpowiadają ciemne barwy a silniejszym jasne: od czarnego przez granatowy, zielony, żółty, czerwony do białego.
Na rysunku pokazałem stan Sprite w momencie tworzenia nowej linii (zielona na górnym rysunku). Rzeczywisty kolor tej linii będzie zależeć od zawartości i wielkości binów z bieżącej analizy fft. Elementarna funkcja scroll pozwala na wprowadzenie tej linii do obszaru Sprite z jednoczesnym przewinięciem wszystkich istniejących linii w dół oraz usunięciem najstarszej linii wodospadu co pokazuje dolna część rysunku.
W skrócie, te wszystkie działania opisują następujące polecenia:
TFT_eSprite img = TFT_eSprite(&tft); // deklaracja grafiki img jako obiektu typu Sprite
....
#define wf_width 216 //szerokość obszaru na wodospad w pixel-ach
#define wf_height 41 //wysokośc obszaru na wodospad w pixel-ach
....
void setup(void) {
....
img.createSprite(wf_width, wf_height); //Rezerwacja pamięci RAM na Sprite
img.fillSprite(TFT_BLACK); //Wypełnienie obszaru Sprite kolorem czarnym
....
}
void setup(void) {
....
//te polecenia powtarzane są 4 razy na sekundę po wyliczeniu transformaty fft oraz przypisaniu koloru do każdego z binów
img.drawPixel(xn, 0, color);//kompletacja linii dla wszystkich xn w pętli
img.scroll(0, 1); // wpisanie nowej linii do obszaru Sprite
img.pushSprite(X, Y); // przesłanie grafiki img do wyświetlacza zaczynając od miejsca X,Y
....
}
Przesłanie zawartości pamięci z nową grafiką do wyświetlacza zapewnia polecenie img.pushSprite(X, Y).
Każda z linii wodospadu ma szerokość 216 pixeli co pozwala na zapisanie 2x107 binów z części dodatniej i ujemnej widma sygnału. Dwa dodatkowo pixel-e nie są zapisywane i rozdzielają na środku biny dodatnie od ujemnych. Przy szerokości analizy fft każdy bin ma ok. 94Hz a więc tworzą one łącznie pasmo ok. +- 10kHz.
Z kolei w pionie obszar Sprite ma szerokość 41 pixel-i a więc może pomieścić 41 linii jednocześnie. Ponieważ odświeżanie grafiki wodospadu dokonywane jest 4 razy w ciągu sekundy to na ekranie zmieście się ok. 41/4 = 10 sekund zapisu wodospadu czyli najniższa jego linia pokazuje stan sprzed 10 sekund.
[Obrazek: https://lx-net.pl/walet/image/fft_c02d.jpg]
Dla potrzeb wyświetlania wodospadu przygotowałem nieco inną grafikę gdzie oś X to częstotliwości widma sygnału a oś Y to czas obserwacji wodospadu w sekundach.
Wydaje się, że z punktu widzenia możliwości grafiki przyjęte założenia: dokładność analizy (fft_size) oraz czas obserwacji wodospadu są zbliżone do optymalnych. Dla 128 binów dodatnich i ujemnych wykorzystuję po 107 dla każdego zakresu a każdy bin ma swój odpowiednik w jednym pixel-u. Gdyby dokładność fft była dwukrotnie większa to miałbym do dyspozycji 256 binów dla każdego z zakresów (czyli 512) a każdy bin miałby szerokość ok. 47Hz i nie udałoby się dopasować każdego bin-a do pixel-a przy ograniczeniu pola wizualizacji do 216 pixel-i. Dodatkowe zwiększenie dokładności fft tylko pogłębia ten problem bo podczas tworzenia grafiki zysk z tej dokładności zostałby utracony ograniczoną dokładnością wyświetlacza. Oczywiście, mając wyświetlacz np. o polu 640 pix-eli miałoby to sens.
Wizualizacja fft za pomocą bar grafu oraz wodospadu
Jak ostatecznie wygląda wodospad fft w Walecie pokazuje film z powyższego linku. Dodatkowo skomentuję obraz na ekranie po włączeniu dwóch silnych sygnałów. Pojawiają się one z lewej strony przy odbiorze LSB ale po prawej stronie widać jakby "cienie" tych sygnałów o istotnie mniejszej intensywności. Cienie te wędrują synchronicznie za sygnałami podstawowymi stanowiąc ich lustrzane odbicie względem środka wykresu (częstotliwość 0Hz). Są to sygnały jakie "produkuje" analiza fft wynikające ze skończonej dokładności zarówno obliczeń jak i kwantyzacji oraz z dużego poziomu analizowanych sygnałów. Poziom tych "cieni" jest jednak niewielki w stosunku do sygnałów podstawowych i optymalnie, w idealnych warunkach, nie powinny być one widoczne wcale. Sygnały podstawowe mają poziom ok. S9 +20dB a ich cienie są dużo bardziej stłumione choć faktycznie są widziane przez fft.
Na Github pojawiło się już oprogramowania zawierające ostatnie rozwiązania opisane jako wersja 1.3.
cdn... lj
Github
Do wizualizacji danych z fft wykorzystałem jeden z elementów biblioteki TFT dla ESP32 o nazwie eSprite lub Sprite. W skrócie mówiąc ta funkcjonalność polega na utworzeniu grafiki w pamięci procesora a następnie przesłaniu jej, z dużą prędkością, do wyświetlacza. Formowanie grafiki w pamięci zapobiega efektom migotania obrazu a duża prędkość przesyłania danych z pamięci do wyświetlacza za pomocą SPI umożliwia animację obrazów. Grafika wodospadu nie potrzebuje dużych prędkości animacji ale za to wykorzystuje kolejną funkcjonalność Sprite, którą pokazałem na rysunku:
[Obrazek: https://lx-net.pl/walet/image/esprite.jpg]
Sprite to prostokątny obszar w pamięci zdefiniowany przez szerokość (width) oraz wysokość (height) - wyrażone w pixel-ach gdzie zmienne X,Y to miejsce na ekranie gdzie należy umieścić utworzona grafikę.
Waterfall to linie na ekranie pokazujące stan widma odbieranego sygnału (biny) w momencie analizy w taki sposób, że amplituda konkretnego bin-a opisana jest kolorem. "Agresywność" koloru związana jest z wielkością amplitudy bin-a tak, że słabszym binom odpowiadają ciemne barwy a silniejszym jasne: od czarnego przez granatowy, zielony, żółty, czerwony do białego.
Na rysunku pokazałem stan Sprite w momencie tworzenia nowej linii (zielona na górnym rysunku). Rzeczywisty kolor tej linii będzie zależeć od zawartości i wielkości binów z bieżącej analizy fft. Elementarna funkcja scroll pozwala na wprowadzenie tej linii do obszaru Sprite z jednoczesnym przewinięciem wszystkich istniejących linii w dół oraz usunięciem najstarszej linii wodospadu co pokazuje dolna część rysunku.
W skrócie, te wszystkie działania opisują następujące polecenia:
TFT_eSprite img = TFT_eSprite(&tft); // deklaracja grafiki img jako obiektu typu Sprite
....
#define wf_width 216 //szerokość obszaru na wodospad w pixel-ach
#define wf_height 41 //wysokośc obszaru na wodospad w pixel-ach
....
void setup(void) {
....
img.createSprite(wf_width, wf_height); //Rezerwacja pamięci RAM na Sprite
img.fillSprite(TFT_BLACK); //Wypełnienie obszaru Sprite kolorem czarnym
....
}
void setup(void) {
....
//te polecenia powtarzane są 4 razy na sekundę po wyliczeniu transformaty fft oraz przypisaniu koloru do każdego z binów
img.drawPixel(xn, 0, color);//kompletacja linii dla wszystkich xn w pętli
img.scroll(0, 1); // wpisanie nowej linii do obszaru Sprite
img.pushSprite(X, Y); // przesłanie grafiki img do wyświetlacza zaczynając od miejsca X,Y
....
}
Przesłanie zawartości pamięci z nową grafiką do wyświetlacza zapewnia polecenie img.pushSprite(X, Y).
Każda z linii wodospadu ma szerokość 216 pixeli co pozwala na zapisanie 2x107 binów z części dodatniej i ujemnej widma sygnału. Dwa dodatkowo pixel-e nie są zapisywane i rozdzielają na środku biny dodatnie od ujemnych. Przy szerokości analizy fft każdy bin ma ok. 94Hz a więc tworzą one łącznie pasmo ok. +- 10kHz.
Z kolei w pionie obszar Sprite ma szerokość 41 pixel-i a więc może pomieścić 41 linii jednocześnie. Ponieważ odświeżanie grafiki wodospadu dokonywane jest 4 razy w ciągu sekundy to na ekranie zmieście się ok. 41/4 = 10 sekund zapisu wodospadu czyli najniższa jego linia pokazuje stan sprzed 10 sekund.
[Obrazek: https://lx-net.pl/walet/image/fft_c02d.jpg]
Dla potrzeb wyświetlania wodospadu przygotowałem nieco inną grafikę gdzie oś X to częstotliwości widma sygnału a oś Y to czas obserwacji wodospadu w sekundach.
Wydaje się, że z punktu widzenia możliwości grafiki przyjęte założenia: dokładność analizy (fft_size) oraz czas obserwacji wodospadu są zbliżone do optymalnych. Dla 128 binów dodatnich i ujemnych wykorzystuję po 107 dla każdego zakresu a każdy bin ma swój odpowiednik w jednym pixel-u. Gdyby dokładność fft była dwukrotnie większa to miałbym do dyspozycji 256 binów dla każdego z zakresów (czyli 512) a każdy bin miałby szerokość ok. 47Hz i nie udałoby się dopasować każdego bin-a do pixel-a przy ograniczeniu pola wizualizacji do 216 pixel-i. Dodatkowe zwiększenie dokładności fft tylko pogłębia ten problem bo podczas tworzenia grafiki zysk z tej dokładności zostałby utracony ograniczoną dokładnością wyświetlacza. Oczywiście, mając wyświetlacz np. o polu 640 pix-eli miałoby to sens.
Wizualizacja fft za pomocą bar grafu oraz wodospadu
Jak ostatecznie wygląda wodospad fft w Walecie pokazuje film z powyższego linku. Dodatkowo skomentuję obraz na ekranie po włączeniu dwóch silnych sygnałów. Pojawiają się one z lewej strony przy odbiorze LSB ale po prawej stronie widać jakby "cienie" tych sygnałów o istotnie mniejszej intensywności. Cienie te wędrują synchronicznie za sygnałami podstawowymi stanowiąc ich lustrzane odbicie względem środka wykresu (częstotliwość 0Hz). Są to sygnały jakie "produkuje" analiza fft wynikające ze skończonej dokładności zarówno obliczeń jak i kwantyzacji oraz z dużego poziomu analizowanych sygnałów. Poziom tych "cieni" jest jednak niewielki w stosunku do sygnałów podstawowych i optymalnie, w idealnych warunkach, nie powinny być one widoczne wcale. Sygnały podstawowe mają poziom ok. S9 +20dB a ich cienie są dużo bardziej stłumione choć faktycznie są widziane przez fft.
Na Github pojawiło się już oprogramowania zawierające ostatnie rozwiązania opisane jako wersja 1.3.
cdn... lj
Github

