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