1
1
import re
2
2
import os
3
3
import struct
4
- import bitstring
5
4
import sys
6
5
import numbers
7
6
from collections import namedtuple , defaultdict
@@ -17,6 +16,7 @@ def int_or_float(s):
17
16
"DBCSignal" , ["name" , "start_bit" , "size" , "is_little_endian" , "is_signed" ,
18
17
"factor" , "offset" , "tmin" , "tmax" , "units" ])
19
18
19
+
20
20
class dbc (object ):
21
21
def __init__ (self , fn ):
22
22
self .name , _ = os .path .splitext (os .path .basename (fn ))
@@ -122,6 +122,16 @@ def lookup_msg_id(self, msg_id):
122
122
msg_id = self .msg_name_to_address [msg_id ]
123
123
return msg_id
124
124
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
+
125
135
def encode (self , msg_id , dd ):
126
136
"""Encode a CAN message using the dbc.
127
137
@@ -131,35 +141,40 @@ def encode(self, msg_id, dd):
131
141
"""
132
142
msg_id = self .lookup_msg_id (msg_id )
133
143
134
- # TODO: Stop using bitstring, which is super slow.
135
144
msg_def = self .msgs [msg_id ]
136
145
size = msg_def [0 ][1 ]
137
146
138
- bsf = bitstring . Bits ( hex = "00" * size )
147
+ result = 0
139
148
for s in msg_def [1 ]:
140
149
ival = dd .get (s .name )
141
150
if ival is not None :
142
- ival = (ival / s .factor ) - s .offset
143
- ival = int (round (ival ))
144
151
145
- # should pack this
152
+ b2 = s . size
146
153
if s .is_little_endian :
147
- ss = s .start_bit
154
+ b1 = s .start_bit
148
155
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 ))
150
161
162
+ if s .is_signed and ival < 0 :
163
+ ival = (1 << b2 ) + ival
151
164
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 )
156
172
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
160
175
161
- bsf |= tbs
162
- return bsf . tobytes ()
176
+ result = struct . pack ( '>Q' , result )
177
+ return result [: size ]
163
178
164
179
def decode (self , x , arr = None , debug = False ):
165
180
"""Decode a CAN message using the dbc.
@@ -195,55 +210,77 @@ def decode(self, x, arr=None, debug=False):
195
210
if debug :
196
211
print name
197
212
198
- blen = 8 * len (x [2 ])
199
-
200
- st = x [2 ].rjust (8 , '\x00 ' )
213
+ st = x [2 ].ljust (8 , '\x00 ' )
201
214
le , be = None , None
202
- size = msg [0 ][1 ]
203
215
204
216
for s in msg [1 ]:
205
217
if arr is not None and s [0 ] not in arr :
206
218
continue
207
219
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
216
230
else :
231
+ b1 = (start_bit // 8 ) * 8 + (- start_bit - 1 ) % 8
232
+ bo = 64 - (b1 + signal_size )
233
+
234
+ if little_endian :
217
235
if le is None :
218
236
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
222
244
223
- if data_bit_pos < 0 :
245
+ if shift_amount < 0 :
224
246
continue
225
- ival = (x2_int >> data_bit_pos ) & ((1 << (s [2 ])) - 1 )
226
247
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 )
229
251
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])
234
256
235
257
if arr is None :
236
- out [s [0 ]] = ival
258
+ out [s [0 ]] = tmp
237
259
else :
238
- out [arr .index (s [0 ])] = ival
260
+ out [arr .index (s [0 ])] = tmp
239
261
return name , out
240
262
241
263
def get_signals (self , msg ):
242
264
msg = self .lookup_msg_id (msg )
243
265
return [sgs .name for sgs in self .msgs [msg ][1 ]]
244
266
267
+
245
268
if __name__ == "__main__" :
246
269
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 ))
247
285
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