]> sigrok.org Git - libsigrokdecode.git/blame - decoders/caliper/pd.py
caliper: slight refactoring of common pieces
[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
25
26class Decoder(srd.Decoder):
27 api_version = 3
28 id = 'caliper'
29 name = 'Caliper'
30 longname = 'Digital calipers'
4afcb4d0 31 desc = 'Protocol of cheap generic digital calipers.'
5ed5cea5
TM
32 license = 'mit'
33 inputs = ['logic']
34 outputs = []
35 channels = (
4afcb4d0
GS
36 {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
37 {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
5ed5cea5
TM
38 )
39 options = (
4afcb4d0
GS
40 {'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
41 'default': 10},
42 {'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
43 'values': ('keep', 'mm', 'inch')},
44 {'id': 'changes', 'desc': 'Changes only', 'default': 'no',
45 'values': ('no', 'yes')},
5ed5cea5 46 )
4afcb4d0 47 tags = ['Analog/digital', 'Sensor']
5ed5cea5 48 annotations = (
4afcb4d0
GS
49 ('measurement', 'Measurement'),
50 ('warning', 'Warning'),
5ed5cea5
TM
51 )
52 annotation_rows = (
53 ('measurements', 'Measurements', (0,)),
54 ('warnings', 'Warnings', (1,)),
55 )
56
5ed5cea5
TM
57 def metadata(self, key, value):
58 if key == srd.SRD_CONF_SAMPLERATE:
59 self.samplerate = value
60
61 def __init__(self):
62 self.reset()
63
64 def reset(self):
65 self.ss_cmd, self.es_cmd = 0, 0
b3fafeb4
GS
66 self.bits = 0
67 self.number = 0
68 self.flags = 0
5ed5cea5
TM
69
70 def start(self):
71 self.out_ann = self.register(srd.OUTPUT_ANN)
72
b3fafeb4
GS
73 def putg(self, ss, es, cls, data):
74 self.put(ss, es, self.out_ann, [cls, data])
75
4afcb4d0
GS
76 # Switch bit order of variable x, which is l bit long.
77 def bitr(self, x, l):
5ed5cea5
TM
78 return int(bin(x)[2:].zfill(l)[::-1], 2)
79
80 def decode(self):
b3fafeb4
GS
81 last_measurement = None
82 timeout_ms = self.options['timeout_ms']
83 want_unit = self.options['unit']
84 show_all = self.options['changes'] == 'no'
5ed5cea5 85 while True:
4afcb4d0 86 clk, data = self.wait([{0: 'r'}, {'skip': round(self.samplerate / 1000)}])
5ed5cea5 87
4afcb4d0 88 # Timeout after inactivity.
b3fafeb4
GS
89 if timeout_ms > 0:
90 if self.samplenum > self.es_cmd + (self.samplerate / (1000 / timeout_ms)):
5ed5cea5 91 if self.bits > 0:
b3fafeb4
GS
92 self.putg(self.ss_cmd, self.samplenum, 1, [
93 'timeout with %s bits in buffer' % (self.bits),
94 'timeout',
95 ])
5ed5cea5
TM
96 self.reset()
97
4afcb4d0 98 # Do nothing if there was timeout without rising clock edge.
5ed5cea5
TM
99 if self.matched == (False, True):
100 continue
101
4afcb4d0 102 # Store position of last activity.
5ed5cea5
TM
103 self.es_cmd = self.samplenum
104
4afcb4d0 105 # Store position of first bit.
5ed5cea5
TM
106 if self.ss_cmd == 0:
107 self.ss_cmd = self.samplenum
108
4afcb4d0 109 # Shift in measured number.
5ed5cea5
TM
110 if self.bits < 16:
111 self.number = (self.number << 1) | (data & 0b1)
4afcb4d0 112 self.bits += 1
5ed5cea5
TM
113 continue
114
4afcb4d0 115 # Shift in flag bits.
5ed5cea5
TM
116 if self.bits < 24:
117 self.flags = (self.flags << 1) | (data & 0b1)
4afcb4d0 118 self.bits += 1
5ed5cea5
TM
119 if self.bits < 24:
120 continue
4afcb4d0 121 # We got last bit of data.
5ed5cea5
TM
122 self.es_cmd = self.samplenum
123
4afcb4d0 124 # Do actual decoding.
5ed5cea5
TM
125
126 negative = ((self.flags & 0b00001000) >> 3)
127 inch = (self.flags & 0b00000001)
128
129 number = self.bitr(self.number, 16)
130
5ed5cea5
TM
131 if negative > 0:
132 number = -number
133
134 inchmm = 25.4 #how many mms in inch
135
136 if inch:
4afcb4d0 137 number = number / 2000
b3fafeb4 138 if want_unit == 'mm':
5ed5cea5
TM
139 number *= inchmm
140 inch = 0
141 else:
4afcb4d0 142 number = number / 100
b3fafeb4 143 if want_unit == 'inch':
4afcb4d0 144 number = round(number / inchmm, 4)
5ed5cea5
TM
145 inch = 1
146
147 units = "in" if inch else "mm"
148
4afcb4d0 149 measurement = (str(number) + units)
5ed5cea5 150
b3fafeb4
GS
151 if show_all or measurement != last_measurement:
152 self.putg(self.ss_cmd, self.es_cmd, 0, [
153 measurement,
154 str(number),
155 ])
156 last_measurement = measurement
5ed5cea5 157
4afcb4d0 158 # Prepare for next packet.
5ed5cea5 159 self.reset()