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