dbc:   Vector DBC (CAN database) files
1 Quick start
2 Bit numbering conventions
3 Parser and serializer
parse-dbc
serialize-dbc
dbc
message
signal
4 Message codec
message-decode
message-encode
signal-decode
signal-encode
message-signal
signal-active?
5 Tokenizer
tokenize
token
6 Limitations
7 License
9.3

dbc: Vector DBC (CAN database) files🔗ℹ

turinglambdaai

 (require dbc) package: dbc

The "dbc" package reads and writes DBC files — the de-facto-standard CAN database format invented by Vector Informatik and consumed by virtually every CAN tool in the automotive industry. A DBC file describes the symbolic layer of a CAN bus: message and signal names, bit layouts, scaling, value tables (choices), comments and attributes.

Everything is implemented in pure Racket: no FFI, no system dependency. The implementation is validated bit-for-bit against the Python cantools library on its own test corpus (Intel and Motorola byte order, signed/unsigned and IEEE float signals, multiplexing, extended multiplexing).

1 Quick start🔗ℹ

Examples:
> (define db (parse-dbc "VERSION \"1.0\"\n\nBO_ 37 ExampleMessage: 8 Vector__XXX\n SG_ VehicleSpeed : 39|16@1+ (0.01,0) [-100|100] \"kph\" Vector__XXX\n SG_ Temperature : 0|12@0- (0.01,250) [-40|215] \"degC\" Vector__XXX\n\nVAL_ 37 VehicleSpeed 0 \"standstill\" 1 \"moving\" ;"))
> (define m (for/first ([e (in-list (dbc-entries db))]
                        #:when (entry-bo? e))
              (entry-bo-message e)))
> (message-name m)

'ExampleMessage

> (message-decode m (bytes 1 176 0 0 0 0 0 0))

'#hasheq((Temperature . 243.6) (VehicleSpeed . 0.0))

> (message-decode m (bytes 1 176 0 0 0 0 0 0) #:decode-choices? #t)

'#hasheq((Temperature . 243.6) (VehicleSpeed . 0.0))

> (message-encode m (hash 'Temperature 250.47 'VehicleSpeed 0.5))

#"\0\5\340\0\0\31\0\0"

A file is parsed with parse-dbc, which returns a dbc? value. Two views live side by side:

  • the entry stream — (dbc-entries db) keeps every grammar element of the file (BO_, CM_, BA_DEF_, VAL_, …) in source order, which is what makes lossless serialization possible; and

  • the codec view — each entry-bo wraps a message? whose signal?s carry the decoded choices/comments/float kinds, ready for message-decode and message-encode.

2 Bit numbering conventions🔗ℹ

A payload bit n lives in byte (quotient n 8) at bit position (remainder n 8) counting from the LSB.

  • Intel @1 (little-endian):: the value’s LSB sits at signal-start-bit; the value occupies bits start … start+len-1, accumulated LSB-first.

  • Motorola @0 (big-endian): the value’s MSB sits at signal-start-bit; the remaining bits walk the sawtooth — from a byte’s LSB the walk jumps to the next byte’s MSB (+15), otherwise it descends one position (-1) — accumulated MSB-first. This is the same placement cantools produces (verified bit by bit).

3 Parser and serializer🔗ℹ

procedure

(parse-dbc text) → dbc?

  text : string?
Parses a full DBC file. Unknown entry kinds are preserved as entry-unparsed values rather than rejected, so files with vendor-specific sections still round-trip.

procedure

(serialize-dbc db) → string?

  db : dbc?
Renders a dbc? back to text. The round-trip guarantee: parse-dbc of the result is equal? to the original value (whitespace is normalized; string escapes are re-applied).

struct

(struct dbc (version entries))

  version : (or/c string? #f)
  entries : list?
The parsed file: the VERSION string (or #f) and the ordered entry stream. Entries are instances of the entry-* structs: entry-version, entry-ns, entry-bs, entry-bu, entry-val-table, entry-bo, entry-cm, entry-ba-def, entry-ba-def-def, entry-ba, entry-val, entry-sig-valtype, entry-sig-mul-val, entry-ev and entry-unparsed.

struct

(struct message (id extended? name dlc transmitter signals))

  id : exact-integer?
  extended? : boolean?
  name : symbol?
  dlc : exact-nonnegative-integer?
  transmitter : (or/c symbol? #f)
  signals : (listof signal?)
One BO_-declared message. id has the 0x80000000-extended flag stripped (see message-extended?).

struct

(struct signal (name
    mux
    mux-ranges
    start-bit
    bit-count
    little-endian?
    signed?
    scale
    offset
    minimum
    maximum
    unit
    receivers
    float-kind
    choices
    comment))
  name : symbol?
  mux : (or/c #f (integer-in 0 65535) 'multiplexor)
  mux-ranges : list?
  start-bit : (integer-in 0 511)
  bit-count : (integer-in 1 64)
  little-endian? : boolean?
  signed? : boolean?
  scale : rational?
  offset : rational?
  minimum : (or/c rational? #f)
  maximum : (or/c rational? #f)
  unit : (or/c string? #f)
  receivers : (listof symbol?)
  float-kind : (or/c #f 'ieee)
  choices : (or/c hash? #f)
  comment : (or/c string? #f)
One SG_-declared signal. mux is #f for a plain signal, 'multiplexor for the M switch and an integer for an mN variant; mux-ranges holds the SG_MUL_VAL_ extended-multiplexing ranges as (cons lo hi) pairs. scale and offset are exact rationals. choices maps raw values to strings (from VAL_ / VAL_TABLE_).

4 Message codec🔗ℹ

procedure

(message-decode msg 
  payload 
  [#:decode-choices? decode-choices?]) 
 → (hash/c symbol? (or/c exact-integer? rational? real? string?))
  msg : message?
  payload : bytes?
  decode-choices? : boolean? = #f
Decodes payload as msg. Returns a hash keyed by signal name. Values are exact integers when the signal’s scale is 1 and its offset is 0, inexact reals otherwise, IEEE floats for float signals, and — with #:decode-choices? — the choice strings for values present in the signal’s value table. Only signals active under the current multiplexor value are included. Signals are scaled with the exact rational arithmetic of scale/offset before the final conversion.

procedure

(message-encode msg    
  values    
  [#:base base    
  #:dlc dlc]) → bytes?
  msg : message?
  values : (hash/c symbol? real?)
  base : (or/c bytes? #f) = #f
  dlc : (or/c #f exact-nonnegative-integer?) = #f
Encodes values (physical, scaled values) into a DLC-sized byte string. Signals absent from values stay at the buffer’s existing bits (#:base) or zero. The multiplexor switch, when present in values, is written first so muxed signals see the correct switch value regardless of hash iteration order.

procedure

(signal-decode sig payload)

 → (or/c exact-integer? rational? real?)
  sig : signal?
  payload : bytes?
Decodes a single signal, ignoring multiplexing.

procedure

(signal-encode sig payload value) → bytes?

  sig : signal?
  payload : bytes?
  value : real?
Encodes a single signal into payload (mutated and returned). Rounding is half-to-even, matching Python’s round and therefore cantools.

procedure

(message-signal msg name) → (or/c signal? #f)

  msg : message?
  name : symbol?
The first signal named name, or #f.

procedure

(signal-active? sig switch) → boolean?

  sig : signal?
  switch : (or/c exact-integer? #f)
Would sig be present in a frame whose multiplexor value is switch? Plain muxed signals match on their mN value; extended-mux signals (with SG_MUL_VAL_ ranges) match on mN or when the switch falls inside a range.

5 Tokenizer🔗ℹ

procedure

(tokenize text) → (listof token?)

  text : string?
Splits DBC text into tokens. DBC has no comments; a ; terminates an entry. Quoted strings may span lines and support \" and \\ escapes.

struct

(struct token (kind text))

  kind : (or/c 'word 'string 'punct)
  text : string?
A 'word is a bare identifier or number (signs fold into negative-number words only when not glued to a preceding word, so 100-200 of SG_MUL_VAL_ splits correctly); a 'string is a resolved quoted string; a 'punct is one of ( ) ; : | , [ ] + - plus the at-sign.

6 Limitations🔗ℹ

  • Attribute definitions and values (BA_DEF_, BA_) are parsed and round-tripped, but attribute semantics are not interpreted.

  • The EV_ (environment variable), SIG_GROUP_ and SGTYPE_ families are kept as opaque entries (parsed structurally where the grammar is regular, entry-unparsed otherwise).

  • J1939-specific conventions (PGN packing inside the 29-bit ID) are out of scope; the extended-ID bit is exposed via message-extended?.

7 License🔗ℹ

MIT. The test corpus includes sample files from the cantools project (MIT).