]> sigrok.org Git - libsigrok.git/blob - hardware/agilent-dmm/sched.c
agilent-dmm: use new serial API
[libsigrok.git] / hardware / agilent-dmm / sched.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Bert Vermeulen <bert@biot.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>
21 #include "libsigrok.h"
22 #include "libsigrok-internal.h"
23 #include "agilent-dmm.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <math.h>
28
29 static void dispatch(const struct sr_dev_inst *sdi)
30 {
31         struct dev_context *devc;
32         const struct agdmm_job *jobs;
33         int64_t now;
34         int i;
35
36         devc = sdi->priv;
37         jobs = devc->profile->jobs;
38         now = g_get_monotonic_time() / 1000;
39         for (i = 0; (&jobs[i])->interval; i++) {
40                 if (now - devc->jobqueue[i] > (&jobs[i])->interval) {
41                         sr_spew("Running job %d.", i);
42                         (&jobs[i])->send(sdi);
43                         devc->jobqueue[i] = now;
44                 }
45         }
46 }
47
48 static void receive_line(const struct sr_dev_inst *sdi)
49 {
50         struct dev_context *devc;
51         const struct agdmm_recv *recvs, *recv;
52         GRegex *reg;
53         GMatchInfo *match;
54         int i;
55
56         devc = sdi->priv;
57
58         /* Strip CRLF */
59         while (devc->buflen) {
60                 if (*(devc->buf + devc->buflen - 1) == '\r'
61                                 || *(devc->buf + devc->buflen - 1) == '\n')
62                         *(devc->buf + --devc->buflen) = '\0';
63                 else
64                         break;
65         }
66         sr_spew("Received '%s'.", devc->buf);
67
68         recv = NULL;
69         recvs = devc->profile->recvs;
70         for (i = 0; (&recvs[i])->recv_regex; i++) {
71                 reg = g_regex_new((&recvs[i])->recv_regex, 0, 0, NULL);
72                 if (g_regex_match(reg, (char *)devc->buf, 0, &match)) {
73                         recv = &recvs[i];
74                         break;
75                 }
76                 g_match_info_unref(match);
77                 g_regex_unref(reg);
78         }
79         if (recv) {
80                 recv->recv(sdi, match);
81                 g_match_info_unref(match);
82                 g_regex_unref(reg);
83         } else
84                 sr_dbg("Unknown line '%s'.", devc->buf);
85
86         /* Done with this. */
87         devc->buflen = 0;
88 }
89
90 SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
91 {
92         struct sr_dev_inst *sdi;
93         struct dev_context *devc;
94         int len;
95
96         (void)fd;
97
98         if (!(sdi = cb_data))
99                 return TRUE;
100
101         if (!(devc = sdi->priv))
102                 return TRUE;
103
104         if (revents == G_IO_IN) {
105                 /* Serial data arrived. */
106                 while(AGDMM_BUFSIZE - devc->buflen - 1 > 0) {
107                         len = serial_read(devc->serial, devc->buf + devc->buflen, 1);
108                         if (len < 1)
109                                 break;
110                         devc->buflen += len;
111                         *(devc->buf + devc->buflen) = '\0';
112                         if (*(devc->buf + devc->buflen - 1) == '\n') {
113                                 /* End of line */
114                                 receive_line(sdi);
115                                 break;
116                         }
117                 }
118         }
119
120         dispatch(sdi);
121
122         if (devc->num_samples >= devc->limit_samples)
123                 sdi->driver->dev_acquisition_stop(sdi, cb_data);
124
125         return TRUE;
126 }
127
128 static int agdmm_send(const struct sr_dev_inst *sdi, const char *cmd)
129 {
130         struct dev_context *devc;
131         char buf[32];
132
133         devc = sdi->priv;
134         sr_spew("Sending '%s'.", cmd);
135         strncpy(buf, cmd, 28);
136         if (!strncmp(buf, "*IDN?", 5))
137                 strncat(buf, "\r\n", 32);
138         else
139                 strncat(buf, "\n\r\n", 32);
140         if (serial_write(devc->serial, buf, strlen(buf)) == -1) {
141                 sr_err("Failed to send: %s.", strerror(errno));
142                 return SR_ERR;
143         }
144         
145         return SR_OK;
146 }
147
148 static int send_stat(const struct sr_dev_inst *sdi)
149 {
150         return agdmm_send(sdi, "STAT?");
151 }
152
153 static int recv_stat_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
154 {
155         struct dev_context *devc;
156         char *s;
157
158         devc = sdi->priv;
159         s = g_match_info_fetch(match, 1);
160         sr_spew("STAT response '%s'.", s);
161
162         /* Max, Min or Avg mode -- no way to tell which, so we'll
163          * set both flags to denote it's not a normal measurement. */
164         if (s[0] == '1')
165                 devc->cur_mqflags |= SR_MQFLAG_MAX | SR_MQFLAG_MIN;
166         else
167                 devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN);
168
169         if (s[1] == '1')
170                 devc->cur_mqflags |= SR_MQFLAG_RELATIVE;
171         else
172                 devc->cur_mqflags &= ~SR_MQFLAG_RELATIVE;
173
174         /* Triggered or auto hold modes. */
175         if (s[2] == '1' || s[3] == '1')
176                 devc->cur_mqflags |= SR_MQFLAG_HOLD;
177         else
178                 devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
179
180         /* Temp/aux mode. */
181         if (s[7] == '1')
182                 devc->mode_tempaux = TRUE;
183         else
184                 devc->mode_tempaux = FALSE;
185
186         /* Continuity mode. */
187         if (s[16] == '1')
188                 devc->mode_continuity = TRUE;
189         else
190                 devc->mode_continuity = FALSE;
191
192         g_free(s);
193
194         return SR_OK;
195 }
196
197 static int recv_stat_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
198 {
199         struct dev_context *devc;
200         char *s;
201
202         devc = sdi->priv;
203         s = g_match_info_fetch(match, 1);
204         sr_spew("STAT response '%s'.", s);
205
206         /* Peak hold mode. */
207         if (s[4] == '1')
208                 devc->cur_mqflags |= SR_MQFLAG_MAX;
209         else
210                 devc->cur_mqflags &= ~SR_MQFLAG_MAX;
211
212         /* Triggered hold mode. */
213         if (s[7] == '1')
214                 devc->cur_mqflags |= SR_MQFLAG_HOLD;
215         else
216                 devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
217
218         g_free(s);
219
220         return SR_OK;
221 }
222
223 static int send_fetc(const struct sr_dev_inst *sdi)
224 {
225         return agdmm_send(sdi, "FETC?");
226 }
227
228 static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
229 {
230         struct dev_context *devc;
231         struct sr_datafeed_packet packet;
232         struct sr_datafeed_analog analog;
233         float fvalue;
234         char *mstr, *eptr;
235
236         sr_spew("FETC reply '%s'.", g_match_info_get_string(match));
237         devc = sdi->priv;
238
239         if (devc->cur_mq == -1)
240                 /* Haven't seen configuration yet, so can't know what
241                  * the fetched float means. Not really an error, we'll
242                  * get metadata soon enough. */
243                 return SR_OK;
244
245         if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
246                 /* An invalid measurement shows up on the display as "O.L", but
247                  * comes through like this. Since comparing 38-digit floats
248                  * is rather problematic, we'll cut through this here. */
249                 fvalue = NAN;
250         } else {
251                 mstr = g_match_info_fetch(match, 1);
252                 fvalue = strtof(mstr, &eptr);
253                 g_free(mstr);
254                 if (fvalue == 0.0 && eptr == mstr) {
255                         sr_err("Invalid float.");
256                         return SR_ERR;
257                 }
258                 if (devc->cur_divider > 0)
259                         fvalue /= devc->cur_divider;
260         }
261
262         memset(&analog, 0, sizeof(struct sr_datafeed_analog));
263         analog.mq = devc->cur_mq;
264         analog.unit = devc->cur_unit;
265         analog.mqflags = devc->cur_mqflags;
266         analog.num_samples = 1;
267         analog.data = &fvalue;
268         packet.type = SR_DF_ANALOG;
269         packet.payload = &analog;
270         sr_session_send(devc->cb_data, &packet);
271
272         devc->num_samples++;
273
274         return SR_OK;
275 }
276
277 static int send_conf(const struct sr_dev_inst *sdi)
278 {
279         return agdmm_send(sdi, "CONF?");
280 }
281
282 static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
283 {
284         struct dev_context *devc;
285         char *mstr;
286
287         sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
288         devc = sdi->priv;
289         mstr = g_match_info_fetch(match, 1);
290         if (!strcmp(mstr, "V")) {
291                 devc->cur_mq = SR_MQ_VOLTAGE;
292                 devc->cur_unit = SR_UNIT_VOLT;
293                 devc->cur_mqflags = 0;
294                 devc->cur_divider = 0;
295         } else if(!strcmp(mstr, "MV")) {
296                 if (devc->mode_tempaux) {
297                         devc->cur_mq = SR_MQ_TEMPERATURE;
298                         /* No way to detect whether Fahrenheit or Celcius
299                          * is used, so we'll just default to Celcius. */
300                         devc->cur_unit = SR_UNIT_CELSIUS;
301                 devc->cur_mqflags = 0;
302                 devc->cur_divider = 0;
303                 } else {
304                         devc->cur_mq = SR_MQ_VOLTAGE;
305                         devc->cur_unit = SR_UNIT_VOLT;
306                         devc->cur_mqflags = 0;
307                         devc->cur_divider = 1000;
308                 }
309         } else if(!strcmp(mstr, "A")) {
310                 devc->cur_mq = SR_MQ_CURRENT;
311                 devc->cur_unit = SR_UNIT_AMPERE;
312                 devc->cur_mqflags = 0;
313                 devc->cur_divider = 0;
314         } else if(!strcmp(mstr, "UA")) {
315                 devc->cur_mq = SR_MQ_CURRENT;
316                 devc->cur_unit = SR_UNIT_AMPERE;
317                 devc->cur_mqflags = 0;
318                 devc->cur_divider = 1000000;
319         } else if(!strcmp(mstr, "FREQ")) {
320                 devc->cur_mq = SR_MQ_FREQUENCY;
321                 devc->cur_unit = SR_UNIT_HERTZ;
322                 devc->cur_mqflags = 0;
323                 devc->cur_divider = 0;
324         } else if(!strcmp(mstr, "RES")) {
325                 if (devc->mode_continuity) {
326                         devc->cur_mq = SR_MQ_CONTINUITY;
327                         devc->cur_unit = SR_UNIT_BOOLEAN;
328                 } else {
329                         devc->cur_mq = SR_MQ_RESISTANCE;
330                         devc->cur_unit = SR_UNIT_OHM;
331                 }
332                 devc->cur_mqflags = 0;
333                 devc->cur_divider = 0;
334         } else if(!strcmp(mstr, "CAP")) {
335                 devc->cur_mq = SR_MQ_CAPACITANCE;
336                 devc->cur_unit = SR_UNIT_FARAD;
337                 devc->cur_mqflags = 0;
338                 devc->cur_divider = 0;
339         } else
340                 sr_dbg("Unknown first argument.");
341         g_free(mstr);
342
343         if (g_match_info_get_match_count(match) == 4) {
344                 mstr = g_match_info_fetch(match, 3);
345                 /* Third value, if present, is always AC or DC. */
346                 if (!strcmp(mstr, "AC"))
347                         devc->cur_mqflags |= SR_MQFLAG_AC;
348                 else if (!strcmp(mstr, "DC"))
349                         devc->cur_mqflags |= SR_MQFLAG_DC;
350                 else
351                         sr_dbg("Unknown third argument.");
352                 g_free(mstr);
353         } else
354                 devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
355
356         return SR_OK;
357 }
358
359 static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
360 {
361         struct dev_context *devc;
362         char *mstr;
363
364         sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
365         devc = sdi->priv;
366         mstr = g_match_info_fetch(match, 1);
367         if (!strncmp(mstr, "VOLT", 4)) {
368                 devc->cur_mq = SR_MQ_VOLTAGE;
369                 devc->cur_unit = SR_UNIT_VOLT;
370                 devc->cur_mqflags = 0;
371                 devc->cur_divider = 0;
372                 if (mstr[4] == ':') {
373                         if (!strcmp(mstr + 4, "AC"))
374                                 devc->cur_mqflags |= SR_MQFLAG_AC;
375                         else if (!strcmp(mstr + 4, "DC"))
376                                 devc->cur_mqflags |= SR_MQFLAG_DC;
377                         else
378                                 /* "ACDC" appears as well, no idea what it means. */
379                                 devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
380                 } else
381                         devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
382         } else if(!strcmp(mstr, "CURR")) {
383                 devc->cur_mq = SR_MQ_CURRENT;
384                 devc->cur_unit = SR_UNIT_AMPERE;
385                 devc->cur_mqflags = 0;
386                 devc->cur_divider = 0;
387         } else if(!strcmp(mstr, "RES")) {
388                 if (devc->mode_continuity) {
389                         devc->cur_mq = SR_MQ_CONTINUITY;
390                         devc->cur_unit = SR_UNIT_BOOLEAN;
391                 } else {
392                         devc->cur_mq = SR_MQ_RESISTANCE;
393                         devc->cur_unit = SR_UNIT_OHM;
394                 }
395                 devc->cur_mqflags = 0;
396                 devc->cur_divider = 0;
397         } else
398                 sr_dbg("Unknown first argument.");
399         g_free(mstr);
400
401         return SR_OK;
402 }
403
404 /* At least the 123x and 125x appear to have this. */
405 static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
406 {
407         struct dev_context *devc;
408         char *mstr;
409
410         sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
411         devc = sdi->priv;
412         mstr = g_match_info_fetch(match, 1);
413         if(!strcmp(mstr, "DIOD")) {
414                 devc->cur_mq = SR_MQ_VOLTAGE;
415                 devc->cur_unit = SR_UNIT_VOLT;
416                 devc->cur_mqflags = SR_MQFLAG_DIODE;
417                 devc->cur_divider = 0;
418         } else
419                 sr_dbg("Unknown single argument.");
420         g_free(mstr);
421
422         return SR_OK;
423 }
424
425 /* This comes in whenever the rotary switch is changed to a new position.
426  * We could use it to determine the major measurement mode, but we already
427  * have the output of CONF? for that, which is more detailed. However
428  * we do need to catch this here, or it'll show up in some other output. */
429 static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
430 {
431         (void)sdi;
432
433         sr_spew("Switch '%s'.", g_match_info_get_string(match));
434
435         return SR_OK;
436 }
437
438 SR_PRIV const struct agdmm_job agdmm_jobs_u123x[] = {
439         { 143, send_stat },
440         { 1000, send_conf },
441         { 143, send_fetc },
442         { 0, NULL }
443 };
444
445 SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
446         { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
447         { "^\\*([0-9])$", recv_switch },
448         { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
449         { "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
450         { "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
451         { "^\"(DIOD)\"$", recv_conf },
452         { NULL, NULL }
453 };
454
455 SR_PRIV const struct agdmm_job agdmm_jobs_u125x[] = {
456         { 143, send_stat },
457         { 1000, send_conf },
458         { 143, send_fetc },
459         { 0, NULL }
460 };
461
462 SR_PRIV const struct agdmm_recv agdmm_recvs_u125x[] = {
463         { "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u125x },
464         { "^\\*([0-9])$", recv_switch },
465         { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
466         { "^(VOLT|CURR|RES|CAP) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
467         { "^(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
468         { "^\"(DIOD)\"$", recv_conf },
469         { NULL, NULL }
470 };