summaryrefslogtreecommitdiff
path: root/decoders/caliper/pd.py
blob: ff7d3141f495f123de7d391dc0c82cee74ed543b (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
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.

import sigrokdecode as srd
from common.srdhelper import bitpack

# Millimeters per inch.
mm_per_inch = 25.4

class Decoder(srd.Decoder):
    api_version = 3
    id = 'caliper'
    name = 'Caliper'
    longname = 'Digital calipers'
    desc = 'Protocol of cheap generic digital calipers.'
    license = 'mit'
    inputs = ['logic']
    outputs = []
    channels = (
        {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
        {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
    )
    options = (
        {'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
            'default': 10},
        {'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
            'values': ('keep', 'mm', 'inch')},
        {'id': 'changes', 'desc': 'Changes only', 'default': 'no',
            'values': ('no', 'yes')},
    )
    tags = ['Analog/digital', 'Sensor']
    annotations = (
        ('measurement', 'Measurement'),
        ('warning', 'Warning'),
    )
    annotation_rows = (
        ('measurements', 'Measurements', (0,)),
        ('warnings', 'Warnings', (1,)),
    )

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

    def __init__(self):
        self.reset()

    def reset(self):
        self.ss, self.es = 0, 0
        self.number_bits = []
        self.flags_bits = []

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

    def putg(self, ss, es, cls, data):
        self.put(ss, es, self.out_ann, [cls, data])

    def decode(self):
        last_measurement = None
        timeout_ms = self.options['timeout_ms']
        want_unit = self.options['unit']
        show_all = self.options['changes'] == 'no'
        wait_cond = [{0: 'r'}]
        if timeout_ms:
            snum_per_ms = self.samplerate / 1000
            timeout_snum = timeout_ms * snum_per_ms
            wait_cond.append({'skip': round(timeout_snum)})
        while True:
            # Sample data at the rising clock edge. Optionally timeout
            # after inactivity for a user specified period. Present the
            # number of unprocessed bits to the user for diagnostics.
            clk, data = self.wait(wait_cond)
            if timeout_ms and not self.matched[0]:
                if self.number_bits or self.flags_bits:
                    count = len(self.number_bits) + len(self.flags_bits)
                    self.putg(self.ss, self.samplenum, 1, [
                        'timeout with {} bits in buffer'.format(count),
                        'timeout ({} bits)'.format(count),
                        'timeout',
                    ])
                self.reset()
                continue

            # Store position of first bit and last activity.
            # Shift in measured number and flag bits.
            if not self.ss:
                self.ss = self.samplenum
            self.es = self.samplenum
            if len(self.number_bits) < 16:
                self.number_bits.append(data)
                continue
            if len(self.flags_bits) < 8:
                self.flags_bits.append(data)
                if len(self.flags_bits) < 8:
                    continue

            # Get raw values from received data bits. Run the number
            # conversion, controlled by flags and/or user specs.
            negative = bool(self.flags_bits[4])
            is_inch = bool(self.flags_bits[7])
            number = bitpack(self.number_bits)
            if negative:
                number = -number
            if is_inch:
                number = number / 2000
                if want_unit == 'mm':
                    number *= mm_per_inch
                    is_inch = False
            else:
                number = number / 100
                if want_unit == 'inch':
                    number = round(number / mm_per_inch, 4)
                    is_inch = True

            units = "in" if is_inch else "mm"

            measurement = (str(number) + units)

            if show_all or measurement != last_measurement:
                self.putg(self.ss, self.es, 0, [
                    measurement,
                    str(number),
                ])
                last_measurement = measurement

            # Prepare for next packet.
            self.reset()