]> sigrok.org Git - libsigrok.git/blame - hardware/agilent-dmm/sched.c
agilent-dmm: code cleanup
[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
a4394fb3 156static int recv_stat(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
a4394fb3 200static int send_fetc(const struct sr_dev_inst *sdi)
e93cdf42
BV
201{
202
203 return agdmm_send(sdi, "FETC?");
204}
205
a4394fb3 206static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
e93cdf42
BV
207{
208 struct dev_context *devc;
209 struct sr_datafeed_packet packet;
210 struct sr_datafeed_analog analog;
211 float fvalue;
212 char *mstr, *eptr;
213
214 sr_spew("agilent-dmm: FETC reply '%s'", g_match_info_get_string(match));
215 devc = sdi->priv;
216
217 if (devc->cur_mq == -1)
218 /* Haven't seen configuration yet, so can't know what
219 * the fetched float means. Not really an error, we'll
220 * get metadata soon enough. */
221 return SR_OK;
222
223 if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
224 /* An invalid measurement shows up on the display as "O.L, but
225 * comes through like this. Since comparing 38-digit floats
226 * is rather problematic, we'll cut through this here. */
227 fvalue = NAN;
228 } else {
229 mstr = g_match_info_fetch(match, 1);
230 fvalue = strtof(mstr, &eptr);
231 g_free(mstr);
232 if (fvalue == 0.0 && eptr == mstr) {
233 sr_err("agilent-dmm: invalid float");
234 return SR_ERR;
235 }
236 if (devc->cur_divider > 0)
237 fvalue /= devc->cur_divider;
238 }
239
240 memset(&analog, 0, sizeof(struct sr_datafeed_analog));
241 analog.mq = devc->cur_mq;
e6b021f3
BV
242 analog.unit = devc->cur_unit;
243 analog.mqflags = devc->cur_mqflags;
e93cdf42
BV
244 analog.num_samples = 1;
245 analog.data = &fvalue;
246 packet.type = SR_DF_ANALOG;
247 packet.payload = &analog;
248 sr_session_send(devc->cb_data, &packet);
249
250 devc->num_samples++;
251
252 return SR_OK;
253}
254
a4394fb3 255static int send_conf(const struct sr_dev_inst *sdi)
e93cdf42
BV
256{
257
258 return agdmm_send(sdi, "CONF?");
259}
260
a4394fb3 261static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
e93cdf42
BV
262{
263 struct dev_context *devc;
264 char *mstr;
265
81599cc5 266 sr_spew("agilent-dmm: CONF? response '%s'", g_match_info_get_string(match));
e93cdf42
BV
267 devc = sdi->priv;
268 mstr = g_match_info_fetch(match, 1);
269 if (!strcmp(mstr, "V")) {
270 devc->cur_mq = SR_MQ_VOLTAGE;
e6b021f3
BV
271 devc->cur_unit = SR_UNIT_VOLT;
272 devc->cur_mqflags = 0;
e93cdf42
BV
273 devc->cur_divider = 0;
274 } else if(!strcmp(mstr, "MV")) {
e6b021f3
BV
275 if (devc->mode_tempaux) {
276 devc->cur_mq = SR_MQ_TEMPERATURE;
277 /* No way to detect whether Fahrenheit or Celcius
278 * is used, so we'll just default to Celcius. */
279 devc->cur_unit = SR_UNIT_CELSIUS;
280 devc->cur_mqflags = 0;
281 devc->cur_divider = 0;
282 } else {
283 devc->cur_mq = SR_MQ_VOLTAGE;
284 devc->cur_unit = SR_UNIT_VOLT;
285 devc->cur_mqflags = 0;
286 devc->cur_divider = 1000;
287 }
e93cdf42
BV
288 } else if(!strcmp(mstr, "A")) {
289 devc->cur_mq = SR_MQ_CURRENT;
e6b021f3
BV
290 devc->cur_unit = SR_UNIT_AMPERE;
291 devc->cur_mqflags = 0;
e93cdf42
BV
292 devc->cur_divider = 0;
293 } else if(!strcmp(mstr, "UA")) {
294 devc->cur_mq = SR_MQ_CURRENT;
e6b021f3
BV
295 devc->cur_unit = SR_UNIT_AMPERE;
296 devc->cur_mqflags = 0;
e93cdf42
BV
297 devc->cur_divider = 1000000;
298 } else if(!strcmp(mstr, "FREQ")) {
299 devc->cur_mq = SR_MQ_FREQUENCY;
e6b021f3
BV
300 devc->cur_unit = SR_UNIT_HERTZ;
301 devc->cur_mqflags = 0;
e93cdf42
BV
302 devc->cur_divider = 0;
303 } else if(!strcmp(mstr, "RES")) {
e6b021f3
BV
304 if (devc->mode_continuity) {
305 devc->cur_mq = SR_MQ_CONTINUITY;
306 devc->cur_unit = SR_UNIT_BOOLEAN;
307 } else {
308 devc->cur_mq = SR_MQ_RESISTANCE;
309 devc->cur_unit = SR_UNIT_OHM;
310 }
311 devc->cur_mqflags = 0;
e93cdf42
BV
312 devc->cur_divider = 0;
313 } else if(!strcmp(mstr, "CAP")) {
314 devc->cur_mq = SR_MQ_CAPACITANCE;
e6b021f3
BV
315 devc->cur_unit = SR_UNIT_FARAD;
316 devc->cur_mqflags = 0;
e93cdf42 317 devc->cur_divider = 0;
e93cdf42
BV
318 } else
319 sr_dbg("agilent-dmm: unknown first argument");
320 g_free(mstr);
321
e066c32a
BV
322 if (g_match_info_get_match_count(match) == 4) {
323 mstr = g_match_info_fetch(match, 3);
e93cdf42
BV
324 /* Third value, if present, is always AC or DC. */
325 if (!strcmp(mstr, "AC"))
e6b021f3 326 devc->cur_mqflags |= SR_MQFLAG_AC;
e93cdf42 327 else if (!strcmp(mstr, "DC"))
e6b021f3 328 devc->cur_mqflags |= SR_MQFLAG_DC;
e93cdf42
BV
329 else
330 sr_dbg("agilent-dmm: unknown third argument");
331 g_free(mstr);
e6b021f3
BV
332 } else
333 devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
e93cdf42
BV
334
335 return SR_OK;
336}
337
a4394fb3
BV
338/* At least the 123x and 125x appear to have this. */
339static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
340{
341 struct dev_context *devc;
342 char *mstr;
343
344 sr_spew("agilent-dmm: CONF? response '%s'", g_match_info_get_string(match));
345 devc = sdi->priv;
346 mstr = g_match_info_fetch(match, 1);
347 if(!strcmp(mstr, "DIOD")) {
348 devc->cur_mq = SR_MQ_VOLTAGE;
349 devc->cur_unit = SR_UNIT_VOLT;
350 devc->cur_mqflags = SR_MQFLAG_DIODE;
351 devc->cur_divider = 0;
352 }
353 g_free(mstr);
354
355 return SR_OK;
356}
357
81599cc5
BV
358/* This comes in whenever the rotary switch is changed to a new position.
359 * We could use it to determine the major measurement mode, but we already
360 * have the output of CONF? for that, which is more detailed. However
361 * we do need to catch this here, or it'll show up in some other output. */
a4394fb3 362static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
e93cdf42
BV
363{
364
365 (void)sdi;
366
81599cc5 367 sr_spew("agilent-dmm: switch '%s'", g_match_info_get_string(match));
e93cdf42
BV
368
369 return SR_OK;
370}
371
372
a4394fb3
BV
373SR_PRIV const struct agdmm_job agdmm_u123x_jobs[] = {
374 { 143, send_stat },
375 { 1000, send_conf },
376 { 143, send_fetc },
e93cdf42
BV
377 { 0, NULL }
378};
379
a4394fb3
BV
380SR_PRIV const struct agdmm_recv agdmm_recvs[] = {
381 { "^\"(\\d\\d.{18}\\d)\"$", recv_stat },
382 { "^\\*([0-9])$", recv_switch },
383 { "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
384 { "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
385 { "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
386 { "^\"(DIOD)\"$", recv_conf },
e93cdf42
BV
387 { NULL, NULL }
388};
389
390