Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: partial reorganization of the CPS format. #32

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

silseva
Copy link
Contributor

@silseva silseva commented Oct 7, 2023

Header:

  • changed the magic field from 64 to 32 bit
  • extented the version number from 16 to 32 bit

Channel:

See also #31

Header:
- changed the magic field from 64 to 32 bit
- extented the version number from 16 to 32 bit

Channel:
- changed transmit power from dBm to mW, extending the field from 8 to 32 bit
- dropped the scan list index field
- dropped the group list index field
- changed the longitude field from 8 to 16 bit to fix #30
Contacts:
- extended info field to 15 bytes, padding the structure to 48 byte and to leave space for future extensions

Channels:
- extended info to 9 bytes, padding the structure to 96 byte and to leave space for future extensions
- dropped the 20kHz channel bandwidth

M17 channel info block:
- dropped split TX and RX CAN and moved to a unique CAN field
- merged CAN and channel mode into the same 8 bit field
- merged encryption type and GNSS transmission in the same 8 bit field
@n1zzo n1zzo force-pushed the master branch 3 times, most recently from 53eaf93 to 2b94b43 Compare October 8, 2023 21:10
@silseva
Copy link
Contributor Author

silseva commented Oct 9, 2023

@tarxvftech
Copy link

LGTM!

Power: Max storable value is a bit less than 4.3MW.

Are we sure that's going to be enough? 😆

@silseva
Copy link
Contributor Author

silseva commented Dec 1, 2023

To do:

  • change encryption type for M17 to support AES-128, 192 and 256;
  • store the M17 AES encryption key, when present. Add a separate block for the keys, with tuples of (key lenght, key) and reference each key by its position in the block as if it was an array;
  • store M17 scrambler key, when present. Scrambler uses 8, 16, and 24 bit sized keys;
  • specify that channel name and description can be up to 32 characters, in which case the NULL terminator is omitted;
  • specify an encoding for channel name and description strings. Plain ASCII?

silseva added 4 commits March 2, 2024 10:43
Updated the format used to store transmitter location:
- removed the +500m offset for altitude, converted the value to a signed type
- fix an error with negative coordinates close to zero, see #40.
@mdiepart
Copy link

Hi!
I wanted to write down a brief summary of what we discussed in Brussels with @silseva.
So I suggested that this code plug system, if we want to see a wide adoption, must include a minimum amount of flexibility.
By flexibility, I mean that each protocol should be able to be fully customized to the specific mode regarding the length of its entries.
Linked to that was the suggestion that the cps should include a way to include your own protocol with a customized length without 1) having to bring this protocol upstream into the specifications and 2) without making the codeplug incompatible with other radios (which could be the same radio with a different firmware). This would allow ham operators to tinker with it more easily, but would also allow industrial manufacturers to use the same CPS system without having to share their protocols with you.

The solution I first suggested was to include a short table at the beginning of the codeplug that would look like this (numbers are just random numbers and this example only considers contacts).

ID (1 byte) Name (null-terminated string) Length (1 byte)
0 Analog 8
1 DMR 32
2 M17 28
3 MyCustomMode 64

And then, for each entry you would have something looking like this (for one analog contact and 2 custom contact)

Mode ID (1 byte) Common data (x bytes) Mode-specific data (see lengths above)
0 aabbcc xxyyzz
3 ddeeff rrssttuuvvww
3 gghhii llmmnnooppqq

The advantage of this is that you do not really need to update the standard at all to include new modes, but you have to define each mode you want to support, including very ubiquitous ones like analog. Also, you raised the concern that managing strings can be quite computationally heavy for the smallest processors.

The final solution we came up with was to reserve the 128 first IDs for pre-defined modes. Which would mean that in all CPS using this standard, ID 0 would always be analog, 1 would always be DMR, 2 would always be M17, and so on...
All IDs from 127 and upward would need to be defined at the beginning of the codeplug.
We spare many bytes because we do not need to re-define ubiquitous modes and we still have the possibility to tinker and add custom modes.

The IDs 127 and up could be defined as explained above, but we could also not put the name of the mode, assuming that if the radio does not know about it, it does not need to know the name of the mode. The risk could be that you may encounter a mode with a "known ID" but whose definition is not what you expect (i.e. if I use id 218 for MyCustomMode and you use id 218 for YourCustomMode). However, it would be required for the table to contain the length of the various entries (contacts, channels, ...). That way you can at least check that the IDs above 127 have the expected length. You also know what is the length of the entries if you do not know about the mode.

This solution also means that the radios are expected to keep a table of the lengths of all the modes included in the standard because it might encounter any of those including some it does not support.

Not having a table for the IDs below 127 also means that any update to the standard including a new protocol would not be backward compatible, because if I don't know about ID 17 yet, then I have no idea what is the length of the entry and so the rest of the codeplug becomes unreadable.

Anyway, that is what I remember about what we talked about more than a month ago.

Any remarks are welcome from anybody that might read this comment 😄

@jancona
Copy link
Contributor

jancona commented Nov 19, 2024

Would it make sense for contacts to be sharable between modes? A contact could look like:

{
  "callsign": "N1ADJ",
  "type": ["DMR","M17"],
  "name": "Jim Ancona",
  "dmr_id": 3127978,
  "location": "Brunswick, ME, US"
}

Then the radio could leverage DMR contacts for M17.

@jancona
Copy link
Contributor

jancona commented Nov 21, 2024

I'm doing a comparison between the OpenRTX Binary Codeplug Format (OBCF) defined in this document and the QDMR Extensible Codeplug File Format. QDMR is trying to support all the functionality in multiple supported radios, while OBCF only need to support OpenRTX's functionality, so OBCF doesn't need to support everything QDMR does. Also OBCF is a binary format while QDMR is YAML which will be translated to radio binary formats.

I started with contacts. I'll do channels in a follow-up.

Contacts

OBCF specifies two contact types, DMR and M17. QDMR has also has two types DMR and Analog DTMF. Presmably it would add an M17 type to support OpenRTX. DTMF contacts "can be used to store commonly used DTMF sequences. For example, it may be used to control the EchoLink feature of a repeater."

DMR Contacts

OBCF Field OBCF Type OBCF Description QDMR Field QDMR Type QDMR Description
name char[32] Display name for the contact name string Specifies the name of the contact. Any string is valid.
contact_settings uint8_t one of 0b00 (Group contact), 0b01 (Private contact) or 0b10 (Broadcast call) type one of PrivateCall, GroupCall or AllCall Specifies the type of the contact.
ID uint32_t DMR ID number integer Specifies the DMR ID for this contact. That is, any integer between 0 and 16777215. This element is mandatory for all types except for the all-call. For the all-call, the default number 16777215 will be used.
N/A ring boolean If true, the radio will ring whenever a call from this contact is received (if supported by the radio). Optional, if omitted set to false.

Differences

  • OBCF supports M17 contacts.
  • QDMR supports DTMF contacts.
  • QDMR supports a ring attribute on DMR contacts.
  • QDMR specifies an ID field that is used for making references within the codeplug, e.g. to add a contact to a list.

All the fields in common are compatible.

@jancona
Copy link
Contributor

jancona commented Nov 21, 2024

More comparison of this document and the QDMR specification, this time for channels.

Channels

Common Attributes

OBCF Field OBCF Type OBCF Description QDMR Field QDMR Type QDMR Description
mode uint8_t Operating mode. One of None, FM, DMR,M17 type digital or analog
name char[32] display name for channel name string Specifies the name of the channel. Any string is valid here.
descr char[32] Description of the channel N/A
traits uint8_t Bit 0:1 channel bandwidth (refer to Bandwidth lookup table). Bit 2 RX only flag. Bit 3:7 reserved rxOnly bool bandwidth is an analog-only attribute
power uint32_t transmit power, in mW power Specifies the transmit power of the channel. Must be one of Min, Low, Mid, High or Max.
rx_frequency uint32_t RX frequency, in Hz rxFrequency float RX frequency in MHz.
tx_frequency uint32_t TX frequency, in Hz txFrequency float TX frequency in MHz.
ch_location geo_t transmitter location N/A
N/A timeout integer Specifies the transmit timeout in seconds. Any integer is valid here. Omitting this field or setting it to 0 will disable the timeout Setting it to !default, the global transmit timeout will be used.
N/A vox integer Specifies the VOX sensitivity for this channel. If set to !default, the global VOX sensitivity will be used. See the section called “Radio settings”. Omitting this value or setting it to 0, will disable the VOX for this channel.
N/A scanList Specifies the optional scan list for this channel.

Differences

  • QDMR doesn't have a channel description, only a name.
  • bandwidth only applies to FM channels in QDMR.
  • power is a scalar value in OBCF. QDMR has it as an enum. Radios I've seen have between two and four choices, e.g. Low, Medium, High and Turbo.
  • QDMR doesn't have a channel location.
  • OBCF doesn't have timeout, vox or scanList attributes.

FM channel attributes

OBCF Field OBCF Type OBCF Description QDMR Field QDMR Type QDMR Description
rxTone uint8_t Bit 0:6 index of the CTCSS tone for receive squelch as in the CTCSS Code Frequencies Lookup Table. Bit 7 enable/disable receive tone squelch. (e.g. disabled 173.8 Hz tone value is 0b00011111) rxTone Specifies the receive sub-tone setting for this channel. That is, the squelch will only open when a certain subtone is received along with the signal. As there are two common subtone standards, this attribute is a map with a single entry. The key specifies the type (either ctcss or dcs) while the value specifies the actual subtone. For CTCSS tones, the value is the subtone frequency in Hz. For DCS it is the code number as an integer. For inverted DCS codes, use negative numbers.
txTone uint8_t Bit 0:6 index of the CTCSS tone to be transmitted as in the CTCSS Code Frequencies Lookup Table. Bit 7 enable/disable tone transmission.(e.g. enabled 107.2 tone value is 0b10001110) txTone Specifies the transmit sub-tone setting for this channel. For details see rxTone above.
admit Specifies the admit criterion for the channel. Must be one of Always, Free or Tone.
N/A squelch integer Specifies the squelch level for the channel. Must be an integer in the range of [1-10]. If set to 0, the squelch gets disabled. If set to !default, the global squelch setting is used.
bandwidth Specifies the bandwidth of the channel. Must either be Narrow or Wide for 12.5kHz or 25kHz respectively.
aprs Specifies the APRS positioning system of this channel. If set, it must be a reference to an analog APRS system.

Differences

  • QDMR supports DCS in addition to CTCSS.
  • OBCF encodes whether receive tone squelch is active in the rxTone value. QDMR has a separate admit fields that includes busy channel lockout. |
  • QDMR supports per-channel analog squelch levels.
  • QDMR has bandwidth as an FM-specific attribute, while OBCF has it global.
  • QDMR has an aprs attribute, which ties the channel to analog APRS.

DMR channel attributes

OBCF Field OBCF Type OBCF Description QDMR Field QDMR Type QDMR Description
colorCode uint8_t RX and TX colour codes used, as defined by ETSI TS 102 361-1 Table 9.18.
Bit 0:3 RX colour code. Bit 4:7 TX colour code. (e.g. RX colour code 0 and TX colour code 15 would be represented as 0b00001111) colorCode integer Specifies the color code of the channel, any number between 0-16 is valid here.
dmr_timeslot uint8_t Timeslot being used, represented in integer form (e.g. timeslot 2 is 0b00000010) timeSlot Specifies the time slot of the channel. Must be one of TS1 or TS2.
contact_index uint16_t Index to retrieve contact info contact Specifies the default transmit contact. This must be a reference to a digital contact. If omitted, no default transmit contact is associated with the channel. Some radios require all channels to have a transmit contact.
admit Specifies the admit criterion for the channel. This must be one of Always, Free or ColorCode.
groupList Specifies the RX group list for this channel.
aprs Specifies the positioning system for this channel.
roaming Specifies the roaming zone for this channel.
radioID Specifies the radio ID for this channel.

Differences

  • OBCF supports separate RX and TX color codes. QDMR only has a single color code.
  • QDMR supports admit criteria of Always, Free or ColorCode.
  • QDMR supports specifying a group list for a channel.
  • QDMR has an aprs attribute, which ties the channel to digital APRS.
  • QDMR supports specifying a roaming zone for a channel.
  • QDMR supports multiple radio ID per radio, and channel-specific radio IDs.

@jancona
Copy link
Contributor

jancona commented Nov 22, 2024

Here I take the lists of differences I compiled above and talk about what, if anything, should change in OpenRTX. I'm a big believer in YAGNI, so I'm in favor of deferring differences related to functionality OpenRTX doesn't have and is unlikely to implement soon until we need them. I included an "OpenRTX Feature?" column in the tables but it's mostly blank. I don't know what contact and channel features OpenRTX has because my CS7000 doesn't have any yet. ;-) Feel free to comment and I'll update the table.

Also, if we are adding features to the codeplug format over time, it needs to be extensible.

Contacts

Section Difference Comment OpenRTX Feature? Action?
Contacts DTMF contacts not now
ring attribute on DMR contacts not now
Channels - Common description attribute see below - 1
power attribute: scalar or enum see below - 2
location attribute see below - 3
timeout, vox, scanList attributes not now
Channels - FM DCS support see below - 4 add
admit attribute see below - 5
per-channel analog squelch levels not now
bandwidth: FM-specific or common see below - 6
aprs attribute not now
Channels - DMR Separate RX and TX color codes see below - 7
admit attribute see below - 5
Channel group list not now
aprs attribute not now
roaming zone not now
channel-specific radio IDs not now

Comments

  1. What is the rationale for the channel description? Neither QDMR or the proprietary CPS for my DMR-6X2 supports it. Unless we have one, perhaps we should remove it.
  2. The radio and CPS interfaces I've seen ask users to choose one of several power levels. So QDMR's approach makes sense to me. What's the argument for having a numeric power level?
  3. Channel location sounds potentially useful. Are there plans to implement any functionality that needs it?
  4. Some people indicated in Discord that they have DCS repeaters in their area.
  5. Encoding tone enable/disable in the high bit of the byte specifying the tone means that on or off are the only options. QDMR has three options and my DMR-6X2 has five. Is it likely that we'll ever need more than on/off?
  6. Is there any reason bandwidth shouldn't be FM-only? Is there a use for it for DMR, M17 or other modes?
  7. Are we likely to implement separate color codes for TX and RX?

@ec1oud
Copy link

ec1oud commented Feb 6, 2025

I think such a format should either be self-describing or using a versioned formal schema, and also directly in the form for putting into memory in one block and accessing at runtime (thus, memory-aligned). It's one of my life goals to invent an all-purpose format like that, and use it for as many things as possible. For example I'm thinking of it as a way that a Scheme implementation could have a "sandbox" like Smalltalk implementations usually do. (Such languages of course are often out of scope for uC firmware, but helps to motivate what I want to do.) Also, to be able to write programs in any language that do not do serialization at all, but just mmap files from ssd and expect to keep their data continuously persistent.

So my approach is to try to fit most data types into 64-bit words, because most powerful processors are 64-bit so that's the alignment that is needed. (But here, 32-bit alignment would be good enough, and you probably want support for smaller data types too. But multiple smaller values can be packed into 64 bits.) But strings tend to be variable-length. A long time ago I implemented my own data format, perhaps a bit like MsgPack, which was not concerned with alignment, and there I started with a string table at the beginning of the file, at first just to have reusable string "tags" for data. So when converting from xml for example, each tag name gets replaced with an 8-bit value and the string added to the string table. I think that's an idea that I will need to reuse in the memory-aligned format. Another thing I've tried is to borrow something like Baudot encoding: if each character is only 5 bits, you can pack a good number of characters into 64-bits. (Borrowing from FT8 encoding could also be interesting.) And from Scheme implementations I borrow the idea of data type tagging / NaN boxing: 3 or so bits in each word tell you its type (and so the numeric range of 64-bit ints is reduced slightly, but it doesn't have much impact on other data types). I've got a prototype over here: https://git.sr.ht/~ecloud/dscm/tree/main/item/expt/vector-scheme It uses a memory pool borrowed from Plan 9 (the one behind malloc) whereas that would be fixed-size on a microcontroller. And there's a string pool and a multi-type vector implementation. To create a dictionary, a vector of keys has an aux "pointer" to a vector of values. But "pointers" are really just small offsets within the same memory block.

I think a codeplug format should be based on some sort of multi-purpose standard; and I would be eager to hear if another such format already exists, so that I don't reinvent the wheel. (I see that too many wheels have been reinvented in the world of ham radio already.) But so far I've checked into most of the formats listed in https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats and have not been satisfied that any of them do quite what I want. I would suggest that you at least do the same: think about whether protobuf or capnproto or asn.1 or flatbuffers or some such might be suitable for this purpose. For most of those, you just have to write a schema, and there is a code generator that writes your s11n code. The trouble as I see it is that I don't want to do s11n at all, and I expect to be able to write back to the data structure in-situ. (That is of course quite hard with variable-length strings, but I suppose that's why SQL databases tend to have both char and varchar data types. If you can fix the length, everything gets easier. But you probably have fixed-size anyway right?) But I think that some of those formats could allow in-situ modification: it's just a matter of writing code for that.

I never got into Newton development, but I read that it had a data format called a "soup", meant for continuous persistence of user data on battery-backed SRAM. With such a size constraint I imagine it had to be quite efficient, and maybe also suitable for today's microcontroller applications. I just haven't gone down that rabbit hole yet, and wonder if it could be worthwhile still.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Codeplug: longitude field in geo_t structure too small
5 participants