feat: offset + CTCSS tone BCD codecs (provisional encodings)

This commit is contained in:
2026-06-14 16:05:48 -04:00
parent 03000708b6
commit 5c597a10b8
2 changed files with 77 additions and 0 deletions
+46
View File
@@ -24,6 +24,17 @@ SUB_SQL_LEVEL = 0x03
SUB_SQL_STATUS = 0x01
SUB_SMETER = 0x02
# --- repeater config (candidate commands; verify on live radio) ---
CMD_OFFSET = 0x0D # set duplex offset frequency
CMD_DUPLEX = 0x0F # set duplex direction
DUP_SIMPLEX = 0x10
DUP_MINUS = 0x11
DUP_PLUS = 0x12
CMD_TONE_PARAM = 0x1B # sub 0x00 = repeater (TX) tone frequency
SUB_RPT_TONE = 0x00
CMD_TONE_SWITCH = 0x16 # sub 0x42 = tone encode on/off
SUB_TONE_ENC = 0x42
Frame = namedtuple("Frame", "to frm cn data")
@@ -66,6 +77,41 @@ def bcd_to_level(data):
return value
def offset_to_bcd(hz):
"""Duplex offset Hz -> 3-byte little-endian BCD in 100 Hz units (PROVISIONAL)."""
if hz < 0:
raise ValueError(f"offset {hz} must be >= 0")
units = round(hz / 100)
digits = f"{units:06d}"
out = bytearray()
for i in range(0, 6, 2):
out.append((int(digits[4 - i]) << 4) | int(digits[5 - i]))
return bytes(out)
def bcd_to_offset(data):
"""3-byte little-endian BCD (100 Hz units) -> Hz (PROVISIONAL)."""
units = 0
for i, byte in enumerate(data[:3]):
units += (byte & 0x0F) * (10 ** (2 * i)) + (byte >> 4) * (10 ** (2 * i + 1))
return units * 100
def tone_to_bcd(hz):
"""CTCSS tone Hz -> 2-byte BCD of tone*10 (88.5 -> 08 85)."""
digits = f"{round(hz * 10):04d}"
return bytes([(int(digits[0]) << 4) | int(digits[1]),
(int(digits[2]) << 4) | int(digits[3])])
def bcd_to_tone(data):
"""2-byte BCD of tone*10 -> CTCSS tone Hz."""
t = 0
for byte in data[:2]:
t = t * 100 + (byte >> 4) * 10 + (byte & 0x0F)
return t / 10.0
def build_frame(cmd, data=b"", to=RADIO_ADDR, frm=CTRL_ADDR):
"""Assemble FE FE <to> <from> <cmd> [data] FD."""
return PREAMBLE + bytes([to, frm, cmd]) + bytes(data) + bytes([EOM])