Main Content

Decode and Write CAN Data in MDF Files

This example shows you how to import and decode CAN data from an MDF file in MATLAB® for analysis, and write the decoded data back to the same file.

The MDF file used in this example was generated from Vector CANoe™ using the "CAN - General System Configuration (CAN)" sample. This example also uses the CAN database file, PowerTrain.dbc, provided with the Vector sample configuration.

View Details of the MDF File

View metadata of an MDF file using the mdfInfo function.

mdfInfo("Logging_MDF.mf4")
ans = 
  MDFInfo with properties:

   File Details
                  Name: "Logging_MDF.mf4"
                  Path: "/tmp/Bdoc24b_2679053_3932096/tp5211b350/vnt-ex42187575/Logging_MDF.mf4"
                Author: ""
            Department: ""
               Project: ""
               Subject: ""
               Comment: ""
               Version: "4.10"
      InitialTimestamp: 2020-06-25 20:41:13.133000000

   Creator Details
     ProgramIdentifier: "MDF4Lib"
     CreatorVendorName: "Vector Informatik GmbH"
       CreatorToolName: "CANoe"
    CreatorToolVersion: "13.0.118"
       CreatorUserName: "michellw"
        CreatorComment: "Created using MdfLog version 1.5.4.0 and Mdf4Lib version 1.7.0.5  X64 (2019-10-22)"

   File Contents
            Attachment: [5x7 table]
     ChannelGroupCount: 62
                 Event: [0x8 eventtable]

Identify CAN Data Frames

According to the ASAM MDF associated standard for bus logging, the event types defined for a CAN bus system can be "CAN_DataFrame", "CAN_RemoteFrame", "CAN_ErrorFrame" or "CAN_OverloadFrame". This example focuses on extracting the CAN data frames, so the bus logging standard will be discussed using "CAN_DataFrame" event type as example. Additionally, note that a standard CAN data frame has up to 8 bytes for its payload and is used to transfer signal values.

The standard specifies that the channel names of the event structure should be prefixed by the event type name, for instance, "CAN_DataFrame". Typically a dot is used as separator character to specify the member channels, for instance, "CAN_DataFrame.ID" or "CAN_DataFrame.DataLength".

Use the mdfChannelInfo function to locate channel names matching "CAN_DataFrame.*". A table with information on matched channels is returned.

mdfChannelInfo("Logging_MDF.mf4", Channel="CAN_DataFrame.*")
ans=24×13 table
                Name                 GroupNumber    GroupNumSamples    GroupAcquisitionName    GroupComment    GroupSourceName    GroupSourcePath      DisplayName         Unit                                 Comment                              ExtendedNamePrefix    SourceName     SourcePath
    _____________________________    ___________    _______________    ____________________    ____________    _______________    _______________    _______________    ___________    __________________________________________________________    __________________    ___________    __________

    "CAN_DataFrame.BitCount"             17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "BitCount"         <undefined>    Frame length in bits.                                                CAN1           <undefined>       CAN1   
    "CAN_DataFrame.BitCount"             29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "BitCount"         <undefined>    Frame length in bits.                                                CAN2           <undefined>       CAN2   
    "CAN_DataFrame.BusChannel"           17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "BusChannel"       <undefined>    Logical bus channel number the frame was sent or received.           CAN1           <undefined>       CAN1   
    "CAN_DataFrame.BusChannel"           29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "BusChannel"       <undefined>    Logical bus channel number the frame was sent or received.           CAN2           <undefined>       CAN2   
    "CAN_DataFrame.DLC"                  17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "DLC"              <undefined>    Data length code.                                                    CAN1           <undefined>       CAN1   
    "CAN_DataFrame.DLC"                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DLC"              <undefined>    Data length code.                                                    CAN2           <undefined>       CAN2   
    "CAN_DataFrame.DataBytes"            17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "DataBytes"        <undefined>    Payload data bytes containing the signal values.                     CAN1           <undefined>       CAN1   
    "CAN_DataFrame.DataBytes"            29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DataBytes"        <undefined>    Payload data bytes containing the signal values.                     CAN2           <undefined>       CAN2   
    "CAN_DataFrame.DataLength"           17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "DataLength"       <undefined>    Length of stored payload in bytes.                                   CAN1           <undefined>       CAN1   
    "CAN_DataFrame.DataLength"           29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DataLength"       <undefined>    Length of stored payload in bytes.                                   CAN2           <undefined>       CAN2   
    "CAN_DataFrame.Dir"                  17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "Dir"              <undefined>    Bit signal indicating the direction (Rx, Tx).                        CAN1           <undefined>       CAN1   
    "CAN_DataFrame.Dir"                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "Dir"              <undefined>    Bit signal indicating the direction (Rx, Tx).                        CAN2           <undefined>       CAN2   
    "CAN_DataFrame.Flags"                17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "Flags"            <undefined>    Combination of bit flags for the message.                            CAN1           <undefined>       CAN1   
    "CAN_DataFrame.Flags"                29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "Flags"            <undefined>    Combination of bit flags for the message.                            CAN2           <undefined>       CAN2   
    "CAN_DataFrame.FrameDuration"        17              8889                  CAN1            <undefined>       <undefined>       CAN_DataFrame     "FrameDuration"    ns             Duration for transmission of the frame in nanoseconds.               CAN1           <undefined>       CAN1   
    "CAN_DataFrame.FrameDuration"        29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "FrameDuration"    ns             Duration for transmission of the frame in nanoseconds.               CAN2           <undefined>       CAN2   
      ⋮

The powertrain data of interest was logged from the CAN 2 network. The output above shows that the data from CAN 2 network has been stored in channel group 29 of the MDF file. View details about channel group 29 using the mdfChannelGroupInfo function by specifying the GroupNumber option.

mdfChannelGroupInfo("Logging_MDF.mf4", GroupNumber=29)
ans=1×13 table
    GroupNumber    AcquisitionName      Comment      NumSamples    DataSize    Sorted    SourceName      SourcePath      SourceComment    SourceType    SourceBusType    SourceBusChannelNumber    SourceSimulated
    ___________    _______________    ___________    __________    ________    ______    ___________    _____________    _____________    __________    _____________    ______________________    _______________

        29              CAN2          <undefined>       7648        206496     true      <undefined>    CAN_DataFrame     <undefined>        Bus             CAN                   2                    false     

View details about all the channels within channel group 29 using mdfChannelInfo.

mdfChannelInfo("Logging_MDF.mf4", GroupNumber=29)
ans=13×13 table
                Name                 GroupNumber    GroupNumSamples    GroupAcquisitionName    GroupComment    GroupSourceName    GroupSourcePath      DisplayName         Unit                                 Comment                              ExtendedNamePrefix    SourceName     SourcePath 
    _____________________________    ___________    _______________    ____________________    ____________    _______________    _______________    _______________    ___________    __________________________________________________________    __________________    ___________    ___________

    "CAN_DataFrame.BitCount"             29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "BitCount"         <undefined>    Frame length in bits.                                                CAN2           <undefined>    CAN2       
    "CAN_DataFrame.BusChannel"           29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "BusChannel"       <undefined>    Logical bus channel number the frame was sent or received.           CAN2           <undefined>    CAN2       
    "CAN_DataFrame.DLC"                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DLC"              <undefined>    Data length code.                                                    CAN2           <undefined>    CAN2       
    "CAN_DataFrame.DataBytes"            29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DataBytes"        <undefined>    Payload data bytes containing the signal values.                     CAN2           <undefined>    CAN2       
    "CAN_DataFrame.DataLength"           29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "DataLength"       <undefined>    Length of stored payload in bytes.                                   CAN2           <undefined>    CAN2       
    "CAN_DataFrame.Dir"                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "Dir"              <undefined>    Bit signal indicating the direction (Rx, Tx).                        CAN2           <undefined>    CAN2       
    "CAN_DataFrame.Flags"                29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "Flags"            <undefined>    Combination of bit flags for the message.                            CAN2           <undefined>    CAN2       
    "CAN_DataFrame.FrameDuration"        29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "FrameDuration"    ns             Duration for transmission of the frame in nanoseconds.               CAN2           <undefined>    CAN2       
    "CAN_DataFrame.ID"                   29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "ID"               <undefined>    ID of the CAN message.                                               CAN2           <undefined>    CAN2       
    "CAN_DataFrame.IDE"                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "IDE"              <undefined>    Identifier Extension bit.                                            CAN2           <undefined>    CAN2       
    "CAN_DataFrame.SingleWire"           29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "SingleWire"       <undefined>    Bit flag indicating a single wire operation.                         CAN2           <undefined>    CAN2       
    "CAN_DataFrame.WakeUp"               29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     "WakeUp"           <undefined>    Bit flag indicating a wake-up message (high voltage).                CAN2           <undefined>    CAN2       
    "t"                                  29              7648                  CAN2            <undefined>       <undefined>       CAN_DataFrame     ""                 s              <undefined>                                                          CAN2           <undefined>    <undefined>

Read CAN Data Frames from the MDF File

Read all data in channel group 29 into a timetable using the mdfRead function. The timetable is structured to follow the ASAM MDF standard logging format. Every row represents one raw CAN frame from the bus, while each column represents a channel within the specified channel group. The channels, such as "CAN_DataFrame.Dir", are named to follow the bus logging standard.

data = mdfRead("Logging_MDF.mf4", GroupNumber=29)
data = 1x1 cell array
    {7648x12 timetable}

canData = data{1}
canData=7648×12 timetable
            t         CAN_DataFrame.BusChannel    CAN_DataFrame.Flags    CAN_DataFrame.Dir    CAN_DataFrame.SingleWire    CAN_DataFrame.WakeUp    CAN_DataFrame.ID    CAN_DataFrame.IDE    CAN_DataFrame.FrameDuration    CAN_DataFrame.BitCount    CAN_DataFrame.DLC    CAN_DataFrame.DataLength       CAN_DataFrame.DataBytes    
        __________    ________________________    ___________________    _________________    ________________________    ____________________    ________________    _________________    ___________________________    ______________________    _________________    ________________________    ______________________________

        2.2601 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.2801 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.3002 sec               2                         1                   "Tx"                      0                         0                     100                  0                      232000                        119                      8                       8                {[      238 2 25 1 0 0 238 2]}
        2.3005 sec               2                         1                   "Tx"                      0                         0                     102                  0                      240000                        123                      8                       8                {[       0 128 59 68 0 0 0 0]}
        2.3006 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.3008 sec               2                         1                   "Tx"                      0                         0                     201                  0                      196000                        101                      6                       6                {[            0 0 0 0 172 38]}
        2.3009 sec               2                         1                   "Tx"                      0                         0                    1020                  0                      110000                         58                      1                       1                {[                         1]}
        2.3201 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.3401 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.3502 sec               2                         1                   "Tx"                      0                         0                     100                  0                      234000                        120                      8                       8                {[      4 0 25 2 119 1 238 2]}
        2.3505 sec               2                         1                   "Tx"                      0                         0                     102                  0                      228000                        117                      8                       8                {[53 127 119 64 0 128 187 67]}
        2.3507 sec               2                         1                   "Tx"                      0                         0                     201                  0                      198000                        102                      6                       6                {[             0 0 0 0 35 40]}
        2.3508 sec               2                         1                   "Tx"                      0                         0                    1020                  0                      110000                         58                      1                       1                {[                         1]}
        2.3601 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.3801 sec               2                         1                   "Tx"                      0                         0                     103                  0                      128000                         67                      2                       2                {[                       1 0]}
        2.4002 sec               2                         1                   "Tx"                      0                         0                     100                  0                      234000                        120                      8                       8                {[     10 0 25 3 119 1 238 2]}
      ⋮

Decode CAN Messages Using the DBC File

Open the DBC file using the canDatabase function.

canDB = canDatabase("PowerTrain_MDF.dbc")
canDB = 
  Database with properties:

             Name: 'PowerTrain_MDF'
             Path: '/tmp/Bdoc24b_2679053_3932096/tp5211b350/vnt-ex42187575/PowerTrain_MDF.dbc'
        UTF8_File: '/tmp/Bdoc24b_2679053_3932096/tp5211b350/vnt-ex42187575/PowerTrain_MDF.dbc'
            Nodes: {2x1 cell}
         NodeInfo: [2x1 struct]
         Messages: {12x1 cell}
      MessageInfo: [12x1 struct]
       Attributes: {11x1 cell}
    AttributeInfo: [11x1 struct]
         UserData: []

The canMessageTimetable function uses the database to decode the message names and signals. The timetable of ASAM standard logging format data is converted into a Vehicle Network Toolbox™ CAN message timetable.

msgTimetable = canMessageTimetable(canData, canDB)
msgTimetable=7648×8 timetable
       Time        ID     Extended           Name                        Data                 Length      Signals       Error    Remote
    __________    ____    ________    __________________    ______________________________    ______    ____________    _____    ______

    2.2601 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.2801 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.3002 sec     100     false      {'EngineData'    }    {[      238 2 25 1 0 0 238 2]}      8       {1x1 struct}    false    false 
    2.3005 sec     102     false      {'EngineDataIEEE'}    {[       0 128 59 68 0 0 0 0]}      8       {1x1 struct}    false    false 
    2.3006 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.3008 sec     201     false      {'ABSdata'       }    {[            0 0 0 0 172 38]}      6       {1x1 struct}    false    false 
    2.3009 sec    1020     false      {'GearBoxInfo'   }    {[                         1]}      1       {1x1 struct}    false    false 
    2.3201 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.3401 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.3502 sec     100     false      {'EngineData'    }    {[      4 0 25 2 119 1 238 2]}      8       {1x1 struct}    false    false 
    2.3505 sec     102     false      {'EngineDataIEEE'}    {[53 127 119 64 0 128 187 67]}      8       {1x1 struct}    false    false 
    2.3507 sec     201     false      {'ABSdata'       }    {[             0 0 0 0 35 40]}      6       {1x1 struct}    false    false 
    2.3508 sec    1020     false      {'GearBoxInfo'   }    {[                         1]}      1       {1x1 struct}    false    false 
    2.3601 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.3801 sec     103     false      {'Ignition_Info' }    {[                       1 0]}      2       {1x1 struct}    false    false 
    2.4002 sec     100     false      {'EngineData'    }    {[     10 0 25 3 119 1 238 2]}      8       {1x1 struct}    false    false 
      ⋮

View the signals stored in the "EngineData" message.

msgTimetable.Signals{3}
ans = struct with fields:
    PetrolLevel: 1
       EngPower: 7.5000
       EngForce: 0
    IdleRunning: 0
        EngTemp: 0
       EngSpeed: 750

Repackage and Visualize Signal Values of Interest

Use the canSignalTimetable function to repackage signal data from each unique message on the bus into a signal timetable. This example creates three individual signal timetables for the three messages of interest, "ABSdata", "EngineData" and "GearBoxInfo", from the CAN message timetable.

signalTimetable1 = canSignalTimetable(msgTimetable, "ABSdata")
signalTimetable1=1147×4 timetable
       Time       AccelerationForce    Diagnostics    GearLock    CarSpeed
    __________    _________________    ___________    ________    ________

    2.3008 sec          -100                0            0            0   
    2.3507 sec           275                0            0            0   
    2.4008 sec           275                0            0            0   
    2.4507 sec           275                0            0            0   
    2.5008 sec           275                0            0            0   
    2.5507 sec           275                0            0            0   
    2.6008 sec           275                0            0            0   
    2.6507 sec           275                0            0            0   
    2.7008 sec           350                0            0            0   
    2.7507 sec           425                0            0          0.5   
    2.8008 sec           425                0            0          0.5   
    2.8507 sec           500                0            0          0.5   
    2.9008 sec           575                0            0          0.5   
    2.9507 sec           575                0            0          0.5   
    3.0008 sec           650                0            0          0.5   
    3.0507 sec           725                0            0          0.5   
      ⋮

signalTimetable2 = canSignalTimetable(msgTimetable, "EngineData")
signalTimetable2=1147×6 timetable
       Time       PetrolLevel    EngPower    EngForce    IdleRunning    EngTemp    EngSpeed
    __________    ___________    ________    ________    ___________    _______    ________

    2.3002 sec         1            7.5          0            0            0         750   
    2.3502 sec         2            7.5        375            0            0           4   
    2.4002 sec         3            7.5        375            0            0          10   
    2.4502 sec         4            7.5        375            0            0          17   
    2.5002 sec         5            7.5        375            0            0          23   
    2.5502 sec         6            7.5        375            0            0          30   
    2.6002 sec         7            7.5        375            0            0          36   
    2.6502 sec         8            7.5        375            0            0          43   
    2.7002 sec         9              9        450            0            0          50   
    2.7502 sec        10           10.5        525            0            0          59   
    2.8002 sec        10           10.5        525            0            0          69   
    2.8502 sec        11             12        600            0            0          80   
    2.9002 sec        11           13.5        675            0            0          92   
    2.9502 sec        12           13.5        675            0            0         106   
    3.0002 sec        13             15        750            0            0         121   
    3.0502 sec        13           16.5        825            0            0         136   
      ⋮

signalTimetable3 = canSignalTimetable(msgTimetable, "GearBoxInfo")
signalTimetable3=1147×3 timetable
       Time       EcoMode    ShiftRequest    Gear
    __________    _______    ____________    ____

    2.3009 sec       0            0           1  
    2.3508 sec       0            0           1  
    2.4009 sec       0            0           1  
    2.4508 sec       0            0           1  
    2.5009 sec       0            0           1  
    2.5508 sec       0            0           1  
    2.6009 sec       0            0           1  
    2.6508 sec       0            0           1  
    2.7009 sec       0            0           1  
    2.7508 sec       0            0           1  
    2.8009 sec       0            0           1  
    2.8508 sec       0            0           1  
    2.9009 sec       0            0           1  
    2.9508 sec       0            0           1  
    3.0009 sec       0            0           1  
    3.0508 sec       0            0           1  
      ⋮

To visualize the signals of interest, columns from the signal timetables can be plotted over time for further analysis.

subplot(3, 1, 1)
plot(signalTimetable1.Time, signalTimetable1.CarSpeed, "r")
title("{\itCarSpeed} Signal from {\itABSdata} Message", "FontWeight", "bold")
xlabel("Timestamp")
ylabel("Car Speed")
subplot(3, 1, 2)
plot(signalTimetable2.Time, signalTimetable2.EngSpeed, "b")
title("{\itEngSpeed} Signal from {\itEngineData} Message", "FontWeight", "bold")
xlabel("Timestamp")
ylabel("Engine Speed")
subplot(3, 1, 3)
plot(signalTimetable3.Time, signalTimetable3.Gear, "y")
title("{\itGear} Signal from {\itGearBoxInfo} Message", "FontWeight", "bold")
xlabel("Timestamp")
ylabel("Gear")

Figure contains 3 axes objects. Axes object 1 with title CarSpeed blank Signal blank from blank ABSdata Message, xlabel Timestamp, ylabel Car Speed contains an object of type line. Axes object 2 with title EngSpeed blank Signal blank from blank EngineData Message, xlabel Timestamp, ylabel Engine Speed contains an object of type line. Axes object 3 with title Gear blank Signal blank from blank GearBoxInfo Message, xlabel Timestamp, ylabel Gear contains an object of type line.

Configure Channel Group and Channel Metadata

The ultimate goal of this example is to write the decoded signal data for the three messages of interest, "ABSdata", "EngineData" and "GearBoxInfo", back into the same MDF file into three new channel groups. In this way the raw and decoded data can be stored in one file.

In addition to data in each timetable, metadata is also an important element that could provide descriptive information about the measurement environment. This section demonstrates how to configure the metadata that goes along with the data, as an optional preparation step before writing to file.

Use function mdfAddChannelGroupMetadata to add to each signal timetable the default or inferred metadata for that particular channel group and its channels.

signalTimetable1 = mdfAddChannelGroupMetadata(signalTimetable1);
signalTimetable2 = mdfAddChannelGroupMetadata(signalTimetable2);
signalTimetable3 = mdfAddChannelGroupMetadata(signalTimetable3);

View the default metadata for each channel group and its channels.

signalTimetable1.Properties.CustomProperties
ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: ""
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""    ""]
                        ChannelComment: [""    ""    ""    ""]
                           ChannelUnit: [""    ""    ""    ""]
                           ChannelType: [FixedLength    FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64 64]
                  ChannelComponentType: [None    None    None    None]
                ChannelCompositionType: [None    None    None    None]
                     ChannelSourceName: [""    ""    ""    ""]
                     ChannelSourcePath: [""    ""    ""    ""]
                  ChannelSourceComment: [""    ""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing    Missing]

signalTimetable2.Properties.CustomProperties
ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: ""
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""    ""    ""    ""]
                        ChannelComment: [""    ""    ""    ""    ""    ""]
                           ChannelUnit: [""    ""    ""    ""    ""    ""]
                           ChannelType: [FixedLength    FixedLength    FixedLength    FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64 64 64 64]
                  ChannelComponentType: [None    None    None    None    None    None]
                ChannelCompositionType: [None    None    None    None    None    None]
                     ChannelSourceName: [""    ""    ""    ""    ""    ""]
                     ChannelSourcePath: [""    ""    ""    ""    ""    ""]
                  ChannelSourceComment: [""    ""    ""    ""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified    Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified    Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0 0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing    Missing    Missing    Missing]

signalTimetable3.Properties.CustomProperties
ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: ""
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""]
                        ChannelComment: [""    ""    ""]
                           ChannelUnit: [""    ""    ""]
                           ChannelType: [FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64]
                  ChannelComponentType: [None    None    None]
                ChannelCompositionType: [None    None    None]
                     ChannelSourceName: [""    ""    ""]
                     ChannelSourcePath: [""    ""    ""]
                  ChannelSourceComment: [""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing]

Create an array to hold the three message names and a cell array to hold the three signal timetables.

msgNames = ["ABSdata", "EngineData", "GearBoxInfo"];
TTs = {signalTimetable1, signalTimetable2, signalTimetable3};

Set property ChannelGroupComment for each timetable in TTs to indicate name of the CAN message from which signal data were decoded.

for ii = 1:numel(TTs)
    TTs{ii}.Properties.CustomProperties.ChannelGroupComment = sprintf("Signal data from CAN message ""%s"".", msgNames(ii));
end

For each CAN message, get the unit of every signal from canDB using the helper function getSignalUnit available at the end of this script. Set property ChannelUnit for each timetable variable to the obtained unit.

for ii = 1:numel(msgNames)
    sigNames = TTs{ii}.Properties.VariableNames;
    for jj = 1:numel(sigNames)
        sigUnit = getSignalUnit(canDB, msgNames(ii), sigNames(jj));
        TTs{ii}.Properties.CustomProperties.ChannelUnit(jj) = sigUnit;
    end
end

View the updated metadata for each channel group and its channels. Note the updated values in property ChannelGroupComment and ChannelUnit.

for ii = 1:numel(TTs)
    TTs{ii}.Properties.CustomProperties
end
ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: "Signal data from CAN message "ABSdata"."
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""    ""]
                        ChannelComment: [""    ""    ""    ""]
                           ChannelUnit: ["N"    ""    ""    "mph"]
                           ChannelType: [FixedLength    FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64 64]
                  ChannelComponentType: [None    None    None    None]
                ChannelCompositionType: [None    None    None    None]
                     ChannelSourceName: [""    ""    ""    ""]
                     ChannelSourcePath: [""    ""    ""    ""]
                  ChannelSourceComment: [""    ""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing    Missing]

ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: "Signal data from CAN message "EngineData"."
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""    ""    ""    ""]
                        ChannelComment: [""    ""    ""    ""    ""    ""]
                           ChannelUnit: ["l"    "kW"    "N"    ""    "degC"    "rpm"]
                           ChannelType: [FixedLength    FixedLength    FixedLength    FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64 64 64 64]
                  ChannelComponentType: [None    None    None    None    None    None]
                ChannelCompositionType: [None    None    None    None    None    None]
                     ChannelSourceName: [""    ""    ""    ""    ""    ""]
                     ChannelSourcePath: [""    ""    ""    ""    ""    ""]
                  ChannelSourceComment: [""    ""    ""    ""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified    Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified    Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0 0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing    Missing    Missing    Missing]

ans = 
CustomProperties with properties:

           ChannelGroupAcquisitionName: ""
                   ChannelGroupComment: "Signal data from CAN message "GearBoxInfo"."
                ChannelGroupSourceName: ""
                ChannelGroupSourcePath: ""
             ChannelGroupSourceComment: ""
                ChannelGroupSourceType: Unspecified
             ChannelGroupSourceBusType: Unspecified
    ChannelGroupSourceBusChannelNumber: 0
                    ChannelDisplayName: [""    ""    ""]
                        ChannelComment: [""    ""    ""]
                           ChannelUnit: [""    ""    ""]
                           ChannelType: [FixedLength    FixedLength    FixedLength]
                       ChannelDataType: [RealLittleEndian    RealLittleEndian    RealLittleEndian]
                        ChannelNumBits: [64 64 64]
                  ChannelComponentType: [None    None    None]
                ChannelCompositionType: [None    None    None]
                     ChannelSourceName: [""    ""    ""]
                     ChannelSourcePath: [""    ""    ""]
                  ChannelSourceComment: [""    ""    ""]
                     ChannelSourceType: [Unspecified    Unspecified    Unspecified]
                  ChannelSourceBusType: [Unspecified    Unspecified    Unspecified]
         ChannelSourceBusChannelNumber: [0 0 0]
                     ChannelReadOption: [Missing    Missing    Missing]

Write Data to MDF File with Customize Channel Group and Channel Metadata

Using the mdfWrite function, write the three timetables in TTs one at a time to Logging_MDF.mf4. Because the MDF file currently has 62 channel groups, TTs{1}, TTs{2} and TTs{3} are written to the end of the file as channel groups 63, 64, and 65.

for ii = 1:numel(TTs)
    mdfWrite("Logging_MDF.mf4", TTs{ii})
end

Add DBC File to MDF File as Attachment

For convenient access to PowerTrain_MDF.dbc in case you need to perform additional data decoding in the future, you can add the DBC file as an embedded attachment to the MDF file using mdfAddAttachment.

mdfAddAttachment("Logging_MDF.mf4", "PowerTrain_MDF.dbc", Embedded=true)

Close DBC File

Close access to the DBC files by clearing its variable from the workspace.

clear canDB

Helper Function

function sigUnit = getSignalUnit(canDB, msgName, sigName)
% Look up the unit of a signal in a message from a CAN database. Return the unit as a string.
    msgIdx = find(strcmp({canDB.MessageInfo.Name}, msgName));
    sigIdx = find(strcmp({canDB.MessageInfo(msgIdx).SignalInfo.Name}, sigName));
    sigUnit = string(canDB.MessageInfo(msgIdx).SignalInfo(sigIdx).Units);
end