diff --git a/app.go b/app.go index c836c7c..64e8a63 100644 --- a/app.go +++ b/app.go @@ -14533,6 +14533,15 @@ func (a *App) IcomSetSplit(on bool) error { return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetIcomSplit(on) }) } +// IcomSetSplitOffset turns split on with a chosen TX offset (Hz) — the +// console's +1/+5/+10 kHz buttons. +func (a *App) IcomSetSplitOffset(hz int64) error { + if a.cat == nil { + return fmt.Errorf("cat not initialized") + } + return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetIcomSplitOffset(true, hz) }) +} + func (a *App) IcomSetAntenna(n int) error { if a.cat == nil { return fmt.Errorf("cat not initialized") diff --git a/changelog.json b/changelog.json index 691a9ba..3442fdb 100644 --- a/changelog.json +++ b/changelog.json @@ -28,7 +28,8 @@ "Icom network audio: “Talk to radio” now works over the LAN too — live microphone straight to the rig, PTT included. With RX audio and a headset, OpsLog is a complete remote station: hear, talk, key CW and log over one network link.", "Icom console: the SUB display shows the sub receiver’s frequency at all times (it was blank until split), and engaging split copies the mode to the TX VFO so it matches the main.", "Icom network audio: the stream now opens regardless of the speaker choice — speakers-off (or a failed output device) was silently disabling the whole stream: no audio session, no recordings, no voice keyer, despite the RX-audio option being ticked.", - "Icom network: a radio in standby keeps its session and the console’s ON button — the quiet-CI-V recovery was tearing the session down every 15 s, so a radio that was off when OpsLog started could never be powered on." + "Icom network: a radio in standby keeps its session and the console’s ON button — the quiet-CI-V recovery was tearing the session down every 15 s, so a radio that was off when OpsLog started could never be powered on.", + "Icom console: PTT and Split move up under the dial — one PTT button, and Split as OFF/+1k/+5k/+10k, DXpedition style. Engaging split also aligns the TX VFO’s mode with the main." ], "fr": [ "Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).", @@ -56,7 +57,8 @@ "Audio réseau Icom : « Talk to radio » fonctionne aussi par le LAN — micro en direct vers la radio, PTT compris. Avec le RX audio et un casque, OpsLog devient une station remote complète : écouter, parler, manipuler la CW et loguer sur un seul lien réseau.", "Console Icom : l’affichage SUB montre la fréquence du sub receiver en permanence (il restait vide hors split), et activer le split copie le mode sur le VFO TX pour qu’il suive le main.", "Audio réseau Icom : le flux s’ouvre désormais indépendamment du choix d’écoute — enceintes coupées (ou périphérique de sortie en échec) désactivait silencieusement tout le flux : pas de session audio, ni enregistrements, ni voice keyer, malgré la case RX audio cochée.", - "Réseau Icom : une radio en veille garde sa session et le bouton ON de la console — la récupération du CI-V muet détruisait la session toutes les 15 s, donc une radio éteinte au lancement d’OpsLog ne pouvait jamais être allumée." + "Réseau Icom : une radio en veille garde sa session et le bouton ON de la console — la récupération du CI-V muet détruisait la session toutes les 15 s, donc une radio éteinte au lancement d’OpsLog ne pouvait jamais être allumée.", + "Console Icom : le PTT et le Split remontent sous le cadran — un bouton PTT, et le Split en OFF/+1k/+5k/+10k, façon DXpedition. Activer le split aligne aussi le mode du VFO TX sur le main." ] }, { diff --git a/frontend/src/components/IcomPanel.tsx b/frontend/src/components/IcomPanel.tsx index 6784bb8..c7dd0c4 100644 --- a/frontend/src/components/IcomPanel.tsx +++ b/frontend/src/components/IcomPanel.tsx @@ -5,7 +5,7 @@ import { IcomSetAFGain, IcomSetRFGain, IcomSetNB, IcomSetNBLevel, IcomSetNR, IcomSetNRLevel, IcomSetANF, IcomSetAPF, IcomSetAGC, IcomSetPreamp, IcomSetAtt, IcomSetFilter, AudioMonitorActive, AudioStartMonitor, AudioStopMonitor, - IcomSetRFPower, IcomSetMicGain, IcomSetSplit, IcomTune, IcomSetPTT, + IcomSetRFPower, IcomSetMicGain, IcomSetSplit, IcomSetSplitOffset, IcomTune, IcomSetPTT, IcomSetScope, IcomScopeData, IcomSetScopeMode, IcomSetScopeEdges, GetCATState, SetCATFrequency, SetCATMode, IcomSetRIT, IcomSetRITOn, IcomSetXITOn, IcomSetAntenna, IcomSetPBTInner, IcomSetPBTOuter, IcomSetManualNotch, IcomSetNotchPos, @@ -786,6 +786,29 @@ export function IcomPanel({ onReportRST, isNetwork = false }: { onReportRST?: (r ); })} + {/* PTT + split, at the operator's hand under the dial — the Transmit + card lower down kept these below the fold, and a console whose PTT + needs scrolling is not a console. */} +
+ + Split + + {[1, 5, 10].map((k) => ( + + ))} +
{/* Live meters — always visible: S (RX, click → RST), Po in watts, SWR. */} diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 31df129..b4fe1d2 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -704,6 +704,8 @@ export function IcomSetScopeMode(arg1:boolean):Promise; export function IcomSetSplit(arg1:boolean):Promise; +export function IcomSetSplitOffset(arg1:number):Promise; + export function IcomSetSquelch(arg1:number):Promise; export function IcomSetVOX(arg1:boolean):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 31ce89f..c345049 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -1346,6 +1346,10 @@ export function IcomSetSplit(arg1) { return window['go']['main']['App']['IcomSetSplit'](arg1); } +export function IcomSetSplitOffset(arg1) { + return window['go']['main']['App']['IcomSetSplitOffset'](arg1); +} + export function IcomSetSquelch(arg1) { return window['go']['main']['App']['IcomSetSquelch'](arg1); } diff --git a/internal/cat/cat.go b/internal/cat/cat.go index 703dd8a..2643ed6 100644 --- a/internal/cat/cat.go +++ b/internal/cat/cat.go @@ -640,6 +640,7 @@ type IcomController interface { SetRFPower(int) error SetMicGain(int) error SetIcomSplit(bool) error + SetIcomSplitOffset(bool, int64) error TuneATU() error SetScope(bool) error // enable/disable the spectrum-scope waveform stream SetScopeMode(bool) error // true = fixed span, false = center-on-VFO diff --git a/internal/cat/icomserial.go b/internal/cat/icomserial.go index 9c6bbc6..e3ca8c5 100644 --- a/internal/cat/icomserial.go +++ b/internal/cat/icomserial.go @@ -1860,10 +1860,16 @@ func (b *IcomSerial) SetMicGain(p int) error { } func (b *IcomSerial) SetIcomSplit(on bool) error { + return b.SetIcomSplitOffset(on, 0) +} + +// SetIcomSplitOffset enables split with a CHOSEN TX offset in Hz (0 = the +// usual convention: +1 kHz on CW, +5 kHz otherwise). The console offers +// +1/+5/+10 directly, DXpedition style. +func (b *IcomSerial) SetIcomSplitOffset(on bool, offsetHz int64) error { if on { - // Enable split with the usual "work him up" TX offset: +1 kHz on CW, - // +5 kHz otherwise (SSB). Set the unselected (TX) VFO to RX+offset first, - // then turn split on. 0x25 0x01 + BCD sets the unselected VFO's frequency. + // Set the unselected (TX) VFO to RX+offset first, then turn split on. + // 0x25 0x01 + BCD sets the unselected VFO's frequency. rx := b.curFreq if rx <= 0 { if hz, err := b.readFreq(); err == nil { @@ -1871,9 +1877,12 @@ func (b *IcomSerial) SetIcomSplit(on bool) error { } } if rx > 0 { - offset := int64(5000) - if b.curModeByte == civ.ModeCW || b.curModeByte == civ.ModeCWR { - offset = 1000 + offset := offsetHz + if offset <= 0 { + offset = 5000 + if b.curModeByte == civ.ModeCW || b.curModeByte == civ.ModeCWR { + offset = 1000 + } } _ = b.exec(append([]byte{civ.CmdVfoFreq, civ.SubVfoUnselected}, civ.FreqToBCD(rx+offset)...)...) }