@@ -231,7 +231,7 @@ class MultiFrameDecoder:
231231 FIRST_FRAME: frame type reflecting the first frame in a multi frame response
232232 CONSEQ_FRAME: frame type reflecting a consequtive frame in a multi frame response
233233 ff_payload_start: the combined payload will start at this byte in the FIRST_FRAME
234- bam_pgn_hex : this is used in J1939 and marks the initial BAM message ID in HEX
234+ bam_pgn : this is used in J1939 and marks the initial BAM message ID in DEC
235235 res_id_list_hex: TP 'response CAN IDs' to process. For nmea/j1939, these are provided by default
236236
237237 """
@@ -245,7 +245,7 @@ def __init__(self, tp_type=""):
245245 "FIRST_FRAME" : 0x10 ,
246246 "CONSEQ_FRAME" : 0x20 ,
247247 "ff_payload_start" : 2 ,
248- "bam_pgn_hex " : "" ,
248+ "bam_pgn " : - 1 ,
249249 "res_id_list_hex" : ["0x7E0" , "0x7E9" , "0x7EA" , "0x7EB" , "0x7EC" , "0x7ED" , "0x7EE" , "0x7EF" , "0x7EA" , "0x7BB" ],
250250 }
251251
@@ -257,7 +257,7 @@ def __init__(self, tp_type=""):
257257 "FIRST_FRAME" : 0x20 ,
258258 "CONSEQ_FRAME" : 0x00 ,
259259 "ff_payload_start" : 8 ,
260- "bam_pgn_hex " : "0xEC00" ,
260+ "bam_pgn " : int ( "0xEC00" , 16 ) ,
261261 "res_id_list_hex" : ["0xEB00" ],
262262 }
263263
@@ -269,7 +269,7 @@ def __init__(self, tp_type=""):
269269 "FIRST_FRAME" : 0x00 ,
270270 "CONSEQ_FRAME" : 0x00 ,
271271 "ff_payload_start" : 2 ,
272- "bam_pgn_hex " : "" ,
272+ "bam_pgn " : - 1 ,
273273 "res_id_list_hex" : [
274274 "0xfed8" ,
275275 "0x1f007" ,
@@ -350,99 +350,118 @@ def construct_new_tp_frame(self, base_frame, payload_concatenated, can_id):
350350 def combine_tp_frames (self , df_raw ):
351351 import pandas as pd
352352
353- bam_pgn_hex = self .frame_struct ["bam_pgn_hex " ]
353+ bam_pgn = self .frame_struct ["bam_pgn " ]
354354 res_id_list = [int (res_id , 16 ) for res_id in self .frame_struct ["res_id_list_hex" ]]
355355
356- df_raw_combined = pd . DataFrame ()
356+ df_list_combined = []
357357
358- # use PGN matching for J1939 and NMEA
358+ # use PGN matching for J1939 and NMEA and update res_id_list to relevant entries
359359 if self .tp_type == "nmea" or self .tp_type == "j1939" :
360- df_raw_excl_tp = df_raw [~ df_raw ["ID" ].apply (self .calculate_pgn ).isin (res_id_list )]
360+ res_id_list_incl_bam = res_id_list
361+ res_id_list_incl_bam .append (bam_pgn )
362+ df_raw_match = df_raw ["ID" ].apply (self .calculate_pgn ).isin (res_id_list_incl_bam )
363+ res_id_list = df_raw ["ID" ][df_raw_match ].apply (self .calculate_pgn ).drop_duplicates ().values .tolist ()
364+
365+ df_raw_tp = df_raw [df_raw_match ]
366+ df_raw_excl_tp = df_raw [~ df_raw_match ]
361367 else :
368+ df_raw_match = df_raw ["ID" ].isin (res_id_list )
369+ res_id_list = df_raw ["ID" ][df_raw_match ].drop_duplicates ().values .tolist ()
370+
371+ df_raw_tp = df_raw [df_raw_match ]
362372 df_raw_excl_tp = df_raw [~ df_raw ["ID" ].isin (res_id_list )]
363373
364- df_raw_combined = df_raw_excl_tp
374+ if len (df_raw .index ) - len (df_raw_tp .index ) - len (df_raw_excl_tp .index ):
375+ print ("Warning - total rows does not equal sum of rows incl/excl transport protocol frames" )
365376
366- for channel , df_raw_channel in df_raw .groupby ("BusChannel" ):
367- for res_id in res_id_list :
368- # filter raw data for response ID and extract a 'base frame'
369- if bam_pgn_hex == "" :
370- bam_pgn = 0
371- else :
372- bam_pgn = int (bam_pgn_hex , 16 )
377+ df_list_combined .append (df_raw_excl_tp )
373378
374- if self .tp_type == "nmea" or self .tp_type == "j1939" :
375- df_raw_filter = df_raw_channel [df_raw_channel ["ID" ].apply (self .calculate_pgn ).isin ([res_id , bam_pgn ])]
376- else :
377- df_raw_filter = df_raw_channel [df_raw_channel ["ID" ].isin ([res_id ])]
378-
379- if df_raw_filter .empty :
380- continue
381-
382- base_frame = df_raw_filter .iloc [0 ]
383-
384- frame_list = []
385- frame_timestamp_list = []
386- payload_concatenated = []
387- ff_length = 0xFFF
388- can_id = None
389- conseq_frame_prev = None
390-
391- # iterate through rows in filtered dataframe
392- for index , row in df_raw_filter .iterrows ():
393- payload = row ["DataBytes" ]
394- first_byte = payload [0 ]
395- row_id = row ["ID" ]
396- row_pgn = self .calculate_pgn (row_id )
397-
398- # check if first frame (either for UDS/NMEA or J1939 case)
399- first_frame_test = (
400- (first_byte & self .frame_struct ["FIRST_FRAME_MASK" ] == self .frame_struct ["FIRST_FRAME" ])
401- & (bam_pgn_hex == "" )
402- ) or (self .tp_type == "j1939" and bam_pgn == row_pgn )
403-
404- # if single frame, save frame directly (excl. 1st byte)
405- if first_byte & self .frame_struct ["SINGLE_FRAME_MASK" ] == self .frame_struct ["SINGLE_FRAME" ]:
406- new_frame = self .construct_new_tp_frame (base_frame , payload , row_id )
407- frame_list .append (new_frame .values .tolist ())
408- frame_timestamp_list .append (index )
409-
410- # if first frame, save info from prior multi frame response sequence,
411- # then initialize a new sequence incl. the first frame payload
412- elif first_frame_test :
413- # create a new frame using information from previous iterations
414- if len (payload_concatenated ) >= ff_length :
415- new_frame = self .construct_new_tp_frame (base_frame , payload_concatenated , can_id )
379+ for res_id in res_id_list :
380+ # filter raw data for response ID and extract a 'base frame'
381+ if self .tp_type == "nmea" or self .tp_type == "j1939" :
382+ df_raw_res_id = df_raw_tp [df_raw_tp ["ID" ].apply (self .calculate_pgn ).isin ([res_id , bam_pgn ])]
383+ else :
384+ df_raw_res_id = df_raw_tp [df_raw_tp ["ID" ].isin ([res_id ])]
416385
386+ if df_raw_res_id .empty :
387+ continue
388+
389+ for channel , df_channel in df_raw_res_id .groupby ("BusChannel" ):
390+
391+ # if J1939, we can't group by CAN ID (as we need both bam_pgn and response)
392+ if self .tp_type == "j1939" :
393+ group = "DataLength"
394+ else :
395+ group = "ID"
396+
397+ for identifier , df_raw_filter in df_channel .groupby (group ):
398+
399+ base_frame = df_raw_filter .iloc [0 ]
400+
401+ frame_list = []
402+ frame_timestamp_list = []
403+ payload_concatenated = []
404+ ff_length = 0xFFF
405+ can_id = None
406+ conseq_frame_prev = None
407+
408+ # iterate through rows in filtered dataframe
409+ for index , row in df_raw_filter .iterrows ():
410+ first_byte = row ["DataBytes" ][0 ]
411+
412+ # check if first frame (either for UDS/NMEA or J1939 case)
413+ if self .tp_type == "j1939" and bam_pgn == self .calculate_pgn (row ["ID" ]):
414+ first_frame_test = True
415+ elif (first_byte & self .frame_struct ["FIRST_FRAME_MASK" ]) == self .frame_struct ["FIRST_FRAME" ]:
416+ first_frame_test = True
417+ else :
418+ first_frame_test = False
419+
420+ # if single frame, save frame directly (excl. 1st byte)
421+ if self .tp_type != "nmea" and (
422+ first_byte & self .frame_struct ["SINGLE_FRAME_MASK" ] == self .frame_struct ["SINGLE_FRAME" ]
423+ ):
424+ new_frame = self .construct_new_tp_frame (base_frame , row ["DataBytes" ], row ["ID" ])
417425 frame_list .append (new_frame .values .tolist ())
418- frame_timestamp_list .append (frame_timestamp )
426+ frame_timestamp_list .append (index )
419427
420- # reset and start on next frame
421- payload_concatenated = []
422- conseq_frame_prev = None
423- frame_timestamp = index
428+ # if first frame, save info from prior multi frame response sequence,
429+ # then initialize a new sequence incl. the first frame payload
430+ elif first_frame_test :
431+ # create a new frame using information from previous iterations
432+ if len (payload_concatenated ) >= ff_length :
433+ new_frame = self .construct_new_tp_frame (base_frame , payload_concatenated , can_id )
424434
425- # for J1939, extract PGN and convert to 29 bit CAN ID for use in baseframe
426- if self .tp_type == "j1939" :
427- pgn_hex = "" .join ("{:02x}" .format (x ) for x in reversed (payload [5 :8 ]))
428- pgn = int (pgn_hex , 16 )
429- can_id = (6 << 26 ) | (pgn << 8 ) | 254
435+ frame_list .append (new_frame .values .tolist ())
436+ frame_timestamp_list .append (frame_timestamp )
430437
431- ff_length = (payload [0 ] & 0x0F ) << 8 | payload [1 ]
438+ # reset and start on next frame
439+ payload_concatenated = []
440+ conseq_frame_prev = None
441+ frame_timestamp = index
432442
433- for byte in payload [self .frame_struct ["ff_payload_start" ] :]:
434- payload_concatenated .append (byte )
443+ # for J1939, extract PGN and convert to 29 bit CAN ID for use in baseframe
444+ if self .tp_type == "j1939" :
445+ pgn_hex = "" .join ("{:02x}" .format (x ) for x in reversed (row ["DataBytes" ][5 :8 ]))
446+ pgn = int (pgn_hex , 16 )
447+ can_id = (6 << 26 ) | (pgn << 8 ) | 254
435448
436- # if consequtive frame, extend payload with payload excl. 1st byte
437- elif first_byte & self .frame_struct ["CONSEQ_FRAME_MASK" ] == self .frame_struct ["CONSEQ_FRAME" ]:
438- if (conseq_frame_prev == None ) or ((first_byte - conseq_frame_prev ) == 1 ):
439- conseq_frame_prev = first_byte
440- for byte in payload [1 :]:
449+ ff_length = (row ["DataBytes" ][0 ] & 0x0F ) << 8 | row ["DataBytes" ][1 ]
450+
451+ for byte in row ["DataBytes" ][self .frame_struct ["ff_payload_start" ] :]:
441452 payload_concatenated .append (byte )
442453
443- df_raw_tp = pd .DataFrame (frame_list , columns = base_frame .index , index = frame_timestamp_list )
444- df_raw_combined = df_raw_combined .append (df_raw_tp )
454+ # if consequtive frame, extend payload with payload excl. 1st byte
455+ elif first_byte & self .frame_struct ["CONSEQ_FRAME_MASK" ] == self .frame_struct ["CONSEQ_FRAME" ]:
456+ if (conseq_frame_prev == None ) or ((first_byte - conseq_frame_prev ) == 1 ):
457+ conseq_frame_prev = first_byte
458+ for byte in row ["DataBytes" ][1 :]:
459+ payload_concatenated .append (byte )
460+
461+ df_raw_res_id_new = pd .DataFrame (frame_list , columns = base_frame .index , index = frame_timestamp_list )
462+ df_list_combined .append (df_raw_res_id_new )
445463
464+ df_raw_combined = pd .concat (df_list_combined )
446465 df_raw_combined .index .name = "TimeStamp"
447466 df_raw_combined = df_raw_combined .sort_index ()
448467
0 commit comments