RTL-SDR dongel · üks antenn aknal · kõik, mis eetris liigub

2026-09-18 FM 101.6

Kõrvalmissioon: raadio, mis lõikab ise lood välja

#rds #radiotext #salvestamine #python #stereo #tooriistad

Eelmises RDS-loos tuli välja, et Raadio 2 RadioText ütleb loo nime: SEMISONIC – Closing time. Sealt oli üks samm mõtteni, mis ei lasknud rahu: kui arvuti teab, mis lugu mängib ja millal see vahetub, siis ta võib raadiot pidevalt salvestada ja iga loo eraldi faili panna. Nagu kassettmakk 1993, aga õigete nimedega.

Nüüd ta teeb seda. See on kõrvalmissioon – põhimissioon on ikka eetri avastamine – aga alati, kui dongel parasjagu midagi muud ei tee, kuulab ta Raadio 2 ja lõikab.

Kuidas see töötab

SDR++-il on moodul IQ Exporter: ta saadab toore IQ-voo (2,4 MSPS, int16) TCP-porti. Minu Python-skript music_recorder.py loeb seda voogu 5-sekundiliste plokkidena ja teeb iga plokiga:

  1. Kanali väljalõikamine – FFT-ga 2,4 MHz → 240 kHz Raadio 2 ümber (sama trikk mis iq.py-s).
  2. FM-demod ja seekord stereo: L+R on 0–15 kHz, L−R istub 38 kHz alamkandjal, mille faasi annab 19 kHz pilot ruutu tõstetuna. L = (L+R) + (L−R), de-emphasis 50 µs, 48 kHz.
  3. RDS viimase 10 s pealt – sama dekooder, sama hääletus.
  4. Kui RadioText muutub (ja püsib kaks korda järjest sama, et üksik viga ei lõikaks), siis eelmine fail kinni, uus lahti: 2026-09-18 19-05 SEMISONIC - Closing time.wav.

Üks detail, mis alguses valesti oli: RadioText muutub loo algusest 10–15 sekundit hiljem. Nii läksid iga loo esimesed sekundid eelmise loo faili. Lahendus: heli kirjutatakse kettale 15 s viivitusega. Loo vahetusel läheb puhvris olev 15 s uude faili, mitte vanasse.

Ja teine: skript küsib iga 3 s rigctl-ilt, kuhu SDR++ on tuunitud. Kui ma lähen lennusidet kuulama, pole 101.6 enam aknas – siis paneb ta pooleli oleva loo kinni ja ootab. Tulen tagasi, jätkab ise.

Kaks lõksu, mis maksid tund aega

FFT pikkus. FM-diskriminaator (angle(x[n]·conj(x[n−1]))) annab n−1 sämplit. 5 s plokk on 1 200 000 sämplit, miinus üks on 1 199 999 – ja sellel arvul on suur algarvuline tegur. Numpy FFT läheb siis Bluesteini algoritmile ja on 10× aeglasem. Kogu RDS-dekooder võttis 7 s, kui ta oleks pidanud võtma 1. Parandus: üks sämpel algusesse juurde, pikkus tagasi 1 200 000 = 2⁷·3·5⁵. Kaks korda kiirem kui reaalaeg.

Baitide liitmine. TCP-lugeja tegi pending += chunk. Pythoni bytes on muutumatu, seega iga liitmine kopeerib kogu senise puhvri – 48 MB ploki puhul umbes gigabait memcpy-d ploki kohta, ja kõik see GIL-i all, nii et numpy jäi seisma. Reaalajas 9,8 s ploki kohta, offline 3,2. Parandus: recv_into otse eelvalmistatud bytearray-sse. Nüüd ~3 s.

Mis kaustas on

music\Raadio 2\
  2026-09-18 18-38 Koit Raudsepp.wav
  2026-09-18 18-52 ...
  songs.csv          <- algus, kestus, jaam, radiotext, fail
  _short\            <- alla 45 s (reklaamipausid, jingle'id)

WAV, 48 kHz, stereo, umbes 11 MB minutis. MP3 pole, sest ühtegi allkirjastatud kodeerijat mul Windowsi turvapoliitika all käivitada ei õnnestu – ja ausalt, ketast on.

Lugusid ma siia ei pane, need on jaama ja artistide omad. Aga songs.csv on omaette huvitav: sealt tuleb varsti välja, mida Raadio 2 tegelikult mängib – kui palju kordub, mis kell mis stiil, kui pikad on lood. Sellest saab eraldi postituse, kui nädal andmeid koos on.

Skriptid: music.ps1 (start/stop/status), music_recorder.py, fix_wav.pyTarkvara.

73, Tõnu

Kommentaarid

Laen…