feat(antenna): three tracking modes for Ultrabeam and SteppIR
The follow loop only ever had one behaviour — re-tune once the rig moved further than a fixed step. The SteppIR's own controller software offers three, and operators arrive with that mental model: every frequency change, past a threshold, or only on a band change. They are real operating trade-offs, not preferences: "always" keeps resonance perfect at the cost of motors running constantly (and on a SteppIR every move inhibits transmit while the elements travel), "band" moves them a handful of times a day. "Always" is implemented as the threshold mode with a 1 kHz threshold rather than as a separate branch, so all modes keep the one deadband reference that matters: the rig frequency last commanded for, NOT the antenna's own reported frequency — a SteppIR flips its reported frequency between the commanded value and its home value, which would re-issue a SET on nearly every poll and leave the operator permanently unable to transmit. Band mode compares against the band last COMMANDED for, not the rig's previous band, so the first move after startup and any move made by hand still get reconciled. It applies to the immediate spot-click path too: a click inside the band the antenna is already resonant in moves nothing. An unset or unrecognised mode resolves to the step mode, so every config written before this option behaves exactly as it did. Also routes the antenna settings block through t() — it was hardcoded English.
This commit is contained in:
@@ -1250,8 +1250,8 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
const [rotatorTest, setRotatorTest] = useState<{ ok: boolean; msg: string } | null>(null);
|
||||
|
||||
// Motorized antenna (Ultrabeam TCP or SteppIR TCP/serial) settings.
|
||||
const [ultrabeam, setUltrabeam] = useState<{ enabled: boolean; type: string; transport: string; host: string; port: number; com: string; baud: number; follow: boolean; step_khz: number; tx_inhibit: boolean; bands: string[]; freq_min_mhz: number; freq_max_mhz: number }>({
|
||||
enabled: false, type: 'ultrabeam', transport: 'tcp', host: '', port: 23, com: '', baud: 9600, follow: false, step_khz: 50, tx_inhibit: false, bands: [], freq_min_mhz: 13, freq_max_mhz: 54,
|
||||
const [ultrabeam, setUltrabeam] = useState<{ enabled: boolean; type: string; transport: string; host: string; port: number; com: string; baud: number; follow: boolean; step_khz: number; track_mode: string; tx_inhibit: boolean; bands: string[]; freq_min_mhz: number; freq_max_mhz: number }>({
|
||||
enabled: false, type: 'ultrabeam', transport: 'tcp', host: '', port: 23, com: '', baud: 9600, follow: false, step_khz: 50, track_mode: 'step', tx_inhibit: false, bands: [], freq_min_mhz: 13, freq_max_mhz: 54,
|
||||
});
|
||||
const [ubTesting, setUbTesting] = useState(false);
|
||||
const [ubTest, setUbTest] = useState<{ ok: boolean; msg: string } | null>(null);
|
||||
@@ -3140,20 +3140,41 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
<div className="border-t border-border/60 pt-3 space-y-2">
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<Checkbox checked={ultrabeam.follow} onCheckedChange={(c) => setUltrabeam((s) => ({ ...s, follow: !!c }))} />
|
||||
Follow rig frequency (auto-tune the antenna)
|
||||
{t('hw.motorFollow')}
|
||||
</label>
|
||||
{ultrabeam.follow && (
|
||||
<div className="flex items-center gap-3 pl-6">
|
||||
<Label className="text-sm">Re-tune step</Label>
|
||||
<Select value={String(ultrabeam.step_khz)} onValueChange={(v) => setUltrabeam((s) => ({ ...s, step_khz: parseInt(v, 10) || 50 }))}>
|
||||
<SelectTrigger className="h-8 w-32"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="25">25 kHz</SelectItem>
|
||||
<SelectItem value="50">50 kHz</SelectItem>
|
||||
<SelectItem value="100">100 kHz</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="text-xs text-muted-foreground">re-tune only when the frequency moves this far</span>
|
||||
<div className="space-y-2 pl-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Label className="text-sm">{t('station.trackModeTip')}</Label>
|
||||
<Select value={ultrabeam.track_mode || 'step'} onValueChange={(v) => setUltrabeam((s) => ({ ...s, track_mode: v }))}>
|
||||
<SelectTrigger className="h-8 w-52"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="always">{t('station.trackAlways')}</SelectItem>
|
||||
<SelectItem value="step">{t('station.trackStep')}</SelectItem>
|
||||
<SelectItem value="band">{t('station.trackBand')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{/* The step is only a question in step mode — the other two modes
|
||||
have nothing to threshold. */}
|
||||
{(ultrabeam.track_mode || 'step') === 'step' && (
|
||||
<div className="flex items-center gap-3">
|
||||
<Label className="text-sm">{t('hw.motorStep')}</Label>
|
||||
<Select value={String(ultrabeam.step_khz)} onValueChange={(v) => setUltrabeam((s) => ({ ...s, step_khz: parseInt(v, 10) || 50 }))}>
|
||||
<SelectTrigger className="h-8 w-32"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="25">25 kHz</SelectItem>
|
||||
<SelectItem value="50">50 kHz</SelectItem>
|
||||
<SelectItem value="100">100 kHz</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{(ultrabeam.track_mode || 'step') === 'always' ? t('station.trackAlwaysTip')
|
||||
: (ultrabeam.track_mode || 'step') === 'band' ? t('station.trackBandTip')
|
||||
: t('station.trackStepTipMode')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user