]>
Commit | Line | Data |
---|---|---|
9456d636 MS |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de> | |
5 | * Copyright (C) 2018 Matthias Schulz <matthschulz@arcor.de> | |
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 2 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 | /* | |
22 | * Voltcraft 13-bytes ASCII protocol parser. | |
23 | * | |
24 | * Bytes 1-3 measuring mode, byte 4 '-' for negative, | |
25 | * bytes 5-9 value, bytes 10-11 unit, bytes 12-13 CRLF 0d 0a. | |
26 | */ | |
27 | ||
28 | #include <config.h> | |
29 | #include <string.h> | |
30 | #include <strings.h> | |
31 | #include <ctype.h> | |
32 | #include <math.h> | |
33 | #include <glib.h> | |
34 | #include <libsigrok/libsigrok.h> | |
35 | #include "libsigrok-internal.h" | |
36 | ||
37 | #define LOG_PREFIX "vc96" | |
38 | ||
39 | /** Parse value from buf, byte 3-8. */ | |
40 | static int parse_value(const uint8_t *buf, struct vc96_info *info, | |
41 | float *result, int *exponent) | |
42 | { | |
43 | int i, is_ol, cnt, dot_pos; | |
44 | char valstr[8 + 1]; | |
45 | ||
46 | (void)info; | |
47 | ||
48 | /* Strip all spaces from bytes 3-8. */ | |
49 | memset(&valstr, 0, 6 + 1); | |
50 | for (i = 0, cnt = 0; i < 6; i++) { | |
51 | if (buf[3 + i] != ' ') | |
52 | valstr[cnt++] = buf[3 + i]; | |
53 | } | |
54 | ||
55 | /* Bytes 5-7: Over limit (various forms) */ | |
56 | is_ol = 0; | |
57 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0; | |
58 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0; | |
59 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0; | |
60 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL")) ? 1 : 0; | |
61 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0; | |
62 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0; | |
63 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0; | |
64 | is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0; | |
65 | if (is_ol != 0) { | |
66 | sr_spew("Over limit."); | |
67 | *result = INFINITY; | |
68 | return SR_OK; | |
69 | } | |
70 | ||
71 | /* Bytes 3-10: Sign, value (up to 5 digits) and decimal point */ | |
72 | sr_atof_ascii((const char *)&valstr, result); | |
73 | ||
74 | dot_pos = strcspn(valstr, "."); | |
75 | if (dot_pos < cnt) | |
76 | *exponent = -(cnt - dot_pos - 1); | |
77 | else | |
78 | *exponent = 0; | |
79 | ||
80 | sr_spew("The display value is %f.", *result); | |
81 | ||
82 | return SR_OK; | |
83 | } | |
84 | ||
85 | static void parse_flags(const char *buf, struct vc96_info *info) | |
86 | { | |
87 | int i, cnt; | |
88 | char unit[4 + 1]; | |
89 | const char *u; | |
90 | ||
91 | /* Bytes 0-1: Measurement mode AC, DC */ | |
92 | info->is_ac = !strncmp(buf, "AC", 2); | |
93 | info->is_dc = !strncmp(buf, "DC", 2); | |
94 | ||
95 | /* Bytes 0-2: Measurement mode DIO, OHM */ | |
96 | info->is_ohm = !strncmp(buf, "OHM", 3); | |
97 | info->is_diode = !strncmp(buf, "DIO", 3); | |
98 | info->is_hfe = !strncmp(buf, "hfe", 3); | |
99 | ||
100 | /* Bytes 3-8: See parse_value(). */ | |
101 | ||
102 | /* Strip all spaces from bytes 9-10. */ | |
103 | memset(&unit, 0, 2 + 1); | |
104 | for (i = 0, cnt = 0; i < 2; i++) { | |
105 | if (buf[9 + i] != ' ') | |
106 | unit[cnt++] = buf[9 + i]; | |
107 | } | |
108 | sr_spew("Bytes 9..10 without spaces \"%.4s\".", unit); | |
109 | ||
110 | /* Bytes 9-10: Unit */ | |
111 | u = (const char *)&unit; | |
112 | if (!g_ascii_strcasecmp(u, "A")) | |
4a6751fd | 113 | info->is_ampere = TRUE; |
9456d636 | 114 | else if (!g_ascii_strcasecmp(u, "mA")) |
4a6751fd | 115 | info->is_milli = info->is_ampere = TRUE; |
9456d636 | 116 | else if (!g_ascii_strcasecmp(u, "uA")) |
4a6751fd | 117 | info->is_micro = info->is_ampere = TRUE; |
9456d636 | 118 | else if (!g_ascii_strcasecmp(u, "V")) |
4a6751fd | 119 | info->is_volt = TRUE; |
9456d636 | 120 | else if (!g_ascii_strcasecmp(u, "mV")) |
4a6751fd | 121 | info->is_milli = info->is_volt = TRUE; |
9456d636 | 122 | else if (!g_ascii_strcasecmp(u, "K")) |
4a6751fd | 123 | info->is_kilo = TRUE; |
9456d636 | 124 | else if (!g_ascii_strcasecmp(u, "M")) |
4a6751fd | 125 | info->is_mega = TRUE; |
9456d636 | 126 | else if (!g_ascii_strcasecmp(u, "")) |
4a6751fd | 127 | info->is_unitless = TRUE; |
9456d636 MS |
128 | |
129 | /* Bytes 0-2: Measurement mode, except AC/DC */ | |
130 | info->is_resistance = !strncmp(buf, "OHM", 3) || | |
131 | (!strncmp(buf, " ", 3) && info->is_ohm); | |
132 | info->is_diode = !strncmp(buf, "DIO", 3) || | |
133 | (!strncmp(buf, " ", 3) && info->is_volt && info->is_milli); | |
134 | info->is_hfe = !strncmp(buf, "hfe", 3) || | |
135 | (!strncmp(buf, " ", 3) && !info->is_ampere && !info->is_volt && | |
136 | !info->is_resistance && !info->is_diode); | |
137 | ||
138 | /* | |
139 | * Note: | |
140 | * - Protocol doesn't distinguish "resistance" from "beep" mode. | |
141 | */ | |
142 | ||
143 | /* Byte 12: Always '\r' (carriage return, 0x0d, 12) */ | |
144 | /* Byte 13: Always '\n' (carriage return, 0x0a, 13) */ | |
145 | } | |
146 | ||
147 | static void handle_flags(struct sr_datafeed_analog *analog, float *floatval, | |
148 | int *exponent, const struct vc96_info *info) | |
149 | { | |
150 | int factor; | |
151 | ||
152 | (void)exponent; | |
153 | ||
154 | /* Factors */ | |
155 | factor = 0; | |
156 | if (info->is_micro) | |
157 | factor -= 6; | |
158 | if (info->is_milli) | |
159 | factor -= 3; | |
160 | if (info->is_kilo) | |
161 | factor += 3; | |
162 | if (info->is_mega) | |
163 | factor += 6; | |
164 | *floatval *= powf(10, factor); | |
165 | ||
166 | /* Measurement modes */ | |
167 | if (info->is_volt) { | |
168 | analog->meaning->mq = SR_MQ_VOLTAGE; | |
169 | analog->meaning->unit = SR_UNIT_VOLT; | |
170 | } | |
171 | if (info->is_ampere) { | |
172 | analog->meaning->mq = SR_MQ_CURRENT; | |
173 | analog->meaning->unit = SR_UNIT_AMPERE; | |
174 | } | |
175 | if (info->is_ohm) { | |
176 | analog->meaning->mq = SR_MQ_RESISTANCE; | |
177 | analog->meaning->unit = SR_UNIT_OHM; | |
178 | } | |
179 | if (info->is_diode) { | |
180 | analog->meaning->mq = SR_MQ_VOLTAGE; | |
181 | analog->meaning->unit = SR_UNIT_VOLT; | |
182 | } | |
183 | if (info->is_hfe) { | |
184 | analog->meaning->mq = SR_MQ_GAIN; | |
185 | analog->meaning->unit = SR_UNIT_UNITLESS; | |
186 | } | |
187 | ||
188 | /* Measurement related flags */ | |
189 | if (info->is_ac) | |
190 | analog->meaning->mqflags |= SR_MQFLAG_AC; | |
191 | if (info->is_dc) | |
192 | analog->meaning->mqflags |= SR_MQFLAG_DC; | |
193 | if (info->is_diode) | |
194 | analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC; | |
195 | } | |
196 | ||
197 | static gboolean flags_valid(const struct vc96_info *info) | |
198 | { | |
199 | int count; | |
200 | ||
201 | /* Does the packet have more than one multiplier? */ | |
202 | count = 0; | |
203 | count += (info->is_micro) ? 1 : 0; | |
204 | count += (info->is_milli) ? 1 : 0; | |
205 | count += (info->is_kilo) ? 1 : 0; | |
206 | count += (info->is_mega) ? 1 : 0; | |
207 | if (count > 1) { | |
208 | sr_dbg("More than one multiplier detected in packet."); | |
209 | return FALSE; | |
210 | } | |
211 | ||
212 | /* Does the packet "measure" more than one type of value? */ | |
213 | count = 0; | |
214 | count += (info->is_ac) ? 1 : 0; | |
215 | count += (info->is_dc) ? 1 : 0; | |
216 | count += (info->is_resistance) ? 1 : 0; | |
217 | count += (info->is_diode) ? 1 : 0; | |
218 | if (count > 1) { | |
219 | sr_dbg("More than one measurement type detected in packet."); | |
220 | return FALSE; | |
221 | } | |
222 | ||
223 | /* Both AC and DC set? */ | |
224 | if (info->is_ac && info->is_dc) { | |
225 | sr_dbg("Both AC and DC flags detected in packet."); | |
226 | return FALSE; | |
227 | } | |
228 | ||
229 | return TRUE; | |
230 | } | |
231 | ||
232 | SR_PRIV gboolean sr_vc96_packet_valid(const uint8_t *buf) | |
233 | { | |
234 | struct vc96_info info; | |
235 | ||
236 | memset(&info, 0x00, sizeof(struct vc96_info)); | |
237 | parse_flags((const char *)buf, &info); | |
238 | ||
4a6751fd | 239 | if (!flags_valid(&info)) |
9456d636 | 240 | return FALSE; |
9456d636 | 241 | |
4a6751fd | 242 | if ((buf[11] != '\r') || (buf[12] != '\n')) |
9456d636 | 243 | return FALSE; |
9456d636 MS |
244 | |
245 | return TRUE; | |
246 | } | |
247 | ||
248 | /** | |
249 | * Parse a protocol packet. | |
250 | * | |
251 | * @param buf Buffer containing the protocol packet. Must not be NULL. | |
252 | * @param floatval Pointer to a float variable. That variable will be modified | |
253 | * in-place depending on the protocol packet. Must not be NULL. | |
254 | * @param analog Pointer to a struct sr_datafeed_analog. The struct will be | |
255 | * filled with data according to the protocol packet. | |
256 | * Must not be NULL. | |
257 | * @param info Pointer to a struct vc96_info. The struct will be filled | |
258 | * with data according to the protocol packet. Must not be NULL. | |
259 | * | |
260 | * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the | |
261 | * 'analog' variable contents are undefined and should not be used. | |
262 | */ | |
263 | SR_PRIV int sr_vc96_parse(const uint8_t *buf, float *floatval, | |
264 | struct sr_datafeed_analog *analog, void *info) | |
265 | { | |
266 | int ret, exponent = 0; | |
267 | struct vc96_info *info_local; | |
268 | ||
269 | info_local = info; | |
270 | ||
271 | /* Don't print byte 12 + 13. Those contain the CR LF. */ | |
272 | sr_dbg("DMM packet: \"%.11s\".", buf); | |
273 | ||
274 | memset(info_local, 0x00, sizeof(struct vc96_info)); | |
275 | ||
276 | if ((ret = parse_value(buf, info_local, floatval, &exponent)) < 0) { | |
277 | sr_dbg("Error parsing value: %d.", ret); | |
278 | return ret; | |
279 | } | |
280 | ||
281 | parse_flags((const char *)buf, info_local); | |
282 | handle_flags(analog, floatval, &exponent, info_local); | |
283 | ||
284 | analog->encoding->digits = -exponent; | |
285 | analog->spec->spec_digits = -exponent; | |
286 | ||
9456d636 MS |
287 | return SR_OK; |
288 | } |