]>
Commit | Line | Data |
---|---|---|
7dc55d93 AG |
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> | |
7dc55d93 AG |
21 | #include <stdlib.h> |
22 | #include <math.h> | |
23 | #include <string.h> | |
24 | #include <errno.h> | |
bbabddbd UH |
25 | #include "libsigrok.h" |
26 | #include "libsigrok-internal.h" | |
27 | #include "protocol.h" | |
7dc55d93 | 28 | |
bbabddbd | 29 | static gboolean lcd14_is_sync_valid(const struct lcd14_packet *packet) |
7dc55d93 | 30 | { |
bbabddbd UH |
31 | int i; |
32 | uint8_t sync; | |
33 | ||
34 | /* Check the syncronization nibbles, and make sure they all match. */ | |
35 | for (i = 0; i < LCD14_PACKET_SIZE; i++) { | |
36 | sync = (packet->raw[i] & LCD14_SYNC_MASK) >> 4; | |
37 | if (sync != (i + 1)) | |
7dc55d93 AG |
38 | return FALSE; |
39 | } | |
40 | return TRUE; | |
41 | } | |
42 | ||
bbabddbd | 43 | static gboolean lcd14_is_selection_good(const struct lcd14_data *data) |
7dc55d93 | 44 | { |
bbabddbd UH |
45 | int n_postfix = 0, n_type = 0; |
46 | ||
47 | /* Does the packet have more than one multiplier? */ | |
48 | if (data->flags & LCD14_NANO) | |
7dc55d93 | 49 | n_postfix++; |
bbabddbd | 50 | if (data->flags & LCD14_MICRO) |
7dc55d93 | 51 | n_postfix++; |
bbabddbd | 52 | if (data->flags & LCD14_MILLI) |
7dc55d93 | 53 | n_postfix++; |
bbabddbd | 54 | if (data->flags & LCD14_KILO) |
7dc55d93 | 55 | n_postfix++; |
bbabddbd | 56 | if (data->flags & LCD14_MEGA) |
7dc55d93 AG |
57 | n_postfix++; |
58 | ||
bbabddbd | 59 | if (n_postfix > 1) |
7dc55d93 AG |
60 | return FALSE; |
61 | ||
bbabddbd UH |
62 | /* Does the packet "measure" more than one type of value? */ |
63 | if (data->flags & LCD14_HZ) | |
7dc55d93 | 64 | n_type++; |
bbabddbd | 65 | if (data->flags & LCD14_OHM) |
7dc55d93 | 66 | n_type++; |
bbabddbd | 67 | if (data->flags & LCD14_FARAD) |
7dc55d93 | 68 | n_type++; |
bbabddbd | 69 | if (data->flags & LCD14_AMP) |
7dc55d93 | 70 | n_type++; |
bbabddbd | 71 | if (data->flags & LCD14_VOLT) |
7dc55d93 | 72 | n_type++; |
bbabddbd | 73 | if (data->flags & LCD14_DUTY) |
7dc55d93 | 74 | n_type++; |
bbabddbd | 75 | if (data->flags & LCD14_CELSIUS) |
7dc55d93 | 76 | n_type++; |
bbabddbd UH |
77 | /* Do not test for hFE. hFE is not implemented and always '1'. */ |
78 | if (n_type > 1) | |
7dc55d93 AG |
79 | return FALSE; |
80 | ||
bbabddbd UH |
81 | /* Both AC and DC? */ |
82 | if ((data->flags & LCD14_AC) && (data->flags & LCD14_DC)) | |
7dc55d93 AG |
83 | return FALSE; |
84 | ||
bbabddbd | 85 | /* OK, no duplicates. */ |
7dc55d93 AG |
86 | return TRUE; |
87 | } | |
88 | ||
bbabddbd UH |
89 | /* We "cook" a raw lcd14_pcaket into a more pallatable form, lcd14_data. */ |
90 | static void lcd14_cook_raw(const struct lcd14_packet *packet, | |
91 | struct lcd14_data *data) | |
7dc55d93 | 92 | { |
bbabddbd UH |
93 | int i, j; |
94 | ||
95 | /* Get the digits out. */ | |
96 | for (i = 0; i < 4; i++) { | |
97 | j = (i << 1) + 1; | |
98 | data->digit[i] = ((packet->raw[j] & ~LCD14_SYNC_MASK) << 4) | | |
99 | ((packet->raw[j + 1] & ~LCD14_SYNC_MASK)); | |
7dc55d93 AG |
100 | } |
101 | ||
bbabddbd UH |
102 | /* Now extract the flags. */ |
103 | data->flags = ((packet->raw[0] & ~LCD14_SYNC_MASK) << 20) | | |
104 | ((packet->raw[9] & ~LCD14_SYNC_MASK) << 16) | | |
105 | ((packet->raw[10] & ~LCD14_SYNC_MASK) << 12) | | |
106 | ((packet->raw[11] & ~LCD14_SYNC_MASK) << 8) | | |
107 | ((packet->raw[12] & ~LCD14_SYNC_MASK) << 4) | | |
108 | ((packet->raw[13] & ~LCD14_SYNC_MASK)); | |
7dc55d93 AG |
109 | } |
110 | ||
bbabddbd UH |
111 | /* |
112 | * Since the DMM does not identify itself in any way shape, or form, we really | |
7dc55d93 | 113 | * don't know for sure who is sending the data. We must use every possible |
bbabddbd UH |
114 | * check to filter out bad packets, especially since the detection mechanism |
115 | * depends on how well we can filter out bad packets packets. | |
116 | */ | |
117 | SR_PRIV gboolean lcd14_is_packet_valid(const struct lcd14_packet *packet, | |
118 | struct lcd14_data *data) | |
7dc55d93 | 119 | { |
bbabddbd UH |
120 | struct lcd14_data placeholder; |
121 | ||
122 | /* Callers not interested in the data, pass NULL. */ | |
123 | if (data == NULL) | |
7dc55d93 | 124 | data = &placeholder; |
bbabddbd UH |
125 | |
126 | if (!lcd14_is_sync_valid(packet)) | |
7dc55d93 AG |
127 | return FALSE; |
128 | ||
129 | lcd14_cook_raw(packet, data); | |
130 | ||
bbabddbd | 131 | if (!lcd14_is_selection_good(data)) |
7dc55d93 AG |
132 | return FALSE; |
133 | ||
bbabddbd | 134 | /* If we made it here, this looks to be a valid packet. */ |
7dc55d93 AG |
135 | return TRUE; |
136 | } | |
137 | ||
138 | static uint8_t lcd14_to_digit(uint8_t raw_digit) | |
139 | { | |
bbabddbd | 140 | /* Take out the decimal point, so we can use a simple switch(). */ |
7dc55d93 | 141 | raw_digit &= ~LCD14_DP_MASK; |
bbabddbd UH |
142 | |
143 | switch (raw_digit) { | |
7dc55d93 AG |
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 | ||
bbabddbd UH |
170 | /* Get a raw floating point value from the data. */ |
171 | static double lcdraw_to_double(struct lcd14_data *data) | |
7dc55d93 | 172 | { |
7dc55d93 AG |
173 | double rawval; |
174 | double multiplier = 1; | |
bbabddbd | 175 | uint8_t digit, raw_digit; |
7dc55d93 AG |
176 | gboolean dp_reached = FALSE; |
177 | int i; | |
178 | ||
bbabddbd UH |
179 | /* We have 4 digits, and we start from the most significant. */ |
180 | for (i = 0; i < 4; i++) { | |
181 | raw_digit = data->digit[i]; | |
7dc55d93 | 182 | digit = lcd14_to_digit(raw_digit); |
bbabddbd | 183 | if (digit == LCD14_LCD_INVALID) { |
7dc55d93 AG |
184 | rawval = NAN; |
185 | break; | |
186 | } | |
bbabddbd UH |
187 | |
188 | /* | |
189 | * Digit 1 does not have a decimal point. Instead, the decimal | |
190 | * point is used to indicate MAX, so we must avoid testing it. | |
191 | */ | |
192 | if ((i > 0) && (raw_digit & LCD14_DP_MASK)) | |
7dc55d93 | 193 | dp_reached = TRUE; |
bbabddbd UH |
194 | if (dp_reached) |
195 | multiplier /= 10; | |
7dc55d93 AG |
196 | rawval = rawval * 10 + digit; |
197 | } | |
198 | rawval *= multiplier; | |
bbabddbd | 199 | if (data->digit[0] & LCD14_D0_NEG) |
7dc55d93 AG |
200 | rawval *= -1; |
201 | ||
bbabddbd UH |
202 | /* See if we need to multiply our raw value by anything. */ |
203 | if (data->flags & LCD14_NANO) | |
7dc55d93 | 204 | rawval *= 1E-9; |
bbabddbd | 205 | else if (data->flags & LCD14_MICRO) |
7dc55d93 | 206 | rawval *= 1E-6; |
bbabddbd | 207 | else if (data->flags & LCD14_MILLI) |
7dc55d93 | 208 | rawval *= 1E-3; |
bbabddbd | 209 | else if (data->flags & LCD14_KILO) |
7dc55d93 | 210 | rawval *= 1E3; |
bbabddbd | 211 | else if (data->flags & LCD14_MEGA) |
7dc55d93 | 212 | rawval *= 1E6; |
7dc55d93 AG |
213 | |
214 | return rawval; | |
215 | } | |
216 | ||
bbabddbd UH |
217 | /* Now see what the value means, and pass that on. */ |
218 | static void lcd14_handle_packet(struct lcd14_data *data, | |
219 | struct dev_context *devc) | |
7dc55d93 | 220 | { |
bbabddbd | 221 | double rawval; |
7dc55d93 AG |
222 | struct sr_datafeed_packet packet; |
223 | struct sr_datafeed_analog *analog; | |
224 | ||
bbabddbd UH |
225 | if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)))) { |
226 | sr_err("Failed to malloc packet."); | |
7dc55d93 AG |
227 | return; |
228 | } | |
bbabddbd UH |
229 | |
230 | if (!(analog->data = g_try_malloc(sizeof(float)))) { | |
231 | sr_err("Failed to malloc data."); | |
7dc55d93 AG |
232 | g_free(analog); |
233 | return; | |
234 | } | |
bbabddbd UH |
235 | |
236 | rawval = lcdraw_to_double(data); | |
237 | ||
238 | analog->num_samples = 1; | |
7dc55d93 | 239 | *analog->data = (float)rawval; |
bbabddbd | 240 | |
7dc55d93 AG |
241 | analog->mq = -1; |
242 | ||
bbabddbd UH |
243 | /* What does the data mean? */ |
244 | if (data->flags & LCD14_VOLT) { | |
7dc55d93 AG |
245 | analog->mq = SR_MQ_VOLTAGE; |
246 | analog->unit = SR_UNIT_VOLT; | |
bbabddbd | 247 | if (data->flags & LCD14_AC) |
7dc55d93 AG |
248 | analog->mqflags |= SR_MQFLAG_AC; |
249 | else | |
250 | analog->mqflags |= SR_MQFLAG_DC; | |
bbabddbd | 251 | } else if (data->flags & LCD14_AMP) { |
7dc55d93 AG |
252 | analog->mq = SR_MQ_CURRENT; |
253 | analog->unit = SR_UNIT_AMPERE; | |
bbabddbd | 254 | if (data->flags & LCD14_AC) |
7dc55d93 AG |
255 | analog->mqflags |= SR_MQFLAG_AC; |
256 | else | |
257 | analog->mqflags |= SR_MQFLAG_DC; | |
bbabddbd UH |
258 | } else if (data->flags & LCD14_OHM) { |
259 | if (data->flags & LCD14_BEEP) | |
7dc55d93 AG |
260 | analog->mq = SR_MQ_CONTINUITY; |
261 | else | |
262 | analog->mq = SR_MQ_RESISTANCE; | |
bbabddbd | 263 | if (!isnan(rawval)) |
7dc55d93 AG |
264 | analog->unit = SR_UNIT_OHM; |
265 | else { | |
266 | analog->unit = SR_UNIT_BOOLEAN; | |
267 | *analog->data = FALSE; | |
268 | } | |
bbabddbd | 269 | } else if (data->flags & LCD14_FARAD) { |
7dc55d93 AG |
270 | analog->mq = SR_MQ_CAPACITANCE; |
271 | analog->unit = SR_UNIT_FARAD; | |
bbabddbd | 272 | } else if (data->flags & LCD14_CELSIUS) { |
7dc55d93 | 273 | analog->mq = SR_MQ_TEMPERATURE; |
bbabddbd | 274 | /* No Kelvin or Fahrenheit from the device, just Celsius. */ |
7dc55d93 | 275 | analog->unit = SR_UNIT_CELSIUS; |
bbabddbd | 276 | } else if (data->flags & LCD14_HZ) { |
7dc55d93 AG |
277 | analog->mq = SR_MQ_FREQUENCY; |
278 | analog->unit = SR_UNIT_HERTZ; | |
bbabddbd | 279 | } else if (data->flags & LCD14_DUTY) { |
7dc55d93 AG |
280 | analog->mq = SR_MQ_DUTY_CYCLE; |
281 | analog->unit = SR_UNIT_PERCENTAGE; | |
bbabddbd | 282 | } else if (data->flags & LCD14_HFE) { |
7dc55d93 AG |
283 | analog->mq = SR_MQ_GAIN; |
284 | analog->unit = SR_UNIT_UNITLESS; | |
bbabddbd | 285 | } else if (data->flags & LCD14_DIODE) { |
7dc55d93 AG |
286 | analog->mq = SR_MQ_VOLTAGE; |
287 | analog->unit = SR_UNIT_VOLT; | |
288 | analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC; | |
bbabddbd UH |
289 | } else { |
290 | sr_warn("Unable to identify measurement mode."); | |
7dc55d93 AG |
291 | } |
292 | ||
293 | /* What other flags are associated with the data? */ | |
bbabddbd | 294 | if (data->flags & LCD14_HOLD) |
7dc55d93 | 295 | analog->mqflags |= SR_MQFLAG_HOLD; |
bbabddbd | 296 | if (data->flags & LCD14_AUTO) |
7dc55d93 | 297 | analog->mqflags |= SR_MQFLAG_AUTORANGE; |
bbabddbd | 298 | if (data->flags & LCD14_REL) |
7dc55d93 | 299 | analog->mqflags |= SR_MQFLAG_RELATIVE; |
7dc55d93 AG |
300 | |
301 | if (analog->mq != -1) { | |
302 | /* Got a measurement. */ | |
bbabddbd | 303 | sr_spew("Measurement value is %f.", rawval); |
7dc55d93 AG |
304 | packet.type = SR_DF_ANALOG; |
305 | packet.payload = analog; | |
306 | sr_session_send(devc->cb_data, &packet); | |
307 | devc->num_samples++; | |
308 | } | |
bbabddbd | 309 | |
7dc55d93 AG |
310 | g_free(analog->data); |
311 | g_free(analog); | |
312 | } | |
313 | ||
314 | static void handle_new_data(struct dev_context *devc, int fd) | |
315 | { | |
bbabddbd UH |
316 | int len, i, offset = 0; |
317 | struct lcd14_packet *packet; | |
318 | struct lcd14_data data; | |
319 | ||
320 | /* Try to get as much data as the buffer can hold. */ | |
7dc55d93 AG |
321 | len = DMM_BUFSIZE - devc->buflen; |
322 | len = serial_read(fd, devc->buf + devc->buflen, len); | |
323 | if (len < 1) { | |
bbabddbd | 324 | sr_err("Serial port read error: %d.", len); |
7dc55d93 AG |
325 | return; |
326 | } | |
327 | devc->buflen += len; | |
328 | ||
bbabddbd UH |
329 | /* Now look for packets in that data. */ |
330 | while ((devc->buflen - offset) >= LCD14_PACKET_SIZE) { | |
331 | packet = (void *)(devc->buf + offset); | |
332 | if (lcd14_is_packet_valid(packet, &data)) { | |
7dc55d93 AG |
333 | lcd14_handle_packet(&data, devc); |
334 | offset += LCD14_PACKET_SIZE; | |
335 | } else { | |
336 | offset++; | |
337 | } | |
338 | } | |
339 | ||
bbabddbd UH |
340 | /* If we have any data left, move it to the beginning of our buffer. */ |
341 | for (i = 0; i < devc->buflen - offset; i++) | |
7dc55d93 AG |
342 | devc->buf[i] = devc->buf[offset + i]; |
343 | devc->buflen -= offset; | |
344 | } | |
345 | ||
bbabddbd | 346 | SR_PRIV int tekpower_dmm_receive_data(int fd, int revents, void *cb_data) |
7dc55d93 AG |
347 | { |
348 | const struct sr_dev_inst *sdi; | |
349 | struct dev_context *devc; | |
350 | ||
351 | if (!(sdi = cb_data)) | |
352 | return TRUE; | |
353 | ||
354 | if (!(devc = sdi->priv)) | |
355 | return TRUE; | |
356 | ||
bbabddbd | 357 | if (revents == G_IO_IN) { |
7dc55d93 AG |
358 | /* Serial data arrived. */ |
359 | handle_new_data(devc, fd); | |
360 | } | |
361 | ||
362 | if (devc->num_samples >= devc->limit_samples) { | |
363 | sdi->driver->dev_acquisition_stop(sdi, cb_data); | |
364 | return TRUE; | |
365 | } | |
366 | ||
367 | return TRUE; | |
368 | } |