From 7c6c2dfea7dc38678d2e4ada8719c95389d2227c Mon Sep 17 00:00:00 2001 From: viktor Date: Fri, 24 Jul 2026 04:28:45 +0900 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B2=D0=B5=D1=80=D1=81=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=88=D0=B8=D0=B2=D0=BA=D0=B8=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8F=20FM100B:=20=D0=B0=D1=80=D1=85=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0=20+=20=D1=87=D1=82=D0=BE?= =?UTF-8?q?=20=D1=82=D0=B0=D0=BA=D0=BE=D0=B5=20=D0=B2=D0=BE=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/fm100b-firmware-re.md: образ = ARM(старт)+Thumb-2/FPU, 1.5МБ, без символов, база RAM ~0x0360xxxx. Карта регионов. КЛЮЧЕВОЕ: «Vocoder» в прошивке = WebRTC audio (шумодав NSX + AGC, открытый код), а НЕ AMBE. Сам AMBE — вне ARM-образа (DSP/ROM A6), в прошивке только интерфейс ATVoiceEnc/Dec + RecvAmbedata. Найдены open-source либы: EasyFlash (NV/калибровка), WebRTC (аудио). Честная оценка: свой вокодер для эфирного DMR невозможен (нет AMBE-совместимости); реально моддить — WebRTC-параметры, кодек-гейны, NV-калибровку, логику DMR. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NikMGoqQWWk9wy2ww2vJAr --- docs/fm100b-firmware-re.md | 51 ++++++++++++++++++++++++++++++++++++++ tools/tuning_read.py | 13 ++++++---- 2 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 docs/fm100b-firmware-re.md diff --git a/docs/fm100b-firmware-re.md b/docs/fm100b-firmware-re.md new file mode 100644 index 0000000..8d261d7 --- /dev/null +++ b/docs/fm100b-firmware-re.md @@ -0,0 +1,51 @@ +# FM100B (HF6853/Auctus A6) — реверс прошивки модуля и вокодера + +Образы: `firmware/FM100B_V1.2.0.32_20260130.bin` (1 527 808 Б), `FM100B_V1.2.0.13` (тот же размер). +Грузятся из внешнего SPI-flash модуля (см. [[rt4d-fm100b-is-hf6853]]). + +## Архитектура кода +- **ARM.** Вектор @0x0: `EA00000E` (B reset) + 7×`LDR PC,[PC,#0x14]` (undef/SWI/abort/IRQ/FIQ). +- **Reset @0x40:** маскирует IRQ/FIQ, ставит режимы CPSR (SYS/FIQ/IRQ стеки) — классический ARM-старт, + затем переключается в **Thumb-2 с FPU** (`b580` push, `ed4d` VPUSH, `f38b` MSR) — основной код Thumb-2. +- Значит ядро XCPU — ARM с VFP (ARM11/Cortex-A/R класс), не Cortex-M. База RAM ≈ 0x0360_xxxx + (по литералам векторов). Символов НЕТ (release), строки — PC-relative (базу в лоб по литералу не взять). + +## Карта регионов (по энтропии + строкам) +| Offset | Что | +|--------|-----| +| 0x000000 | вектора ARM + startup | +| 0x000100–0x0b0000 | основной код (Thumb-2), ent≈6.3 | +| 0x0b0000–0x0d0000 | разреженная регулярная таблица (нули + повтор `80 29`), ent 2.5–3.6 — коэфф./LUT (не AMBE-кодбук) | +| 0x0cc000–0x0d0000 | **символы/лог-строки аудио-подсистемы** (ATC_*, «vocoder mutex», «codec ad/da») | +| 0x0d0000–0x160000 | код/данные | +| 0x15b000–0x15e000 | **WebRTC-аудио**: `VocoderVersion\WebRTC\source\nsx_core.c` (шумодав NSX), `digital_agc.c` (AGC); функции `WebRtcNsx_ProcessCore`, `WebRtcAgc_CalculateGain` | +| ~0x160000 | строки/константы, `EasyFlash` (NV) | +| 0x170000–конец | хвост (padding) | + +## ⭐ Что такое «вокодер» в прошивке +**«Vocoder» здесь = WebRTC audio processing** (noise suppression NSX + digital AGC), т.е. пред/пост- +обработка ГОЛОСА, а **НЕ AMBE-кодек**. Это открытый код (github WebRTC) — можно понять и тюнить. + +**Сам AMBE (DMR-кодек) — вне модифицируемого ARM-кода.** В образе только ИНТЕРФЕЙС к нему: +`ATVoiceEncCnf`/`ATVoiceDecCnf`, `ATCRecvAmbedataCnf`/`ATCRecvAmbe6sdataCnf`, `vocoder mutex`. +AMBE выполняется на отдельном DSP-ядре/хардварном блоке A6 (RDA8809 двухъядерный; jhart99: +«вокодер на шине XCPU»). Отдельного DSP-блоба в этом образе по энтропии не видно → AMBE, вероятно, +в ROM чипа или в кремнии. + +## Используемые open-source библиотеки (найдены по строкам) +- **EasyFlash** (armink/EasyFlash) — NV-хранилище во flash. **Здесь лежит калибровка** (VCCN/Mod/PWR, + см. docs/tuning-software-protocol.md). Формат документирован → можно читать/править cal прямо во flash-дампе. +- **WebRTC** (nsx, digital_agc) — аудио. + +## Честная оценка «свой вокодер» +- Заменить AMBE на свой кодек **для эфирного DMR нельзя** — другие рации ждут AMBE (нет совместимости). +- Сам AMBE в этом ARM-образе не лежит (DSP/ROM) → «переписать вокодер» здесь нечего. +- РЕАЛЬНО моддится: параметры WebRTC (агрессивность шумодава/AGC, качество звука), кодек-гейны + (`ATC_MICCodecGainSet`/`ATC_SPKCodecGainSet`), DTMF, логика управления DMR, NV-калибровка (EasyFlash). + +## Реальные следующие шаги (реверс+правка+тест) +1. **API-поверхность:** извлечь полный список ATC_*/SP*-команд (это протокол 0x68 между AT32 и модулем) + — карта того, чем вообще можно управлять. Уже частично в docs/fm100b-commands.md. +2. **WebRTC-параметры:** найти конфиг NSX/AGC (режим/пороги) в коде, изменить, перешить модуль + (режим «Обновление DMR чипа»), проверить звук/приём. Это и есть «поменять и посмотреть заведётся ли». +3. **EasyFlash NV:** распарсить NV-регион (cal) во flash-дампе — прямой доступ к калибровке без live-протокола. diff --git a/tools/tuning_read.py b/tools/tuning_read.py index d48c372..1358707 100644 --- a/tools/tuning_read.py +++ b/tools/tuning_read.py @@ -24,7 +24,10 @@ CMDS = { "CmdEnd": "34 52 05 ee 79", } -def hx(name): return bytes.fromhex(CMDS[name].replace(" ", "")) +def hx(name): + if name.startswith("hex:"): + return bytes.fromhex(name[4:].replace(" ", "")) + return bytes.fromhex(CMDS[name].replace(" ", "")) # powershell: open port, run a sequence of (write, wait) steps, capture each response PS = r''' @@ -47,7 +50,7 @@ foreach ($s in $steps) {{ if ($buf.Count -gt 0 -and ($sw.ElapsedMilliseconds-$last) -gt {idle}) {{ break }} }} $hex = ($buf | ForEach-Object {{ "{{0:x2}}" -f $_ }}) -join "" - Write-Output ("RSP:"+$name+":"+$hex) + Write-Output ("RSP|"+$name+"|"+$hex) }} $p.Close() Write-Output "DONE" @@ -72,8 +75,8 @@ def run(port, seq, dwell=120, tmo=700, idle=200): res=[] for line in out.split("\n"): if line.startswith("ERR:"): sys.exit(line) - if line.startswith("RSP:"): - _,name,hexs=line.split(":",2) + if line.startswith("RSP|"): + _,name,hexs=line.split("|",2) res.append((name, bytes.fromhex(hexs) if hexs else b"")) return res @@ -107,7 +110,7 @@ def main(): print("Нет ответов от рации (powershell не вернул RSP). Порт/режим?") return for name,rx in res: - tag=f" {name:12s} TX={CMDS[name]}" + tag=f" {name:14s} TX={CMDS.get(name, name[4:] if name.startswith('hex:') else name)}" if not rx: print(tag+" RX=(нет ответа)") else: