Main Content

Write Channel Group Data from an Existing MDF-File to a New MDF-File

This example shows how to process data logged in a channel group of an MDF-file and write the updated data to a new file. The MDF-file used in this example VehicleData.mf4 contains data logged from two simulations in two channel groups, but the example only works with data in channel group 2.

You will first read data from channel group 2 into a timetable, and then perform post-processing on the data copy. Lastly, you will create a new MDF-file that has the same file metadata as VehicleData.mf4, and write the updated data into the new MDF-file as a new channel group.

Open MDF-File

Open access to the MDF-file using the mdf function.

mdfObj = mdf("VehicleData.mf4")
mdfObj = 
  MDF with properties:

   File Details
                 Name: 'VehicleData.mf4'
                 Path: 'C:\Users\michellw\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\michellw.MDF_BaT_Publish\vnt-ex28629694\VehicleData.mf4'
               Author: ''
           Department: ''
              Project: ''
              Subject: ''
              Comment: 'Example file demonstrating workflows of writing to MDF files.'
              Version: '4.10'
             DataSize: 2252350
     InitialTimestamp: 2022-01-14 19:53:57.000000000

   Creator Details
    ProgramIdentifier: 'MATLAB'
              Creator: [1×1 struct]

   File Contents
           Attachment: [1×1 struct]
         ChannelNames: {2×1 cell}
         ChannelGroup: [1×2 struct]

   Options
           Conversion: Numeric

Inspect details about the two channel groups.

mdfObj.ChannelGroup
ans=1×2 struct array with fields:
    AcquisitionName
    Comment
    NumSamples
    DataSize
    Sorted
    SourceInfo
    Channel

Read Channel Group Data with Metadata Included

Read data from channel group 2 using the read function with optional argument IncludeMetadata set to true. The output timetable chanGrp2TT is a copy of data for all channels in channel group 2.

chanGrp2TT = read(mdfObj, 2, IncludeMetadata=true)
chanGrp2TT=92033×3 timetable
         Time         AirFlow    FuelRate       time   
    ______________    _______    ________    __________

    0 sec             17.294       1.209              0
    0.00056199 sec    17.263       1.209     0.00056199
    0.0033719 sec     17.112       1.209      0.0033719
    0.01 sec          16.776      1.1729           0.01
    0.02 sec          16.316      1.1409           0.02
    0.03 sec          15.907      1.1124           0.03
    0.04 sec          15.546      1.0873           0.04
    0.05 sec          15.228      1.0652           0.05
    0.055328 sec      15.075      1.0652       0.055328
    0.055328 sec      15.075      1.0652       0.055328
    0.055328 sec      15.075      1.0652       0.055328
    0.06 sec          14.949      1.0458           0.06
    0.064672 sec      14.832      1.0458       0.064672
    0.07 sec          14.707      1.0289           0.07
    0.08 sec          14.497      1.0143           0.08
    0.09 sec          14.317      1.0019           0.09
      ⋮

Metadata for channel group 2 and its channels are available in the timetable as custom properties. Metadata for a channel group are stored as the timetable's custom properties for the entire table, and the property names are prefixed with "ChannelGroup". Metadata for individual channels are stored as the timetable's custom properties for the variables, and the property names are prefixed with "Channel".

View metadata for channel group 2 and all channels in this group.

chanGrp2TT.Properties.CustomProperties
ans = 
CustomProperties with properties:

    ChannelGroupAcquisitionName: ""
            ChannelGroupComment: "Simulation of engine gas dynamics."
         ChannelGroupSourceInfo: [1×1 struct]
             ChannelDisplayName: [""    ""    ""]
                 ChannelComment: [""    ""    ""]
                    ChannelUnit: ["g/s"    "g/s"    "s"]
                    ChannelType: [FixedLength    FixedLength    Master]
                ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian]
                 ChannelNumBits: [64 64 64]
           ChannelComponentType: [None    None    None]
         ChannelCompositionType: [None    None    None]
              ChannelSourceInfo: [1×3 struct]
              ChannelReadOption: [Numeric    Numeric    Numeric]

Note that when calling the read function, IncludeMetadata is set to false by default. Therefore, IncludeMetadata must be set to true if your ultimate goal for reading channel group data is to write to a new channel group with channel group and channel metadata carried over.

Reduce Number of Samples in the Data Copy

Create a stacked plot using stackedplot to visualize data in channels AirFlow and FuelRate.

stackedplot(chanGrp2TT, ["AirFlow", "FuelRate"])

It can be observed from the stacked plot that the value of FuelRate stays at 0 from around 182.17 sec until the end of the recording at 900 sec, indicating a possible fault during measurement. For this particular application, the recorded values of AirFlow are useless where the values of FuelRate are invalid. Therefore, you decide to discard the invalid data samples where FuelRate is zero.

Find row index of the last "good" value of FuelRate where it is non-zero.

lastNonZeroIdx = find(chanGrp2TT.FuelRate ~= 0, 1, "last")
lastNonZeroIdx = 18634

Reduce the number of data samples in channel group 2 to only keep the valid values from row index 1 until lastNonZeroIdx.

chanGrp2TT = chanGrp2TT(1:lastNonZeroIdx, :)
chanGrp2TT=18634×3 timetable
         Time         AirFlow    FuelRate       time   
    ______________    _______    ________    __________

    0 sec             17.294       1.209              0
    0.00056199 sec    17.263       1.209     0.00056199
    0.0033719 sec     17.112       1.209      0.0033719
    0.01 sec          16.776      1.1729           0.01
    0.02 sec          16.316      1.1409           0.02
    0.03 sec          15.907      1.1124           0.03
    0.04 sec          15.546      1.0873           0.04
    0.05 sec          15.228      1.0652           0.05
    0.055328 sec      15.075      1.0652       0.055328
    0.055328 sec      15.075      1.0652       0.055328
    0.055328 sec      15.075      1.0652       0.055328
    0.06 sec          14.949      1.0458           0.06
    0.064672 sec      14.832      1.0458       0.064672
    0.07 sec          14.707      1.0289           0.07
    0.08 sec          14.497      1.0143           0.08
    0.09 sec          14.317      1.0019           0.09
      ⋮

Add Channels in the Data Copy

Divide the air flow by fuel rate to get the air fuel ratio, and assign the calculated values to a new timetable variable named AirFuelRatio.

chanGrp2TT.AirFuelRatio = chanGrp2TT.AirFlow./chanGrp2TT.FuelRate
chanGrp2TT=18634×4 timetable
         Time         AirFlow    FuelRate       time       AirFuelRatio
    ______________    _______    ________    __________    ____________

    0 sec             17.294       1.209              0       14.304   
    0.00056199 sec    17.263       1.209     0.00056199       14.278   
    0.0033719 sec     17.112       1.209      0.0033719       14.154   
    0.01 sec          16.776      1.1729           0.01       14.303   
    0.02 sec          16.316      1.1409           0.02       14.301   
    0.03 sec          15.907      1.1124           0.03         14.3   
    0.04 sec          15.546      1.0873           0.04       14.298   
    0.05 sec          15.228      1.0652           0.05       14.296   
    0.055328 sec      15.075      1.0652       0.055328       14.152   
    0.055328 sec      15.075      1.0652       0.055328       14.152   
    0.055328 sec      15.075      1.0652       0.055328       14.152   
    0.06 sec          14.949      1.0458           0.06       14.294   
    0.064672 sec      14.832      1.0458       0.064672       14.182   
    0.07 sec          14.707      1.0289           0.07       14.293   
    0.08 sec          14.497      1.0143           0.08       14.292   
    0.09 sec          14.317      1.0019           0.09        14.29   
      ⋮

Inspect the updated channel metadata and note the missing values for the newly added channel.

chanGrp2TT.Properties.CustomProperties
ans = 
CustomProperties with properties:

    ChannelGroupAcquisitionName: ""
            ChannelGroupComment: "Simulation of engine gas dynamics."
         ChannelGroupSourceInfo: [1×1 struct]
             ChannelDisplayName: [""    ""    ""    <missing>]
                 ChannelComment: [""    ""    ""    <missing>]
                    ChannelUnit: ["g/s"    "g/s"    "s"    <missing>]
                    ChannelType: [FixedLength    FixedLength    Master    Missing]
                ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    Missing]
                 ChannelNumBits: [64 64 64 NaN]
           ChannelComponentType: [None    None    None    Missing]
         ChannelCompositionType: [None    None    None    Missing]
              ChannelSourceInfo: [1×4 struct]
              ChannelReadOption: [Numeric    Numeric    Numeric    Missing]

Customize Channel Group and Channel Metadata

Update the channel group comment to provide information about the processing done on data.

chanGrpComment = chanGrp2TT.Properties.CustomProperties.ChannelGroupComment
chanGrpComment = 
"Simulation of engine gas dynamics."
chanGrp2TT.Properties.CustomProperties.ChannelGroupComment = chanGrpComment + " Update: Removed invalid samples and added new channel ""AirFuelRatio"".";

Update the channel comment for AirFlow and FuelRate to provide more information about the channels. You can use either the variable index or the variable name to index into the ChannelComment metadata.

chanGrp2TT.Properties.CustomProperties.ChannelComment(1) = "Air flow logged from simulation.";
chanGrp2TT.Properties.CustomProperties.ChannelComment("FuelRate") = "Fuel rate logged from simulation.";

Add a channel comment for AirFuelRatio.

chanGrp2TT.Properties.CustomProperties.ChannelComment(4) = "Air fuel ratio calculated by dividing air flow by fuel rate.";

Inspect the channel metadata and note the updated values in ChannelGroupComment and ChannelComment. It is okay to leave the missing values if you do not need to customize that specific metadata. Later, when writing to an MDF-file, the default or derived values will be applied accordingly.

chanGrp2TT.Properties.CustomProperties
ans = 
CustomProperties with properties:

    ChannelGroupAcquisitionName: ""
            ChannelGroupComment: "Simulation of engine gas dynamics. Update: Removed invalid samples and added new channel "AirFuelRatio"."
         ChannelGroupSourceInfo: [1×1 struct]
             ChannelDisplayName: [""    ""    ""    <missing>]
                 ChannelComment: ["Air flow logged from simulation."    "Fuel rate logged from simulation."    ""    "Air fuel ratio calculated by dividing air flow by fuel rate."]
                    ChannelUnit: ["g/s"    "g/s"    "s"    <missing>]
                    ChannelType: [FixedLength    FixedLength    Master    Missing]
                ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    Missing]
                 ChannelNumBits: [64 64 64 NaN]
           ChannelComponentType: [None    None    None    Missing]
         ChannelCompositionType: [None    None    None    Missing]
              ChannelSourceInfo: [1×4 struct]
              ChannelReadOption: [Numeric    Numeric    Numeric    Missing]

Create a New MDF-File

In this step, you create a new MDF-file named VehicleDataNew.mf4 that has the same file metadata as the original file VehicleData.mf4.

Obtain a structure that contains file metadata for the original MDF-file VehicleData.mf4 using function mdfInfo.

info = mdfInfo("VehicleData.mf4")
info = struct with fields:
                 Name: 'VehicleData.mf4'
                 Path: 'C:\Users\michellw\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\michellw.MDF_BaT_Publish\vnt-ex28629694\VehicleData.mf4'
               Author: ''
           Department: ''
              Project: ''
              Subject: ''
              Comment: 'Example file demonstrating workflows of writing to MDF files.'
              Version: '4.10'
    ProgramIdentifier: 'MATLAB'
     InitialTimestamp: 2022-01-14 19:53:57.000000000
              Creator: [1×1 struct]
           Attachment: [1×1 struct]

info.Creator
ans = struct with fields:
     VendorName: 'The MathWorks, Inc.'
       ToolName: 'MATLAB'
    ToolVersion: '9.12.0.1846952 (R2022a) Prerelease Update 1'
       UserName: ''
        Comment: ''

Call function mdfCreate with optional argument FileInfo set to the structure just obtained. This creates a new skeleton MDF-file VehicleDataNew.mf4 on disk with the same file metadata as VehicleData.mf4.

mdfCreate("VehicleDataNew.mf4", FileInfo=info)
ans = 
"C:\Users\michellw\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\michellw.MDF_BaT_Publish\vnt-ex28629694\VehicleDataNew.mf4"

FileInfo is an optional name-value pair. If unspecified, mdfCreate creates a new skeleton MDF-file using default file metadata.

Note that mdfCreate only sets the applicable metadata to the newly created file. For example, fields like Name and Path are specific to each file, so they are ignored by the function.

Examine the File Metadata

To confirm the mdfCreate call created the MDF-file with file metadata correctly applied, call mdfInfo again to examine the file metadata for VehicleDataNew.mf4.

infoNew = mdfInfo("VehicleDataNew.mf4")
infoNew = struct with fields:
                 Name: 'VehicleDataNew.mf4'
                 Path: 'C:\Users\michellw\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\michellw.MDF_BaT_Publish\vnt-ex28629694\VehicleDataNew.mf4'
               Author: ''
           Department: ''
              Project: ''
              Subject: ''
              Comment: 'Example file demonstrating workflows of writing to MDF files.'
              Version: '4.10'
    ProgramIdentifier: 'MATLAB'
     InitialTimestamp: 2022-01-14 19:53:57.000000000
              Creator: [1×1 struct]
           Attachment: [0×1 struct]

infoNew.Creator
ans = struct with fields:
     VendorName: 'The MathWorks, Inc.'
       ToolName: 'MATLAB'
    ToolVersion: '9.12.0.1846952 (R2022a) Prerelease Update 1'
       UserName: 'michellw'
        Comment: ''

Write Updated Data to a New Channel Group in the New MDF-File

Call function mdfWrite with property GroupNumber unspecified. When used as such, the mdfWrite function appends the input timetable to the end of the target file as a new channel group. Because VehicleDataNew.mf4 at this point is a skeleton file with no channel group, this operation writes chanGrp2TT into channel group 1.

mdfWrite("VehicleDataNew.mf4", chanGrp2TT)
Warning: Timetable has variable "time" with ChannelType "Master" in addition to the row times. Only one master channel synchronizing by time is allowed per channel group. Row times used for write operation and data in this variable disregarded.

You see a warning about the time variable because the time data has been read into the timetable both as the row times and as a variable time. When a variable that contains time data is present in addition to the row times, the variable is disregarded.

Examine the Data

To confirm the mdfWrite call updated the MDF-file as expected, open access to VehicleDataNew.mf4 to read data with metadata included for verification.

mdfObjNew = mdf("VehicleDataNew.mf4");

Read data from channel group 1 from the new MDF-file with metadata included. Note the reduced number of samples and the new channel AirFuelRatio.

chanGrp1TTNew = read(mdfObjNew, 1, IncludeMetadata=true)
chanGrp1TTNew=18634×4 timetable
         Time         AirFlow    FuelRate    AirFuelRatio       time   
    ______________    _______    ________    ____________    __________

    0 sec             17.294       1.209        14.304                0
    0.00056199 sec    17.263       1.209        14.278       0.00056199
    0.0033719 sec     17.112       1.209        14.154        0.0033719
    0.01 sec          16.776      1.1729        14.303             0.01
    0.02 sec          16.316      1.1409        14.301             0.02
    0.03 sec          15.907      1.1124          14.3             0.03
    0.04 sec          15.546      1.0873        14.298             0.04
    0.05 sec          15.228      1.0652        14.296             0.05
    0.055328 sec      15.075      1.0652        14.152         0.055328
    0.055328 sec      15.075      1.0652        14.152         0.055328
    0.055328 sec      15.075      1.0652        14.152         0.055328
    0.06 sec          14.949      1.0458        14.294             0.06
    0.064672 sec      14.832      1.0458        14.182         0.064672
    0.07 sec          14.707      1.0289        14.293             0.07
    0.08 sec          14.497      1.0143        14.292             0.08
    0.09 sec          14.317      1.0019         14.29             0.09
      ⋮

Examine the Channel Group and Channel Metadata

Examine the metadata for channel group 1 and channels in this group from the new MDF-file by accessing the timetable custom properties.

chanGrp1TTNew.Properties.CustomProperties
ans = 
CustomProperties with properties:

    ChannelGroupAcquisitionName: ""
            ChannelGroupComment: "Simulation of engine gas dynamics. Update: Removed invalid samples and added new channel "AirFuelRatio"."
         ChannelGroupSourceInfo: [1×1 struct]
             ChannelDisplayName: [""    ""    ""    ""]
                 ChannelComment: ["Air flow logged from simulation."    "Fuel rate logged from simulation."    "Air fuel ratio calculated by dividing air flow by fuel rate."    ""]
                    ChannelUnit: ["g/s"    "g/s"    ""    "s"]
                    ChannelType: [FixedLength    FixedLength    FixedLength    Master]
                ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian]
                 ChannelNumBits: [64 64 64 64]
           ChannelComponentType: [None    None    None    None]
         ChannelCompositionType: [None    None    None    None]
              ChannelSourceInfo: [1×4 struct]
              ChannelReadOption: [Numeric    Numeric    Numeric    Numeric]

Close MDF-Files

Close access to the MDF-files by clearing their variables from the workspace.

clear mdfObj mdfObjNew