TDMS文件格式内部结构

概览

本文详细说明了TDM流(TDMS)文件格式的内部结构。

内容

逻辑结构

TDMS文件将数据划分为三个不同层次的对象。最顶层由单个对象组成,其中包含了文件本身特有的信息,如作者或标题。每个文件可包含任意数量的组,每组又可包含任意数量的通道。在下图中,文件example events.tdms包含两个组,每组包含两个通道。

每个TDMS对象都有唯一的路径来进行标识。每个路径都是一个字符串,其中包含在TDMS层次结构中的对象名称及其所有者名称,由正斜杠分隔开来。每个名称都加引号。对象名称中的所有单引号都替换为双引号。下表展示了每种TDMS对象类型的路径格式示例:

对象名称对象路径
--文件/
Measured Data/'Measured Data'
Amplitude Sweep通道/'Measured Data'/'Amplitude Sweep'
Dr.T's Events/'Dr.T''s Events'
Time通道/'Dr.T''s Events'/'Time'

为了使所有TDMS客户端应用程序正常工作,每个TDMS文件都必须包含一个file对象。每个file对象都必须包含一个group对象,对应通道路径中使用的每个组名。此外,一个file对象可以包含任意数量没有通道的组对象。

每个TDMS对象可具有任意数量的属性。每个TDMS属性由名称(始终为字符串)、类型标识符和值组成。属性的典型数据类型包括数值类型,如整型或浮点数、时间标识或字符串。TDMS属性不支持数组。如果TDMS文件位于NI DataFinder的搜索区域内,所有属性都将自动可用于搜索。

只有TDMS文件中的通道对象可以包含原始数据数组。当前TDMS版本仅支持一维数组。

进制布局

每个TDMS文件都包含两种类型的数据:元数据和原始数据。元数据是存储在对象或属性中的描述性数据。原始数据则是附加到通道对象的数据数组。TDMS文件包含一个连续数据块中多个通道的原始数据。为了能够从该数据块提取原始数据,TDMS文件使用原始数据索引,其中包括有关数据块组成的信息,包括与该数据对应的通道,数据块针对该通道包含的值的数量,以及数据存储的顺序。

TDMS数据布局

数据以段为单位写入TDMS文件。每次将数据附加到TDMS文件时,都会创建一个新的数据段。有关此规则的例外情况,请参考本文的元数据原始数据部分。数据段由以下三部分组成:

  • 前端—包含基本信息,如标识文件为TDMS的标签、版本号,以及元数据和原始数据的长度信息。
  • 元数据—包含数据段中全部对象的名称和属性。对于包含原始数据(通道)的对象,元数据部分还包含索引信息,用于在数据段中找到该对象的原始数据。
  • 原始数据—与数据段中包含的任意对象相关联的所有原始数据的连续数据块。原始数据部分可以包含交错的数据值或一系列的连续数据块。原始数据部分还可以包含来自DAQmx的原始数据。

TDMS文件中的所有字符串(如对象路径、属性名称、属性值和原始数据值)均以UTF-8 Unicode编码。除原始数据值外,所有这些值都以32位无符号整型开头,包含以字节为单位的字符串长度,不包含长度值本身。TDMS文件中的字符串可能以NULL终止,但是由于存储了长度信息,当读取文件时,NULL终止符将被忽略。

TDMS文件中的时间标识以两个部分组成的结构形式存储:

  • (i64)自纪元01/01/1904 00:00:00.00 UTC以来的秒数(使用公历并忽略闰秒)
  • (u64)一秒的正分数(2^-64)

布尔值每个存储为1个字节,其中1表示TRUE,0表示FALSE。

前端

前端不仅包含用于验证数据段的信息,还包含用于随机访问TDMS文件的信息。以下示例显示了TDMS文件前端部分的二进制占用空间情况:

二进制布局(十六进制)说明
54 44 53 6D"TDSm"标签
0E 00 00 00ToC掩码0x1110(数据段包含对象列表、元数据、原始数据)
69 12 00 00版本号(4713)
E6 00 00 00 00 00 00 00下一个数据段偏移(值:230)
DE 00 00 00 00 00 00 00原始数据偏移(值:222)

上表中的前端部分包含以下信息:

  • 前端以4字节标签开头,用于标识TDMS数据段("TDSm")。
  • 接下来的四个字节用作位掩码,表示该数据段包含哪种数据。该位掩码称为ToC(目录)。以下标志的任意组合都可以在ToC中进行编码:
    标志说明
    #define kTocMetaData         (1L<<1)数据段包含元数据
    #define kTocRawData          (1L<<3)数据段包含原始数据
    #define kTocDAQmxRawData     (1L<<7)数据段包含DAQmx原始数据
    #define kTocInterleavedData  (1L<<5)数据段中的原始数据是交错的(如果未设置标志,数据则是连续的)
    #define kTocBigEndian        (1L<<6)数据段中的所有数值(包括前端、原始数据和元数据)均采用大端格式(如果未设置标志,则数据为小端格式)。ToC不受字节顺序影响,始终为小端格式。
    #define kTocNewObjList       (1L<<2)数据段包含新的对象列表(例如,该数据段中的通道与上一个数据段所包含的通道不同)
  • 接下来的四个字节包含一个版本号(32位无符号整型),指定数据段所遵循的最早TDMS修订版。在撰写本文时,版本号为4713。TDMS唯一的前期版本号为4712。

    注意:
    版本号4713与LabVIEW中的TDMS文件格式版本2.0相对应。版本号4712与LabVIEW中的TDMS文件格式版本1.0相对应。
  • 接下来的八个字节(64位无符号整型)描述了剩余数据段的长度(数据段的总长度减去前端长度)。如果文件中附加了其他数据段,则可以使用该数字来定位下一个数据段的起始点。如果应用程序在编写TDMS文件时遇到严重问题(崩溃、断电),则该整型的所有字节都可以为0xFF。该情况仅适用于文件的最后一个数据段。
  • 最后八个字节(64位无符号整型)描述了该数据段中元信息的总长度。这一信息用于随机访问原始数据。如果该数据段不包含任何元数据(属性、索引信息、对象列表),则该值为0。

数据

TDMS元数据由三个不同层次的数据对象组成,包括文件、组和通道。这些对象类型均可包含任意数量的属性。元数据部分在磁盘上具有以下二进制布局:

  • 该数据段中新对象的数量(32位无符号整型)。
  • 每个对象的二进制表示。

磁盘上单个TDMS对象的二进制布局由多个部分按以下顺序组成。对象可能仅包含这些组成部分的子集,具体取决于存储在特定数据段中的信息。

  • 对象路径(字符串)
  • 原始数据索引
    • 如果在数据段中未将任何原始数据分配给该对象,则将存储一个32位无符号整型(0xFFFFFFFF),而不是索引信息。
    • 如果该对象在数据段中包含DAQmx原始数据,则原始数据索引的前四个字节为“69 12 00 00”(表示原始数据包含DAQmx格式更改换算值)或“69 13 00 00”(表示原始数据包含DAQmx数字线路换算值)。前四个字节之后紧跟的是有关DAQmx原始数据索引的信息。有关FAQmx原始数据索引的更多信息,请参考下一条内容。
    • 如果该对象在数据段中的原始数据索引与前一个数据段中同一对象的索引完全匹配,则将存储一个32位无符号整型(0x0000000),而不是索引信息。
    • 如果该对象包含的原始数据与上一数据段中分配的索引信息不匹配,则将为原始数据存储一个新索引:
      • 原始数据索引的长度(32位无符号整型)
      • 数据类型(tdsDataType枚举,存储为32位整型)
      • 数组维数(32位无符号整型)(在TDMS文件格式版本2.0中,1是唯一的有效值)
      • 值的数量(64位无符号整型)
      • 总大小(以字节为单位,64位无符号整型)(仅存储用于可变长度数据类型,例如字符串)
  • 如果原始数据索引为DAQmx原始数据索引,则该索引将包含以下信息:
    • 数据类型(32位无符号整型)(其中“FF FF FF FF”表示原始数据为DAQmx原始数据)
    • 数组维数(32位无符号整型)(在TDMS文件格式版本2.0中,1是唯一的有效值)
    • 值的数量(64位无符号整型),也称为“块大小”
    • 格式更改换算值的向量
      • 向量大小(32位无符号整型)
        以下内容适用于第一个格式更改换算值的信息。
      • DAQmx数据类型(32位无符号整型)
      • 原始缓冲区索引(32位无符号整型)
      • 跨度内的原始字节偏移(32位无符号整型)
      • 样本格式位图(32位无符号整型)
      • 换算ID(32位无符号整型)
        (如果向量大小大于1,则该对象包含多个格式更改换算值,并且可以重复前几项中的信息。)
    • 原始数据宽度的向量
      • 向量大小(32位无符号整型)
      • 向量中的元素(每个元素都是32位无符号整型)
  • 属性的数量(32位无符号整型)
  • 属性。每个属性将存储以下信息:
    • 名称(字符串)
    • 数据类型(tdsDataType)
    • 值(数值存储为二进制,字符串存储为上述格式)。

下表列举了一个组和一个通道的元信息示例。其中,组包含两个属性、一个字符串和一个整型。通道包含原始数据索引,无属性。

二进制占用空间(十六进制)说明
02 00 00 00对象数
08 00 00 00第一个对象路径的长度
2F 27 47 72
6F 75 70 27
对象路径(/'Group')
FF FF FF FF原始数据索引(“FF FF FF FF”表示未将原始数据分配给对象)
02 00 00 00/'Group'的属性数量
04 00 00 00第一个属性名称的长度
70 72 6F 70属性名称(prop)
20 00 00 00属性值的数据类型(tdsTypeString)
05 00 00 00属性值的长度(仅适用于字符串)
76 61 6C 75
65
prop属性值(value)
03 00 00 00第二个属性名称的长度
6E 75 6D属性名称(num)
03 00 00 00属性值的数据类型(tdsTypeI32)
0A 00 00 00num属性值(10)
13 00 00 00第二个对象路径的长度
2F 27 47 72
6F 75 70 27
2F 27 43 68
61 6E 6E 65
6C 31 27
第二个对象的路径(/'Group'/'Channel1')
14 00 00 00索引信息的长度
03 00 00 00分配给该对象的原始数据的数据类型
01 00 00 00原始数据数组的维数(必须为1)
02 00 00 00
00 00 00 00
原始数据值的数量
00 00 00 00/'Group'/'Channel1'的属性数(无属性)

下表列举了DAQmx原始数据索引的示例。

二进制占用空间(十六进制)说明
03 00 00 00对象数
23 00 00 00组对象路径的长度
2F 27 4D 65
61 73 75 72
65 64 20 54
68 72 6F 75
67 68 70 75
74 20 44 61
74 61 20 28
56 6F 6C 74
73 29 27
对象路径(/'Measured Throughput Data (Volts)')
FF FF FF FF原始数据索引(“FF FF FF FF”表示未将原始数据分配给对象)
00 00 00 00/'Measured Throughput Data (Volts)'的属性数
34 00 00 00通道对象路径的长度
2F 27 4D 65
61 73 75 72
65 64 20 54
68 72 6F 75
67 68 70 75
74 20 44 61
74 61 20 28
56 6F 6C 74
73 29 27 2F
27 50 58 49
31 53 6C 6F
74 30 33 2d
61 69 30 27
69 12 00 00
/'Measured Throughput Data (Volts)'/'PXI1Slot03-ai0'
69 12 00 00DAQmx原始数据索引,包含格式更改换算值
FF FF FF FF数据类型,DAQmx原始数据
01 00 00 00数据维数
00 00 00 00
00 00 00 00
值的数量,此数据段中没有值
01 00 00 00
格式更改换算值的向量大小
05 00 00 00
第一个格式更改换算值的DAQmx数据类型
00 00 00 00
第一个格式更改换算值的原始缓冲区索引
00 00 00 00
跨度内的原始字节偏移
00 00 00 00
格式位图示例
00 00 00 00
换算ID
01 00 00 00
原始数据宽度的向量大小
08 00 00 00
原始数据宽度向量中的第一个元素
06 00 00 00
/'Measured Throughput Data (Volts)'/'PXI1Slot03-ai0'的属性数
11 00 00 00第一个属性名称的长度
4E 49 5F 53
63 61 6C 69
6E 67 5F 53
74 61 74 75
73
属性名称("NI_Scaling_Status")
20 00 00 00属性值的数据类型(tdsTypeString)
08 00 00 00属性值的长度(仅适用于字符串)
75 6E 73 63
61 6C 65 64
prop属性值("unscaled")
13 00 00 00第二个属性名称的长度
4E 49 5F 4E
75 6D 62 65
72 5F 4F 66
5F 53 63 61
6C 65 73
属性名称("NI_Number_Of_Scales")
07 00 00 00属性值的数据类型(tdsTypeU32)
02 00 00 00属性值(2)
16 00 00 00第三个属性名称的长度
4E 49 5F 53
63 61 6C 65
5B 31 5D 5F
53 63 61 6C
65 5F 54 79
70 65
属性名称("NI_Scale[1]_Scale_Type")
20 00 00 00属性的数据类型(tdsTypeString)
06 00 00 00属性值的长度
4C 69 6E 65
61 72/span>
属性值("Linear")
18 00 00 00第四个属性名称的长度
4E 49 5F 53
63 61 6C 65
5B 31 5D 5F
4C 69 6E 65
61 72 5F 53
6C 6F 70 65
属性名称("NI_Scale[1]_Linear_Slope")
0A 00 00 00属性的数据类型(tdsTypeDoubleFloat)
04 E9 47 DD
CB 17 1D 3E
属性值(1.693433E-9)
1E 00 00 00第五个属性名称的长度
4E 49 5F 53
63 61 6C 65
5B 31 5D 5F
4C 69 6E 65
61 72 5F 59
5F 49 6E 74
65 72 63 65
70 74
属性名称("NI_Scale[1]_Linear_Y_Intercept")
0A 00 00 00属性的数据类型(tdsTypeDoubleFloat)
00 00 00 00
00 00 00 00
属性值(0)
1F 00 00 00第六个属性名称的长度
4E 49 5F 53
63 61 6C 65
5B 31 5D 5F
4C 69 6E 65
61 72 5F 59
6E 70 75 74
5F 53 6F 75
72 63 65
属性名称("NI_Scale[1]_Linear_Input_Source")
07 00 00 00属性的数据类型(tdsTypeU32)
00 00 00 00属性值(0)

如上表所示,通道"/'Measured Throughput Data (Volts)'/'PXI1Slot03-ai0"包含两种换算值。一种换算值是格式更改,其信息存储在DAQmx原始数据索引中。另一种换算值是线性换算值,其信息存储为TDMS属性。格式更改换算值是可识别的,其中线性缩放器的斜率是1.693433E-9,截距是0,输入源ID是0。

与之前数据段相匹配的元信息可以在后续数据段中省略。这属于可选操作,但省略冗余的元信息能够显著提高文件的读取速度。如选择写入冗余信息,后续可使用LabVIEW、LabWindows/CVI、MeasurementStudio等提供的TDMS碎片整理功能移除这些信息。

  • 如果将新对象写入下一个数据段,则新数据段将包含上一个数据段的所有对象,以及此处描述的新对象。如果新数据段不包含上一个数据段的任何通道,或者数据段中通道的顺序发生变化,则新数据段需要包含所有对象的新列表。有关更多信息,请参阅本文的优化部分。
  • 如果将新属性写入上一数据段中已存在的对象,则会将新属性添加到该对象。
  • 如果将已经存在的属性写入对象,则将覆盖该属性之前的值。

    注意:
    在TDMS文件格式版本2.0中,为现有对象的名称属性指定值将重命名该对象。

以下示例显示了紧跟上述数据段的数据段元数据部分的二进制占用空间。写入新数据段的唯一元信息是新属性值。

二进制布局(十六进制)说明
01 00 00 00新对象/更改对象数
08 00 00 00对象路径的长度
2F 27 47 72
6F 75 70 27
对象路径(/'Group')
FF FF FF FF原始数据索引(未将原始数据分配给对象)
01 00 00 00新对象/更改属性数
03 00 00 00属性名称的长度
6E 75 6D属性名称(num)
03 00 00 00属性值的数据类型(tdsTypeI32)
07 00 00 00num新的属性值(7)

原始数据

数据段最终包含与每个通道关联的原始数据。所有通道的数据数组都按照通道在数据段的元信息部分中显示的确切顺序连接在一起。数值数据需要根据前端中的小端/大端标志进行格式化。请注意,通道在首次写入后便无法更改其大小端格式或数据类型。

字符串类型通道经过预处理,可实现快速随机访问。所有字符串都以一个连续的内存段相连。该连续内存段中每个字符串第一个字符偏移都存储到32位无符号整型数组中。该偏移值数组首先存储,然后再存储连接的字符串值。得益于这种布局,客户端应用程序可以通过重新定位文件指针(最多三次)从文件中的任意位置访问任意字符串值,而无需读取客户端不需要的任何数据。

如果数据段之间的元信息不变,则前端和元信息部分可以完全省略,而原始数据可以附加到文件末尾。接下来的每个原始数据块都具有相同的二进制布局,可以根据前端和元信息来计算数据块数量,具体步骤如下所示:

  1. 计算通道的原始数据大小。每个通道的元信息都包含数据类型数组维数值的数量。有关详细信息,请参阅本文的元数据部分。每个数据类型都与类型大小相关。您可以通过以下方法计算通道的原始数据大小:数据类型的类型大小×数组维数×值的数量。如果总大小(以字节为单位)值有效,则通道的原始数据大小就等于该值。
  2. 通过累加所有通道的原始数据大小,可以计算一个数据块的原始数据大小。
  3. 通过以下方法计算总数据块的原始数据大小:下一个数据段偏移 - 原始数据偏移。如果下一个数据段偏移的值为-1,则总数据块的原始数据大小等于文件大小减去原始数据的绝对起始位。
  4. 通过以下方式计算数据块的数量:总数据块的原始数据大小 ÷ 一个数据块的原始数据大小

原始数据可分为两种布局类型:交错和非交错。数据段前端的ToC位掩码表明数据段中的数据是否交错。例如:将32位整型值存储到通道1(1、2、3)和通道2(4、5、6)将产生以下布局:

数据布局二进制占用空间(十六进制)
非交错01 00 00 00  02 00 00 00  03 00 00 00  
04 00 00 00  05 00 00 00  06 00 00 00
交错01 00 00 00  04 00 00 00  02 00 00 00  
05 00 00 00  03 00 00 00  06 00 00 00

数据类型值

以下枚举类型描述了TDMS文件中属性或通道的数据类型。对于属性,数据类型值将存储在名称和二进制值之间。对于通道,数据类型将成为原始数据索引的一部分。

typedef enum {
    tdsTypeVoid,
    tdsTypeI8,   
    tdsTypeI16,   
    tdsTypeI32,   
    tdsTypeI64,
    tdsTypeU8,   
    tdsTypeU16,   
    tdsTypeU32,   
    tdsTypeU64,
    tdsTypeSingleFloat,   
    tdsTypeDoubleFloat,   
    tdsTypeExtendedFloat,   
    tdsTypeSingleFloatWithUnit=0x19,   
    tdsTypeDoubleFloatWithUnit,   
    tdsTypeExtendedFloatWithUnit,
    tdsTypeString=0x20,   
    tdsTypeBoolean=0x21,   
    tdsTypeTimeStamp=0x44,
    tdsTypeFixedPoint=0x4F,
    tdsTypeComplexSingleFloat=0x08000c,
    tdsTypeComplexDoubleFloat=0x10000d,
    tdsTypeDAQmxRawData=0xFFFFFFFF
} tdsDataType;

注意:

  • 有关在LabVIEW中使用tdsTypeTimeStamp的更多信息,请参阅《LabVIEW时间标识》文章。
  • 带单位的LabVIEW浮点类型可转换为带unit_string名称属性的浮点通道,将单位以字符串形式包含在内。

有关TDMS写入功能的更多信息,请参阅《借助基于VI的API编写TDMS文件》

定义属性

LabVIEW波形在TDMS文件中表示为数值通道,其中波形属性作为属性添加到通道中。

  • wf_start_time – 该属性表示波形被采集或生成的时刻。如果时间信息是相对的或波形不在时域(如频域)内,该属性值为零。
  • wf_start_offset – 该属性用于LabVIEW Express动态数据类型。频域数据和直方图结果将该值用作x-轴上的第一个值。
  • wf_increment – 该属性表示x-轴上连续两个采样点之间的增量。
  • wf_samples – 该属性表示波形中的采样数。

优化

如前几节所述,应用格式定义将创建完全有效的TDMS文件。但是,TDMS支持NI软件(如LabVIEW、LabWindows/CVI、MeasurementStudio等)常用的各种优化。如果应用程序尝试读取NI软件编写的数据,则需要支持本段所述的优化机制。

增量信息示例

只有当对象路径、属性和原始索引等元信息发生变化时,才会添加到数据段中。以下示例说明了什么是增量元信息。

在第一次写入迭代中,将写入通道1和通道2。每个通道具有三个32位整型值(1、2、3和4、5、6)和几个描述性属性。第一个数据段的元信息部分包含通道1和通道2的路径、属性和原始数据索引。设置ToC位字段的kTocMetaDatakTocNewObjListkTocRawData标志。第一次写入迭代会创建一个数据段。下表描述了第一个数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0E 00 00 00  69 12 00 00
8F 00 00 00  00 00 00 00  77 00 00 00
00 00 00 00
对象数02 00 00 00
元信息对象113 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 31 27 14
00 00 00 03  00 00 00 01  00 00 00 03
00 00 00 00  00 00 00 01  00 00 00 04
00 00 00 70  72 6F 70 20  00 00 00 05
00 00 00 76  61 6C 69 64
元信息对象213 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 32 27 14
00 00 00 03  00 00 00 01  00 00 00 03
00 00 00 00  00 00 00 00  00 00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道204 00 00 00  05 00 00 00  06 00 00 00

在第二次写入迭代中,所有属性均未更改,通道1和通道2仍分别具有三个值,并且未写入其他通道。因此,该迭代将不会写入任何元数据。上一个数据段的元数据仍然有效。该迭代不会创建新的数据段;相反,它仅会将原始数据附加到现有数据段中,然后更新前端部分的下一个数据段偏移。下表描述了更新数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0E 00 00 00  69 12 00 00
A7 00 00 00  00 00 00 00  77 00 00 00
00 00 00 00
对象数02 00 00 00
元信息对象113 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 31 27 14
00 00 00 03  00 00 00 01  00 00 00 03
00 00 00 00  00 00 00 01  00 00 00 04
00 00 00 70  72 6F 70 20  00 00 00 05
00 00 00 76  61 6C 69 64
元信息对象213 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 32 27 14
00 00 00 03  00 00 00 01  00 00 00 03
00 00 00 00  00 00 00 00  00 00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道204 00 00 00  05 00 00 00  06 00 00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道204 00 00 00  05 00 00 00  06 00 00 00

在上表中,最后两行包含在第二次写入迭代期间附加到第一个数据段的数据。

第三次写入迭代将向每个通道再添加三个值。在通道1中,status属性曾在第一个数据段中设置为valid,但现在需要设置为error。这次迭代将创建一个新的数据段,该数据段的元数据部分现包含属性通道的对象路径、名称、类型和值。之后读取文件时,error值将覆盖先前写入的valid值。但是,之前的有效值将保留在文件中,除非对其进行了碎片整理。下表描述了第二个数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0A 00 00 00  69 12 00 00
50 00 00 00  00 00 00 00  38 00 00 00
00 00 00 00
对象数01 00 00 00
元信息对象113 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 31 27 00
00 00 00 01  00 00 00 04  00 00 00 70  
72 6F 70 20  00 00 00 05  00 00 00 65  
72 72 6F 72
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道204 00 00 00  05 00 00 00  06 00 00 00

第四次写入迭代添加了一个额外通道voltage,其中包含五个值(7、8、9、10、11)。该迭代将在TDMS文件中创建一个新的数据段,即第三个数据段。由于前一个数据段的所有其他元数据仍然有效,第四个数据段的元数据部分仅包括对象路径、属性和通道电压的索引信息。原始数据部分包含通道1的三个值,通道2的三个值和通道电压的五个值。下表描述了第三个数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0A 00 00 00  69 12 00 00
5E 00 00 00  00 00 00 00  32 00 00 00
00 00 00 00
对象数01 00 00 00
元信息对象312 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 76 6F  6C 74 61 67  65 27 14 00
00 00 03 00  00 00 01 00  00 00 05 00
00 00 00 00  00 00 00 00  00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道204 00 00 00  05 00 00 00  06 00 00 00
原始数据通道307 00 00 00  08 00 00 00  09 00 00 00
0A 00 00 00  0B 00 00 00

在第四个数据段中,通道2现有27个值。所有其他通道保持不变。现在,元数据部分包含通道2的对象路径和新原始数据索引,不包含通道2的属性。下表描述了第四个数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0A 00 00 00  69 12 00 00
BF 00 00 00  00 00 00 00  33 00 00 00
00 00 00 00
对象数01 00 00 00
元信息对象213 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 32 27 14
00 00 00 03  00 00 00 01  00 00 00 1B
00 00 00 00  00 00 00 00  00 00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道201 00 00 00  02 00 00 00  03 00 00 00
04 00 00 00  05 00 00 00  06 00 00 00
07 00 00 00  08 00 00 00  09 00 00 00
0A 00 00 00  0B 00 00 00  0C 00 00 00
0D 00 00 00  0E 00 00 00  0F 00 00 00
10 00 00 00  11 00 00 00  12 00 00 00
13 00 00 00  14 00 00 00  15 00 00 00
16 00 00 00  17 00 00 00  18 00 00 00
19 00 00 00  1A 00 00 00  1B 00 00 00
原始数据通道307 00 00 00  08 00 00 00  09 00 00 00
0A 00 00 00  0B 00 00 00

在第五个数据段中,应用程序将停止编写通道2。该应用程序仅继续编写通道1和通道电压。由于通道顺序的变化,需要编写新的通道路径列表。必须设置ToC位kTocNewObjList。新数据段的元数据部分必须包含所有对象路径的完整列表,但不包含属性和原始数据索引,除非它们也发生变化。下表描述了第五个数据段的二进制占用空间。

部分二进制占用空间(十六进制)
前端54 44 53 6D  0E 00 00 00  69 12 00 00
61 00 00 00  00 00 00 00  41 00 00 00
00 00 00 00
对象数02 00 00 00
元信息对象113 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 63 68  61 6E 6E 65  6C 31 27 00
00 00 00 00  00 00 00
元信息对象212 00 00 00  2F 27 67 72  6F 75 70 27
2F 27 76 6F  6C 74 61 67  65 27 00 00
00 00 00 00  00 00
原始数据通道101 00 00 00  02 00 00 00  03 00 00 00
原始数据通道307 00 00 00  08 00 00 00  09 00 00 00
0A 00 00 00  0B 00 00 00

索引文件

写入TDMS文件的所有数据都存储在扩展名为*.tdms的文件中。TDMS文件可以随附*.tdms_index可选索引文件。索引文件用于加快*.tdms文件的读取速度。如果NI应用程序打开没有索引文件的TDMS文件,则将自动创建索引文件。如果LabVIEW或LabWindows/CVI等NI应用程序编写TDMS文件,则将同时创建索引文件和主文件。

索引文件是*.tdms文件的精确副本,不同之处在于它不包含任何原始数据,并且每个数据段都以TDSh标签而不是TDSm标签开头。索引文件包含各种信息,可精确定位*.tdms文件中任意通道的任意值。

结论​

简而言之,TDMS文件格式旨在以极高的速度写入和读取测量数据,同时保持描述性信息的层次结构。尽管二进制布局本身非常简单,但是通过增量写入元数据实现的优化可能会导致文件配置异常复杂。

更多​资源

Was this information helpful?

Yes

No