# 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.