Im zweiten Teil ging es um Kontrollframes, Datenframes und die Geschwindigkeitsstufen. Hier geht es darum, wie zwei Stationen diese Bausteine benutzen. Grundlage ist wieder der Quellcode von xara 0.1.3, vor allem die Crates xara-arq und xara-host.
Verbindungsaufbau auf HF
Der Ablauf, wenn DC8WAN eine Station N1CALL bei 2300 Hz anruft:
Anrufer (DC8WAN) Angerufener (N1CALL)
---------------- --------------------
Connect-Request (Code 26, F0) ------->
misst SNR, wählt Stufe q (max. 4)
<------- Connect-Reply (Code 15..18, F1)
Header-Datenframe auf Stufe q ------->
(Rufzeichen, Flags, Angebot n)
Header ok?
<------- BREAK (49) oder IDLE (50)
Startstufe n (Code 50+n) ------->
<------- erste Daten vom Angerufenen
Der Connect-Request ist eine Tonfolge aus dem Codebuch, die aus dem Rufzeichen der Gegenstation berechnet wird. Beim ersten Versuch bekommt er einen um 9 Symbole verlängerten Vorlauf, bei Wiederholungen nicht mehr.
Der Angerufene misst das SNR des Requests und wählt daraus eine Startstufe zwischen 1 und 4, mit 6 dB Sicherheitsabstand. Diese Stufe steckt in der Nummer des Antwortcodes. Der Anrufer schickt darauf seinen Header als ersten echten Datenframe auf genau dieser Stufe. Der Header enthält sein Rufzeichen, ob er registriert ist, welche Kompression er möchte und welche Stufe er für die Rückrichtung anbietet.
Kommt der Header mit falscher CRC an, antwortet der Angerufene mit NACK und eine Stufe tiefer. Ist das Gültig-Flag nicht gesetzt, kommt REJECT (43). Sonst ist die Verbindung aufgebaut. Der Angerufene antwortet normalerweise mit BREAK und übernimmt damit das Sendrecht. Im Chat-Modus antwortet er mit IDLE.
Klappt gar nichts, wiederholt der Anrufer den Request. Ohne Header nach 30 Sekunden gibt der Angerufene auf und meldet dem Client CANCELPENDING. Während einer bestehenden Verbindung liegt der Watchdog bei 60 Sekunden.
Ein Datendurchgang
Hat eine Station das Sendrecht, schickt sie einen Datenframe oder zwei direkt hintereinander. Zwei Frames gibt es erst, wenn die Stufe mehr als fünf Durchgänge lang stabil geblieben ist. Dann kommt eine Quittung auf zwei Frames.
Die Gegenstation antwortet mit einem Kontrollframe, der zugleich Quittung und Stufenwahl ist:
| Antwort | Bedeutung |
|---|---|
| ACK_STAY (1) | angekommen, Stufe bleibt |
| ACK_UP1 bis ACK_UP4 (7, 5, 8, 9) | angekommen, 1 bis 4 Stufen höher |
| ACK_DOWN (6) | angekommen, eine Stufe tiefer |
| ACK_FIRST_ONLY / ACK_SECOND_ONLY (11, 10) | von zwei Frames kam nur einer an |
| NACK (2), NACK_MISSED (19) | nicht angekommen |
Hört der Sender keine Quittung, fragt er mit ACK_REQ nach. Dafür gibt es zwei Codes (12 und 13), die sich abwechseln. So kann der Empfänger eine wiederholte Nachfrage von einer neuen unterscheiden, ähnlich dem Alternating-Bit-Verfahren.
Will der Empfänger selbst etwas senden, antwortet er mit BREAK. Der bisherige Sender gibt dann mit GO_AHEAD (14) oder mit einem Startstufen-Code ab. Haben beide nichts zu senden, wechseln sie in den Zustand IDLE und tauschen nur noch kurze IDLE- und NO_TRAFFIC-Frames aus. Beendet wird die Verbindung mit QRT, auf Wunsch gefolgt von einer CW-ID.
Wie die Stufe gewählt wird
Die Geschwindigkeitsanpassung steckt in xara-arq/src/hf/adapt.rs. Jede Stufe hat in der Tabelle eine SNR-Schwelle und einen PAPR-Wert. Daraus berechnet xara eine Schrittschwelle:
Schwelle = SNR-Schwelle - (9,4 dB - PAPR der Stufe)
Der Empfänger vergleicht das gemessene SNR des letzten Frames mit dieser Schwelle plus einem Sicherheitsabstand (Margin). Für jede mögliche Zielstufe gilt:
Reserve(d) = SNR - Schwelle(Stufe + d) - Margin - (d - 1)
Jede zusätzliche Stufe über die erste hinaus kostet also ein weiteres dB Reserve. Hochgeschaltet wird nur, wenn die Reserve positiv ist und der Füllstand im Steuerbyte des Senders zeigt, dass genug Daten warten. Für einen Sprung um vier Stufen muss der Füllstand auf dem Maximum 6 stehen, für eine Stufe reicht 3. Ist die Reserve für die aktuelle Stufe negativ, geht es eine Stufe herunter.
Der Margin selbst ist nicht fest:
- Er startet bei 2 dB und sinkt mit jedem guten Frame um 1/30 dB, bis auf mindestens 1,5 dB.
- Geht ein Frame verloren, steigt er um 1 dB. War es der erste Frame nach einem Hochschalten, kommen 4 dB pro bereits gescheitertem Versuch dazu. Kam der Verlust nach weniger als 15 guten Frames, gibt es bis zu 3 dB zusätzlich.
- Danach baut er sich über die nächsten 15 guten Frames wieder ab.
Nach einem gescheiterten Hochschalten sind Sprünge um zwei oder mehr Stufen gesperrt. Geht der allererste QAM-Frame verloren, versucht xara QAM vorerst nicht mehr. Einige Stufen sind in der Tabelle so markiert, dass sie beim Herunterschalten übersprungen werden.
Das gemessene SNR wird asymmetrisch geglättet: Steigt es, nimmt xara den Mittelwert aus altem und neuem Wert. Fällt es, gilt sofort der neue Wert. Das Verfahren reagiert also schnell auf Verschlechterungen und vorsichtig auf Verbesserungen.
Dasselbe Prinzip gilt für das Format der Kontrollframes. Liegt das SNR über der Schwelle des kurzen Formats F2 und sind im letzten Kontrollframe keine Töne verloren gegangen, wünscht sich der Empfänger F2. Sonst geht er auf F1 oder F0 zurück. Diese Wahl steht in den oberen zwei Bits des Steuerbytes jedes Datenframes.
Timing
Auf Kurzwelle ist das Timing kritisch: Jede Seite muss wissen, wann die Quittung frühestens kommen kann und wann sie als verloren gilt. xara misst deshalb für jede Stufe die tatsächliche Verzögerung zwischen Ende der eigenen Aussendung und Ende der Quittung und mittelt sie über die Verbindung. Stufen, auf denen noch nichts gemessen wurde, bekommen einen Schätzwert aus der Messung der Nachbarstufe plus einer festen Decodierzeit aus einer Tabelle. Für die höheren Stufen sind diese Werte größer, bei 2300 Hz steigen sie auf etwa das Dreieinhalbfache der unteren Stufen.
Winlink-Besonderheiten
xara liest auf dem Datenstrom mit, ob gerade das Winlink-Protokoll B2F läuft. Erkennt es eine Zeile FS ... (die Antwort auf angebotene Nachrichten), darf die Startstufe bei einer registrierten Verbindung für den folgenden Datenblock direkt bis an die QAM-Grenze gehen. Eine Zeile FQ zeigt das Ende der Sitzung an.
Außerdem hält der Angerufene das Sendrecht kurz fest, nachdem die Verbindung steht: bis zu 3,3 Sekunden, wenn vom Client noch nichts kam, und bis zu 4,5 Sekunden, wenn erst ein Teil der Daten da ist. Kommt in dieser Zeit ein Prompt vom Client, erkennbar an der Endung > plus Wagenrücklauf, sendet xara sofort.
Kompression
Zwei Verfahren gibt es, ausgehandelt im Header und nur bei registrierter Gegenstelle aktiv:
COMPRESSION TEXT nutzt einen Wörterbuch-Code mit variabler Länge und acht Zuständen. Die Tabelle liegt als textcomp.dict im Quellcode. Jeder Eintrag ordnet einer Zeichenfolge ein Bitmuster und einen Folgezustand zu, häufige Folgen bekommen kurze Muster. Bit 5 im Steuerbyte markiert komprimierte Frames.
COMPRESSION FILES sammelt die Daten 150 ms lang, schneidet sie in Blöcke bis 32500 Byte und komprimiert jeden mit Deflate (Stufe 6). Jeder Block bekommt zwei Byte Vorspann: Das oberste Bit sagt, ob komprimiert wurde, die restlichen 15 Bit enthalten die Länge. Wird ein Block durch Deflate nicht kleiner, geht er unkomprimiert raus.
KISS und CQ ohne Verbindung
Neben ARQ kann VARA einzelne Rahmen ohne Verbindung senden, angeliefert über den KISS-Port 8100. Dafür nutzt xara die Stufen des 500-Hz-Modus, unabhängig von der eingestellten Bandbreite. Der Kanalzugriff läuft über p-Persistenz mit einem Standardwert von 0,3 und einem Raster von 200 ms. Längere Rahmen werden in bis zu 18 Fragmente zerlegt.
Ein Sonderfall ist der CQ-Frame: 6 Byte Nutzdaten (Rufzeichen in 7 × 6 Bit, dazu Bandbreite und SSID) plus CRC-16, gesendet auf einer eigenen langsamen FSK-Stufe. Der Client bekommt beim Empfang eine Zeile wie CQFRAME N1CALL 2300.
Die Host-Schnittstelle
Winlink-Clients sprechen mit dem Modem über zwei TCP-Verbindungen: Kommandos und Statusmeldungen auf Port 8300, Nutzdaten auf Port 8301. Beide Ports lassen sich mit --port verschieben, der Datenport liegt immer eins höher. Kommandos sind ASCII-Zeilen, abgeschlossen mit Wagenrücklauf (\r). xara antwortet mit OK oder WRONG, und zwar auch bei Kleinigkeiten wie Kleinschreibung oder einem Leerzeichen am Ende, genau wie das Original.
Die wichtigsten Kommandos:
| Kommando | Wirkung |
|---|---|
MYCALL DC8WAN DC8WAN-1 | eigene Rufzeichen setzen (bis zu fünf) |
LISTEN ON / LISTEN OFF / LISTEN CQ | auf Anrufe hören |
CONNECT DC8WAN N1CALL | Verbindung aufbauen (FM auch mit VIA und Digis) |
DISCONNECT / ABORT | sauber trennen oder sofort abbrechen |
BW500 / BW2300 / BW2750 | Bandbreite wählen |
COMPRESSION OFF / TEXT / FILES | Kompression |
CHAT ON / WINLINK SESSION / P2P SESSION | Sitzungsart |
CWID ON | CW-ID beim Trennen |
CQFRAME DC8WAN 2300 | CQ-Frame senden |
TUNE ... / DRIVELEVEL ... | Abstimmton, Sendepegel |
CLEANTXBUFFER | Sendepuffer leeren |
VERSION | liefert VERSION VARA HF v4.9.0 |
Der Ablauf einer abgehenden Verbindung sieht auf dem Kommandoport laut vectors/host/messages.json so aus. Die Kommentare rechts sind von mir, {n} steht für die jeweilige Anzahl Bytes im Sendepuffer:
> CONNECT N0CALL N1CALL
< OK
< PTT ON Connect-Request
< BUSY ON
< PTT OFF
< UNENCRYPTED LINK Connect-Reply empfangen
< BITRATE (4) 175 bps TX
< PTT ON Header
< PTT OFF
< CONNECTED N0CALL N1CALL 2300 BREAK empfangen
< PTT ON Startstufe senden
...
< BUFFER {n} ACK für einen Datenframe
< BITRATE (4) 175 bps TX
< PTT ON
...
< BUFFER 0
> DISCONNECT
< OK
< PTT ON
< PTT OFF
< DISCONNECTED
Unabhängig davon meldet xara fünf Sekunden nach MYCALL die Zeilen REGISTERED und ENCRYPTION DISABLED. Beim Trennen liegen zwischen PTT OFF und DISCONNECTED noch QRT und gegebenenfalls die CW-ID, im Listing oben gekürzt.
xara bildet diese Meldungen Zeichen für Zeichen nach, bis zu den zwei Leerzeichen in BITRATE (4) 175 bps. Welche Ausgaben in welcher Reihenfolge kommen, ist in vectors/host/ festgehalten und wird von der Testsuite geprüft.
Fazit
Was mir beim Lesen des Codes auffiel: VARA ist an vielen Stellen sparsam mit Sendezeit. Quittungen tragen gleich die Stufenwahl mit, die Kontrollframes schrumpfen bei gutem Kanal von 1,3 auf 0,3 Sekunden, und der Füllstand im Steuerbyte verhindert, dass für zwei Restbytes hochgeschaltet wird. Gleichzeitig reagiert die Anpassung schnell auf Verschlechterung und zögerlich auf Verbesserung, was auf Kurzwelle mit Fading sinnvoll ist.
Wer tiefer einsteigen will, findet den Code unter github.com/x0rro/xara. Die Testvektoren in vectors/ sind dabei eine gute Ergänzung, weil sie konkrete Werte zu fast jeder Tabelle enthalten.