Skip to main content

CBC Schemas

CBC (Compact Binary Coding) is a bit-packed binary format used in MAPS for extremely compact, deterministic wire formats.

Schemas define:

  • the application and version
  • one or more messages
  • a list of ordered fields for each message
  • the bit-width, type, and rules for each field

There is no inference: every bit is accounted for by the schema.


1. Description

CBC is designed for constrained and expensive links such as:

  • NTN / satellite transports
  • narrowband cellular
  • low-power IoT networks

Fields are encoded as a contiguous bitstream:

  • integers with custom bit-widths
  • bitmasks
  • nested structs
  • optional fields
  • encode/decode expressions for scaling

The CBC schema format is represented in JSON and stored in the schema field of a SchemaConfig with format: "cbc".


2. Schema Format

A CBC schema document has this shape:

{
"application": "exampleApp",
"version": "1.0",
"description": "Example application",
"messages": [
{
"name": "heartbeat",
"direction": "UPLINK",
"messageKey": 65280,
"description": "Device heartbeat",
"fields": [ /* field specs */ ]
}
]
}

2.1 Top-level fields

  • application
    Logical application or schema owner.

  • version
    Human-readable schema version string.

  • description
    Description of the CBC schema/application.

  • messages
    Array of message definitions.

2.2 Message definition

Each message contains:

  • name
    Message name, unique within the application.

  • direction
    E.g. "UPLINK", "DOWNLINK".

  • messageKey
    Integer identifier used on the wire.

  • description
    Human-readable description.

  • fields
    Ordered list of field specifications.
    Fields are packed into the bitstream in this order.


3. Field Specification

Fields are generated from FieldSpecification and serialised via toFieldMap(...).
A field object may contain:

  • name (string)
    Field name.

  • type (string)
    Storage/semantic type, for example:

    • uint → unsigned integer
    • int → signed integer
    • bitmask → flag field
    • struct → nested structure
  • size (integer, bits)
    Number of bits used for this field.
    Required for numeric/bitmask fields; omitted for some struct usages.

  • optional (boolean, only when true)
    If true, the field is not guaranteed to be present.
    Presence rules are defined by the protocol logic.

  • fixed (boolean, only when false)
    When omitted, the field is treated as fixed-length.
    When fixed: false, the field does not occupy a fixed number of bits (e.g. variable-length constructs).

  • description (string)
    Human-readable description.

  • decalc (string)
    Expression applied when decoding: logical = f(raw).
    Example: "v/1000".

  • encalc (string)
    Expression applied when encoding: raw = f(logical).
    Example: "v*1000".

  • min / max (number)
    Optional logical minimum and maximum values.

  • enum (object)
    Mapping from raw numeric value (as string) to label:

    "enum": {
    "0": "UNKNOWN",
    "1": "GOOD",
    "2": "BAD"
    }
  • fields (array)
    Nested field list for type: "struct".

3.1 Example field

{
"name": "latitude",
"type": "int",
"size": 18,
"decalc": "v/1000",
"encalc": "v*1000",
"description": "Latitude in degrees, 0.001° resolution"
}
  • Raw 18-bit signed integer on the wire.
  • Logical value = raw / 1000.
  • Encoded value = logical * 1000.

4. CBC SchemaConfig Example

CBC schemas are embedded into SchemaConfig.schema with format: "cbc".

{
"versionId": "1",
"name": "viasat-heartbeat-cbc",
"description": "Viasat NB-NTN heartbeat message (CBC-encoded)",
"labels": {
"uniqueId": "b1dc43de-4c9b-5d86-9425-cf958eeb598d",
"resource": "sensor",
"interface": "sensor.viasat.heartbeat",
"comments": "NB-NTN proof-of-concept CBC schema"
},
"format": "cbc",
"schema": {
"application": "viasatNtnProofOfConcept",
"version": "1.1",
"description": "Viasat NB-NTN proof of concept for efficient binary data transport",
"messages": [{
"description": "A basic health/config status message for NB-NTN devices.",
"direction": "UPLINK",
"name": "heartbeat",
"messageKey": 65280,
"fields": [
{ "name": "secOfDay", "type": "uint", "size": 17 },

{ "name": "location", "type": "struct", "optional": true, "fields": [
{ "name": "latitude", "type": "int", "size": 18, "decalc": "v/1000", "encalc": "v*1000" },
{ "name": "longitude", "type": "int", "size": 19, "decalc": "v/1000", "encalc": "v*1000" }
]},

{ "name": "signal", "type": "struct", "fields": [
{ "name": "rsrp", "type": "int", "size": 9 },
{ "name": "rsrq", "type": "int", "size": 6 },
{ "name": "sinr", "type": "int", "size": 6 },
{ "name": "rssi", "type": "int", "size": 9, "optional": true },
{ "name": "cellId", "type": "uint","size": 32, "optional": true }
]},

{ "name": "psmConfig", "type": "struct", "optional": true, "fields": [
{ "name": "tauT3412", "type": "bitmask", "size": 8 },
{ "name": "actT3324", "type": "bitmask", "size": 8 }
]},

{ "name": "edrxConfig", "type": "struct", "optional": true, "fields": [
{ "name": "edrxCycle", "type": "bitmask", "size": 4 },
{ "name": "edrxPtw", "type": "bitmask", "size": 4 }
]},

{ "name": "counter", "type": "uint", "size": 16, "optional": true }
]
}]
}
}

5. Java Usage

5.1 Registering a CBC schema

SchemaConfig cbcConfig = SchemaConfig.builder()
.name("viasat-heartbeat-cbc")
.format("cbc")
.schema(cbcSchemaJsonObject) // the CBC application/messages JSON
.build();

schemaRepository.register(cbcConfig);

5.2 Processing a CBC payload

At runtime, MAPS:

  1. Resolves the SchemaConfig (typically by topic + labels/matchExpression).
  2. Loads and caches the CBC schema.
  3. Decodes the bitstream:
    • walks messages[].fields[] in order
    • reads size bits per field
    • applies decalc expressions where defined
  4. Produces a Typed Event with named, typed fields.
  5. Passes the Typed Event through:
    • filtering
    • transformations
    • statistics
    • format conversion (e.g. CBC → JSON/Avro/Protobuf)

Encoding performs the inverse process using encalc.


6. CBC-Specific Notes

  • Bit layout is canonical: no padding, no inferred alignment.
  • Use struct to group related fields (e.g. signal, location, psmConfig).
  • Use decalc / encalc to avoid wasting bits on floating point; store scaled integers instead.
  • optional marks fields that may or may not be present. Actual presence rules must be honoured by encoder/decoder logic.
  • When evolving CBC schemas:
    • Prefer appending new fields at the end.
    • Avoid changing existing size or reordering fields.
    • If incompatible change is required, version the schema separately via version and/or versionId.