]> sigrok.org Git - libsigrok.git/blame - src/hardware/scpi-dmm/protocol.c
scpi-dmm: Add support for Owon XDM2041
[libsigrok.git] / src / hardware / scpi-dmm / protocol.c
CommitLineData
7a396ff5
GS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2018 Gerhard Sittig <gerhard.sittig@gmx.net>
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 <config.h>
3cdad416
GS
21#include <math.h>
22#include <string.h>
7a396ff5
GS
23#include "protocol.h"
24
3cdad416
GS
25#define WITH_CMD_DELAY 0 /* TODO See which devices need delays. */
26
27SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi)
28{
29 if (WITH_CMD_DELAY)
30 g_usleep(WITH_CMD_DELAY * 1000);
ddeaa49d
PA
31
32 if (!scpi->no_opc_command)
33 sr_scpi_get_opc(scpi);
3cdad416
GS
34}
35
36SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_number(
37 const struct sr_dev_inst *sdi, enum sr_mq mq, enum sr_mqflag flag)
38{
39 struct dev_context *devc;
40 size_t i;
41 const struct mqopt_item *item;
42
43 devc = sdi->priv;
44 for (i = 0; i < devc->model->mqopt_size; i++) {
45 item = &devc->model->mqopts[i];
46 if (item->mq != mq || item->mqflag != flag)
47 continue;
48 return item;
49 }
50
51 return NULL;
52}
53
54SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_text(
55 const struct sr_dev_inst *sdi, const char *text)
56{
57 struct dev_context *devc;
58 size_t i;
59 const struct mqopt_item *item;
60
61 devc = sdi->priv;
62 for (i = 0; i < devc->model->mqopt_size; i++) {
63 item = &devc->model->mqopts[i];
64 if (!item->scpi_func_query || !item->scpi_func_query[0])
65 continue;
66 if (!g_str_has_prefix(text, item->scpi_func_query))
67 continue;
68 return item;
69 }
70
71 return NULL;
72}
73
74SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi,
08f3b427
GS
75 enum sr_mq *mq, enum sr_mqflag *flag, char **rsp,
76 const struct mqopt_item **mqitem)
3cdad416
GS
77{
78 struct dev_context *devc;
79 const char *command;
80 char *response;
81 const char *have;
82 int ret;
83 const struct mqopt_item *item;
84
85 devc = sdi->priv;
86 if (mq)
87 *mq = 0;
88 if (flag)
89 *flag = 0;
90 if (rsp)
91 *rsp = NULL;
08f3b427
GS
92 if (mqitem)
93 *mqitem = NULL;
3cdad416 94
28877994 95 scpi_dmm_cmd_delay(sdi->conn);
3cdad416
GS
96 command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_FUNC);
97 if (!command || !*command)
98 return SR_ERR_NA;
99 response = NULL;
100 ret = sr_scpi_get_string(sdi->conn, command, &response);
3cdad416
GS
101 if (ret != SR_OK)
102 return ret;
103 if (!response || !*response)
104 return SR_ERR_NA;
105 have = response;
106 if (*have == '"')
107 have++;
108
109 ret = SR_ERR_NA;
110 item = scpi_dmm_lookup_mq_text(sdi, have);
111 if (item) {
112 if (mq)
113 *mq = item->mq;
114 if (flag)
115 *flag = item->mqflag;
08f3b427
GS
116 if (mqitem)
117 *mqitem = item;
3cdad416 118 ret = SR_OK;
2111d157
PA
119 } else {
120 sr_warn("Unknown measurement quantity: %s", have);
3cdad416
GS
121 }
122
123 if (rsp) {
124 *rsp = response;
125 response = NULL;
126 }
127 g_free(response);
128
129 return ret;
130}
131
132SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi,
133 enum sr_mq mq, enum sr_mqflag flag)
134{
135 struct dev_context *devc;
136 const struct mqopt_item *item;
137 const char *mode, *command;
138 int ret;
139
140 devc = sdi->priv;
141 item = scpi_dmm_lookup_mq_number(sdi, mq, flag);
142 if (!item)
143 return SR_ERR_NA;
144
145 mode = item->scpi_func_setup;
146 command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_SETUP_FUNC);
3cdad416 147 scpi_dmm_cmd_delay(sdi->conn);
28877994
GS
148 ret = sr_scpi_send(sdi->conn, command, mode);
149 if (ret != SR_OK)
150 return ret;
3cdad416 151
28877994 152 return SR_OK;
3cdad416
GS
153}
154
155SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch)
156{
157 struct sr_scpi_dev_inst *scpi;
158 struct dev_context *devc;
159 struct scpi_dmm_acq_info *info;
160 struct sr_datafeed_analog *analog;
161 int ret;
162 enum sr_mq mq;
163 enum sr_mqflag mqflag;
164 char *mode_response;
165 const char *p;
166 char **fields;
167 size_t count;
168 char prec_text[20];
169 const struct mqopt_item *item;
170 int prec_exp;
171 const char *command;
172 char *response;
173 gboolean use_double;
174 int sig_digits, val_exp;
175 int digits;
176 enum sr_unit unit;
395c1850 177 double limit;
3cdad416
GS
178
179 scpi = sdi->conn;
180 devc = sdi->priv;
181 info = &devc->run_acq_info;
182 analog = &info->analog[ch];
183
184 /*
185 * Get the meter's current mode, keep the response around.
186 * Skip the measurement if the mode is uncertain.
187 */
08f3b427 188 ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response, &item);
3cdad416
GS
189 if (ret != SR_OK) {
190 g_free(mode_response);
191 return ret;
192 }
193 if (!mode_response)
194 return SR_ERR;
195 if (!mq) {
196 g_free(mode_response);
197 return +1;
198 }
199
200 /*
201 * Get the last comma separated field of the function query
202 * response, or fallback to the model's default precision for
203 * the current function. This copes with either of these cases:
204 * VOLT +1.00000E-01,+1.00000E-06
205 * DIOD
206 * TEMP THER,5000,+1.00000E+00,+1.00000E-01
207 */
208 p = sr_scpi_unquote_string(mode_response);
209 fields = g_strsplit(p, ",", 0);
210 count = g_strv_length(fields);
211 if (count >= 2) {
212 snprintf(prec_text, sizeof(prec_text),
213 "%s", fields[count - 1]);
214 p = prec_text;
08f3b427
GS
215 } else if (!item) {
216 p = NULL;
217 } else if (item->default_precision == NO_DFLT_PREC) {
218 p = NULL;
3cdad416 219 } else {
08f3b427
GS
220 snprintf(prec_text, sizeof(prec_text),
221 "1e%d", item->default_precision);
222 p = prec_text;
3cdad416
GS
223 }
224 g_strfreev(fields);
225
226 /*
227 * Need to extract the exponent value ourselves, since a strtod()
228 * call will "eat" the exponent, too. Strip space, strip sign,
229 * strip float number (without! exponent), check for exponent
230 * and get exponent value. Accept absence of Esnn suffixes.
231 */
232 while (p && *p && g_ascii_isspace(*p))
233 p++;
234 if (p && *p && (*p == '+' || *p == '-'))
235 p++;
236 while (p && *p && g_ascii_isdigit(*p))
237 p++;
238 if (p && *p && *p == '.')
239 p++;
240 while (p && *p && g_ascii_isdigit(*p))
241 p++;
242 ret = SR_OK;
243 if (!p || !*p)
244 prec_exp = 0;
245 else if (*p != 'e' && *p != 'E')
246 ret = SR_ERR_DATA;
247 else
248 ret = sr_atoi(++p, &prec_exp);
249 g_free(mode_response);
250 if (ret != SR_OK)
251 return ret;
252
253 /*
254 * Get the measurement value. Make sure to strip trailing space
255 * or else number conversion may fail in fatal ways. Detect OL
256 * conditions. Determine the measurement's precision: Count the
257 * number of significant digits before the period, and get the
258 * exponent's value.
259 *
260 * The text presentation of values is like this:
261 * +1.09450000E-01
262 * Skip space/sign, count digits before the period, skip to the
263 * exponent, get exponent value.
264 *
265 * TODO Can sr_parse_rational() return the exponent for us? In
266 * addition to providing a precise rational value instead of a
267 * float that's an approximation of the received value? Can the
268 * 'analog' struct that we fill in carry rationals?
269 *
270 * Use double precision FP here during conversion. Optionally
271 * downgrade to single precision later to reduce the amount of
272 * logged information.
273 */
274 command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_VALUE);
275 if (!command || !*command)
276 return SR_ERR_NA;
3cdad416 277 scpi_dmm_cmd_delay(scpi);
28877994 278 ret = sr_scpi_get_string(scpi, command, &response);
3cdad416
GS
279 if (ret != SR_OK)
280 return ret;
281 g_strstrip(response);
282 use_double = devc->model->digits > 6;
283 ret = sr_atod_ascii(response, &info->d_value);
284 if (ret != SR_OK) {
285 g_free(response);
286 return ret;
287 }
288 if (!response)
289 return SR_ERR;
395c1850
GS
290 limit = 9e37;
291 if (info->d_value > +limit) {
3cdad416 292 info->d_value = +INFINITY;
395c1850 293 } else if (info->d_value < -limit) {
3cdad416
GS
294 info->d_value = -INFINITY;
295 } else {
296 p = response;
297 while (p && *p && g_ascii_isspace(*p))
298 p++;
299 if (p && *p && (*p == '-' || *p == '+'))
300 p++;
301 sig_digits = 0;
302 while (p && *p && g_ascii_isdigit(*p)) {
303 sig_digits++;
304 p++;
305 }
306 if (p && *p && *p == '.')
307 p++;
308 while (p && *p && g_ascii_isdigit(*p))
309 p++;
310 ret = SR_OK;
311 if (!p || !*p)
312 val_exp = 0;
313 else if (*p != 'e' && *p != 'E')
314 ret = SR_ERR_DATA;
315 else
316 ret = sr_atoi(++p, &val_exp);
317 }
318 g_free(response);
319 if (ret != SR_OK)
320 return ret;
321 /*
322 * TODO Come up with the most appropriate 'digits' calculation.
323 * This implementation assumes that either the device provides
324 * the resolution with the query for the meter's function, or
325 * the driver uses a fallback text pretending the device had
326 * provided it. This works with supported Agilent devices.
327 *
328 * An alternative may be to assume a given digits count which
329 * depends on the device, and adjust that count based on the
330 * value's significant digits and exponent. But this approach
331 * fails if devices change their digits count depending on
332 * modes or user requests, and also fails when e.g. devices
333 * with "100000 counts" can provide values between 100000 and
334 * 120000 in either 4 or 5 digits modes, depending on the most
335 * recent trend of the values. This less robust approach should
336 * only be taken if the mode inquiry won't yield the resolution
337 * (as e.g. DIOD does on 34405A, though we happen to know the
338 * fixed resolution for this very mode on this very model).
339 *
340 * For now, let's keep the prepared code path for the second
341 * approach in place, should some Agilent devices need it yet
342 * benefit from re-using most of the remaining acquisition
343 * routine.
344 */
345#if 1
346 digits = -prec_exp;
347#else
348 digits = devc->model->digits;
349 digits -= sig_digits;
350 digits -= val_exp;
351#endif
352
353 /*
354 * Fill in the 'analog' description: value, encoding, meaning.
355 * Callers will fill in the sample count, and channel name,
356 * and will send out the packet.
357 */
358 if (use_double) {
359 analog->data = &info->d_value;
360 analog->encoding->unitsize = sizeof(info->d_value);
361 } else {
362 info->f_value = info->d_value;
363 analog->data = &info->f_value;
364 analog->encoding->unitsize = sizeof(info->f_value);
365 }
3cdad416
GS
366 analog->encoding->digits = digits;
367 analog->meaning->mq = mq;
368 analog->meaning->mqflags = mqflag;
369 switch (mq) {
370 case SR_MQ_VOLTAGE:
371 unit = SR_UNIT_VOLT;
372 break;
373 case SR_MQ_CURRENT:
374 unit = SR_UNIT_AMPERE;
375 break;
376 case SR_MQ_RESISTANCE:
377 case SR_MQ_CONTINUITY:
378 unit = SR_UNIT_OHM;
379 break;
380 case SR_MQ_CAPACITANCE:
381 unit = SR_UNIT_FARAD;
382 break;
383 case SR_MQ_TEMPERATURE:
384 unit = SR_UNIT_CELSIUS;
385 break;
386 case SR_MQ_FREQUENCY:
387 unit = SR_UNIT_HERTZ;
388 break;
a1619831
AG
389 case SR_MQ_TIME:
390 unit = SR_UNIT_SECOND;
391 break;
3cdad416
GS
392 default:
393 return SR_ERR_NA;
394 }
395 analog->meaning->unit = unit;
396 analog->spec->spec_digits = digits;
397
398 return SR_OK;
399}
400
d0b602f0
TK
401SR_PRIV int scpi_dmm_get_meas_gwinstek(const struct sr_dev_inst *sdi, size_t ch)
402{
403 struct sr_scpi_dev_inst *scpi;
404 struct dev_context *devc;
405 struct scpi_dmm_acq_info *info;
406 struct sr_datafeed_analog *analog;
407 int ret;
408 enum sr_mq mq;
409 enum sr_mqflag mqflag;
410 char *mode_response;
411 const char *p;
412 const struct mqopt_item *item;
413 const char *command;
414 char *response;
415 gboolean use_double;
33aa8117 416 double limit;
d0b602f0
TK
417 int sig_digits, val_exp;
418 int digits;
419 enum sr_unit unit;
420 int mmode;
421
422 scpi = sdi->conn;
423 devc = sdi->priv;
424 info = &devc->run_acq_info;
425 analog = &info->analog[ch];
426
427 /*
428 * Get the meter's current mode, keep the response around.
429 * Skip the measurement if the mode is uncertain.
430 */
431 ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response, &item);
432 if (ret != SR_OK) {
433 g_free(mode_response);
434 return ret;
435 }
436 if (!mode_response)
437 return SR_ERR;
438 if (!mq) {
439 g_free(mode_response);
440 return +1;
441 }
442 mmode = atoi(mode_response);
443 g_free(mode_response);
444
445 /*
446 * Get the current reading from the meter.
447 */
448 scpi_dmm_cmd_delay(scpi);
449 command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_VALUE);
450 if (!command || !*command)
451 return SR_ERR_NA;
452 scpi_dmm_cmd_delay(scpi);
453 ret = sr_scpi_get_string(scpi, command, &response);
454 if (ret != SR_OK)
455 return ret;
456 g_strstrip(response);
457 use_double = devc->model->digits > 6;
458 ret = sr_atod_ascii(response, &info->d_value);
459 if (ret != SR_OK) {
460 g_free(response);
461 return ret;
462 }
463 if (!response)
464 return SR_ERR;
395c1850 465 limit = 9e37;
33306b13
PA
466 if (devc->model->infinity_limit != 0.0)
467 limit = devc->model->infinity_limit;
468 if (info->d_value >= +limit) {
d0b602f0 469 info->d_value = +INFINITY;
33306b13 470 } else if (info->d_value <= -limit) {
d0b602f0
TK
471 info->d_value = -INFINITY;
472 } else {
473 p = response;
474 while (p && *p && g_ascii_isspace(*p))
475 p++;
476 if (p && *p && (*p == '-' || *p == '+'))
477 p++;
478 sig_digits = 0;
479 while (p && *p && g_ascii_isdigit(*p)) {
480 sig_digits++;
481 p++;
482 }
483 if (p && *p && *p == '.')
484 p++;
485 while (p && *p && g_ascii_isdigit(*p))
486 p++;
487 ret = SR_OK;
488 if (!p || !*p)
489 val_exp = 0;
490 else if (*p != 'e' && *p != 'E')
491 ret = SR_ERR_DATA;
492 else
493 ret = sr_atoi(++p, &val_exp);
494 }
495 g_free(response);
496 if (ret != SR_OK)
497 return ret;
498
499 /*
33aa8117 500 * Make sure we report "INFINITY" when meter displays "0L".
d0b602f0
TK
501 */
502 switch (mmode) {
503 case 7:
504 case 16:
33aa8117 505 /* In resitance modes 0L reads as 1.20000E8 or 1.99999E8. */
395c1850 506 limit = 1.2e8;
33aa8117
GS
507 if (strcmp(devc->model->model, "GDM8255A") == 0)
508 limit = 1.99999e8;
33aa8117
GS
509 if (info->d_value >= limit)
510 info->d_value = +INFINITY;
d0b602f0
TK
511 break;
512 case 13:
33aa8117 513 /* In continuity mode 0L reads as 1.20000E3. */
d0b602f0
TK
514 if (info->d_value >= 1.2e3)
515 info->d_value = +INFINITY;
516 break;
517 case 17:
33aa8117 518 /* In diode mode 0L reads as 1.00000E0. */
d0b602f0
TK
519 if (info->d_value == 1.0e0)
520 info->d_value = +INFINITY;
521 break;
522 }
523
524 /*
33aa8117
GS
525 * Calculate 'digits' based on the result of the optional
526 * precision reading which was done at acquisition start.
527 * The GW-Instek manual gives the following information
528 * regarding the resolution:
d0b602f0 529 *
33aa8117
GS
530 * Type Digit
531 * -------- ------
532 * Slow 5 1/2
533 * Medium 4 1/2
534 * Fast 3 1/2
d0b602f0 535 */
d0b602f0
TK
536 digits = devc->model->digits;
537 if (devc->precision && *devc->precision) {
33aa8117 538 if (g_str_has_prefix(devc->precision, "Slow"))
d0b602f0 539 digits = 6;
33aa8117 540 else if (g_str_has_prefix(devc->precision, "Mid"))
d0b602f0 541 digits = 5;
33aa8117 542 else if (g_str_has_prefix(devc->precision, "Fast"))
d0b602f0
TK
543 digits = 4;
544 else
33aa8117 545 sr_info("Unknown precision: '%s'", devc->precision);
d0b602f0
TK
546 }
547
548 /*
549 * Fill in the 'analog' description: value, encoding, meaning.
550 * Callers will fill in the sample count, and channel name,
551 * and will send out the packet.
552 */
553 if (use_double) {
554 analog->data = &info->d_value;
555 analog->encoding->unitsize = sizeof(info->d_value);
556 } else {
557 info->f_value = info->d_value;
558 analog->data = &info->f_value;
559 analog->encoding->unitsize = sizeof(info->f_value);
560 }
d0b602f0
TK
561 analog->encoding->digits = digits;
562 analog->meaning->mq = mq;
563 analog->meaning->mqflags = mqflag;
564 switch (mq) {
565 case SR_MQ_VOLTAGE:
566 unit = SR_UNIT_VOLT;
567 break;
568 case SR_MQ_CURRENT:
569 unit = SR_UNIT_AMPERE;
570 break;
571 case SR_MQ_RESISTANCE:
572 case SR_MQ_CONTINUITY:
573 unit = SR_UNIT_OHM;
574 break;
575 case SR_MQ_CAPACITANCE:
576 unit = SR_UNIT_FARAD;
577 break;
578 case SR_MQ_TEMPERATURE:
579 switch (mmode) {
580 case 15:
581 unit = SR_UNIT_FAHRENHEIT;
582 break;
583 case 9:
584 default:
585 unit = SR_UNIT_CELSIUS;
586 }
587 break;
588 case SR_MQ_FREQUENCY:
589 unit = SR_UNIT_HERTZ;
590 break;
591 case SR_MQ_TIME:
592 unit = SR_UNIT_SECOND;
593 break;
594 default:
595 return SR_ERR_NA;
596 }
597 analog->meaning->unit = unit;
598 analog->spec->spec_digits = digits;
599
600 return SR_OK;
601}
602
3cdad416 603/* Strictly speaking this is a timer controlled poll routine. */
7a396ff5
GS
604SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data)
605{
3cdad416
GS
606 struct sr_dev_inst *sdi;
607 struct sr_scpi_dev_inst *scpi;
7a396ff5 608 struct dev_context *devc;
3cdad416
GS
609 struct scpi_dmm_acq_info *info;
610 gboolean sent_sample;
611 size_t ch;
612 struct sr_channel *channel;
613 int ret;
7a396ff5
GS
614
615 (void)fd;
3cdad416 616 (void)revents;
7a396ff5 617
3cdad416
GS
618 sdi = cb_data;
619 if (!sdi)
7a396ff5 620 return TRUE;
3cdad416
GS
621 scpi = sdi->conn;
622 devc = sdi->priv;
623 if (!scpi || !devc)
7a396ff5 624 return TRUE;
3cdad416
GS
625 info = &devc->run_acq_info;
626
627 sent_sample = FALSE;
628 ret = SR_OK;
629 for (ch = 0; ch < devc->num_channels; ch++) {
630 /* Check the channel's enabled status. */
631 channel = g_slist_nth_data(sdi->channels, ch);
632 if (!channel->enabled)
633 continue;
634
635 /*
636 * Prepare an analog measurement value. Note that digits
637 * will get updated later.
638 */
639 info->packet.type = SR_DF_ANALOG;
640 info->packet.payload = &info->analog[ch];
641 sr_analog_init(&info->analog[ch], &info->encoding[ch],
642 &info->meaning[ch], &info->spec[ch], 0);
643
644 /* Just check OPC before sending another request. */
645 scpi_dmm_cmd_delay(sdi->conn);
7a396ff5 646
3cdad416
GS
647 /*
648 * Have the model take and interpret a measurement. Lack
649 * of support is pointless, failed retrieval/conversion
650 * is considered fatal. The routine will fill in the
651 * 'analog' details, except for channel name and sample
652 * count (assume one value per channel).
653 *
654 * Note that non-zero non-negative return codes signal
655 * that the channel's data shell get skipped in this
656 * iteration over the channels. This copes with devices
657 * or modes where channels may provide data at different
658 * rates.
659 */
660 if (!devc->model->get_measurement) {
661 ret = SR_ERR_NA;
662 break;
663 }
664 ret = devc->model->get_measurement(sdi, ch);
665 if (ret > 0)
666 continue;
667 if (ret != SR_OK)
668 break;
669
670 /* Send the packet that was filled in by the model's routine. */
671 info->analog[ch].num_samples = 1;
672 info->analog[ch].meaning->channels = g_slist_append(NULL, channel);
673 sr_session_send(sdi, &info->packet);
674 g_slist_free(info->analog[ch].meaning->channels);
675 sent_sample = TRUE;
676 }
677 if (sent_sample)
678 sr_sw_limits_update_samples_read(&devc->limits, 1);
679 if (ret != SR_OK) {
680 /* Stop acquisition upon communication or data errors. */
681 sr_dev_acquisition_stop(sdi);
682 return TRUE;
7a396ff5 683 }
3cdad416
GS
684 if (sr_sw_limits_check(&devc->limits))
685 sr_dev_acquisition_stop(sdi);
7a396ff5
GS
686
687 return TRUE;
688}