]> sigrok.org Git - libsigrokdecode.git/blame - decoders/caliper/pd.py
caliper: use common code for bit accumulation
[libsigrokdecode.git] / decoders / caliper / pd.py
CommitLineData
5ed5cea5
TM
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
24import sigrokdecode as srd
95435ac2
GS
25from common.srdhelper import bitpack
26
27# Millimeters per inch.
28mm_per_inch = 25.4
5ed5cea5
TM
29
30class Decoder(srd.Decoder):
31 api_version = 3
32 id = 'caliper'
33 name = 'Caliper'
34 longname = 'Digital calipers'
4afcb4d0 35 desc = 'Protocol of cheap generic digital calipers.'
5ed5cea5
TM
36 license = 'mit'
37 inputs = ['logic']
38 outputs = []
39 channels = (
4afcb4d0
GS
40 {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
41 {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
5ed5cea5
TM
42 )
43 options = (
4afcb4d0
GS
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')},
5ed5cea5 50 )
4afcb4d0 51 tags = ['Analog/digital', 'Sensor']
5ed5cea5 52 annotations = (
4afcb4d0
GS
53 ('measurement', 'Measurement'),
54 ('warning', 'Warning'),
5ed5cea5
TM
55 )
56 annotation_rows = (
57 ('measurements', 'Measurements', (0,)),
58 ('warnings', 'Warnings', (1,)),
59 )
60
5ed5cea5
TM
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):
95435ac2
GS
69 self.ss, self.es = 0, 0
70 self.number_bits = []
71 self.flags_bits = []
5ed5cea5
TM
72
73 def start(self):
74 self.out_ann = self.register(srd.OUTPUT_ANN)
75
b3fafeb4
GS
76 def putg(self, ss, es, cls, data):
77 self.put(ss, es, self.out_ann, [cls, data])
78
5ed5cea5 79 def decode(self):
b3fafeb4
GS
80 last_measurement = None
81 timeout_ms = self.options['timeout_ms']
82 want_unit = self.options['unit']
83 show_all = self.options['changes'] == 'no'
b213c046
GS
84 snum_per_ms = self.samplerate / 1000
85 timeout_snum = timeout_ms * snum_per_ms
5ed5cea5 86 while True:
b213c046 87 clk, data = self.wait([{0: 'r'}, {'skip': round(snum_per_ms)}])
5ed5cea5 88
4afcb4d0 89 # Timeout after inactivity.
b3fafeb4 90 if timeout_ms > 0:
95435ac2
GS
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),
b3fafeb4
GS
96 'timeout',
97 ])
5ed5cea5
TM
98 self.reset()
99
4afcb4d0 100 # Do nothing if there was timeout without rising clock edge.
5ed5cea5
TM
101 if self.matched == (False, True):
102 continue
103
95435ac2
GS
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)
5ed5cea5 111 continue
95435ac2
GS
112 if len(self.flags_bits) < 8:
113 self.flags_bits.append(data)
114 if len(self.flags_bits) < 8:
5ed5cea5 115 continue
5ed5cea5 116
95435ac2
GS
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:
5ed5cea5 123 number = -number
95435ac2 124 if is_inch:
4afcb4d0 125 number = number / 2000
b3fafeb4 126 if want_unit == 'mm':
95435ac2
GS
127 number *= mm_per_inch
128 is_inch = False
5ed5cea5 129 else:
4afcb4d0 130 number = number / 100
b3fafeb4 131 if want_unit == 'inch':
95435ac2
GS
132 number = round(number / mm_per_inch, 4)
133 is_inch = True
5ed5cea5 134
95435ac2 135 units = "in" if is_inch else "mm"
5ed5cea5 136
4afcb4d0 137 measurement = (str(number) + units)
5ed5cea5 138
b3fafeb4 139 if show_all or measurement != last_measurement:
95435ac2 140 self.putg(self.ss, self.es, 0, [
b3fafeb4
GS
141 measurement,
142 str(number),
143 ])
144 last_measurement = measurement
5ed5cea5 145
4afcb4d0 146 # Prepare for next packet.
5ed5cea5 147 self.reset()