mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-10-23 04:11:29 +00:00
155 lines
5.1 KiB
Python
155 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Generates delta {reg,val} tables between the 3 hopper presets (AM650, FM476, FM95) for fast hopping without SRES.
|
||
|
||
Uses CC1101 power-on-reset (POR) values for registers that a preset doesn't explicitly set, since the current flow performs SRES before loading each preset (which resets those registers to their default values).
|
||
|
||
Sources:
|
||
- AM650 = subghz_device_cc1101_preset_ook_650khz_async_regs (cc1101_configs.c)
|
||
- FM476 = subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs (cc1101_configs.c)
|
||
- FM95 = Custom_preset_data in setting_user
|
||
"""
|
||
|
||
# POR values (CC1101 datasheet Table 23) for records that
|
||
# appear in at least one of the 3 presets but not all.
|
||
POR_DEFAULTS = {
|
||
0x02: 0x3F, # IOCFG0
|
||
0x03: 0x07, # FIFOTHR
|
||
0x07: 0x04, # PKTCTRL1
|
||
0x08: 0x45, # PKTCTRL0
|
||
0x0B: 0x0F, # FSCTRL1
|
||
0x10: 0x8C, # MDMCFG4
|
||
0x11: 0x22, # MDMCFG3
|
||
0x12: 0x02, # MDMCFG2
|
||
0x13: 0x22, # MDMCFG1
|
||
0x14: 0xF8, # MDMCFG0
|
||
0x15: 0x47, # DEVIATN
|
||
0x18: 0x18, # MCSM0
|
||
0x19: 0x14, # FOCCFG
|
||
0x1B: 0x03, # AGCCTRL2
|
||
0x1C: 0x40, # AGCCTRL1
|
||
0x1D: 0x91, # AGCCTRL0
|
||
0x20: 0xFB, # WORCTRL
|
||
0x21: 0x56, # FREND1
|
||
0x22: 0x10, # FREND0
|
||
}
|
||
|
||
# Registers that, if changed, force SCAL (synthesizer recalibration)
|
||
FREQ_SYNTH_REGS = {0x0B, 0x0C, 0x0D, 0x0E, 0x0F} # FSCTRL1/0, FREQ2/1/0
|
||
|
||
# Explicit definitions (only what each preset actually writes)
|
||
AM650 = {
|
||
0x02: 0x0D, 0x03: 0x07, 0x08: 0x32, 0x0B: 0x06,
|
||
0x10: 0x17, 0x11: 0x32, 0x12: 0x30, 0x13: 0x00, 0x14: 0x00,
|
||
0x18: 0x18, 0x19: 0x18, 0x1B: 0x07, 0x1C: 0x00, 0x1D: 0x91,
|
||
0x20: 0xFB, 0x21: 0xB6, 0x22: 0x11,
|
||
}
|
||
AM650_PA = [0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||
|
||
FM476 = {
|
||
0x02: 0x0D, 0x07: 0x04, 0x08: 0x32, 0x0B: 0x06,
|
||
0x10: 0x67, 0x11: 0x83, 0x12: 0x04, 0x13: 0x02, 0x14: 0x00, 0x15: 0x47,
|
||
0x18: 0x18, 0x19: 0x16, 0x1B: 0x07, 0x1C: 0x00, 0x1D: 0x91,
|
||
0x20: 0xFB, 0x21: 0x56, 0x22: 0x10,
|
||
}
|
||
FM476_PA = [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||
|
||
FM95 = {
|
||
0x02: 0x0D, 0x07: 0x04, 0x08: 0x32, 0x0B: 0x06,
|
||
0x10: 0x67, 0x11: 0x83, 0x12: 0x04, 0x13: 0x02, 0x15: 0x24,
|
||
0x18: 0x18, 0x19: 0x16, 0x1B: 0x07, 0x1C: 0x00, 0x1D: 0x91,
|
||
0x20: 0xFB, 0x21: 0x56, 0x22: 0x10,
|
||
}
|
||
FM95_PA = [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||
|
||
PRESETS = {
|
||
"AM650": (AM650, AM650_PA),
|
||
"FM476": (FM476, FM476_PA),
|
||
"FM95": (FM95, FM95_PA),
|
||
}
|
||
|
||
# Union of all records played by at least one preset
|
||
ALL_REGS = sorted(set().union(*[set(r.keys()) for r, _ in PRESETS.values()]))
|
||
|
||
|
||
def effective(regs):
|
||
"""Applies POR defaults to ALL_REGS records not explicitly set."""
|
||
out = {}
|
||
for reg in ALL_REGS:
|
||
if reg in regs:
|
||
out[reg] = regs[reg]
|
||
elif reg in POR_DEFAULTS:
|
||
out[reg] = POR_DEFAULTS[reg]
|
||
else:
|
||
raise ValueError(f"Reg 0x{reg:02X} without known default POR")
|
||
return out
|
||
|
||
|
||
def gen_delta(name_a, name_b):
|
||
eff_a = effective(PRESETS[name_a][0])
|
||
eff_b = effective(PRESETS[name_b][0])
|
||
pa_a, pa_b = PRESETS[name_a][1], PRESETS[name_b][1]
|
||
|
||
delta = []
|
||
needs_scal = False
|
||
for reg in ALL_REGS:
|
||
if eff_a[reg] != eff_b[reg]:
|
||
delta.append((reg, eff_b[reg]))
|
||
if reg in FREQ_SYNTH_REGS:
|
||
needs_scal = True
|
||
|
||
pa_changed = (pa_a != pa_b)
|
||
return delta, needs_scal, (pa_b if pa_changed else None)
|
||
|
||
|
||
def main():
|
||
names = list(PRESETS.keys())
|
||
print("// === Auto-generated by gen_preset_delta.py ===")
|
||
print("// subghz_preset_delta.c\n")
|
||
print('#include <stdint.h>')
|
||
print('#include <stddef.h>')
|
||
print('#include "subghz_preset_delta.h"\n')
|
||
|
||
entries = {}
|
||
for a in names:
|
||
for b in names:
|
||
if a == b:
|
||
continue
|
||
delta, needs_scal, pa = gen_delta(a, b)
|
||
sym = f"preset_delta_{a}_to_{b}"
|
||
print(f"// {a} -> {b}: {len(delta)} records change"
|
||
f"{' (+SCAL)' if needs_scal else ''}"
|
||
f"{' (+PA)' if pa else ''}")
|
||
print(f"static const uint8_t {sym}[] = {{")
|
||
for reg, val in delta:
|
||
print(f" 0x{reg:02X}, 0x{val:02X},")
|
||
print("};")
|
||
if pa:
|
||
pa_sym = f"{sym}_pa"
|
||
print(f"static const uint8_t {pa_sym}[8] = {{ " +
|
||
", ".join(f"0x{x:02X}" for x in pa) + " };")
|
||
else:
|
||
pa_sym = "NULL"
|
||
print()
|
||
entries[(a, b)] = (sym, len(delta) * 2, needs_scal, pa_sym)
|
||
|
||
# Lookup table
|
||
print("// Order: HOP_AM650=0, HOP_FM476=1, HOP_FM95=2")
|
||
print("const PresetDeltaEntry preset_delta_table[HOP_PRESET_COUNT][HOP_PRESET_COUNT] = {")
|
||
for a in names:
|
||
print(" {")
|
||
for b in names:
|
||
if a == b:
|
||
print(" {0}, // self, unused")
|
||
continue
|
||
sym, dlen, scal, pa_sym = entries[(a, b)]
|
||
print(f" {{ .delta = {sym}, .delta_len = {dlen}, "
|
||
f".needs_scal = {'true' if scal else 'false'}, "
|
||
f".pa_table = {pa_sym} }}, // {a} -> {b}")
|
||
print(" },")
|
||
print("};")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|