summaryrefslogtreecommitdiff
path: root/decoders/sae_j1850_vpw/pd.py
blob: 8faba64c2a274480e2d2f7b02ece53748e1fe5fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Anthony Symons <antus@pcmhacking.net>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##

import sigrokdecode as srd

class SamplerateError(Exception):
    pass

def timeuf(t):
    return int (t * 1000.0 * 1000.0)

def normalize_time(t):
    if t >= 1.0:
        return '%d s' % t
    elif t >= 0.001:
        return '%d ms' % (t * 1000.0)
    elif t >= 0.000001:
        return '%d μs' % (t * 1000.0 * 1000.0)
    elif t >= 0.000000001:
        return '%d ns' % (t * 1000.0 * 1000.0 * 1000.0)
    else:
        return '%f' % t

class Decoder(srd.Decoder):
    api_version = 3
    id = 'sae_j1850_vpw'
    name = 'SAE J1850 VPW'
    longname = 'SAE J1850 VPW.'
    desc = 'SAE J1850 Variable Pulse Width 1x and 4x.'
    license = 'gplv2+'
    inputs = ['logic']
    outputs = []
    tags = ['Automotive']
    channels = (
        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
    )
    annotations = (
        ('time', 'Time'),
        ('raw', 'Raw'),
        ('sof', 'SOF'),
        ('ifs', 'EOF/IFS'),
        ('data', 'Data'),
        ('packet', 'Packet'),
    )
    annotation_rows = (
        ('packets', 'Packets', (5,)),
        ('bytes', 'Bytes', (4,)),
        ('raws', 'Raws', (1,2,3,)),
        ('times', 'Times', (0,)),
    )

    def __init__(self):
        self.reset()

    def reset(self):
        self.state = 'IDLE'
        self.samplerate = None
        self.byte = 0      # the byte offset in the packet
        self.mode = 0      # for by packet decode
        self.data = 0      # the current byte
        self.datastart = 0 # sample number this byte started at
        self.csa = 0       # track the last byte seperately to retrospectively add the CS marker
        self.csb = 0
        self.count = 0     # which bit number we are up to
        self.active = 0    # which logic level is considered active

        # vpw timings. ideal, min and max tollerances.
        # From SAE J1850 1995 rev section 23.406

        self.sof = 200
        self.sofl = 164
        self.sofh = 245  # 240 by the spec, 245 so a 60us 4x sample will pass
        self.long = 128
        self.longl = 97
        self.longh = 170 # 164 by the spec but 170 for low sample rate tolerance.
        self.short = 64
        self.shortl = 24 # 35 by the spec, 24 to allow down to 6us as measured in practice for 4x @ 1mhz sampling
        self.shorth = 97
        self.ifs = 240
        self.spd = 1     # set to 4 when a 4x SOF is detected (VPW high speed frame)

    def handle_bit(self, ss, es, b):
        self.data |= (b << 7-self.count) # MSB-first
        self.put(ss, es, self.out_ann, [1, ["%d" % b]])
        if self.count == 0:
            self.datastart = ss
        if self.count == 7:
            self.csa = self.datastart # for CS
            self.csb = self.samplenum # for CS
            self.put(self.datastart, self.samplenum, self.out_ann, [4, ["%02X" % self.data]])
            # add protocol parsing here
            if self.byte == 0:
                self.put(self.datastart, self.samplenum, self.out_ann, [5, ['Priority','Prio','P']])
            elif self.byte == 1:
                self.put(self.datastart, self.samplenum, self.out_ann, [5, ['Destination','Dest','D']])
            elif self.byte == 2:
                self.put(self.datastart, self.samplenum, self.out_ann, [5, ['Source','Src','S']])
            elif self.byte == 3:
                self.put(self.datastart, self.samplenum, self.out_ann, [5, ['Mode','M']])
                self.mode = self.data
            elif self.mode == 1 and self.byte == 4: # mode 1 payload
                self.put(self.datastart, self.samplenum, self.out_ann, [5, ['Pid','P']])

            # prepare for next byte
            self.count = -1
            self.data = 0
            self.byte = self.byte + 1 # track packet offset
        self.count = self.count + 1

    def metadata(self, key, value):
        if key == srd.SRD_CONF_SAMPLERATE:
            self.samplerate = value

    def start(self):
        self.out_ann = self.register(srd.OUTPUT_ANN)

    def decode(self):
        if not self.samplerate:
            raise SamplerateError('Cannot decode without samplerate.')

        self.wait({0: 'e'})
        es = self.samplenum
        while True:
            ss = es
            pin, = self.wait({0: 'e'})
            es = self.samplenum

            samples = es - ss
            txt = normalize_time(samples / self.samplerate)
            self.put(ss, es, self.out_ann, [0, [txt]])
            t = timeuf(samples / self.samplerate)
            if self.state == 'IDLE': # detect and set speed from the size of sof
                if pin == self.active and t in range(self.sofl , self.sofh):
                    self.put(ss, es, self.out_ann, [1, ['1X SOF', 'S1', 'S']])
                    self.spd = 1
                    self.data = 0
                    self.count = 0
                    self.state = 'DATA'
                elif pin == self.active and t in range(int(self.sofl / 4) , int(self.sofh / 4)):
                    self.put(ss, es, self.out_ann, [1, ['4X SOF', 'S4', '4']])
                    self.spd = 4
                    self.data = 0
                    self.count = 0
                    self.state = 'DATA'

            elif self.state == 'DATA':
                if t >= int(self.ifs / self.spd):
                    self.state = 'IDLE'
                    self.put(ss, es, self.out_ann, [1, ["EOF/IFS", "E"]]) # EOF=239-280 IFS=281+
                    self.put(self.csa, self.csb, self.out_ann, [5, ['Checksum','CS','C']]) # retrospective print of CS
                    self.byte = 0 # reset packet offset
                elif t in range(int(self.shortl / self.spd), int(self.shorth / self.spd)):
                    if pin == self.active:
                        self.handle_bit(ss, es, 1)
                    else:
                        self.handle_bit(ss, es, 0)
                elif t in range(int(self.longl / self.spd), int(self.longh / self.spd)):
                    if pin == self.active:
                        self.handle_bit(ss, es, 0)
                    else:
                        self.handle_bit(ss, es, 1)