Skip to content

feat: complete OpenGD77 CPS CSV import/export (Contacts, TG Lists, DTMF, APRS + full channel fields) #38

Description

@pskillen

Problem

OpenGD77 is our first-class CPS target, but our OpenGD77 CSV support is only partially implemented. A full OpenGD77 CPS export is a directory of CSV files, and today we only handle two of them, and only a subset of their columns:

File Import status
Channels.csv ⚠️ Partial — imported, but only ~10 of ~28 columns are kept
Zones.csv ✅ Imported (name + members)
Contacts.csv ❌ Not imported (skipped as unrecognised)
TG_Lists.csv ❌ Not imported
DTMF.csv ❌ Not imported
APRS.csv ❌ Not imported

There is also no export back to OpenGD77 CSV yet.

A recent real export to scope against (gitignored sample): /Users/patricks/git_personal/dmr-programming/1701/opengd77-cps-export — 6 files (Channels.csv, Zones.csv, Contacts.csv, TG_Lists.csv, DTMF.csv, APRS.csv).

Intended outcome

Make OpenGD77 CPS CSV a complete, round-trip import/export format: import the full set of files (and the full set of channel columns), and export the internal models back to the same CSV file set such that the OpenGD77 CPS accepts them.

Import — the remaining files

  • Contacts.csv → DMR contacts / talk groups. Columns: Contact Name, ID, ID Type (Group/Private), TS Override. Note the OpenGD77 distinction: a Group call ID is effectively a talk group; a Private call ID is a contact. Map onto Contact / TalkGroup models (currently stubs in src/models/codeplug.ts).
  • TG_Lists.csv → TG lists. Columns: TG List Name, Contact1Contact32 (member contact names, matched by name at the import boundary like zones do today). Map onto TgList (stub).
  • DTMF.csv → DTMF contacts. Columns: Contact Name, Code. Decide whether to model now or capture/round-trip as opaque (the sample is header-only/empty).
  • APRS.csv → APRS config. Many columns (APRS config Name, SSID, Via1/2, Icon, Comment text, Latitude, Longitude, TX Frequency, Baud rate, …). Likely capture/round-trip rather than fully model initially — confirm during design.

Import — full channel fields

Channels.csv currently keeps only Channel Number, Channel Name, Channel Type, Rx/Tx Frequency, Contact, TG List, Latitude, Longitude, Use Location. The export header also includes: Bandwidth (kHz), Colour Code, Timeslot, DMR ID, TS1_TA_Tx, TS2_TA_Tx ID, RX Tone, TX Tone, Squelch, Power, Rx Only, Zone Skip, All Skip, TOT, VOX, No Beep, No Eco, APRS. These are currently dropped, which makes a lossless round-trip impossible. Extend the Channel model (or a vendor-extras side-channel) to carry them.

Export

  • Serialise the internal models back to the OpenGD77 CSV file set (by header name, matching the target CPS version's expected columns — not column index).
  • Round-trip friendly: import a real export, then export, and reproduce an equivalent codeplug (modulo documented lossy fields).
  • Denormalise id-based relationships back to OpenGD77's name-based references (zone members, TG-list members, channel → contact / TG list).

Affected

  • Import dispatch: src/lib/import/index.ts currently hard-codes importAdapters[0] and branches only on channels vs zones; ImportResult (src/lib/import/types.ts) only carries channels? + zones?. Both need extending for contacts / TG lists / DTMF / APRS.
  • Adapter: src/lib/import/opengd77/adapter.ts detectKind (filename + header heuristics) and parse.ts (new parsers; extend parseChannels).
  • Models: src/models/codeplug.ts — flesh out Contact, TalkGroup, TgList stubs; extend Channel for the dropped DMR/FM fields.
  • Export: new OpenGD77 CSV serialiser (see dependencies).
  • Tests: extend src/lib/import/opengd77/parse.test.ts / index.test.ts against the sample file set.

Notes / dependencies

Out of scope

Workflow note (for whoever picks this up)

Likely multi-commit work: branch from origin/main, use atomic conventional commits per logical change (do not batch into one big end-of-plan commit), and open a PR linking Closes #. Pair with the docs/features / progress-tracking skills for the build log.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions