Skip to main content...

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.

Important
This project only accepts pull requests – issue/bug reports are disabled on the repo itself. That applies to hardware verification too: even a “yes, this works as documented” result with no code change should come in as a small PR updating the relevant “unverified” language below (or in Supported Radios / Tech Specs) to reflect what you confirmed, not as a message asking someone to read it and update the docs for you.

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:

  1. 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.
  2. 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

Prerequisites
  1. Fork the repository and create your branch from master.
  2. Test locally./start.sh and exercise the change in a real browser; the Deployment Guide covers the other setup paths if you need them.
  3. 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.
  4. 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.

See Also

Last Revision: 2026-07-12 -- Document Version: 49b0071
Permanent Link: <https://w0chp.radio/ogd77-webcpsd/contributing/>