feat(rotator): multiple rotors of any type, plus a CAT PTT hotkey

Settings -> Rotator is now a list like the amplifiers: add/remove rotors,
mix PstRotator / Rotator Genius / ARCO, and a Rotator Genius can drive both
its ports (one entry = two rotors). The compass gains a rotor selector, and
a per-rotor motorized-antenna flag so the boom/pattern paths show only for
the rotor carrying the Ultrabeam/SteppIR.

Also in this batch: a keyboard PTT hotkey (hold-to-talk or toggle, reusing
the audio PTT method with a CAT fallback), and TCI spot push to the
panadapter with click-to-fill of the callsign.
This commit is contained in:
2026-08-04 09:01:21 +02:00
parent 9846354bf9
commit b3fdd0b7fa
11 changed files with 964 additions and 441 deletions
+50 -28
View File
@@ -1945,6 +1945,9 @@ export namespace main {
digital_default: string;
share_enabled: boolean;
share_port: number;
ptt_hotkey_enabled: boolean;
ptt_hotkey: string;
ptt_hotkey_toggle: boolean;
static createFrom(source: any = {}) {
return new CATSettings(source);
@@ -1987,6 +1990,9 @@ export namespace main {
this.digital_default = source["digital_default"];
this.share_enabled = source["share_enabled"];
this.share_port = source["share_port"];
this.ptt_hotkey_enabled = source["ptt_hotkey_enabled"];
this.ptt_hotkey = source["ptt_hotkey"];
this.ptt_hotkey_toggle = source["ptt_hotkey_toggle"];
}
}
export class CabrilloResult {
@@ -2804,11 +2810,52 @@ export namespace main {
}
}
export class RotatorDevice {
id: string;
name: string;
type: string;
host: string;
port: number;
has_elevation: boolean;
rotator_num: number;
dual: boolean;
motorized: boolean;
name2: string;
motorized2: boolean;
transport: string;
com_port: string;
baud: number;
static createFrom(source: any = {}) {
return new RotatorDevice(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
this.type = source["type"];
this.host = source["host"];
this.port = source["port"];
this.has_elevation = source["has_elevation"];
this.rotator_num = source["rotator_num"];
this.dual = source["dual"];
this.motorized = source["motorized"];
this.name2 = source["name2"];
this.motorized2 = source["motorized2"];
this.transport = source["transport"];
this.com_port = source["com_port"];
this.baud = source["baud"];
}
}
export class RotatorHeading {
enabled: boolean;
ok: boolean;
azimuth: number;
raw: string;
rotors: string[];
active: number;
motorized: boolean;
static createFrom(source: any = {}) {
return new RotatorHeading(source);
@@ -2820,34 +2867,9 @@ export namespace main {
this.ok = source["ok"];
this.azimuth = source["azimuth"];
this.raw = source["raw"];
}
}
export class RotatorSettings {
enabled: boolean;
type: string;
host: string;
port: number;
has_elevation: boolean;
rotator_num: number;
transport: string;
com_port: string;
baud: number;
static createFrom(source: any = {}) {
return new RotatorSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.enabled = source["enabled"];
this.type = source["type"];
this.host = source["host"];
this.port = source["port"];
this.has_elevation = source["has_elevation"];
this.rotator_num = source["rotator_num"];
this.transport = source["transport"];
this.com_port = source["com_port"];
this.baud = source["baud"];
this.rotors = source["rotors"];
this.active = source["active"];
this.motorized = source["motorized"];
}
}
export class ScpStatus {