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