]>
Commit | Line | Data |
---|---|---|
d375b3c3 AG |
1 | /* |
2 | * This file is part of the sigrok project. | |
3 | * | |
4 | * Copyright (C) 2012 Bert Vermeulen <bert@biot.com> | |
5 | * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <glib.h> | |
22 | #include "libsigrok.h" | |
23 | #include "libsigrok-internal.h" | |
d375b3c3 AG |
24 | #include "radioshack-dmm.h" |
25 | #include <stdlib.h> | |
26 | #include <math.h> | |
27 | #include <string.h> | |
28 | #include <errno.h> | |
29 | ||
d375b3c3 AG |
30 | static gboolean rs_22_812_is_checksum_valid(const rs_22_812_packet *data) |
31 | { | |
32 | uint8_t *raw = (void *) data; | |
33 | uint8_t sum = 0; | |
34 | size_t i; | |
35 | for(i = 0; i < RS_22_812_PACKET_SIZE - 1; i++) | |
36 | sum += raw[i]; | |
37 | /* This is just a funky constant added to the checksum */ | |
38 | sum += 57; | |
39 | sum -= data->checksum; | |
40 | return(sum == 0); | |
41 | } | |
42 | ||
43 | static gboolean rs_22_812_is_mode_valid(rs_22_812_mode mode) | |
44 | { | |
45 | return(mode < RS_22_812_MODE_INVALID); | |
46 | } | |
47 | ||
48 | static gboolean rs_22_812_is_selection_good(const rs_22_812_packet *data) | |
49 | { | |
50 | int n_postfix = 0; | |
51 | int n_type = 0; | |
52 | /* Does the packet have more than one multiplier ? */ | |
53 | if(data->indicatrix1 & RS_22_812_IND1_KILO) | |
54 | n_postfix++; | |
55 | if(data->indicatrix1 & RS_22_812_IND1_MEGA) | |
56 | n_postfix++; | |
57 | if(data->indicatrix1 & RS_22_812_IND1_MILI) | |
58 | n_postfix++; | |
59 | if(data->indicatrix2 & RS_22_812_IND2_MICRO) | |
60 | n_postfix++; | |
61 | if(data->indicatrix2 & RS_22_812_IND2_NANO) | |
62 | n_postfix++; | |
63 | if(n_postfix > 1) | |
64 | return FALSE; | |
65 | ||
66 | /* Does the packet "measure" more than one type of value ?*/ | |
67 | if(data->indicatrix1 & RS_22_812_IND1_HZ) | |
68 | n_type++; | |
69 | if(data->indicatrix1 & RS_22_812_IND1_OHM) | |
70 | n_type++; | |
71 | if(data->indicatrix1 & RS_22_812_IND1_FARAD) | |
72 | n_type++; | |
73 | if(data->indicatrix1 & RS_22_812_IND1_AMP) | |
74 | n_type++; | |
75 | if(data->indicatrix1 & RS_22_812_IND1_VOLT) | |
76 | n_type++; | |
77 | if(data->indicatrix2 & RS_22_812_IND2_DBM) | |
78 | n_type++; | |
79 | if(data->indicatrix2 & RS_22_812_IND2_SEC) | |
80 | n_type++; | |
81 | if(data->indicatrix2 & RS_22_812_IND2_DUTY) | |
82 | n_type++; | |
83 | if(data->indicatrix2 & RS_22_812_IND2_HFE) | |
84 | n_type++; | |
85 | if(n_type > 1) | |
86 | return FALSE; | |
87 | ||
88 | /* OK, no duplicates */ | |
89 | return TRUE; | |
90 | } | |
91 | ||
92 | /* Since the RS 22-812 does not identify itslef in any way shape, or form, | |
93 | * we really don't know for sure who is sending the data. We must use every | |
94 | * possible check to filter out bad packets, especially since detection of the | |
95 | * 22-812 depends on how well we can filter the packets */ | |
96 | SR_PRIV gboolean rs_22_812_is_packet_valid(const rs_22_812_packet *packet) | |
97 | { | |
98 | /* Unfortunately, the packet doesn't have a signature, so we must | |
99 | * compute its checksum first */ | |
100 | if(!rs_22_812_is_checksum_valid(packet)) | |
101 | return FALSE; | |
102 | ||
103 | if(!rs_22_812_is_mode_valid(packet->mode)) | |
104 | return FALSE; | |
105 | ||
106 | if(!rs_22_812_is_selection_good(packet)) { | |
107 | return FALSE; | |
108 | } | |
109 | /* Made it here, huh? Then this looks to be a valid packet */ | |
110 | return TRUE; | |
111 | } | |
112 | ||
113 | static uint8_t rs_22_812_to_digit(uint8_t raw_digit) | |
114 | { | |
115 | /* Take out the decimal point, so we can use a simple switch() */ | |
116 | raw_digit &= ~RS_22_812_DP_MASK; | |
117 | switch(raw_digit) | |
118 | { | |
119 | case 0x00: | |
120 | case RS_22_812_LCD_0: | |
121 | return 0; | |
122 | case RS_22_812_LCD_1: | |
123 | return 1; | |
124 | case RS_22_812_LCD_2: | |
125 | return 2; | |
126 | case RS_22_812_LCD_3: | |
127 | return 3; | |
128 | case RS_22_812_LCD_4: | |
129 | return 4; | |
130 | case RS_22_812_LCD_5: | |
131 | return 5; | |
132 | case RS_22_812_LCD_6: | |
133 | return 6; | |
134 | case RS_22_812_LCD_7: | |
135 | return 7; | |
136 | case RS_22_812_LCD_8: | |
137 | return 8; | |
138 | case RS_22_812_LCD_9: | |
139 | return 9; | |
140 | default: | |
141 | return 0xff; | |
142 | } | |
143 | } | |
144 | ||
145 | typedef enum { | |
146 | READ_ALL, | |
147 | READ_TEMP, | |
148 | } value_type; | |
149 | ||
150 | static double lcdraw_to_double(rs_22_812_packet *rs_packet, value_type type) | |
151 | { | |
152 | /* ********************************************************************* | |
153 | * Get a raw floating point value from the data | |
154 | **********************************************************************/ | |
155 | double rawval; | |
156 | double multiplier = 1; | |
157 | uint8_t digit; | |
158 | gboolean dp_reached = FALSE; | |
159 | int i, end; | |
160 | switch(type) { | |
161 | case READ_TEMP: | |
162 | /* Do not parse the last digit */ | |
163 | end = 1; | |
164 | break; | |
165 | case READ_ALL: | |
166 | default: | |
167 | /* Parse all digits */ | |
168 | end = 0; | |
169 | } | |
170 | /* We have 4 digits, and we start from the most significant */ | |
171 | for(i = 3; i >= end; i--) | |
172 | { | |
173 | uint8_t raw_digit = *(&(rs_packet->digit4) + i); | |
174 | digit = rs_22_812_to_digit(raw_digit); | |
175 | if(digit == 0xff) { | |
176 | rawval = NAN; | |
177 | break; | |
178 | } | |
179 | /* Digit 1 does not have a decimal point. Instead, the decimal | |
180 | * point is used to indicate MAX, so we must avoid testing it */ | |
181 | if( (i < 3) && (raw_digit & RS_22_812_DP_MASK) ) | |
182 | dp_reached = TRUE; | |
183 | if(dp_reached) multiplier /= 10; | |
184 | rawval = rawval * 10 + digit; | |
185 | } | |
186 | rawval *= multiplier; | |
187 | if(rs_packet->info & RS_22_812_INFO_NEG) | |
188 | rawval *= -1; | |
189 | ||
190 | /* See if we need to multiply our raw value by anything */ | |
191 | if(rs_packet->indicatrix1 & RS_22_812_IND2_NANO) { | |
192 | rawval *= 1E-9; | |
193 | } else if(rs_packet->indicatrix2 & RS_22_812_IND2_MICRO) { | |
194 | rawval *= 1E-6; | |
195 | } else if(rs_packet->indicatrix1 & RS_22_812_IND1_MILI) { | |
196 | rawval *= 1E-3; | |
197 | } else if(rs_packet->indicatrix1 & RS_22_812_IND1_KILO) { | |
198 | rawval *= 1E3; | |
199 | } else if(rs_packet->indicatrix1 & RS_22_812_IND1_MEGA) { | |
200 | rawval *= 1E6; | |
201 | } | |
202 | ||
203 | return rawval; | |
204 | } | |
205 | ||
206 | static gboolean rs_22_812_is_celsius(rs_22_812_packet *rs_packet) | |
207 | { | |
208 | return((rs_packet->digit4 & ~RS_22_812_DP_MASK) == RS_22_812_LCD_C); | |
209 | } | |
210 | ||
211 | static gboolean rs_22_812_is_shortcirc(rs_22_812_packet *rs_packet) | |
212 | { | |
213 | return((rs_packet->digit2 & ~RS_22_812_DP_MASK) == RS_22_812_LCD_h); | |
214 | } | |
215 | ||
7c41dc47 AG |
216 | static gboolean rs_22_812_is_logic_high(rs_22_812_packet *rs_packet) |
217 | { | |
218 | sr_spew("digit 2: %x", rs_packet->digit2 & ~RS_22_812_DP_MASK); | |
219 | return((rs_packet->digit2 & ~RS_22_812_DP_MASK) == RS_22_812_LCD_H); | |
220 | } | |
221 | ||
d375b3c3 AG |
222 | static void rs_22_812_handle_packet(rs_22_812_packet *rs_packet, |
223 | rs_dev_ctx *devc) | |
224 | { | |
225 | double rawval = lcdraw_to_double(rs_packet, READ_ALL); | |
226 | /* ********************************************************************* | |
227 | * Now see what the value means, and pass that on | |
228 | **********************************************************************/ | |
229 | struct sr_datafeed_packet packet; | |
230 | struct sr_datafeed_analog *analog; | |
231 | ||
886a52b6 | 232 | /* TODO: Check malloc return value. */ |
d375b3c3 AG |
233 | analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)); |
234 | analog->num_samples = 1; | |
886a52b6 | 235 | /* TODO: Check malloc return value. */ |
d375b3c3 AG |
236 | analog->data = g_try_malloc(sizeof(float)); |
237 | *analog->data = (float)rawval; | |
238 | analog->mq = -1; | |
239 | ||
240 | switch(rs_packet->mode) { | |
241 | case RS_22_812_MODE_DC_V: | |
242 | analog->mq = SR_MQ_VOLTAGE; | |
243 | analog->unit = SR_UNIT_VOLT; | |
244 | analog->mqflags |= SR_MQFLAG_DC; | |
245 | break; | |
246 | case RS_22_812_MODE_AC_V: | |
247 | analog->mq = SR_MQ_VOLTAGE; | |
248 | analog->unit = SR_UNIT_VOLT; | |
249 | analog->mqflags |= SR_MQFLAG_AC; | |
250 | break; | |
251 | case RS_22_812_MODE_DC_UA: | |
252 | case RS_22_812_MODE_DC_MA: | |
253 | case RS_22_812_MODE_DC_A: | |
254 | analog->mq = SR_MQ_CURRENT; | |
255 | analog->unit = SR_UNIT_AMPERE; | |
256 | analog->mqflags |= SR_MQFLAG_DC; | |
257 | break; | |
258 | case RS_22_812_MODE_AC_UA: | |
259 | case RS_22_812_MODE_AC_MA: | |
260 | case RS_22_812_MODE_AC_A: | |
261 | analog->mq = SR_MQ_CURRENT; | |
262 | analog->unit = SR_UNIT_AMPERE; | |
263 | analog->mqflags |= SR_MQFLAG_AC; | |
264 | break; | |
265 | case RS_22_812_MODE_OHM: | |
266 | analog->mq = SR_MQ_RESISTANCE; | |
267 | analog->unit = SR_UNIT_OHM; | |
268 | break; | |
269 | case RS_22_812_MODE_FARAD: | |
270 | analog->mq = SR_MQ_CAPACITANCE; | |
271 | analog->unit = SR_UNIT_FARAD; | |
272 | break; | |
273 | case RS_22_812_MODE_CONT: | |
274 | analog->mq = SR_MQ_CONTINUITY; | |
275 | analog->unit = SR_UNIT_BOOLEAN; | |
276 | *analog->data = rs_22_812_is_shortcirc(rs_packet); | |
277 | break; | |
278 | case RS_22_812_MODE_DIODE: | |
279 | analog->mq = SR_MQ_VOLTAGE; | |
280 | analog->unit = SR_UNIT_VOLT; | |
281 | analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC; | |
282 | break; | |
283 | case RS_22_812_MODE_HZ: | |
284 | case RS_22_812_MODE_VOLT_HZ: | |
285 | case RS_22_812_MODE_AMP_HZ: | |
286 | analog->mq = SR_MQ_FREQUENCY; | |
287 | analog->unit = SR_UNIT_HERTZ; | |
288 | break; | |
289 | case RS_22_812_MODE_LOGIC: | |
7c41dc47 AG |
290 | /* No matter whether or not we have an actual voltage reading, |
291 | * we are measuring voltage, so we set our MQ as VOLTAGE */ | |
292 | analog->mq = SR_MQ_VOLTAGE; | |
293 | if(!isnan(rawval)) { | |
294 | /* We have an actual voltage */ | |
295 | analog->unit = SR_UNIT_VOLT; | |
296 | } else { | |
297 | /* We have either HI or LOW */ | |
298 | analog->unit = SR_UNIT_BOOLEAN; | |
299 | *analog->data = rs_22_812_is_logic_high(rs_packet); | |
300 | } | |
d375b3c3 AG |
301 | break; |
302 | case RS_22_812_MODE_HFE: | |
303 | analog->mq = SR_MQ_GAIN; | |
304 | analog->unit = SR_UNIT_UNITLESS; | |
305 | break; | |
306 | case RS_22_812_MODE_DUTY: | |
307 | case RS_22_812_MODE_VOLT_DUTY: | |
308 | case RS_22_812_MODE_AMP_DUTY: | |
309 | analog->mq = SR_MQ_DUTY_CYCLE; | |
310 | analog->unit = SR_UNIT_PERCENTAGE; | |
311 | break; | |
312 | case RS_22_812_MODE_WIDTH: | |
313 | case RS_22_812_MODE_VOLT_WIDTH: | |
314 | case RS_22_812_MODE_AMP_WIDTH: | |
315 | analog->mq = SR_MQ_PULSE_WIDTH; | |
316 | analog->unit = SR_UNIT_SECOND; | |
317 | case RS_22_812_MODE_TEMP: | |
318 | analog->mq = SR_MQ_TEMPERATURE; | |
319 | /* We need to reparse */ | |
320 | *analog->data = lcdraw_to_double(rs_packet, READ_TEMP); | |
321 | analog->unit = rs_22_812_is_celsius(rs_packet)? | |
322 | SR_UNIT_CELSIUS:SR_UNIT_FAHRENHEIT; | |
323 | break; | |
324 | case RS_22_812_MODE_DBM: | |
325 | analog->mq = SR_MQ_POWER; | |
326 | analog->unit = SR_UNIT_DECIBEL_MW; | |
327 | analog->mqflags |= SR_MQFLAG_AC; | |
328 | break; | |
329 | default: | |
dccbd0ed | 330 | sr_warn("Unknown mode: %d.", rs_packet->mode); |
d375b3c3 AG |
331 | break; |
332 | } | |
333 | ||
334 | if(rs_packet->info & RS_22_812_INFO_HOLD) { | |
335 | analog->mqflags |= SR_MQFLAG_HOLD; | |
336 | } | |
337 | if(rs_packet->digit4 & RS_22_812_DIG4_MAX) { | |
338 | analog->mqflags |= SR_MQFLAG_MAX; | |
339 | } | |
340 | if(rs_packet->indicatrix2 & RS_22_812_IND2_MIN) { | |
341 | analog->mqflags |= SR_MQFLAG_MIN; | |
342 | } | |
343 | if(rs_packet->info & RS_22_812_INFO_AUTO) { | |
344 | analog->mqflags |= SR_MQFLAG_AUTORANGE; | |
345 | } | |
346 | ||
347 | if (analog->mq != -1) { | |
348 | /* Got a measurement. */ | |
dccbd0ed | 349 | sr_spew("Value: %f.", rawval); |
d375b3c3 AG |
350 | packet.type = SR_DF_ANALOG; |
351 | packet.payload = analog; | |
352 | sr_session_send(devc->cb_data, &packet); | |
353 | devc->num_samples++; | |
354 | } | |
355 | g_free(analog->data); | |
356 | g_free(analog); | |
357 | } | |
358 | ||
359 | static void handle_new_data(rs_dev_ctx *devc, int fd) | |
360 | { | |
361 | int len; | |
362 | size_t i; | |
363 | size_t offset = 0; | |
364 | /* Try to get as much data as the buffer can hold */ | |
365 | len = RS_DMM_BUFSIZE - devc->buflen; | |
366 | len = serial_read(fd, devc->buf + devc->buflen, len); | |
367 | if (len < 1) { | |
dccbd0ed | 368 | sr_err("Serial port read error!"); |
d375b3c3 AG |
369 | return; |
370 | } | |
371 | devc->buflen += len; | |
372 | ||
373 | /* Now look for packets in that data */ | |
374 | while((devc->buflen - offset) >= RS_22_812_PACKET_SIZE) | |
375 | { | |
376 | rs_22_812_packet * packet = (void *)(devc->buf + offset); | |
377 | if( rs_22_812_is_packet_valid(packet) ) | |
378 | { | |
379 | rs_22_812_handle_packet(packet, devc); | |
380 | offset += RS_22_812_PACKET_SIZE; | |
381 | } else { | |
382 | offset++; | |
383 | } | |
384 | } | |
385 | ||
386 | /* If we have any data left, move it to the beginning of our buffer */ | |
387 | for(i = 0; i < devc->buflen - offset; i++) | |
388 | devc->buf[i] = devc->buf[offset + i]; | |
389 | devc->buflen -= offset; | |
390 | } | |
391 | ||
392 | SR_PRIV int radioshack_receive_data(int fd, int revents, void *cb_data) | |
393 | { | |
642e9d62 | 394 | struct sr_dev_inst *sdi; |
d375b3c3 AG |
395 | struct dev_context *devc; |
396 | ||
397 | if (!(sdi = cb_data)) | |
398 | return TRUE; | |
399 | ||
400 | if (!(devc = sdi->priv)) | |
401 | return TRUE; | |
402 | ||
403 | if (revents == G_IO_IN) | |
404 | { | |
405 | /* Serial data arrived. */ | |
406 | handle_new_data(devc, fd); | |
407 | } | |
408 | ||
409 | if (devc->num_samples >= devc->limit_samples) { | |
410 | sdi->driver->dev_acquisition_stop(sdi, cb_data); | |
411 | return TRUE; | |
412 | } | |
413 | ||
414 | return TRUE; | |
415 | } |