11import re
22import os
33import struct
4- import bitstring
54import sys
65import numbers
76from collections import namedtuple , defaultdict
@@ -17,6 +16,7 @@ def int_or_float(s):
1716 "DBCSignal" , ["name" , "start_bit" , "size" , "is_little_endian" , "is_signed" ,
1817 "factor" , "offset" , "tmin" , "tmax" , "units" ])
1918
19+
2020class dbc (object ):
2121 def __init__ (self , fn ):
2222 self .name , _ = os .path .splitext (os .path .basename (fn ))
@@ -122,6 +122,16 @@ def lookup_msg_id(self, msg_id):
122122 msg_id = self .msg_name_to_address [msg_id ]
123123 return msg_id
124124
125+ def reverse_bytes (self , x ):
126+ return ((x & 0xff00000000000000 ) >> 56 ) | \
127+ ((x & 0x00ff000000000000 ) >> 40 ) | \
128+ ((x & 0x0000ff0000000000 ) >> 24 ) | \
129+ ((x & 0x000000ff00000000 ) >> 8 ) | \
130+ ((x & 0x00000000ff000000 ) << 8 ) | \
131+ ((x & 0x0000000000ff0000 ) << 24 ) | \
132+ ((x & 0x000000000000ff00 ) << 40 ) | \
133+ ((x & 0x00000000000000ff ) << 56 )
134+
125135 def encode (self , msg_id , dd ):
126136 """Encode a CAN message using the dbc.
127137
@@ -131,35 +141,40 @@ def encode(self, msg_id, dd):
131141 """
132142 msg_id = self .lookup_msg_id (msg_id )
133143
134- # TODO: Stop using bitstring, which is super slow.
135144 msg_def = self .msgs [msg_id ]
136145 size = msg_def [0 ][1 ]
137146
138- bsf = bitstring . Bits ( hex = "00" * size )
147+ result = 0
139148 for s in msg_def [1 ]:
140149 ival = dd .get (s .name )
141150 if ival is not None :
142- ival = (ival / s .factor ) - s .offset
143- ival = int (round (ival ))
144151
145- # should pack this
152+ b2 = s . size
146153 if s .is_little_endian :
147- ss = s .start_bit
154+ b1 = s .start_bit
148155 else :
149- ss = self .bits_index [s .start_bit ]
156+ b1 = (s .start_bit // 8 ) * 8 + (- s .start_bit - 1 ) % 8
157+ bo = 64 - (b1 + s .size )
158+
159+ ival = (ival / s .factor ) - s .offset
160+ ival = int (round (ival ))
150161
162+ if s .is_signed and ival < 0 :
163+ ival = (1 << b2 ) + ival
151164
152- if s .is_signed :
153- tbs = bitstring .Bits (int = ival , length = s .size )
154- else :
155- tbs = bitstring .Bits (uint = ival , length = s .size )
165+ shift = b1 if s .is_little_endian else bo
166+ mask = ((1 << b2 ) - 1 ) << shift
167+ dat = (ival & ((1 << b2 ) - 1 )) << shift
168+
169+ if s .is_little_endian :
170+ mask = self .reverse_bytes (mask )
171+ dat = self .reverse_bytes (dat )
156172
157- lpad = bitstring .Bits (bin = "0b" + "0" * ss )
158- rpad = bitstring .Bits (bin = "0b" + "0" * (8 * size - (ss + s .size )))
159- tbs = lpad + tbs + rpad
173+ result &= ~ mask
174+ result |= dat
160175
161- bsf |= tbs
162- return bsf . tobytes ()
176+ result = struct . pack ( '>Q' , result )
177+ return result [: size ]
163178
164179 def decode (self , x , arr = None , debug = False ):
165180 """Decode a CAN message using the dbc.
@@ -195,55 +210,77 @@ def decode(self, x, arr=None, debug=False):
195210 if debug :
196211 print name
197212
198- blen = 8 * len (x [2 ])
199-
200- st = x [2 ].rjust (8 , '\x00 ' )
213+ st = x [2 ].ljust (8 , '\x00 ' )
201214 le , be = None , None
202- size = msg [0 ][1 ]
203215
204216 for s in msg [1 ]:
205217 if arr is not None and s [0 ] not in arr :
206218 continue
207219
208- # big or little endian?
209- # see http://vi-firmware.openxcplatform.com/en/master/config/bit-numbering.html
210- if s [3 ] is False :
211- ss = self .bits_index [s [1 ]]
212- if be is None :
213- be = struct .unpack (">Q" , st )[0 ]
214- x2_int = be
215- data_bit_pos = (blen - (ss + s [2 ]))
220+ start_bit = s [1 ]
221+ signal_size = s [2 ]
222+ little_endian = s [3 ]
223+ signed = s [4 ]
224+ factor = s [5 ]
225+ offset = s [6 ]
226+
227+ b2 = signal_size
228+ if little_endian :
229+ b1 = start_bit
216230 else :
231+ b1 = (start_bit // 8 ) * 8 + (- start_bit - 1 ) % 8
232+ bo = 64 - (b1 + signal_size )
233+
234+ if little_endian :
217235 if le is None :
218236 le = struct .unpack ("<Q" , st )[0 ]
219- x2_int = le >> (64 - 8 * size )
220- ss = s [1 ]
221- data_bit_pos = ss
237+ shift_amount = b1
238+ tmp = le
239+ else :
240+ if be is None :
241+ be = struct .unpack (">Q" , st )[0 ]
242+ shift_amount = bo
243+ tmp = be
222244
223- if data_bit_pos < 0 :
245+ if shift_amount < 0 :
224246 continue
225- ival = (x2_int >> data_bit_pos ) & ((1 << (s [2 ])) - 1 )
226247
227- if s [4 ] and (ival & (1 << (s [2 ]- 1 ))): # signed
228- ival -= (1 << s [2 ])
248+ tmp = (tmp >> shift_amount ) & ((1 << b2 ) - 1 )
249+ if signed and (tmp >> (b2 - 1 )):
250+ tmp -= (1 << b2 )
229251
230- # control the offset
231- ival = ( ival * s [ 5 ]) + s [ 6 ]
232- #if debug:
233- # print "%40s %2d %2d %7.2f %s" % (s[0], s[1], s[2], ival , s[-1])
252+ tmp = tmp * factor + offset
253+
254+ # if debug:
255+ # print "%40s %2d %2d %7.2f %s" % (s[0], s[1], s[2], tmp , s[-1])
234256
235257 if arr is None :
236- out [s [0 ]] = ival
258+ out [s [0 ]] = tmp
237259 else :
238- out [arr .index (s [0 ])] = ival
260+ out [arr .index (s [0 ])] = tmp
239261 return name , out
240262
241263 def get_signals (self , msg ):
242264 msg = self .lookup_msg_id (msg )
243265 return [sgs .name for sgs in self .msgs [msg ][1 ]]
244266
267+
245268if __name__ == "__main__" :
246269 from opendbc import DBC_PATH
270+ import numpy as np
271+
272+ dbc_test = dbc (os .path .join (DBC_PATH , 'toyota_prius_2017_pt_generated.dbc' ))
273+ msg = ('STEER_ANGLE_SENSOR' , {'STEER_ANGLE' : - 6.0 , 'STEER_RATE' : 4 , 'STEER_FRACTION' : - 0.2 })
274+ encoded = dbc_test .encode (* msg )
275+ decoded = dbc_test .decode ((0x25 , 0 , encoded ))
276+ assert decoded == msg
277+
278+ dbc_test = dbc (os .path .join (DBC_PATH , 'hyundai_santa_fe_2019_ccan.dbc' ))
279+ decoded = dbc_test .decode ((0x2b0 , 0 , "\xfa \xfe \x00 \x07 \x12 " ))
280+ assert np .isclose (decoded [1 ]['SAS_Angle' ], - 26.2 )
281+
282+ msg = ('SAS11' , {'SAS_Stat' : 7.0 , 'MsgCount' : 0.0 , 'SAS_Angle' : - 26.200000000000003 , 'SAS_Speed' : 0.0 , 'CheckSum' : 0.0 })
283+ encoded = dbc_test .encode (* msg )
284+ decoded = dbc_test .decode ((0x2b0 , 0 , encoded ))
247285
248- dbc_test = dbc (os .path .join (DBC_PATH , sys .argv [1 ]))
249- print dbc_test .get_signals (0xe4 )
286+ assert decoded == msg
0 commit comments