Kenwood TM-D710: Phase 1 capture and the memory line (#113) - #114
Open
ww8l wants to merge 2 commits into
Open
Conversation
The TM-D710 is a **live-mode** radio and the fourth programming modality in this app: no clone image and no card file, one ASCII command per memory, `\r` terminated, the radio answering in kind. Every other driver here clones an image, writes binary records at flash addresses, or patches a file the radio wrote to a microSD card. Measured on Tim's radio, not inherited: ID -> ID TM-D710 non-G, K-type baud -> 57600 CHIRP's driver assumes 9600; this is silent there ME 999 -> N an empty slot, 962 of 1000 round trip-> 17 ms all 1000 slots in 17.2 s MU -> 42 fields order anchored at p1 and p26 by single-change diffs Two published claims died on contact with the radio, both from the table this was about to generate code from: - LA3QMA documents menu brightness as `0=off, 1=max`. It is Menu 501, `OFF/LEVEL 1~LEVEL 8`. Measured by changing it to LEVEL 3 and watching p26 go 8 -> 3 alone. Generating from that enum would have shipped an 8-level control as a checkbox. - `MU` is **not exhaustive**: p28 is Menu 503 and p29 is Menu 507, so 504 CONTRAST, 505 DISPLAY REVERSE and 506 have no parameter at all. Also measured: the first command after opening the port can answer `?` because a previous write left the parser mid-line, so one `?` is not a refusal. `ask_settling` retries once; a second `?` is real. `memory.rs` models the `ME` line field by field and its gate is the live-mode form of the byte-identical re-encode every card radio here is held to: **all 38 captured slots re-emit character-identically.** Fields whose meaning is not established are carried through as the radio's own text, so a slot can be read and written back long before every field is understood. Shift is decoded against Tim's actual repeaters (447.275 and 145.310 minus, 147.360 plus) rather than against the documentation. The driver identifies and nothing else — the scaffolding stance the FT5D was registered under. It could claim `SettingsReader` today on the strength of `MU`, and deliberately does not: the same capability flag would put a settings write in front of an operator, and **nothing has ever been written to this radio**. A D710G is named and refused; its menu set is a different radio's and has not been measured. `kenwood_tmd710_probe.rs` is the measuring instrument that produced all of the above, `#[ignore]`d like the FT5D's `hw_probe` so `cargo test` stays hardware-free. npm run ci green: 446 tests, clippy clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013cZgbB4FwqZ3Z4cdHt6vMH
Tim's counter-proposal, and a better shape than mine: build the write path first, then changes are made from here and verified on the radio's screen — instead of him setting things by hand for me to read back. Every write proves itself by reading the slot back and comparing the whole line. That is not belt-and-braces, it is the only evidence available: this radio has no checksum and no commit step, so a malformed line draws `?` but a well-formed line the radio interprets differently draws nothing at all. The D890UV had a settings field the firmware owned that silently reverted after a write; read-back is what makes that visible rather than a lie in the report. - `write_memory` / `write_name` — write, re-read, refuse to claim success unless the line comes back identical - `write_menu` — `MU` sets all 42 parameters at once, so it returns which ones did NOT take rather than throwing. A field the firmware owns is a finding, not an exception - `Menu` — the 42-field line, kept as text. `with_field` pads to the width the radio used, because `8` where the radio said `08` shifts every field after it: one badly formatted field is not one wrong setting, it is potentially forty-two. A too-wide value is refused - `d710_restore` — puts the radio back from the captured transcript. This is what makes writing to a real radio a reasonable thing to do at all HARDWARE, on Tim's radio: Ladder step 1 (identity write) PASSED. Slot 000 read, written back identically, re-read: byte-for-byte the same line. Ladder step 2 in flight: slots 500-503 were empty (`N`) and now hold known tone and DCS values for Tim to read off the radio's own screen. Nothing populated was touched. Also measured, free of radio time: joining the radio's 38 memories to Tim's channel library on frequency AND callsign, field 9 (Tone) predicts the library's TX tone 33 right / 0 wrong under "standard 42-tone CTCSS list, 0-indexed", while field 10 (CTCSS) gets 27/6 — so field 9 is the transmit tone and field 10 is a separate squelch tone. The ENDS of the table are what slots 500/501 are for; DCS is unmeasured entirely, which is what 502/503 are for. npm run ci green: 453 tests, clippy clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013cZgbB4FwqZ3Z4cdHt6vMH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First half of #113, following the
new-radioskill. Phase 1 is complete and measured on the radio; Phase 2 is partial. The radio does not appear in the app yet — that is Phase 3.The TM-D710 is a fourth programming modality
No clone image and no card file. One ASCII command per memory,
\rterminated, the radio answering in kind:Every existing driver clones an image (UV-5R, TD-H3), writes binary records at flash addresses (AnyTone), or patches a file the radio wrote to a microSD card (FT5D, ID-52, TH-D75). Consequences: a write here is not atomic — a failure halfway leaves the radio half-programmed, which nothing else in this repo can do — and a "backup" is a transcript rather than a blob.
Measured, not inherited
ID TM-D710, K-type — non-Gkenwood_live.pyassumes 9600; this radio is silent thereN— 962 of 1000, zero unexpected repliesMUreads with nothing changedTwo published claims died on contact with the radio, both from the table this was about to generate code from:
0=off, 1=max. It is Menu 501,OFF/LEVEL 1~LEVEL 8. Proven by setting LEVEL 3 and watchingp26go8 -> 3alone. Generating a schema from that enum would have shipped an 8-level control as a checkbox. It was ambiguous on value alone — Menu 501 BRIGHTNESS and Menu 504 CONTRAST both default to 8, and the radio read 8 — so it was settled by changing it, not by position.MUis not exhaustive.p28is Menu 503 andp29is Menu 507, so 504 CONTRAST, 505 DISPLAY REVERSE and 506 have noMUparameter at all. Settings the operator can see are not all reachable this way.Also measured: the first command after opening the port can answer
?because a previous write left the parser mid-line. One?is not a refusal —ask_settlingretries once, a second?is real. That is measured behaviour, not defensive coding.The Phase 2 gate
memory.rsmodels theMEline field by field, and its gate is the live-mode form of the byte-identical re-encode every card radio here is held to: all 38 captured slots re-emit character-identically. Field widths are preserved rather than normalised —0and000are the same number and not the same line.Fields whose meaning is not established are carried through as the radio's own text, so a slot round-trips long before every field is understood. Shift is decoded against Tim's actual repeaters (447.275 and 145.310 minus, 147.360 plus), not against the documentation that describes them.
The full capture lives in gitignored
scratchpad/, so four real lines are embedded in the tests and a second test checks the whole corpus when it is present on this machine.Capabilities: none, deliberately
The driver identifies and nothing else — the scaffolding stance the FT5D was registered under. It could claim
SettingsReadertoday on the strength ofMUand does not: the same capability flag puts a settings write in front of an operator, and nothing has ever been written to this radio. The registry guard carries that reasoning. A D710G is named and refused — different menu set, not measured.Not done, and what it is waiting on
MEline from an app channel — blocked on the tone and DCS index tables. The captured indices (12,08,18) have no established meaning here, and a published table would be a guess about what a number means. One measurement pass settles it.tx_bands, andrx_bandsmodelling the receiver's real gaps. Until then no model names thisdriver_key, so nothing is reachable from the UI.Verification
npm run cigreen: 446 tests, clippy-D warningsclean. Nothing to verify in the dev app — with no seed entry, this changes nothing an operator can see, by design.🤖 Generated with Claude Code
https://claude.ai/code/session_013cZgbB4FwqZ3Z4cdHt6vMH