feat(rotator): add Hy-Gain DCU-1 backend (RotorCard DXA, Rotor-EZ, Green Heron)
New internal/rotator/dcu1 client speaking the DCU-1 command set (AP1nnn / AM1 to go-to, AI1 to read bearing), over a serial COM port (4800 baud default) or a raw TCP serial-over-IP bridge. Azimuth-only; Stop re-commands the current bearing since the base set has no stop opcode. Wired through app.go (normRotorType, default port 4001, GoTo/Heading/Stop/test switches, park returns the same "PstRotator only" note as ARCO) and exposed in the rotor settings as "Hy-Gain DCU-1 (RotorCard DXA, Rotor-EZ, Green Heron)" with the ARCO's serial/TCP transport chooser and a bilingual hint. Protocol is implemented from the standard DCU-1 spec and still needs field confirmation against a real RotorCard DXA.
This commit is contained in:
@@ -3388,6 +3388,8 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
const dev = d as any;
|
||||
const isRG = dev.type === 'rotgenius';
|
||||
const isARCO = dev.type === 'arco';
|
||||
const isDCU1 = dev.type === 'dcu1';
|
||||
const isSerialCap = isARCO || isDCU1; // COM-port or serial-over-IP controllers
|
||||
const transport = dev.transport ?? 'tcp';
|
||||
return (
|
||||
<div key={dev.id || i} className="rounded-xl border border-border bg-card/40 p-3 space-y-3">
|
||||
@@ -3406,12 +3408,13 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
{/* Each backend gets its default port: Rotator Genius 9006, ARCO 4001
|
||||
(placeholder — must match the ARCO's LAN menu), PstRotator 12000. */}
|
||||
<Select value={dev.type ?? 'pst'}
|
||||
onValueChange={(v) => patch(i, { type: v as any, port: v === 'rotgenius' ? 9006 : v === 'arco' ? 4001 : 12000 })}>
|
||||
onValueChange={(v) => patch(i, { type: v as any, port: v === 'rotgenius' ? 9006 : (v === 'arco' || v === 'dcu1') ? 4001 : 12000, ...(v === 'dcu1' ? { transport: 'serial' } : {}) })}>
|
||||
<SelectTrigger className="h-9"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="pst">PstRotator (UDP)</SelectItem>
|
||||
<SelectItem value="rotgenius">Rotator Genius (4O3A, native)</SelectItem>
|
||||
<SelectItem value="arco">GS-232A controller (microHAM ARCO, ERC…)</SelectItem>
|
||||
<SelectItem value="dcu1">Hy-Gain DCU-1 (RotorCard DXA, Rotor-EZ, Green Heron)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -3428,8 +3431,8 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
{/* The ARCO is reachable over the LAN (TCP) or its USB virtual COM. */}
|
||||
{isARCO && (
|
||||
{/* ARCO and DCU-1 controllers reach over the LAN (TCP) or a serial COM. */}
|
||||
{isSerialCap && (
|
||||
<div className="space-y-1">
|
||||
<Label>Connection</Label>
|
||||
<Select value={transport} onValueChange={(v) => patch(i, { transport: v as any })}>
|
||||
@@ -3449,7 +3452,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
{t('rot.rgDual')}
|
||||
</label>
|
||||
)}
|
||||
{isARCO && transport === 'serial' ? (
|
||||
{isSerialCap && transport === 'serial' ? (
|
||||
<div className="space-y-1 max-w-xs">
|
||||
<Label>COM port</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -3478,16 +3481,16 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
<div className="space-y-1 col-span-2">
|
||||
<Label>Host / IP</Label>
|
||||
<Input value={dev.host ?? ''} onChange={(e) => patch(i, { host: e.target.value })}
|
||||
placeholder={isRG || isARCO ? '192.168.1.60' : '127.0.0.1'} className="font-mono" />
|
||||
placeholder={isRG || isSerialCap ? '192.168.1.60' : '127.0.0.1'} className="font-mono" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>{isRG || isARCO ? 'TCP port' : 'UDP port'}</Label>
|
||||
<Label>{isRG || isSerialCap ? 'TCP port' : 'UDP port'}</Label>
|
||||
<PortInput value={dev.port} onChange={(n) => patch(i, { port: n })}
|
||||
fallback={isRG ? 9006 : isARCO ? 4001 : 12000} className="font-mono" />
|
||||
fallback={isRG ? 9006 : isSerialCap ? 4001 : 12000} className="font-mono" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isRG && !isARCO && (
|
||||
{!isRG && !isSerialCap && (
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<Checkbox checked={!!dev.has_elevation} onCheckedChange={(c) => patch(i, { has_elevation: !!c })} />
|
||||
This rotator supports elevation (VHF / satellite)
|
||||
@@ -3495,6 +3498,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
)}
|
||||
{isRG && <p className="text-xs text-muted-foreground">{t('rot.rgHint')}</p>}
|
||||
{isARCO && <p className="text-xs text-muted-foreground">{t('rot.arcoHint')}</p>}
|
||||
{isDCU1 && <p className="text-xs text-muted-foreground">{t('rot.dcu1Hint')}</p>}
|
||||
{/* Which antenna this rotor carries — only relevant with >1 rotor. */}
|
||||
{multi && (
|
||||
<div className="space-y-1 max-w-xs">
|
||||
|
||||
Reference in New Issue
Block a user