Den Rotor vom Telefon drehen — mit der Sicherung davor - #200
Merged
Merged
Conversation
Die Seite behandelte das Entsperren bis hierher GAR NICHT.
`visibilitychange` gab es genau einmal, und zwar um das Mikrofon
loszulassen. Was dabei wirklich passiert:
* Der WebSocket ist tot, aber `onclose` kommt erst, wenn das System
die Seite auftaut -- und der Wiederholungsabstand ist inzwischen auf
10 s gewachsen. Man sieht sekundenlang nichts, obwohl das Funkgeraet
bereit ist. LIVE GEMESSEN, gleiche Ausfalldauer (55 s):
ohne Wecken: Server zurueck 05:23:37, Seite da 05:23:45 -> 8 s
mit Wecken: Server zurueck 05:25:10, Seite da 05:25:13 -> 0 s
(dieselbe Sekunde wie das Wecken)
* Der AudioContext ist von iOS angehalten. Ein angehaltener Kontext
ruft seinen Rueckruf nie wieder auf -- der Ton bliebe stumm, bis
jemand TON AUS und wieder EIN drueckt.
* Wasserfall und S-Meter zeigen den Stand von VOR dem Sperren, ohne
das zu sagen. Dieselbe Gattung Luege wie ein stehender Wasserfall:
er behauptet ein leeres Band, und danach wird nicht gerufen. Jetzt
zieht das Aufwachen denselben Strich, den ein Bandwechsel zieht --
oben das Neue, unten das Alte.
Entschieden wird in `aufwachen.js`, und NICHT am `readyState` allein:
iOS friert die Seite ein, die Gegenseite raeumt auf, und der Socket
meldet noch OFFEN. Wer darauf wartet, dass `onclose` kommt, wartet unter
Umstaenden ewig. Darum zaehlt der Zustand UND die Stille -- kommt seit
drei Sekunden nichts an, wird neu verbunden, ganz gleich was der Socket
behauptet. Eine ueberfluessige Neuverbindung kostet eine halbe Sekunde,
eine ausgelassene den Moment, in dem man das Telefon in die Hand nimmt.
Die Grenze fuer "das Bild ist alt" liegt mit 1,5 s UNTER der fuer "der
Socket ist tot" (3 s): lieber einmal zu frueh sagen, dass das Bild von
vorhin ist, als einmal zu spaet. Ein falsch beschriftetes altes Bild ist
harmlos, ein unbeschriftetes nicht.
Die Abonnements muessen nicht erneuert werden -- sie haengen am
`open`-Ereignis und kommen mit der neuen Verbindung von selbst. Die
Spots schon, die laufen waehrend des Schlafens ab.
Drei Wege ins Aufwachen, weil nicht jedes System `visibilitychange`
zuverlaessig meldet: `visibilitychange`, `pageshow` mit `persisted`,
`focus`. Mehrfaches Aufwachen schadet nicht -- die Entscheidung wirft
eine lebendige Verbindung nicht weg, und genau das ist geprueft.
17 Pruefpunkte (pruefe-aufwachen.mjs), dazu die Live-Messung oben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Punkt 2 der App-Zeitachse. Am anderen Ende der Leitung steht ein Mast
mit einer Antenne darauf, der sich auf Zuruf minutenlang dreht, und
niemand steht daneben. Danach richtet sich alles hier.
── DIE NAHTSTELLE ─────────────────────────────────────────────────────
Der RotctldClient wird von `RotorLogbookPanel` angelegt -- einem
FENSTER. Dasselbe Muster wie beim Logbuch, wo `WorkedBefore` am
`RotorLogbookPanel` haengt und der TCI-Server ihn deshalb nicht
anfassen durfte: ein Netzdienst, der an einem Fenster haengt, stirbt
mit dem Fenster.
`RadioModel` bekommt darum eine REGISTRIERUNG, keinen Besitz: das
Panel meldet seinen Client an, das Modell haelt einen QPointer. Der
sauberere Weg waere, den Besitz hierher zu holen (wie bei
`m_spotModel`, das 2026-05-12 aus `SpotHubDialog` hierher kam) -- das
ist eine Architekturaenderung an einem Geraet, das an Martins Mast
haengt, und CLAUDE.md verlangt dafuer den Betreiber. Bis dahin der
kleine, umkehrbare Schritt.
── DIE SPERREN ────────────────────────────────────────────────────────
rotor:; -> rotor_ist:<grad>,<zustand>,<frisch 0|1>;
rotor_to:<grad>; -> rotor_ok:<grad>; oder rotor_err:<grund>;
rotor_stop:; -> rotor_ok:stop;
ABFRAGEN darf jeder angemeldete Client -- hinsehen bewegt nichts.
DREHEN nur mit `TciAllowRemoteRotor=True`, ab Werk FALSE: dieselbe
Strenge wie beim Senden, aus demselben Grund. Abgelehnt wird MIT
Grund; ein stummes Nein ist von einem Defekt nicht zu unterscheiden,
und beim Rotor sieht man zehn Sekunden lang ohnehin nichts.
`frisch` ist kein Beiwerk: eine Nadel, die eine veraltete Stellung
zeigt, ohne das zu sagen, ist schlimmer als eine, die nichts zeigt --
so steht es schon im Kopf von RotorController.h.
── WAS toDouble() ANGERICHTET HAETTE ──────────────────────────────────
`RotorPeilung::lies()` nimmt NICHT einfach `toDouble()`:
* aus "" und "abc" macht `toDouble()` eine 0 -- und NULL GRAD IST
NORD. Eine leere Zeile haette die Antenne nach Norden gedreht.
* NaN besteht jeden Bereichsvergleich, weil jeder Vergleich mit NaN
falsch ist. Ein naiver Bereichstest laesst NaN durch.
* 360 ist dieselbe Richtung wie 0 und gehoert angenommen; 361 ist
ein Tippfehler. Ausserhalb wird ABGELEHNT statt umgerechnet: wer
-90 schickt, hat sich vertan, und 270 Grad sind eine andere
Antwort als "ich habe mich vertan".
── AUF DER SEITE ──────────────────────────────────────────────────────
Das Feld BLEIBT VERSTECKT, bis Longpath einen Rotor meldet. An der QRP
gibt es keinen, und ein leeres Feld, das nichts kann, ist auf einem
Telefon reiner Platzverbrauch.
Die Scheibe WAEHLT nur; gedreht wird mit dem Knopf darunter --
dieselbe Entscheidung wie bei der Sendetaste (Entwurf 1, 2026-10-04):
ein Tipp, der sofort einen Mast dreht, ist in einer Hosentasche eine
schlechte Idee. Eine Wahl verfaellt nach 15 s; sie gehoert sonst dem
Finger von vorhin.
Nord ist OBEN und gezaehlt wird im Uhrzeigersinn -- `atan2(x, -y)`,
nicht `atan2(y, x)`. Letzteres waere Ost = 0 und gegen den
Uhrzeigersinn und sieht auf einer runden Scheibe genauso plausibel
aus; der Pruefstand haelt beide Verwechslungen ausdruecklich fest.
Messing fuer die gemessene Stellung, Blau gestrichelt fuer das
gewaehlte Ziel -- die beiden duerfen nie zu verwechseln sein.
── GEPRUEFT ───────────────────────────────────────────────────────────
48 Pruefpunkte: 8 (RotorPeilung) + 9 (TCI, ueber den echten
WebSocket) + 31 (Seite, ohne Browser).
Live gegen die Attrappe: Osten angetippt -> "AUF 90° DREHEN", Knopf
gedrueckt -> der Rotor nimmt an, meldet "DREHT …", und die Nadel
wandert (143° -> 140° -> …). Danach steht der Knopf wieder auf
"RICHTUNG WÄHLEN" -- die Sicherung ist zu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Konflikte, zwei verschiedene Antworten -- darum nachgesehen statt
pauschal aufgeloest:
* handfunke/app.js: beide Stellen sind reine Ergaenzungen, mains Seite
ist leer -> unsere behalten.
* tests/CMakeLists.txt: main hat dort den Eintrag fuer
tst_audio_rate_matcher (#198) ergaenzt, wir die beiden Rotor-Staende
-> BEIDE behalten. Hier haette "unsere behalten" den Driftausgleich
aus der Pruefliste geworfen, und niemand haette es gemerkt, weil die
Datei trotzdem baut.
Danach neu konfiguriert und gegen frische Binaerdateien gemessen:
tst_rotor_peilung 8
tst_tci_rotor 9
tst_audio_rate_matcher 9
pruefe-rotor 31
pruefe-spots 43
pruefe-aufwachen 17
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Punkt 2 der App-Zeitachse. Am anderen Ende der Leitung steht ein Mast mit
einer Antenne darauf, der sich auf Zuruf minutenlang dreht, und niemand steht
daneben. Danach richtet sich alles hier.
Die Nahtstelle
Der
RotctldClientwird vonRotorLogbookPanelangelegt — einem Fenster.Dasselbe Muster wie beim Logbuch, wo
WorkedBeforedort hängt und derTCI-Server ihn deshalb nicht anfassen durfte: ein Netzdienst, der an einem
Fenster hängt, stirbt mit dem Fenster.
RadioModelbekommt darum eine Registrierung, keinen Besitz — das Panelmeldet seinen Client an, das Modell hält einen
QPointer. Der sauberere Wegwäre, den Besitz hierher zu holen (wie bei
m_spotModel, das 2026-05-12 ausSpotHubDialoghierher kam); das ist eine Architekturänderung an einem Gerät,das am Mast hängt, und CLAUDE.md verlangt dafür den Betreiber. Bis dahin der
kleine, umkehrbare Schritt.
Die Sperren
Abfragen darf jeder angemeldete Client — hinsehen bewegt nichts.
Drehen nur mit
TciAllowRemoteRotor=True, ab Werk False: dieselbeStrenge wie beim Senden, aus demselben Grund. Abgelehnt wird mit Grund; ein
stummes Nein ist von einem Defekt nicht zu unterscheiden, und beim Rotor sieht
man zehn Sekunden lang ohnehin nichts.
frischist kein Beiwerk: eine Nadel, die eine veraltete Stellung zeigt, ohnedas zu sagen, ist schlimmer als eine, die nichts zeigt — so steht es schon im
Kopf von
RotorController.h.Was
toDouble()angerichtet hätte""und"abc"machttoDouble()eine 0 — und null Grad ist Nord.Eine leere Zeile hätte die Antenne nach Norden gedreht.
ist. Ein naiver Bereichstest lässt NaN durch.
Tippfehler. Außerhalb wird abgelehnt statt umgerechnet: wer −90 schickt,
hat sich vertan, und 270 Grad sind eine andere Antwort als „ich habe mich
vertan".
Auf der Seite
Das Feld bleibt versteckt, bis Longpath einen Rotor meldet. An der QRP gibt
es keinen, und ein leeres Feld, das nichts kann, ist auf einem Telefon reiner
Platzverbrauch.
Die Scheibe wählt nur; gedreht wird mit dem Knopf darunter — dieselbe
Entscheidung wie bei der Sendetaste: ein Tipp, der sofort einen Mast dreht, ist
in einer Hosentasche eine schlechte Idee. Eine Wahl verfällt nach 15 s; sie
gehört sonst dem Finger von vorhin.
Nord ist oben und gezählt wird im Uhrzeigersinn —
atan2(x, -y), nichtatan2(y, x). Letzteres wäre Ost = 0 und gegen den Uhrzeigersinn und sieht aufeiner runden Scheibe genauso plausibel aus; der Prüfstand hält beide
Verwechslungen ausdrücklich fest.
Messing für die gemessene Stellung, Blau gestrichelt für das gewählte Ziel —
die beiden dürfen nie zu verwechseln sein.
Geprüft
48 Prüfpunkte: 8 (
tst_rotor_peilung) + 9 (tst_tci_rotor, über denechten WebSocket) + 31 (
pruefe-rotor.mjs, ohne Browser).Live gegen die Attrappe: Osten angetippt → „AUF 90° DREHEN", Knopf gedrückt →
der Rotor nimmt an, meldet „DREHT …", und die Nadel wandert (143° → 140° → …).
Danach steht der Knopf wieder auf „RICHTUNG WÄHLEN" — die Sicherung ist zu.
🤖 Generated with Claude Code