简介
ULog 是用于记录数据的文件格式,该格式是自解析的,即它包含了自己记录的消息格式。
Ulog可以用于记录输入、内部状态、和log消息。所有二进制数据使用小端格式。
数据类型
ULog使用C语言中的数据类型
Type | Bytes |
---|---|
int8_t,uint8_t | 1 |
int16_t, uint16_t | 1 |
int32_t, uint32_t | 4 |
int64_t, uint64_t | 8 |
float | 4 |
double | 8 |
bool, char | 1 |
所有以上类型都可以以数组形式使用,比如float[5]
,所有的字符串char[length]
都不包含\0
,字符串是大小写敏感的。
文件组织
ULog文件有以下三个部分组成
文件头 |
---|
定义段 |
数据段 |
文件头
文件头固定16字节长度,数据如下:
0x55 0x4c 0x6f 0x67 0x01 0x12 0x35 | 0x01 | uint64_t |
---|---|---|
File magic (7B) | Version (1B) | Timestamp (8B) |
Timestamp是一个64位整型数据,标记了日记记录的开始时间,单位是毫秒。
定义段
定义部分是变长的,包含了版本信息、数据格式定义和参数值。
定义部分和数据部分都由消息流组成。
消息
每个消息都由消息头开始
struct message_header_s {
uint16_t msg_size;
uint8_t msg_type
};
msg_size
是消息除去头部分后的字节数(头部分占3字节)。
msg_type
有如下定义:
- ‘B’: 标记位消息
- ‘F’ :单个(复合)类型的格式定义,可以在另一个定义中作为嵌套类型记录或使用。
- ‘I’:信息消息
- ‘M’:复合信息消息
- ‘P’:参数消息
标记位消息
struct ulog_message_flag_bits_s {
uint8_t compat_flags[8];
uint8_t incompat_flags[8];
uint64_t appended_offsets[3]; ///< file offset(s) for appended data if appending bit is set
};
该消息必须作为第一条消息,紧跟在文件头后面,所以它有确定的偏移量;
- compat_flags:
目前没有任何定义,设置为0 - incompat_flags:
如果日志包含数据并且appended_offsets数组有非零值,则incompat_flags[0]的LSB设置为1 - appended_offsets:
数据的文件偏移量,如果没有数据段,appended_offsets必须全部设为0。附加数据使用以下流程:
1、设置incompat_flags位
2、将第一个attachment_offsets设置为0到日志文件的长度。
3、添加有效消息
格式定义消息
struct message_format_s {
struct message_header_s header;
char format[header.msg_size-hdr_size];
};
header
是上文中的消息头。
format
具有以下格式的纯文本字符串:
message_name:field0;field1;…
他可以有任意字段的field,每个field以;
分隔。field具有以下格式:
type field_name / type[array_length] field_name
type
是上文中提到的数据类型,或者是嵌套类型。
field_name
是用户自定义的名称,但是有一些名称是特殊的:
timestamp
每条记录的消息必须包含一个时间戳字段,但不需要是第一个字段。目前使用的类型是uint64_t,单位是微秒。对于相同的消息,时间戳必须是单调递增的。Padding
如果名称以_padding开头的字段名称,解析器会忽略其数据。 这些字段用于确保正确对齐。
信息消息
struct message_info_s {
struct message_header_s header;
uint8_t key_len;
char key[key_len];
char value[header.msg_size-hdr_size-1-key_len]
};
header
是上文中的消息头。
key_len
key的长度。
key
是一个纯字符串:
message_name:field
field类似于格式定义消息,不同的是它只有一个field且不需要;
结尾。
value
包含key中描述的数据
特定的信息消息在整个日志最多出现一次,解析器可以将其存储为字典
预定义的信息消息是:
key | Description | Example for value |
---|---|---|
char[value_len] sys_name | Name of the system | “PX4” |
char[value_len] ver_hw | Hardware version (board) | “PX4FMU_V4” |
char[value_len] ver_hw_subtype | Board subversion (variation) | “V2” |
char[value_len] ver_sw | Software version (git tag) | “7f65e01” |
char[value_len] ver_sw_branch | git branch | “master” |
uint32_t ver_sw_release | Software version (see below) | 0x010401ff |
char[value_len] sys_os_name | Operating System Name | “Linux” |
char[value_len] sys_os_ver | OS version (git tag) | “9f82919” |
uint32_t ver_os_release | OS version (see below) | 0x010401ff |
char[value_len] sys_toolchain | Toolchain Name | “GNU GCC” |
char[value_len] sys_toolchain_ver | Toolchain Version | “6.2.1” |
char[value_len] sys_mcu | Chip name and revision | “STM32F42x, rev A” |
char[value_len] sys_uuid | Unique identifier for vehicle (eg. MCU ID) | “392a93e32fa3” |
char[value_len] replay | File name of replayed log if in replay mode | “log001.ulg” |
int32_t time_ref_utc | UTC Time offset in seconds | -3600 |
ver_sw_release和ver_os_release的格式为:0xAABBCCTT,其中AA为major,BB为minor,CC为patch,TT为类型。 类型定义如下:> = 0:开发,> = 64:alpha版本,> = 128:beta版本,> = 192:RC版本,== 255:发布版本。 因此,例如0x010402ff转换为发行版v1.4.2。
复合信息消息
struct ulog_message_info_multiple_header_s {
uint8_t is_continued; ///< can be used for arrays
uint8_t key_len;
char key[key_len];
char value[header.msg_size-hdr_size-2-key_len]
};
与信息消息相同,但是它可以有多个具有相同key的消息(解析器将它们存储为列表)。 is_continued可用于拆分消息:如果设置为1,则它是具有相同key的上一消息的一部分。
参数消息
struct message_info_s {
struct message_header_s header;
uint8_t key_len;
char key[key_len];
char value[header.msg_size-hdr_size-1-key_len]
};
如果一个参数在运行时动态改变,该条消息也可出现在数据段。它的数据格式必须是int32_t/float.
数据段
下列消息会出现在数据段:
- ‘A’:订阅消息
- ‘R’:取消订阅消息
- ‘D’:日志数据
- ‘L’:字符串消息
- ‘S’:同步消息
- ‘O’:丢失
- ‘I’:信息消息
- ‘M’:复合信息消息
- ‘P’:参数消息
订阅消息
struct message_add_logged_s {
struct message_header_s header;
uint8_t multi_id;
uint16_t msg_id;
char message_name[header.msg_size-hdr_size-3];
};
该消息必须出现在message_data_s前,用于登记message_data_s消息的id和名称。
header
:上文中定义的消息头。
multi_id
:相同的消息可以有多个实例,比如有两个相同的传感器,则默认第一个为0。
msg_id
:用于匹配message_data_s的唯一id。
message_name
:消息名称,必须与message_format_s定义中:
message_name:field0;field1;…
的名称message_name保持一致。
取消订阅消息
目前没有使用
日志数据消息
struct message_data_s {
struct message_header_s header;
uint16_t msg_id;
uint8_t data[header.msg_size-hdr_size];
};
header
:上文中定义的消息头。
msg_id
:message_add_logged_s定义的唯一id。
data
:message_format_s中定义的二进制数据部分。
字符串消息
struct message_logging_s {
struct message_header_s header;
uint8_t log_level;
uint64_t timestamp;
char message[header.msg_size-hdr_size-9]
};
header
:上文中定义的消息头。
log_level
:类似linux中的消息级别:
Name | Level value | Meaning |
---|---|---|
EMERG | ‘0’ | System is unusable |
ALERT | ‘1’ | Action must be taken immediately |
CRIT | ‘2’ | Critical conditions |
ERR | ‘3’ | Error conditions |
WARNING | ‘4’ | Warning conditions |
NOTICE | ‘5’ | Normal but significant condition |
INFO | ‘6’ | Informational |
DEBUG | ‘7’ | Debug-level messages |
timestamp
:微妙时间戳。
message
:消息内容。
同步消息
目前未使用
丢失
标记给定持续时间的丢失(丢失的日志消息),以ms为单位。 丢失可能发生在设备不够快时。
struct message_dropout_s {
struct message_header_s header;
uint16_t duration;
};
header
:上文中定义的消息头。
duration
:持续时间