ReallyOpenGD77 WebCPSd Contributing Guide
First off, thank you for considering contributing to ReallyOpenGD77 WebCPSd! Most of what’s listed here isn’t code review or style nitpicks – it’s real-hardware verification. This project reverse-engineered the OpenGD77 protocol from firmware source and cross-checked byte offsets against qdmr and decompiled reference sources, but reverse-engineering only gets you so far – it needs owners of specific radios to confirm things actually work as intended.
STM32 Family: Confirm DM-1701 / MD-2017 / MD-9600
MD-UV380/RT-3S read/write is verified against real hardware. DM-1701, MD-2017, and MD-9600 use the same write framing and are assumed to share MD-UV380’s FLASH-based memory map (see applyRadioFamilyMemoryMap() in frontend/js/app.js), but that assumption hasn’t been checked against real hardware. If you have one of these radios, please test read/write and open a PR updating this section with the result – if the memory map turns out to differ, applyRadioFamilyMemoryMap() is where a third radio-family branch would go.
Satellite Support: Verify Against Real Hardware
Satellite orbital-element read/write (see OGD_ADDR.SATCFG and the satellite codec in frontend/js/app.js) targets a separate flash region from the rest of the codeplug – byte offsets are cross-checked against qdmr’s OpenGD77BaseCodeplug::SatelliteElement/AdditionalSettingsElement and round-trip/unit-tested against a real TLE, but nobody has confirmed a write actually lands correctly on a physical radio (or that the firmware’s on-device Doppler correction picks it up) yet. If you have an OpenGD77-family radio, please: write a satellite, confirm the radio’s satellite menu shows it correctly, and open a PR noting the result.
Radio Tools: Verify Against Real Hardware
Boot Text and Band Limits (see OGD_ADDR.BOOT_SETTINGS/UHF_MIN/VHF_MIN in frontend/js/app.js) have byte offsets cross-checked against qdmr’s BootSettingsElement/GeneralSettingsElement, but – like satellites – nobody has confirmed they land correctly on a physical radio yet. Backup/Restore (backupRadio()/restoreRadio()) doesn’t have new offsets to verify – it just reuses the same segments as Read/Write – but a full backup-then-restore round-trip against real hardware, confirming the codeplug comes back byte-identical, hasn’t been tried either. If you have an OpenGD77-family radio, please: set boot text and confirm it shows at power-on, try a backup/restore round-trip, and open a PR noting the result.
DMR ID Database: Verify Caller-ID Display, and MK22 Radios
The DMR ID Database writer/reader (writeDmrIdDatabase()/readDmrIdDatabase() in frontend/js/app.js) targets a flash region entirely separate from the regular codeplug – addresses, the two-region split, the 12-byte header, and the 6-bit record compression were cross-checked against the official CPS’s decompiled source (via an independent decompilation of the same source) and unit-tested for internal consistency. Write and read-back are now confirmed working against a real Retevis RT-3S (STM32/MD-UV380 family). Still needed:
- Confirmation that the firmware actually shows the right name on an incoming call after a write – nobody’s checked that yet, only that the bytes round-trip correctly.
- A test against an MK22-family radio (GD-77/DM-1801/RD-5R), since only STM32 has been tried.
If you can help with either, please open a PR noting which radio/record-length/voice-prompt-memory combination you used.
Nice to Have
- More repeater data sources (DMR-MARC, TGIF, etc.)
- Localization / translations
- More theme presets
Submitting Changes
- Fork the repository and create your branch from
master. - Test locally –
./start.shand exercise the change in a real browser; the Deployment Guide covers the other setup paths if you need them. - Real hardware, where relevant – for anything touching radio read/write, a verification result against actual hardware (even without a code change) is a genuinely valuable contribution on its own; see the sections above. Submit it as a PR, same as code – there’s no issue tracker to post it to instead.
- Submit a Pull Request against the repository with a clear description of your changes.
Project Structure
ReallyOpenGD77-WebCPSd/
├── backend/
│ ├── main.py ← FastAPI application + all API routes
│ └── requirements.txt ← Python dependencies
├── frontend/
│ ├── index.html ← HTML shell
│ ├── css/
│ │ └── styles.css ← Stylesheet
│ ├── js/
│ │ └── app.js ← Codec, WebUSB protocol, and UI logic
│ └── fonts/
│ └── *.woff2 ← Self-hosted fonts (no Google Fonts CDN call)
├── tools/
│ └── setup-linux-webusb.sh ← One-time Linux WebUSB permissions setup
├── deployments/
│ ├── setup-caddy-https.sh ← HTTPS reverse proxy setup (Let's Encrypt or self-signed)
│ ├── docker/
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ └── Caddyfile ← Optional HTTPS reverse proxy config (Docker)
│ └── proxmox/
│ └── deploy-lxc.sh ← Proxmox VE LXC deployer (install/update)
├── start.sh ← Quick start script
├── LICENSE.md ← MIT license, full text
└── README.md
The architecture is intentionally simple – one Python file for the backend, a handful of static files for the frontend. Easy to read, easy to modify, easy to deploy.