OSF 4 Format
This is the technical documentation of the OSF4 data format. See also:
Overview
Measuring systems generate data of various data types and recording formats from a wide variety of sources. Starting with physical measured variables such as pressure, vibrations or temperatures, through electrical variables, to any binary data blocks such as images. These variables are generated over a time axis. Either in fixed time intervals or sampling rates, or with individual time stamps depending on how they change. In order to be able to store this data permanently with a high degree of reliability, the data stream is converted into OSF files (opt. streaming files) using a "streaming" format in OSF files (optimeas streaming format).
Each file begins with a Magic header that marks the OSF format and the start of the binary format and the start of the binary data stream. This is followed by an XML block description of the channels, data types and recording formats.
Support is provided:
- Standard data types, such as currently bool, double, float, int64, int32, int16, int8, string (attribute "datatype")
- Scaled integer values, e.g. to save memory at high sampling rates (scaling via attribute "scale")
- Extended data formats with application-specific content Static or dynamic length
- Recording of time-stamped or equidistant data records
- Recording as single value, list of N-values (vector) or matrices
- Any markers within the data stream
- Repeated resynchronization to the time base,
- Binary data is always stored in little endian format (Intel)
- The time base for all time data is nanoseconds (ns) since Epoch, in the form of an int64_t value
Further information on:
==== Example of an OSF file ====
OSF4 30269
<?xml version="1.0" encoding="UTF-8"?>
<osf version="1" created_timezoneoffset="7200000" creator="smartdevice:14001000021" created_utc="1304013600000">
<channels count="181">
<channel physicalunit="°C" name="ME03/YP031T407" physicaldimension="temperature" datatype="double" index="0" user_reference="{002d376e-f265-4f58-82c7-358a3b72ca45}"/>
<channel physicalunit="°C" name="Pt1000/AMP04PT02" physicaldimension="temperature" datatype="double" index="1" user_reference="{01f61f02-4179-4c4e-b423-bddd7ff4cd1e}"/>
<channel physicalunit="°C" name="ME27/RL040T401" physicaldimension="temperature" datatype="double" index="2" user_reference="{05981247-5a72-4df9-855a-36fbe9caa3a9}"/>
<channel physicalunit="°C" name="ME22/TJ030T406" physicaldimension="temperature" datatype="double" index="3" user_reference="{060c9013-3e78-42e1-b9e5-c7f1b32e7a7f}"/>
<channel name="Schranksignale/JV001_DoorOpen_bus" datatype="bool" index="4" user_reference="{06e92479-c709-4636-ab88-5d85027af0cb}"/>
<channel physicalunit="°C" name="Schranksignale/JV001_Temp_Bottom_bus" physicaldimension="temperature" datatype="double" index="5" user_reference="{0755b76e-015c-4f22-9986-077220de1260}"/>
<channel physicalunit="°C" name="ME25/TJ010T411" physicaldimension="" datatype="double" index="6" user_reference="{07d039b9-8886-4708-8db8-970cf0bc925c}"/>
<channel physicalunit="°C" name="ME19/TA000T441" physicaldimension="" datatype="double" index="7" user_reference="{0c771f1b-6004-4a48-82ce-a3f5e8a6c4e0}"/>
...
</channels>
<info>
<info name="Some UTF-8 String Value" datatype="string" value="This is a comment"/>
<info name="Some Byte Array Value" datatype="bytearray" value="SGVsbG8sAFdvcmxkIQ=="/>
<info name="Some Numerical Value" datatype="float" value="-42.1"/>
<info name="Some Numerical Value 2" datatype="uint32" value="123"/>
<info physicalunit="°C" name="system/CPU_Temperature" type="double" value="43.4"/>
</info>
</osf>
[BEGIN OF BINARY DATA]...
From position 30269, binary data follows, which progressively contain data records of the individual channels.
The info area is ideal. Meta information can be saved there in the file for the measurement data.
The following entries must be present:
- name
- value
If no data type is specified, "string" is used. The remaining parameters are optional!
Structure of the OSF4 format
The OSF file essentially consists of the following blocks
- MAGIC header
- XML information block to describe the stored data
- binary data block
- XML closing block (optional) for quick access to information in the data
- MAGIC Trailer (optional)
MAGIC Header, OSF4
The Magic Header of the file contains exactly one line in ASCII format, which is is terminated with LF. BOMs from the UTF format or other encodings are not included at the beginning of the file.
Example:
OSF4 173762\n
The integer number indicates how many bytes follow in the following XML header. This means that it can be cut out immediately when reading and sent to a parser for processing.
XML meta information (start)
All META blocks (start, update, end) are in UTF-8 with LF Unix line endings (0x0A). The decoding of an XML text with LF (0x0A) breaks should generally not be a problem for PC software. software.
XML Prologue
The XML block with the META information for each channel begins with the typical XML prologue:
<?xml version="1.0" encoding="UTF-8"?>
with which the XML version and encoding are defined. For OSF files this is this is always UTF-8.
XML document node osf
The document node osf provides further information on the file format and the origin of the file.
<osf version="4"
created_utc="2009-06-30T18:30:00+02:00"
creator="smartdevice:14001000078"
created_at_longitude="50.2"
created_at_latitude="8.65"
created_at_altitude="193"
reason="BOOT"
total_seq_no="0"
triggered_seq_no="0"
namespacesep="."
tag="preview"
comment"=""
...
</osf>
The attributes of the node have the following meaning:
- Version: Versioning of the OSF-4 format, here initially 1 [default: "1"]
- created_utc: System timestamp when creating the file in ISO 8601 compliant format. Contains the date and time in UTC format plus optionally the time zone information
- creator: A text that uniquely identifies the object that created the file. This can be, for example, a program name, a device device serial number or a UUID.
- created_at_longitude: Optional. Geographical longitude when the file of the file. If the position is known when the file is created, this is entered.
- Created_at_latitude: Optional. Geographical latitude when creating of the file. If the position is known when the file is created, this is entered.
- created_at_altitude: Optional. Geographical altitude when creating of the file. If the position is known when the file is created, this is entered.
- Reason: Reason for creating the file. Possible values: "BOOT" - System start "SEQUENCE" - Division in terms of file size or file length over time "TRIGGERED" - Trigger condition fulfilled (rising edge of the trigger)
- total_seq_no: Absolute index of the file since system start starting with 0.
- triggered_seq_no: Relative index of the file since the last rising edge of the trigger starting with 0.
- namespacesep: Namespace separator for the hierarchical arrangement of the channel names. [default: "."]
- tag: Tag (category) of the OSF file [default: "preview"]
- comment: User-defined comment
Listing of channels channels
The only sub-node currently contained in osf is the list of channels is included. In the future, further information could be added, e.g. comments, calculation rules or filter settings could be included in the future. be included.
<channels count="181">
...
</channels>
The attributes of the node have the following meaning:
- Count: Number of channels listed.
Details of a channel channel
**Example
<channel
index="0"
name="channel1"
channeltype="scalar"
datatype="double"
timeincrement="1000000"
sizeoflengthvalue="2"
physicalunit="V"
reference="user defined reference string"
physicaldimension="temperature"
comment=""
displayname=""
uselogscale="false"
mimetype=""
spectrumtype="amplitude"
<!-- for matrix-valued and vector-valued channels -->
rows="1"
row_caption=""
row_physicalunit=""
row_min="0.0"
row_inc="0.0"
row_max="0.0"
row_align="center"
row_labels="TBD"
columns="1"
column_caption=""
column_physicalunit=""
column_min="0.0"
column_inc="0.0"
column_max="0.0"
column_align="center"
column_labels="TBD"
/>
The attributes of the node have the following meaning:
-
index: Indexing within this file to identify individual individual data records. The index for the first channel always starts at 0. at 0 and is monotonically increasing. The last index is therefore the channel number minus 1.
-
reference: [optional] An arbitrary identifier or a UUID that uniquely identifies the object that generated the data. has generated the data. Is used on an application-specific basis
-
name: Channel name with path if applicable. Is the application-related designation of the data stream.
-
physicalunit: [optional] Display unit of the channel, usually in SI notation, i.e. with differentiation between lower and upper case uppercase letters, prefixes etc., in future SW versions this unit designation will also be will also be converted on the basis of this unit designation. [default: ""]
-
datatype: Data type of the channel. In addition to standard types for Boolean, integer and floating point values, any other types are permitted, application-specific identifiers for data types are permitted.
-
cannode: Only valid if datatype=candata. Number of the CAN node. Starting with 1, the number of the CAN node is specified here from which the data is generated.
-
Timeincrement: [optional], fixed time increment in nanoseconds for equidistantly stored data. Unless timeincrement is not specified or "0", the data will be is provided with a timestamp. Depending on this entry the data blocks for data with a fixed time increment and data with a timestamp are structured differently. See block definition.
-
Channel type: Channel type of the channel. The structure of a data block is already structure of a data block is defined in advance. Depending on this definition, additional description parameters attribute are created.
- scalar: Channel with one data point per value. Example: datatype="double" returns a data track with a value above the time. Example datatype="gpsdata" returns the geoposition over time.
- vector: Channel with n scalar values in the data block. The result is a one-dimensional value field with the specified data type over time. over time. Example: FFT or one-dimensional classifications over over time.
- matrix: Channel with y[d1,d2] values over x. n pairs of values in the data block. The x value is the absolute time in nanoseconds as int64. Example: Rainflow classification matrix over time
- binary: Channel with any data blocks over time. Example: JPEG files over time. [default: "scalar"]
-
sizeoflengthvalue: [required], Each data block has a different length, which is prefixed to each data block with the data type is prefixed to each data block. Possible values are:
- 2: 2-byte integer - uint16, lengths up to 64 kByte
If the maximum block size is not sufficient to hold all the data samples of the data stream, the data stream is divided into several sufficiently small sufficiently small blocks. - 4: 4-byte integer - uint32, lengths up to 4 GByte (theoretically) [default: "2"]
- 2: 2-byte integer - uint16, lengths up to 64 kByte
-
Scale**: [optional] for integer data types (scalar, vector, matrix). Scaling of the stored integer value to the desired desired physical size [default: "1.0"]
-
offset: [optional] for integer data types (scalar, vector, matrix). Offset of the scaled integer value to the desired physical size [default: "0.0"]
-
physicaldimension: [optional for future extensions] English designation of the physical dimension, e.g. "temperature", "force", "torque", "velocity", "pressure", ... [default: ""]
-
comment: [optional] comment [default: ""]
-
displayname: [optional] Name of the channel in the display [default: ""]
-
uselogscale: [optional] Use single-logarithmic representation [default: "false"]
-
mimetype: [optional] MIME type of the channel content [default: ""]
-
Spectrum type: [optional] Spectrum type of the channel [default: "amplitude"] Possible representations:
- "amplitude" - Representation of real function values (or the amplitude of complex function values)
- "realImag" - Cartesian representation of complex function values
- "ampPhaseRad" - Polar representation of complex function values (phase in radians)
- "ampPhaseDeg" - Polar representation of complex function values (phase in degrees)
-
rows: [optional] Number of rows of the matrix/vector-valued channel [default: "1"]
-
row_caption: [optional] Row description of the matrix/vector-valued channel [default: ""]
-
row_physicalunit: [optional] Physical unit of the row of the matrix/vector-valued channel [default: ""]
-
row_min: [optional] Minimum interpolation point of the row of the matrix/vector-valued channel [default: "0.0"]
-
row_inc: [optional] interpolation point increment of the row of the matrix/vector-valued channel [default: "0.0"]
-
row_max: [optional] Maximum interpolation point of the row of the matrix/vector-valued channel [default: "0.0"]
-
row_align: [optional] Visual orientation of the row component of the value of a matrix/vector-valued channel [default: "center"] Possible representations:
- "left" - Left-aligned display
- "center" - Centered representation
- "right" - Right-aligned representation
-
row_labels: [optional] to be clarified with JAK [default: ""]
-
Columns**: [optional] Number of columns of the matrix/vector-valued channel [default: "1"]
-
column_caption: [optional] Column description of the matrix/vector-valued channel [default: ""]
-
column_physicalunit: [optional] Physical unit of the column of the matrix/vector-valued channel [default: ""]
-
column_min: [optional] Minimum interpolation point of the column of the matrix/vector-valued channel [default: "0.0"]
-
column_inc: [optional] Support point increment of the column of the matrix/vector-valued channel [default: "0.0"]
-
column_max: [optional] Maximum interpolation point of the column of the matrix/vector-valued channel [default: "0.0"]
-
row_align: [optional] Visual orientation of the column component of the value of a matrix/vector-valued channel [default: "center"] Possible representations:
- "left" - Left-aligned display
- "center" - Centered display
- "right" - Right-aligned display
-
row_labels: [optional] [default: ""]
If measured values are saved directly as a (signed) integer value with a short bit length for data reduction short bit length, these must be converted into the physical size using the attributes scale and offset attributes to convert them to the physical size. The following applies:
physical = scale * binary + offset
Standard data types
- Standard data types are (encoding: little-endian (Intel)):
-
bool: 1-byte, true (== 1) /false (== 0)
-
int8: 1-byte, signed
-
int16: 2-byte, signed
-
int32: 4-byte, with sign
-
int64: 8-byte, with sign
-
int64: 8-byte, signed - used for timestamp
-
float: 4-byte floating point (single precision), IEEE 754
-
double: 8-byte floating point (double precision), IEEE 754
-
complex<float>: (reserved for future applications) real/imag representation of complex numbers, each as float
-
complex<double>: (reserved for future applications) real/imag Representation of complex numbers, each as double
-
string: UTF-8 encoded characters without string termination. The length is specified by the block description.
-
candata: Contains the following struct to describe a CAN message, extends the definition in
include/linux/can.h
struct can_frame {
uint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
uint8 can_dlc; /* frame payload length in byte (0 .. 8) */
uint8 data[8];
} __attribute__((aligned(8))); -
gpsdata: Longitude, Latitude, Altitude - each as double value according to the following struct:
struct gps_location {
double longitude;
double latitude;
double altitude;
};
-
Binary data block
The binary data block begins directly after the XML
header information. The position in the file is specified in the MAGIC HEADER
is specified: OSF4 173762\n
The file is basically divided into individual data blocks. Each data block describes a sequence of 1 to N data samples of a channel. The data stream of the channel can be continued in subsequent blocks. By specifying its size, each data block automatically points to the next following data block. This means that even if the the write process is interrupted immediately (e.g. due to a power failure or interruption interruption of the data connection), the data can be read until the end of the data stream/file remains readable.
The basic structure of the data format therefore allows it:
- to simply skip information that is not required
- to retrofit any data and channel types for specific applications
- ignore unknown data blocks
- to be able to interpret the data up to the last data block
Same access for all data blocks
The start of a binary data block is defined as follows:
- uint16, channel index (as in version 3 too)
- <sizeoflengthvalue>: In the channel description of the header
defined. Depending on the definition of the size of the data word for
the length specification of the following block 2-4 bytes (uint16,uint32):
Number of subsequent bytes for control byte, meta information and
the actual data samples.
- The next channel data block therefore starts at <start>+2+<sizeoflengthvalue>+<size of the data block in byte>
- If the writing of the file has been aborted prematurely the end of the data block is beyond the end of the file. In In this case, reading can continue until the end of the file, provided that a complete data sample can be decoded. An incomplete data sample is discarded.
- uint8, control byte, determines the content of the subsequent meta information block in the data stream
By specifying the size of the block, the data block can be or unknown or not (yet) implemented channeltype or datatype can be skipped or read in one piece for interpretation. be read in one piece.
In the event of interpretation or data errors in a block, the data stream for the error message, the data stream for the respective channel can initially be interrupted. channel can initially be interrupted. The interpretation of the OSF stream can be continued with the next block. Further data blocks for the channel can be ignored until an absolute timestamp allows the data to be correct interpretation of the data again.
There are basically two different types of channels are distinguished:
- Channels with fixed time increment (equidistant)
- Channels with time stamp on each data sample
Control byte and meta information in the data stream
The control byte is the same for all channel and data types, which meta information is inserted before the actual data of the block. are inserted.
In order to reduce the number of implementation variants, the structure of the structure of the following data block is specified via a 7-bit ENUM and a control bit.
Control bit 7 indicates whether the data block contains 1- or n values/value pairs are contained in the data block.
The structure of the blockContent control byte as an enumeration of values is as follows as follows:
Value | Enum | Meaning | Data block |
---|---|---|---|
0 | bcMetaData | Use of this block type only for internal use, e.g. special functions and file termination, not assigned to a channel. | uint32: L := length of the text block uin8[L+1]: UTF-8 encoded text block with '0' termination. |
1 | bcTrustedTimestamp | Absolute timestamp in "ns since Epoch", the previous data value is valid and constant up to this point in time | int64: ns since Epoch |
2 | bcTimebaseRealign | Adjustment of the time axis | int64: Absolute timestamp in "ns since Epoch" int64: Shift of the time base in ns. > 0: Forward jump, gaping data < 0: Backward jump, overlapping data |
3 | bcStatusEvent | Status Event | int64: Absolute timestamp in "ns since Epoch" uint32: Status-Word |
4 | bcMessageEvent | Timestamped text entry | int64: Absolute timestamp in "ns since Epoch", applies ONLY to text message. uint32: L := length of the text message. uin8[L+1]: UTF-8 encoded text message it '\0' termination. |
5 | bcContinuedData | Append data with fixed sampling rate | if option bit 7 is set: uint32: N := number of samples, ... otherwise N := 1 Nx: Y: Data according to channel description (time axis connects to previous data block.) |
6 | bcStartData | First data block with data of fixed sampling rate in the stream | int64: Absolute timestamp in "ns since Epoch" if option bit 7 is set: uint32: N := number of samples, ... otherwise N := 1 Nx: Y: Data according to channel description (time axis starts in this data block, e.g. for data stream with trigger control) |
7 | bcContinuedRelStampData | append data with relative timestamp | if option bit 7 is set: uint32: N := number of samples, ... otherwise N := 1 Nx: uint32: relative distance to the previous data sample in ns Y: Data according to channel description. |
8 | bcAbsTimeStampData | Append data with absolute time stamp. Can also be the first data block with relative time stamps. | if option bit 7 is set: uint32: N := number of samples, ... otherwise N := 1 Nx: int64: Absolute timestamp in "ns since Epoch" Y: Data according to channel description |
Restriction of block types with regard to channel information:
ENUM type | Equidistant data | Time-stamped data |
---|---|---|
bcTrustedTimestamp | not allowed | allowed |
bcTimebaseRealign | allowed | allowed |
bcStatusEvent | N/A | N/A |
bcMessage | N/A | N/A |
bcContinuedData | allowed | not allowed |
bcContinuedRelStampData | not allowed | allowed |
bcAbsTimeStampData | N/A | N/A |
By synchronizing the time base to external sources such as GPS, DCF77 or NTP, time jumps in any direction can occur in the data stream time jumps can occur in any direction. The sequence of the measurement data is therefore strictly monotonic in relation to the running time of the device, but not the assignment to the absolute time base. With a time synchronization with correction of the time axis, a bcTimebaseRealign block is written. In the subsequent interpretation of the data, this information can be used to
- "Gaps" by the insertion of certain error values be closed
- "overlaps" are corrected by cutting out data or
- time stamps of previous data are corrected / recalculated.
Several events can occur in one channel. For each event, a bcMessage block is coded with the UTC absolute time value of the event and an application-specific text block.
With certain data reduction methods, the situation can arise that a (time-stamped) data sample remains valid for a long time. remains valid for a long time. Examples are Boolean channels where only changes are changes are recorded or measured values that remain constant over long periods of time remain constant within a tolerance band. To display live data the bcTrustedTimestamp block is incorporated into the data stream at shorter data stream, typically at the end of each transmitted time interval. transmitted time interval. In a graphical representation, a constant curve up to the specified point in time could be drawn without a symbol for a measuring point. The interpretation is optional. When writing to a file, bcTrustedTimestamp is inserted before the file termination for the corresponding channels.
Data with fixed time increment (equidistant)
In the channel definition, for channels with a fixed time increment, the specification of the timeincrement with a value [not equal to zero]{.ul} in nano-seconds (ns) is mandatory.
After the header and meta information, the data stream of the channel first contains a first a bcStartData block. The data records are stored in bcStartData- or bcContinuedData blocks and start either with the given timestamp, e.g. after a trigger event ("Segment") or connect to the data stream that has already been transferred.
The blocks for time-stamped information bcTrustedTimestamp, bcContinuedRelStampData, bcAbsTimeStampData, bcStartRelStampData must not be used for equidistant scanning.
Data with time stamp
In the channel definition for channels with time-stamped data records the timeincrement is not included or is zero.
After the header and meta information, the data stream of the channel contains each data record depending on the block description
- optionally an int64 value as a time stamp for the next relative value
- mandatory on each data set
- an int64 absolute timestamp in ns since epoch OR
- a uint32 value as a relative time interval (max. 4 seconds) to the previous data sample
The blocks for equidistant information bcContinuedData, bcStartData must not be used for equidistant scanning.
Examples of data with a timestamp
- Data samples according to data types and number of samples (t,y,t,y,y,t,y...)
- For each data pair: uint32 as relative time to the last data sample or to the last absolute timestamp. The data value then according to channel definition and datatype
- Example: Data format=double; Bit0=1; Number of values=1; (1xuint64, 1xdouble) 16 bytes to end of block
- Example: Data format=double; Bit0=0; Number of values=1; (1xuint32, 1xdouble) 12 bytes to end of block
- Example: Data format=int32; bit1=0; valueorder=multiplex; Number of values=20; (20x (uint32, int32)) 160 bytes to end of block
- Example: Data format=int32; bit1=1; valueorder=multiplex; Number of values=20; (20x (uint64, int32)) 168 bytes to end of block
- Example: Data format=int32; bit1=0; valueorder=linear; number of values=20; (20xuint64) values=20; (20xuint32, 20xint32) 160 bytes to end of block
- Example: Data format=int32; bit1=1; valueorder=linear; number of values=20; (20xuint32, 20xuint32) values=20; (20xuint64, 20xint32) 240 bytes to end of block
- Example: Data format=gpsdata; bit1=1; number of values=1; (1xunit64,3xdouble) 32 bytes to end of block
- Example: Data format=candata; Bit1=1; Number of values=1; 1xuint64 (8 bytes) + depending on the 1st length byte - 6..13 bytes to end of block
Special functions (index >= 0xFFFF)
Since it is extremely unlikely (or not very clever) to store more than than 65000 channels in an OSF file, the following index values are reserved at the end of the value range are reserved for special control functions:
- 0xFFFF / EndOFS: Regular end of the OSF streaming file, final XML block
The structure of the data block for these entries follows the description for of an XML option block.
- uint16: "channel index" (== 0xFFFF)
- uint32: Length of the subsequent option block (e.g. up to the beginning of the MAGIC-END tag)
- uint8: bcMetaData - control byte (== zero)
- string, XML block, UTF-8 without prologue
This block is optional and does not necessarily have to be included in an Osf file!
Structure of the final frame
<trailer finalized_utc="2019-08-12T12:23:01+2.00" reason="fileStartGrid_min">
<channels count="8">
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="0" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="1" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="2" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="3" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="4" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="5" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="6" last_utc="2019-11-19T23:19:59"/>
<channel segments="1473" last_ns="1384899599997800000" samples="29452" index="7" last_utc="2019-11-19T23:19:59"/>
</channels>
</trailer>
<trailer>
- finalized_utc: Time (system time) at which the file was closed. was closed
- reason: Reason for the end of the file, e.g:
- maxFileLen_kB: Closing the file was triggered when the maximum file size (memory) was reached
- maxFileLen_s: Closing the file was triggered when the maximum maximum file length (time)
- fileStartGrid_min: Closing the file was triggered when the next time grid point (e.g. at :00, :15, :30 and :45 of an hour). and :45 of an hour).
- triggerEnd: Closing the file was triggered at the end of a trigger period (one event per file)
- ...: further application-specific information.
- shutDown: OsfWriter object was deleted when the file was open, in this case, the channel information may be incomplete.
<channels>
- count: Number of channels when closing the file
<channel>
- index: channel index
- first_ns: high-resolution ns timestamp of the first data sample, UTC Nano-Seconds since Epoch (1970)
- samples: Number of data samples actually contained
- last_ns: high-resolution ns timestamp of the last data sample, UTC Nano-Seconds since Epoch (1970)
MAGIC Trailer, OSF_STREAM_END
The final trailer is always 40 bytes long
OSF_STREAM_END 321316454==============
This is followed by '=' characters to fill up to the 40 bytes.
The specified number corresponds to the seek position in the file at which the final XML data block with statistical information begins begins, i.e. exactly the 0xFFFF control code would be read next.