From 9ce7cf3b69d8047e2e82bee26e0b75fcf5be5b3e Mon Sep 17 00:00:00 2001 From: rouggy Date: Sun, 6 Sep 2026 23:01:08 +0200 Subject: [PATCH] feat: a typed watering hole carries its mode; Yaesu RTTY sideband MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit His log settles the 28.074 case: "SetCATFrequency 28.074 MHz" and the state still reads mode=USB — nothing sent a mode, so the rig simply stayed where it was. A spot click has always carried one; a frequency typed by hand carried none. It now uses the same table and the same tolerance as a spot (±3 kHz of a known FT8/FT4/JS8 frequency), and only towards the digital modes: tuning AWAY from one leaves the mode alone, because there the frequency says nothing about what the operator means. RTTY on Yaesu is a choice the log cannot make: ADIF records "RTTY" and the rig has MD06 (RTTY-L) and MD09 (RTTY-U). The older lower sideband stays the default and a station whose FSK controller wants the upper one says so once in Settings → CAT. --- app.go | 12 +++++++++-- changelog.json | 8 ++++++-- frontend/src/App.tsx | 25 ++++++++++++++++++++++- frontend/src/components/SettingsModal.tsx | 13 +++++++++++- frontend/src/lib/i18n.tsx | 4 ++-- frontend/wailsjs/go/models.ts | 2 ++ internal/cat/yaesu.go | 25 +++++++++++++++++++---- internal/cat/yaesu_test.go | 21 ++++++++++++++++++- 8 files changed, 97 insertions(+), 13 deletions(-) diff --git a/app.go b/app.go index 505ee12..84c46e4 100644 --- a/app.go +++ b/app.go @@ -157,6 +157,7 @@ const ( // the rule since is that nothing a backend does may be steered by another // backend's setting. keyCATYaesuLowLines = "cat.yaesu.low_dtr_rts" // deassert DTR/RTS on connect + keyCATYaesuRTTYUSB = "cat.yaesu.rtty_usb" // set RTTY on USB rather than the older LSB keyCATKenwoodLowLines = "cat.kenwood.low_dtr_rts" // deassert DTR/RTS on connect keyCATKenwoodDataMode = "cat.kenwood.data_mode" // data modes → "usb" | "data" (MD6) | "keep" keyCATIcomPort = "cat.icom.port" // Icom USB CI-V serial port (e.g. COM5) @@ -510,7 +511,11 @@ type CATSettings struct { // Per-backend: deassert DTR and RTS after opening the CAT port, for // interfaces that read either line as PTT. Off by default: lowering them // stops some USB-serial interfaces transmitting at all. - YaesuLowLines bool `json:"yaesu_low_lines"` + YaesuLowLines bool `json:"yaesu_low_lines"` + // YaesuRTTYUSB: ADIF says "RTTY" and Yaesu has both sidebands, so the rig + // cannot be driven from the logged mode alone. Off = RTTY-L, the older + // convention. + YaesuRTTYUSB bool `json:"yaesu_rtty_usb"` KenwoodLowLines bool `json:"kenwood_low_lines"` IcomPort string `json:"icom_port"` // Icom USB CI-V serial port (e.g. COM5) IcomBaud int `json:"icom_baud"` // Icom CI-V baud (default 115200) @@ -8317,7 +8322,7 @@ func (a *App) GetCATSettings() (CATSettings, error) { if a.settings == nil { return CATSettings{Backend: "omnirig", OmniRigNum: 1, PollMs: 250}, fmt.Errorf("db not initialized") } - m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDVKDax, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATXieguPTTLine, keyCATYaesuPort, keyCATYaesuBaud, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATKenwoodLink, keyCATYaesuLowLines, keyCATKenwoodLowLines, keyCATKenwoodDataMode, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPttHotkeyEnabled, keyCATPttHotkey, keyCATPttHotkeyToggle, keyCATPollMs, keyCATDelayMs, keyCATOffsetOn, keyCATOffsetHz, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort, keyCATShareProto, keyCATShareTCIPort, keyCATDigiUSB) + m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDVKDax, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATXieguPTTLine, keyCATYaesuPort, keyCATYaesuBaud, keyCATYaesuRTTYUSB, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATKenwoodLink, keyCATYaesuLowLines, keyCATKenwoodLowLines, keyCATKenwoodDataMode, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPttHotkeyEnabled, keyCATPttHotkey, keyCATPttHotkeyToggle, keyCATPollMs, keyCATDelayMs, keyCATOffsetOn, keyCATOffsetHz, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort, keyCATShareProto, keyCATShareTCIPort, keyCATDigiUSB) if err != nil { return CATSettings{}, err } @@ -8343,6 +8348,7 @@ func (a *App) GetCATSettings() (CATSettings, error) { KenwoodLink: kenwoodLinkOr(m[keyCATKenwoodLink], m[keyCATKenwoodHost]), KenwoodBaud: 9600, YaesuLowLines: m[keyCATYaesuLowLines] == "1", + YaesuRTTYUSB: m[keyCATYaesuRTTYUSB] == "1", KenwoodLowLines: m[keyCATKenwoodLowLines] == "1", KenwoodDataMode: m[keyCATKenwoodDataMode], IcomPort: m[keyCATIcomPort], @@ -8556,6 +8562,7 @@ func (a *App) SaveCATSettings(s CATSettings) error { keyCATKenwoodLink: kenwoodLinkOr(s.KenwoodLink, s.KenwoodHost), keyCATKenwoodBaud: strconv.Itoa(s.KenwoodBaud), keyCATYaesuLowLines: b01(s.YaesuLowLines), + keyCATYaesuRTTYUSB: b01(s.YaesuRTTYUSB), keyCATKenwoodLowLines: b01(s.KenwoodLowLines), keyCATKenwoodDataMode: strings.ToLower(strings.TrimSpace(s.KenwoodDataMode)), keyCATIcomPort: strings.TrimSpace(s.IcomPort), @@ -16283,6 +16290,7 @@ func (a *App) reloadCAT() { // (a rig file that hides the VFO, a Freq property meaning A on one model // and B on another); talking to the radio directly removes it. yz := cat.NewYaesu(s.YaesuPort, s.YaesuBaud, s.DigitalDefault) + yz.SetRTTYUpper(s.YaesuRTTYUSB) yz.SetLowerLines(s.YaesuLowLines) a.cat.Start(yz) case "kenwood", "elecraft": diff --git a/changelog.json b/changelog.json index dc6dd3d..3b68972 100644 --- a/changelog.json +++ b/changelog.json @@ -16,7 +16,9 @@ "ADIF export: a record is written on one line again. ADDRESS is a multi-line field by the standard and callbooks and other loggers fill it that way — “Kabul”, four blank lines, “Afghanistan” — and OpsLog wrote it out as it was, so a record ran down a dozen lines with the next apparently starting in the middle of the page. Line breaks inside a value are now joined with a comma, which is how an address reads on one line anyway. The files were always valid (ADIF counts bytes); they were unreadable.", "Club Log uploads are no longer refused as “not configured”. The check added for services with no credentials demanded a Club Log API key, which nobody has ever set — OpsLog carries its own application key — so an operator whose live upload had worked for months was turned away when sending QSOs by hand. Each service’s requirements now live beside the uploader that enforces them, and the message names the fields that are actually missing.", "FT map: the callsign, square and report show on hover again. The invisible circle that catches the clicks sits on top of the dot, so it takes the hover too — and the label was bound only to the dot underneath, which left the map silent from the moment the stations became clickable.", - "Yaesu CAT now drives the older radios. The FTDX10, FT-991A, FT-891 and FT-710 write a frequency in nine digits; everything before them — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — writes eight and answers a nine-digit command with a rejection, which is what an FTDX3000 owner saw: every FA refused and a radio that would not follow. The width is taken from the rig’s own reply rather than from a table of models, so a set is in the format that radio speaks — including models this backend has never heard of." + "Yaesu CAT now drives the older radios. The FTDX10, FT-991A, FT-891 and FT-710 write a frequency in nine digits; everything before them — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — writes eight and answers a nine-digit command with a rejection, which is what an FTDX3000 owner saw: every FA refused and a radio that would not follow. The width is taken from the rig’s own reply rather than from a table of models, so a set is in the format that radio speaks — including models this backend has never heard of.", + "Typing a digital watering hole sets the mode with it. A spot click has always carried one; a frequency typed by hand carried none, so the rig stayed in SSB on 28.074 while the operator waited for decodes. Same table and same tolerance as a spot (±3 kHz of a known FT8/FT4/JS8 frequency), only towards the digital modes: tuning away from one leaves the mode alone, because there the frequency says nothing about what you mean to do.", + "Yaesu: RTTY can be set on USB (Settings → CAT). ADIF records only “RTTY” and the rig has both sidebands, so the log cannot answer for it — the older RTTY-L stays the default, and a station whose FSK controller wants the upper one says so once." ], "fr": [ "[NEW] Les couleurs de mise en évidence WSJT-X / JTDX sont au choix (Réglages → UDP), une par verdict — watchlist, nouveau DXCC, nouvelle bande, contactée. Seul le fond se règle : la couleur du texte en est déduite, pour qu’une couleur choisie ne revienne jamais illisible dans la fenêtre du décodeur. L’option « griser les stations déjà contactées » garde sa raison d’être — elle décide SI les doublons sont marqués, pas de quelle couleur — et s’appelle désormais « Marquer les stations déjà contactées ».", @@ -32,7 +34,9 @@ "Export ADIF : un enregistrement tient de nouveau sur une ligne. ADDRESS est un champ multiligne selon la norme, et les callbooks comme les autres logiciels le remplissent ainsi — « Kabul », quatre lignes vides, « Afghanistan » — qu’OpsLog recopiait tel quel : un enregistrement s’étalait sur une douzaine de lignes, le suivant semblant commencer au milieu de la page. Les retours à la ligne dans une valeur sont désormais réunis par une virgule, ce qui est de toute façon la façon de lire une adresse sur une ligne. Les fichiers étaient valides (l’ADIF compte les octets) ; ils étaient illisibles.", "Les envois vers Club Log ne sont plus refusés comme « non configuré ». Le contrôle ajouté pour les services sans identifiants réclamait une clé API Club Log que personne n’a jamais saisie — OpsLog embarque la sienne — et un opérateur dont l’envoi automatique fonctionnait depuis des mois se voyait éconduit au moment d’envoyer des QSO à la main. Les exigences de chaque service vivent désormais à côté du code qui les applique, et le message nomme les champs réellement manquants.", "Carte FTx : l’indicatif, le locator et le report réapparaissent au survol. Le cercle invisible qui capte les clics est au-dessus du point, donc il capte aussi le survol — et l’étiquette n’était liée qu’au point du dessous, ce qui rendait la carte muette dès que les stations sont devenues cliquables.", - "Le CAT Yaesu pilote désormais les postes plus anciens. FTDX10, FT-991A, FT-891 et FT-710 écrivent une fréquence sur neuf chiffres ; tout ce qui précède — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — l’écrit sur huit et rejette une commande à neuf chiffres. C’est ce que voyait un possesseur de FTDX3000 : chaque FA refusée et une radio qui ne suivait pas. Le format est pris dans la réponse du poste plutôt que dans une table de modèles : l’envoi part donc dans la langue de cette radio-là, y compris pour des modèles que ce backend ne connaît pas." + "Le CAT Yaesu pilote désormais les postes plus anciens. FTDX10, FT-991A, FT-891 et FT-710 écrivent une fréquence sur neuf chiffres ; tout ce qui précède — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — l’écrit sur huit et rejette une commande à neuf chiffres. C’est ce que voyait un possesseur de FTDX3000 : chaque FA refusée et une radio qui ne suivait pas. Le format est pris dans la réponse du poste plutôt que dans une table de modèles : l’envoi part donc dans la langue de cette radio-là, y compris pour des modèles que ce backend ne connaît pas.", + "Taper une fréquence d’appel numérique règle le mode avec elle. Un clic sur un spot en portait un depuis toujours ; une fréquence tapée à la main n’en portait aucun, si bien que le poste restait en SSB sur 28.074 pendant qu’on attendait les décodages. Même table et même tolérance qu’un spot (±3 kHz d’une fréquence FT8/FT4/JS8 connue), et seulement vers les modes numériques : en s’en éloignant le mode n’est pas touché, car là la fréquence ne dit rien de ce qu’on veut faire.", + "Yaesu : le RTTY peut être placé en USB (Réglages → CAT). L’ADIF n’enregistre que « RTTY » et le poste a les deux bandes latérales : le log ne peut pas répondre à sa place. Le RTTY-L ancien reste par défaut, et une station dont l’interface FSK veut la supérieure le dit une fois." ] }, { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 58484d4..6a2d1e5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5926,7 +5926,29 @@ export default function App() { const mhz = parseFloat(mhzStr); if (!Number.isFinite(mhz) || mhz < 0.1 || mhz > 3000) return; noteManualEdit(); - SetCATFrequency(Math.round(mhz * 1_000_000)).catch(() => {}); + const hz = Math.round(mhz * 1_000_000); + SetCATFrequency(hz).catch(() => {}); + tuneModeForWateringHole(hz); + }; + + // 28.074 IS FT8, and typing it says so. + // + // A spot click has always carried a mode; a frequency typed by hand carried + // none, so the rig stayed in whatever it was — an FTDX3000 landing on 28.074 + // in USB while the operator waited for decodes. Nobody tunes a watering hole + // to listen to it in SSB, and this is the same table and the same tolerance a + // spot click is judged with (±3 kHz of a known digital frequency). + // + // Only when it actually changes something, and only towards the digital + // modes: tuning away from 28.074 leaves the mode alone, because there the + // frequency says nothing about what the operator means to do. + const tuneModeForWateringHole = (hz: number) => { + const m = inferDigitalMode(hz); + if (!m || m === mode) return; + setMode(m); + applyModePreset(m); + if (catState.enabled && catState.connected && !locks.mode) SetCATMode(m).catch(() => {}); + ConfigureDecoderMode(m).catch(() => {}); }; // Carry out what was typed in the call field. Bands go through the ordinary // band change, so the antennas, the power table and the outbound integrations @@ -5944,6 +5966,7 @@ export default function App() { const b = bandForMHz(hz / 1_000_000); if (b) setBand(b); if (catState.enabled && catState.connected) SetCATFrequency(hz).catch(() => {}); + tuneModeForWateringHole(hz); showToast((hz / 1_000_000).toFixed(3) + ' MHz'); }; diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx index 54bf451..112074c 100644 --- a/frontend/src/components/SettingsModal.tsx +++ b/frontend/src/components/SettingsModal.tsx @@ -1628,7 +1628,7 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged const [icomCustom, setIcomCustom] = useState(false); const [catCfg, setCatCfg] = useState({ enabled: false, backend: 'omnirig', omnirig_rig: 1, omnirig_vfo: '', flex_host: '', flex_port: 4992, flex_spots: false, flex_decode_spots: false, flex_decode_secs: 120, flex_dvk_dax: false, - yaesu_port: '', yaesu_baud: 38400, yaesu_low_lines: false, kenwood_low_lines: false, kenwood_port: '', kenwood_baud: 9600, kenwood_host: '', kenwood_link: 'usb', kenwood_data_mode: 'usb', xiegu_port: '', xiegu_baud: 19200, xiegu_addr: 0x70, xiegu_ptt_line: '', + yaesu_port: '', yaesu_baud: 38400, yaesu_low_lines: false, yaesu_rtty_usb: false, kenwood_low_lines: false, kenwood_port: '', kenwood_baud: 9600, kenwood_host: '', kenwood_link: 'usb', kenwood_data_mode: 'usb', xiegu_port: '', xiegu_baud: 19200, xiegu_addr: 0x70, xiegu_ptt_line: '', icom_port: '', icom_baud: 115200, icom_addr: 0x98, icom_net_host: '', icom_net_user: '', icom_net_pass: '', icom_net_audio: false, tci_host: '', tci_port: 40001, tci_spots: false, poll_ms: 250, delay_ms: 0, offset_on: false, offset_hz: 0, digital_default: 'FT8', share_enabled: false, share_port: 4532, share_proto: 'rigctl', share_tci_port: 40001, @@ -3532,6 +3532,17 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged {t('cat.lowerLinesHint')} + {/* ADIF records "RTTY" and nothing more, and the rig has two + sidebands to put it on — so this is the operator's answer, not + something the log can supply. */} +
+ + {t('cat.yaesuRttyUsbHint')} +
)} {['icom', 'xiegu', 'kenwood', 'elecraft'].includes(catCfg.backend) && ( diff --git a/frontend/src/lib/i18n.tsx b/frontend/src/lib/i18n.tsx index ec79ed9..6dd1441 100644 --- a/frontend/src/lib/i18n.tsx +++ b/frontend/src/lib/i18n.tsx @@ -410,7 +410,7 @@ const en: Dict = { 'extsvc.hint': 'Upload logged QSOs to online logbooks. Each service uploads automatically on a new QSO when enabled; timing is per-service (immediate, or a 1–2 min delay so a mis-logged QSO can still be fixed first).', 'hw.motorTxInhibit': 'Inhibit transmission while the antenna is moving', 'hw.motorTxInhibitHint': 'Blocks the FlexRadio from transmitting while the elements move (needs FlexRadio in API mode + this antenna enabled). No effect with other radios.', 'hw.motorAntenna': 'Ultrabeam / Steppir', 'hw.motorEnable': 'Enable antenna control', 'hw.motorType': 'Antenna type', 'hw.motorTransport': 'Connection', 'hw.motorTcp': 'Network (TCP)', 'hw.motorSerial': 'Serial (COM)', 'hw.motorCom': 'Serial port', 'hw.motorComUb': '(2nd port of the USB cable)', 'hw.motorBaud': 'Baud', 'hw.steppirHint': 'SteppIR controllers are RS-232 serial (the DATA OUT DB9 port). Serial = a USB↔RS-232 (FTDI) adapter, shown as a COM port. Network = a serial-to-Ethernet bridge (as for the Ultrabeam).', 'hw.steppirRange': 'Tunable range', 'hw.steppirRangeHint': "The SteppIR's frequency coverage. On a band outside this range (e.g. 30 m on a 20 m–6 m SteppIR) OpsLog won't try to tune the antenna and won't inhibit transmission. Default 13–54 MHz (20 m–6 m); widen the low edge (e.g. 6) for a 40 m-equipped SteppIR.", 'hw.motorBands': 'Covered bands', 'hw.motorStep': 'Re-tune step', 'hw.motorBandFreqHint': 'Frequency each band button tunes the antenna to (kHz). Leave empty for the default shown.', 'hw.motorFollow': 'Follow rig frequency (auto-tune the antenna)', 'hw.ultrabeam': 'Antenna (Ultrabeam)', 'hw.audioVoice': 'Audio devices & voice keyer', // CAT panel body - 'cat.radioMyRig': 'MY_RIG', 'cat.radioMyRigPh': 'e.g. IC-7610 + PGXL', 'cat.radioMyRigHint': 'Written into MY_RIG on every QSO made with this radio, ahead of the per-band station in Operating conditions — that one says what you planned to use, this one says which radio is keying. Leave it empty to keep the old behaviour.', 'cat.radio': 'Radio', 'cat.radioNamePh': 'Name (e.g. FTDX10)', 'cat.radioAddHint': 'Add a radio, copied from this one', 'cat.radioRemoveHint': 'Remove this radio', 'cat.radioHint': 'Everything below configures the selected radio. Switch between them from the CAT button in the status bar — the logbook, the callsign and the rest of the station stay as they are.', 'cat.enable': 'Enable CAT', 'cat.brand': 'Radio', 'cat.backend': 'Backend', 'cat.optOmnirig': 'OmniRig', 'cat.optFlex': 'FlexRadio (API)', 'cat.share': 'Share CAT with other programs', 'cat.shareHint': 'Another program then reaches the radio through OpsLog — with every backend, not only the native ones. For Hamlib pick the rig model "Hamlib NET rigctl" and enter 127.0.0.1:4532; for TCI point the program at 127.0.0.1:40001.', 'cat.shareProto': 'Protocol', 'cat.shareRigctl': 'Hamlib NET rigctl — WSJT-X, JTDX, MSHV, Log4OM', 'cat.shareTci': 'TCI — Expert Electronics', 'cat.shareTciClash': 'The CAT backend is TCI too: ExpertSDR is probably already using port 40001 on this PC. Give the server another port, or share over Hamlib instead.', 'cat.sharePort': 'Sharing port', 'cat.pttKey': 'Enable PTT hotkey', 'cat.pttKeyPress': 'Press a key…', 'cat.pttKeyNone': 'Click to set a key', 'cat.pttKeyClear': 'Clear', 'cat.pttKeyToggle': 'Toggle mode (press to key, press again to unkey)', 'cat.pttKeyHint': 'While OpsLog is focused, this key keys the transmitter — held down by default (release to stop), or latched in toggle mode. It uses the Audio → PTT method (CAT / RTS / DTR), falling back to CAT keying. Pick a key you never type while logging (e.g. Pause, ScrollLock, or a footswitch mapped to one) — OpsLog swallows it so it never lands in a field.', 'cat.optXiegu': 'Xiegu (USB)', 'cat.xieguPort': 'Xiegu CAT port', 'cat.xieguBaudHint': 'Must match the radio menu (G90 default: 19200).', 'cat.xieguAddrHint': 'Factory CI-V address of the G90/X6100 family: 0x70.', 'cat.xieguPTTLine': 'How the rig is keyed', 'cat.xieguPTTCiv': 'CI-V command', 'cat.xieguPTTHint': 'A G90 does not transmit on the CI-V command: interfaces like the DE-19 key it on RTS or DTR. Pick the line yours uses \u2014 it is also what lets WSJT-X transmit through the shared CAT link.', 'cat.optYaesu': 'Yaesu (USB)', 'cat.optKenwood': 'Kenwood (USB, network)', 'cat.optElecraft': 'Elecraft K3/K4 (USB, network)', 'cat.elecraftHint': 'Digital modes automatically use DATA A (MD6+DT0) — the sub-mode FT8 audio needs.', 'cat.civTrace': 'Log the CAT protocol', 'cat.civTraceHint': 'Writes every CAT frame to and from the radio into the log \u2014 CI-V as hex, Kenwood as the text it exchanges. For reporting a rig that answers oddly \u2014 a button that does the wrong thing, a frequency that jumps. Session only: it is a diagnostic, and it makes the log large.', 'cat.kenwoodPort': 'Kenwood COM port', 'cat.kenwoodPortHint': 'The rig\u2019s CAT/USB serial port (TS-590, TS-890, TS-990, TS-2000, and Elecraft K3/K4 which speak the same dialect).', 'cat.kenwoodBaudHint': 'Must match MENU on the radio: a TS-590 leaves the factory at 9600, a TS-890 at 115200.', 'cat.kwLink': 'Connection', 'cat.kwLinkBridge': 'RS-232 to Ethernet (serial bridge)', 'cat.kwLinkNative': 'The radio\u2019s own network protocol', 'cat.kwLinkNativeHint': 'Not supported yet. It is a session with its own framing and login, not the CAT byte stream over a socket, so it has to be implemented per radio — and against one. Use USB, or a serial-to-Ethernet bridge, until then.', 'cat.kwLinkUsb': 'USB / serial', 'cat.kwLinkNet': 'Network', 'cat.kenwoodHostHint': 'The network option carries the SAME CAT commands over TCP instead of a cable: a serial-over-network bridge (ser2net, an Ethernet-serial adapter, a Raspberry Pi in the shack), or a radio that exposes its raw CAT port. It is NOT Hamlib — port 4532 is rigctld, a different protocol, and it is what OpsLog itself SERVES under \u201cShare CAT\u201d.', 'cat.kenwoodHost': 'Or over the network (host:port)', 'cat.yaesuPort': 'Yaesu CAT port', 'cat.yaesuPortHint': 'The rig CAT port — on an FTDX10/FTDX101 over USB this is the ENHANCED COM port, not the standard one.', 'cat.yaesuBaudHint': 'Must match the radio menu (FTDX10/FTDX101 default: 38400).', 'cat.lowerLines': 'Lower the DTR and RTS lines on connect', 'cat.lowerLinesHint': 'If your radio is always on TX, tick this.', 'cat.kwDataMode': 'Data modes (FT8/PSK…) use', 'cat.kwDataUsb': 'USB', 'cat.kwDataMd6': 'DATA A — MD6+DT0 (Elecraft K3/K4)', 'cat.kwDataKeep': 'Leave the rig’s mode unchanged', 'cat.kwDataHint': 'What OpsLog sets on the rig for a data mode. No single command fits every rig: an Elecraft K3/K4 wants DATA (MD6); a TS-590SG/TS-990S data mode is a USB modifier set on the rig, so pick USB or, safest, "Leave unchanged" and switch the rig to DATA yourself. On MD6 a plain Kenwood (TS-590/990) would land on FSK/RTTY — do not use it there.', 'cat.optIcom': 'Icom (CI-V USB)', 'cat.optIcomNet': 'Icom (CI-V network)', 'cat.optTci': 'TCI', 'flxpw.title': 'FlexRadio \u2014 power per band and mode', 'flxpw.hint': 'TX power applied when the band or mode changes. Leave a box empty to leave the power alone.', 'flxpw.band': 'Band', 'flxb.title': 'FlexRadio \u2014 per-band antennas and power', 'flxb.hint': 'Antennas are applied when the band changes; power when the band or the mode changes. Leave a power box empty to leave it alone.', 'flxb.rxAnt': 'RX antenna', 'flxb.txAnt': 'TX antenna', 'flxb.noRadio': 'No antennas reported yet \u2014 connect the FlexRadio, then reopen this panel.', 'flxpw.phone': 'Phone', 'flxpw.digi': 'Digital', 'flxpw.noBands': 'No bands configured \u2014 add them in Lists \u2192 Bands.', 'flxpw.saved': 'Saved', + 'cat.radioMyRig': 'MY_RIG', 'cat.radioMyRigPh': 'e.g. IC-7610 + PGXL', 'cat.radioMyRigHint': 'Written into MY_RIG on every QSO made with this radio, ahead of the per-band station in Operating conditions — that one says what you planned to use, this one says which radio is keying. Leave it empty to keep the old behaviour.', 'cat.radio': 'Radio', 'cat.radioNamePh': 'Name (e.g. FTDX10)', 'cat.radioAddHint': 'Add a radio, copied from this one', 'cat.radioRemoveHint': 'Remove this radio', 'cat.radioHint': 'Everything below configures the selected radio. Switch between them from the CAT button in the status bar — the logbook, the callsign and the rest of the station stay as they are.', 'cat.enable': 'Enable CAT', 'cat.brand': 'Radio', 'cat.backend': 'Backend', 'cat.optOmnirig': 'OmniRig', 'cat.optFlex': 'FlexRadio (API)', 'cat.share': 'Share CAT with other programs', 'cat.shareHint': 'Another program then reaches the radio through OpsLog — with every backend, not only the native ones. For Hamlib pick the rig model "Hamlib NET rigctl" and enter 127.0.0.1:4532; for TCI point the program at 127.0.0.1:40001.', 'cat.shareProto': 'Protocol', 'cat.shareRigctl': 'Hamlib NET rigctl — WSJT-X, JTDX, MSHV, Log4OM', 'cat.shareTci': 'TCI — Expert Electronics', 'cat.shareTciClash': 'The CAT backend is TCI too: ExpertSDR is probably already using port 40001 on this PC. Give the server another port, or share over Hamlib instead.', 'cat.sharePort': 'Sharing port', 'cat.pttKey': 'Enable PTT hotkey', 'cat.pttKeyPress': 'Press a key…', 'cat.pttKeyNone': 'Click to set a key', 'cat.pttKeyClear': 'Clear', 'cat.pttKeyToggle': 'Toggle mode (press to key, press again to unkey)', 'cat.pttKeyHint': 'While OpsLog is focused, this key keys the transmitter — held down by default (release to stop), or latched in toggle mode. It uses the Audio → PTT method (CAT / RTS / DTR), falling back to CAT keying. Pick a key you never type while logging (e.g. Pause, ScrollLock, or a footswitch mapped to one) — OpsLog swallows it so it never lands in a field.', 'cat.optXiegu': 'Xiegu (USB)', 'cat.xieguPort': 'Xiegu CAT port', 'cat.xieguBaudHint': 'Must match the radio menu (G90 default: 19200).', 'cat.xieguAddrHint': 'Factory CI-V address of the G90/X6100 family: 0x70.', 'cat.xieguPTTLine': 'How the rig is keyed', 'cat.xieguPTTCiv': 'CI-V command', 'cat.xieguPTTHint': 'A G90 does not transmit on the CI-V command: interfaces like the DE-19 key it on RTS or DTR. Pick the line yours uses \u2014 it is also what lets WSJT-X transmit through the shared CAT link.', 'cat.optYaesu': 'Yaesu (USB)', 'cat.optKenwood': 'Kenwood (USB, network)', 'cat.optElecraft': 'Elecraft K3/K4 (USB, network)', 'cat.elecraftHint': 'Digital modes automatically use DATA A (MD6+DT0) — the sub-mode FT8 audio needs.', 'cat.civTrace': 'Log the CAT protocol', 'cat.civTraceHint': 'Writes every CAT frame to and from the radio into the log \u2014 CI-V as hex, Kenwood as the text it exchanges. For reporting a rig that answers oddly \u2014 a button that does the wrong thing, a frequency that jumps. Session only: it is a diagnostic, and it makes the log large.', 'cat.kenwoodPort': 'Kenwood COM port', 'cat.kenwoodPortHint': 'The rig\u2019s CAT/USB serial port (TS-590, TS-890, TS-990, TS-2000, and Elecraft K3/K4 which speak the same dialect).', 'cat.kenwoodBaudHint': 'Must match MENU on the radio: a TS-590 leaves the factory at 9600, a TS-890 at 115200.', 'cat.kwLink': 'Connection', 'cat.kwLinkBridge': 'RS-232 to Ethernet (serial bridge)', 'cat.kwLinkNative': 'The radio\u2019s own network protocol', 'cat.kwLinkNativeHint': 'Not supported yet. It is a session with its own framing and login, not the CAT byte stream over a socket, so it has to be implemented per radio — and against one. Use USB, or a serial-to-Ethernet bridge, until then.', 'cat.kwLinkUsb': 'USB / serial', 'cat.kwLinkNet': 'Network', 'cat.kenwoodHostHint': 'The network option carries the SAME CAT commands over TCP instead of a cable: a serial-over-network bridge (ser2net, an Ethernet-serial adapter, a Raspberry Pi in the shack), or a radio that exposes its raw CAT port. It is NOT Hamlib — port 4532 is rigctld, a different protocol, and it is what OpsLog itself SERVES under \u201cShare CAT\u201d.', 'cat.kenwoodHost': 'Or over the network (host:port)', 'cat.yaesuPort': 'Yaesu CAT port', 'cat.yaesuPortHint': 'The rig CAT port — on an FTDX10/FTDX101 over USB this is the ENHANCED COM port, not the standard one.', 'cat.yaesuBaudHint': 'Must match the radio menu (FTDX10/FTDX101 default: 38400).', 'cat.lowerLines': 'Lower the DTR and RTS lines on connect', 'cat.yaesuRttyUsb': 'RTTY on USB (RTTY-U)', 'cat.yaesuRttyUsbHint': '— ADIF records only “RTTY” and the rig has both sidebands. Off sets RTTY-L, the older convention; tick it if your FSK controller or decoder wants the upper one.', 'cat.lowerLinesHint': 'If your radio is always on TX, tick this.', 'cat.kwDataMode': 'Data modes (FT8/PSK…) use', 'cat.kwDataUsb': 'USB', 'cat.kwDataMd6': 'DATA A — MD6+DT0 (Elecraft K3/K4)', 'cat.kwDataKeep': 'Leave the rig’s mode unchanged', 'cat.kwDataHint': 'What OpsLog sets on the rig for a data mode. No single command fits every rig: an Elecraft K3/K4 wants DATA (MD6); a TS-590SG/TS-990S data mode is a USB modifier set on the rig, so pick USB or, safest, "Leave unchanged" and switch the rig to DATA yourself. On MD6 a plain Kenwood (TS-590/990) would land on FSK/RTTY — do not use it there.', 'cat.optIcom': 'Icom (CI-V USB)', 'cat.optIcomNet': 'Icom (CI-V network)', 'cat.optTci': 'TCI', 'flxpw.title': 'FlexRadio \u2014 power per band and mode', 'flxpw.hint': 'TX power applied when the band or mode changes. Leave a box empty to leave the power alone.', 'flxpw.band': 'Band', 'flxb.title': 'FlexRadio \u2014 per-band antennas and power', 'flxb.hint': 'Antennas are applied when the band changes; power when the band or the mode changes. Leave a power box empty to leave it alone.', 'flxb.rxAnt': 'RX antenna', 'flxb.txAnt': 'TX antenna', 'flxb.noRadio': 'No antennas reported yet \u2014 connect the FlexRadio, then reopen this panel.', 'flxpw.phone': 'Phone', 'flxpw.digi': 'Digital', 'flxpw.noBands': 'No bands configured \u2014 add them in Lists \u2192 Bands.', 'flxpw.saved': 'Saved', 'cat.icomNetHost': 'Rig IP / hostname', 'cat.icomNetUser': 'Network user (ID)', 'cat.icomNetPass': 'Network password', 'cat.icomNetHint': "Connects to the rig's built-in LAN server directly — no RS-BA1 or Remote Utility needed (close them first). Use the Network User1 ID/Password set in the rig's Network menu. A rig in standby is powered on automatically.", 'cat.icomNetListen': 'Play it through the speakers', 'cat.icomNetListenHint': 'Untick to keep the stream silent — recordings and the voice keyer still work. Same switch as the speaker button on the Icom console.', 'cat.icomNetAudio': 'Stream RX audio over the network (experimental)', @@ -958,7 +958,7 @@ const fr: Dict = { 'rot.enable': 'Activer le contrôle du rotator', 'rot.testOkRG': 'Connecté — le Rotator Genius a accepté la commande (rotation vers 0°).', 'rot.testOkRead': 'Connecté — le contrôleur a répondu avec son azimut. Rien n’a bougé : ce test ne fait que lire la position.', 'rot.type': 'Type de rotator', 'rot.rotatorNum': 'Rotator n°', 'rot.dual': 'Deux rotors', 'rot.rotor1': 'Rotor 1', 'rot.rotor2': 'Rotor 2', 'rot.name': 'Nom', 'rot.antenna': 'Antenne', 'rot.antenna1': 'Antenne rotor 1', 'rot.antenna2': 'Antenne rotor 2', 'rot.name1': 'Nom du rotor 1', 'rot.name2': 'Nom du rotor 2', 'rot.antStd': 'Antenne standard', 'rot.antMotor': 'Motorisée (Ultrabeam/SteppIR)', 'rot.add': 'Ajouter un rotor', 'rot.remove': 'Supprimer le rotor', 'rot.none': 'Aucun rotor configuré. Cliquez sur « Ajouter un rotor ».', 'rot.rgDual': 'Ce Rotator Genius pilote deux rotors (en ajoute un second)', 'rot.testRotor1': 'Tester rotor 1', 'rot.testRotor2': 'Tester rotor 2', 'rot.dualHint': 'Deux rotors indépendants de n\'importe quel type — par exemple un ARCO et un Rotator Genius côte à côte. Configurez chacun ci-dessous ; OpsLog affiche un sélecteur Rotor 1/2 sur le compas pour choisir celui qu\'il tourne et affiche. Réglez chaque rotor sur « Motorisée » pour que les tracés de boom/diagramme (inverse, bidirectionnel) n\'apparaissent que pour celui qui porte l\'Ultrabeam/SteppIR. (Pour un seul Rotator Genius pilotant deux rotors, réglez les deux sur Rotator Genius avec le même hôte et le rotator n° 1 et 2.)', 'rot.rgHint': 'Parle directement à un Rotator Genius 4O3A en TCP (port 9006 par défaut) — sans PstRotator. Le n° choisit lequel des deux rotators du boîtier piloter.', 'rot.arcoHint': "Parle directement à tout contrôleur réglé sur Yaesu GS-232A — sans PstRotator. microHAM ARCO : régler Config → LAN → CONTROL PROTOCOL (ou USB CONTROL PROTOCOL) sur « Yaesu GS-232A » ; en USB la vitesse est sans importance. ERC (Easy Rotor Control, ERC Mini compris) : son émulation DOIT être réglée sur GS-232 — un ERC laissé en Hy-Gain DCU-1 parle un autre jeu de commandes et ne répondra pas — puis choisir son port COM et la même vitesse que dans la configuration de l'ERC.", 'rot.dcu1Hint': "Parle le jeu de commandes Hy-Gain DCU-1 (RotorCard DXA, Idiom Press Rotor-EZ, Green Heron). Connexion via le port COM du contrôleur (un DCU-1 est à 4800 bauds ; RotorCard/Green Heron peuvent différer — reprendre la vitesse du contrôleur) ou en TCP via un pont série-sur-IP. Azimut uniquement, pas d'élévation. Nouveau pilote — signalez si votre contrôleur nécessite une autre commande ou vitesse.", 'rot.hint': "OpsLog envoie des commandes UDP à PstRotator. Active l'écouteur UDP de PstRotator (Setup → Communication → UDP) avant de tester.", 'extsvc.hint': "Envoie les QSO enregistrés vers des carnets en ligne. Chaque service upload automatiquement à chaque nouveau QSO si activé ; le délai est propre à chaque service (immédiat, ou 1–2 min pour corriger un QSO mal saisi avant).", 'hw.motorTxInhibit': "Inhiber la transmission pendant que l'antenne bouge", 'hw.motorTxInhibitHint': "Empêche le FlexRadio d'émettre pendant que les éléments bougent (nécessite le FlexRadio en API + cette antenne activée). Sans effet avec les autres radios.", 'hw.motorAntenna': 'Antenne motorisée', 'hw.motorEnable': "Activer le contrôle de l'antenne", 'hw.motorType': "Type d'antenne", 'hw.motorTransport': 'Connexion', 'hw.motorTcp': 'Réseau (TCP)', 'hw.motorSerial': 'Série (COM)', 'hw.motorCom': 'Port série', 'hw.motorComUb': '(2e port du câble USB)', 'hw.motorBaud': 'Débit', 'hw.steppirHint': "Les contrôleurs SteppIR sont en RS-232 série (port DB9 « DATA OUT »). Série = un adaptateur USB↔RS-232 (FTDI), vu comme un port COM. Réseau = un pont série-Ethernet (comme pour l'Ultrabeam).", 'hw.steppirRange': 'Plage accordable', 'hw.steppirRangeHint': "La couverture en fréquence de la SteppIR. Sur une bande hors de cette plage (p. ex. 30 m avec une SteppIR 20 m-6 m), OpsLog n'essaie pas d'accorder l'antenne et n'inhibe pas l'émission. Défaut 13-54 MHz (20 m-6 m) ; abaisse la borne basse (p. ex. 6) pour une SteppIR équipée 40 m.", 'hw.motorBands': 'Bandes couvertes', 'hw.motorStep': 'Pas de réaccord', 'hw.motorBandFreqHint': "Fréquence sur laquelle chaque bouton de bande accorde l'antenne (kHz). Laisser vide pour le défaut affiché.", 'hw.motorFollow': "Suivre la fréquence du rig (accord auto de l'antenne)", 'hw.ultrabeam': 'Antenne (Ultrabeam)', 'hw.audioVoice': 'Périphériques audio & manipulateur vocal', - 'cat.radioMyRig': 'MY_RIG', 'cat.radioMyRigPh': 'ex. IC-7610 + PGXL', 'cat.radioMyRigHint': "Inscrit dans MY_RIG sur chaque QSO fait avec cette radio, avant la station par bande des Conditions de trafic — celle-ci dit ce qui était prévu, celle-là dit quelle radio émet. Laisser vide conserve l'ancien comportement.", 'cat.radio': 'Radio', 'cat.radioNamePh': 'Nom (ex. FTDX10)', 'cat.radioAddHint': 'Ajouter une radio, copiée sur celle-ci', 'cat.radioRemoveHint': 'Supprimer cette radio', 'cat.radioHint': "Tout ce qui suit configure la radio sélectionnée. On passe de l'une à l'autre par le bouton CAT de la barre d'état — le carnet, l'indicatif et le reste de la station ne bougent pas.", 'cat.enable': 'Activer le CAT', 'cat.brand': 'Radio', 'cat.backend': 'Backend', 'cat.optOmnirig': 'OmniRig', 'cat.optFlex': 'FlexRadio (API)', 'cat.share': 'Partager le CAT avec les autres logiciels', 'cat.shareHint': "Un autre logiciel atteint alors la radio à travers OpsLog — avec tous les backends, pas seulement les natifs. Pour Hamlib, choisissez le modèle « Hamlib NET rigctl » et saisissez 127.0.0.1:4532 ; pour TCI, pointez le logiciel sur 127.0.0.1:40001.", 'cat.shareProto': 'Protocole', 'cat.shareRigctl': 'Hamlib NET rigctl — WSJT-X, JTDX, MSHV, Log4OM', 'cat.shareTci': 'TCI — Expert Electronics', 'cat.shareTciClash': 'Le backend CAT est aussi en TCI : ExpertSDR occupe probablement déjà le port 40001 sur ce PC. Donnez un autre port au serveur, ou partagez en Hamlib.', 'cat.sharePort': 'Port de partage', 'cat.pttKey': 'Activer la touche PTT', 'cat.pttKeyPress': 'Appuyez sur une touche…', 'cat.pttKeyNone': 'Cliquez pour définir une touche', 'cat.pttKeyClear': 'Effacer', 'cat.pttKeyToggle': 'Mode bascule (appui = émission, nouvel appui = arrêt)', 'cat.pttKeyHint': "Quand OpsLog a le focus, cette touche passe la radio en émission — maintenue par défaut (relâcher pour arrêter), ou verrouillée en mode bascule. Elle utilise la méthode PTT de Audio → PTT (CAT / RTS / DTR), avec repli sur le CAT. Choisissez une touche que vous ne tapez jamais en journalisant (ex. Pause, Arrêt défil., ou une pédale mappée dessus) — OpsLog l'intercepte pour qu'elle n'atterrisse pas dans un champ.", 'cat.optXiegu': 'Xiegu (USB)', 'cat.xieguPort': 'Port CAT Xiegu', 'cat.xieguBaudHint': 'Doit correspondre au menu de la radio (G90 par défaut : 19200).', 'cat.xieguAddrHint': "Adresse CI-V d'usine de la famille G90/X6100 : 0x70.", 'cat.xieguPTTLine': 'Passage en \u00e9mission', 'cat.xieguPTTCiv': 'Commande CI-V', 'cat.xieguPTTHint': 'Un G90 ne passe pas en \u00e9mission sur la commande CI-V : les interfaces comme le DE-19 le pilotent par RTS ou DTR. Choisissez la ligne de la v\u00f4tre \u2014 c\u2019est aussi ce qui permet \u00e0 WSJT-X d\u2019\u00e9mettre via le partage CAT.', 'cat.optYaesu': 'Yaesu (USB)', 'cat.optKenwood': 'Kenwood (USB, réseau)', 'cat.optElecraft': 'Elecraft K3/K4 (USB, réseau)', 'cat.elecraftHint': 'Les modes numériques passent automatiquement en DATA A (MD6+DT0) — le sous-mode dont l’audio FT8 a besoin.', 'cat.civTrace': 'Journaliser le protocole CAT', 'cat.civTraceHint': '\u00c9crit dans le journal chaque trame CAT \u00e9chang\u00e9e avec la radio \u2014 CI-V en hexad\u00e9cimal, Kenwood en texte. Pour signaler une radio qui r\u00e9pond de travers \u2014 un bouton qui fait autre chose, une fr\u00e9quence qui saute. Valable pour la session seulement : c\u2019est un diagnostic, et le journal grossit vite.', 'cat.kenwoodPort': 'Port COM Kenwood', 'cat.kenwoodPortHint': 'Le port s\u00e9rie CAT/USB de la radio (TS-590, TS-890, TS-990, TS-2000, ainsi que les Elecraft K3/K4 qui parlent le m\u00eame dialecte).', 'cat.kenwoodBaudHint': 'Doit correspondre au MENU de la radio : un TS-590 sort d\u2019usine \u00e0 9600, un TS-890 \u00e0 115200.', 'cat.kwLink': 'Connexion', 'cat.kwLinkBridge': 'RS-232 vers Ethernet (pont s\u00e9rie)', 'cat.kwLinkNative': 'Protocole r\u00e9seau propre \u00e0 la radio', 'cat.kwLinkNativeHint': "Pas encore pris en charge. C'est une session avec son propre encapsulage et son identification, pas le flux CAT dans une socket : cela s'impl\u00e9mente radio par radio, et face \u00e0 une vraie. En attendant : USB, ou un pont s\u00e9rie-Ethernet.", 'cat.kwLinkUsb': 'USB / série', 'cat.kwLinkNet': 'Réseau', 'cat.kenwoodHostHint': "L'option réseau transporte les MÊMES commandes CAT en TCP au lieu d'un câble : un pont série-réseau (ser2net, adaptateur Ethernet-série, Raspberry Pi au shack), ou une radio qui expose son port CAT brut. Ce n'est PAS du Hamlib — le port 4532 est celui de rigctld, un autre protocole, et c'est celui qu'OpsLog lui-même PROPOSE sous « Partager le CAT ».", 'cat.kenwoodHost': 'Ou par le r\u00e9seau (h\u00f4te:port)', 'cat.yaesuPort': 'Port CAT Yaesu', 'cat.yaesuPortHint': "Le port CAT de la radio — sur un FTDX10/FTDX101 en USB c'est le port COM ENHANCED, pas le standard.", 'cat.yaesuBaudHint': 'Doit correspondre au menu de la radio (FTDX10/FTDX101 par défaut : 38400).', 'cat.lowerLines': 'Abaisser les lignes DTR et RTS à la connexion', 'cat.lowerLinesHint': 'Si votre radio est toujours en émission, cochez ceci.', 'cat.kwDataMode': 'Modes data (FT8/PSK…)', 'cat.kwDataUsb': 'USB', 'cat.kwDataMd6': 'DATA A — MD6+DT0 (Elecraft K3/K4)', 'cat.kwDataKeep': 'Ne pas changer le mode de la radio', 'cat.kwDataHint': "Ce qu'OpsLog règle sur la radio pour un mode data. Aucune commande unique ne convient à toutes : un Elecraft K3/K4 veut DATA (MD6) ; sur un TS-590SG/TS-990S le mode data est un modificateur d'USB réglé sur la radio, choisissez donc USB ou, plus sûr, « Ne pas changer » et passez la radio en DATA vous-même. En MD6 un Kenwood classique (TS-590/990) tomberait en FSK/RTTY — à ne pas utiliser là.", 'cat.optIcom': 'Icom (CI-V USB)', 'cat.optIcomNet': 'Icom (CI-V réseau)', 'cat.optTci': 'TCI', 'flxpw.title': 'FlexRadio \u2014 puissance par bande et par mode', 'flxpw.hint': 'Puissance d\u2019\u00e9mission appliqu\u00e9e au changement de bande ou de mode. Laissez une case vide pour ne pas toucher \u00e0 la puissance.', 'flxpw.band': 'Bande', 'flxb.title': 'FlexRadio \u2014 antennes et puissance par bande', 'flxb.hint': 'Les antennes sont appliqu\u00e9es au changement de bande ; la puissance au changement de bande ou de mode. Laissez une case de puissance vide pour ne pas y toucher.', 'flxb.rxAnt': 'Antenne RX', 'flxb.txAnt': 'Antenne TX', 'flxb.noRadio': 'Aucune antenne remont\u00e9e \u2014 connectez le FlexRadio, puis rouvrez ce panneau.', 'flxpw.phone': 'Phonie', 'flxpw.digi': 'Num\u00e9rique', 'flxpw.noBands': 'Aucune bande configur\u00e9e \u2014 ajoutez-les dans Listes \u2192 Bandes.', 'flxpw.saved': 'Enregistr\u00e9', + 'cat.radioMyRig': 'MY_RIG', 'cat.radioMyRigPh': 'ex. IC-7610 + PGXL', 'cat.radioMyRigHint': "Inscrit dans MY_RIG sur chaque QSO fait avec cette radio, avant la station par bande des Conditions de trafic — celle-ci dit ce qui était prévu, celle-là dit quelle radio émet. Laisser vide conserve l'ancien comportement.", 'cat.radio': 'Radio', 'cat.radioNamePh': 'Nom (ex. FTDX10)', 'cat.radioAddHint': 'Ajouter une radio, copiée sur celle-ci', 'cat.radioRemoveHint': 'Supprimer cette radio', 'cat.radioHint': "Tout ce qui suit configure la radio sélectionnée. On passe de l'une à l'autre par le bouton CAT de la barre d'état — le carnet, l'indicatif et le reste de la station ne bougent pas.", 'cat.enable': 'Activer le CAT', 'cat.brand': 'Radio', 'cat.backend': 'Backend', 'cat.optOmnirig': 'OmniRig', 'cat.optFlex': 'FlexRadio (API)', 'cat.share': 'Partager le CAT avec les autres logiciels', 'cat.shareHint': "Un autre logiciel atteint alors la radio à travers OpsLog — avec tous les backends, pas seulement les natifs. Pour Hamlib, choisissez le modèle « Hamlib NET rigctl » et saisissez 127.0.0.1:4532 ; pour TCI, pointez le logiciel sur 127.0.0.1:40001.", 'cat.shareProto': 'Protocole', 'cat.shareRigctl': 'Hamlib NET rigctl — WSJT-X, JTDX, MSHV, Log4OM', 'cat.shareTci': 'TCI — Expert Electronics', 'cat.shareTciClash': 'Le backend CAT est aussi en TCI : ExpertSDR occupe probablement déjà le port 40001 sur ce PC. Donnez un autre port au serveur, ou partagez en Hamlib.', 'cat.sharePort': 'Port de partage', 'cat.pttKey': 'Activer la touche PTT', 'cat.pttKeyPress': 'Appuyez sur une touche…', 'cat.pttKeyNone': 'Cliquez pour définir une touche', 'cat.pttKeyClear': 'Effacer', 'cat.pttKeyToggle': 'Mode bascule (appui = émission, nouvel appui = arrêt)', 'cat.pttKeyHint': "Quand OpsLog a le focus, cette touche passe la radio en émission — maintenue par défaut (relâcher pour arrêter), ou verrouillée en mode bascule. Elle utilise la méthode PTT de Audio → PTT (CAT / RTS / DTR), avec repli sur le CAT. Choisissez une touche que vous ne tapez jamais en journalisant (ex. Pause, Arrêt défil., ou une pédale mappée dessus) — OpsLog l'intercepte pour qu'elle n'atterrisse pas dans un champ.", 'cat.optXiegu': 'Xiegu (USB)', 'cat.xieguPort': 'Port CAT Xiegu', 'cat.xieguBaudHint': 'Doit correspondre au menu de la radio (G90 par défaut : 19200).', 'cat.xieguAddrHint': "Adresse CI-V d'usine de la famille G90/X6100 : 0x70.", 'cat.xieguPTTLine': 'Passage en \u00e9mission', 'cat.xieguPTTCiv': 'Commande CI-V', 'cat.xieguPTTHint': 'Un G90 ne passe pas en \u00e9mission sur la commande CI-V : les interfaces comme le DE-19 le pilotent par RTS ou DTR. Choisissez la ligne de la v\u00f4tre \u2014 c\u2019est aussi ce qui permet \u00e0 WSJT-X d\u2019\u00e9mettre via le partage CAT.', 'cat.optYaesu': 'Yaesu (USB)', 'cat.optKenwood': 'Kenwood (USB, réseau)', 'cat.optElecraft': 'Elecraft K3/K4 (USB, réseau)', 'cat.elecraftHint': 'Les modes numériques passent automatiquement en DATA A (MD6+DT0) — le sous-mode dont l’audio FT8 a besoin.', 'cat.civTrace': 'Journaliser le protocole CAT', 'cat.civTraceHint': '\u00c9crit dans le journal chaque trame CAT \u00e9chang\u00e9e avec la radio \u2014 CI-V en hexad\u00e9cimal, Kenwood en texte. Pour signaler une radio qui r\u00e9pond de travers \u2014 un bouton qui fait autre chose, une fr\u00e9quence qui saute. Valable pour la session seulement : c\u2019est un diagnostic, et le journal grossit vite.', 'cat.kenwoodPort': 'Port COM Kenwood', 'cat.kenwoodPortHint': 'Le port s\u00e9rie CAT/USB de la radio (TS-590, TS-890, TS-990, TS-2000, ainsi que les Elecraft K3/K4 qui parlent le m\u00eame dialecte).', 'cat.kenwoodBaudHint': 'Doit correspondre au MENU de la radio : un TS-590 sort d\u2019usine \u00e0 9600, un TS-890 \u00e0 115200.', 'cat.kwLink': 'Connexion', 'cat.kwLinkBridge': 'RS-232 vers Ethernet (pont s\u00e9rie)', 'cat.kwLinkNative': 'Protocole r\u00e9seau propre \u00e0 la radio', 'cat.kwLinkNativeHint': "Pas encore pris en charge. C'est une session avec son propre encapsulage et son identification, pas le flux CAT dans une socket : cela s'impl\u00e9mente radio par radio, et face \u00e0 une vraie. En attendant : USB, ou un pont s\u00e9rie-Ethernet.", 'cat.kwLinkUsb': 'USB / série', 'cat.kwLinkNet': 'Réseau', 'cat.kenwoodHostHint': "L'option réseau transporte les MÊMES commandes CAT en TCP au lieu d'un câble : un pont série-réseau (ser2net, adaptateur Ethernet-série, Raspberry Pi au shack), ou une radio qui expose son port CAT brut. Ce n'est PAS du Hamlib — le port 4532 est celui de rigctld, un autre protocole, et c'est celui qu'OpsLog lui-même PROPOSE sous « Partager le CAT ».", 'cat.kenwoodHost': 'Ou par le r\u00e9seau (h\u00f4te:port)', 'cat.yaesuPort': 'Port CAT Yaesu', 'cat.yaesuPortHint': "Le port CAT de la radio — sur un FTDX10/FTDX101 en USB c'est le port COM ENHANCED, pas le standard.", 'cat.yaesuBaudHint': 'Doit correspondre au menu de la radio (FTDX10/FTDX101 par défaut : 38400).', 'cat.lowerLines': 'Abaisser les lignes DTR et RTS à la connexion', 'cat.yaesuRttyUsb': 'RTTY en USB (RTTY-U)', 'cat.yaesuRttyUsbHint': '— l’ADIF n’enregistre que « RTTY » et le poste a les deux bandes latérales. Décoché, le poste passe en RTTY-L, la convention ancienne ; cochez si votre interface FSK ou votre décodeur attend la supérieure.', 'cat.lowerLinesHint': 'Si votre radio est toujours en émission, cochez ceci.', 'cat.kwDataMode': 'Modes data (FT8/PSK…)', 'cat.kwDataUsb': 'USB', 'cat.kwDataMd6': 'DATA A — MD6+DT0 (Elecraft K3/K4)', 'cat.kwDataKeep': 'Ne pas changer le mode de la radio', 'cat.kwDataHint': "Ce qu'OpsLog règle sur la radio pour un mode data. Aucune commande unique ne convient à toutes : un Elecraft K3/K4 veut DATA (MD6) ; sur un TS-590SG/TS-990S le mode data est un modificateur d'USB réglé sur la radio, choisissez donc USB ou, plus sûr, « Ne pas changer » et passez la radio en DATA vous-même. En MD6 un Kenwood classique (TS-590/990) tomberait en FSK/RTTY — à ne pas utiliser là.", 'cat.optIcom': 'Icom (CI-V USB)', 'cat.optIcomNet': 'Icom (CI-V réseau)', 'cat.optTci': 'TCI', 'flxpw.title': 'FlexRadio \u2014 puissance par bande et par mode', 'flxpw.hint': 'Puissance d\u2019\u00e9mission appliqu\u00e9e au changement de bande ou de mode. Laissez une case vide pour ne pas toucher \u00e0 la puissance.', 'flxpw.band': 'Bande', 'flxb.title': 'FlexRadio \u2014 antennes et puissance par bande', 'flxb.hint': 'Les antennes sont appliqu\u00e9es au changement de bande ; la puissance au changement de bande ou de mode. Laissez une case de puissance vide pour ne pas y toucher.', 'flxb.rxAnt': 'Antenne RX', 'flxb.txAnt': 'Antenne TX', 'flxb.noRadio': 'Aucune antenne remont\u00e9e \u2014 connectez le FlexRadio, puis rouvrez ce panneau.', 'flxpw.phone': 'Phonie', 'flxpw.digi': 'Num\u00e9rique', 'flxpw.noBands': 'Aucune bande configur\u00e9e \u2014 ajoutez-les dans Listes \u2192 Bandes.', 'flxpw.saved': 'Enregistr\u00e9', 'cat.icomNetHost': 'IP / nom d\'hôte du poste', 'cat.icomNetUser': 'Utilisateur réseau (ID)', 'cat.icomNetPass': 'Mot de passe réseau', 'cat.icomNetHint': "Se connecte directement au serveur LAN intégré du poste — sans RS-BA1 ni Remote Utility (ferme-les d'abord). Utilise l'ID/mot de passe Network User1 configurés dans le menu Network du poste. Un poste en veille est allumé automatiquement.", 'cat.icomNetListen': 'Écouter dans les enceintes', 'cat.icomNetListenHint': 'Décochez pour garder le flux silencieux — enregistrements et voice keyer fonctionnent toujours. Même interrupteur que le bouton haut-parleur de la console Icom.', 'cat.icomNetAudio': 'Diffuser l’audio RX par le réseau (expérimental)', diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index 4675484..41f3fbb 100644 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -2379,6 +2379,7 @@ export namespace main { kenwood_baud: number; kenwood_data_mode: string; yaesu_low_lines: boolean; + yaesu_rtty_usb: boolean; kenwood_low_lines: boolean; icom_port: string; icom_baud: number; @@ -2432,6 +2433,7 @@ export namespace main { this.kenwood_baud = source["kenwood_baud"]; this.kenwood_data_mode = source["kenwood_data_mode"]; this.yaesu_low_lines = source["yaesu_low_lines"]; + this.yaesu_rtty_usb = source["yaesu_rtty_usb"]; this.kenwood_low_lines = source["kenwood_low_lines"]; this.icom_port = source["icom_port"]; this.icom_baud = source["icom_baud"]; diff --git a/internal/cat/yaesu.go b/internal/cat/yaesu.go index 4bcbdfb..8f520fe 100644 --- a/internal/cat/yaesu.go +++ b/internal/cat/yaesu.go @@ -108,8 +108,10 @@ type Yaesu struct { // and VS is used — see ReadState. rxVFOCmd string // freqDigits is how many digits this rig writes a frequency in, LEARNED from - // its own replies. See setFreqWidth. + // its own replies. See learnFreqWidth. freqDigits int + // rttyUpper picks RTTY-U over RTTY-L — see SetRTTYUpper. + rttyUpper bool curFreq int64 curRXFreq int64 @@ -140,6 +142,18 @@ func NewYaesu(portName string, baud int, digital string) *Yaesu { return &Yaesu{portName: strings.TrimSpace(portName), baud: baud, digital: digital, curVFO: "A"} } +// SetRTTYUpper chooses which sideband RTTY is set on. +// +// Yaesu has both — MD06 is RTTY-L, MD09 is RTTY-U — and ADIF has neither: it +// says "RTTY" and stops there, so the rig cannot be driven from the logged mode +// alone. LSB is the older convention and stays the default; an operator whose +// FSK controller or decoder wants the other one says so once here. +func (y *Yaesu) SetRTTYUpper(v bool) { + y.mu.Lock() + defer y.mu.Unlock() + y.rttyUpper = v +} + // SetLowerLines chooses whether DTR and RTS are deasserted on connect. Set // before Connect. func (y *Yaesu) SetLowerLines(v bool) { @@ -375,7 +389,7 @@ func (y *Yaesu) SetMode(mode string) error { if y.port == nil { return fmt.Errorf("yaesu: not connected") } - d := yaesuModeDigit(mode, y.curFreq) + d := yaesuModeDigit(mode, y.curFreq, y.rttyUpper) if d == 0 { return fmt.Errorf("yaesu: no CAT mode for %q", mode) } @@ -608,7 +622,7 @@ func resolveYaesuVFOs(freqA, freqB int64, vfo string, split bool) (tx, rx int64, // yaesuModeDigit maps an ADIF mode to the MD digit. SSB has no single digit — // the sideband follows the worldwide convention (LSB below 10 MHz, USB above), // which is why the current frequency is part of the decision. -func yaesuModeDigit(mode string, freqHz int64) byte { +func yaesuModeDigit(mode string, freqHz int64, rttyUpper bool) byte { switch strings.ToUpper(strings.TrimSpace(mode)) { case "SSB": if freqHz > 0 && freqHz < 10_000_000 { @@ -626,7 +640,10 @@ func yaesuModeDigit(mode string, freqHz int64) byte { case "AM": return '5' case "RTTY": - return '6' + if rttyUpper { + return '9' // RTTY-U + } + return '6' // RTTY-L, the older convention case "": return 0 default: diff --git a/internal/cat/yaesu_test.go b/internal/cat/yaesu_test.go index f0099a3..69bf7a1 100644 --- a/internal/cat/yaesu_test.go +++ b/internal/cat/yaesu_test.go @@ -123,7 +123,7 @@ func TestYaesuModeDigit(t *testing.T) { {"", 14074000, 0}, // nothing to set } for _, c := range cases { - if got := yaesuModeDigit(c.mode, c.hz); got != c.want { + if got := yaesuModeDigit(c.mode, c.hz, false); got != c.want { t.Errorf("yaesuModeDigit(%q, %d) = %q, want %q", c.mode, c.hz, got, c.want) } } @@ -406,3 +406,22 @@ func TestYaesuAntennaCommand(t *testing.T) { } } } + +// ADIF says "RTTY" and stops there, but Yaesu has both sidebands and the rig +// has to be told one. LSB is the older convention and the default; the other is +// a station's own choice, made once. +func TestYaesuRTTYSideband(t *testing.T) { + if got := yaesuModeDigit("RTTY", 14_080_000, false); got != '6' { + t.Errorf("RTTY = %q, want RTTY-L", got) + } + if got := yaesuModeDigit("RTTY", 14_080_000, true); got != '9' { + t.Errorf("RTTY (upper) = %q, want RTTY-U", got) + } + // The switch is about RTTY and nothing else. + if got := yaesuModeDigit("FT8", 28_074_000, true); got != 'C' { + t.Errorf("FT8 = %q, want DATA-U", got) + } + if got := yaesuModeDigit("CW", 14_030_000, true); got != '3' { + t.Errorf("CW = %q, want CW-U", got) + } +}