Appearance
File formats
The exact bytes each recorder writes, for anyone writing an importer, a parser or a conversion script. Everything here is a property of the writer, not a specification the writer follows — where the output departs from BUSMASTER's own conventions, that is said explicitly.
Both formats use CRLF line endings and UTC timestamps throughout. Neither carries a timezone, an interface name inside the file, or a bus bitrate that means anything.
Raw capture — BUSMASTER text
Header
Eleven lines, written when the file is opened. Ten of them are fixed literals; only the date line varies.
***BUSMASTER Ver 3.2.2***
***PROTOCOL CAN***
***NOTE: PLEASE DO NOT EDIT THIS DOCUMENT***
***[START LOGGING SESSION]***
***START DATE AND TIME DD:MM:YYYY hh:mm:ss:000***
***HEX***
***SYSTEM MODE***
***START CHANNEL BAUD RATE***
***CHANNEL 1 - Kvaser - Kvaser Hybrid 2xCAN/LIN #0 (Channel 0), Serial Number- 0, Firmware- 0x00000399 0x0003000f - 500000 bps***
***END CHANNEL BAUD RATE***
***<Time><Tx/Rx><Channel><CAN ID><Type><DLC><DataBytes>***| Field | Notes |
|---|---|
DD:MM:YYYY | day, month, four-digit year, colon-separated, UTC |
hh:mm:ss | UTC, and the seconds value is one greater than the real one |
:000 | a constant; there is no sub-second part in the header |
the CHANNEL 1 line | a fixed literal. It names Kvaser hardware and 500000 bps whatever the interface actually is |
Two consequences for a parser: do not read the bus bitrate or the adapter from the header, and do not treat the start second as exact.
Files written by BUSMASTER itself may additionally carry a ***START DATABASE FILES*** block between the header and the legend. This recorder never writes one, and anything reading these files should skip every line beginning with * rather than count header lines.
Data lines
One line per received frame, appended and flushed immediately.
hh:mm:ss:tttt Rx 1 0xID x DLC B0 B1 … Bn| Field | Value | Notes |
|---|---|---|
hh:mm:ss | UTC time of the frame | from the kernel's receive timestamp |
tttt | 0000–9999 | tenths of a millisecond — the microsecond count divided by 100 |
Rx | always Rx | the recorder never writes Tx; it does not transmit |
1 | always 1 | channel number, a constant |
0xID | uppercase hex, no leading zeros | the identifier masked to 29 bits |
x | always x | type; see below |
DLC | 0–8 | decimal |
B0 … | uppercase hex, two digits each | DLC bytes, space-separated |
Example:
07:59:56:1193 Rx 1 0x40C1 x 8 00 00 00 00 16 00 18 00Remote-transmission frames take a shortened form — the type becomes xr, the length is written as 0, and no data bytes follow:
07:59:56:1193 Rx 1 0x40C1 xr 0Three properties a parser has to accommodate:
- The type is always
x. The identifier is masked with the extended-frame mask before printing and the type letter is a constant, so an 11-bit standard frame and a 29-bit extended frame are written identically. The distinction is not recoverable from the file. - Error frames are absent. Any frame carrying the error flag is discarded before the line is formatted. A gap in a capture may be silence or may be an error storm; the file cannot tell you which.
- The identifier is not zero-padded.
0x40C1and0x1040have four digits,0x7has one. Match on the0x…field, not on column position.
Footer
Two lines, written on rotation and on a clean shutdown.
***END DATE AND TIME DD:MM:YYYY hh:mm:ss:000***
***[STOP LOGGING SESSION]***The date is taken from the wall clock at the moment of closing, not from the last frame. Two defects in it, both harmless as long as you know:
- The month is one lower than the real month. The header's month is correct; the footer's is not. In January it prints
00. - The seconds value is one greater than the real one, as in the header.
A file that ends without these two lines was not closed cleanly — the process was killed or the Brain lost power. Such a file is otherwise valid and can be read to its last complete line.
Parsing it
Every non-data line begins with *, which is the only reliable discriminator:
sh
zcat can0.3.log.gz | grep -v '^\*' | awk '{print $1, $4, $6}'Fields are space-separated with no quoting and no escaping, so awk, cut and split() all work directly. Field 1 is the time, field 4 the identifier, field 5 the type, field 6 the length, and fields 7 onwards the data bytes.
Converting the time to seconds past midnight UTC:
sh
zcat can0.3.log.gz | grep -v '^\*' |
awk -F'[: ]' '{printf "%.4f\n", $1*3600 + $2*60 + $3 + $4/10000}'Decoded capture — CSV
One file per combination of function code, instance and broadcast identifier. The file extension is .log, the content is CSV.
Header row
datetime,<field>,<field>,…The column names after datetime are generated from the frame's own field definitions in the MUXEN frame library, so the authoritative list for any given file is the header line in that file. Read it; do not assume it.
For a combination the recorder does not decode, the header is exactly:
datetime,and every row carries a timestamp and one empty field.
Data rows
YYYY-MM-DDThh:mm:ss.mmmZ,<value>,<value>,…| Field | Value |
|---|---|
YYYY-MM-DDThh:mm:ss.mmmZ | ISO 8601, UTC, milliseconds. The Z is literal |
| the rest | the decoded field values, in header order |
There is no quoting, no escaping and no footer. A row is appended and flushed as each frame arrives.
What is decoded
The decoder is keyed on the function code and the broadcast identifier only — the instance is not part of the key, so every instance of a given function decodes with the same columns. Thirty-one combinations are recognised:
| Device function | Frames decoded |
|---|---|
| Button | life |
| Power output, Bloc 8 | life, io, current 1, current 2 |
| Power source interconnection | life, state, current |
| Power source genset | life, state |
| Power source battery | life, state, cycle |
| Converter | life, state |
| Motor | life |
| Lighting | life |
| Display | life |
| Solar | life, state |
| Navigation instruments | relative wind, true wind, heading, speed, distance to destination, time to destination, time |
| IMOCA keel team access | system state, keel state, capacity state |
| Generic I/O | life |
Anything else on the bus still produces a file, with the empty header described above. That is worth remembering when sizing a folder: the file count follows the bus, not this table.
Parsing it
The format is plain comma-separated with a header line, so any CSV reader works:
sh
zcat can0_device_5_0_1.1.log.gz | head -1 # the columns
zcat can0_device_5_0_1.1.log.gz | tail -n +2 # the rowsThe timestamp column parses directly as ISO 8601 in every spreadsheet and every scripting language. Note the CRLF endings: readers that split on \n alone leave a trailing carriage return on the last field of each row.
What neither format records
Worth stating once, because each of these is a question a parser author eventually asks:
| Not recorded | Consequence |
|---|---|
| the interface name, inside the file | it is only in the filename |
| the bus bitrate | the header's value is a fixed literal |
| standard versus extended identifiers | everything is written as extended |
| error frames | absent from the raw capture entirely |
| transmitted frames | the recorder never transmits while recording |
| the recorder version | nothing in either file identifies the writer |
| any gap or overrun marker | a dropped frame leaves no trace |
