]>
Commit | Line | Data |
---|---|---|
e756c595 J |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
ab2b21fb | 4 | * Copyright (C) 2012 Bert Vermeulen <bert@biot.com> |
e756c595 J |
5 | * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.com> |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
ab2b21fb | 21 | #include <stdio.h> |
e756c595 | 22 | #include <config.h> |
ab2b21fb J |
23 | #include <stdlib.h> |
24 | #include <math.h> | |
25 | #include <string.h> | |
26 | #include <glib.h> | |
27 | #include <errno.h> | |
28 | #include <scpi.h> | |
29 | #include <libsigrok/libsigrok.h> | |
30 | #include "libsigrok-internal.h" | |
e756c595 J |
31 | #include "protocol.h" |
32 | ||
ab2b21fb J |
33 | /* Get the current state of the meter and sets analog object parameters. */ |
34 | SR_PRIV int fl45_get_status(const struct sr_dev_inst *sdi, | |
35 | struct sr_datafeed_analog *analog, int idx) | |
e756c595 | 36 | { |
e756c595 | 37 | struct dev_context *devc; |
ab2b21fb J |
38 | char *cmd, *func; |
39 | int res; | |
40 | ||
41 | res = 0; | |
42 | ||
43 | /* Command string to read current function. */ | |
44 | cmd = g_strdup_printf("FUNC%d?", idx + 1); | |
45 | sr_dbg("Sent command: %s.", cmd); | |
46 | ||
47 | if (!(devc = sdi->priv)) | |
48 | return TRUE; | |
49 | ||
50 | /* Default settings. */ | |
51 | analog[idx].meaning->mq = 0; | |
52 | analog[idx].meaning->unit = 0; | |
53 | analog[idx].meaning->mqflags = 0; | |
54 | ||
55 | /* Get a response to the FUNC? command. */ | |
56 | res = fl45_scpi_get_response(sdi, cmd); | |
57 | if (res == SR_ERR) | |
58 | return res; | |
59 | sr_dbg("Response to FUNC: %s.", devc->response); | |
60 | ||
61 | /* Set up analog mq, unit and flags. */ | |
62 | if (res == SR_OK && devc->response != NULL) { | |
63 | func = devc->response; | |
64 | if (strcmp(func, "AAC") == 0) { | |
65 | analog[idx].meaning->mq = SR_MQ_CURRENT; | |
66 | analog[idx].meaning->unit = SR_UNIT_AMPERE; | |
67 | analog[idx].meaning->mqflags = SR_MQFLAG_AC; | |
68 | } else if (strcmp(func, "AACDC") == 0) { | |
69 | analog[idx].meaning->mq = SR_MQ_CURRENT; | |
70 | analog[idx].meaning->unit = SR_UNIT_AMPERE; | |
71 | analog[idx].meaning->mqflags = SR_MQFLAG_AC; | |
72 | } else if (strcmp(func, "ADC") == 0) { | |
73 | analog[idx].meaning->mq = SR_MQ_CURRENT; | |
74 | analog[idx].meaning->unit = SR_UNIT_AMPERE; | |
75 | analog[idx].meaning->mqflags = SR_MQFLAG_DC; | |
76 | } else if (strcmp(func, "CONT") == 0) { | |
77 | analog[idx].meaning->mq = SR_MQ_CONTINUITY; | |
78 | analog->meaning->unit = SR_UNIT_BOOLEAN; | |
79 | } else if (strcmp(func, "DIODE") == 0) { | |
80 | analog[idx].meaning->mq = SR_MQ_VOLTAGE; | |
81 | analog[idx].meaning->unit = SR_UNIT_VOLT; | |
82 | analog[idx].meaning->mqflags = SR_MQFLAG_DIODE; | |
83 | } else if (strcmp(func, "FREQ") == 0) { | |
84 | analog[idx].meaning->mq = SR_MQ_FREQUENCY; | |
85 | analog[idx].meaning->unit = SR_UNIT_HERTZ; | |
86 | } else if (strcmp(func, "OHMS") == 0) { | |
87 | analog[idx].meaning->mq = SR_MQ_RESISTANCE; | |
88 | analog[idx].meaning->unit = SR_UNIT_OHM; | |
89 | } else if (strcmp(func, "VAC") == 0) { | |
90 | analog[idx].meaning->mq = SR_MQ_VOLTAGE; | |
91 | analog[idx].meaning->unit = SR_UNIT_VOLT; | |
92 | analog[idx].meaning->mqflags = SR_MQFLAG_AC; | |
93 | } else if (strcmp(func, "VACDC") == 0) { | |
94 | analog[idx].meaning->mq = SR_MQ_VOLTAGE; | |
95 | analog[idx].meaning->unit = SR_UNIT_VOLT; | |
96 | analog[idx].meaning->mqflags |= SR_MQFLAG_AC; | |
97 | analog[idx].meaning->mqflags |= SR_MQFLAG_DC; | |
98 | } else if (strcmp(func, "VDC") == 0) { | |
99 | analog[idx].meaning->mq = SR_MQ_VOLTAGE; | |
100 | analog[idx].meaning->unit = SR_UNIT_VOLT; | |
101 | analog[idx].meaning->mqflags = SR_MQFLAG_DC; | |
102 | } | |
103 | } | |
104 | ||
105 | /* Is the meter in autorange mode? */ | |
106 | res = fl45_scpi_get_response(sdi, "AUTO?"); | |
107 | if (res == SR_ERR) | |
108 | return res; | |
109 | sr_dbg("Response to AUTO: %s.", devc->response); | |
110 | if (res == SR_OK && devc->response != NULL) { | |
111 | if (strcmp(devc->response, "1") == 0) | |
112 | analog[idx].meaning->mqflags |= SR_MQFLAG_AUTORANGE; | |
113 | } | |
114 | ||
115 | return SR_OK; | |
116 | } | |
117 | ||
118 | SR_PRIV int fl45_get_modifiers(const struct sr_dev_inst *sdi, | |
119 | struct sr_datafeed_analog *analog, int idx) | |
120 | { | |
121 | struct dev_context *devc; | |
122 | int res, mod; | |
123 | ||
124 | if (!(devc = sdi->priv)) | |
125 | return TRUE; | |
126 | ||
127 | /* Get modifier value. */ | |
128 | res = fl45_scpi_get_response(sdi, "MOD?"); | |
129 | if (res == SR_ERR) | |
130 | return res; | |
131 | sr_dbg("Response to MOD: %s.", devc->response); | |
132 | if (res == SR_OK && devc->response != NULL) { | |
133 | mod = atoi(devc->response); | |
134 | if (mod & 0x01) { | |
135 | analog[idx].meaning->mqflags |= SR_MQFLAG_MIN; | |
136 | sr_dbg("MIN bit set: %s.", "1"); | |
137 | } | |
138 | if (mod & 0x02) { | |
139 | analog[idx].meaning->mqflags |= SR_MQFLAG_MAX; | |
140 | sr_dbg("MAX bit set: %s.", "2"); | |
141 | } | |
142 | if (mod & 0x04) { | |
143 | analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD; | |
144 | sr_dbg("HOLD bit set: %s.", "4"); | |
145 | } | |
146 | if (mod & 0x08) { | |
147 | sr_dbg("dB bit set: %s.", "8"); | |
148 | analog[idx].meaning->mq = SR_MQ_POWER_FACTOR; | |
149 | analog[idx].meaning->unit = SR_UNIT_DECIBEL_MW; | |
150 | analog[idx].meaning->mqflags = 0; | |
151 | analog[idx].encoding->digits = 2; | |
152 | analog[idx].spec->spec_digits = 2; | |
153 | } | |
154 | if (mod & 0x10) { | |
155 | sr_dbg("dB Power mod bit set: %s.", "16"); | |
156 | analog[idx].meaning->mq = SR_MQ_POWER; | |
157 | analog[idx].meaning->unit = SR_UNIT_DECIBEL_SPL; | |
158 | analog[idx].meaning->mqflags = 0; | |
159 | analog[idx].encoding->digits = 2; | |
160 | analog[idx].spec->spec_digits = 2; | |
161 | } | |
162 | if (mod & 0x20) { | |
163 | sr_dbg("REL bit set: %s.", "32"); | |
164 | analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD; | |
165 | } | |
166 | } | |
167 | ||
168 | return SR_OK; | |
169 | } | |
170 | ||
171 | int get_reading_dd(char *reading, size_t size) | |
172 | { | |
173 | int pe, pd, digits; | |
174 | unsigned int i; | |
175 | char expstr[3]; | |
176 | char *eptr; | |
177 | long exp; | |
178 | ||
179 | /* Calculate required precision. */ | |
180 | ||
181 | pe = pd = digits = 0; | |
182 | ||
183 | /* Get positions for '.' end 'E'. */ | |
184 | for (i = 0; i < size; i++) { | |
185 | if (reading[i] == '.') | |
186 | pd = i; | |
187 | if (reading[i] == 'E') { | |
188 | pe = i; | |
189 | break; | |
190 | } | |
191 | } | |
192 | ||
193 | digits = (pe - pd) - 1; | |
194 | ||
195 | /* Get exponent element. */ | |
196 | expstr[0] = reading[pe + 1]; | |
197 | expstr[1] = reading[pe + 2]; | |
198 | expstr[2] = '\0'; | |
199 | errno = 0; | |
200 | exp = strtol(expstr, &eptr, 10); | |
201 | if (errno != 0) | |
202 | return 2; | |
203 | /* A negative exponent increses digits, a positive one reduces. */ | |
204 | exp = exp * (-1); | |
205 | ||
206 | /* Adjust digits taking into account exponent. */ | |
207 | digits = digits + exp; | |
208 | ||
209 | return digits; | |
210 | } | |
211 | ||
212 | SR_PRIV int fl45_scpi_receive_data(int fd, int revents, void *cb_data) | |
213 | { | |
214 | struct sr_dev_inst *sdi; | |
215 | struct dev_context *devc; | |
216 | struct sr_datafeed_packet packet; | |
217 | struct sr_datafeed_analog analog[2]; | |
218 | struct sr_analog_encoding encoding[2]; | |
219 | struct sr_analog_meaning meaning[2]; | |
220 | struct sr_analog_spec spec[2]; | |
221 | struct sr_channel *channel; | |
222 | char *reading; | |
223 | float fv; | |
224 | int res, digits; | |
225 | unsigned int i; | |
226 | int sent_ch[2]; | |
e756c595 J |
227 | |
228 | (void)fd; | |
ab2b21fb | 229 | (void)revents; |
e756c595 J |
230 | |
231 | if (!(sdi = cb_data)) | |
232 | return TRUE; | |
233 | ||
234 | if (!(devc = sdi->priv)) | |
235 | return TRUE; | |
236 | ||
ab2b21fb J |
237 | res = 0; |
238 | sent_ch[0] = sent_ch[1] = 0; | |
239 | ||
240 | /* Process the list of channels. */ | |
241 | for (i = 0; i < devc->num_channels; i++) { | |
242 | /* Note: digits/spec_digits will be overridden later. */ | |
243 | sr_analog_init(&analog[i], &encoding[i], &meaning[i], &spec[i], 0); | |
244 | ||
245 | /* Detect current meter function. */ | |
246 | res = fl45_get_status(sdi, analog, i); | |
247 | ||
248 | /* Get channel data. */ | |
249 | if (i == 0) | |
250 | channel = sdi->channels->data; | |
251 | else | |
252 | channel = sdi->channels->next->data; | |
253 | ||
254 | /* Is channel enabled? */ | |
255 | if (analog[i].meaning->mq != 0 && channel->enabled) { | |
256 | /* Then get a reading from it. */ | |
257 | if (i == 0) | |
258 | res = fl45_scpi_get_response(sdi, "VAL1?"); | |
259 | if (i == 1) | |
260 | res = fl45_scpi_get_response(sdi, "VAL2?"); | |
261 | /* Note: Fluke 45 sends all data in text strings. */ | |
262 | reading = devc->response; | |
263 | ||
264 | /* Deal with OL reading. */ | |
265 | if (strcmp(reading, "+1E+9") == 0) { | |
266 | fv = INFINITY; | |
267 | sr_dbg("Reading OL (infinity): %s.", | |
268 | devc->response); | |
269 | } else if (res == SR_OK && reading != NULL) { | |
270 | /* Convert reading to float. */ | |
271 | sr_dbg("Meter reading string: %s.", reading); | |
272 | res = sr_atof_ascii(reading, &fv); | |
273 | digits = get_reading_dd(reading, strlen(reading)); | |
274 | analog[i].encoding->digits = digits; | |
275 | analog[i].spec->spec_digits = digits; | |
276 | ||
277 | } else { | |
278 | sr_dbg("Invalid float string: '%s'.", reading); | |
279 | return SR_ERR; | |
280 | } | |
281 | ||
282 | /* Are we on a little or big endian system? */ | |
283 | #ifdef WORDS_BIGENDIAN | |
284 | analog[i].encoding->is_bigendian = TRUE; | |
285 | #else | |
286 | analog[i].encoding->is_bigendian = FALSE; | |
287 | #endif | |
288 | ||
289 | /* Apply any modifiers. */ | |
290 | res = fl45_get_modifiers(sdi, analog, i); | |
291 | ||
292 | /* Channal flag. */ | |
293 | sent_ch[i] = 1; | |
294 | ||
295 | /* Set up analog object. */ | |
296 | analog[i].num_samples = 1; | |
297 | analog[i].data = &fv; | |
298 | analog[i].meaning->channels = g_slist_append(NULL, channel); | |
299 | ||
300 | packet.type = SR_DF_ANALOG; | |
301 | packet.payload = &analog[i]; | |
302 | ||
303 | sr_session_send(sdi, &packet); | |
304 | ||
305 | g_slist_free(analog[i].meaning->channels); | |
306 | } | |
e756c595 J |
307 | } |
308 | ||
ab2b21fb J |
309 | /* Update appropriate channel limits. */ |
310 | if (sent_ch[0] || sent_ch[1]) | |
311 | sr_sw_limits_update_samples_read(&devc->limits, 1); | |
312 | ||
313 | /* Are we done collecting samples? */ | |
314 | if (sr_sw_limits_check(&devc->limits)) | |
315 | sr_dev_acquisition_stop(sdi); | |
316 | ||
e756c595 J |
317 | return TRUE; |
318 | } | |
ab2b21fb J |
319 | |
320 | SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd) | |
321 | { | |
322 | struct dev_context *devc; | |
323 | devc = sdi->priv; | |
324 | ||
325 | /* Attempt to get a SCPI reponse. */ | |
326 | if (sr_scpi_get_string(sdi->conn, cmd, &devc->response) != SR_OK) | |
327 | return SR_ERR; | |
328 | ||
329 | /* Deal with RS232 '=>' prompt. */ | |
330 | if (strcmp(devc->response, "=>") == 0) { | |
331 | /* | |
332 | * If the response is a prompt then ignore and read the next | |
333 | * response in the buffer. | |
334 | */ | |
c9cfcd25 | 335 | g_free(devc->response); |
ab2b21fb J |
336 | devc->response = NULL; |
337 | /* Now attempt to read again. */ | |
338 | if (sr_scpi_get_string(sdi->conn, NULL, &devc->response) != SR_OK) | |
339 | return SR_ERR; | |
340 | } | |
341 | ||
342 | /* NULL RS232 error prompts. */ | |
c9cfcd25 GS |
343 | if (strcmp(devc->response, "!>") == 0 || |
344 | (strcmp(devc->response, "?>") == 0)) { | |
ab2b21fb | 345 | /* Unable to execute CMD. */ |
c9cfcd25 | 346 | g_free(devc->response); |
ab2b21fb J |
347 | devc->response = NULL; |
348 | } | |
349 | ||
350 | return SR_OK; | |
351 | } |