Decode J1939 Data from MDF-Files
This example shows you how to import and decode J1939 data from MDF-files in MATLAB® for analysis. The MDF-file used in this example was generated from Vector CANoe using the "System Configuration (J1939)" sample. This example also uses the CAN database file, Powertrain_J1939_MDF.dbc
, provided with the Vector sample configuration.
Open the MDF-File
Open access to the MDF-file using the mdf
function.
m = mdf("LoggingMDF_J1939.mf4")
m = MDF with properties: File Details Name: 'LoggingMDF_J1939.mf4' Path: '/tmp/Bdoc22b_2134332_1826941/tpb2a92164/vnt-ex76385747/LoggingMDF_J1939.mf4' Author: '' Department: '' Project: '' Subject: '' Comment: '' Version: '4.10' DataSize: 3743994 InitialTimestamp: 2021-04-21 14:05:13.232000000 Creator Details ProgramIdentifier: 'MDF4Lib' Creator: [1x1 struct] File Contents Attachment: [5x1 struct] ChannelNames: {43x1 cell} ChannelGroup: [1x43 struct] Options Conversion: Numeric
Identify J1939 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". J1939 is a protocol built on top of the CAN protocol. A J1939 parameter group (PG) is a set of parameters belonging to the same topic and sharing the same transmission rate. For example, the Electronic Engine Controller 1 (EEC1) PG contains engine speed, engine torque demand percent, actual engine torque percent, etc. Each parameter group is addressed via a unique number called the parameter group number (PGN). J1939 PGs are transmitted as CAN frames, and the MDF-file reflects that a J1939 PG is logged as a "CAN_DataFrame".
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 a separator character to specify the member channels, for instance, "CAN_DataFrame.ID" or "CAN_DataFrame.DataLength".
Use the channelList
function to filter on channel names exactly matching "CAN_DataFrame". A table with information on matched channels is returned.
channelList(m, "CAN_DataFrame", "ExactMatch", true)
ans=2×18 table
ChannelName ChannelGroupNumber ChannelGroupNumSamples ChannelGroupAcquisitionName ChannelGroupComment ChannelGroupSourceInfo ChannelDisplayName ChannelUnit ChannelComment ChannelDescription ChannelExtendedNamePrefix ChannelType ChannelDataType ChannelNumBits ChannelComponentType ChannelCompositionType ChannelConversionType ChannelSourceInfo
_______________ __________________ ______________________ ___________________________ ___________________ ______________________ __________________ ___________ ______________ __________________ _________________________ ___________ _______________ ______________ ____________________ ______________________ _____________________ _________________
"CAN_DataFrame" 13 26054 CAN2 <undefined> 1x1 struct "" <undefined> bus event data "bus event data" CAN2 FixedLength ByteArray 152 None Structure None 1x1 struct
"CAN_DataFrame" 14 92720 CAN1 <undefined> 1x1 struct "" <undefined> bus event data "bus event data" CAN1 FixedLength ByteArray 152 None Structure None 1x1 struct
The J1939 powertrain data of interest was logged from the CAN 2 network. The channelList
output above shows that the data from CAN 2 network has been stored in channel group 13 of the MDF-file. View the channel group details using the ChannelGroup
property.
m.ChannelGroup(13)
ans = struct with fields:
AcquisitionName: 'CAN2'
Comment: ''
NumSamples: 26054
DataSize: 703458
Sorted: 1
SourceInfo: [1x1 struct]
Channel: [14x1 struct]
Read J1939 CAN Data Frames from the MDF-File
Read all data in channel group 13 into a timetable using the read
function. The timetable is structured to follow the ASAM MDF standard logging format. Each 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. However, because timetable column names must be valid MATLAB variable names, they might not be identical to the channel names. Most unsupported characters are converted to underscores. Because "." is not supported in a MATLAB variable name, "CAN_DataFrame.Dir" is altered to "CAN_DataFrame_Dir" in the table.
canData = read(m, 13)
canData=26054×14 timetable
Time 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 CAN_DataFrame t
____________ ________________________ ___________________ _________________ ________________________ ____________________ ________________ _________________ ___________________________ ______________________ _________________ ________________________ ___________________________________ _________________________________________________________________ ________
0.000568 sec 2 1 1 0 0 2565799910 1 0 0 8 8 {[ 105 52 169 232 0 131 0 16]} {[ 1 8 230 255 238 152 105 52 169 232 0 131 0 16 0 0 0 0 0]} 0.000568
0.27057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.27057
0.29057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.29057
0.30058 sec 2 1 1 0 0 2565866470 1 0 0 8 8 {[ 255 0 255 255 255 255 255 255]} {[ 1 8 230 3 240 152 255 0 255 255 255 255 255 255 0 0 0 0 0]} 0.30058
0.30116 sec 2 1 1 0 0 2566810854 1 0 0 8 8 {[255 255 255 255 255 255 255 255]} {[1 8 230 108 254 152 255 255 255 255 255 255 255 255 0 0 0 0 0]} 0.30116
0.31057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.31057
0.33057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.33057
0.35058 sec 2 1 1 0 0 2565866470 1 0 0 8 8 {[ 255 0 255 255 255 255 255 255]} {[ 1 8 230 3 240 152 255 0 255 255 255 255 255 255 0 0 0 0 0]} 0.35058
0.35115 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.35115
0.35173 sec 2 1 1 0 0 2566810854 1 0 0 8 8 {[255 255 255 255 255 255 255 255]} {[1 8 230 108 254 152 255 255 255 255 255 255 255 255 0 0 0 0 0]} 0.35173
0.3523 sec 2 1 1 0 0 2566844902 1 0 0 8 8 {[ 255 0 0 12 255 255 224 255]} {[ 1 8 230 241 254 152 255 0 0 12 255 255 224 255 0 0 0 0 0]} 0.3523
0.37057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.37057
0.39057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.39057
0.40058 sec 2 1 1 0 0 2565866470 1 0 0 8 8 {[ 255 0 255 255 255 255 255 255]} {[ 1 8 230 3 240 152 255 0 255 255 255 255 255 255 0 0 0 0 0]} 0.40058
0.40116 sec 2 1 1 0 0 2566810854 1 0 0 8 8 {[255 255 255 255 255 255 255 255]} {[1 8 230 108 254 152 255 255 255 255 255 255 255 255 0 0 0 0 0]} 0.40116
0.41057 sec 2 1 1 0 0 2565866726 1 0 0 8 8 {[ 255 255 255 208 7 255 255 255]} {[ 1 8 230 4 240 152 255 255 255 208 7 255 255 255 0 0 0 0 0]} 0.41057
⋮
Decode J1939 Parameter Groups Using the DBC-File
Open the database file using the canDatabase
function.
canDB = canDatabase("Powertrain_J1939_MDF.dbc")
canDB = Database with properties: Name: 'Powertrain_J1939_MDF' Path: '/tmp/Bdoc22b_2134332_1826941/tpb2a92164/vnt-ex76385747/Powertrain_J1939_MDF.dbc' UTF8_File: '/tmp/Bdoc22b_2134332_1826941/tp62154099_0f4f_487d_bb81_1ba80d23737c' Nodes: {12x1 cell} NodeInfo: [12x1 struct] Messages: {93x1 cell} MessageInfo: [93x1 struct] Attributes: {3x1 cell} AttributeInfo: [3x1 struct] UserData: []
The j1939ParameterGroupTimetable
function uses the database to decode the raw CAN Data into PGs, PGNs and signals. The timetable of ASAM standard logging format data is converted into a Vehicle Network Toolbox™ J1939 parameter group timetable.
j1939PGTimetable = j1939ParameterGroupTimetable(canData, canDB)
j1939PGTimetable=26030×8 timetable
Time Name PGN Priority PDUFormatType SourceAddress DestinationAddress Data Signals
____________ ________ _____ ________ _____________________ _____________ __________________ ___________________________________ ____________
0.000568 sec ACL 60928 6 Peer-to-Peer (Type 1) 230 255 {[ 105 52 169 232 0 131 0 16]} {1x1 struct}
0.27057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.29057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.30058 sec EEC2_EMS 61443 6 Broadcast (Type 2) 230 255 {[ 255 0 255 255 255 255 255 255]} {1x1 struct}
0.30116 sec TCO1_TCO 65132 6 Broadcast (Type 2) 230 255 {[255 255 255 255 255 255 255 255]} {1x1 struct}
0.31057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.33057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.35058 sec EEC2_EMS 61443 6 Broadcast (Type 2) 230 255 {[ 255 0 255 255 255 255 255 255]} {1x1 struct}
0.35115 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.35173 sec TCO1_TCO 65132 6 Broadcast (Type 2) 230 255 {[255 255 255 255 255 255 255 255]} {1x1 struct}
0.3523 sec CCVS_EMS 65265 6 Broadcast (Type 2) 230 255 {[ 255 0 0 12 255 255 224 255]} {1x1 struct}
0.37057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.39057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
0.40058 sec EEC2_EMS 61443 6 Broadcast (Type 2) 230 255 {[ 255 0 255 255 255 255 255 255]} {1x1 struct}
0.40116 sec TCO1_TCO 65132 6 Broadcast (Type 2) 230 255 {[255 255 255 255 255 255 255 255]} {1x1 struct}
0.41057 sec EEC1_EMS 61444 6 Broadcast (Type 2) 230 255 {[ 255 255 255 208 7 255 255 255]} {1x1 struct}
⋮
View the signal data stored in the third PG of the timetable, which is one instance of the "EEC1_EMS" PG.
signalData = j1939PGTimetable.Signals{3}
signalData = struct with fields:
EngDemandPercentTorque: 130
EngStarterMode: 15
SrcAddrssOfCtrllngDvcForEngCtrl: 255
EngSpeed: 250
ActualEngPercentTorque: 130
DriversDemandEngPercentTorque: 130
EngTorqueMode: 15
Repackage and Visualize Signal Values of Interest
Use the j1939SignalTimetable
function to repackage signal data from each unique PGN on the bus into a signal timetable. This example creates two individual signal timetables for the two PGs of interest, "EEC1_EMS" and "TCO1_TCO", from the J1939 PG timetable.
signalTimetable1 = j1939SignalTimetable(j1939PGTimetable, "ParameterGroups", "EEC1_EMS")
signalTimetable1=12043×7 timetable
Time EngDemandPercentTorque EngStarterMode SrcAddrssOfCtrllngDvcForEngCtrl EngSpeed ActualEngPercentTorque DriversDemandEngPercentTorque EngTorqueMode
___________ ______________________ ______________ _______________________________ ________ ______________________ _____________________________ _____________
0.27057 sec 130 15 255 250 130 130 15
0.29057 sec 130 15 255 250 130 130 15
0.31057 sec 130 15 255 250 130 130 15
0.33057 sec 130 15 255 250 130 130 15
0.35115 sec 130 15 255 250 130 130 15
0.37057 sec 130 15 255 250 130 130 15
0.39057 sec 130 15 255 250 130 130 15
0.41057 sec 130 15 255 250 130 130 15
0.43057 sec 130 15 255 250 130 130 15
0.45115 sec 130 15 255 250 130 130 15
0.47057 sec 130 15 255 250 130 130 15
0.49057 sec 130 15 255 250 130 130 15
0.51057 sec 130 15 255 250 130 130 15
0.53057 sec 130 15 255 250 130 130 15
0.55115 sec 130 15 255 250 130 130 15
0.57057 sec 130 15 255 250 130 130 15
⋮
signalTimetable2 = j1939SignalTimetable(j1939PGTimetable, "ParameterGroups", "TCO1_TCO")
signalTimetable2=4817×14 timetable
Time TachographVehicleSpeed TachographOutputShaftSpeed DirectionIndicator TachographPerformance HandlingInformation SystemEvent DriverCardDriver2 Driver2TimeRelatedStates Overspeed DriverCardDriver1 Driver1TimeRelatedStates DriveRecognize Driver2WorkingState Driver1WorkingState
___________ ______________________ __________________________ __________________ _____________________ ___________________ ___________ _________________ ________________________ _________ _________________ ________________________ ______________ ___________________ ___________________
0.30116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.35173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.40116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.45173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.50116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.55173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.60116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.65173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.70116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.75173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.80116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.85173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.90116 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
0.95173 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
1.0012 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
1.0517 sec 256 8191.9 3 3 3 3 3 15 3 3 15 3 7 7
⋮
You can alternatively choose to convert the whole J1939 PG timetable into a struct containing multiple J1939 signal timetables for each individual PG, and index into it to get data for a particular PG.
signalTimetables = j1939SignalTimetable(j1939PGTimetable)
signalTimetables = struct with fields:
ACL: [1x14 timetable]
CCVS_EMS: [2408x19 timetable]
DD: [240x5 timetable]
EEC1_EMS: [12043x7 timetable]
EEC2_EMS: [4817x10 timetable]
ET1_EMS: [240x6 timetable]
HOURS_EMS: [240x2 timetable]
LFC_EMS: [480x2 timetable]
SERV: [240x6 timetable]
TCO1_TCO: [4817x14 timetable]
VDHR_EMS: [240x2 timetable]
VI_EMS: [24x1 timetable]
VW_SSC: [240x4 timetable]
signalTimetables.EEC1_EMS
ans=12043×7 timetable
Time EngDemandPercentTorque EngStarterMode SrcAddrssOfCtrllngDvcForEngCtrl EngSpeed ActualEngPercentTorque DriversDemandEngPercentTorque EngTorqueMode
___________ ______________________ ______________ _______________________________ ________ ______________________ _____________________________ _____________
0.27057 sec 130 15 255 250 130 130 15
0.29057 sec 130 15 255 250 130 130 15
0.31057 sec 130 15 255 250 130 130 15
0.33057 sec 130 15 255 250 130 130 15
0.35115 sec 130 15 255 250 130 130 15
0.37057 sec 130 15 255 250 130 130 15
0.39057 sec 130 15 255 250 130 130 15
0.41057 sec 130 15 255 250 130 130 15
0.43057 sec 130 15 255 250 130 130 15
0.45115 sec 130 15 255 250 130 130 15
0.47057 sec 130 15 255 250 130 130 15
0.49057 sec 130 15 255 250 130 130 15
0.51057 sec 130 15 255 250 130 130 15
0.53057 sec 130 15 255 250 130 130 15
0.55115 sec 130 15 255 250 130 130 15
0.57057 sec 130 15 255 250 130 130 15
⋮
To visualize a signal of interest, variables from the signal timetables can be plotted over time for further analysis. For this example, look at the "EngineSpeed" signal from the "EEC1_EMS" PG.
plot(signalTimetable1.Time, signalTimetable1.EngSpeed, "r") title("{\itEngineSpeed} signal from {\itEEC1\_EMS} PG", "FontWeight", "bold") xlabel("Timestamp") ylabel("Engine Speed")
Close the Files
Close access to the MDF-file and the DBC-file by clearing their variables from the workspace.
clear m clear canDB