feat: qrz clickable in cw decoder
This commit is contained in:
Binary file not shown.
@@ -7698,7 +7698,9 @@ func (a *App) startUltrabeam() {
|
|||||||
a.ubFollowStop = nil
|
a.ubFollowStop = nil
|
||||||
}
|
}
|
||||||
if a.ultrabeam != nil {
|
if a.ultrabeam != nil {
|
||||||
a.ultrabeam.Stop()
|
// Background teardown so saving Settings doesn't block on an in-progress
|
||||||
|
// connect (Stop waits for the dial timeout).
|
||||||
|
go a.ultrabeam.Stop()
|
||||||
a.ultrabeam = nil
|
a.ultrabeam = nil
|
||||||
}
|
}
|
||||||
s, err := a.GetUltrabeamSettings()
|
s, err := a.GetUltrabeamSettings()
|
||||||
@@ -7896,7 +7898,9 @@ func (a *App) SaveAntGeniusSettings(s AntGeniusSettings) error {
|
|||||||
// startAntGenius stops any existing client and starts a fresh one if enabled.
|
// startAntGenius stops any existing client and starts a fresh one if enabled.
|
||||||
func (a *App) startAntGenius() {
|
func (a *App) startAntGenius() {
|
||||||
if a.antgenius != nil {
|
if a.antgenius != nil {
|
||||||
a.antgenius.Stop()
|
// Background teardown so saving Settings doesn't block on an in-progress
|
||||||
|
// connect (Stop waits for the dial timeout).
|
||||||
|
go a.antgenius.Stop()
|
||||||
a.antgenius = nil
|
a.antgenius = nil
|
||||||
}
|
}
|
||||||
s, err := a.GetAntGeniusSettings()
|
s, err := a.GetAntGeniusSettings()
|
||||||
@@ -7932,7 +7936,7 @@ func (a *App) AntGeniusDeselect(port int) error {
|
|||||||
return a.antgenius.Activate(port, 0)
|
return a.antgenius.Activate(port, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PowerGenius XL (4O3A) amplifier fan control (TCP, default port 9006) ─────
|
// ── PowerGenius XL (4O3A) amplifier fan control (TCP, default port 9008) ─────
|
||||||
|
|
||||||
// PGXLSettings is the JSON shape for the Hardware → PowerGenius panel.
|
// PGXLSettings is the JSON shape for the Hardware → PowerGenius panel.
|
||||||
type PGXLSettings struct {
|
type PGXLSettings struct {
|
||||||
@@ -7942,7 +7946,7 @@ type PGXLSettings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetPGXLSettings() (PGXLSettings, error) {
|
func (a *App) GetPGXLSettings() (PGXLSettings, error) {
|
||||||
out := PGXLSettings{Port: 9006}
|
out := PGXLSettings{Port: 9008}
|
||||||
if a.settings == nil {
|
if a.settings == nil {
|
||||||
return out, fmt.Errorf("db not initialized")
|
return out, fmt.Errorf("db not initialized")
|
||||||
}
|
}
|
||||||
@@ -7963,7 +7967,7 @@ func (a *App) SavePGXLSettings(s PGXLSettings) error {
|
|||||||
return fmt.Errorf("db not initialized")
|
return fmt.Errorf("db not initialized")
|
||||||
}
|
}
|
||||||
if s.Port <= 0 || s.Port > 65535 {
|
if s.Port <= 0 || s.Port > 65535 {
|
||||||
s.Port = 9006
|
s.Port = 9008
|
||||||
}
|
}
|
||||||
for k, v := range map[string]string{
|
for k, v := range map[string]string{
|
||||||
keyPGXLEnabled: boolStr(s.Enabled),
|
keyPGXLEnabled: boolStr(s.Enabled),
|
||||||
@@ -7981,7 +7985,10 @@ func (a *App) SavePGXLSettings(s PGXLSettings) error {
|
|||||||
// startPGXL stops any existing client and starts a fresh one if enabled.
|
// startPGXL stops any existing client and starts a fresh one if enabled.
|
||||||
func (a *App) startPGXL() {
|
func (a *App) startPGXL() {
|
||||||
if a.pgxl != nil {
|
if a.pgxl != nil {
|
||||||
a.pgxl.Stop()
|
// Stop() can block up to the dial timeout waiting for an in-progress
|
||||||
|
// connect; tear down in the background so saving Settings (this runs on
|
||||||
|
// the Wails RPC goroutine) doesn't freeze the UI.
|
||||||
|
go a.pgxl.Stop()
|
||||||
a.pgxl = nil
|
a.pgxl = nil
|
||||||
}
|
}
|
||||||
s, err := a.GetPGXLSettings()
|
s, err := a.GetPGXLSettings()
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ export function FlexPanel() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// PowerGenius XL direct connection (fan mode), independent of the Flex link.
|
// PowerGenius XL direct connection (fan mode), independent of the Flex link.
|
||||||
const [pg, setPg] = useState<{ connected: boolean; fan_mode?: string }>({ connected: false });
|
const [pg, setPg] = useState<{ connected: boolean; fan_mode?: string; host?: string; last_error?: string }>({ connected: false });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let alive = true;
|
let alive = true;
|
||||||
const tick = async () => { try { const s: any = await GetPGXLStatus(); if (alive) setPg(s || { connected: false }); } catch {} };
|
const tick = async () => { try { const s: any = await GetPGXLStatus(); if (alive) setPg(s || { connected: false }); } catch {} };
|
||||||
@@ -441,15 +441,18 @@ export function FlexPanel() {
|
|||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{st.amp_operate ? 'Amplifier is in line (transmitting through PA).' : 'Amplifier bypassed (standby).'}
|
{st.amp_operate ? 'Amplifier is in line (transmitting through PA).' : 'Amplifier bypassed (standby).'}
|
||||||
</span>
|
</span>
|
||||||
{/* Fan mode — only when the PowerGenius direct connection is up
|
{/* Fan mode — shown when the PowerGenius is configured (Settings →
|
||||||
(configured in Settings → PowerGenius). */}
|
PowerGenius). The dot shows the direct-connection state; the
|
||||||
{pg.connected && (
|
selector is disabled until connected (hover it for the error). */}
|
||||||
<label className="flex items-center gap-1.5 text-xs">
|
{(pg.host || pg.connected) && (
|
||||||
|
<label className="flex items-center gap-1.5 text-xs" title={pg.connected ? 'PowerGenius connected' : (pg.last_error || 'PowerGenius offline')}>
|
||||||
|
<span className={cn('size-1.5 rounded-full', pg.connected ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-rose-500')} />
|
||||||
<span className="text-muted-foreground">Fan</span>
|
<span className="text-muted-foreground">Fan</span>
|
||||||
<select
|
<select
|
||||||
|
disabled={!pg.connected}
|
||||||
value={(pg.fan_mode || 'CONTEST').toUpperCase()}
|
value={(pg.fan_mode || 'CONTEST').toUpperCase()}
|
||||||
onChange={(e) => { const v = e.target.value; setPg((s) => ({ ...s, fan_mode: v })); PGXLSetFanMode(v).catch(() => {}); }}
|
onChange={(e) => { const v = e.target.value; setPg((s) => ({ ...s, fan_mode: v })); PGXLSetFanMode(v).catch(() => {}); }}
|
||||||
className="h-8 rounded-md border border-orange-300 bg-card px-2 text-xs font-semibold text-orange-800 outline-none focus:border-orange-500"
|
className="h-8 rounded-md border border-orange-300 bg-card px-2 text-xs font-semibold text-orange-800 outline-none focus:border-orange-500 disabled:opacity-40"
|
||||||
>
|
>
|
||||||
<option value="STANDARD">Standard</option>
|
<option value="STANDARD">Standard</option>
|
||||||
<option value="CONTEST">Contest</option>
|
<option value="CONTEST">Contest</option>
|
||||||
|
|||||||
@@ -639,7 +639,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
const [antgenius, setAntgenius] = useState<{ enabled: boolean; host: string }>({ enabled: false, host: '' });
|
const [antgenius, setAntgenius] = useState<{ enabled: boolean; host: string }>({ enabled: false, host: '' });
|
||||||
|
|
||||||
// PowerGenius XL (4O3A) amp fan-control settings.
|
// PowerGenius XL (4O3A) amp fan-control settings.
|
||||||
const [pgxl, setPgxl] = useState<{ enabled: boolean; host: string; port: number }>({ enabled: false, host: '', port: 9006 });
|
const [pgxl, setPgxl] = useState<{ enabled: boolean; host: string; port: number }>({ enabled: false, host: '', port: 9008 });
|
||||||
|
|
||||||
// WinKeyer CW keyer settings + macro editor.
|
// WinKeyer CW keyer settings + macro editor.
|
||||||
type WKMac = { label: string; text: string };
|
type WKMac = { label: string; text: string };
|
||||||
@@ -1962,7 +1962,6 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
<>
|
<>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title="Antenna (Ultrabeam)"
|
title="Antenna (Ultrabeam)"
|
||||||
hint="OpsLog talks to the Ultrabeam controller over TCP — typically via an RS232↔Ethernet adapter. Enter its IP address and port; the pattern direction (Normal / 180° / Bidirectional) is then controlled from the status bar."
|
|
||||||
/>
|
/>
|
||||||
<div className="space-y-4 max-w-xl">
|
<div className="space-y-4 max-w-xl">
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
@@ -2024,9 +2023,6 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
{ubTest.msg}
|
{ubTest.msg}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Once enabled, the antenna's direction control (Normal / 180° / Bidirectional) appears in the bottom status bar, next to CAT and Rotator. Changing the direction re-tunes the elements at the current frequency.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -2052,11 +2048,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
placeholder="192.168.1.60"
|
placeholder="192.168.1.60"
|
||||||
className="font-mono"
|
className="font-mono"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">TCP port is fixed at 9007.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Once enabled, an Antenna Genius button appears in the top bar to show/hide the antenna-switch widget. In the widget, the A and B buttons select that antenna for the matching port; clicking an already-selected port deselects it (sets the port to None).
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -2088,14 +2080,11 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
<Input
|
<Input
|
||||||
type="number" min={1} max={65535}
|
type="number" min={1} max={65535}
|
||||||
value={pgxl.port}
|
value={pgxl.port}
|
||||||
onChange={(e) => setPgxl((s) => ({ ...s, port: parseInt(e.target.value) || 9006 }))}
|
onChange={(e) => setPgxl((s) => ({ ...s, port: parseInt(e.target.value) || 9008 }))}
|
||||||
className="font-mono"
|
className="font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Default port is 9006. Once enabled, a fan-mode dropdown (Standard / Contest / Broadcast) appears next to the amplifier Operate button in the FlexRadio tab.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -2179,7 +2168,6 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
|||||||
<>
|
<>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title="CW Keyer"
|
title="CW Keyer"
|
||||||
hint="Drive a K1EL WinKeyer (WK1/2/3) over a serial port to send Morse from OpsLog. Enable it, pick the COM port and keying parameters, then connect from the keyer panel (Tools → WinKeyer CW keyer)."
|
|
||||||
/>
|
/>
|
||||||
<div className="space-y-4 max-w-2xl">
|
<div className="space-y-4 max-w-2xl">
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
|
|||||||
@@ -339,7 +339,10 @@ func (f *Flex) handleStatus(payload string) {
|
|||||||
// Transmit object — RF/tune power, VOX, speech processor, monitor, mic,
|
// Transmit object — RF/tune power, VOX, speech processor, monitor, mic,
|
||||||
// tune carrier. Field names per the SmartSDR API (logged so the exact set
|
// tune carrier. Field names per the SmartSDR API (logged so the exact set
|
||||||
// is auditable against a real radio).
|
// is auditable against a real radio).
|
||||||
if len(fields) >= 1 && fields[0] == "transmit" {
|
// "transmit band <N> band_name=… rfpower=…" lines are PER-BAND power
|
||||||
|
// presets, not the current TX state — ignore them, otherwise the last
|
||||||
|
// band's rfpower (e.g. 630m=100) clobbers the real current value.
|
||||||
|
if len(fields) >= 1 && fields[0] == "transmit" && !(len(fields) >= 2 && fields[1] == "band") {
|
||||||
if !f.txRawLogged {
|
if !f.txRawLogged {
|
||||||
f.txRawLogged = true
|
f.txRawLogged = true
|
||||||
debugLog.Printf("Flex: FIRST transmit status: %s", payload)
|
debugLog.Printf("Flex: FIRST transmit status: %s", payload)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultPort = 9006
|
defaultPort = 9008
|
||||||
dialTimeout = 5 * time.Second
|
dialTimeout = 5 * time.Second
|
||||||
ioTimeout = 3 * time.Second
|
ioTimeout = 3 * time.Second
|
||||||
pollEvery = 1500 * time.Millisecond
|
pollEvery = 1500 * time.Millisecond
|
||||||
|
|||||||
Reference in New Issue
Block a user