]> sigrok.org Git - libsigrokdecode.git/blob - decoders/caliper/pd.py
caliper: use common code for bit accumulation
[libsigrokdecode.git] / decoders / caliper / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
5 ##
6 ## Permission is hereby granted, free of charge, to any person obtaining a copy
7 ## of this software and associated documentation files (the "Software"), to deal
8 ## in the Software without restriction, including without limitation the rights
9 ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 ## copies of the Software, and to permit persons to whom the Software is
11 ## furnished to do so, subject to the following conditions:
12 ##
13 ## The above copyright notice and this permission notice shall be included in all
14 ## copies or substantial portions of the Software.
15 ##
16 ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 ## SOFTWARE.
23
24 import sigrokdecode as srd
25 from common.srdhelper import bitpack
26
27 # Millimeters per inch.
28 mm_per_inch = 25.4
29
30 class Decoder(srd.Decoder):
31     api_version = 3
32     id = 'caliper'
33     name = 'Caliper'
34     longname = 'Digital calipers'
35     desc = 'Protocol of cheap generic digital calipers.'
36     license = 'mit'
37     inputs = ['logic']
38     outputs = []
39     channels = (
40         {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
41         {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
42     )
43     options = (
44         {'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
45             'default': 10},
46         {'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
47             'values': ('keep', 'mm', 'inch')},
48         {'id': 'changes', 'desc': 'Changes only', 'default': 'no',
49             'values': ('no', 'yes')},
50     )
51     tags = ['Analog/digital', 'Sensor']
52     annotations = (
53         ('measurement', 'Measurement'),
54         ('warning', 'Warning'),
55     )
56     annotation_rows = (
57         ('measurements', 'Measurements', (0,)),
58         ('warnings', 'Warnings', (1,)),
59     )
60
61     def metadata(self, key, value):
62        if key == srd.SRD_CONF_SAMPLERATE:
63             self.samplerate = value
64
65     def __init__(self):
66         self.reset()
67
68     def reset(self):
69         self.ss, self.es = 0, 0
70         self.number_bits = []
71         self.flags_bits = []
72
73     def start(self):
74         self.out_ann = self.register(srd.OUTPUT_ANN)
75
76     def putg(self, ss, es, cls, data):
77         self.put(ss, es, self.out_ann, [cls, data])
78
79     def decode(self):
80         last_measurement = None
81         timeout_ms = self.options['timeout_ms']
82         want_unit = self.options['unit']
83         show_all = self.options['changes'] == 'no'
84         snum_per_ms = self.samplerate / 1000
85         timeout_snum = timeout_ms * snum_per_ms
86         while True:
87             clk, data = self.wait([{0: 'r'}, {'skip': round(snum_per_ms)}])
88
89             # Timeout after inactivity.
90             if timeout_ms > 0:
91                 if self.samplenum > self.es + timeout_snum:
92                     if self.number_bits or self.flags_bits:
93                         count = len(self.number_bits) + len(self.flags_bits)
94                         self.putg(self.ss, self.samplenum, 1, [
95                             'timeout with %s bits in buffer' % (count),
96                             'timeout',
97                         ])
98                     self.reset()
99
100             # Do nothing if there was timeout without rising clock edge.
101             if self.matched == (False, True):
102                 continue
103
104             # Store position of first bit and last activity.
105             # Shift in measured number and flag bits.
106             if not self.ss:
107                 self.ss = self.samplenum
108             self.es = self.samplenum
109             if len(self.number_bits) < 16:
110                 self.number_bits.append(data)
111                 continue
112             if len(self.flags_bits) < 8:
113                 self.flags_bits.append(data)
114                 if len(self.flags_bits) < 8:
115                     continue
116
117             # Get raw values from received data bits. Run the number
118             # conversion, controlled by flags and/or user specs.
119             negative = bool(self.flags_bits[4])
120             is_inch = bool(self.flags_bits[7])
121             number = bitpack(self.number_bits)
122             if negative:
123                 number = -number
124             if is_inch:
125                 number = number / 2000
126                 if want_unit == 'mm':
127                     number *= mm_per_inch
128                     is_inch = False
129             else:
130                 number = number / 100
131                 if want_unit == 'inch':
132                     number = round(number / mm_per_inch, 4)
133                     is_inch = True
134
135             units = "in" if is_inch else "mm"
136
137             measurement = (str(number) + units)
138
139             if show_all or measurement != last_measurement:
140                 self.putg(self.ss, self.es, 0, [
141                     measurement,
142                     str(number),
143                 ])
144                 last_measurement = measurement
145
146             # Prepare for next packet.
147             self.reset()