diff --git a/github_docs/de-Funktionen.md b/github_docs/de-Funktionen.md index 9157f9e..122165f 100644 --- a/github_docs/de-Funktionen.md +++ b/github_docs/de-Funktionen.md @@ -277,9 +277,13 @@ Nicht jede im ON4KST-Chat eingeloggte Station nimmt am gerade laufenden Contest KST4Contest kann darauf mit einem vorher festgelegten Text reagieren. Die eingehende Privatnachricht bleibt dabei sichtbar; sie wird weder blockiert noch verworfen. Davon getrennt lässt sich eine QRG-Antwort aktivieren, die auf typische Fragen wie `qrg?`, `freq?` oder `pse qrg` reagiert. -Bei zwei gleichzeitig geöffneten Chat-Kategorien bleibt der Zusammenhang erhalten: Die Antwort wird in der Kategorie der eingegangenen Nachricht gesendet. Eine QRG-Anfrage erhält außerdem nur die QRG dieser Kategorie und nicht eine Liste aller konfigurierten Frequenzen. +Bei zwei gleichzeitig geöffneten Chat-Kategorien bleibt der Zusammenhang erhalten: Die Antwort wird an das vollständige Absenderrufzeichen und in der Kategorie der eingegangenen Nachricht gesendet. Eine QRG-Anfrage erhält außerdem nur die QRG dieser Kategorie und nicht eine Liste aller konfigurierten Frequenzen. -Automatische Antworten benötigen Grenzen. KST4Contest versieht sie daher mit `[KST4C Automsg]`, ignoriert entsprechend gekennzeichnete Nachrichten bei der allgemeinen und QRG-bezogenen Antwort und begrenzt weitere Antworten an dieselbe Station in derselben Kategorie auf eine Nachricht innerhalb von zwei Minuten. Der Schutz gilt gemeinsam für beide Antwortarten. +Fehlt die QRG der betreffenden Kategorie, sendet KST4Contest keine inhaltslose Auskunft. Auch ein leerer oder protokollwidriger allgemeiner Antworttext wird nicht in die Sendequeue übernommen. + +Automatische Antworten benötigen Grenzen. KST4Contest versieht sie daher mit `[KST4C Automsg]`, ignoriert entsprechend gekennzeichnete Nachrichten und begrenzt weitere Antworten an dasselbe vollständige Rufzeichen in derselben Kategorie auf eine Nachricht innerhalb von zwei Minuten. Der Schutz gilt gemeinsam für die allgemeine und die QRG-bezogene Antwort. + +Die Sperrzeit wird nur nach einer vollständigen und lokal gültigen Antwort gestartet. Eine fehlende QRG oder ein verworfener Antworttext verhindert daher keine spätere gültige Antwort. Im Klartext: Die Funktion verhindert keine Massenanfragen. Sie verhindert aber, dass der Empfänger jede davon einzeln mit derselben Absage beantworten muss. Sie soll keine Unterhaltung simulieren und erst recht keine endlose Diskussion mit einem zweiten automatischen Client beginnen. @@ -287,6 +291,7 @@ Konfiguration, erkannte QRG-Anfragen und genaue Kategorienzuordnung: [Konfigurat --- + ## Multi-Channel-Login (ab v1.26) Gleichzeitiger Login in **zwei Chat-Kategorien** (z. B. 144 MHz und 432 MHz). Beide Chats werden parallel überwacht. @@ -422,14 +427,54 @@ Einstellungen: [Win-Test-Netzwerk-Listener](de-Konfiguration#win-test-netzwerk-l -## PSTRotator-Interface (ab v1.31, vollständig ab v1.40) +## PSTRotator-Interface (ab v1.31, vollständig konfigurierbar ab v1.40) -KST4Contest kann die Antennenrichtung direkt über **PSTRotator** steuern. Wenn in der Benutzerliste eine Station ausgewählt wird, kann der Rotator automatisch auf den QTF zur ausgewählten Station gedreht werden. +KST4Contest kann die Antenne über die UDP-Schnittstelle von PSTRotator auf die ausgewählte Gegenstation drehen. Der dafür verwendete Azimut wird aus dem eigenen und dem fremden Locator berechnet. -Konfiguration: [Konfiguration – PSTRotator-Einstellungen](de-Konfiguration#pstrotator-einstellungen-ab-v131) +Nach der Auswahl einer Station steht im **Further Info**-Bereich der Button **Turn ant1 to …** zur Verfügung: + +![PSTRotator-Steuerung für die ausgewählte Station](pstrotator_turn_antenna.png) + +Ein Klick führt folgende Schritte aus: + +1. KST4Contest beendet den Tracking-Modus von PSTRotator. +2. Der QTF der ausgewählten Station wird als ganzzahliger Azimut übertragen. +3. PSTRotator steuert den konfigurierten Rotator. +4. Die zurückgemeldete Position wird als aktuelle QTF in KST4Contest übernommen. + +Der Button bleibt auch sichtbar, wenn die PSTRotator-Integration deaktiviert ist. In diesem Fall wird kein Rotatorbefehl gesendet. + +### Rückmeldung und SPID-Kompatibilität + +KST4Contest fragt die aktuelle Azimutposition alle zwei Sekunden ab. Die Rückmeldung aktualisiert das eigene QTF-Feld und damit alle Funktionen, die von der Antennenrichtung abhängen. + +Einige SPID-Konfigurationen übernehmen den ersten Richtungsbefehl gelegentlich nicht. KST4Contest prüft deshalb zwei Sekunden nach dem Befehl, ob PSTRotator eine Bewegung beziehungsweise das Erreichen des Ziels gemeldet hat. + +Ist die Position unverändert und wurde das Ziel nicht erreicht, sendet KST4Contest einmalig eine Kompatibilitätssequenz über `0°` und anschließend erneut den eigentlichen Zielwert. + +Diese Prüfung läuft im Hintergrund. Die Benutzeroberfläche bleibt während der zwei Sekunden bedienbar. Wird zwischenzeitlich ein neuer Drehbefehl ausgelöst, ersetzt er die noch ausstehende Prüfung des vorherigen Befehls. + +### Was bestätigt die Positionsanzeige? + +Die angezeigte QTF ist die von PSTRotator gemeldete Azimutposition. Sie bestätigt, dass KST4Contest eine auswertbare UDP-Rückmeldung erhalten hat. + +Sie beweist nicht in jedem Aufbau, dass die Antenne mechanisch exakt in dieser Richtung steht. Das hängt von PSTRotator, dem angeschlossenen Controller, der Kalibrierung, möglichen Offsets und der tatsächlichen Rückmeldung des Rotors ab. + +UDP selbst bestätigt außerdem keine Paketzustellung. Bleibt die QTF unverändert, sollten daher zuerst folgende Punkte geprüft werden: + +- **UDP Control** ist in PSTRotator aktiviert. +- Host und Steuerport stimmen überein. +- Der Rückmeldeport `Steuerport + 1` ist frei. +- Die Firewall lässt beide UDP-Richtungen zu. +- PSTRotator zeigt selbst eine plausible Rotorposition an. + +Im Klartext: KST4Contest liefert die Zielrichtung und verarbeitet die gemeldete Position. Die mechanische Realität bleibt Aufgabe des Rotators – und gelegentlich der Blick aus dem Fenster. + +Konfiguration und Portbelegung: [Konfiguration – PSTRotator-Einstellungen](de-Konfiguration#pstrotator-einstellungen-ab-v131-vollstaendig-konfigurierbar-ab-v140). --- + ## Band-Upgrade-Hinweis nach einem Logeintrag Meldet UCXLog oder Win-Test einen neuen Logeintrag mit Bandinformation, prüft KST4Contest, ob die gearbeitete Station noch ein weiteres gemeinsames Band anbietet. diff --git a/github_docs/de-Konfiguration.md b/github_docs/de-Konfiguration.md index 349636a..f89579a 100644 --- a/github_docs/de-Konfiguration.md +++ b/github_docs/de-Konfiguration.md @@ -626,9 +626,19 @@ Eine zweckmäßige Nachricht ist beispielsweise: Sri, I am not taking part in this contest. No skeds. ``` +Das Präfix `[KST4C Automsg]` muss nicht in das Eingabefeld geschrieben werden. KST4Contest ergänzt es automatisch. + +Die beim Empfänger sichtbare Nachricht lautet daher beispielsweise: + +```text +[KST4C Automsg] Sri, I am not taking part in this contest. No skeds. +``` + Die eingegangene Privatnachricht bleibt sichtbar. Die Funktion blockiert oder verwirft keine Anfrage, sondern erspart lediglich die wiederholte manuelle Antwort. -Die Antwort wird in derselben Chat-Kategorie gesendet, in der die Privatnachricht eingegangen ist. Das ist bei einem parallelen Login in zwei Kategorien entscheidend: Eine Nachricht aus dem Microwave-Chat darf nicht versehentlich im VHF/UHF-Chat beantwortet werden. +Die Antwort wird an das vollständige Rufzeichen des Absenders einschließlich eines vorhandenen Suffixes und in derselben Chat-Kategorie gesendet, in der die Privatnachricht eingegangen ist. Das ist bei einem parallelen Login in zwei Kategorien entscheidend: Eine Nachricht aus dem Microwave-Chat darf nicht versehentlich im VHF/UHF-Chat beantwortet werden. + +Ein leerer oder ausschließlich aus Leerzeichen bestehender Antworttext erzeugt keine automatische Nachricht. Enthält der Text ein Protokoll-Trennzeichen wie `|` oder einen Zeilenumbruch, wird die Antwort ebenfalls verworfen. ### Automatische QRG-Antwort @@ -642,16 +652,24 @@ freq? pse qrg ``` -Die Antwort enthält nur die QRG der Kategorie, in der die Anfrage eingegangen ist: +Die Antwort enthält ausschließlich die QRG der Kategorie, in der die Anfrage eingegangen ist: | Eingegangene Privatnachricht | Verwendete QRG | |---|---| | Hauptkategorie | aktuelle QRG der Hauptkategorie | | zweite Chat-Kategorie | aktuelle QRG der zweiten Kategorie | +Eine mögliche Antwort lautet: + +```text +[KST4C Automsg] QRG is: 144.300.00 +``` + Die Werte stammen aus denselben QRG-Feldern, die auch von `MYQRG` und `SECONDQRG` verwendet werden. Die Haupt-QRG kann manuell eingetragen oder durch die [TRX-Synchronisation](#trx-sync-einstellungen) aktualisiert werden. Für die zweite Kategorie wird der dort konfigurierte beziehungsweise manuell eingetragene Wert verwendet. -Sind die allgemeine und die QRG-bezogene Antwort gleichzeitig aktiviert, hat die QRG-Antwort Vorrang. Eine erkannte QRG-Anfrage erzeugt daher nicht zusätzlich den allgemeinen Antworttext. +Ist für die betreffende Kategorie keine QRG vorhanden, sendet KST4Contest keine unvollständige Antwort. Eine Nachricht wie `QRG is:` ohne Frequenz würde zwar auf die Frage reagieren, dem Anfragenden aber keine Information liefern. Sie wird deshalb bereits vor der Übergabe an die Sendequeue verworfen. + +Sind die allgemeine und die QRG-bezogene Antwort gleichzeitig aktiviert, hat die QRG-Antwort Vorrang. Eine erkannte QRG-Anfrage erzeugt daher nicht zusätzlich den allgemeinen Antworttext. Fehlt die benötigte QRG, fällt KST4Contest auch nicht auf die allgemeine Antwort zurück. ### Schutz vor wiederholten Antworten @@ -663,9 +681,16 @@ Jede automatisch erzeugte Nachricht trägt das feste Präfix: Die allgemeine und die QRG-bezogene Antwort reagieren nicht auf Nachrichten, die dieses Präfix bereits enthalten. Dadurch beantworten sich zwei entsprechend arbeitende Clients nicht gegenseitig in einer Schleife. -Zusätzlich gilt eine gemeinsame Sperrzeit von zwei Minuten für beide Antwortarten. Die Sperre wird getrennt je Rufzeichen und Chat-Kategorie geführt. Hat eine Station gerade in der Hauptkategorie eine automatische Antwort erhalten, kann sie deshalb weiterhin eine Antwort in der zweiten Kategorie erhalten. Weitere Nachrichten derselben Station in derselben Kategorie lösen während der folgenden zwei Minuten dagegen keine neue automatische Antwort aus. +Zusätzlich gilt eine gemeinsame Sperrzeit von zwei Minuten für beide Antwortarten. Die Sperre wird getrennt nach vollständigem Rufzeichen und Chat-Kategorie geführt. -Die Sperrzeit beginnt nur, wenn KST4Contest tatsächlich eine Antwort sendet. +Daraus folgt: + +- `CALLSIGN-2` und `CALLSIGN-70` besitzen getrennte Sperrzeiten. +- Dasselbe vollständige Rufzeichen kann in einer anderen Chat-Kategorie unabhängig beantwortet werden. +- Eine allgemeine Antwort sperrt für zwei Minuten auch eine QRG-Antwort an dasselbe Rufzeichen in derselben Kategorie. +- Eine QRG-Antwort sperrt entsprechend auch die allgemeine Antwort. + +Die Sperrzeit beginnt nur, wenn KST4Contest eine vollständige und lokal gültige Antwort in die Sendequeue übernimmt. Eine fehlende QRG, ein leerer allgemeiner Antworttext oder ein wegen ungültiger Zeichen verworfener Text startet keine Sperrzeit. Sobald die fehlende Information korrigiert wurde, kann daher unmittelbar eine gültige Antwort erzeugt werden. > **Hinweis**: Der Antworttext sollte den tatsächlichen Status eindeutig benennen. Wer den Contest nur beobachtet und keine Skeds fahren möchte, sollte genau das mitteilen. Eine vage Nachricht erzeugt im Zweifel nur die nächste Rückfrage – und damit exakt die Arbeit, welche die Funktion vermeiden soll. @@ -717,16 +742,63 @@ Nach Änderungen **Save Settings** verwenden, damit Port, Stationsname, Broadcas Datenbehandlung und QRG-Auswahl: [Log-Synchronisation – Win-Test](de-Log-Synchronisation#win-test) -## PSTRotator-Einstellungen (ab v1.31) +## PSTRotator-Einstellungen (ab v1.31, vollständig konfigurierbar ab v1.40) -KST4Contest kann die Antennenrichtung über PSTRotator steuern. +KST4Contest kann eine ausgewählte Antennenrichtung über die UDP-Schnittstelle von [PSTRotator](https://www.pstrotator.com/) einstellen und die von PSTRotator gemeldete aktuelle Position als eigene QTF übernehmen. -Einstellungen: -- **Aktivieren/Deaktivieren**: Checkbox in den Preferences (ab v1.40). -- **IP-Adresse**: IP-Adresse des PSTRotator-Rechners (Standard: `127.0.0.1` bei Betrieb auf demselben PC). -- **Port**: Kommunikationsport von PSTRotator. +Die Einstellungen befinden sich im Reiter **Station**: -> **Hinweis**: Nach einem Klick auf den Richtungs-Button wartet KST4Contest kurz auf die Rotatorantwort. Bei langsamen Rotoren (z. B. SPID) kann es zu einer kleinen Verzögerung kommen. +| Einstellung | Standardwert | Verwendung | +|---|---:|---| +| **Enable PSTRotator** | deaktiviert | Startet die UDP-Kommunikation mit PSTRotator | +| **PSTRotator host** | `127.0.0.1` | Hostname oder IP-Adresse des Rechners, auf dem PSTRotator läuft | +| **PSTRotator UDP port** | `12000` | UDP-Port, auf dem PSTRotator die Steuerbefehle empfängt | + +Bei Betrieb auf demselben Rechner ist `127.0.0.1` normalerweise die eindeutigste Einstellung. Läuft PSTRotator auf einem anderen Rechner im Stationsnetz, muss dessen erreichbare IP-Adresse oder DNS-Name eingetragen werden. + +Der Port darf zwischen `1` und `65534` liegen. Port `65535` ist nicht möglich, weil PSTRotator seine Positionsmeldungen auf dem jeweils folgenden Port sendet. + +### PSTRotator vorbereiten + +In PSTRotator muss unter **Communication → UDP Control Port** derselbe UDP-Port eingetragen werden wie in KST4Contest. Anschließend muss **UDP Control** in PSTRotator aktiviert werden. + +Bei der Standardeinstellung ergibt sich folgendes Portpaar: + +| Richtung | UDP-Port | +|---|---:| +| KST4Contest → PSTRotator | `12000` | +| PSTRotator → KST4Contest | `12001` | + +KST4Contest bindet den Empfangsport automatisch. Er wird nicht separat konfiguriert. + +Bei Betrieb auf zwei Rechnern müssen die lokale Firewall und das Stationsnetz UDP-Pakete in beiden Richtungen zulassen. Ist der Empfangsport bereits durch ein anderes Programm belegt, kann KST4Contest die Positionsmeldungen nicht empfangen. + +Das vollständige UDP-Protokoll ist im [PSTRotatorAz User Manual](https://www.qsl.net/yo3dmu/ANT/PstRotatorAz%20User%20Manual.pdf) beschrieben. + +### Übernahme der aktuellen QTF + +KST4Contest fragt PSTRotator alle zwei Sekunden nach der aktuellen Azimutposition und dem Betriebsmodus. Die zurückgemeldete Azimutposition wird als `actualQTF` übernommen. + +Bei aktivierter PSTRotator-Integration ist das QTF-Feld im Hauptfenster deshalb nicht manuell editierbar. Es zeigt die zuletzt von PSTRotator gemeldete Position. + +Diese QTF wird unter anderem verwendet für: + +- den Richtungsfilter, +- die Bewertung von Richtungsgelegenheiten, +- den Priority Score, +- die Darstellung des Antennensektors auf der Stationskarte, +- die AP- und Sked-Timeline und +- die Variable `MYQTF`. + +Eine empfangene Rotatorposition ist damit nicht nur eine Anzeige. Sie verändert mehrere Funktionen, die auf der aktuellen Antennenrichtung beruhen. + +KST4Contest verwendet derzeit nur den Azimut. Eine Elevationssteuerung oder eine vollständige Azimut-/Elevationsnachführung ist nicht Bestandteil dieser Integration. + +### Änderungen übernehmen + +Aktivierung, Host und Port werden beim Start der Rotatorverbindung ausgewertet. Nach einer Änderung sollte die ON4KST-Verbindung getrennt und erneut aufgebaut oder KST4Contest neu gestartet werden. + +Anschließend **Save Settings** verwenden, damit die Werte auch beim nächsten Programmstart wiederhergestellt werden. --- diff --git a/github_docs/en-Configuration.md b/github_docs/en-Configuration.md index e9934a8..102e74a 100644 --- a/github_docs/en-Configuration.md +++ b/github_docs/en-Configuration.md @@ -628,14 +628,97 @@ Changes take effect during the current connection. Use **Save Settings** afterwa ## Messagehandling Settings (from v1.25) -New settings section with the following options: +![Automatic reply settings](client_settings_window_messagehandling.png) -- **Auto-reply to all incoming messages**: Configurable automatic reply to private messages. -- **Auto-reply with own CQ QRG**: When someone asks for your QRG, KST4Contest automatically replies with the content of the `MYQRG` variable. -- **Default filter for the userinfo window**: Pre-configured message filter for the station info window *(for Gianluca :-) )*. +The most important use of the general automatic reply concerns stations which are logged into the ON4KST chat but are not taking part in the current contest. During larger contests, sked requests are sometimes sent to many logged-in stations without first checking whether they are participating. Without an automatic reply, the recipients would have to enter the same refusal repeatedly. + +KST4Contest can answer these requests with a predefined message. A separate function provides the local QRG when a private message contains a recognised frequency request. Both functions can be enabled independently. + +### General automatic reply + +**Enable automatic reply to all private messages** answers incoming private messages with the text entered in the adjacent field. One common text is used for both chat categories. The configured capitalisation is preserved. + +A suitable message is: + +```text +Sri, I am not taking part in this contest. No skeds. +``` + +Do not add the `[KST4C Automsg]` prefix to the configured text. KST4Contest inserts it automatically. + +The message received by the remote station may therefore be: + +```text +[KST4C Automsg] Sri, I am not taking part in this contest. No skeds. +``` + +The incoming private message remains visible. The function neither blocks nor discards the request; it merely avoids entering the same answer repeatedly. + +The reply is addressed to the sender's complete callsign, including any visible suffix, and sent through the chat category in which the private message was received. This distinction matters when two categories are connected at the same time: a request received through the microwave chat must not be answered accidentally through the VHF/UHF chat. + +An empty or whitespace-only answer does not produce an automatic message. A configured text containing the protocol separator `|` or a line break is rejected as well. + +### Automatic QRG reply + +**Enable automatic QRG replies** reacts to common QRG requests. Matching is case-insensitive and looks for the following text fragments: + +```text +ur qrg? +your qrg? +qrg? +freq? +pse qrg +``` + +The answer contains only the QRG belonging to the category in which the request was received: + +| Incoming private message | QRG used for the reply | +|---|---| +| Primary category | current QRG of the primary category | +| Second chat category | current QRG of the second category | + +A possible reply is: + +```text +[KST4C Automsg] QRG is: 144.300.00 +``` + +The values come from the same QRG fields used by `MYQRG` and `SECONDQRG`. The primary QRG may be entered manually or updated through [TRX synchronisation](#trx-sync-settings). The second category uses the value configured or entered for that category. + +If no QRG is available for the incoming category, KST4Contest does not send an incomplete reply. A message containing `QRG is:` without a frequency would technically answer the request while providing no useful information. It is therefore rejected before reaching the transmit queue. + +When both automatic-reply functions are enabled, the QRG reply takes precedence. A recognised QRG request does not additionally produce the general reply. If the required QRG is missing, KST4Contest does not fall back to the general answer. + +### Protection against repeated replies + +Every automatically generated reply contains the fixed prefix: + +```text +[KST4C Automsg] +``` + +The general and QRG-specific functions ignore messages which already contain this prefix. This prevents two clients with automatic replies enabled from answering each other indefinitely. + +A common two-minute cooldown additionally applies to both reply types. The cooldown is tracked separately for each complete callsign and chat category. + +This means: + +- `CALLSIGN-2` and `CALLSIGN-70` have separate cooldowns. +- The same complete callsign can still receive an independent reply in another chat category. +- A general reply also suppresses a QRG reply to the same callsign in the same category for two minutes. +- A QRG reply likewise suppresses the general reply. + +The cooldown starts only after KST4Contest has produced a complete, locally valid message and placed it in the transmit queue. A missing QRG, an empty general reply or a message rejected because of invalid characters does not start the cooldown. Once the missing information has been corrected, a valid reply can therefore be generated immediately. + +> **Note**: The configured text should describe the actual operating status clearly. If the station is only observing the contest and does not accept skeds, say exactly that. A vague automatic message is likely to produce another question – which is precisely the work this function is intended to avoid. + +Changes take effect during the current connection. Use **Save Settings** afterwards to retain the enable settings and general reply text for the next program start. + +Further background: [Automatic Replies to Private Messages](en-Features#automatic-replies-to-private-messages-from-v125). --- + ## Win-Test Network Listener (from v1.31) The Win-Test network listener processes the native Win-Test UDP protocol. It is independent of the general QSO UDP listener on port `12060` and has three separate tasks: @@ -679,16 +762,63 @@ Data handling and QRG selection: [Log Synchronisation – Win-Test](en-Log-Sync# --- -## PSTRotator Settings (from v1.31) +## PSTRotator Settings (from v1.31)## PSTRotator Settings (from v1.31, fully configurable from v1.40) -KST4Contest can control antenna direction via PSTRotator. +KST4Contest can set an antenna direction through the PSTRotator UDP interface and use the current position reported by PSTRotator as the local QTF. -Settings: -- **Enable/Disable**: Checkbox in Preferences (from v1.40). -- **IP address**: IP address of the PSTRotator computer (default: `127.0.0.1` when running on the same PC). -- **Port**: Communication port of PSTRotator. +The settings are located in the **Station** tab: -> **Note**: After clicking a direction button, KST4Contest waits briefly for the rotator response. With slow rotors (e.g. SPID) there may be a small delay. +| Setting | Default | Purpose | +|---|---:|---| +| **Enable PSTRotator** | disabled | Starts UDP communication with PSTRotator | +| **PSTRotator host** | `127.0.0.1` | Hostname or IP address of the computer running PSTRotator | +| **PSTRotator UDP port** | `12000` | UDP port on which PSTRotator receives control commands | + +When both applications run on the same computer, `127.0.0.1` is normally the clearest setting. If PSTRotator runs on another computer in the station network, enter its reachable IP address or DNS name. + +The port must be between `1` and `65534`. Port `65535` cannot be used because PSTRotator reports its position on the following port. + +### Preparing PSTRotator + +Configure the same UDP port under **Communication → UDP Control Port** in PSTRotator and enable **UDP Control**. + +The default configuration uses the following pair: + +| Direction | UDP port | +|---|---:| +| KST4Contest → PSTRotator | `12000` | +| PSTRotator → KST4Contest | `12001` | + +KST4Contest binds the return port automatically. It is not configured separately. + +When the programs run on different computers, the local firewalls and station network must permit UDP traffic in both directions. KST4Contest cannot receive position reports if another program already occupies the return port. + +The complete UDP protocol is documented in the [PSTRotatorAz User Manual](https://www.qsl.net/yo3dmu/ANT/PstRotatorAz%20User%20Manual.pdf). + +### Updating the local QTF + +KST4Contest asks PSTRotator for the current azimuth and operating mode every two seconds. The reported azimuth becomes `actualQTF`. + +While PSTRotator integration is enabled, the QTF field in the main window is therefore read-only. It displays the most recent position reported by PSTRotator. + +This QTF is used by: + +- the direction filter; +- the derivation of direction opportunities; +- the Priority Score; +- the antenna sector on the station map; +- the AP and sked timeline; and +- the `MYQTF` variable. + +A received rotator position is therefore more than a displayed value. It changes several functions which depend on the current antenna direction. + +The current integration uses azimuth only. Elevation control and complete azimuth/elevation tracking are outside its present scope. + +### Applying changed settings + +The enable setting, host and port are evaluated when the rotator connection is started. After changing them, disconnect and reconnect the ON4KST session or restart KST4Contest. + +Use **Save Settings** afterwards so that the values are restored at the next program start. --- diff --git a/github_docs/en-Features.md b/github_docs/en-Features.md index d72fcaa..20c0611 100644 --- a/github_docs/en-Features.md +++ b/github_docs/en-Features.md @@ -185,6 +185,27 @@ KST4Contest detects such messages that contain your own callsign and automatical --- +## Automatic Replies to Private Messages (from v1.25) + +Not every station logged into the ON4KST chat is taking part in the current contest. Sked requests may nevertheless be sent to many visible callsigns without first checking whether they are participating. Without an automatic reply, those stations would have to enter the same refusal repeatedly. + +KST4Contest can answer such private messages with a predefined text. The incoming message remains visible; it is neither blocked nor discarded. A separate QRG reply recognises common requests such as `qrg?`, `freq?` and `pse qrg`. + +When two chat categories are connected, the original context is retained. The reply is addressed to the complete sender callsign and sent through the category in which the request arrived. A QRG request receives only the QRG belonging to that category. + +If the required QRG is missing, KST4Contest sends no incomplete answer. Empty or locally invalid general reply texts are rejected as well. + +Automatic replies need limits. KST4Contest adds the fixed prefix `[KST4C Automsg]`, ignores incoming messages which already contain that prefix and permits only one automatic reply to the same complete callsign in the same category within two minutes. The cooldown is shared by the general and QRG-specific reply functions. + +A rejected reply does not start the cooldown. After entering the missing QRG or correcting the configured text, KST4Contest can therefore answer the next request immediately. + +In plain terms: the function cannot prevent indiscriminate sked requests. It prevents the recipient from having to answer every one of them with the same refusal. It is not intended to simulate a conversation, let alone start an endless discussion with another automatic client. + +Configuration, recognised QRG requests and category handling: [Configuration – Messagehandling Settings](en-Configuration#messagehandling-settings-from-v125). + +--- + + ## Multi-Channel Login (from v1.26) Simultaneous login to **two chat categories** (e.g. 144 MHz and 432 MHz). Both chats are monitored in parallel. @@ -319,9 +340,48 @@ Settings: [Win-Test Network Listener](en-Configuration#win-test-network-listener ## PSTRotator Interface (from v1.31, fully configurable from v1.40) -KST4Contest can control antenna direction directly via **PSTRotator**. When a station is selected in the user list, the rotator can automatically be turned to the QTF of the selected station. +KST4Contest can point the antenna at the selected remote station through the PSTRotator UDP interface. The required azimuth is calculated from the local and remote locators. -Configuration: [Configuration – PSTRotator Settings](en-Configuration#pstrotator-settings-from-v131) +After selecting a station, the **Further Info** section provides the **Turn ant1 to …** button: + +![PSTRotator control for the selected station](pstrotator_turn_antenna.png) + +Pressing the button performs the following steps: + +1. KST4Contest disables PSTRotator tracking mode. +2. The QTF of the selected station is transmitted as an integer azimuth. +3. PSTRotator controls the configured rotor. +4. The reported position becomes the current QTF in KST4Contest. + +The button remains visible while PSTRotator integration is disabled. In that case, no rotator command is sent. + +### Position feedback and SPID compatibility + +KST4Contest requests the current azimuth every two seconds. Position reports update the local QTF field and every function which depends on the antenna direction. + +Some SPID configurations occasionally ignore the first direction command. KST4Contest therefore checks two seconds later whether PSTRotator reported movement or reached the requested target. + +If the position remained unchanged and the target was not reached, KST4Contest sends one compatibility sequence through `0°` followed by the actual target value. + +This check runs in the background. The user interface remains responsive during the two-second interval. A new direction command replaces the pending check belonging to the previous command. + +### What does the reported position confirm? + +The displayed QTF is the azimuth reported by PSTRotator. It confirms that KST4Contest received a usable UDP position message. + +Depending on the station setup, it does not necessarily prove that the antenna is mechanically aligned to exactly that value. This still depends on the controller, calibration, configured offsets and the feedback available to PSTRotator. + +UDP itself provides no delivery acknowledgement. If the displayed QTF does not change, check: + +- that **UDP Control** is enabled in PSTRotator; +- that the host and control port match; +- that `control port + 1` is available for the position report; +- that the firewall permits both UDP directions; and +- that PSTRotator itself displays a plausible rotor position. + +In plain terms: KST4Contest provides the target and processes the reported position. The mechanical reality remains the responsibility of the rotor – and occasionally a glance outside. + +Configuration and port assignment: [Configuration – PSTRotator Settings](en-Configuration#pstrotator-settings-from-v131-fully-configurable-from-v140). --- diff --git a/github_docs/pstrotator_turn_antenna.png b/github_docs/pstrotator_turn_antenna.png new file mode 100644 index 0000000..084a175 Binary files /dev/null and b/github_docs/pstrotator_turn_antenna.png differ diff --git a/src/main/java/kst4contest/controller/ChatController.java b/src/main/java/kst4contest/controller/ChatController.java index 3565459..881fcc3 100644 --- a/src/main/java/kst4contest/controller/ChatController.java +++ b/src/main/java/kst4contest/controller/ChatController.java @@ -37,6 +37,7 @@ import java.nio.charset.StandardCharsets; import kst4contest.logic.FrequencyTextParser; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.concurrent.ScheduledFuture; @@ -69,8 +70,30 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList public static final int MAX_BEACON_TEXT_LENGTH = 120; private static final long INITIAL_BEACON_DELAY_MILLIS = 10_000L; - private PstRotatorClient rotatorClient; - private Consumer viewRotorCallback; + private volatile PstRotatorClient rotatorClient; + private Consumer viewRotorCallback; + + /* + * The rotator retry must never block the JavaFX Application Thread. + * A daemon scheduler performs the delayed SPID compatibility check. + */ + private final ScheduledExecutorService rotatorCommandScheduler = + Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread( + runnable, + "PSTRotator-Command-Retry" + ); + thread.setDaemon(true); + return thread; + }); + + private ScheduledFuture pendingRotatorRetry; + + /* + * Updated directly by the PSTRotator receiver thread. This avoids reading + * a JavaFX property from the command scheduler. + */ + private volatile double lastReportedRotatorAzimuth = Double.NaN; private Kst4ContestApplication view; //effectively final, for recoupling of the controller to the view @@ -303,41 +326,142 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList rotatorClient.start(); } - /** - * sets rotator to "AZ DEGREE" by button click

- * Note that there is a workaround for spid rotators:
- * The AZ will be set, after 'time' secs it will be controlled if the rotator started, If not, the rotator will
- * be homed to 0 deg for very shord period, then the AZ value will be set again. - *
- * @param azimuth - */ - public void rotateTo(double azimuth) { + /** + * Sends a new azimuth to PSTRotator without blocking the JavaFX thread. + * + *

Some SPID configurations occasionally ignore the first azimuth + * command. KST4Contest therefore checks the latest reported position after + * two seconds. If no movement was reported and the target has not already + * been reached, the original compatibility sequence is sent again.

+ * + * @param azimuth required antenna azimuth in degrees + */ + public void rotateTo(double azimuth) { + if (!Double.isFinite(azimuth)) { + LOGGER.log( + Level.WARNING, + "Ignoring invalid PSTRotator azimuth: {0}", + azimuth + ); + return; + } - double beforeRotateAzWas = chatPreferences.getActualQTF().getValue(); + PstRotatorClient activeClient = rotatorClient; + if (activeClient == null) { + LOGGER.log( + Level.WARNING, + "Cannot rotate antenna to {0} degrees: " + + "PSTRotator integration is not active.", + azimuth + ); + return; + } - if (rotatorClient != null) { - rotatorClient.setTrackingMode(false); - System.out.println("Chatcontroller, Info: turning ant to " + azimuth + " by user request"); - rotatorClient.setAzimuth(azimuth); + double targetAzimuth = normalizeAzimuth(azimuth); + double positionBeforeCommand = lastReportedRotatorAzimuth; - Object lockDelay = new Object(); - synchronized (lockDelay) { - try{ + activeClient.setTrackingMode(false); - TimeUnit.SECONDS.sleep(2);; //wait 2s, then check if rotator does anything due SPID - // sometimes does simply not accept a rotating value for first try! - } catch (InterruptedException e) { + LOGGER.log( + Level.INFO, + "Sending PSTRotator azimuth requested by the operator: {0}", + targetAzimuth + ); - } - } + activeClient.setAzimuth(targetAzimuth); - if (chatPreferences.getActualQTF().getValue() == beforeRotateAzWas) { - rotatorClient.setAzimuth(0); //do some reset - rotatorClient.setAzimuth(azimuth); //then rotate - } + ScheduledFuture previousRetry = pendingRotatorRetry; + if (previousRetry != null) { + previousRetry.cancel(false); + } - } - } + pendingRotatorRetry = rotatorCommandScheduler.schedule( + () -> retryRotatorCommandIfRequired( + positionBeforeCommand, + targetAzimuth + ), + 2, + TimeUnit.SECONDS + ); + } + + /** + * Performs the delayed SPID compatibility check. + * + *

No retry is required when the requested position has already been + * reached or when PSTRotator reported movement after the original command. + * Missing feedback is treated like an unchanged position.

+ */ + private void retryRotatorCommandIfRequired( + double positionBeforeCommand, + double targetAzimuth + ) { + PstRotatorClient activeClient = rotatorClient; + if (activeClient == null) { + return; + } + + double currentAzimuth = lastReportedRotatorAzimuth; + + if (Double.isFinite(currentAzimuth) + && angularDistance(currentAzimuth, targetAzimuth) < 0.5) { + + LOGGER.log( + Level.FINE, + "PSTRotator reached the requested azimuth without retry: {0}", + targetAzimuth + ); + return; + } + + boolean noPositionFeedback = + !Double.isFinite(currentAzimuth); + + boolean positionUnchanged = + Double.isFinite(positionBeforeCommand) + && Double.isFinite(currentAzimuth) + && angularDistance( + positionBeforeCommand, + currentAzimuth + ) < 0.5; + + if (!noPositionFeedback && !positionUnchanged) { + LOGGER.log( + Level.FINE, + "PSTRotator reported movement towards {0}; no retry required.", + targetAzimuth + ); + return; + } + + LOGGER.log( + Level.WARNING, + "PSTRotator reported no movement after the command for {0} degrees; " + + "sending the SPID compatibility retry.", + targetAzimuth + ); + + activeClient.setAzimuth(0.0); + activeClient.setAzimuth(targetAzimuth); + } + + /** + * Returns the smallest angular distance between two azimuth values. + */ + private static double angularDistance(double first, double second) { + double difference = Math.abs( + normalizeAzimuth(first) - normalizeAzimuth(second) + ); + return Math.min(difference, 360.0 - difference); + } + + /** + * Normalises an azimuth to the range from 0 inclusive to 360 exclusive. + */ + private static double normalizeAzimuth(double azimuth) { + double normalized = azimuth % 360.0; + return normalized < 0.0 ? normalized + 360.0 : normalized; + } /** * Called when an external logger reports that a QSO was logged. @@ -441,16 +565,42 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList } - public void stopRotator() { - if (rotatorClient != null) { - rotatorClient.stop(); - } - } + public void stopRotator() { + ScheduledFuture pendingRetry = pendingRotatorRetry; + if (pendingRetry != null) { + pendingRetry.cancel(false); + pendingRotatorRetry = null; + } + + PstRotatorClient activeClient = rotatorClient; + rotatorClient = null; + lastReportedRotatorAzimuth = Double.NaN; + + if (activeClient != null) { + activeClient.stop(); + } + } @Override public void onAzimuthUpdate(double azimuth) { - // We are in the rotor client thread. JavaFX properties must be updated on the FX thread. - Runnable fxUpdate = () -> chatPreferences.getActualQTF().setValue(azimuth); + if (!Double.isFinite(azimuth)) { + LOGGER.log( + Level.WARNING, + "Ignoring invalid azimuth reported by PSTRotator: {0}", + azimuth + ); + return; + } + + double normalizedAzimuth = normalizeAzimuth(azimuth); + lastReportedRotatorAzimuth = normalizedAzimuth; + + /* + * The callback runs in the PSTRotator receiver thread. JavaFX properties + * must be updated on the JavaFX Application Thread. + */ + Runnable fxUpdate = () -> + chatPreferences.getActualQTF().setValue(normalizedAzimuth); if (Platform.isFxApplicationThread()) { fxUpdate.run(); diff --git a/src/main/java/kst4contest/controller/MessageBusManagementThread.java b/src/main/java/kst4contest/controller/MessageBusManagementThread.java index f2e81b8..41fcc84 100644 --- a/src/main/java/kst4contest/controller/MessageBusManagementThread.java +++ b/src/main/java/kst4contest/controller/MessageBusManagementThread.java @@ -1167,16 +1167,22 @@ public class MessageBusManagementThread extends Thread { // } // } - // ==== Unified auto-answer (generic + QRG) with ping-pong guard and per-remote cooldown ==== + // ==== Unified auto-answer (generic + QRG) with ping-pong guard +// and per-remote cooldown ==== final String incomingText = newMessageArrived.getMessageText(); - final String incomingLower = (incomingText == null) ? "" : incomingText.toLowerCase(Locale.ROOT); + final String incomingLower = + (incomingText == null) + ? "" + : incomingText.toLowerCase(Locale.ROOT); - // Never answer another automatically generated message. +// Never answer another automatically generated message. if (!isAutoMessage(newMessageArrived)) { boolean qrgRequested = false; - if (this.client.getChatPreferences().isMessageHandling_autoAnswerToQRGRequestEnabled()) { + if (this.client.getChatPreferences() + .isMessageHandling_autoAnswerToQRGRequestEnabled()) { + for (String lookForQRGString : qrgQuestionTexts) { if (incomingLower.contains(lookForQRGString)) { qrgRequested = true; @@ -1185,36 +1191,47 @@ public class MessageBusManagementThread extends Thread { } } - boolean genericEnabled = this.client.getChatPreferences().isMsgHandling_autoAnswerEnabled(); + boolean genericEnabled = + this.client.getChatPreferences() + .isMsgHandling_autoAnswerEnabled(); - // A QRG reply takes precedence over the generic reply. - String payload = null; + String automaticAnswerText = buildAutoAnswerMessageText( + newMessageArrived, + qrgRequested, + genericEnabled + ); - if (qrgRequested) { - payload = "QRG is: " + getAutoAnswerQrgForCategory(newMessageArrived.getChatCategory()); - } else if (genericEnabled) { - - payload = this.client.getChatPreferences().getMessageHandling_autoAnswerTextMainCat(); - } - - // Apply the cooldown only when this client is about to send a reply. - if (payload != null && isAutoAnswerAllowedNow(newMessageArrived)) { + /* + * Invalid or incomplete replies are rejected before the cooldown + * is checked or updated. A missing QRG must therefore not suppress + * a later valid reply. + */ + if (automaticAnswerText != null + && isAutoAnswerAllowedNow(newMessageArrived)) { ChatMessage automaticAnswer = new ChatMessage(); ChatMember itsMe = new ChatMember(); - itsMe.setCallSign(this.client.getChatPreferences().getStn_loginCallSign()); + + itsMe.setCallSign( + this.client.getChatPreferences() + .getStn_loginCallSign() + ); automaticAnswer.setSender(itsMe); - automaticAnswer.setReceiver(newMessageArrived.getSender()); - automaticAnswer.setChatCategory(newMessageArrived.getChatCategory()); - - // The fixed prefix prevents automatic clients from answering each other. - automaticAnswer.setMessageText("/CQ " + newMessageArrived.getSender().getCallSign() - + " " + AUTOANSWER_PREFIX + " " + payload); + automaticAnswer.setReceiver( + newMessageArrived.getSender() + ); + automaticAnswer.setChatCategory( + newMessageArrived.getChatCategory() + ); + automaticAnswer.setMessageText(automaticAnswerText); this.client.getMessageTXBus().add(automaticAnswer); - // Record only locally generated replies, not the later server echo. + /* + * Record the cooldown only after a complete and locally + * validated reply has been placed in the transmit queue. + */ markLocalAutoAnswerSent(newMessageArrived); } } @@ -1906,6 +1923,101 @@ public class MessageBusManagementThread extends Thread { } + /** + * Builds and validates one automatic private reply. + * + *

A QRG request is answered only when the QRG belonging to the + * incoming chat category is available. The generic answer is used + * only for other private messages and only when it contains actual + * text.

+ * + *

The complete message is validated before it enters the transmit + * queue. Invalid configuration values must neither produce an empty + * automatic reply nor start the cooldown.

+ * + * @param incoming incoming private message + * @param qrgRequested whether the message contains a recognised QRG request + * @param genericEnabled whether the general automatic reply is enabled + * @return validated message text or {@code null} when no reply may be sent + */ + private String buildAutoAnswerMessageText( + ChatMessage incoming, + boolean qrgRequested, + boolean genericEnabled + ) { + if (incoming == null + || incoming.getSender() == null + || incoming.getSender().getCallSign() == null + || incoming.getSender().getCallSign().isBlank()) { + + System.err.println( + "KST4Contest auto-answer skipped: " + + "incoming message has no valid sender callsign." + ); + return null; + } + + String payload; + + if (qrgRequested) { + String qrg = getAutoAnswerQrgForCategory( + incoming.getChatCategory() + ); + + if (qrg == null || qrg.isBlank()) { + System.err.println( + "KST4Contest QRG auto-answer skipped for " + + incoming.getSender().getCallSign() + + ": no QRG is available for chat category " + + autoAnswerCooldownKey(incoming) + + "." + ); + return null; + } + + payload = "QRG is: " + qrg.trim(); + + } else if (genericEnabled) { + payload = this.client.getChatPreferences() + .getMessageHandling_autoAnswerTextMainCat(); + + if (payload == null || payload.isBlank()) { + System.err.println( + "KST4Contest generic auto-answer skipped for " + + incoming.getSender().getCallSign() + + ": the configured answer text is empty." + ); + return null; + } + + payload = payload.trim(); + + } else { + return null; + } + + String messageText = + "/CQ " + + incoming.getSender().getCallSign().trim() + + " " + + AUTOANSWER_PREFIX + + " " + + payload; + + try { + return On4KstProtocol.messageText(messageText); + + } catch (IllegalArgumentException invalidMessage) { + System.err.println( + "KST4Contest auto-answer skipped for " + + incoming.getSender().getCallSign() + + ": " + + invalidMessage.getMessage() + ); + return null; + } + } + /** * Returns whether a message carries the fixed marker used for automatic replies. */ diff --git a/src/main/java/kst4contest/model/ChatPreferences.java b/src/main/java/kst4contest/model/ChatPreferences.java index 724c896..a97299a 100644 --- a/src/main/java/kst4contest/model/ChatPreferences.java +++ b/src/main/java/kst4contest/model/ChatPreferences.java @@ -820,7 +820,11 @@ public class ChatPreferences { } public void setStn_pstRotatorPort(int stn_pstRotatorPort) { - if (stn_pstRotatorPort < 1 || stn_pstRotatorPort > 65535) { + /* + * PSTRotator sends position reports to the configured UDP port + 1. + * Port 65535 would therefore require the invalid local port 65536. + */ + if (stn_pstRotatorPort < 1 || stn_pstRotatorPort > 65534) { this.stn_pstRotatorPort = 12000; } else { this.stn_pstRotatorPort = stn_pstRotatorPort; diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index 4b5c1e5..9eceebf 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -9402,7 +9402,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL txtFld_station_pstRotatorPort.getText().trim() ); - if (configuredPort < 1 || configuredPort > 65535) { + if (configuredPort < 1 || configuredPort > 65534) { throw new NumberFormatException(); } @@ -9412,7 +9412,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } catch (NumberFormatException exception) { showUserInputErrorWindow( "\"" + txtFld_station_pstRotatorPort.getText() - + "\" is not a valid UDP port. Enter a value between 1 and 65535." + + "\" is not a valid UDP port. Enter a value between 1 and 65534. PSTRotator reports its position on the following UDP port." ); } @@ -9523,7 +9523,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } catch (NumberFormatException exception) { showUserInputErrorWindow( "\"" + stn_txtServerPort.getText() - + "\" is not a valid TCP port. Enter a value between 1 and 65535." + + "\" is not a valid TCP port. Enter a value between 1 and 65534. PSTRotator reports its position on the following UDP port." ); stn_txtServerPort.setText( @@ -10249,7 +10249,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL showUserInputErrorWindow( "\"" + txtFld_asUDPPortInt.getText() + "\" is not a valid UDP port. " - + "Enter a value between 1 and 65535." + + "Enter a value between 1 and 65534. PSTRotator reports its position on the following UDP port." ); txtFld_asUDPPortInt.setText( diff --git a/udpReaderBackup.txt b/udpReaderBackup.txt index c7743b6..a5193de 100644 --- a/udpReaderBackup.txt +++ b/udpReaderBackup.txt @@ -2389,4 +2389,5 @@ DJ8MS;Tor_70cm;JO54UC;StringProperty [value: null]; wkd true; wkd144 false; wkd4 DK0MM;Jens/Alex;JN49IU;StringProperty [value: 432.305]; wkd true; wkd144 false; wkd432true; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; 2: 144/432 MHz SM6VTZ;Chris .135;JO58UJ;StringProperty [value: 144.135]; wkd true; wkd144 true; wkd432false; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; 2: 144/432 MHz SM6VTZ;Chris .135;JO58UJ;StringProperty [value: 432.135]; wkd true; wkd144 true; wkd432true; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; 2: 144/432 MHz -LA0BY;Stefan @ hilltop;JO59IX;StringProperty [value: 144.062]; wkd true; wkd144 true; wkd432false; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; 2: 144/432 MHz \ No newline at end of file +LA0BY;Stefan @ hilltop;JO59IX;StringProperty [value: 144.062]; wkd true; wkd144 true; wkd432false; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; 2: 144/432 MHz +LA0BY;null;JO49ML;StringProperty [value: null]; wkd true; wkd144 false; wkd432true; wkd1240false; wkd2300false; wkd3400false; wkd5600false; wkd10Gfalse ; null \ No newline at end of file diff --git a/website/src/features/automatic-replies.md b/website/src/features/automatic-replies.md new file mode 100644 index 0000000..f49ebcf --- /dev/null +++ b/website/src/features/automatic-replies.md @@ -0,0 +1,97 @@ +--- +title: Automatic Private Replies +icon: ↩️ +category: ON4KST Chat +since: "1.25" +summary: Answer repeated private messages or QRG requests without losing the complete callsign, chat category or protection against automatic reply loops. +description: KST4Contest can send a predefined answer to incoming private messages and provide the QRG belonging to the category in which a request was received. +tagsList: + - ON4KST + - automatic reply + - private message + - QRG + - sked request + - dual chat +related: + - dual-chat + - macros + - sked-reminder +--- + +## Why use an automatic reply? + +Not every station visible in the ON4KST chat is taking part in the current contest. Some operators may be monitoring activity, testing their station or simply remaining logged in while not accepting skeds. + +That distinction is not always checked before requests are sent. During a busy contest, the same station may therefore receive several similar private messages and have to enter the same refusal repeatedly. + +KST4Contest can provide that answer automatically while leaving the incoming message visible. + +## Two separate reply functions + +The general automatic reply sends one configured text in response to an incoming private message. A practical example is: + +```text +Sri, I am not taking part in this contest. No skeds. +``` + +The QRG reply serves a different purpose. It recognises common questions such as: + +```text +qrg? +freq? +pse qrg +``` + +and answers with the QRG belonging to the chat category in which the request was received: + +```text +[KST4C Automsg] QRG is: 144.300.00 +``` + +When both functions are enabled, a recognised QRG request receives the QRG reply only. It does not additionally trigger the general answer. + +## Category and callsign remain part of the message + +An automatic reply is addressed to the complete callsign of the sender, including any visible suffix. It is also sent through the same chat category as the incoming message. + +A request from `CALLSIGN-70` is therefore not silently redirected to `CALLSIGN`, and a message received through the microwave chat is not answered through the primary VHF/UHF category. + +This is particularly important in station setups which use separate suffixes for different bands or operating positions. + +## Missing information is not useful information + +KST4Contest sends a QRG reply only when a QRG is available for the relevant category. It does not produce an answer containing only: + +```text +QRG is: +``` + +A general answer must likewise contain actual text and must not contain characters which would break the ON4KST protocol frame. + +Rejecting these replies locally is intentional. An automatic response which contains no usable information has saved nobody any work. + +## Preventing loops and repeated answers + +Every automatic reply contains: + +```text +[KST4C Automsg] +``` + +Messages which already contain this prefix are not answered automatically. Two KST4Contest clients therefore do not continue replying to each other. + +A shared two-minute cooldown additionally applies to the general and QRG-specific functions. It is tracked separately for each complete callsign and chat category. + +A rejected reply does not start the cooldown. If a QRG was missing and is entered afterwards, the next request can be answered immediately. + +## Operational limits + +The function reacts to incoming text. It does not decide whether the sender's request was reasonable, whether a sked could be possible later or whether the configured refusal still reflects the current operating status. + +The general reply should therefore be enabled only while its text remains correct. A message which says that the station is not participating becomes misleading if the operator starts working the contest half an hour later. + +In plain terms: automatic replies remove repetitive typing. They do not take over the conversation. + +[Read the complete configuration and recognised QRG requests in the manual.](/manual/en/configuration/#messagehandling-settings-from-v125) + +[Read how two chat categories and suffixed callsigns are kept separate.](/features/dual-chat/) \ No newline at end of file diff --git a/website/src/features/rotator-control.md b/website/src/features/rotator-control.md new file mode 100644 index 0000000..ff3c264 --- /dev/null +++ b/website/src/features/rotator-control.md @@ -0,0 +1,77 @@ +--- +title: PSTRotator Control +icon: 🧭 +category: Station Control +since: "1.31" +summary: Point the antenna at a selected chat station and use the azimuth reported by PSTRotator throughout the KST4Contest operating context. +description: KST4Contest calculates the direction of a selected station, sends it to PSTRotator and uses the reported antenna position for filters, priorities, timelines and map displays. +tagsList: + - PSTRotator + - antenna rotator + - azimuth + - QTF + - UDP + - SPID +related: + - priority-score + - timeline + - airscout +--- + +## Why connect the rotator to the chat client? + +A station selected in the chat already has a locator and a calculated QTF. Entering the same direction manually into another application adds another small task at precisely the point where the operator is preparing a contact. + +KST4Contest can send that direction directly to PSTRotator. + +![PSTRotator control for the selected station](/manual/assets/pstrotator_turn_antenna.png) + +## Sending a direction + +Press **Turn ant1 to …** in the Further Info section of the selected station. + +KST4Contest: + +1. calculates the azimuth from both station locators; +2. disables PSTRotator tracking mode; +3. sends the target as an integer azimuth; and +4. receives the current position reported by PSTRotator. + +The function uses UDP. PSTRotator receives commands on the configured control port and reports its position on the following port. + +With the default control port `12000`, KST4Contest therefore listens on `12001`. + +## One position, several consumers + +The reported azimuth becomes the current QTF in KST4Contest. The value is subsequently used by: + +- the direction filter; +- direction-opportunity detection; +- the Priority Score; +- the antenna sector on the map; +- the AP and sked timeline; and +- the `MYQTF` message variable. + +This shared use is intentional. The antenna should not be shown pointing in one direction while the filters and candidate calculations continue to assume another. + +## SPID compatibility check + +Some SPID configurations occasionally fail to react to the first direction command. + +KST4Contest checks the reported position after two seconds. If no movement was reported and the target has not been reached, it sends one compatibility sequence through `0°` and then repeats the intended target. + +The check runs in the background and does not block the user interface. + +## Limits of the integration + +The current interface controls and evaluates azimuth only. It does not provide elevation tracking. + +The reported QTF describes what PSTRotator returns. Whether the antenna mechanically reaches that exact position still depends on the rotor, controller, calibration and configured offsets. + +UDP also provides no delivery confirmation. Correct host, port, return port and firewall settings remain necessary. + +In plain terms: KST4Contest removes the repeated transfer of a direction from one window to another. It does not turn an uncalibrated rotor into a calibrated one. + +[Read the complete setup and port assignment in the manual.](/manual/en/configuration/#pstrotator-settings-from-v131-fully-configurable-from-v140) + +[Open the PSTRotator website.](https://www.pstrotator.com/) \ No newline at end of file