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