跳至内容

ULog 文件格式

ULog 是用于记录信息的文件格式。该格式具有自描述性,即包含格式和 uORB 记录的消息类型。本文档是 ULog 文件格式规范文档。它特别适用于有兴趣编写 ULog 分析器/序列器并需要对文件进行解码/编码的人员。

PX4 使用 ULog 将 uORB 主题记录为与以下来源相关(但不限于以下来源)的信息:

  • 设备输入: 传感器、RC 输入等
  • 内部状态: CPU 负载、姿态、EKF 状态等。
  • 字符串信息: printf 发言,包括 PX4_INFO()PX4_ERR().

格式使用 小二进制 所有二进制类型的内存布局(数据类型的最小有效字节 (LSB) 位于最低内存地址)。

数据类型

以下二进制类型用于记录日志。它们都与 C 语言中的类型相对应。

类型大小(字节
int8_t, uint8_t1
int16_t, uint16_t2
int32_t, uint32_t4
int64_t, uint64_t8
浮动4
双人8
bool, char1

此外,这些类型还可用作固定大小的数组:例如 浮点[5].

字符串 (字符[长度]) 不包含终端 NULL 字符 '\0' 在最后。

信息

字符串比较是区分大小写的,在比较报文名称时应考虑到这一点。 添加订阅.

ULog 文件结构

ULog 文件有以下三个部分:

----------------------
| 标题
----------------------
| 定义
----------------------
| 数据
----------------------

下文将对每个部分进行说明。

页眉部分

标头是固定大小的部分,格式如下(16 字节):

平实
----------------------------------------------------------------------
| 0x55 0x4c 0x6f 0x67 0x01 0x12 0x35 | 0x01 | uint64_t | uint64_t
| 文件魔法 (7B) | 版本 (1B) | 时间戳 (8B)
----------------------------------------------------------------------
  • 文件魔术(7 个字节): 文件类型指示符读作 "ULogXYZ,其中 XYZ 是神奇字节序列 0x01 0x12 0x35";
  • 版本(1 个字节): 文件格式版本(目前为 1)
  • 时间戳(8 字节): uint64_t 整数,表示记录开始的时间(微秒)。

定义 & 数据部分 报文头

定义和数据 各节包含许多 信息.每个报文前都有一个报文头:

c
结构 消息头 {
  uint16_t msg_size;
  uint8_t msg_type;
};
  • msg_size 是不含报文头的报文大小(字节)。
  • msg_type 定义内容,是一个单字节。

信息

下面的信息部分以其对应的字符为前缀。 msg_type.

定义部分

定义部分包含软件版本、报文格式、初始参数值等基本信息。

本节中的报文类型有

  1. 标志位
  2. 格式定义
  3. 信息
  4. 多种信息
  5. 参数
  6. 默认参数

'B':标志位信息

信息

该信息必须是 第一条信息 紧跟在文件头部分之后,因此它与文件开头的偏移量是固定不变的!

该信息为日志解析器提供日志是否可解析的信息。

c
结构 ulog_message_flag_bits_s {
  结构 message_header_s 标头; // msg_type = 'B';
  uint8_t compat_flags[8];
  uint8_t 不兼容标记[8];
  uint64_t 附加偏置[3]; 如果设置了追加位,则 // 追加数据的文件偏移量
};
  • compat_flags:兼容标志位

    • 这些标志表示日志文件中存在与任何 ULog 分析器兼容的特征。
    • compat_flags[0]: 默认参数 (位 0):如果设置,日志将包含 默认参数信息

    这些位可用于 ULog 未来与现有解析器兼容的变更。例如,可以通过在标准中定义一个新位来添加新的信息类型,而现有的解析器将忽略新的信息类型。这意味着如果其中一个未知位被设置,解析器就可以忽略该位。

  • 不兼容标记不兼容标志位。

    • incompat_flags[0]: DATA_APPENDED (位 0):如果设置,则日志包含附加数据,并且至少有一个 附加偏置 为非零。

    其余位目前未定义,必须设置为 0。这可用于引入现有解析器无法处理的破坏性变化。例如,当一个旧的 ULog 分析程序没有 DATA_APPENDED 读取较新的 ULog 时,它会停止解析日志,因为日志中会包含不符合规格的信息/概念。如果解析器发现任何未指定的位被设置,则必须拒绝解析日志。

  • 附加偏置:附加数据的文件偏移量(以 0 为基准)。如果不追加数据,则所有偏移量都必须为 0。这可用于可靠地为可能在信息中间停止的日志添加数据。例如,崩溃转储。

    附加数据的进程应该这样做:

    • 设置相关的 不兼容标记 位掩码
    • 设置第一个 附加偏置 为日志文件的长度,因为新数据将从这里开始。
    • 附加数据部分有效的任何类型的信息。

在未来的 ULog 规范中,可能会有更多字段附加在此报文的末尾。这意味着解析器不能假定此报文的长度是固定的。如果 msg_size 大于预期(目前为 40),任何额外的字节都必须被忽略/丢弃。

'F':格式信息

格式化信息以单个字符串定义单个信息名称及其内部字段。

c
结构 消息格式 {
  结构 message_header_s 标头; // msg_type = 'F';
  烧焦 格式[header.msg_size];
};
  • 格式 是一个纯文本字符串,格式如下: message_name:field0;field1;
    • 字段的数量可以任意设置(最少 1 个),字段之间用 ;.
    • 消息名称:包含这些允许字符的任意非空字符串: a-zA-Z0-9_-/ (并不同于任何 基本类型).

A 领域 格式如下 类型 字段名或数组: 类型[数组长度] 字段名称 使用(只支持固定大小的数组)。 字段名 必须由字符集 a-zA-Z0-9_.

A 类型基本二进制类型消息名称 的格式定义(嵌套使用)。

  • 类型在定义之前就可以使用。
    • 例如:信息 信息A:信息B[2] msg_b 可以在 MessageB:uint_8[3] 数据
  • 可以任意嵌套,但 无循环依赖
    • 例如 信息A:信息B[2] msg_b 及样品; 信息B:信息A[4] msg_a

有些字段名称比较特殊:

  • 时戳:每种报文格式都有一个 订阅信息 必须包含时间戳字段(例如,仅作为另一种格式嵌套定义的一部分使用的报文格式可能不包含时间戳字段)。
    • 其类型必须是 uint64_t.
    • 单位为微秒。
    • 时间戳必须始终是单调递增的。 msg_id (同上)。
  • _padding{}:字段名称以 _ 填充 例如 _padding[3]) 不应显示,其数据必须被阅读器忽略。
    • 这些字段可由写入者插入,以确保正确对齐。
    • 如果填充字段是最后一个字段,则可以不记录该字段,以避免写入不必要的数据。
    • 这意味着 message_data_s.data 将因填充的大小而缩短。
    • 不过,在嵌套定义中使用报文时,仍然需要填充。
  • 一般来说,报文字段不一定对齐(即报文中的字段偏移量不一定是其数据大小的倍数),因此阅读器必须始终使用适当的内存复制方法来访问单个字段。

'I':信息留言

信息报文定义了字典类型定义 密钥 : 价值 对任何信息,包括但不限于硬件版本、软件版本、软件的构建工具链等。

c
结构 ulog_message_info_header_s {
  结构 message_header_s 标头; // msg_type = 'I';
  uint8_t key_len;
  烧焦 密钥[key_len];
  烧焦 价值[header.msg_size-1-key_len]
};
  • key_len:键值长度
  • 密钥:包含关键字符串,格式为类型名例如 char[value_len] sys_toolchain_ver.名称的有效字符: a-zA-Z0-9_-/.类型可以是 基本类型,包括数组.
  • 价值:包含数据(长度为 value_len)对应的 密钥 例如 9.4.0.

信息

信息报文中定义的密钥必须是唯一的。也就是说,具有相同键值的定义不能超过一个。

解析器可以字典形式存储信息。

预定义的信息报文有

密钥说明数值示例
char[value_len] sys_name系统名称"PX4";
char[value_len] ver_hw硬件版本(板)"PX4FMU_V4";
char[value_len] ver_hw_subtype执行局颠覆(变体)"V2";
char[value_len] ver_sw软件版本(git 标签)"7f65e01";
char[value_len] ver_sw_branchgit 分支"主人";
uint32_t ver_sw_release软件版本(见下文)0x010401ff
char[value_len] sys_os_name操作系统名称"Linux";
char[value_len] sys_os_ver操作系统版本(git 标签)"9f82919";
uint32_t ver_os_release操作系统版本(见下文)0x010401ff
char[value_len] sys_toolchain工具链名称"GNU GCC";
char[value_len] sys_toolchain_ver工具链版本"6.2.1";
char[value_len] sys_mcu芯片名称和修订版STM32F42x,修订版 A";
char[value_len] sys_uuid载具的唯一标识符(如 MCU ID)"392a93e32fa3"...
char[value_len] log_type日志类型(未指定时为完整日志)使命";
char[value_len] 重放如果在重放模式下,重放日志的文件名"log001.ulg";
int32_t time_ref_utc以秒为单位的 UTC 时间偏移-3600

信息

value_len 的数据大小。 价值.这在 密钥.

  • 的格式。 ver_sw_release版本号 是:0xAABBCCTT, 其中 AA 为 专业BB 是 辅修CC 是补丁,TT 是 类型.
    • 类型 定义如下 >= 0发展、 >= 64:阿尔法版本、 >= 128:测试版、 = 192:RC 版本、 == 255发布版本。
    • 例如 0x010402FF 转化为发布版本 v1.4.2。

该信息也可用于数据部分(但这是首选部分)。

'M':多种信息消息

多重信息报文的作用与信息报文相同,但适用于长报文或具有相同密钥的多个报文。

c
结构 ulog_message_info_multiple_header_s {
  结构 message_header_s 标头; // msg_type = 'M';
  uint8_t is_continued; // 可用于数组
  uint8_t key_len;
  烧焦 密钥[key_len];
  烧焦 价值[header.msg_size-2-key_len]
};
  • is_continued 可用于分割报文:如果设置为 1,则它是具有相同密钥的上一条报文的一部分。

解析器可将所有信息以二维列表的形式存储,其顺序与日志中出现的信息顺序相同。

有效名称和类型与信息报文相同。

'P':参数信息

中的参数信息 定义 部分定义了启动日志记录时载具的参数值。其格式与 信息留言.

c
结构 消息信息
  结构 message_header_s 标头; // msg_type = 'P';
  uint8_t key_len;
  烧焦 密钥[key_len];
  烧焦 价值[header.msg_size-1-key_len]
};

如果参数在运行时发生动态变化,该信息也可以是 用于数据部分 也是如此。

数据类型仅限于 int32_t浮动.名称的有效字符: a-zA-Z0-9_-/.

'Q':默认参数信息

默认参数信息定义了特定载具和设置的参数默认值。

c
结构 ulog_message_parameter_default_header_s {
  结构 message_header_s 标头; // msg_type = 'Q';
  uint8_t 默认类型;
  uint8_t key_len;
  烧焦 密钥[key_len];
  烧焦 价值[header.msg_size-2-key_len]
};
  • 默认类型 是一个位字段,用于定义该值属于哪个组。
    • 必须至少设置一位:
      • 1<<0:全系统默认
      • 1<<1:当前配置(例如机身)的默认值

日志可能不包含所有参数的默认值。在这种情况下,缺省值等于参数值,不同的缺省类型会被独立处理。

该报文也可用于数据部分,其数据类型和命名与参数报文相同。

本节在第一节开始前结束。 订阅信息记录 信息,以先到者为准。

数据部分

中的信息类型 数据 部分是:

  1. 订阅
  2. 退订
  3. 记录数据
  4. 登录字符串
  5. 标签 已登录 字符串
  6. 同步
  7. 辍学标志
  8. 信息
  9. 多种信息
  10. 参数
  11. 默认参数

A:订阅信息

通过名称订阅信息,并为其赋予一个在 记录的数据 信息.这必须在第一个相应的 记录的数据 信息.

c
结构 消息添加日志 {
  结构 message_header_s 标头; // msg_type = 'A';
  uint8_t multi_id;
  uint16_t msg_id;
  烧焦 消息名称[header.msg_size-3];
};
  • multi_id信息格式:同一信息格式可以有多个实例,例如系统有两个相同类型的传感器。默认情况下,第一个实例必须为 0。
  • msg_id:与之匹配的唯一 ID 记录的数据 信息 数据。首次使用时必须将其设置为 0,然后再增加。
    • 相同 msg_id 不得重复用于不同的订阅。
  • 消息名称:要订阅的信息名称。必须与 信息格式 定义。

R:退订信息

退订信息,标记不再记录该信息(目前未使用)。

c
结构 删除已登录的消息 {
  结构 message_header_s 标头; // msg_type = 'R';
  uint16_t msg_id;
};

'D':记录的数据信息

c
结构 消息数据 {
  结构 message_header_s 标头; // msg_type = 'D';
  uint16_t msg_id;
  uint8_t 数据[header.msg_size-2];
};

有关填充字段的特殊处理,请参阅上文。

'L':已登录字符串信息

记录的字符串信息,即 printf() 输出。

c
结构 消息记录
  结构 message_header_s 标头; // msg_type = 'L';
  uint8_t log_level;
  uint64_t 时间戳;
  烧焦 信息[header.msg_size-9]
};
  • 时戳单位:微秒
  • 日志级别:与 Linux 内核相同:
名称水平值意义
应急'0'系统无法使用
警报'1'必须立即采取行动
CRIT'2'关键条件
ERR'3'错误条件
警告'4'警告条件
注意事项'5'正常但重要的条件
信息'6'信息
DEBUG'7'调试级信息

'C':标签已登录字符串消息

c
结构 消息记录标签 {
  结构 message_header_s 标头; // msg_type = 'C';
  uint8_t log_level;
  uint16_t 标签
  uint64_t 时间戳;
  烧焦 信息[header.msg_size-11]
};
  • 标签:代表日志信息字符串来源的 id。它可以代表进程、线程或类,具体取决于系统结构。

    • 例如,针对运行多个进程以控制不同有效载荷、外部磁盘和串行设备等的机载计算机的参考实现,可以使用一个 uint16_t 枚举 进入 标签 属性如下
    c
    枚举 class ulog_tag : uint16_t {
      未分配、
      mavlink_handler、
      ppk_handler、
      相机处理程序、
      ptp_handler、
      串行处理程序、
      看门狗
      io_service、
      cbuf、
      ulg
    };
  • 时戳单位:微秒

  • 日志级别:与 Linux 内核相同:

名称水平值意义
应急'0'系统无法使用
警报'1'必须立即采取行动
CRIT'2'关键条件
ERR'3'错误条件
警告'4'警告条件
注意事项'5'正常但重要的条件
信息'6'信息
DEBUG'7'调试级信息

'S':同步信息

同步信息,这样阅读器就可以通过搜索下一条同步信息来恢复已损坏的信息。

c
结构 消息同步
  结构 message_header_s 标头; // msg_type = 'S';
  uint8_t 同步魔法[8];
};
  • 同步魔法:[0x2F、0x73、0x13、0x20、0x25、0x0C、0xBB、0x12] (0x2F, 0x73, 0x13, 0x20, 0x25, 0x0C, 0xBB, 0x12)

'O':辍学信息

标记以毫秒为单位、持续时间为给定值的中断(丢失日志信息)。

例如,如果设备速度不够快,就会出现掉线现象。

c
结构 消息注销
  结构 message_header_s 标头; // msg_type = 'O';
  uint16_t 持续时间
};

与定义科共享的信息

由于 "定义 "部分和 "数据 "部分使用相同的报文头格式,因此它们也共享下面列出的相同报文:

对解析器的要求

有效的 ULog 解析器必须满足以下要求:

  • 必须忽略未知信息(但可以打印警告信息)
  • 还能解析未来/未知的文件格式版本(但会打印警告)。
  • 必须拒绝解析包含未知不兼容位的日志 (不兼容标记标志位 信息) 表示日志中包含解析器无法处理的破坏性更改。
  • 解析器必须能够正确处理在信息中间突然结束的日志。未完成的信息应直接丢弃。
  • 对于附加数据:解析器可以假设数据部分存在,即偏移量指向定义部分之后的位置。
    • 附加数据必须被视为常规数据部分的一部分。

已知的解析器实现

文件格式版本历史

第 2 版中的更改

  • 增加 多种信息消息标志位 信息 以及向日志添加数据的功能。
    • 用于将碰撞数据添加到现有日志中。
    • 如果数据被添加到日志中,而日志在信息中间被剪切,则无法使用版本 1 解析器进行解析。
  • 除此之外,如果解析器忽略未知信息,则可实现向前和向后兼容性。