Quick Start#

ltcodecs encodes structured Python dictionaries and ROS messages into compact bit streams. A codec is configured with a field dictionary: each field name maps to a codec alias and the parameters for that field codec.

Install#

Install the package from PyPI:

pip install ltcodecs

For local development, install the checkout in editable mode:

pip install -e .

Encode a Dictionary#

The quickest way to use ltcodecs is with ltcodecs.dict_codec.DictCodec. This path does not require ROS.

from bitstring import ConstBitStream
import ltcodecs

fields = {
    "name": {
        "codec": "string",
        "max_length": 10,
    },
    "count": {
        "codec": "integer",
        "min_value": 0,
        "max_value": 100,
    },
    "enabled": {
        "codec": "bool",
    },
}

message = {
    "name": "test",
    "count": 21,
    "enabled": True,
}

codec = ltcodecs.DictCodec(fields)
bits, metadata = codec.encode(message)

decoded = codec.decode(ConstBitStream(bits))
assert decoded == message

The bits value is a bitstring.Bits object. The metadata return value is usually None unless the field configuration includes one of the metadata encoder aliases.

Encode Metadata#

encode() for the Message/Dictionary codecs returns a tuple of (bits, metadata). The first value is the encoded bit stream. The second value is a small metadata dictionary for values that should travel outside the compressed payload, such as packet routing fields. Most codecs return None for metadata, and you can ignore it. It is possible to define fields that use metadata encoders rather than (or in addition to) adding bits to the output stream.

Use YAML Configuration#

Codec configurations are commonly stored as YAML. The same dictionary codec above can be written as:

name:
  codec: string
  max_length: 10
count:
  codec: integer
  min_value: 0
  max_value: 100
enabled:
  codec: bool

Load the YAML file with ltcodecs.dict_codec.DictCodec.from_codec_file():

from bitstring import ConstBitStream
import ltcodecs

codec = ltcodecs.DictCodec.from_codec_file("message_codec.yaml")

bits, _metadata = codec.encode({
    "name": "test",
    "count": 21,
    "enabled": True,
})

decoded = codec.decode(ConstBitStream(bits))

Nested Dictionaries and Optional Fields#

A field can itself be a dictionary. Use the dict codec and provide a nested fields mapping:

nested:
  codec: dict
  fields:
    value:
      codec: uint8

Optional fields are controlled by a boolean field. If the controlling value is true, the target fields are encoded. If false, those fields use no bits.

has_extra:
  codec: optional
  target_fields:
    extra:
      codec: uint8

The input dictionary for that configuration would include both the controlling field and the target field:

message = {
    "has_extra": True,
    "extra": 42,
}

Encode a ROS Message#

Use ltcodecs.ros_message_codec.RosMessageCodec for ROS messages. If no field dictionary is provided, ltcodecs asks ARMW for the message fields and chooses codecs from the ROS field types.

from bitstring import ConstBitStream
import armw
import ltcodecs

String = armw.import_message("std_msgs", "String")

msg = String()
msg.data = "hello"

codec = ltcodecs.RosMessageCodec("std_msgs/String")
bits, metadata = codec.encode(msg)

decoded = codec.decode(ConstBitStream(bits))
assert decoded.data == "hello"

You can also provide a YAML field configuration for a ROS message:

data:
  codec: string
  max_length: 10
codec = ltcodecs.RosMessageCodec.from_codec_file(
    "std_msgs/String",
    "string_msg_codec.yaml",
)

ROS 1 and ROS 2 Type Names#

ROS message fields may use ROS 1 style type names such as pkg/Msg and pkg/Msg[] or ROS 2 introspection names such as pkg/msg/Msg and sequence<pkg/msg/Msg>. ltcodecs normalizes these forms when it infers fields from a ROS message or when an explicit field config contains a ROS type.

For arrays, use the array codecs directly when writing explicit configs:

samples:
  codec: variable_len_array
  max_length: 10
  element_type: uint8

points:
  codec: fixed_len_array
  length: 3
  element_type: msg
  element_params:
    ros_type: geometry_msgs/Point

Checksums#

Top-level dictionary and ROS message codecs can append a checksum. Supported values are crc8 and crc32.

codec = ltcodecs.DictCodec(fields, checksum="crc8")
bits, _metadata = codec.encode(message)
decoded = codec.decode(ConstBitStream(bits))

If the received checksum does not match the encoded content, decode raises ValueError.

Next Steps#

See Codec Reference for the codec reference, Codec Aliases for the full alias table, and Python API for the generated API reference.