]> sigrok.org Git - libsigrok.git/blob - hardware/tekpower-dmm/protocol.c
a468cb9a417e56e638ed81fc8b4290f45d69b452
[libsigrok.git] / hardware / tekpower-dmm / protocol.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <glib.h>
21 #include "libsigrok.h"
22 #include "libsigrok-internal.h"
23 #include "config.h"
24 #include "protocol.h"
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h>
28 #include <errno.h>
29
30
31 static gboolean lcd14_is_sync_valid(const lcd14_packet *packet)
32 {
33         size_t i;
34         /* Check the syncronization nibbles, and make sure they all match */
35         for(i = 0; i < LCD14_PACKET_SIZE; i++)
36         {
37                 uint8_t sync = (packet->raw[i] & LCD14_SYNC_MASK) >> 4;
38                 if(sync != (i+1) )
39                         return FALSE;
40         }
41         return TRUE;
42 }
43
44 static gboolean lcd14_is_selection_good(const lcd14_data *data)
45 {
46         int n_postfix = 0;
47         int n_type = 0;
48         /* Does the packet have more than one multiplier ? */
49         if(data->flags & LCD14_NANO)
50                 n_postfix++;
51         if(data->flags & LCD14_MICRO)
52                 n_postfix++;
53         if(data->flags & LCD14_MILLI)
54                 n_postfix++;
55         if(data->flags & LCD14_KILO)
56                 n_postfix++;
57         if(data->flags & LCD14_MEGA)
58                 n_postfix++;
59
60         if(n_postfix > 1)
61                 return FALSE;
62
63         /* Does the packet "measure" more than one type of value ?*/
64         if(data->flags & LCD14_HZ)
65                 n_type++;
66         if(data->flags & LCD14_OHM)
67                 n_type++;
68         if(data->flags & LCD14_FARAD)
69                 n_type++;
70         if(data->flags & LCD14_AMP)
71                 n_type++;
72         if(data->flags & LCD14_VOLT)
73                 n_type++;
74         if(data->flags & LCD14_DUTY)
75                 n_type++;
76         if(data->flags & LCD14_CELSIUS)
77                 n_type++;
78         /* Do not test for hFE. hFE is not implemented and always '1' */
79         if(n_type > 1)
80                 return FALSE;
81
82         /* Both AC and DC ? */
83         if( (data->flags & LCD14_AC) && (data->flags & LCD14_DC) )
84                 return FALSE;
85
86         /* OK, no duplicates */
87         return TRUE;
88 }
89
90 /* We "cook" a raw lcd14_pcaket into a more pallatable form, lcd14_data */
91 static void lcd14_cook_raw(const lcd14_packet *packet, lcd14_data * data)
92 {
93         size_t i;
94
95         /* Get the digits out */
96         for(i = 0; i < 4; i++)
97         {
98                 size_t j = (i << 1) + 1;
99                 data->digit[i] = ( (packet->raw[j] & ~LCD14_SYNC_MASK) << 4 ) |
100                                  ( (packet->raw[j+1] & ~LCD14_SYNC_MASK) );
101         }
102
103         /* Now extract the flags */
104         data->flags = ( (packet->raw[0] & ~LCD14_SYNC_MASK) << 20) |
105                       ( (packet->raw[9] & ~LCD14_SYNC_MASK) << 16) |
106                       ( (packet->raw[10]& ~LCD14_SYNC_MASK) << 12) |
107                       ( (packet->raw[11]& ~LCD14_SYNC_MASK) <<  8) |
108                       ( (packet->raw[12]& ~LCD14_SYNC_MASK) <<  4) |
109                       ( (packet->raw[13]& ~LCD14_SYNC_MASK) );
110 }
111
112
113 /* Since the DMM does not identify itslef in any way shape, or form, we really
114  * don't know for sure who is sending the data. We must use every possible
115  * check to filter out bad packets, especially since detection mechanism depends
116  * on how well we can filter out bad packets packets */
117 SR_PRIV gboolean lcd14_is_packet_valid(const lcd14_packet *packet,
118                                        lcd14_data *data)
119 {
120         /* Callers not interested in the data, pass NULL */
121         lcd14_data placeholder;
122         if(data == NULL)
123                 data = &placeholder;
124         /* We start with our syncronization nibbles, then move to more advanced
125          * checks */
126         if(!lcd14_is_sync_valid(packet))
127                 return FALSE;
128
129         lcd14_cook_raw(packet, data);
130
131         if(!lcd14_is_selection_good(data))
132                 return FALSE;
133
134         /* Made it here, huh? Then this looks to be a valid packet */
135         return TRUE;
136 }
137
138 static uint8_t lcd14_to_digit(uint8_t raw_digit)
139 {
140         /* Take out the decimal point, so we can use a simple switch() */
141         raw_digit &= ~LCD14_DP_MASK;
142         switch(raw_digit)
143         {
144         case 0x00:
145         case LCD14_LCD_0:
146                 return 0;
147         case LCD14_LCD_1:
148                 return 1;
149         case LCD14_LCD_2:
150                 return 2;
151         case LCD14_LCD_3:
152                 return 3;
153         case LCD14_LCD_4:
154                 return 4;
155         case LCD14_LCD_5:
156                 return 5;
157         case LCD14_LCD_6:
158                 return 6;
159         case LCD14_LCD_7:
160                 return 7;
161         case LCD14_LCD_8:
162                 return 8;
163         case LCD14_LCD_9:
164                 return 9;
165         default:
166                 return LCD14_LCD_INVALID;
167         }
168 }
169
170 static double lcdraw_to_double(lcd14_data *data)
171 {
172         /* *********************************************************************
173          * Get a raw floating point value from the data
174          **********************************************************************/
175         double rawval;
176         double multiplier = 1;
177         uint8_t digit;
178         gboolean dp_reached = FALSE;
179         int i;
180
181         /* We have 4 digits, and we start from the most significant */
182         for(i = 0; i < 4; i++)
183         {
184                 uint8_t raw_digit = data->digit[i];
185                 digit = lcd14_to_digit(raw_digit);
186                 if(digit == LCD14_LCD_INVALID) {
187                         rawval = NAN;
188                         break;
189                 }
190                 /* Digit 1 does not have a decimal point. Instead, the decimal
191                  * point is used to indicate MAX, so we must avoid testing it */
192                 if( (i > 0) && (raw_digit & LCD14_DP_MASK) )
193                         dp_reached = TRUE;
194                 if(dp_reached) multiplier /= 10;
195                 rawval = rawval * 10 + digit;
196         }
197         rawval *= multiplier;
198         if(data->digit[0] & LCD14_D0_NEG)
199                 rawval *= -1;
200
201         /* See if we need to multiply our raw value by anything */
202         if(data->flags & LCD14_NANO) {
203                 rawval *= 1E-9;
204         } else if(data->flags & LCD14_MICRO) {
205                 rawval *= 1E-6;
206         } else if(data->flags & LCD14_MILLI) {
207                 rawval *= 1E-3;
208         } else if(data->flags & LCD14_KILO) {
209                 rawval *= 1E3;
210         } else if(data->flags & LCD14_MEGA) {
211                 rawval *= 1E6;
212         }
213
214         return rawval;
215 }
216
217 static void lcd14_handle_packet(lcd14_data *data, struct dev_context *devc)
218 {
219         double rawval = lcdraw_to_double(data);
220         /* *********************************************************************
221          * Now see what the value means, and pass that on
222          **********************************************************************/
223         struct sr_datafeed_packet packet;
224         struct sr_datafeed_analog *analog;
225
226         if( !(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))) ) {
227                 sr_err("failed to malloc packet");
228                 return;
229         }
230         analog->num_samples = 1;
231         if( !(analog->data = g_try_malloc(sizeof(float))) ) {
232                 sr_err("failed to malloc data");
233                 g_free(analog);
234                 return;
235         }
236         *analog->data = (float)rawval;
237         analog->mq = -1;
238
239         /* What does the data mean ? */
240         if(data->flags & LCD14_VOLT) {
241                 analog->mq = SR_MQ_VOLTAGE;
242                 analog->unit = SR_UNIT_VOLT;
243                 if(data->flags & LCD14_AC)
244                         analog->mqflags |= SR_MQFLAG_AC;
245                 else
246                         analog->mqflags |= SR_MQFLAG_DC;
247         }
248         else if(data->flags & LCD14_AMP) {
249                 analog->mq = SR_MQ_CURRENT;
250                 analog->unit = SR_UNIT_AMPERE;
251                 if(data->flags & LCD14_AC)
252                         analog->mqflags |= SR_MQFLAG_AC;
253                 else
254                         analog->mqflags |= SR_MQFLAG_DC;
255         }
256         else if(data->flags & LCD14_OHM) {
257                 if(data->flags & LCD14_BEEP)
258                         analog->mq = SR_MQ_CONTINUITY;
259                 else
260                         analog->mq = SR_MQ_RESISTANCE;
261                 if(!isnan(rawval) )
262                         analog->unit = SR_UNIT_OHM;
263                 else {
264                         analog->unit = SR_UNIT_BOOLEAN;
265                         *analog->data = FALSE;
266                 }
267         }
268         else if(data->flags & LCD14_FARAD) {
269                 analog->mq = SR_MQ_CAPACITANCE;
270                 analog->unit = SR_UNIT_FARAD;
271         }
272         else if(data->flags & LCD14_CELSIUS) {
273                 analog->mq = SR_MQ_TEMPERATURE;
274                 /* No Kelvin or Fahrenheit from the deive, just Celsius */
275                 analog->unit = SR_UNIT_CELSIUS;
276         }
277         else if(data->flags & LCD14_HZ) {
278                 analog->mq = SR_MQ_FREQUENCY;
279                 analog->unit = SR_UNIT_HERTZ;
280         }
281         else if(data->flags & LCD14_DUTY) {
282                 analog->mq = SR_MQ_DUTY_CYCLE;
283                 analog->unit = SR_UNIT_PERCENTAGE;
284         }
285         else if(data->flags & LCD14_HFE) {
286                 analog->mq = SR_MQ_GAIN;
287                 analog->unit = SR_UNIT_UNITLESS;
288         }
289         else if(data->flags & LCD14_DIODE) {
290                 analog->mq = SR_MQ_VOLTAGE;
291                 analog->unit = SR_UNIT_VOLT;
292                 analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
293         }
294         else {
295                 sr_warn("unable to identify measurement mode");
296         }
297
298         /* What other flags are associated with the data? */
299         if(data->flags & LCD14_HOLD) {
300                 analog->mqflags |= SR_MQFLAG_HOLD;
301         }
302         if(data->flags & LCD14_AUTO) {
303                 analog->mqflags |= SR_MQFLAG_AUTORANGE;
304         }
305         if(data->flags & LCD14_REL) {
306                 analog->mqflags |= SR_MQFLAG_RELATIVE;
307         }
308
309         if (analog->mq != -1) {
310                 /* Got a measurement. */
311                 sr_spew("val %f", rawval);
312                 packet.type = SR_DF_ANALOG;
313                 packet.payload = analog;
314                 sr_session_send(devc->cb_data, &packet);
315                 devc->num_samples++;
316         }
317         g_free(analog->data);
318         g_free(analog);
319 }
320
321 static void handle_new_data(struct dev_context *devc, int fd)
322 {
323         int len;
324         size_t i;
325         size_t offset = 0;
326         /* Try to get as much data as the buffer can hold */
327         len = DMM_BUFSIZE - devc->buflen;
328         len = serial_read(fd, devc->buf + devc->buflen, len);
329         if (len < 1) {
330                 sr_err("serial port read error!");
331                 return;
332         }
333         devc->buflen += len;
334
335         /* Now look for packets in that data */
336         while((devc->buflen - offset) >= LCD14_PACKET_SIZE)
337         {
338                 lcd14_packet * packet = (void *)(devc->buf + offset);
339                 lcd14_data data;
340                 if( lcd14_is_packet_valid(packet, &data) )
341                 {
342                         lcd14_handle_packet(&data, devc);
343                         offset += LCD14_PACKET_SIZE;
344                 } else {
345                         offset++;
346                 }
347         }
348
349         /* If we have any data left, move it to the beginning of our buffer */
350         for(i = 0; i < devc->buflen - offset; i++)
351                 devc->buf[i] = devc->buf[offset + i];
352         devc->buflen -= offset;
353 }
354
355 SR_PRIV int lcd14_receive_data(int fd, int revents, void *cb_data)
356 {
357         const struct sr_dev_inst *sdi;
358         struct dev_context *devc;
359
360         if (!(sdi = cb_data))
361                 return TRUE;
362
363         if (!(devc = sdi->priv))
364                 return TRUE;
365
366         if (revents == G_IO_IN)
367         {
368                 /* Serial data arrived. */
369                 handle_new_data(devc, fd);
370         }
371
372         if (devc->num_samples >= devc->limit_samples) {
373                 sdi->driver->dev_acquisition_stop(sdi, cb_data);
374                 return TRUE;
375         }
376
377         return TRUE;
378 }