Files
kst4contest/docs/superpowers/specs/2026-10-16-so2r-design.md
T
Rsclub2_2andClaude Opus 5.5 5b7c774f5f SO2R: two radios in one instance (#46) (#137)
* Spec: SO2R with two radios in one instance (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Plan: SO2R implementation (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Move the rotator command rules into RotatorControl (#46)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Store the SO2R switch, the second Win-Test station and rotator 2 (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Route frequency reports to radio 1 or 2 (#46)

Add the RadioSlot enum (FIRST, SECOND) with its QRG and QTF targets, and
the pure RadioSlotRouter that decides per RadioInfo packet and per
Win-Test station which radio a report belongs to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Send RadioInfo of radio 2 to SECONDQRG when SO2R is on (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Send Win-Test STATUS of the second station to SECONDQRG when SO2R is on (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Run a second PSTRotator for radio 2 with SO2R (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Start rotator 2 only while an ON4KST session runs (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Settings for SO2R, the second Win-Test station and rotator 2 (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Turn antenna 2 and show QTF2 with SO2R (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Make the SO2R switches observable so Apply shows them (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document SO2R (#46)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refuse clashing PSTRotator ports in both directions (#46)

Writing either port now checks same-or-adjacent against the drafted value of
the other; rotor 1 is only checked while SO2R and rotor 2 are on. Also fixes
a misplaced Javadoc, stale WinTest parseStatus Javadoc, docs wording and
removes an unused import and dead comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove the executed SO2R plan (#46)

The plan's content lives on in the commits, the spec and PROJECT_CONTEXT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:14:27 +02:00

8.3 KiB

Entwurf — SO2R: zwei Funkgeräte, eine Instanz (#46)

Freigegeben vom Maintainer am 2026-10-16 (Gespräch). Erster Schritt zu #46; MO2R und Multi-Operator über mehrere Rechner sind eigene Vorhaben mit eigenem Entwurf.

Warum

KST4Contest kennt heute genau ein Funkgerät:

  • Jede RadioInfo-Meldung aus dem Logprogramm (N1MM, DXLog, UCXLog) überschreibt MYQRG. Das Feld RadioNr wird nicht gelesen. Wer mit zwei Geräten arbeitet, sieht MYQRG bei jedem Gerätewechsel springen, und die Makros tragen die QRG des zuletzt gemeldeten Geräts.
  • Win-Test kennt einen einzigen Stationsnamen-Filter, also ebenfalls ein Gerät.
  • Es gibt einen PSTRotator-Client, also nur „Antenne 1 drehen".

Ein SO2R-Platz (ein Operator, ein Rechner, eine ON4KST-Anmeldung, zwei Geräte mit je eigener Antenne) braucht pro Gerät eine eigene QRG und einen eigenen Rotor.

Umfang

Drin:

  • Pro Gerät eine eigene QRG: Gerät 1 schreibt MYQRG, Gerät 2 schreibt SECONDQRG.
  • Zuordnung über RadioNr (N1MM, DXLog, UCXLog) und über je einen Win-Test-Stationsnamen.
  • Ein zweiter PSTRotator mit Button „Antenne 2 drehen" und eigenem QTF-Feld.
  • Ein Schalter „SO2R", standardmäßig aus. Aus heißt: alles wie heute.

Bewusst nicht drin:

  • Ein eigenes Rechenband pro Gerät (Pfadanalyse, Tropo-Spalte, Karte bleiben wie heute).
  • Ein „aktiver Platz", auf den sich Gearbeitet, Skeds und Makros beziehen.
  • Strahlrichtung beider Antennen auf der Karte.
  • Mehr als zwei Geräte.
  • MO2R und Multi-Operator. Das Modell schließt sie nicht aus (siehe unten), wird aber nicht dafür ausgebaut.

Modell

RadioSlot (core)

Genau zwei Instanzen, Platz 1 und Platz 2. Ein Platz bündelt:

Platz 1 Platz 2
QRG-Ziel getMYQRGFirstCat() (MYQRG) getMYQRGSecondCat() (SECONDQRG)
Rotor RotatorControl über stn_pstRotator* RotatorControl über stn_pstRotator2*
gemeldete Richtung getActualQTF() neu getActualQTF2()
Win-Test-Stationsname vorhandener Filter logsynch_wintestNetworkStationNameOfWintestClient1 neu logsynch_wintestNetworkStationNameOfWintestClient2

Platz 2 existiert nur bei eingeschaltetem SO2R. Ohne SO2R gibt es keinen Rotor 2 und kein QTF2.

RadioSlot ist die Einheit, an die ein späteres MO2R einen Operator oder eine Arbeitsfläche hängen kann. Für SO2R wird nichts davon gebaut.

RotatorControl (core, herausgezogen)

Drehbefehl, Wiederholung und die Toleranz „erreicht" (1°, ROTATOR_REACHED_TOLERANCE_DEGREES) liegen heute im ChatController. Sie ziehen unverändert in eine eigene Klasse, die einmal pro Platz instanziert wird: eigener PstRotatorClient, eigener Wiederholungs-Scheduler, eigene zuletzt gemeldete Richtung. Das Verhalten bleibt bitgenau gleich, einschließlich der vorhandenen Kompatibilitäts-Wiederholung; der ChatController delegiert nur noch.

PSTRotator antwortet auf Port+1. Eine zweite PSTRotator-Instanz braucht also eigene Ports; die Vorgabe für Rotor 2 ist 12010 (Antwort auf 12011).

RadioSlotRouter (core, rein)

Entscheidet, zu welchem Platz eine Meldung gehört. Rein und ohne Abhängigkeiten, damit die Regel ohne Netz testbar ist.

RadioInfo:

SO2R RadioNr Platz
aus beliebig, auch fehlend 1
an 1 1
an 2 2
an fehlend 1
an anderer Wert oder nicht lesbar verworfen, geloggt

Win-Test STATUS:

SO2R Stationsname Platz
aus wie heute: Filter leer oder passt 1, sonst verworfen
an passt auf Name von Platz 2 2
an Name von Platz 1 leer oder passt 1
an sonst verworfen

Namensvergleich wie heute beim Filter (equalsIgnoreCase). Die Filterung der ADDQSO-Pakete nach Stationsname bleibt unberührt.

Datenfluss und Fäden

  • ReadUDPbyUCXMessageThread liest zusätzlich RadioNr, fragt den Router und schreibt die QRG über uiDispatcher.runOnUi in das QRG-Ziel des Platzes. Der Weg für eine RadioInfo ohne Freq oder Mode bleibt unverändert (Paket verwerfen, weiter empfangen).
  • ReadUDPByWintestThread fragt den Router mit dem Stationsnamen; Passfrequenz-Option und Formatierung bleiben, nur das Ziel hängt vom Platz ab.
  • Rotor-Rückmeldungen laufen im Empfangsfaden des jeweiligen Rotors und schreiben die Richtung des Platzes, wie heute bei Rotor 1. Nichts wartet auf dem AWT-Ereignisfaden.

Einstellungen

Reiter TRX-Abgleich

  • Neuer Schalter „SO2R: zwei Geräte getrennt führen" (trxSynch_so2rEnabled, Vorgabe aus).
  • Bei SO2R an wird der vorhandene Win-Test-Stationsnamen-Filter zum Namen für Platz 1; ein neues Feld nimmt den Namen für Platz 2 auf. Bei SO2R aus ist das neue Feld ausgegraut.
  • Hinweistext: N1MM, DXLog und UCXLog werden über RadioNr zugeordnet, dafür ist nichts einzustellen.

Reiter Station, Abschnitt PSTRotator

  • Zweiter Block „PSTRotator 2 (SO2R)": Schalter, Host, Port (Vorgaben aus, 127.0.0.1, 12010).
  • Nur aktiv bei SO2R an. Der Hinweis „antwortet auf Port+1" gilt auch hier.
  • Gleicher Port wie Rotor 1 wird beim Übernehmen abgelehnt, mit Meldung, wie andere ungültige Eingaben.

Übernehmen

Wie seit dem Entwurf vom 2026-10-16 (Einstellungen mit „Übernehmen"): alles wirkt erst mit Übernehmen/Schließen oder Speichern.

  • Ändern sich SO2R oder ein Wert von Rotor 2, wird Rotor 2 einmal neu gestartet oder gestoppt — ein eigener Effekt-Schlüssel neben dem von Rotor 1.
  • SO2R aus stoppt Rotor 2 und leert QTF2.

preferences.xml

Neue Elemente: trxSynch_so2rEnabled, logsynch_wintestNetworkStationNameOfWintestClient2, stn_pstRotator2Enabled, stn_pstRotator2Host, stn_pstRotator2Port. Fehlen sie in einer älteren Datei, gelten die Vorgaben; keine Migration. Alles gilt pro Profil.

Oberfläche

Stationspanel

  • Neben „Antenne 1 Richtung {0} drehen" steht „Antenne 2 Richtung {0} drehen" (selected.turnAntenna2, de/en).
  • Sichtbar nur bei SO2R an und aktiviertem Rotor 2. Ausgegraut nach derselben Regel wie canTurnAntenna bei Rotor 1 (Rotor nicht verbunden, Station ohne Locator).
  • Überall, wo heute „Antenne 1 drehen" angeboten wird, kommt „Antenne 2 drehen" dazu.

Sendezeile

  • Reihenfolge MYQRG | SECONDQRG | QTF | QTF2. QTF2 zeigt die Richtung von Rotor 2, nur zur Anzeige, und ist nur bei SO2R sichtbar.
  • Bei SO2R an lautet der Tooltip von SECONDQRG „QRG Gerät 2 (aus RadioNr 2 bzw. Win-Test-Station 2)".
  • Bei SO2R aus sieht die Zeile aus wie heute.

Statusleiste

Rotor 2 meldet seinen Zustand wie Rotor 1 über ThreadStatusCallback, aber unter eigenem Thread-Namen, und bekommt so einen eigenen Knopf in der Statusleiste. Ein Ausfall von Rotor 2 berührt Rotor 1 nicht.

Unverändert

QTF-Filter der Stationsliste (Rotor 1), Karte, Pfadanalyse, Gearbeitet-Logik. Die Makros MYQRG und SECONDQRG tragen bei SO2R die Frequenzen der beiden Geräte.

Dokumentation

  • Changelog-Eintrag de/en.
  • Handbuch: kurzer Abschnitt „SO2R" — N1MM/DXLog/UCXLog brauchen nichts, Win-Test braucht zwei Stationsnamen, die zweite PSTRotator-Instanz braucht einen anderen Port.

Tests

Core:

  • RadioSlotRouterTest: alle Zeilen der beiden Tabellen oben.
  • RotatorControlTest: Toleranz „erreicht", keine Wiederholung bei Bewegung, die vorhandene Wiederholung bei Stillstand — mit Fake-Client und Fake-Scheduler. Die bisherigen Rotor-Tests bleiben unverändert grün; sie sichern das Herausziehen ab.
  • Empfänger: ein echtes RadioInfo-Paket mit RadioNr 2 bzw. ein Win-Test-STATUS mit Stationsname 2 setzt SECONDQRG, MYQRG bleibt unverändert. Bei SO2R aus setzt dasselbe Paket MYQRG.
  • ChatPreferences: Roundtrip der fünf neuen Elemente; eine alte Datei ohne sie ergibt die Vorgaben (SO2R aus, Port 12010).

App:

  • Einstellungen (DraftedSettingsTableTest-Gerüst): Übernehmen startet bzw. stoppt Rotor 2 genau einmal; gleicher Port wie Rotor 1 wird abgelehnt; SO2R aus stoppt Rotor 2 und leert QTF2.
  • Oberfläche (Compose-UI-Tests wie beim Stationspanel): Button Antenne 2 und QTF2 nur bei SO2R sichtbar; Button ohne Rotor 2 ausgegraut; ohne SO2R ist die Sendezeile wie heute.
  • i18n: neue Schlüssel de/en, vom Erzeuger beim Bau geprüft.

Live, durch den Maintainer:

  • N1MM oder DXLog mit zwei Geräten: Gerätewechsel bewegt MYQRG und SECONDQRG getrennt.
  • Zwei PSTRotator-Instanzen auf verschiedenen Ports: beide Buttons drehen die richtige Antenne, QTF und QTF2 folgen.
  • Win-Test mit zwei Stationen, falls verfügbar.