Main Content

Read Data From ADTF DAT Files

This example shows how to read data from ADTF DAT files using adtfFileReader and adtfStreamReader objects.

To read the data, you first create an adtfFileReader object that will act as a file handler object for the DAT file. This object gives useful information about the streams present in the DAT file and its contents. Following are the three main ways you can create an adtfFileReader object:

Next, you select the data to be read from the DAT file, which creates an adtfStreamReader object. You can then use this object to read the data. Following are some of the common ways you can read the data:

The data you read from the DAT file will be present in the MATLAB workspace, in raw form.

Create adtfFileReader Object

Using DAT file only

Create an adtfFileReader object by specifying ADTF DAT file name as the only argument to create the file reader object, to read data from streams like images and videos.

datFileName = fullfile("C:","data","sample_can_video.dat");
fileReader  = adtfFileReader(datFileName) %#ok
fileReader = 
              DataFileName: "C:\data\sample_can_video.dat"
       DescriptionFileName: ""
           PluginDirectory: ""
               StreamCount: 2
                StreamInfo: 

    StreamIndex    StreamName      StreamType      StartTime     EndTime      ItemCount    SubstreamInfo
    ___________    __________    ______________    _________    __________    _________    _____________

         1         {'rawcan'}    {'UNRESOLVED'}      0 sec      14.805 sec       743       {0×1 struct} 
         2         {'video' }    {'adtf/image'}      0 sec      14.799 sec       149       {0×1 struct} 


From the StreamInfo property, note that

  • Stream 1 is named 'rawcan'. It contains 743 data items spanning from 0 to 14.805 seconds. However, this stream is not supported for reading as its 'StreamType' is 'UNRESOLVED'. To read such streams, we might need to some additional ADTF plugins, as explained here.

  • Stream 2 is named ‘video’. It is an 'adtf/image' stream, which is a common ADTF stream type for video and image streams. It contains 149 image frames, recorded over an interval of 14.799 seconds.

  • Both streams do not contain any substreams, hence the 'SubstreamInfo' field has an empty struct.

Using DAT file and DDL description file

Some DAT files contain structured data (for example, CAN data). To read such data into MATLAB workspace, you need a DDL description file containing details about the structure of the data within the streams. Specify the name of the DDL description file as an additional argument while creating an adtfFileReader object. Note that the DAT file sample_can.adtfdat contains dummy data and is used for demonstration purposes only.

datFileName = fullfile("C:","data","sample_can.adtfdat");
ddlFileName = fullfile("C:","data","sample_can.description");
fileReader  = adtfFileReader(datFileName, ddlFileName) %#ok
fileReader = 
              DataFileName: "C:\data\sample_can.adtfdat"
       DescriptionFileName: "C:\data\sample_can.description"
           PluginDirectory: ""
               StreamCount: 1
                StreamInfo: 

    StreamIndex    StreamName         StreamType          StartTime    EndTime     ItemCount    SubstreamInfo
    ___________    __________    _____________________    _________    ________    _________    _____________

         1         {'rawcan'}    {'adtf/devicetb/can'}      0 sec      0.98 sec       99        {0×1 struct} 


Note that the above output shows a different stream type, 'adtf/devicetb/can', for the CAN stream, unlike the 'UNRESOLVED' stream type in the previous section. This due to the fact that the sample_can.adtfdat is in ADTF 3.0 format file whereas sample_can_video.dat is an ADTF 2.0 format file. For the 2.0 format, additional plugins may be necessary to read the data.

In some cases, DDL can be stored internally into a DAT file itself. Then the DDL description file is not required.

Use ADTF Plugins, Given a DAT File and/or DDL Description File

An ADTF Plugin is a compiled object that provides additional functionality to ADTF Runtime. They are very specific to ADTF framework and you can read more about them here.

In certain cases, ADTF Plugins are necessary to read data from streams. For such cases, specify the path to the folder storing the plugins as an additional argument while creating an adtfFileReader object. Replace the value of pluginFolder variable with the path on your system that contain the plugins.

datFileName  = fullfile("C:","data","sample_can_video.dat"); 
ddlFileName  = fullfile("C:","data","sample_can_video.description");
pluginFolder = fullfile("C:",'pluginFolder');

fileReader  = adtfFileReader(datFileName, ddlFileName, pluginFolder) %#ok
fileReader = 
              DataFileName: "C:\data\sample_can_video.dat"
       DescriptionFileName: "C:\data\sample_can_video.description"
           PluginDirectory: "C:\pluginFolder"
               StreamCount: 2
                StreamInfo: 

    StreamIndex    StreamName         StreamType          StartTime     EndTime      ItemCount    SubstreamInfo
    ___________    __________    _____________________    _________    __________    _________    _____________

         1         {'rawcan'}    {'adtf/devicetb/can'}      0 sec      14.805 sec       743       {0×1 struct} 
         2         {'video' }    {'adtf/image'       }      0 sec      14.799 sec       149       {0×1 struct} 


Note that there are different versions of same plugin for every Operating System. If plugins are required to read streams in the DAT file, and you do not specify their path, the StreamType value for those will be ‘UNRESOLVED’.

For cases where DDL description file is not required, but a plugin is needed, then you can use the following syntax:

datFileName  = fullfile("C:","data","sample_can_video.dat"); 
pluginFolder = fullfile("C:",'pluginFolder');

fileReader  = adtfFileReader(datFileName, pluginFolder) %#ok
fileReader = 
              DataFileName: "C:\data\sample_can_video.dat"
       DescriptionFileName: ""
           PluginDirectory: "C:\pluginFolder"
               StreamCount: 2
                StreamInfo: 

    StreamIndex    StreamName         StreamType          StartTime     EndTime      ItemCount    SubstreamInfo
    ___________    __________    _____________________    _________    __________    _________    _____________

         1         {'rawcan'}    {'adtf/devicetb/can'}      0 sec      14.805 sec       743       {0×1 struct} 
         2         {'video' }    {'adtf/image'       }      0 sec      14.799 sec       149       {0×1 struct} 


Select and Read Data

Read Data from Single Stream

Create the adtfFileReader object. Note that the DAT file sample_struct.dat contains dummy data and is used for demonstration purposes only.

datFileName = fullfile("C:","data","sample_struct.dat");
ddlFileName = fullfile("C:","data","sample_struct.description");
fileReader  = adtfFileReader(datFileName, ddlFileName)
fileReader = 
              DataFileName: "C:\data\sample_struct.dat"
       DescriptionFileName: "C:\data\sample_struct.description"
           PluginDirectory: ""
               StreamCount: 2
                StreamInfo: 

    StreamIndex       StreamName          StreamType       StartTime    EndTime     ItemCount    SubstreamInfo
    ___________    ________________    ________________    _________    ________    _________    _____________

         1         {'FirstStream' }    {'adtf2/legacy'}    0.09 sec     1.07 sec       99        {0×1 struct} 
         2         {'SecondStream'}    {'adtf2/legacy'}    0.09 sec     0.98 sec       90        {0×1 struct} 


Select the stream to be read by specifying their stream index.

streamReader = select(fileReader, 1);

Read the first item in the selected stream.

dataItem = readNext(streamReader)
dataItem = struct with fields:
    StreamIndex: 1
           Data: [1×1 struct]

In the structure dataItem, 'StreamIndex' field shows the selected stream index and the 'Data' field.

disp(dataItem.Data);
     ChunkTimestamp: 90000
    SampleTimestamp: 90000
            IsValid: 1
               Item: [1×1 struct]
           ItemName: 'tFirstStream'

Structure item.Data, contains the actual data 'Item', and 'ItemName' representing the struct name given to the data item when the DAT file was created. 'ChunkTimestamp' (in microseconds) is the time at which this data was written into the DAT file and 'SampleTimestamp' (in microseconds) is the time at which this data was recorded or computed (let us say from a sensor). 'IsValid' when set to logical(1) means that the data inside 'Item' is valid, otherwise at logical(0) it will contain a string of error message explaining why a valid data is not extracted.

% Display time at which data was written
fprintf("ChunkTimestamp = %d\n",dataItem.Data.ChunkTimestamp);
ChunkTimestamp = 90000
% Display time at which data was created
fprintf("SampleTimestamp = %d\n",dataItem.Data.SampleTimestamp);
SampleTimestamp = 90000
% Display data
disp(dataItem.Data.Item)
    signal1: [1×1 struct]
    signal2: [2×1 double]
    Signal1: [1×1 struct]
    Signal2: [2×1 double]

You can also iterate over all the data items in the selected stream.

% Read one item at a time
while hasNext(streamReader)
    dataItem = readNext(streamReader);
    % Process data
end

Alternatively, you can read all data items at once and iterate over it later.

% Read everythin at once
items = read(streamReader);

% Iterate over the data
for i=1:streamReader.DataCount
    timestamp = items.Data(i).ChunkTimestamp;
    data = items.Data(i).Item; 
    % Process data
end

Read Data with TimeRange and IndexRange Filters

Create the adtfFileReader object.

datFileName = fullfile("C:","data","sample_can.adtfdat");
ddlFileName = fullfile("C:","data","sample_can.description");
fileReader  = adtfFileReader(datFileName, ddlFileName)
fileReader = 
              DataFileName: "C:\data\sample_can.adtfdat"
       DescriptionFileName: "C:\data\sample_can.description"
           PluginDirectory: ""
               StreamCount: 1
                StreamInfo: 

    StreamIndex    StreamName         StreamType          StartTime    EndTime     ItemCount    SubstreamInfo
    ___________    __________    _____________________    _________    ________    _________    _____________

         1         {'rawcan'}    {'adtf/devicetb/can'}      0 sec      0.98 sec       99        {0×1 struct} 


Use the name-value argument, IndexRange, in the select function to filter the search to the last 10 data items in the selected stream.

streamIndex  = 1;
startIndex   = fileReader.StreamInfo(streamIndex).ItemCount - 9; % 10th element index from last
endIndex     = fileReader.StreamInfo(streamIndex).ItemCount; % last index
streamReader = select(fileReader, streamIndex, IndexRange=[startIndex endIndex]); %#ok

Use the name-value argument, TimeRange, to filter the search to all the data items recorded between 1 to 2 seconds, across the selected streams.

startTime    = seconds(0.1);
endTime      = seconds(0.2);
streamReader = select(fileReader, TimeRange=[startTime endTime]); %#ok
INFO : All streams are selected.

Next, you can iterate through the items using the readNext and hasNext functions, or read all items at once using readNext function. See Read Data from Single Stream section for more information.

Reading multiple streams

Create the adtfFileReader object.

datFileName = fullfile("C:","data","sample_struct.dat");
ddlFileName = fullfile("C:","data","sample_struct.description");
fileReader  = adtfFileReader(datFileName, ddlFileName)
fileReader = 
              DataFileName: "C:\data\sample_struct.dat"
       DescriptionFileName: "C:\data\sample_struct.description"
           PluginDirectory: ""
               StreamCount: 2
                StreamInfo: 

    StreamIndex       StreamName          StreamType       StartTime    EndTime     ItemCount    SubstreamInfo
    ___________    ________________    ________________    _________    ________    _________    _____________

         1         {'FirstStream' }    {'adtf2/legacy'}    0.09 sec     1.07 sec       99        {0×1 struct} 
         2         {'SecondStream'}    {'adtf2/legacy'}    0.09 sec     0.98 sec       90        {0×1 struct} 


You can select streams by specifying their stream indices. To select all streams by default, then do not specify any stream indices.

While reading data from multiple streams simultaneously, it is possible that there are unequal number of data items across different streams. To illustrate, perform the following selection.

firstStreamIndex  = 1; 
secondStreamIndex = 2;
startTime = seconds(0.98);
endTime   = seconds(2.0);

streamReader = select(fileReader, [firstStreamIndex, secondStreamIndex], TimeRange=[startTime endTime]);

fprintf("Number of elements in stream 1 = %d\n",streamReader.DataCount(firstStreamIndex));
Number of elements in stream 1 = 10
fprintf("Number of elements in stream 2 = %d\n",streamReader.DataCount(secondStreamIndex));
Number of elements in stream 2 = 1

Note that first stream has 10 items and second stream has only 1 item. If you read all data items at once using the read function, then stream 1 will return an array of 10 structures, and stream 2 will return a single structure.

allData = read(streamReader)
allData=2×1 struct array with fields:
    StreamIndex
    Data

When you read data one-by-one, during the first call to readNext, you get one structure for each stream as expected.

data1 = readNext(streamReader)
data1=2×1 struct array with fields:
    StreamIndex
    Data

In the next call to readNext, we only get items for stream 1.

data2 = readNext(streamReader)
data2 = struct with fields:
    StreamIndex: 1
           Data: [1×1 struct]

Note that although one of the streams has reached end of selection, readNext still returns the data items from the remaining streams. Similarly, the hasNext function will return true even if one of the streams in the selection has data available to read.

hasNext(streamReader)
ans = logical
   1