feat(omnirig): say which of its two CW bits means CW on this rig

Reported on an IC-7610 shared through OmniRig: clicking a CW spot put
the radio in CW-R every time, and the only way out was to edit the
OmniRig rig file by hand.

OmniRig's model has two CW modes, PM_CW_U and PM_CW_L, and nothing in it
says which one an .ini calls plain CW. Icom rig files disagree with each
other — on some PM_CW_U is CI-V mode 0x03 (CW), on others 0x07 (CW-R) —
so asking for "CW" is a question with two right answers and OpsLog was
only ever giving one of them.

Settings ▸ CAT ▸ OmniRig now carries the answer for THIS rig, and it is
pushed to a radio that is already connected rather than waiting for a
reconnect: which bit a mode maps to is not worth dropping the link, and
with it WSJT-X's rigctl session. Same shape as the Yaesu RTTY sideband,
for the same reason.

Nothing changes for anyone whose rig file already agrees with us.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-09-08 12:05:31 +02:00
co-authored by Claude Opus 5
parent 82cd5c5d0b
commit f8f8de4a30
8 changed files with 80 additions and 14 deletions
+16 -2
View File
@@ -121,6 +121,12 @@ const (
keyCATOmniRigNum = "cat.omnirig.rig" // 1 or 2
// Which VFO to believe when OmniRig names one. "" = trust the rig file.
keyCATOmniRigVFO = "cat.omnirig.vfo" // "" | "A" | "B"
// OmniRig's model has two CW modes — PM_CW_U and PM_CW_L — and nothing says
// which one an .ini file calls plain CW. Icom files disagree with each other:
// on some, PM_CW_U is CI-V mode 0x03 (CW) and on others it is 0x07 (CW-R).
// An operator clicking a CW spot on an IC-7610 landed in CW-R and had to edit
// the rig file to get out of it. This says which bit means CW on THIS rig.
keyCATOmniRigCWLower = "cat.omnirig.cw_lower" // "1" → send PM_CW_L for CW
// Put the RADIO in USB for a digital mode instead of asking for the mode by
// name. Every backend, because the reason is the radio, not the link.
keyCATDigiUSB = "cat.digi_usb"
@@ -483,6 +489,9 @@ type CATSettings struct {
// the .ini: an IC-7610 file was seen declaring VFO B permanently while the
// operator worked on the main VFO, so OpsLog wrote to A and read B.
OmniRigVFO string `json:"omnirig_vfo"` // "" | "A" | "B"
// OmniRigCWLower sends PM_CW_L instead of PM_CW_U when the mode is CW — for
// the rig files whose PM_CW_U is the REVERSE sideband. See the key comment.
OmniRigCWLower bool `json:"omnirig_cw_lower"`
// DigiAsUSB puts the radio in USB when a digital mode is selected, rather
// than naming the mode. What the QSO is LOGGED as never changes.
DigiAsUSB bool `json:"digi_as_usb"`
@@ -8366,7 +8375,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, 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)
m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATOmniRigCWLower, 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
}
@@ -8472,6 +8481,7 @@ func (a *App) GetCATSettings() (CATSettings, error) {
if out.DigitalDefault == "" {
out.DigitalDefault = "FT8"
}
out.OmniRigCWLower = m[keyCATOmniRigCWLower] == "1"
if v := strings.ToUpper(strings.TrimSpace(m[keyCATOmniRigVFO])); v == "A" || v == "B" {
out.OmniRigVFO = v
}
@@ -8588,6 +8598,7 @@ func (a *App) SaveCATSettings(s CATSettings) error {
keyCATBackend: s.Backend,
keyCATOmniRigNum: strconv.Itoa(s.OmniRigNum),
keyCATOmniRigVFO: strings.ToUpper(strings.TrimSpace(s.OmniRigVFO)),
keyCATOmniRigCWLower: boolStr(s.OmniRigCWLower),
keyCATDigiUSB: boolStr(s.DigiAsUSB),
keyCATFlexHost: strings.TrimSpace(s.FlexHost),
keyCATFlexPort: strconv.Itoa(s.FlexPort),
@@ -16338,6 +16349,9 @@ func (a *App) reloadCAT() {
if s.Enabled && s.Backend == "yaesu" && a.cat != nil {
_ = a.cat.YaesuDo(func(y cat.YaesuController) error { y.SetRTTYUpper(s.YaesuRTTYUSB); return nil })
}
if s.Enabled && s.Backend == "omnirig" && a.cat != nil {
_ = a.cat.OmniRigDo(func(o cat.OmniRigController) error { o.SetCWLower(s.OmniRigCWLower); return nil })
}
// Nothing about the link changed → leave it connected. See catLinkSig.
if sig := catLinkSig(s); sig == a.catSig {
applog.Printf("cat: settings saved, link unchanged — staying connected")
@@ -16362,7 +16376,7 @@ func (a *App) reloadCAT() {
// Spawning OmniRig.exe ourselves (even with /Embedding) on every
// reloadCAT raised the existing instance's window to the front,
// which is what Log4OM avoids by relying entirely on COM activation.
a.cat.Start(cat.NewOmniRig(s.OmniRigNum, s.OmniRigVFO))
a.cat.Start(cat.NewOmniRig(s.OmniRigNum, s.OmniRigVFO, s.OmniRigCWLower))
case "flex":
// Native FlexRadio (SmartSDR) TCP API — no OmniRig needed.
fb := cat.NewFlex(s.FlexHost, s.FlexPort, s.FlexSpots)
+4 -2
View File
@@ -5,12 +5,14 @@
"en": [
"After an update, OpsLog starts again. The fix that stopped Defender calling the updater a trojan removed the helper that waited for the old process to die, and nothing took over the job: the new instance was patient with the single-instance lock for twenty seconds while the old one is allowed thirty to shut down — closing a remote logbook, a CAT session, sometimes a backup. Where that ran long the new process gave up in silence, leaving no window and a leftover OpsLog in the task manager. It now waits for the previous process itself, ending the instant it does; and if it really has not gone, it says so instead of claiming OpsLog is already running.",
"A UDP row set to multicast on an address that is not one now listens anyway. 127.0.0.1 in the group box is the common mistake — it is the address every other field in every other program wants — but a multicast group runs 224.0.0.0 to 239.255.255.255, and joining anything else failed on every interface with a Windows error naming nothing the operator had typed. The row simply did not run. It now listens on unicast, which is what such an address means, and says so in the log.",
"The confirmation defaults added for the newest services were blank. HAMLOG.online arrived after most profiles were set up, so it had no default at all — and blank is not a status anybody chose. Every service now starts the same way: the sent side at R (waiting to go out), the received side at N. A blank left by a service that did not exist when you last saved is filled in; a status you chose yourself is untouched."
"The confirmation defaults added for the newest services were blank. HAMLOG.online arrived after most profiles were set up, so it had no default at all — and blank is not a status anybody chose. Every service now starts the same way: the sent side at R (waiting to go out), the received side at N. A blank left by a service that did not exist when you last saved is filled in; a status you chose yourself is untouched.",
"OmniRig: a setting for rig files whose CW is the reverse one. OmniRig has two CW modes and nothing says which one a rig file calls plain CW — some Icom files map PM_CW_U to CW, others to CW-R — so clicking a CW spot on an IC-7610 landed the radio in CW-R, and the only way out was to edit the rig file. Settings → CAT → OmniRig now has a tick box for it, applied at once without dropping the link. (If your VFOs read the wrong way round on the same rig, the VFO override beside it is the answer: rig files disagree there too.)"
],
"fr": [
"Après une mise à jour, OpsLog redémarre. Le correctif qui a fait cesser la détection en cheval de Troie a supprimé l'assistant qui attendait la mort de l'ancien processus, et rien n'a repris ce travail : la nouvelle instance patientait vingt secondes sur le verrou d'instance unique alors que l'ancienne dispose de trente pour se fermer — elle referme un journal distant, une session CAT, parfois une sauvegarde. Quand cela durait, le nouveau processus abandonnait en silence : pas de fenêtre, et un OpsLog restant dans le gestionnaire des tâches. Il attend désormais l'ancien processus lui-même, et repart à l'instant où celui-ci s'arrête ; et s'il n'est vraiment pas parti, il le dit au lieu d'annoncer qu'OpsLog tourne déjà.",
"Une ligne UDP réglée en multicast sur une adresse qui n'en est pas une écoute désormais quand même. 127.0.0.1 dans le champ groupe est l'erreur classique — c'est l'adresse que réclame tout autre champ de tout autre programme — mais un groupe multicast va de 224.0.0.0 à 239.255.255.255, et rejoindre autre chose échouait sur toutes les interfaces avec une erreur Windows ne nommant rien de ce que l'opérateur avait saisi. La ligne ne tournait tout simplement pas. Elle écoute maintenant en unicast, ce que veut dire une telle adresse, et le dit dans le journal.",
"Les statuts par défaut des services les plus récents étaient vides. HAMLOG.online est arrivé après la configuration de la plupart des profils : il n'avait donc aucun défaut — et vide n'est pas un statut que quelqu'un a choisi. Chaque service démarre désormais pareil : côté envoi R (en attente de départ), côté réception N. Un vide laissé par un service qui n'existait pas lors de votre dernier enregistrement est comblé ; un statut que vous avez choisi n'est pas touché."
"Les statuts par défaut des services les plus récents étaient vides. HAMLOG.online est arrivé après la configuration de la plupart des profils : il n'avait donc aucun défaut — et vide n'est pas un statut que quelqu'un a choisi. Chaque service démarre désormais pareil : côté envoi R (en attente de départ), côté réception N. Un vide laissé par un service qui n'existait pas lors de votre dernier enregistrement est comblé ; un statut que vous avez choisi n'est pas touché.",
"OmniRig : un réglage pour les fichiers de rig dont la CW est l'inverse. OmniRig a deux modes CW et rien ne dit lequel un fichier appelle CW tout court — certains fichiers Icom associent PM_CW_U à CW, d'autres à CW-R — si bien qu'un clic sur un spot CW mettait un IC-7610 en CW-R, sans autre issue que de modifier le fichier de rig. Réglages → CAT → OmniRig a désormais une case pour cela, appliquée aussitôt sans couper la liaison. (Si vos VFO sont inversés sur la même radio, le sélecteur de VFO juste à côté est la réponse : les fichiers de rig divergent là aussi.)"
]
},
{
+11 -1
View File
@@ -1859,7 +1859,7 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
// IC-7610 the moment they chose Other.
const [icomCustom, setIcomCustom] = useState(false);
const [catCfg, setCatCfg] = useState<CATSettings>({
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,
enabled: false, backend: 'omnirig', omnirig_rig: 1, omnirig_vfo: '', omnirig_cw_lower: false, 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, 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,
@@ -3640,6 +3640,16 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
<p className="text-[10px] text-muted-foreground">{t('cat.omnirigVfoHint')}</p>
</div>
)}
{catCfg.backend === 'omnirig' && (
<div className="space-y-1 col-span-2">
<label className="flex items-start gap-2 text-sm cursor-pointer">
<Checkbox className="mt-0.5" checked={!!catCfg.omnirig_cw_lower}
onCheckedChange={(c) => setCatCfg((s) => ({ ...s, omnirig_cw_lower: !!c }))} />
<span>{t('cat.omnirigCwLower')}</span>
</label>
<p className="text-[10px] text-muted-foreground">{t('cat.omnirigCwLowerHint')}</p>
</div>
)}
{catCfg.backend === 'flex' && (
<>
<div className="space-y-1">
+2 -2
View File
@@ -421,7 +421,7 @@ const en: Dict = {
'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)',
'cat.icomNetAudioHint': 'Play the rigs received audio through your Listening device (Settings → Audio) over the 50003 stream. Experimental — the audio framing is pending on-rig verification; leave off if control misbehaves.',
'cat.omnirigRig': 'OmniRig rig slot', 'cat.omnirigVfo': 'VFO to read', 'cat.omnirigVfoAuto': 'As reported by the rig file', 'cat.omnirigVfoA': 'Always VFO A (main)', 'cat.omnirigVfoB': 'Always VFO B (sub)', 'cat.omnirigVfoHint': 'OmniRig reports the active VFO from the rig file, and some files get it wrong — the frequency then follows the other VFO and appears frozen. Force one here if that happens.', 'cat.flexIp': 'FlexRadio IP', 'cat.port': 'Port', 'cat.flexSpots': 'Show cluster spots on the panadapter', 'cat.flexSpotsHint': "(spots from OpsLog's DX cluster appear on the radio, auto-expire after 30 min)", 'cat.flexDecodeSpots': 'Show WSJT-X decodes on the panadapter', 'cat.flexDecodeSpotsHint': '(heard FT8/FT4 stations from your WSJT-X/JTDX UDP feed, one spot per call)', 'cat.flexDecodeSecs': 'Display for', 'cat.flexDecodeSecsHint': 'seconds before a station is removed',
'cat.omnirigRig': 'OmniRig rig slot', 'cat.omnirigVfo': 'VFO to read', 'cat.omnirigVfoAuto': 'As reported by the rig file', 'cat.omnirigVfoA': 'Always VFO A (main)', 'cat.omnirigVfoB': 'Always VFO B (sub)', 'cat.omnirigCwLower': 'This rig file calls CW “PM_CW_L” — tick if clicking a CW spot lands the radio in CW-R', 'cat.omnirigCwLowerHint': 'OmniRig has two CW modes and nothing says which one a rig file calls plain CW: some Icom files map PM_CW_U to CW, others to CW-R. Tick this and OpsLog asks for the other one. Applied at once — the radio does not have to reconnect.', 'cat.omnirigVfoHint': 'OmniRig reports the active VFO from the rig file, and some files get it wrong — the frequency then follows the other VFO and appears frozen. Force one here if that happens.', 'cat.flexIp': 'FlexRadio IP', 'cat.port': 'Port', 'cat.flexSpots': 'Show cluster spots on the panadapter', 'cat.flexSpotsHint': "(spots from OpsLog's DX cluster appear on the radio, auto-expire after 30 min)", 'cat.flexDecodeSpots': 'Show WSJT-X decodes on the panadapter', 'cat.flexDecodeSpotsHint': '(heard FT8/FT4 stations from your WSJT-X/JTDX UDP feed, one spot per call)', 'cat.flexDecodeSecs': 'Display for', 'cat.flexDecodeSecsHint': 'seconds before a station is removed',
'cat.flexOptions': 'What OpsLog does with the radio', 'flexzoom.title': 'Zoom the panadapter on a spot click', 'flexzoom.enable': 'Resize the panadapter when a spot is clicked', 'flexzoom.cw': 'CW', 'flexzoom.ssb': 'Phone', 'flexzoom.digi': 'Digital', 'flexzoom.centre': 'Re-centre on the spot as well', 'flexzoom.centreHint': '(off keeps your window where you put it and only changes its width)', 'spotcol.title': 'Spot colours on the panadapter', 'spotcol.enable': 'Colour spots by status', 'spotcol.reset': 'restore defaults', 'spotcol.send': 'Send this status to the panadapter', 'spotcol.text': 'Text colour', 'spotcol.bg': 'Background colour', 'spotcol.s_new': 'Entity never worked', 'spotcol.s_new-band-mode': 'Entity new on this band AND mode', 'spotcol.s_new-band': 'Entity new on this band', 'spotcol.s_new-mode': 'Entity new in this mode', 'spotcol.s_new-slot': 'New slot', 'spotcol.s_new-pota': 'New POTA park', 'spotcol.s_new-county': 'New US county', 'spotcol.s_new-pfx': 'New WPX prefix', 'spotcol.s_my-call': 'My own callsign', 'spotcol.s_worked': 'Callsign already worked', 'spotcol.s_none': 'Nothing new', 'cat.flexDvkDax': 'Switch transmit audio to DAX for voice messages',
'cat.flexDvkDaxHint': '(pressed while a DVK message or a QSO recording goes out, and put back afterwards so your microphone works again)',
'cat.icomPort': 'Icom CI-V port', 'cat.selectCom': 'Select COM port', 'cat.noPorts': 'No ports found', 'cat.baud': 'Baud rate', 'cat.icomModel': 'Rig model', 'cat.icomModelOther': 'Other (custom address)', 'cat.civAddr': 'CI-V address (hex)', 'cat.civHint': 'Pick your model to set the CI-V address automatically (or choose "Other" and type it). Set "CI-V USB Echo Back" OFF and CI-V baud to match on the rig.',
@@ -1030,7 +1030,7 @@ const fr: Dict = {
'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 laudio RX par le réseau (expérimental)',
'cat.icomNetAudioHint': 'Écoute laudio reçu du poste sur ton périphérique d’écoute (Réglages → Audio) via le flux 50003. Expérimental — le format audio reste à vérifier sur le poste ; laisse désactivé si le contrôle se comporte mal.',
'cat.omnirigRig': 'Slot OmniRig', 'cat.omnirigVfo': 'VFO à lire', 'cat.omnirigVfoAuto': 'Selon le fichier radio', 'cat.omnirigVfoA': 'Toujours le VFO A (principal)', 'cat.omnirigVfoB': 'Toujours le VFO B (secondaire)', 'cat.omnirigVfoHint': "OmniRig indique le VFO actif d'après le fichier radio, et certains fichiers se trompent — la fréquence suit alors l'autre VFO et paraît figée. Forcez-en un ici le cas échéant.", 'cat.flexIp': 'IP FlexRadio', 'cat.port': 'Port', 'cat.flexSpots': 'Afficher les spots cluster sur le panadapter', 'cat.flexSpotsHint': "(les spots du cluster DX d'OpsLog apparaissent sur la radio, expirent après 30 min)", 'cat.flexDecodeSpots': 'Afficher les décodes WSJT-X sur le panadapter', 'cat.flexDecodeSpotsHint': '(stations FT8/FT4 entendues via ton flux UDP WSJT-X/JTDX, un spot par station)', 'cat.flexDecodeSecs': 'Affichage pendant', 'cat.flexDecodeSecsHint': 'secondes avant retrait d\'une station',
'cat.omnirigRig': 'Slot OmniRig', 'cat.omnirigVfo': 'VFO à lire', 'cat.omnirigVfoAuto': 'Selon le fichier radio', 'cat.omnirigVfoA': 'Toujours le VFO A (principal)', 'cat.omnirigVfoB': 'Toujours le VFO B (secondaire)', 'cat.omnirigCwLower': 'Ce fichier de rig appelle la CW « PM_CW_L » — cocher si un clic sur un spot CW met la radio en CW-R', 'cat.omnirigCwLowerHint': 'OmniRig a deux modes CW et rien ne dit lequel un fichier de rig appelle CW tout court : certains fichiers Icom associent PM_CW_U à CW, dautres à CW-R. Cochez et OpsLog demande lautre. Appliqué aussitôt — la radio na pas besoin de se reconnecter.', 'cat.omnirigVfoHint': "OmniRig indique le VFO actif d'après le fichier radio, et certains fichiers se trompent — la fréquence suit alors l'autre VFO et paraît figée. Forcez-en un ici le cas échéant.", 'cat.flexIp': 'IP FlexRadio', 'cat.port': 'Port', 'cat.flexSpots': 'Afficher les spots cluster sur le panadapter', 'cat.flexSpotsHint': "(les spots du cluster DX d'OpsLog apparaissent sur la radio, expirent après 30 min)", 'cat.flexDecodeSpots': 'Afficher les décodes WSJT-X sur le panadapter', 'cat.flexDecodeSpotsHint': '(stations FT8/FT4 entendues via ton flux UDP WSJT-X/JTDX, un spot par station)', 'cat.flexDecodeSecs': 'Affichage pendant', 'cat.flexDecodeSecsHint': 'secondes avant retrait d\'une station',
'cat.flexOptions': "Ce quOpsLog fait avec la radio", 'flexzoom.title': 'Zoomer le panadapter au clic sur un spot', 'flexzoom.enable': 'Redimensionner le panadapter au clic sur un spot', 'flexzoom.cw': 'CW', 'flexzoom.ssb': 'Phonie', 'flexzoom.digi': 'Numérique', 'flexzoom.centre': 'Recentrer aussi sur le spot', 'flexzoom.centreHint': '(décoché, ta fenêtre reste où tu las mise et seule sa largeur change)', 'spotcol.title': 'Couleurs des spots sur le panadapter', 'spotcol.enable': 'Colorer les spots selon leur statut', 'spotcol.reset': 'valeurs par défaut', 'spotcol.send': 'Envoyer ce statut au panadapter', 'spotcol.text': 'Couleur du texte', 'spotcol.bg': 'Couleur de fond', 'spotcol.s_new': 'Entité jamais contactée', 'spotcol.s_new-band-mode': 'Entité nouvelle sur cette bande ET ce mode', 'spotcol.s_new-band': 'Entité nouvelle sur cette bande', 'spotcol.s_new-mode': 'Entité nouvelle dans ce mode', 'spotcol.s_new-slot': 'Nouveau slot', 'spotcol.s_new-pota': 'Nouveau parc POTA', 'spotcol.s_new-county': 'Nouveau comté US', 'spotcol.s_new-pfx': 'Nouveau préfixe WPX', 'spotcol.s_my-call': 'Mon propre indicatif', 'spotcol.s_worked': 'Indicatif déjà contacté', 'spotcol.s_none': 'Rien de neuf',
'cat.flexDvkDax': "Basculer l'audio d'émission sur DAX pour les messages vocaux",
'cat.flexDvkDaxHint': "(enfoncé le temps d'un message DVK ou d'un enregistrement de QSO, puis remis comme avant pour retrouver ton micro)",
+2
View File
@@ -2360,6 +2360,7 @@ export namespace main {
backend: string;
omnirig_rig: number;
omnirig_vfo: string;
omnirig_cw_lower: boolean;
digi_as_usb: boolean;
flex_host: string;
flex_port: number;
@@ -2414,6 +2415,7 @@ export namespace main {
this.backend = source["backend"];
this.omnirig_rig = source["omnirig_rig"];
this.omnirig_vfo = source["omnirig_vfo"];
this.omnirig_cw_lower = source["omnirig_cw_lower"];
this.digi_as_usb = source["digi_as_usb"];
this.flex_host = source["flex_host"];
this.flex_port = source["flex_port"];
+17
View File
@@ -1145,6 +1145,23 @@ func (m *Manager) YaesuDo(fn func(YaesuController) error) error {
})
}
// OmniRigController is the handful of OmniRig preferences that can be changed
// without dropping the rig link.
type OmniRigController interface {
SetCWLower(bool) // which of OmniRig's two CW bits means plain CW
}
// OmniRigDo dispatches an OmniRig preference onto the CAT goroutine.
func (m *Manager) OmniRigDo(fn func(OmniRigController) error) error {
return m.exec(func(b Backend) error {
oc, ok := b.(OmniRigController)
if !ok {
return fmt.Errorf("active CAT backend is not OmniRig")
}
return fn(oc)
})
}
// KenwoodController is the Kenwood/Elecraft CW-over-CAT capability (the KY keyer),
// so a K3 can key CW through its single CAT link instead of a second COM port.
type KenwoodController interface {
+23 -2
View File
@@ -36,6 +36,14 @@ type OmniRig struct {
// on the main VFO) and read B — the frequency "never followed the knob",
// while it was following the other one all along.
ForceVFO string
// CWLower sends PM_CW_L rather than PM_CW_U when asked for CW.
//
// OmniRig has two CW modes and nothing says which one an .ini file calls
// plain CW. Icom rig files disagree: on some PM_CW_U is CI-V mode 0x03 (CW),
// on others it is 0x07 (CW-R). An operator clicking a CW spot on an IC-7610
// landed in CW-R every time and had to edit the rig file to get out of it.
// This is the setting that means he does not have to.
CWLower bool
omnirig *ole.IDispatch
rig *ole.IDispatch
@@ -76,7 +84,7 @@ type OmniRig struct {
// NewOmniRig creates a non-connected backend. Call Connect before use.
// NewOmniRig builds the backend. forceVFO is "" to follow whatever the rig file
// reports, or "A"/"B" to override it — see the ForceVFO field.
func NewOmniRig(rigNum int, forceVFO string) *OmniRig {
func NewOmniRig(rigNum int, forceVFO string, cwLower bool) *OmniRig {
if rigNum < 1 || rigNum > 2 {
rigNum = 1
}
@@ -84,7 +92,7 @@ func NewOmniRig(rigNum int, forceVFO string) *OmniRig {
if v != "A" && v != "B" {
v = ""
}
return &OmniRig{RigNum: rigNum, ForceVFO: v}
return &OmniRig{RigNum: rigNum, ForceVFO: v, CWLower: cwLower}
}
func (o *OmniRig) Name() string { return "omnirig" }
@@ -602,6 +610,13 @@ func (o *OmniRig) SetFrequency(hz int64) error {
return nil
}
// SetCWLower chooses which of OmniRig's two CW bits means plain CW.
//
// Applied to the RUNNING backend, because the CAT link does not depend on it:
// dropping the rig — and with it WSJT-X's rigctl session — to change which bit
// a mode maps to would cost far more than it fixes.
func (o *OmniRig) SetCWLower(on bool) { o.CWLower = on }
// SetMode maps an ADIF mode to the OmniRig PM_* bit and pushes it to the rig.
// For SSB, the USB/LSB side is chosen from the rig's current frequency
// following worldwide convention (LSB below 14 MHz, USB above).
@@ -625,7 +640,13 @@ func (o *OmniRig) SetMode(mode string) error {
)
switch strings.ToUpper(strings.TrimSpace(mode)) {
case "CW":
// Which bit means plain CW is a property of the RIG FILE, not of CW —
// see the CWLower field.
if o.CWLower {
bit, bitName = pmCWL, "PM_CW_L"
} else {
bit, bitName = pmCWU, "PM_CW_U"
}
case "SSB":
// Decide USB vs LSB from the frequency. Prefer the freq we just COMMANDED
// (a clicked spot sets freq then mode ~150ms later): OmniRig's Freq