W związku z urlopem postanowiłem opisać wykonaną w ostatnim czasie konstrukcję. W zawodzie nauczyciela akademickiego jest to 36 dni roboczych plus w moim przypadku 26 dni zaległych z zeszłego roku. Jest to więc trochę czasu - za dużo.
Wszystko zaczęło się od znalezienia tunera DVB-T Opticam HD N2. Jest to mało wartościowe urządzenie, ponieważ nie obsługuje DVB-T2. Poniżej znajdują się fotografie tunera i pilota:
Po jego rozkręceniu okazało się, że głowica tj. blok w.cz. (RF) jest osobnym modułem. Jest to bardzo rzadkie. Jeszcze rzadszy jest fakt, że do układu występującego w głowicy jest dokumentacja (przynajmniej jakaś). W tym przypadku mamy do czynienia z układem R820T, który to wydaje się być zgodny (przynajmniej na poziomie rejestrów) z tak popularnym R820T2 w RTL-SDR. Poniżej zamieszczam zdjęcia wymontowanej głowicy:
Na podstawie przykładowego schematu głowicy z datasheet i opisu wyprowadzeń R820T można ustalić opis wyprowadzeń tego modułu:
Głowica wydaje się odpowiadać temu, co jest w datasheet:
Wymieniłem tylko kwarc z 16 MHz (oryginalny) na 28,8 MHz. Niestety nie udało mi się tego poprawnie uruchomić z oryginalnym kwarcem.
Mając tak atrakcyjny moduł zdecydowałem się zrobić własny odbiornik SDR (Software-Defined Radio) z wykorzystaniem STM32. Generalnie w SDR chodzi o realizację części odbiornika w sposób programowy z wykorzystaniem procesora DSP lub układu FPGA. Podstawą jest tu jednak tzw. próbkowanie kwadraturowe. Wstępnie można zajrzeć do popularnej i bardzo wartościowej książki Richarda Lyonsa:
Pierwszy raz miałem styczność z tym podręcznikiem wiele lat temu w trakcie studiów. Można tam znaleźć ideę odnośnie tego zagadnienia:
Widzimy tu mnożenie (analogowe) sygnału wejściowego przez sinus i kosinus o częstotliwości równej częstotliwości środkowej sygnału wejściowego. Potem są dwa (analogowe) filtry dolnoprzepustowe o częstotliwości granicznej równej połowie pasma sygnału wejściowego (idealizując). Potem można próbkować sygnały z wyjść tych filtrów, by uzyskać próbki sygnałów i(t) (składowa synfazowa) i q(t) (składowa kwadraturowa) tj. odpowiednio dyskretne i(n) i q(n). W ten złożony sposób uzyskuje się zespoloną postać sygnału:
Da się z tego już obliczyć moduł i fazę na podstawie twierdzenia Pitagorasa i funkcji tangensa w trójkącie prostokątnym. To już umożliwia bardo wiele np. budowę demodulatorów dla modulacji analogowych i cyfrowych, ale "matematycznie" tj. w kodzie programu. Stąd właśnie mowa o SDR. Jest to jednak nie jedyne rozwiązanie. Można zastosować też mieszanie cyfrowe:
Tutaj sygnał wejściowy jest próbkowany (w sposób dolnopasmowy lub pasmowy) i dalej wszystko jest programowe tj. mamy mnożenie przez sin i cos oraz filtry cyfrowe dolnoprzepustowe. Potrzeba jednak do tego znacznie więcej mocy obliczeniowej. Generalnie też można stosować sztuczki przy odpowiednio dobranej częstotliwości próbkowania (jak na rysunku). Da się wtedy zastosować tzw. ciągi mieszające. Zdecydowałem się właśnie na próbkowanie sygnału pasmowego, który to stanowi sygnał pośredniej częstotliwości p.cz. (IF) na wyjściu głowicy z R820T. Zatem odbiornik jest zrealizowany wg. powyższego rysunku. W datasheet R820T2 podano m.in. schemat blokowy odbiornika:
Jak widać dalszemu próbkowaniu podlega sygnał p.cz. (tutaj oznaczony przez LIF). Potem jest już DSP, czyli cyfrowe przetwarzanie sygnałów. Kolejno powstał więc schemat blokowy całego układu oparty o podany w datasheet pomysł i wzmiankowane wyżej mieszanie cyfrowe:
W tym przypadku sygnał p.cz. z głowicy podawany jest do wzmacniacza p.cz. i potem jest już próbkowany sygnał pasmowy. Wzmacniacz ten jest potrzebny, ponieważ maksymalna wartość sygnału różnicowego dla R820T2 to 1,00 Vpp (wg. datasheet - faktycznie jest trochę mniej). Jest to za mało, by w pełni wykorzystać dynamikę przetwornika A/C w STM32F407 (wymagane 0...3,000 V). Ponadto mimo odpowiedniej konfiguracji rejestrów w R820T pasmo jest znacznie za szerokie, by dało się to próbkować przez STM32. Stąd potrzeba nie tylko wzmocnienia, ale też zawężenia pasma. Zaprojektowałem więc bardzo prosty wzmacniacz p.cz.:
Występuje tu wstępnie transformator szerokopasmowy, by pobrać sygnał różnicowy z głowicy i przekształcić go w niesymetryczny. Potem zastosowałem wzmacniacz WE, by uzyskać wstępne wzmocnienie i dopasowanie impedancji. Impedancja wyjściowa tego stopnia to mniej więcej tyle, co R3. Jest to więc dopasowane do filtrów ceramicznych. Zastosowałem tu filtry od fonii różnicowej z telewizora SFE 4,5 MHz. Po filtrach jest drugi stopień WE o rezystancji wejściowej w przybliżeniu 1 kΩ, by odpowiednio obciążyć filtry. Z uwagi na minimalne napięcie zasilania, raptem +5 V z płytki STM32F4DISCOVERY i duże sygnały zdecydowałem się na obciążenie indukcyjne L1 (zwarcie dla prądu stałego). Ostatecznie stopień WK zapewnia dopasowanie impedancji do przetwornika A/C. Dwa stopnie WE kompensują tłumienie filtrów i zapewniają dodatkowe wzmocnienie. Dzielnik R15-R16 ustala składową stałą dla przetwornika A/C. Wykonany odbiornik wygląda tak:
Zastosowanie trzech filtrów ceramicznych w torze p.cz. wynika z konieczności uzyskania odpowiedniej selektywności. Charakterystyka pojedynczego filtru jest w datasheet Muraty:
Można to przekształcić na dane tabelaryczne (program Engauge Digitizer) i pobawić się tym w Excelu:
Oczywiście czym więcej filtrów połączonych kaskadowo (charakterystyki k1, k2, k3), tym większe nachylenie zboczy charakterystyki, węższe pasmo (z tego samego powodu) i większe tłumienie dla częstotliwości środkowej. Daje się uzyskać B jakieś 400 kHz przy tłumieniu -60 dB. Coś takiego sobie założyłem względem skąpych (jak na ten projekt) możliwości STM32 i obliczeń, jakie zrobiłem dla tego przetwornika A/C. Cały wzmacniacz ma następującą charakterystykę (wyniki pomiarów) dla Ui=250 mVpp:
Uzyskuje się wzmocnienie ok. 17 dB, co przy ok. 85 dB z R820T2 wydaje się mieć sens. Jednak R820T z pewnością tego nie osiąga.
Kiedy już miałem blok analogowy należało dobrać częstotliwość próbkowania dla próbkowania pasmowego. Tutaj można próbkować sygnał pasmowy z częstotliwością mniejszą, niż to wynika z twierdzenia o próbkowaniu. Unika się wciąż aliasingu, ale inna będzie częstotliwość środkowa po próbkowaniu (dochodzą też oczywiście powielenia widmowe). Obliczenia są przedstawione poniżej:
Wybrałem m=10. Z uwagi na rozdzielczość nastawy częstotliwości próbkowania (timer w STM32 do wyzwalania A/C pracującego z DMA, buforem kołowym i dwoma callback) fs=857,1 kHz. Musiałem ustawić bardzo krótkie próbkowanie w STM32, by to było możliwe. Nie jest to więc dokładnie, jak być powinno. Ponadto trzeba brać pod uwagę, że tor analogowy przetwornika A/C musi przenieść całe pasmo sygnału pasmowego tj. ponad 4,5 MHz. Z uwagi na próbkowanie pasmowe uzyskuje się nową częstotliwość środkową dla sygnału dyskretnego. Tutaj będzie to 260 kHz.
Odnośnie samego R820T wykorzystałem kod Erica Brombaugh dostępny na Githubie. Wstępnie przeportowałem ten kod do STM32F407 i HAL dla STM32 oraz STM32IDE. Następnie zmodyfikowałem oryginalny kod i trochę go ulepszyłem wg. własnych upodobań i potrzeb. Tutaj pojawiły się pierwsze problemy. Okazuje się, że R820T nie może pracować na tej samej szynie I2C, co DAC audio CS43L22 z płytki STM32F4DISCOVERY. Jest konflikt adresów I2C, ponieważ CS43L22 daje też ACK dla adresu na który nie powinien reagować. Jest to skrajnie dziwna, ale prawdziwa sytuacja. Opisałem to na forum STM32: Link.
Wstępnie jest skalowanie próbek z A/C. Pozbywam się więc składowej stałej i mam mniejsze wartości, co zmniejsza ryzyko jakichś przepełnień przy obliczeniach zmiennoprzecinkowych. Najpierw przetwarzane są 4 pierwsze próbki w zdarzeniu HAL_ADC_ConvHalfCpltCallback, potem 4 kolejne w HAL_ADC_ConvCpltCallback. W tym przypadku nie da się zastosować ciągów mieszających przy mieszaniu cyfrowym z uwagi na zastosowaną częstotliwość próbkowania. Trzeba więc zrobić odpowiedni NCO w taki sposób, by zachować ciągłość fazy przy zerowaniu licznika tablicy "lookup table". To nie jest takie oczywiste, jak się wydaje. Uzyskałem jednak pomoc na StackExchange: Link.
Filtry cyfrowe są o nieskończonej odpowiedzi impulsowej (IIR). Jest to bardzo kontrowersyjne. Normalnie stosuje się w takich przypadkach FIR wysokiego rzędu, ale nie mam takich możliwości obliczeniowych. Stąd szybkie w obliczeniu IIR o mniejszym rzędzie, ale niezłej selektywności w porównaniu do FIR o tym samym rzędzie. Wada to nieliniowa charakterystyka fazowa i przez to niestałe opóźnienie grupowe. Mimo tego jakoś to działa. Zastosowałem filtry Czebyszewa 5-tego rzędu (trochę za mało). Jest więc jeszcze gorzej z fazą. Jakoś to działa, bo wykorzystywana jest tylko część charakterystyki, dalej jest już duże tłumienie. Filtry początkowo projektowałem wg. własnego skryptu w Matlabie. Wykorzystałem metodę transformacji biliniowej i oczywiście odpowiedni prototyp analogowy filtru. Ten był już projektowany (właściwie jego transmitancja), jak dla filtrów analogowych. Potem jednak z tego zrezygnowałem, bo metoda optymalizacyjna jaką ma FDATOOL w Matlabie (obecnie filter designer) jest dużo lepsza. Niestety dla uproszczeń programu w STM32 zrobiłem filtry jednosekcyjne, jako "direct form II". Jest to więc robione w taki sposób:
Do STM32IDE trzeba tylko odpowiednio przekopiować współczynniki filtru. Mam do tego zrobiony prosty skrypt w Matlabie. Po filtrach można (i na tak słabym mikrokontrolerze trzeba) zrobić decymację tj. obniżenie częstotliwości próbkowania. Nic się na tym nie traci, zaś wszystko jest dobrane tak, by nie było aliasingu. Generalnie przyjęte 105 kHz częstotliwości granicznej to nie jest B/2 (B=400 kHz), ale nie dałem rady na tym mikrokontrolerze zrobić nic więcej - fs=214,3 kHz to i tak bardzo dużo do obliczeń w czasie rzeczywistym z STM32F407. Dobrze, że jest tu całkiem sensowne FPU z przydatnymi instrukcjami dla DSP.
Dalej dla dyskretnych sygnałów i(n) i q(n) można już robić demodulatory. Postanowiłem zrobić FM (szerokopasmowe), AM, CW (bardzo proste z komparatorem) i testowe wystawianie na przetworniki C/A bezpośrednio i(n) i q(n). Dla FM przydatna jest ta praca (znakomity materiał):
Jest to zrobione w ten sposób dla sygnału ciągłego:
Zaś po dyskretyzacji:
Obliczam jednak inaczej pochodne dla i(n) i q(n) z uwagi na szumy. Wykazuje się, że potem jeszcze trzeba zastosować arcus sinus:
Bez tego działa to nieliniowo, ale jeżeli interesuje nas np. odbiór FSK (lub jakiejś jego wersji), to można to sobie odpuścić. Tutaj musiałem zrobić to z kolejną tablicą "lookup table". Niestety nie miałem możliwości zrobienia normowania względem modułu. Ostatecznie jest filtr dolnoprzepustowy, by usunąć powstałe szumy - też niestety IIR. Przyjąłem częstotliwość graniczną 10 kHz. Słychać nieco zniekształcenia, ale sam dyskryminator jest bardzo liniowy. Nie pracuje dla pełnej dewiacji 75 kHz, ale wystarczy to do odbioru monofonicznego. Ostatecznie próbki m.cz. trafiają już bez decymacji i po skalowaniu do C/A i ostatecznie na CS43L22. Dla AM stosuję jednak znacznie węższe filtry dolnoprzepustowe dla i(n) i q(n).
Z pomocą generatora w.cz. i generatora funkcji oraz oscyloskopu zbadałem ten dyskryminator.
Zastosowałem modulację sygnałem zewnętrznym 239 Hz. Generator G4-116 ma tą świetną cechę, że zachowuje nastawioną dewiację dla FM i głębokość modulacji dla AM przy podaniu sygnału zewnętrznego. Sam się dostraja do wartości maksymalnej tego sygnału. Generator w.cz. był dołączony do wejścia głowicy i ustawiony na 100 MHz. Podałem 540 µV (wartość skuteczna). Odbiornik dostroiłem do 100 MHz. Kolejno na X oscyloskopu dołączony był sygnał modulujący (do niego proporcjonalna jest chwilowa wartość częstotliwości), zaś do wejścia Y sygnał z C/A w STM32. Bardzo dobrze to wyszło. By uzyskać pełną charakterystykę (również część nieliniowa) ustawiłem dewiację 90 kHz. Liniowo pracuje dla 45 kHz. Wyszło coś takiego:
Nie jest idealnie, ale musiałem przyjąć 1000 kompromisów robiąc ten odbiornik z uwagi na słaby STM32. Można się już pobawić z mniejszą dewiacją (45 kHz) i obserwacją przebiegu (tj. zależnego od czasu). Poprawne dostrojenie generatora do 100 MHz:
Odstrojenie "w górę":
Odstrojenie "w dół":
Przy dewiacji 90 kHz przebieg na wyjściu C/A będzie mocno zniekształcony zgodnie z charakterystyką statyczną, jaka była wcześniej:
Co udało się odebrać? Oczywiście stacje z zakresu 88-108 MHz. Nie jest to Hi-Fi, ale jest nieźle, jak na tak prymitywny odbiornik i program. Poniżej nagrania dla stacji UKF na 93,3 MHz:
Tutaj na 94,8 MHz:
Odbiór w Grudziądzu na pokojowej antenie telewizyjnej ze wzmacniaczem szerokopasmowym. Można też sobie posłuchać terkotu FSK na 433,92 MHz, ale słychać też czasem GFSK:
W Grudziądzu udało mi się też usyłszeć coś z analogowych łączności amatorskich w paśmie 2 m i 70 cm z jakichś przemienników. Jednak ten dyskryminator słabo się nadaje do wąskopasmowej modulacji. Generalnie słychać sporo transmisji cyfrowych - również w pasmach amatorskich.
Odnośnie AM wystarczy obliczyć moduł. Ten po filtrze (znowu IIR) i odpowiednim skalowaniu można już wykorzystać jako sygnał m.cz. Dalej analogicznie jak w FM. Nie mam ARW (AGC), ale jest automatyczne skalowanie względem wartości modułu, więc jest jakaś namiastka cyfrowa. Wychodzi coś takiego:
Górny sygnał to m.cz. na wyjściu C/A w STM32, dolny zaś to sygnał p.cz. na wejściu przetwornika. Głębokość 90%. Na wejściu głowicy 288 µV (wartość skuteczna). Częstotliwość nośna 100 MHz. Tu pojawia się problem, bo R820T pracuje od ok. 45 MHz. Faktycznie PLL chwyta od ok. 26 MHz, ale wzmocnienie jest małe. Można ewentualnie posłuchać pasma lotniczego. Jednak nic nie udało mi się odebrać oprócz jakichś transmisji cyfrowych. Zrobiłem więc skrajnie uproszczony blok dodatkowej przemiany (tzw. up converter) na mieszaczu szerokopasmowym podwójnie zrównoważonym.
Zrezygnowałem tu z przedwzmacniacza i jakichkolwiek filtrów na wejściu. Zastosowałem generatora przebiegu prostokątnego jako heterodynę. Wszystko jest więc przesunięte o 50 MHz. Odpuściłem nawet zasilanie przez złącze RF. Zmontowany moduł wygląda tak:
Po dołączeniu do odbiornika:
Coś tam można odebrać na KF. Kilka stacji:
7,30 MHz
11,64 MHz
11,97 MHz
13,70 MHz
17,65 MHz
Z łączności amatorskich słychać było przez chwilę CW i różne transmisje cyfrowe, radary pozahoryzontalne itd. Odbiór był na ok. trzech metrach przewodu rozwieszonego po pokoju.
Edukacyjnie można też wrócić do początku tego opisu. Mowa była o sygnałach i(t) i q(t). Można je wystawić na wyjścia C/A w STM32. Poniżej przebiegi:
Górny przebieg to i(t), dolny z kolei q(t). Jak widać jest szum, ale to efekt niedostatecznego tłumienia filtrów dolnoprzepustowych. Pomiary były robione znowu dla 100 MHz i 288 µV (wartość skuteczna) na wejściu głowicy. Warto zobaczyć to w trybie XY. Jest to bardzo ciekawe. Teoretycznie byłby stacjonarny punkt, jak na początkowym rysunku. Jednak z uwagi na szumy fazowe tak nie będzie. Więc punkt ten będzie się poruszał w kierunku zależnym od znaku odstrojenia częstotliwości i z prędkością zależną od wartości odstrojenia. Nagrałem więc z tego film:
Podczas nagrywania filmu starałem się dostrajać generator. Widać więc chwilowe niemal zatrzymanie.
Odbiornik sterowany jest przez konsolę UART i terminal RealTerm. Dostępnych jest trochę poleceń do nastawy częstotliwości, demodulatora itd.:
Spoiler:
help - this message
read <addr> - read reg (R820T2)
write <addr> <data> - write reg, data (R820T2)
dump - dump all regs (R820T2)
freq <frequency> - Set freq in MHz
lna_gain <gain> - Set gain of LNA [0 - 15]
mixer_gain <gain> - Set gain Mixer [0 - 15]
vga_gain <gain> - Set gain VGA [1 - 49.3 dB]
lna_agc_ena <state> - Enable LNA AGC [0 / 1]
mixer_agc_ena <state> - Enable Mixer AGC [0 / 1]
bandwidth <bw> - Set IF bandwidth [1 - 15]
init - default SDR state
volume <vol> - audio volume for CS43L22 [0 - 100]
mute - muting of CS43L22
unmute - unmuting of CS43L22
demod_type <type> <CW upper lvl> <CW hyst> - Set demodulator type [AM/FM/IQ/CW]
tune <start_freq> <step> - Manual tune from start_freq [MHz] with step [MHz]
scan <start_freq> <step> <mod_thres> <Mute> - Scan from start_freq [MHz] with step [MHz], mod_thres [dB] and Mute [0/1]
read <addr> - read reg (R820T2)
write <addr> <data> - write reg, data (R820T2)
dump - dump all regs (R820T2)
freq <frequency> - Set freq in MHz
lna_gain <gain> - Set gain of LNA [0 - 15]
mixer_gain <gain> - Set gain Mixer [0 - 15]
vga_gain <gain> - Set gain VGA [1 - 49.3 dB]
lna_agc_ena <state> - Enable LNA AGC [0 / 1]
mixer_agc_ena <state> - Enable Mixer AGC [0 / 1]
bandwidth <bw> - Set IF bandwidth [1 - 15]
init - default SDR state
volume <vol> - audio volume for CS43L22 [0 - 100]
mute - muting of CS43L22
unmute - unmuting of CS43L22
demod_type <type> <CW upper lvl> <CW hyst> - Set demodulator type [AM/FM/IQ/CW]
tune <start_freq> <step> - Manual tune from start_freq [MHz] with step [MHz]
scan <start_freq> <step> <mod_thres> <Mute> - Scan from start_freq [MHz] with step [MHz], mod_thres [dB] and Mute [0/1]
Po uruchomieniu w konsoli jest coś takiego:
Można np. ustawić demodulację FM i potem częstotliwość:
Ułatwieniem jest komenda "tune", czyli ręczne dostrajanie:
Tutaj strojenie jest od 100 MHz z krokiem 0,1 MHz. Podawany jest też powiedzmy, że uśredniony moduł na wejściu przetwornika. Nie uwzględniałem tu wzmocnień całego toru.
Jest też wyszukiwanie względem modułu tj. komenda "scan":
Tutaj jest od 100 MHz z krokiem 0,1 MHz, progiem -22 dB i bez wyciszenia CS43L22.
W mojej wersji wzmocnienie dla toru p.cz. w R820T2 nastawiam przez wyprowadzenie IF AGC.
Stąd mam do tego PWM z STM32 i filtr dolnoprzepustowy. Można w ten sposób uzyskać lepszą rozdzielczość nastawy wzmocnienia. Byłoby to też wygodniejsze do zrobienia ARW (jak dałoby się to zdążyć zrobić mając tak niewiele taktów zegara STM32). Jest więc do tego komenda:
Kilka innych komend:
Generalnie założony cel został osiągnięty. Powstał prosty i edukacyjny SDR. Z pewnością nie jest to konkurencja dla RTL-SDR.
Projekt zamieściłem też w repozytorium na Githubie: R820T2-and-STM32F407-SDR-receiver.
Mam nadzieję, że nie przesadziłem z obszernością opisu. Może więc komuś udało się dotrzeć do tego miejsca. W razie pojawienia się zainteresowania chętnie odpowiem na wszelkie pytania.
Fajne? Ranking DIY