Het grote idee
De server staat thuis, vlak naast de radio en Thetis. De client (op je laptop, pc of Android-telefoon) kan overal staan — in de woonkamer, op kantoor, aan de andere kant van de wereld. Tussen die twee loopt één dunne netwerkverbinding. Daarover moet alles: het geluid van de ontvanger, het spectrum- en watervalbeeld, de signaalsterkte, én elke knop die je indrukt.
De grote uitdaging is vertraging (latency). Als je de zendknop indrukt of aan de afstemknop draait, moet dat nu gebeuren — niet een halve seconde later. Daarom is heel ThetisLink gebouwd rond één principe:
Dit document gaat over het rechterstuk: de TL2-server → het netwerk → de client. Hoe het geluid ontstaat (de demodulatie, de FFT-channelizer) staat in het zusterdocument “Hoe een VRX werkt”.
Inhoud
1. Twee soorten verkeer
Alles wat over de lijn gaat valt in twee groepen, en die hebben heel verschillende eisen:
- Continue stromen — het geluid, het spectrum, de S-meter. Die komen tientallen keren per seconde, eindeloos. Hier telt: op tijd en vloeiend. Eén pakketje dat te laat komt is waardeloos: het geluid van 100 ms geleden wil je niet meer horen, dan ben je liever verder.
- Losse opdrachten — een frequentie zetten, een mode kiezen, het volume schuiven, de zendknop. Die komen alleen als jij iets doet. Hier telt: meteen reageren.
Beide groepen reizen over dezelfde verbinding, maar de manier waarop bepaalt of het systeem snel en soepel aanvoelt. Dat brengt ons bij de belangrijkste keuze.
2. Waarom UDP en geen TCP
Er zijn twee manieren om data over internet te sturen. Een vergelijking met de post helpt:
- TCP is als aangetekende post: elk pakket wordt bevestigd, en raakt er één kwijt, dan wordt die opnieuw gestuurd en wacht alles erachter tot hij binnen is. Gegarandeerd compleet — maar één hapering laat de hele stroom stokken.
- UDP is als gewone briefkaarten: je gooit ze op de bus en ze komen (meestal) aan, maar er is geen bevestiging en geen herzending. Raakt er één kwijt, dan is hij gewoon weg — de rest reist ongehinderd door.
Voor live audio is dat tweede precies wat je wilt. Een verloren pakketje van 20 ms hoor je hooguit als een minuscuul klikje; daar wachten zou veel erger zijn. ThetisLink gebruikt daarom UDP — voor álle verkeer, audio én opdrachten.
3. De server vinden (auto-discovery)
Voordat er iets kan reizen, moet de client weten waar de server staat. Dat kan op twee manieren:
- Automatisch (mDNS). De server roept op het lokale netwerk om: “hier ben ik, ik ben een ThetisLink-server.” De client luistert en toont hem in een lijstje — je hoeft geen IP-adres te kennen. Hetzelfde mechanisme als waarmee je telefoon een printer of Chromecast vindt.
- Handmatig. Zit de client in een ander netwerk (via VPN, of over internet), dan werkt dat omroepen niet — daar tik je het IP-adres en de poort gewoon zelf in.
mDNS
(service-type _thetislink._udp); de server zet zijn versie en een vriendelijke naam in
de aankondiging. Buiten het lokale netwerk: handmatig adres en poort 4580.4. Eén pakket, één envelop
Omdat alles over dezelfde socket reist, moet de ontvanger van elk pakketje meteen kunnen zien wat het is. Daarom begint elk pakket met een korte, vaste kop van 4 bytes — als het adres op een envelop:
Het type is de kern: daaraan ziet de ontvanger of dit een stukje RX1-audio is, een spectrumrij, een nieuwe frequentie, een S-meterstand of een druk op de zendknop. Zo kan één socket tientallen verschillende soorten berichten door elkaar dragen zonder dat ze verward raken.
5. Audio: Opus over UDP
Het gedemoduleerde geluid (uit de VRX-keten, of rechtstreeks van Thetis of een Yaesu) is een stroom audiomonsters. Die ongecomprimeerd versturen zou onnodig veel bandbreedte kosten. Daarom wordt het eerst gecomprimeerd met de Opus-codec — een moderne, zeer efficiënte audiocompressie die speciaal goed is in lage vertraging.
Het geluid wordt in hapjes van 20 ms geknipt; elk hapje wordt apart als Opus-blokje gecodeerd en in één UDP-pakket gestopt. Naast de Opus-bytes zelf zitten in dat pakket twee belangrijke getallen:
- een volgnummer (sequence) — pakketje 1, 2, 3, … zodat de client de juiste volgorde kent;
- een tijdstempel — wanneer dit hapje hoort te spelen.
Smal of breed — dezelfde keuze als in de VRX-keten
Net als bij de iFFT-keuze (zie het VRX-document) kan de audio smalband of breedband zijn:
- Smalband: 8 kHz audio, ~12,8 kbit/s — prima voor spraak, heel weinig data.
- Breedband: 16 kHz audio, ~20–24 kbit/s (afhankelijk van de stroom) — duidelijk helderder, ongeveer het dubbele aan data.
Welke van de twee bepaalt de client met één schakelaar; de server codeert daarop. (Op de TX-kant — als je zelf zendt — wordt altijd breedband gebruikt voor de beste microfoonkwaliteit.)
6. Veel geluidsstromen over één lijn
ThetisLink kan meer dan één ding tegelijk laten horen: de hoofdontvanger (RX1), de tweede ontvanger (RX2/VFO-B), de virtuele ontvangers VRX1 en VRX2, en het geluid van een aangesloten Yaesu-radio. Al die stromen reizen door dezelfde socket. Hoe blijven ze uit elkaar?
Heel simpel: elk soort stroom heeft zijn eigen pakkettype (en elk een eigen volgnummer-teller). De client ziet aan het type-label meteen in welk audiokanaal een pakketje thuishoort en stuurt het naar de juiste afspeel-buffer.
Daarnaast bestaat er een “gebundelde” variant die meerdere kanalen in één pakket stopt met hetzelfde volgnummer en tijdstempel — handig als kanalen exact gelijk moeten lopen (bijvoorbeeld een stereo links/rechts-beeld).
7. De jitter-buffer
Pakketjes reizen niet allemaal even snel over het netwerk. De één doet er 8 ms over, de volgende 21 ms, soms komt er één vóór zijn voorganger aan. Die wisselende vertraging heet jitter. Zou je elk pakketje meteen afspelen zodra het binnenkomt, dan klonk het hakkelig en door elkaar.
De oplossing is een kleine jitter-buffer aan de clientkant: een wachtkamertje van een handvol pakketjes. Binnenkomende pakketten worden daar op volgnummer op de juiste plek gelegd; het afspelen loopt er met een vaste, rustige tik doorheen. Zo wordt een hobbelige aankomst een vloeiende weergave.
Hier zit precies de afweging uit het begin: een diepere buffer is gladder maar trager; een ondiepe buffer is sneller maar gevoeliger voor uitval. ThetisLink houdt de buffer daarom zo klein mogelijk en past de diepte automatisch aan: meet hij weinig jitter, dan krimpt de buffer (minder vertraging); wordt het netwerk grillig, dan groeit hij net genoeg om haperingen te voorkomen.
8. Het spectrum over de lijn
Het spectrum- en watervalbeeld is een rij bins (zie het VRX-document): per bin een waarde die zegt hoeveel energie daar zit. Zo'n rij kan groot zijn — duizenden tot tienduizenden bins. Die elke keer volledig en op volle precisie sturen zou veel bandbreedte kosten, dus hier speelt de tweede prioriteit: bandbreedte.
Twee knoppen houden dat in toom:
- Hoeveel bytes per bin. De huidige server stuurt 1 byte per bin (256 niveaus) — ruim genoeg voor een vloeiend beeld. Het protocol kan ook 2 bytes per bin beschrijven voor meer dynamiek, maar dat is voor de toekomst gereserveerd.
- Hoe vaak per seconde. Het beeld wordt standaard zo'n 15 keer per seconde ververst; dat is instelbaar (rustiger = minder data, sneller = vloeiender).
In elk spectrumpakket zitten naast de bins ook de middenfrequentie, de breedte (span) en een referentieniveau, zodat de client het beeld op de juiste plek en schaal kan tekenen. En net als bij audio heeft elke bron — RX1, RX2, VRX1, VRX2 — zijn eigen pakkettype.
9. De S-meter
De signaalsterkte komt langs drie verschillende wegen binnen, afhankelijk van de bron:
- Hoofdontvanger (RX1/RX2): de server stuurt een kant-en-klare waarde — het vermogen in dBm — in een compact pakketje van een paar bytes. Afhankelijk van de gekozen meterbron is dat een gemiddelde of piek uit Thetis, of een uit de spectrum-bins afgeleide waarde.
- VRX-kanalen: hiervoor stuurt de server geen aparte S-meter. De client rekent de sterkte zelf uit het ontvangen spectrum: hij telt het vermogen van de bins binnen de doorlaatband op en zet dat met een vaste ijk-correctie om naar dBm (zie het VRX-document, hoofdstuk over de S-meter).
- Yaesu-radio: de meterwaarde zit ingebakken in de statusberichten van de radio en komt rechtstreeks via CAT uit het toestel (een eigen schaal, geen dBm).
De schaal (de vertrouwde S-units: S9 = −73 dBm, elke S-unit 6 dB) wordt altijd in de client getekend. De server levert het rauwe getal; de client maakt er de naald van.
10. Instellingen en knoppen
Draai je aan de afstemknop of kies je een mode, dan stuurt de client een klein opdracht-pakketje: meestal niet meer dan een identificatie (“dit is het volume”, “dit is de mode”, “dit is de filterbreedte”) plus een waarde. Frequentie en mode hebben zelfs hun eigen pakkettype. Dat is alles — een paar bytes, meteen verstuurd.
Zoals in hoofdstuk 2 al bleek: er komt geen aparte bevestiging terug. In plaats daarvan meldt de server voortdurend de actuele stand terug. Die teruggemelde waarde is je bevestiging: zie je de frequentie meeveranderen, dan is de opdracht aangekomen. Zo blijft de client altijd in sync met wat de radio écht doet, ook als iemand aan de radio zelf draait.
11. De zendknop (PTT)
De zendknop is het meest tijdkritische signaal van allemaal — hier telt elke milliseconde. Daarom is er geen apart, traag knopbericht. In plaats daarvan zet de client een vlag in de TX-audiopakketten: zodra je de knop indrukt, dragen je uitgaande audiopakketjes het PTT-vlaggetje, en de server schakelt de radio naar zenden. Het indrukken en je spraak reizen zo in één en dezelfde stroom mee — niets dat op elkaar hoeft te wachten.
Omdat meerdere clients tegelijk verbonden kunnen zijn, bewaakt de server dat er maar één tegelijk zendt. Wil een tweede client zenden terwijl de eerste al bezig is, dan krijgt die een kort “geweigerd”-bericht terug in plaats van dat ze door elkaar gaan praten.
Veiligheid: de zender blijft niet ongewild aan staan
Bij remote bediening is dit cruciaal: een zender die per ongeluk blijft “hangen” (blijft zenden) is gevaarlijk — voor je apparatuur, voor de band en voor je vergunning. Daarom is de zendknop bewust geen aan/uit-grendel, maar een vlag die de client zo'n 50× per seconde opnieuw meestuurt en bevestigt. De zender blijft dus alléén aan zolang die stroom van “PTT-aan”-pakketjes blijft binnenkomen.
Valt de verbinding weg terwijl je zendt — wifi hapert, de laptop valt in slaap, de app crasht — dan stopt die stroom vanzelf, en grijpen twee onafhankelijke vangnetten in:
- Pakket-timeout (~0,5 s): komen er een halve seconde lang geen pakketten meer binnen, dan laat de server de zender automatisch los.
- Heartbeat-timeout (~2 s): blijft ook de “hartslag” die de verbinding levend houdt langer dan twee seconden weg, dan beschouwt de server de verbinding als verbroken — PTT los, plus een alarm.
Het ergste wat een netwerkstoring dus kan doen, is je zender binnen een fractie van een seconde tot hooguit twee seconden terugzetten naar ontvangst — nooit eindeloos laten doorzenden. Dat is het klassieke dodemansknop-principe (dead-man's switch).
12. Verbinden en beveiligen
Bij het verbinden wisselen client en server eerst kort uit wat ze allebei kunnen. De client vertelt welke mogelijkheden hij ondersteunt (breedband-audio, spectrum, een tweede ontvanger…), de server antwoordt met de doorsnede: alleen wat ze allebei aankunnen wordt gebruikt. Zo zet een nieuwere kant zijn extra's vanzelf uit tegenover een oudere — binnen dezelfde protocolversie. Een heel andere protocolversie wordt wél geweigerd; server en clients moeten dan samen worden bijgewerkt.
Staat de server open op internet, dan kun je een wachtwoord instellen. Het wachtwoord zelf gaat nooit over de lijn: de server stuurt een willekeurige “uitdaging”, de client beantwoordt die met een berekend antwoord op basis van het wachtwoord (HMAC), en optioneel komt er nog een tijdscode (TOTP, zoals een authenticator-app) overheen.
13. Het latency-budget
Tel je alles bij elkaar op, dan zie je waar de vertraging tussen “geluid bij de radio” en “geluid in je koptelefoon” vandaan komt:
Dit is het netwerk- en codecbudget, vanaf het moment dat de audio klaarstaat om te verzenden. Voor VRX-audio komt daar vóór de Opus-codering nog de channelizer-blokvertraging bij (~16 ms, zie het VRX-document); voor de Yaesu-radio's spelen de USB-CODEC en CAT mee.
Dit is waarom de keuzes in dit document zijn zoals ze zijn: UDP in plaats van TCP, korte Opus-frames, een minimale adaptieve buffer, opdrachten zonder trage bevestiging, en de PTT-vlag mee in de audiostroom. Stuk voor stuk dezelfde regel: haal de vertraging weg die je kúnt weghalen.
Tot slot — alles samen
De server pakt het geluid (gecomprimeerd met Opus, in hapjes van 20 ms), het spectrum (bins, compact gecodeerd) en de S-meter in genummerde pakketjes, en stuurt die over één UDP-verbinding op poort 4580 naar de client. Jouw bedieningen reizen als kleine opdracht-pakketjes terug; de server meldt de stand voortdurend, zodat client en radio in sync blijven. Een minimale jitter-buffer maakt van de hobbelige aankomst weer vloeiend geluid, en overal staat hetzelfde voorop: zo min mogelijk vertraging.
Samen met het zusterdocument “Hoe een VRX werkt” heb je daarmee de hele keten te pakken: van radiogolf, via demodulatie, tot het geluid en beeld op je scherm — waar je ook bent.