M0LTE.Tait.Codeplug 0.8.0

dotnet add package M0LTE.Tait.Codeplug --version 0.8.0
                    
NuGet\Install-Package M0LTE.Tait.Codeplug -Version 0.8.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="M0LTE.Tait.Codeplug" Version="0.8.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="M0LTE.Tait.Codeplug" Version="0.8.0" />
                    
Directory.Packages.props
<PackageReference Include="M0LTE.Tait.Codeplug" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add M0LTE.Tait.Codeplug --version 0.8.0
                    
#r "nuget: M0LTE.Tait.Codeplug, 0.8.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package M0LTE.Tait.Codeplug@0.8.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=M0LTE.Tait.Codeplug&version=0.8.0
                    
Install as a Cake Addin
#tool nuget:?package=M0LTE.Tait.Codeplug&version=0.8.0
                    
Install as a Cake Tool

M0LTE.Tait.Codeplug

A Tait TM8100/TM8200 codeplug library, reverse-engineered from Free Serial Analyzer captures of the Windows CPS. It reads and writes the codeplug over the serial programming interface without the CPS, exposes a typed, version-pinned field map for the whole CPS Data form and the channel table, and applies the Packet.NET (PDN) upgrade profiles. The read + write path is hardware-validated against a real TM8100.

The CLI front-end that ships from the same repo, tait-codeplug, is a thin layer over this library: see github.com/M0LTE/tait-codeplug for prebuilt binaries.

The protocol, in short

  • ASCII-hex, line-oriented, CR-terminated, strictly lock-step (every command gets one > prompt before the next).
  • Records share the .m8p framing <addr:4hex><len:2hex><data><checksum:2hex>; checksum is the CCDI-family negated sum over the decoded bytes (whole record sums to 0 mod 256). addr is (section << 8) | index.
  • Session: ^ (reset → v), # (enter programming → >), ld{C05}, d00{C01}. Read a section: r<section>. Write: b, i<arg>, a run of w<record>, e. Teardown: ^.
  • Baud opens at 9600, switches to 19200 for the transfer.

The full write-up is in docs/research/tait-codeplug-protocol.md and the programming brief it came from is docs/research/tait-codeplug-programming-brief.md, both in the packet.net repo where this code started life.

What is here

  • CodeplugChecksum / CodeplugRecord / CodeplugImage - the record model, checksum, and .m8p load/save + section map. Fully offline and unit-tested.
  • Fields/ - the typed, version-pinned field map (CodeplugFields, CodeplugEnums, ChannelBits): channels (frequency, bandwidth, power, split-TX, CTCSS/DCS), the whole CPS Data form (its General, Serial Communications, RF Modems, SDM and TOTAL Transparent Mode tabs live in the one data/signalling record; the GPS and Customer Data tabs are separate records; plus the unit data identity), and audio taps. Each field is pinned by a test.
  • FieldConsole - name/value access used by the dump/get/set CLI verbs.
  • CodeplugFields.ApplyPdnBasic() / ApplyPdnExtra() - the two PDN upgrade profiles (see below).
  • ISerialLine / SerialPortLine - the byte seam (mirrors Packet.Radio.Tait.ISerialIo); tests substitute a scripted mock radio.
  • TaitProgrammer - the lock-step transport state machine (connect, interrogate, read, write).

Using it

using M0LTE.Tait.Codeplug;

// Offline: load a CPS .m8p save and read a field.
CodeplugImage image = CodeplugImage.LoadM8p(File.ReadAllText("radio.m8p"));
CodeplugFields fields = CodeplugFields.Open(image);
Console.WriteLine(FieldConsole.Get(fields, "ch0.bandwidth"));

// Live: read the codeplug off a radio latched into programming mode (power-cycle it as you connect).
using var programmer = new TaitProgrammer(new SerialPortLine("/dev/ttyUSB0"));
CodeplugImage live = programmer.ReadImage();

PDN upgrade profiles

Two composable patches that upgrade a radio to the Packet.NET feature set without touching its RF config (channels, frequencies, power), so they layer safely onto a radio already provisioned for its environment. They change only the data record (0x09). For a radio arriving from a foreign application, prefer a clean flash of a full codeplug first, then apply a profile.

  • pdn-basic enables the CCDI command channel that carries Packet.Radio.Tait's telemetry and control: averaged/instantaneous RSSI, forward/reverse power, PA temperature, status/identity, transmitter keying, and the PROGRESS stream for carrier-sense (DCD) and external-PTT edges. It sets CCDI-mode-allowed on, power-up state to Command (so the radio is always CCDI-reachable), progress messages on, and the command baud to 28800.
  • pdn-extra includes pdn-basic and adds the TNC-less internal FFSK packet modem plus the SDM side channel used for mode signalling: transparent mode on, ignore-escape-sequence off (so the transport can escape back to command mode - without this the radio wedges), ignore-subaudible on the data path, the transparent terminal baud (28800) and over-air FFSK baud (2400), and SDM + CCDI SDM output. The over-air baud must match at both ends; adjust the bauds and the data port for your setup.

Status and safety

The read + write path is hardware-validated against a real TM8100 (a same-image write round-tripped every writable record byte-identical), and the field map is validated field-by-field against real-radio CPS saves. Field writes are byte-identical to the CPS's own saves. Safety rails:

  1. Snapshot the current codeplug before writing (the CLI's patch verb does this automatically).
  2. Codeplug region only - this never writes firmware.
  3. Version-pin: the write path refuses a radio whose database version is not in its validated set (currently 0094 / 0095); the field offsets are version-specific.
  4. The field map enforces the CPS's own input rules (value ranges, character sets, and the "only available if ..." availability dependencies), so the library will not write a state the CPS rejects.
  5. Bench on a sacrificial radio first, and re-read (after a power-cycle) to verify a write.

A single-record write is acked but not committed by the radio, so a live field change writes the whole codeplug; that is the validated write path.

Licence

AGPL-3.0-or-later. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0 55 8/21/2026
0.7.0 53 8/21/2026
0.6.1 56 8/21/2026
0.6.0 49 8/21/2026
0.5.0 48 8/21/2026
0.4.1 54 8/20/2026
0.4.0 47 8/20/2026
0.3.0 61 8/20/2026