GobySoft / dccl

Dynamic Compact Control Language
Other
17 stars 13 forks source link

Feature: Option to compute and verify checksums (CRC) #111

Open miquelmassot opened 1 year ago

miquelmassot commented 1 year ago

Hi!

I am in the process of defining a set of messages and I'd like to add a simple checksum at the "end" of the messages (similar to NMEA0183). Before using DCCL I was using this simple two-byte checksum below:

def rs232_checksum(the_bytes):
    """Compute the checksum of a byte array."""
    return b"%02X" % (sum(the_bytes) & 0xFF)

I am thinking about :package: :arrow_right: :package: wrapping my message (without checksum) into another with checksum so that I can pack it - compute its checksum - and write it. I thought this is quite convolved and maybe there is a better solution. Any suggestions?

The icing on the :cake: would be to be able to verify (and trust!) the messages received over dodgy :ocean: comms channels.

tsaubergine commented 1 year ago

Hi -

Thanks for the suggestion. Usually when I need a checksum I build it into the driver layer (e.g. Goby ModemDriver) e.g.,

[DCCL message (encoded)][CRC16 or CRC32]

and verify the checksum upon receipt before calling dccl::Codec::decode.

That said I can see some utility with adding a checksum as a custom codec in dccl, e.g.

syntax="proto2";
import "dccl/option_extensions.proto";

message NavigationReport {
  option ([dccl](https://libdccl.org/4.0/namespacedccl.html).msg) = { codec_version: 4
                        id: 124
                        max_bytes: 32 };
  required double x = 1 [(dccl.field) = { min: -10000 max: 10000 precision: 1 }];
 //.... other fields

  required uint32 crc = 100 [(dccl.field) = { codec: "dccl.crc16" } ];
// or   required uint32 crc = 100 [(dccl.field) = { codec: "dccl.crc32" } ];
}

An 8 bit checksum (such as the one you suggested) is pretty prone to missed detections with multiple bit errors (since you have a 1/256 chance to have the same checksum), but that would be an option too (also CRC algorithms tend to be more robust than the NMEA-0183 XOR checksum).

To implement the above custom codec ("dccl.crc16"), I'd need to add something to allow the DCCL FieldCodecs to access the full Bitset for encoding and decoding, probably a function like FieldCodecBase::const Bitset& root_bitset(); which returns the top level Bitset for encode and decode operations.

Then the codec itself can throw an exception if the checksum doesn't match upon receipt. It would have to be defined as the last field in the message to be effective.

I'll take a look at this if I get bored, though it's not a top priority for me. I'd likely merge this effort with two other somewhat related tasks: 1) implementing #92 and 2) adding a custom codec (e.g. "dccl.descriptor_hash") that does a hash of the current google::protobuf::Descriptor to catch mismatches with the sender and receiver .proto file definitions.