Skip to content

Commit 858d150

Browse files
committed
added script to find bits that transition from 0 to 1
1 parent 90c64b6 commit 858d150

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

examples/can_bit_transition.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# How to use can_bit_transition.py to reverse engineer a single bit field
2+
3+
Let's say our goal is to find a brake pedal signal (caused by your foot pressing the brake pedal vs adaptive cruise control braking).
4+
5+
The following process will allow you to quickly find bits that are always 0 during a period of time (when you know you were not pressing the brake with your foot) and always 1 in a different period of time (when you know you were pressing the brake with your foot).
6+
7+
Open up a drive in cabana where you can find a place you used the brake pedal and another place where you did not use the brake pedal (and you can identify when you were on the brake pedal and when you were not). You may want to go out for a drive and put something in front of the camera after you put your foot on the brake and take it away before you take your foot off the brake so you can easily identify exactly when you had your foot on the brake based on the video in cabana. This is critical because this script needs the brake signal to always be high the entire time for one of the time frames. A 10 second time frame worked well for me.
8+
9+
I found a drive where I knew I was not pressing the brake between timestamp 50.0 thru 65.0 and I was pressing the brake between timestamp 69.0 thru 79.0. Determine what the timestamps are in cabana by plotting any message and putting your mouse over the plot at the location you want to discover the timestamp. The tool tip on mouse hover has the format: timestamp: value
10+
11+
Now download the log from cabana (Save Log button) and run the script passing in the timestamps
12+
(replace csv file name with cabana log you downloaded and time ranges with your own)
13+
```
14+
./can_bit_transition.py ./honda_crv_ex_2017_can-1520354796875.csv 50.0-65.0 69.0-79.0
15+
```
16+
17+
The script will output bits that were always low in the first time range and always high in the second time range (and vice versa)
18+
```
19+
id 17c 0 -> 1 at byte 4 bitmask 1
20+
id 17c 0 -> 1 at byte 6 bitmask 32
21+
id 221 1 -> 0 at byte 0 bitmask 4
22+
id 1be 0 -> 1 at byte 0 bitmask 16
23+
```
24+
25+
Now I go back to cabana and graph the above bits by searching for the message by id, double clicking on the appropriate bit, and then selecting "show plot". I already knew that message id 0x17c is both user brake and adaptive cruise control braking combined, so I plotted one of those signals along side 0x221 and 0x1be. By replaying a drive I could see that 0x221 was not a brake signal (when high at random times that did not correspond to braking). Next I looked at 0x1be and I found it was the brake pedal signal I was looking for (went high whenever I pressed the brake pedal and did not go high when adaptive cruise control was braking).

examples/can_bit_transition.py

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
3+
import binascii
4+
import csv
5+
import sys
6+
7+
class Message():
8+
"""Details about a specific message ID."""
9+
def __init__(self, message_id):
10+
self.message_id = message_id
11+
self.ones = [0] * 8 # bit set if 1 is always seen
12+
self.zeros = [0] * 8 # bit set if 0 is always seen
13+
14+
def printBitDiff(self, other):
15+
"""Prints bits that transition from always zero to always 1 and vice versa."""
16+
for i in xrange(len(self.ones)):
17+
zero_to_one = other.zeros[i] & self.ones[i]
18+
if zero_to_one:
19+
print 'id %s 0 -> 1 at byte %d bitmask %d' % (self.message_id, i, zero_to_one)
20+
one_to_zero = other.ones[i] & self.zeros[i]
21+
if one_to_zero:
22+
print 'id %s 1 -> 0 at byte %d bitmask %d' % (self.message_id, i, one_to_zero)
23+
24+
25+
class Info():
26+
"""A collection of Messages."""
27+
28+
def __init__(self):
29+
self.messages = {} # keyed by MessageID
30+
31+
def load(self, filename, start, end):
32+
"""Given a CSV file, adds information about message IDs and their values."""
33+
with open(filename, 'rb') as input:
34+
reader = csv.reader(input)
35+
next(reader, None) # skip the CSV header
36+
for row in reader:
37+
if not len(row): continue
38+
time = float(row[0])
39+
bus = int(row[2])
40+
if time < start or bus > 127:
41+
continue
42+
elif time > end:
43+
break
44+
if row[1].startswith('0x'):
45+
message_id = row[1][2:] # remove leading '0x'
46+
else:
47+
message_id = hex(int(row[1]))[2:] # old message IDs are in decimal
48+
49+
if row[3].startswith('0x'):
50+
data = row[3][2:] # remove leading '0x'
51+
else:
52+
data = row[3]
53+
new_message = False
54+
if message_id not in self.messages:
55+
self.messages[message_id] = Message(message_id)
56+
new_message = True
57+
message = self.messages[message_id]
58+
bytes = bytearray.fromhex(data)
59+
for i in xrange(len(bytes)):
60+
ones = int(bytes[i])
61+
message.ones[i] = ones if new_message else message.ones[i] & ones
62+
# Inverts the data and masks it to a byte to get the zeros as ones.
63+
zeros = (~int(bytes[i])) & 0xff
64+
message.zeros[i] = zeros if new_message else message.zeros[i] & zeros
65+
66+
def PrintUnique(log_file, low_range, high_range):
67+
# find messages with bits that are always low
68+
start, end = map(float, low_range.split('-'))
69+
low = Info()
70+
low.load(log_file, start, end)
71+
# find messages with bits that are always high
72+
start, end = map(float, high_range.split('-'))
73+
high = Info()
74+
high.load(log_file, start, end)
75+
# print messages that go from low to high
76+
found = False
77+
for message_id in high.messages:
78+
if message_id in low.messages:
79+
high.messages[message_id].printBitDiff(low.messages[message_id])
80+
found = True
81+
if not found: print 'No messages that transition from always low to always high found!'
82+
83+
if __name__ == "__main__":
84+
if len(sys.argv) < 4:
85+
print 'Usage:\n%s log.csv <low-start>-<low-end> <high-start>-<high-end>' % sys.argv[0]
86+
sys.exit(0)
87+
PrintUnique(sys.argv[1], sys.argv[2], sys.argv[3])

0 commit comments

Comments
 (0)