The parser
utility reads a binary codeplug (.rdt
) for the Hytera S1 mini U1 (HYTS10) radio and, if an output file is provided, writes a set of LPD channels (the first 16) into it, correctly forming the internal links between channel records.
This project is useful for quick audits of settings and for automatically generating standard configurations based on firmware templates.
- Parsing .rdt:
- Read the channel list (RX/TX frequencies, CTCSS/CDCSS, service fields).
- Read selected settings (buttons, flags/modes, currently selected channel, etc.—as reverse-engineering progresses).
- Writing:
- If
out_path
is provided, the tool builds a new channel block and overwrites it in the output.rdt
, adding the first 16 LPD channels.
- If
Important: when programming the radio, the RCDB version in the codeplug must match the RCDB version on the radio. In practice, the vendor software will likely refuse to write an incompatible codeplug anyway.
- Python 3.8+
- construct
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
# or simply:
pip install construct
.
├── parser.py
├── *.py # other project files
├── templates/ # reference codeplugs for different firmware versions
│ ├── HYTS10_V1.1.20.009.rdt
│ ├── HYTS10_V1.1.20.010.rdt
│ └── ...
└── README.md
templates/
contains “clean”/minimal codeplugs for different HYTS10 firmware versions. Use a template that matches your radio’s version to minimize field-level incompatibilities.
python3 parser.py path/to/input.rdt
Example output:
== parsed codeplug:
Container:
magic = b'HYTX' (total 4)
version_1_firmware = u'V1.1.20.009' (total 11)
version_2_rcdb_data = u'V1.1.20.009' (total 11)
model = u'HYT-S10' (total 7)
model_number = u'S101-00S00001-Uc-1-D' (total 20)
freq_range = Container:
from = 43000000
to = 47000000
serial = /* skipped */
device_alias = /* skipped */
buttons = Container:
pwr_short = 133
unknown1 = 28
unknown2 = 66
volume_up_long = 135
unknown3 = 66
volume_down_long = 255
settings_13d = Container:
KEYPRESS_SOUND = True
POWER_ONOFF_SOUND = True
SERVICE_SOUND = True
b4 = True
b5 = True
b6 = True
b7 = True
vox_level = 247
vox_sensitivity = {'raw': 247, 'value': <VOXSensitivity.MID: 1>}
squelch_settings = Container:
SQUELCH_ENABLED = True
SQUELCH_NORMAL = True
call_battery_save_time = 3
settings_1f7 = Container:
b1 = True
CALL_CTCSS_TAIL_REVERSE_180 = True
b5 = True
settings_1f8 = Container:
BACKLIGHT_LED = True
VOICE_NOTIFICATIONS = True
CALL_CTCSS_TAIL_REVERT = True
b5 = True
b6 = True
b7 = True
backlight_duration = 3
settings_1fb = Container:
TXPOW_HIGH_1 = True
TXPOW_HIGH_2 = True
CALL_PRE_EMPHASIS = True
settings_1fc = Container:
b2 = True
b3 = True
b4 = True
b5 = True
settings_1fd = Container:
b2 = True
ALLOW_CHANNEL_COPY = True
settings_1fe = Container:
b0 = True
b1 = True
b3 = True
selected_channel = 1
mic_gain = 32
settings_220 = 241
tx_allow_mode = {'raw': 241, 'value': <TXAllowMode.AllowIfChannelFree: 1>}
call_timeout = 12
settings_22d = Container:
b1 = True
ALLOW_AIRCLONE = True
b4 = True
b5 = True
b6 = True
b7 = True
write_password = None
read_password = None
<...>
== parsed channels:
H-01
RX: 433.07500 MHz squelch: CTCSS, tone: 67.0 Hz
TX: 433.07500 MHz squelch: CTCSS, tone: 67.0 Hz
H-02
RX: 433.10000 MHz
TX: 433.10000 MHz squelch: CTCSS, tone: 88.0 Hz
H-03
RX: 433.12500 MHz squelch: DCS, octal code: 023
TX: 433.12500 MHz
H-04
RX: 433.15000 MHz squelch: DCS_INVERT, octal code: 023
TX: 433.15000 MHz
H-05
RX: 433.17500 MHz squelch: DCS, octal code: 666
TX: 433.17500 MHz
python3 parser.py path/to/input.rdt path/to/output.rdt
input.rdt
— the source codeplug (typically fromtemplates/
for your firmware version, or a dump from your radio).output.rdt
— the destination file. A new channel block with LPD 1–16 will be written there.
The list is produced by the create_channels()
function (see the code). By default it defines the first 16 LPD channels (433.0750 … 433.4000) with neutral squelch tones (or as configured in the project). You can change frequencies/sub-tones by editing that function.
RCDB/firmware: use a template from templates/
that matches your radio’s version. A mismatch may lead to a failed upload or incorrect behavior.
- Determine your radio’s firmware version.
- Pick a matching template from templates/ (or dump a codeplug from the radio).
- Verify the file parses:
python3 parser.py templates/HYTS10_V1.1.20.009.rdt
- Generate an output with LPD 1–16:
python3 parser.py templates/HYTS10_V1.1.20.009.rdt LPD16.rdt
- Upload
LPD16.rdt
into the radio using the standard procedure.
If you want a different set, edit create_channels()
:
def create_channels():
return [
dict(
rx_freq=433.07500, tx_freq=433.07500,
rx_tone_squelch={"kind": "none"}, tx_tone_squelch={"kind": "none"},
padding=0xFFFFFFFF
),
# add your channels here…
]
The parser will correctly set prev/next, pack frequencies, and write the block back into the target file.
- Writing currently supports regenerating the entire channel block “from scratch.” In-place edits are possible but not yet exposed via CLI.
- Several service fields (flags/buttons/masks) are still being researched; they’ll be added to the construct schema as they’re understood.
- For firmware versions other than
V1.1.20.009
, offsets/biases may differ.
All files and scripts are provided “as is.” Use at your own risk. Always make a backup of the original codeplug before writing. Compatibility with a specific firmware version must be validated on your side.