fix: ground-truth CI-V offset/tone on live ID-5100
- offset amount (0x0D) is REJECTED (NG) by the radio -> removed the offset codec + set_offset/get_offset; tune_repeater now sets only duplex direction (radio applies its configured auto-offset) - repeater tone (0x1B/00) uses a 3-byte field (00 + 2-byte BCD); set_tone prepends the pad, get_tone strips it - duplex (0x0F) and tone-switch (0x16/42) verified working - added get_duplex; verified full tune_repeater on a live repeater (all steps OK)
This commit is contained in:
@@ -157,35 +157,39 @@ class Radio:
|
||||
data = self.transact(civ.CMD_METER, bytes([civ.SUB_SQL_STATUS])).data
|
||||
return bool(data[1]) if len(data) > 1 else False
|
||||
|
||||
# ---- repeater config (candidate commands; verify live) ----------------
|
||||
# ---- repeater config (verified on live ID-5100 2026-06-14) ------------
|
||||
# Offset *amount* is not CI-V-settable on this radio (cmd 0x0D -> NG); the
|
||||
# radio applies its configured auto-offset when a duplex direction is set.
|
||||
def set_duplex(self, direction):
|
||||
"""direction: '+', '-', or 'simplex'."""
|
||||
"""direction: '+', '-', or 'simplex'. Radio uses its configured offset."""
|
||||
code = {"+": civ.DUP_PLUS, "-": civ.DUP_MINUS,
|
||||
"simplex": civ.DUP_SIMPLEX}.get(direction)
|
||||
if code is None:
|
||||
raise ValueError(f"bad duplex {direction!r}")
|
||||
return self._command(civ.CMD_DUPLEX, bytes([code]))
|
||||
|
||||
def set_offset(self, hz):
|
||||
return self._command(civ.CMD_OFFSET, civ.offset_to_bcd(hz))
|
||||
|
||||
def get_offset(self):
|
||||
return civ.bcd_to_offset(self.transact(civ.CMD_OFFSET).data)
|
||||
def get_duplex(self):
|
||||
code = self.transact(civ.CMD_DUPLEX).data[0]
|
||||
return {civ.DUP_PLUS: "+", civ.DUP_MINUS: "-",
|
||||
civ.DUP_SIMPLEX: "simplex"}.get(code, f"0x{code:02X}")
|
||||
|
||||
def set_tone(self, hz):
|
||||
# wire format: sub(0x00) + 0x00 pad + 2-byte BCD (00 00 08 85 = 88.5 Hz)
|
||||
return self._command(civ.CMD_TONE_PARAM,
|
||||
bytes([civ.SUB_RPT_TONE]) + civ.tone_to_bcd(hz))
|
||||
bytes([civ.SUB_RPT_TONE, 0x00]) + civ.tone_to_bcd(hz))
|
||||
|
||||
def get_tone(self):
|
||||
# reply data = sub + pad + 2-byte BCD; take the last two bytes
|
||||
return civ.bcd_to_tone(self.transact(civ.CMD_TONE_PARAM,
|
||||
bytes([civ.SUB_RPT_TONE])).data[1:])
|
||||
bytes([civ.SUB_RPT_TONE])).data[2:])
|
||||
|
||||
def set_tone_on(self, on):
|
||||
return self._command(civ.CMD_TONE_SWITCH,
|
||||
bytes([civ.SUB_TONE_ENC, 0x01 if on else 0x00]))
|
||||
|
||||
def tune_repeater(self, rec):
|
||||
"""Apply a normalized record: freq -> duplex/offset -> tone.
|
||||
"""Apply a normalized record: freq -> duplex direction -> tone.
|
||||
Offset amount comes from the radio's own config (not CI-V-settable).
|
||||
Returns (steps, errors): each step True/False; errors keyed by step."""
|
||||
steps, errors = {}, {}
|
||||
|
||||
@@ -196,10 +200,7 @@ class Radio:
|
||||
steps[name] = False; errors[name] = str(e)
|
||||
|
||||
attempt("freq", lambda: self.set_frequency(int(round(rec["output_mhz"] * 1e6))))
|
||||
duplex = rec.get("duplex", "simplex")
|
||||
attempt("duplex", lambda: self.set_duplex(duplex))
|
||||
if duplex != "simplex":
|
||||
attempt("offset", lambda: self.set_offset(int(round(rec.get("offset_mhz", 0) * 1e6))))
|
||||
attempt("duplex", lambda: self.set_duplex(rec.get("duplex", "simplex")))
|
||||
if rec.get("tone_mode") == "ctcss" and rec.get("tone_hz"):
|
||||
attempt("tone_freq", lambda: self.set_tone(rec["tone_hz"]))
|
||||
attempt("tone_on", lambda: self.set_tone_on(True))
|
||||
|
||||
Reference in New Issue
Block a user