SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
agilentwaveform2sdds.cpp
Go to the documentation of this file.
1/**
2 * @file agilentwaveform2sdds.cpp
3 * @brief Capture and save waveform data from Agilent oscilloscopes to SDDS format.
4 *
5 * @details
6 * This program communicates with Agilent oscilloscopes over Ethernet using the VXI-11 protocol.
7 * It retrieves waveform data and saves it in the SDDS (Self Describing Data Set) format. Users
8 * can specify the IP address, output filename, and the channel to capture data from. Additional
9 * options allow customization of timeout, sample rate, acquisition points, and averaging parameters.
10 *
11 * @section Usage
12 * ```
13 * ./agilentwaveform2sdds -ip <ip_address>
14 * -f <output_file>
15 * -c <scope_channel>
16 * [-t <milliseconds>]
17 * [-s <sample_rate>]
18 * [-n <points>]
19 * [-a <num_to_average>]
20 * ```
21 *
22 * @section Options
23 * | Required | Description |
24 * |---------------------------------------|---------------------------------------------------------------------------------------|
25 * | `-ip` | IP address of the oscilloscope (e.g., 128.243.74.232). |
26 * | `-f` | Filename (without extension) for saving the waveform data. |
27 * | `-c` | Scope channel to capture data from (e.g., 1, 2, A, B). |
28
29 * | Optional | Description |
30 * |-----------|-----------------------------------------------------------------------------------|
31 * | `-t` | Timeout duration in milliseconds (default: 10000 ms). |
32 * | `-s` | Set the sample rate (e.g., 1e9 for 1 GS/s). |
33 * | `-n` | Minimum number of acquisition points. |
34 * | `-a` | Number to average (use <=0 for none). |
35 *
36 * @subsection Incompatibilities
37 * - `-s` (sample rate) and `-n` (number of points) are mutually dependent:
38 * - If neither is specified, defaults are used.
39 * - If one is specified, the other is calculated based on time range.
40 * - `-t` must be a positive value and defaults to 10000 ms if not set.
41 *
42 * @subsection Specific Requirements
43 * - The `-c` argument must correspond to valid channels recognized by the oscilloscope.
44 *
45 * @copyright
46 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
47 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
48 *
49 * @license
50 * This file is distributed under the terms of the Software License Agreement
51 * found in the file LICENSE included with this distribution.
52 *
53 * @authors
54 * Based on software by Steve Sharples, Univ. of Nottingham.
55 * Modified by R. Soliday and N. Kuklev.
56 */
57
58#include <cstdio>
59#include <cstdlib>
60#include <cstring>
61#include <cstdint>
62#include <memory>
63#include <rpc/rpc.h>
64#include <pthread.h>
65#include "SDDS.h"
66
67typedef long Device_Link;
68typedef long Device_Flags;
69typedef long Device_ErrorCode;
70
71struct Device_Error {
72 Device_ErrorCode error;
73};
74
75struct Create_LinkParms {
76 long clientId;
77 bool_t lockDevice;
78 unsigned long lock_timeout;
79 char *device;
80};
81
82struct Create_LinkResp {
83 Device_ErrorCode error;
84 Device_Link lid;
85 unsigned short abortPort;
86 unsigned long maxRecvSize;
87};
88
89struct Device_WriteParms {
90 Device_Link lid;
91 unsigned long io_timeout;
92 unsigned long lock_timeout;
93 Device_Flags flags;
94 struct {
95 unsigned int data_len;
96 char *data_val;
97 } data;
98};
99
100struct Device_WriteResp {
101 Device_ErrorCode error;
102 unsigned long size;
103};
104
105struct Device_ReadParms {
106 Device_Link lid;
107 unsigned long requestSize;
108 unsigned long io_timeout;
109 unsigned long lock_timeout;
110 Device_Flags flags;
111 char termChar;
112};
113
114struct Device_ReadResp {
115 Device_ErrorCode error;
116 long reason;
117 struct {
118 unsigned int data_len;
119 char *data_val;
120 } data;
121};
122
123#define create_link 10
124#define device_write 11
125#define device_read 12
126#define destroy_link 23
127
128#define VXI11_DEFAULT_TIMEOUT 10000 /* in ms */
129#define VXI11_READ_TIMEOUT 2000 /* in ms */
130#define VXI11_CLIENT CLIENT
131#define VXI11_LINK Create_LinkResp
132#define VXI11_MAX_CLIENTS 256 /* maximum number of unique IP addresses/clients */
133#define VXI11_NULL_READ_RESP 50 /* vxi11_receive() return value if a query \
134 * times out ON THE INSTRUMENT (and so we have \
135 * to resend the query again) */
136#define VXI11_NULL_WRITE_RESP 51 /* vxi11_send() return value if a sent command \
137 * times out ON THE INSTRUMENT. */
138
139struct CLINK {
140 VXI11_CLIENT *client;
141 VXI11_LINK *link;
142};
143
144#define DEVICE_CORE 0x0607AF
145#define DEVICE_CORE_VERSION 1
146#define RCV_END_BIT 0x04 // An end indicator has been read
147#define RCV_CHR_BIT 0x02 // A termchr is set in flags and a character which matches termChar is transferred
148
149char VXI11_IP_ADDRESS[VXI11_MAX_CLIENTS][20];
150CLIENT *VXI11_CLIENT_ADDRESS[VXI11_MAX_CLIENTS];
151int VXI11_DEVICE_NO = 0;
152int VXI11_LINK_COUNT[VXI11_MAX_CLIENTS];
153
154static struct timeval TIMEOUT = {25, 0};
155
156#ifndef BOOL
157# define BOOL int
158#endif
159#ifndef TRUE
160# define TRUE 1
161#endif
162#ifndef FALSE
163# define FALSE 0
164#endif
165
166bool sc(const char *con, const char *var);
167
168int agilent_init(CLINK *clink);
169int agilent_set_averages(CLINK *clink, int no_averages);
170void agilent_set_for_auto(CLINK *clink);
171long agilent_get_data(CLINK *clink, char chan, char *buf, unsigned long buf_len, unsigned long timeout);
172long agilent_get_data(CLINK *clink, char chan, int digitise, char *buf, unsigned long buf_len, unsigned long timeout);
173int agilent_get_preamble(CLINK *clink, char *buf, unsigned long buf_len);
174void agilent_scope_channel_str(char chan, char *source);
175int agilent_set_for_capture(CLINK *clink, double s_rate, long npoints, unsigned long timeout);
176long agilent_calculate_no_of_bytes(CLINK *clink, char chan, unsigned long timeout);
177
178int vxi11_open_device(const char *ip, CLINK *clink);
179int vxi11_open_device(const char *ip, CLINK *clink, char *device);
180int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, char *device);
181int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, char *device);
182int vxi11_send(CLINK *clink, const char *cmd);
183int vxi11_send(CLINK *clink, const char *cmd, unsigned long len);
184int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd);
185int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len);
186int vxi11_close_device(const char *ip, CLINK *clink);
187int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link);
188int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link);
189double vxi11_obtain_double_value(CLINK *clink, const char *cmd);
190double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout);
191long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout);
192long vxi11_receive(CLINK *clink, char *buffer, unsigned long len);
193long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout);
194long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout);
195long vxi11_obtain_long_value(CLINK *clink, const char *cmd);
196long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout);
197
198enum clnt_stat create_link_1(Create_LinkParms *argp, Create_LinkResp *clnt_res, CLIENT *clnt);
199enum clnt_stat device_write_1(Device_WriteParms *argp, Device_WriteResp *clnt_res, CLIENT *clnt);
200enum clnt_stat destroy_link_1(Device_Link *argp, Device_Error *clnt_res, CLIENT *clnt);
201enum clnt_stat device_read_1(Device_ReadParms *argp, Device_ReadResp *clnt_res, CLIENT *clnt);
202
203bool_t xdr_Create_LinkParms(XDR *xdrs, Create_LinkParms *objp);
204bool_t xdr_Create_LinkResp(XDR *xdrs, Create_LinkResp *objp);
205bool_t xdr_Device_ErrorCode(XDR *xdrs, Device_ErrorCode *objp);
206bool_t xdr_Device_Link(XDR *xdrs, Device_Link *objp);
207bool_t xdr_Device_WriteParms(XDR *xdrs, Device_WriteParms *objp);
208bool_t xdr_Device_WriteResp(XDR *xdrs, Device_WriteResp *objp);
209bool_t xdr_Device_Flags(XDR *xdrs, Device_Flags *objp);
210bool_t xdr_Device_Error(XDR *xdrs, Device_Error *objp);
211bool_t xdr_Device_ReadParms(XDR *xdrs, Device_ReadParms *objp);
212bool_t xdr_Device_ReadResp(XDR *xdrs, Device_ReadResp *objp);
213
214int main(int argc, char **argv) {
215 const char *progname;
216 const char *serverIP = nullptr;
217 char chnl = '\0'; /* we use '1' to '4' for channels, and 'A' to 'D' for FUNC[1...4] */
218 FILE *f_wf = nullptr;
219 char wfname[256] = {0};
220 long buf_size = 0;
221 char *buf = nullptr;
222 unsigned long timeout = VXI11_DEFAULT_TIMEOUT; /* in ms (= 10 seconds) */
223
224 long bytes_returned = 0;
225 bool got_ip = false;
226 bool got_scope_channel = false;
227 bool got_file = false;
228 bool got_no_averages = false;
229 int no_averages = 0;
230 int index = 1;
231 double s_rate = 0.0;
232 long npoints = 0;
233 double actual_s_rate = 0.0;
234 long actual_npoints = 0;
235 CLINK *clink = nullptr; /* client link (actually a structure containing CLIENT and VXI11_LINK pointers) */
236
237 short val = 0;
238 double vgain = 0.0, voffset = 0.0, hinterval = 0.0, hoffset = 0.0;
239 long i = 0;
240
241 clink = new (std::nothrow) CLINK; /* allocate some memory */
242 if (!clink) {
243 std::fprintf(stderr, "Error: Memory allocation for CLINK failed.\n");
244 return EXIT_FAILURE;
245 }
246
247 progname = argv[0];
248
249 while (index < argc) {
250 if (sc(argv[index], "-filename") || sc(argv[index], "-f") || sc(argv[index], "-file")) {
251 if (index + 1 >= argc) {
252 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
253 delete clink;
254 return EXIT_FAILURE;
255 }
256 std::snprintf(wfname, sizeof(wfname), "%s.sdds", argv[++index]);
257 got_file = true;
258 continue;
259 }
260
261 if (sc(argv[index], "-ip") || sc(argv[index], "-ip_address") || sc(argv[index], "-IP")) {
262 if (index + 1 >= argc) {
263 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
264 delete clink;
265 return EXIT_FAILURE;
266 }
267 serverIP = argv[++index];
268 got_ip = true;
269 continue;
270 }
271
272 if (sc(argv[index], "-channel") || sc(argv[index], "-c") || sc(argv[index], "-scope_channel")) {
273 if (index + 1 >= argc) {
274 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
275 delete clink;
276 return EXIT_FAILURE;
277 }
278 std::sscanf(argv[++index], "%c", &chnl);
279 got_scope_channel = true;
280 continue;
281 }
282
283 if (sc(argv[index], "-sample_rate") || sc(argv[index], "-s") || sc(argv[index], "-rate")) {
284 if (index + 1 >= argc) {
285 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
286 delete clink;
287 return EXIT_FAILURE;
288 }
289 std::sscanf(argv[++index], "%lg", &s_rate);
290 continue;
291 }
292
293 if (sc(argv[index], "-no_points") || sc(argv[index], "-n") || sc(argv[index], "-points")) {
294 if (index + 1 >= argc) {
295 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
296 delete clink;
297 return EXIT_FAILURE;
298 }
299 std::sscanf(argv[++index], "%ld", &npoints);
300 continue;
301 }
302
303 if (sc(argv[index], "-averages") || sc(argv[index], "-a") || sc(argv[index], "-aver")) {
304 if (index + 1 >= argc) {
305 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
306 delete clink;
307 return EXIT_FAILURE;
308 }
309 std::sscanf(argv[++index], "%d", &no_averages);
310 got_no_averages = true;
311 continue;
312 }
313
314 if (sc(argv[index], "-timeout") || sc(argv[index], "-t")) {
315 if (index + 1 >= argc) {
316 std::fprintf(stderr, "Error: Missing value for %s.\n", argv[index]);
317 delete clink;
318 return EXIT_FAILURE;
319 }
320 std::sscanf(argv[++index], "%lu", &timeout);
321 continue;
322 }
323
324 std::fprintf(stderr, "Warning: Unknown argument %s skipped.\n", argv[index]);
325 index++;
326 }
327
328 if (!got_file || !got_scope_channel || !got_ip) {
329 std::fprintf(stderr, "%s: Grabs a waveform from an Agilent scope via Ethernet.\n", progname);
330 std::fprintf(stderr, "Usage: %s [required arguments] [optional arguments]\n\n", progname);
331 std::fprintf(stderr, "Required Arguments:\n");
332 std::fprintf(stderr, " -ip, -ip_address, -IP IP address of the scope (e.g., 128.243.74.232)\n");
333 std::fprintf(stderr, " -f, -filename, -file Filename (without extension)\n");
334 std::fprintf(stderr, " -c, -channel, -scope_channel Scope channel (1,2,3,4,A,B,C,D)\n\n");
335 std::fprintf(stderr, "Optional Arguments:\n");
336 std::fprintf(stderr, " -t, -timeout Timeout in milliseconds (default: 10000 ms)\n");
337 std::fprintf(stderr, " -s, -sample_rate, -rate Set sample rate (e.g., 1e9 for 1 GS/s)\n");
338 std::fprintf(stderr, " -n, -no_points, -points Set minimum number of acquisition points\n");
339 std::fprintf(stderr, " -a, -averages, -aver Set number of averages (<=0 means none)\n\n");
340 std::fprintf(stderr, "Output:\n");
341 std::fprintf(stderr, " filename.sdds ASCII data of waveform\n\n");
342 std::fprintf(stderr, "Example:\n");
343 std::fprintf(stderr, " %s -ip 128.243.74.232 -f output -c 2 -s 1e9\n", progname);
344 delete clink;
345 return EXIT_FAILURE;
346 }
347
348 f_wf = std::fopen(wfname, "w");
349 if (f_wf != nullptr) {
350 if (vxi11_open_device(serverIP, clink) != 0) {
351 std::fprintf(stderr, "Error: Failed to open device.\n");
352 delete clink;
353 return EXIT_FAILURE;
354 }
355
356 if (agilent_init(clink) != 0) {
357 std::fprintf(stderr, "Error: Initialization failed.\n");
358 vxi11_close_device(serverIP, clink);
359 delete clink;
360 return EXIT_FAILURE;
361 }
362
363 agilent_set_for_capture(clink, s_rate, npoints, timeout);
364
365 if (got_no_averages) {
366 if (agilent_set_averages(clink, no_averages) != 0) {
367 std::fprintf(stderr, "Warning: Failed to set averages.\n");
368 }
369 }
370
371 buf_size = agilent_calculate_no_of_bytes(clink, chnl, timeout);
372 buf = new (std::nothrow) char[buf_size];
373 if (!buf) {
374 std::fprintf(stderr, "Error: Memory allocation for buffer failed.\n");
375 vxi11_close_device(serverIP, clink);
376 delete clink;
377 return EXIT_FAILURE;
378 }
379
380 hinterval = vxi11_obtain_double_value(clink, ":WAV:XINC?", timeout);
381 hoffset = vxi11_obtain_double_value(clink, ":WAV:XORIGIN?");
382 vgain = vxi11_obtain_double_value(clink, ":WAV:YINC?");
383 voffset = vxi11_obtain_double_value(clink, ":WAV:YORIGIN?");
384
385 bytes_returned = agilent_get_data(clink, chnl, 0, buf, buf_size, timeout);
386 if (bytes_returned <= 0) {
387 std::fprintf(stderr, "Error: Problem reading the data.\n");
388 std::fclose(f_wf);
389 delete[] buf;
390 vxi11_close_device(serverIP, clink);
391 delete clink;
392 return EXIT_FAILURE;
393 }
394
395 actual_s_rate = vxi11_obtain_double_value(clink, ":ACQ:SRAT?");
396 actual_npoints = vxi11_obtain_long_value(clink, ":ACQ:POINTS?");
397 std::printf("Sample rate used: %g (%g GSa/s); acquisition points: %ld\n", actual_s_rate, (actual_s_rate / 1e9), actual_npoints);
398
399 agilent_set_for_auto(clink);
400
401 /* Writing SDDS header */
402 std::fprintf(f_wf, "SDDS1\n");
403 std::fprintf(f_wf, "&parameter name=VerticalGain, type=double, &end\n");
404 std::fprintf(f_wf, "&parameter name=VerticalOffset, type=double, &end\n");
405 std::fprintf(f_wf, "&parameter name=HorizontalInterval, type=double, &end\n");
406 std::fprintf(f_wf, "&parameter name=HorizontalOffset, type=double, &end\n");
407 std::fprintf(f_wf, "&column name=Index, type=long, &end\n");
408 std::fprintf(f_wf, "&column name=Waveform, type=double, &end\n");
409 std::fprintf(f_wf, "&column name=Timebase, type=double, &end\n");
410 std::fprintf(f_wf, "&column name=DelayedTimebase, type=double, &end\n");
411 std::fprintf(f_wf, "&data mode=ascii, &end\n");
412 std::fprintf(f_wf, "%lg\n", vgain);
413 std::fprintf(f_wf, "%lg\n", voffset);
414 std::fprintf(f_wf, "%lg\n", hinterval);
415 std::fprintf(f_wf, "%lg\n", hoffset);
416 std::fprintf(f_wf, "\t%ld\n", bytes_returned / 2);
417
418 for (i = 0; i < bytes_returned; i += 2) {
419 std::memcpy(&val, buf + i, 2);
420 std::fprintf(f_wf, "%ld %lg %lg %lg\n", (i / 2), val * vgain - voffset, (i / 2) * hinterval, (i / 2) * hinterval + hoffset);
421 }
422
423 std::fclose(f_wf);
424 delete[] buf;
425
426 vxi11_close_device(serverIP, clink);
427 delete clink;
428 return EXIT_SUCCESS;
429 } else {
430 std::fprintf(stderr, "Error: Could not open file '%s' for writing.\n", wfname);
431 delete clink;
432 return EXIT_FAILURE;
433 }
434}
435
436/* string compare (sc) function for parsing... ignore */
437bool sc(const char *con, const char *var) {
438 return (std::strcmp(con, var) == 0);
439}
440
441int vxi11_open_device(const char *ip, CLINK *clink) {
442 char device[6];
443 std::strncpy(device, "inst0", sizeof(device));
444 device[5] = '\0'; // Ensure null-termination
445 return vxi11_open_device(ip, clink, device);
446}
447
448int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) {
449
450#ifdef __APPLE__
451 *client = clnt_create(const_cast<char *>(ip), DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
452#else
453 *client = clnt_create(ip, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
454#endif
455
456 if (*client == nullptr) {
457#ifdef __APPLE__
458 clnt_pcreateerror(const_cast<char *>(ip));
459#else
460 clnt_pcreateerror(ip);
461#endif
462 return -1;
463 }
464
465 return vxi11_open_link(ip, client, link, device);
466}
467
468int vxi11_open_device(const char *ip, CLINK *clink, char *device) {
469 int ret;
470 int l;
471 int device_no = -1;
472
473 for (l = 0; l < VXI11_MAX_CLIENTS; l++) {
474 if (std::strcmp(ip, VXI11_IP_ADDRESS[l]) == 0) {
475 device_no = l;
476 break;
477 }
478 }
479
480 if (device_no < 0) {
481 if (VXI11_DEVICE_NO >= VXI11_MAX_CLIENTS) {
482 std::fprintf(stderr, "Error: Maximum of %d clients allowed.\n", VXI11_MAX_CLIENTS);
483 ret = -VXI11_MAX_CLIENTS;
484 } else {
485 ret = vxi11_open_device(ip, &(clink->client), &(clink->link), device);
486 std::strncpy(VXI11_IP_ADDRESS[VXI11_DEVICE_NO], ip, sizeof(VXI11_IP_ADDRESS[0]) - 1);
487 VXI11_IP_ADDRESS[VXI11_DEVICE_NO][sizeof(VXI11_IP_ADDRESS[0]) - 1] = '\0'; // Ensure null-termination
488 VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO] = clink->client;
489 VXI11_LINK_COUNT[VXI11_DEVICE_NO] = 1;
490 VXI11_DEVICE_NO++;
491 }
492 } else {
493 clink->client = VXI11_CLIENT_ADDRESS[device_no];
494 ret = vxi11_open_link(ip, &(clink->client), &(clink->link), device);
495 VXI11_LINK_COUNT[device_no]++;
496 }
497 return ret;
498}
499
500int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) {
501
502 Create_LinkParms link_parms;
503
504 /* Set link parameters */
505 link_parms.clientId = reinterpret_cast<intptr_t>(*client);
506 link_parms.lockDevice = 0;
507 link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
508 link_parms.device = device;
509
510 *link = new (std::nothrow) Create_LinkResp();
511 if (!*link) {
512 std::fprintf(stderr, "Error: Memory allocation for Create_LinkResp failed.\n");
513 return -2;
514 }
515
516 if (create_link_1(&link_parms, *link, *client) != RPC_SUCCESS) {
517#ifdef __APPLE__
518 clnt_perror(*client, const_cast<char *>(ip));
519#else
520 clnt_perror(*client, ip);
521#endif
522 delete *link;
523 *link = nullptr;
524 return -2;
525 }
526 return 0;
527}
528
529enum clnt_stat create_link_1(Create_LinkParms *argp, Create_LinkResp *clnt_res, CLIENT *clnt) {
530 return clnt_call(clnt, create_link,
531 reinterpret_cast<xdrproc_t>(xdr_Create_LinkParms), reinterpret_cast<caddr_t>(argp),
532 reinterpret_cast<xdrproc_t>(xdr_Create_LinkResp), reinterpret_cast<caddr_t>(clnt_res),
533 TIMEOUT);
534}
535
536bool_t xdr_Create_LinkParms(XDR *xdrs, Create_LinkParms *objp) {
537
538#if defined(SOLARIS) && !defined(_LP64)
539 long *buf;
540#else
541 int32_t *buf;
542#endif
543
544 if (xdrs->x_op == XDR_ENCODE) {
545 buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
546 if (buf == nullptr) {
547 if (!xdr_long(xdrs, &objp->clientId))
548 return FALSE;
549 if (!xdr_bool(xdrs, &objp->lockDevice))
550 return FALSE;
551 if (!xdr_u_long(xdrs, &objp->lock_timeout))
552 return FALSE;
553
554 } else {
555 IXDR_PUT_INT32(buf, objp->clientId);
556 IXDR_PUT_BOOL(buf, objp->lockDevice);
557 IXDR_PUT_U_INT32(buf, objp->lock_timeout);
558 }
559 if (!xdr_string(xdrs, &objp->device, ~0))
560 return FALSE;
561 return TRUE;
562 } else if (xdrs->x_op == XDR_DECODE) {
563 buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
564 if (buf == nullptr) {
565 if (!xdr_long(xdrs, &objp->clientId))
566 return FALSE;
567 if (!xdr_bool(xdrs, &objp->lockDevice))
568 return FALSE;
569 if (!xdr_u_long(xdrs, &objp->lock_timeout))
570 return FALSE;
571
572 } else {
573 objp->clientId = IXDR_GET_INT32(buf);
574 objp->lockDevice = IXDR_GET_BOOL(buf);
575 objp->lock_timeout = IXDR_GET_U_INT32(buf);
576 }
577 if (!xdr_string(xdrs, &objp->device, ~0))
578 return FALSE;
579 return TRUE;
580 }
581
582 if (!xdr_long(xdrs, &objp->clientId))
583 return FALSE;
584 if (!xdr_bool(xdrs, &objp->lockDevice))
585 return FALSE;
586 if (!xdr_u_long(xdrs, &objp->lock_timeout))
587 return FALSE;
588 if (!xdr_string(xdrs, &objp->device, ~0))
589 return FALSE;
590 return TRUE;
591}
592
593bool_t xdr_Create_LinkResp(XDR *xdrs, Create_LinkResp *objp) {
594 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
595 return FALSE;
596 if (!xdr_Device_Link(xdrs, &objp->lid))
597 return FALSE;
598 if (!xdr_u_short(xdrs, &objp->abortPort))
599 return FALSE;
600 if (!xdr_u_long(xdrs, &objp->maxRecvSize))
601 return FALSE;
602 return TRUE;
603}
604
605bool_t xdr_Device_ErrorCode(XDR *xdrs, Device_ErrorCode *objp) {
606 return xdr_long(xdrs, objp);
607}
608
609bool_t xdr_Device_Link(XDR *xdrs, Device_Link *objp) {
610 return xdr_long(xdrs, objp);
611}
612
613int agilent_init(CLINK *clink) {
614 int ret;
615 ret = vxi11_send(clink, ":SYSTEM:HEADER 0");
616 if (ret < 0) {
617 std::fprintf(stderr, "Error: Could not send ':SYSTEM:HEADER 0' command.\n");
618 return ret;
619 }
620 vxi11_send(clink, ":ACQUIRE:COMPLETE 100");
621 if (SDDS_IsBigEndianMachine() == 0) {
622 vxi11_send(clink, ":WAVEFORM:BYTEORDER LSBFIRST");
623 } else {
624 vxi11_send(clink, ":WAVEFORM:BYTEORDER MSBFIRST");
625 }
626 vxi11_send(clink, ":WAVEFORM:FORMAT BINARY");
627 return 0;
628}
629
630int vxi11_send(CLINK *clink, const char *cmd) {
631 return vxi11_send(clink, cmd, std::strlen(cmd));
632}
633
634int vxi11_send(CLINK *clink, const char *cmd, unsigned long len) {
635 return vxi11_send(clink->client, clink->link, cmd, len);
636}
637
638int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd) {
639 return vxi11_send(client, link, cmd, std::strlen(cmd));
640}
641
642int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len) {
643 Device_WriteParms write_parms;
644 int bytes_left = static_cast<int>(len);
645 char *send_cmd;
646
647 send_cmd = new (std::nothrow) char[len];
648 if (!send_cmd) {
649 std::fprintf(stderr, "Error: Memory allocation for send_cmd failed.\n");
650 return -VXI11_NULL_WRITE_RESP;
651 }
652 std::memcpy(send_cmd, cmd, len);
653
654 write_parms.lid = link->lid;
655 write_parms.io_timeout = VXI11_DEFAULT_TIMEOUT;
656 write_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
657
658 /* We can only write (link->maxRecvSize) bytes at a time, so we sit in a loop,
659 * writing a chunk at a time, until we're done. */
660
661 do {
662 Device_WriteResp write_resp;
663 std::memset(&write_resp, 0, sizeof(write_resp));
664
665 if (static_cast<unsigned int>(bytes_left) <= link->maxRecvSize) {
666 write_parms.flags = 8;
667 write_parms.data.data_len = bytes_left;
668 } else {
669 write_parms.flags = 0;
670 /* We need to check that maxRecvSize is a sane value (ie >0). Believe it
671 * or not, on some versions of Agilent Infiniium scope firmware the scope
672 * returned "0", which breaks Rule B.6.3 of the VXI-11 protocol. Nevertheless
673 * we need to catch this, otherwise the program just hangs. */
674 if (link->maxRecvSize > 0) {
675 write_parms.data.data_len = link->maxRecvSize;
676 } else {
677 write_parms.data.data_len = 4096; /* pretty much anything should be able to cope with 4kB */
678 }
679 }
680 write_parms.data.data_val = send_cmd + (len - bytes_left);
681
682 if (device_write_1(&write_parms, &write_resp, client) != RPC_SUCCESS) {
683 delete[] send_cmd;
684 return -VXI11_NULL_WRITE_RESP; /* The instrument did not acknowledge the write, just completely
685 dropped it. There was no vxi11 comms error as such, the
686 instrument is just being rude. Usually occurs when the instrument
687 is busy. If we don't check this first, then the following
688 line causes a seg fault */
689 }
690 if (write_resp.error != 0) {
691 std::fprintf(stderr, "vxi11_user: write error: %d\n", static_cast<int>(write_resp.error));
692 delete[] send_cmd;
693 return -(write_resp.error);
694 }
695 bytes_left -= write_resp.size;
696 } while (bytes_left > 0);
697
698 delete[] send_cmd;
699 return 0;
700}
701
702enum clnt_stat device_write_1(Device_WriteParms *argp, Device_WriteResp *clnt_res, CLIENT *clnt) {
703 return clnt_call(clnt, device_write,
704 reinterpret_cast<xdrproc_t>(xdr_Device_WriteParms), reinterpret_cast<caddr_t>(argp),
705 reinterpret_cast<xdrproc_t>(xdr_Device_WriteResp), reinterpret_cast<caddr_t>(clnt_res),
706 TIMEOUT);
707}
708
709bool_t xdr_Device_WriteParms(XDR *xdrs, Device_WriteParms *objp) {
710 if (!xdr_Device_Link(xdrs, &objp->lid))
711 return FALSE;
712 if (!xdr_u_long(xdrs, &objp->io_timeout))
713 return FALSE;
714 if (!xdr_u_long(xdrs, &objp->lock_timeout))
715 return FALSE;
716 if (!xdr_Device_Flags(xdrs, &objp->flags))
717 return FALSE;
718 if (!xdr_bytes(xdrs, &objp->data.data_val, reinterpret_cast<u_int *>(&objp->data.data_len), ~0))
719 return FALSE;
720 return TRUE;
721}
722
723bool_t xdr_Device_WriteResp(XDR *xdrs, Device_WriteResp *objp) {
724 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
725 return FALSE;
726 if (!xdr_u_long(xdrs, &objp->size))
727 return FALSE;
728 return TRUE;
729}
730
731bool_t xdr_Device_Flags(XDR *xdrs, Device_Flags *objp) {
732 return xdr_long(xdrs, objp);
733}
734
735bool_t xdr_Device_Error(XDR *xdrs, Device_Error *objp) {
736 return xdr_Device_ErrorCode(xdrs, &objp->error);
737}
738
739int agilent_set_averages(CLINK *clink, int no_averages) {
740 char cmd[256];
741
742 if (no_averages <= 0) {
743 return vxi11_send(clink, ":ACQ:AVER 0");
744 } else {
745 std::snprintf(cmd, sizeof(cmd), ":ACQ:COUNT %d", no_averages);
746 if (vxi11_send(clink, cmd) != 0) {
747 std::fprintf(stderr, "Warning: Failed to set acquisition count.\n");
748 return -1;
749 }
750 return vxi11_send(clink, ":ACQ:AVER 1");
751 }
752}
753
754void agilent_set_for_auto(CLINK *clink) {
755 vxi11_send(clink, ":ACQ:SRAT:AUTO 1;:ACQ:POINTS:AUTO 1;:RUN");
756}
757
758int vxi11_close_device(const char *ip, CLINK *clink) {
759 int l, ret;
760 int device_no = -1;
761
762 /* Which instrument are we referring to? */
763 for (l = 0; l < VXI11_MAX_CLIENTS; l++) {
764 if (std::strcmp(ip, VXI11_IP_ADDRESS[l]) == 0) {
765 device_no = l;
766 break;
767 }
768 }
769 /* Something's up if we can't find the IP address! */
770 if (device_no == -1) {
771 std::fprintf(stderr, "vxi11_close_device: error: No record of opening device with IP %s.\n", ip);
772 ret = -4;
773 } else { /* Found the IP, there's more than one link to that instrument,
774 * so keep track and just close the link */
775 if (VXI11_LINK_COUNT[device_no] > 1) {
776 ret = vxi11_close_link(ip, clink->client, clink->link);
777 VXI11_LINK_COUNT[device_no]--;
778 }
779 /* Found the IP, it's the last link, so close the device (link
780 * AND client) */
781 else {
782 ret = vxi11_close_device(ip, clink->client, clink->link);
783 }
784 }
785 return ret;
786}
787
788int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link) {
789 int ret;
790
791 ret = vxi11_close_link(ip, client, link);
792
793 clnt_destroy(client);
794
795 return ret;
796}
797
798int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link) {
799 Device_Error dev_error;
800 std::memset(&dev_error, 0, sizeof(dev_error));
801
802 if (destroy_link_1(&link->lid, &dev_error, client) != RPC_SUCCESS) {
803#ifdef __APPLE__
804 clnt_perror(client, const_cast<char *>(ip));
805#else
806 clnt_perror(client, ip);
807#endif
808 return -1;
809 }
810
811 return 0;
812}
813
814enum clnt_stat destroy_link_1(Device_Link *argp, Device_Error *clnt_res, CLIENT *clnt) {
815 return clnt_call(clnt, destroy_link,
816 reinterpret_cast<xdrproc_t>(xdr_Device_Link), reinterpret_cast<caddr_t>(argp),
817 reinterpret_cast<xdrproc_t>(xdr_Device_Error), reinterpret_cast<caddr_t>(clnt_res),
818 TIMEOUT);
819}
820
821double vxi11_obtain_double_value(CLINK *clink, const char *cmd) {
822 return vxi11_obtain_double_value(clink, cmd, VXI11_READ_TIMEOUT);
823}
824
825double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout) {
826 char buf[50] = {0}; /* 50=arbitrary length... more than enough for one number in ascii */
827 double val = 0.0;
828 if (vxi11_send_and_receive(clink, cmd, buf, sizeof(buf), timeout) != 0) {
829 std::fprintf(stderr, "Warning: Failed to obtain double value for command '%s'. Returning 0.0.\n", cmd);
830 return 0.0;
831 }
832 val = std::strtod(buf, nullptr);
833 return val;
834}
835
836long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) {
837 int ret;
838 long bytes_returned;
839 do {
840 ret = vxi11_send(clink, cmd);
841 if (ret != 0) {
842 if (ret != -VXI11_NULL_WRITE_RESP) {
843 std::fprintf(stderr, "Error: Could not send command '%s'. Function vxi11_send returned %d.\n", cmd, ret);
844 return -1;
845 } else {
846 std::fprintf(stderr, "Info: VXI11_NULL_WRITE_RESP in vxi11_send_and_receive, resending query.\n");
847 }
848 }
849
850 bytes_returned = vxi11_receive(clink, buf, buf_len, timeout);
851 if (bytes_returned <= 0) {
852 if (bytes_returned > -VXI11_NULL_READ_RESP) {
853 std::fprintf(stderr, "Error: Problem reading reply for command '%s'. Function vxi11_receive returned %ld.\n", cmd, bytes_returned);
854 return -2;
855 } else {
856 std::fprintf(stderr, "Info: VXI11_NULL_READ_RESP in vxi11_send_and_receive, resending query.\n");
857 }
858 }
859 } while (bytes_returned == -VXI11_NULL_READ_RESP || ret == -VXI11_NULL_WRITE_RESP);
860 return 0;
861}
862
863long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) {
864 Device_ReadParms read_parms;
865 Device_ReadResp read_resp;
866 long curr_pos = 0;
867
868 read_parms.lid = link->lid;
869 read_parms.requestSize = len;
870 read_parms.io_timeout = timeout; /* in ms */
871 read_parms.lock_timeout = timeout; /* in ms */
872 read_parms.flags = 0;
873 read_parms.termChar = 0;
874
875 do {
876 std::memset(&read_resp, 0, sizeof(read_resp));
877
878 read_resp.data.data_val = buffer + curr_pos;
879 read_parms.requestSize = len - curr_pos; // Never request more total data than originally specified in len
880
881 if (device_read_1(&read_parms, &read_resp, client) != RPC_SUCCESS) {
882 return -VXI11_NULL_READ_RESP; /* there is nothing to read. Usually occurs after sending a query
883 which times out on the instrument. If we don't check this first,
884 then the following line causes a seg fault */
885 }
886 if (read_resp.error != 0) {
887 /* Read failed for reason specified in error code.
888 * 0 no error
889 * 4 invalid link identifier
890 * 11 device locked by another link
891 * 15 I/O timeout
892 * 17 I/O error
893 * 23 abort
894 */
895
896 std::fprintf(stderr, "vxi11_user: read error: %d\n", static_cast<int>(read_resp.error));
897 return -(read_resp.error);
898 }
899
900 if ((static_cast<unsigned long>(curr_pos + read_resp.data.data_len) <= len)) {
901 curr_pos += read_resp.data.data_len;
902 }
903 if ((read_resp.reason & RCV_END_BIT) || (read_resp.reason & RCV_CHR_BIT)) {
904 break;
905 } else if ((static_cast<unsigned long>(curr_pos) == len)) {
906 std::fprintf(stderr, "vxi11_user: read error: buffer too small. Read %ld bytes without hitting terminator.\n", curr_pos);
907 return -100;
908 }
909 } while (true);
910 return curr_pos; /* actual number of bytes received */
911}
912
913long vxi11_receive(CLINK *clink, char *buffer, unsigned long len) {
914 return vxi11_receive(clink, buffer, len, VXI11_READ_TIMEOUT);
915}
916
917long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
918 return vxi11_receive(clink->client, clink->link, buffer, len, timeout);
919}
920
921enum clnt_stat device_read_1(Device_ReadParms *argp, Device_ReadResp *clnt_res, CLIENT *clnt) {
922 return clnt_call(clnt, device_read,
923 reinterpret_cast<xdrproc_t>(xdr_Device_ReadParms), reinterpret_cast<caddr_t>(argp),
924 reinterpret_cast<xdrproc_t>(xdr_Device_ReadResp), reinterpret_cast<caddr_t>(clnt_res),
925 TIMEOUT);
926}
927
928bool_t xdr_Device_ReadParms(XDR *xdrs, Device_ReadParms *objp) {
929#if defined(SOLARIS) && !defined(_LP64)
930 long *buf;
931#else
932 int32_t *buf;
933#endif
934
935 if (xdrs->x_op == XDR_ENCODE) {
936 if (!xdr_Device_Link(xdrs, &objp->lid))
937 return FALSE;
938 buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
939 if (buf == nullptr) {
940 if (!xdr_u_long(xdrs, &objp->requestSize))
941 return FALSE;
942 if (!xdr_u_long(xdrs, &objp->io_timeout))
943 return FALSE;
944 if (!xdr_u_long(xdrs, &objp->lock_timeout))
945 return FALSE;
946
947 } else {
948 IXDR_PUT_U_INT32(buf, objp->requestSize);
949 IXDR_PUT_U_INT32(buf, objp->io_timeout);
950 IXDR_PUT_U_INT32(buf, objp->lock_timeout);
951 }
952 if (!xdr_Device_Flags(xdrs, &objp->flags))
953 return FALSE;
954 if (!xdr_char(xdrs, &objp->termChar))
955 return FALSE;
956 return TRUE;
957 } else if (xdrs->x_op == XDR_DECODE) {
958 if (!xdr_Device_Link(xdrs, &objp->lid))
959 return FALSE;
960 buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
961 if (buf == nullptr) {
962 if (!xdr_u_long(xdrs, &objp->requestSize))
963 return FALSE;
964 if (!xdr_u_long(xdrs, &objp->io_timeout))
965 return FALSE;
966 if (!xdr_u_long(xdrs, &objp->lock_timeout))
967 return FALSE;
968
969 } else {
970 objp->requestSize = IXDR_GET_U_INT32(buf);
971 objp->io_timeout = IXDR_GET_U_INT32(buf);
972 objp->lock_timeout = IXDR_GET_U_INT32(buf);
973 }
974 if (!xdr_Device_Flags(xdrs, &objp->flags))
975 return FALSE;
976 if (!xdr_char(xdrs, &objp->termChar))
977 return FALSE;
978 return TRUE;
979 }
980
981 if (!xdr_Device_Link(xdrs, &objp->lid))
982 return FALSE;
983 if (!xdr_u_long(xdrs, &objp->requestSize))
984 return FALSE;
985 if (!xdr_u_long(xdrs, &objp->io_timeout))
986 return FALSE;
987 if (!xdr_u_long(xdrs, &objp->lock_timeout))
988 return FALSE;
989 if (!xdr_Device_Flags(xdrs, &objp->flags))
990 return FALSE;
991 if (!xdr_char(xdrs, &objp->termChar))
992 return FALSE;
993 return TRUE;
994}
995
996bool_t xdr_Device_ReadResp(XDR *xdrs, Device_ReadResp *objp) {
997 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
998 return FALSE;
999 if (!xdr_long(xdrs, &objp->reason))
1000 return FALSE;
1001 if (!xdr_bytes(xdrs, &objp->data.data_val, reinterpret_cast<u_int *>(&objp->data.data_len), ~0))
1002 return FALSE;
1003 return TRUE;
1004}
1005
1006long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout) {
1007 char buf[50] = {0}; /* 50=arbitrary length... more than enough for one number in ascii */
1008 if (vxi11_send_and_receive(clink, cmd, buf, sizeof(buf), timeout) != 0) {
1009 std::fprintf(stderr, "Warning: Failed to obtain long value for command '%s'. Returning 0.\n", cmd);
1010 return 0;
1011 }
1012 return std::strtol(buf, nullptr, 10);
1013}
1014
1015/* Lazy wrapper function with default read timeout */
1016long vxi11_obtain_long_value(CLINK *clink, const char *cmd) {
1017 return vxi11_obtain_long_value(clink, cmd, VXI11_READ_TIMEOUT);
1018}
1019
1020long agilent_get_data(CLINK *clink, char chan, char *buf, unsigned long buf_len, unsigned long timeout) {
1021 return agilent_get_data(clink, chan, 1, buf, buf_len, timeout);
1022}
1023
1024long agilent_get_data(CLINK *clink, char chan, int digitise, char *buf, unsigned long buf_len, unsigned long timeout) {
1025 char source[20] = {0};
1026 char cmd[256] = {0};
1027 int ret = 0;
1028 long bytes_returned = 0;
1029
1030 agilent_scope_channel_str(chan, source);
1031 std::snprintf(cmd, sizeof(cmd), ":WAV:SOURCE %s", source);
1032 ret = vxi11_send(clink, cmd);
1033 if (ret < 0) {
1034 std::fprintf(stderr, "Error: Could not send ':WAV:SOURCE %s' command.\n", source);
1035 return ret;
1036 }
1037
1038 if (digitise != 0) {
1039 ret = vxi11_send(clink, ":DIG");
1040 if (ret < 0) {
1041 std::fprintf(stderr, "Warning: Failed to digitize.\n");
1042 }
1043 }
1044
1045 do {
1046 ret = vxi11_send(clink, ":WAV:DATA?");
1047 if (ret < 0) {
1048 std::fprintf(stderr, "Warning: Failed to send ':WAV:DATA?' command.\n");
1049 return ret;
1050 }
1051 bytes_returned = vxi11_receive_data_block(clink, buf, buf_len, timeout);
1052 } while (bytes_returned == -VXI11_NULL_READ_RESP);
1053 /* We have to check for this after a :DIG because it could take a very long time */
1054 /* We should probably handle ret, but keeping it consistent with original code */
1055
1056 return bytes_returned;
1057}
1058
1059int agilent_get_preamble(CLINK *clink, char *buf, unsigned long buf_len) {
1060 int ret;
1061 long bytes_returned;
1062
1063 ret = vxi11_send(clink, ":WAV:PRE?");
1064 if (ret < 0) {
1065 std::fprintf(stderr, "Error: Could not send ':WAV:PRE?' command.\n");
1066 return ret;
1067 }
1068
1069 bytes_returned = vxi11_receive(clink, buf, buf_len);
1070
1071 return static_cast<int>(bytes_returned);
1072}
1073
1074void agilent_scope_channel_str(char chan, char *source) {
1075 switch (chan) {
1076 case 'A':
1077 case 'a':
1078 std::strcpy(source, "FUNC1");
1079 break;
1080 case 'B':
1081 case 'b':
1082 std::strcpy(source, "FUNC2");
1083 break;
1084 case 'C':
1085 case 'c':
1086 std::strcpy(source, "FUNC3");
1087 break;
1088 case 'D':
1089 case 'd':
1090 std::strcpy(source, "FUNC4");
1091 break;
1092 case '1':
1093 std::strcpy(source, "CHAN1");
1094 break;
1095 case '2':
1096 std::strcpy(source, "CHAN2");
1097 break;
1098 case '3':
1099 std::strcpy(source, "CHAN3");
1100 break;
1101 case '4':
1102 std::strcpy(source, "CHAN4");
1103 break;
1104 default:
1105 std::fprintf(stderr, "Warning: Unknown channel '%c'. Using channel 1.\n", chan);
1106 std::strcpy(source, "CHAN1");
1107 break;
1108 }
1109}
1110
1111long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
1112 /* I'm not sure what the maximum length of this header is, I'll assume it's
1113 * 11 (#9 + 9 digits) */
1114 unsigned long necessary_buffer_size;
1115 char *in_buffer = nullptr;
1116 int ret = 0;
1117 int ndigits = 0;
1118 unsigned long returned_bytes = 0;
1119 int l = 0;
1120 char scan_cmd[20] = {0};
1121 necessary_buffer_size = len + 12;
1122 in_buffer = new (std::nothrow) char[necessary_buffer_size];
1123 if (!in_buffer) {
1124 std::fprintf(stderr, "Error: Memory allocation for in_buffer failed.\n");
1125 return -3;
1126 }
1127 ret = vxi11_receive(clink, in_buffer, necessary_buffer_size, timeout);
1128 if (ret < 0) {
1129 delete[] in_buffer;
1130 return ret;
1131 }
1132 if (in_buffer[0] != '#') {
1133 std::fprintf(stderr, "vxi11_user: data block error: data block does not begin with '#'\n");
1134 std::fprintf(stderr, "First 20 characters received were: '");
1135 for (l = 0; l < 20 && l < static_cast<int>(necessary_buffer_size); l++) {
1136 std::fprintf(stderr, "%c", in_buffer[l]);
1137 }
1138 std::fprintf(stderr, "'\n");
1139 delete[] in_buffer;
1140 return -3;
1141 }
1142
1143 /* first find out how many digits */
1144 if (std::sscanf(in_buffer, "#%1d", &ndigits) != 1) {
1145 std::fprintf(stderr, "vxi11_user: Failed to parse number of digits in data block header.\n");
1146 delete[] in_buffer;
1147 return -3;
1148 }
1149 /* some instruments, if there is a problem acquiring the data, return only "#0" */
1150 if (ndigits > 0) {
1151 /* now that we know, we can convert the next <ndigits> bytes into an unsigned long */
1152 std::snprintf(scan_cmd, sizeof(scan_cmd), "##%dlu", ndigits);
1153 if (std::sscanf(in_buffer, scan_cmd, &returned_bytes) != 1) {
1154 std::fprintf(stderr, "vxi11_user: Failed to parse number of returned bytes.\n");
1155 delete[] in_buffer;
1156 return -3;
1157 }
1158 if (returned_bytes > len) {
1159 std::fprintf(stderr, "vxi11_user: Received more bytes than buffer can hold.\n");
1160 delete[] in_buffer;
1161 return -3;
1162 }
1163 std::memcpy(buffer, in_buffer + (ndigits + 2), returned_bytes);
1164 delete[] in_buffer;
1165 return static_cast<long>(returned_bytes);
1166 } else {
1167 delete[] in_buffer;
1168 return 0;
1169 }
1170}
1171
1172int agilent_set_for_capture(CLINK *clink, double s_rate, long npoints, unsigned long timeout) {
1173 long actual_npoints = 0; /* actual number of points returned */
1174 double time_range = 0.0;
1175 double auto_srat = 0.0; /* sample rate whilst on auto setting */
1176 long auto_npoints = 0; /* no of points whilst on auto setting */
1177 double expected_s_rate = 0.0; /* based on s_rate passed to us, or npoints */
1178 double actual_s_rate = 0.0; /* what it ends up as */
1179 double xinc = 0.0; /* xincrement (only need for ETIM mode) */
1180 char cmd[256] = {0};
1181 char etim_result[256] = {0};
1182 int ret_val = 0;
1183 int not_enough_memory = 0;
1184
1185 /* First we need to find out if we're in "ETIM" (equivalent time) mode,
1186 * because things are done differently. You can't set the sample rate,
1187 * and if you query it, you get a meaningless answer. You must work out
1188 * what the effective sample rate is from the waveform xincrement. A
1189 * pain in the ass, quite frankly. */
1190
1191 vxi11_send_and_receive(clink, ":ACQ:MODE?", etim_result, sizeof(etim_result), VXI11_READ_TIMEOUT);
1192
1193 /* Equivalent time (ETIM) mode: */
1194 if (std::strncmp("ETIM", etim_result, 4) == 0) {
1195 /* Find out the time range displayed on the screen */
1196 time_range = vxi11_obtain_double_value(clink, ":TIM:RANGE?");
1197
1198 /* Find the xincrement, whilst we're still in auto (points) mode */
1199 auto_npoints = vxi11_obtain_long_value(clink, ":ACQ:POINTS?");
1200
1201 /* Set the no of acquisition points to manual */
1202 vxi11_send(clink, ":ACQ:POINTS:AUTO 0");
1203
1204 if (npoints <= 0) { // if we've not been passed a value for npoints
1205 npoints = auto_npoints;
1206 }
1207 /* Remember we want at LEAST the number of points specified.
1208 * To some extent, the xinc value is determined by the
1209 * number of points. So to get the best xinc value we ask
1210 * for double what we actually want. */
1211 std::snprintf(cmd, sizeof(cmd), ":ACQ:POINTS %ld", (2 * npoints) - 1);
1212 vxi11_send(clink, cmd);
1213
1214 /* Unfortunately we have to do a :DIG, to make sure our changes have
1215 * been registered */
1216 vxi11_send(clink, ":DIG");
1217
1218 /* Find the xincrement is now*/
1219 xinc = vxi11_obtain_double_value(clink, ":WAV:XINC?", timeout);
1220
1221 /* Work out the number of points there _should_ be to cover the time range */
1222 actual_npoints = static_cast<long>((time_range / xinc) + 0.5);
1223
1224 /* Set the number of points accordingly. Hopefully the
1225 * xincrement won't have changed! */
1226 std::snprintf(cmd, sizeof(cmd), ":ACQ:POINTS %ld", actual_npoints);
1227 vxi11_send(clink, cmd);
1228
1229 /* This is a bit anal... we can work out very easily what the equivalent
1230 * sampling rate is (1 / xinc); the scope seems to store this value
1231 * somewhere, even though it doesn't use it. We may as well write it
1232 * to the scope, in case some user program asks for it while in
1233 * equivalent time mode. Should not be depended upon, though! */
1234
1235 std::snprintf(cmd, sizeof(cmd), ":ACQ:SRAT %G", (1 / xinc));
1236 vxi11_send(clink, cmd);
1237 }
1238
1239 /* Real time (RTIM, NORM or PDET) mode: */
1240 else {
1241 /* First find out what the sample rate is set to.
1242 * Each time you switch from auto to manual for either of these, the
1243 * scope remembers the values from last time you set these manually.
1244 * This is not very useful to us. We want to be able to set either the
1245 * sample rate (and derive npoints from that and the timebase), or the
1246 * minimum number of points (and derive the sample rate) or let
1247 * the scope choose sensible values for both of these. We only want to
1248 * capture the data for the time period displayed on the scope screen,
1249 * which is equal to the time range. If you leave the scope to do
1250 * everything auto, then it always acquires a bit more than what's on
1251 * the screen.
1252 */
1253 auto_srat = vxi11_obtain_double_value(clink, ":ACQ:SRAT?");
1254
1255 /* Set the sample rate (SRAT) and no of acquisition points to manual */
1256 vxi11_send(clink, ":ACQ:SRAT:AUTO 0;:ACQ:POINTS:AUTO 0");
1257
1258 /* Find out the time range displayed on the screen */
1259 time_range = vxi11_obtain_double_value(clink, ":TIM:RANGE?");
1260
1261 /* If we've not been passed a sample rate (ie s_rate <= 0) then... */
1262 if (s_rate <= 0) {
1263 /* ... if we've not been passed npoints, let scope set rate */
1264 if (npoints <= 0) {
1265 s_rate = auto_srat;
1266 }
1267 /* ... otherwise set the sample rate based on no of points. */
1268 else {
1269 s_rate = static_cast<double>(npoints) / time_range;
1270 }
1271 }
1272 /* We make a note here of what we're expecting the sample rate to be.
1273 * If it has to change for any reason (dodgy value, or not enough
1274 * memory) we will know about it.
1275 */
1276 expected_s_rate = s_rate;
1277
1278 /* Now we set the number of points to acquire. Of course, the scope
1279 * may not have enough memory to acquire all the points, so we just
1280 * sit in a loop, reducing the sample rate each time, until it's happy.
1281 */
1282 do {
1283 /* Send scope our desired sample rate. */
1284 std::snprintf(cmd, sizeof(cmd), ":ACQ:SRAT %G", s_rate);
1285 vxi11_send(clink, cmd);
1286 /* Scope will choose next highest allowed rate.
1287 * Find out what this is */
1288 actual_s_rate = vxi11_obtain_double_value(clink, ":ACQ:SRAT?");
1289
1290 /* Calculate the number of points on display (and round up for rounding errors) */
1291 npoints = static_cast<long>((time_range * actual_s_rate) + 0.5);
1292
1293 /* Set the number of points accordingly */
1294 /* Note this won't necessarily be the no of points you receive, eg if you have
1295 * sin(x)/x interpolation turned on, you will probably get more. */
1296 std::snprintf(cmd, sizeof(cmd), ":ACQ:POINTS %ld", npoints);
1297 vxi11_send(clink, cmd);
1298
1299 /* We should do a check, see if there's enough memory */
1300 actual_npoints = vxi11_obtain_long_value(clink, ":ACQ:POINTS?");
1301
1302 if (actual_npoints < npoints) {
1303 not_enough_memory = 1;
1304 ret_val = -1; /* We should report this fact to the calling function */
1305 s_rate = s_rate * 0.75 * (static_cast<double>(actual_npoints) / static_cast<double>(npoints));
1306 } else {
1307 not_enough_memory = 0;
1308 }
1309 } while (not_enough_memory == 1);
1310 /* Will possibly remove the explicit printf's here, maybe leave it up to the
1311 * calling function to spot potential problems (the user may not care!) */
1312 if (actual_s_rate != expected_s_rate) {
1313 // std::printf("Warning: the sampling rate has been adjusted,\n");
1314 // std::printf("from %g to %g, because ", expected_s_rate, actual_s_rate);
1315 if (ret_val == -1) {
1316 // std::printf("there was not enough memory.\n");
1317 } else {
1318 // std::printf("because %g Sa/s is not a valid sample rate.\n", expected_s_rate);
1319 ret_val = -2;
1320 }
1321 }
1322 }
1323 return ret_val;
1324}
1325
1326long agilent_calculate_no_of_bytes(CLINK *clink, char chan, unsigned long timeout) {
1327 char cmd[256] = {0};
1328 char source[20] = {0};
1329 double hinterval = 0.0, time_range = 0.0;
1330 double srat = 0.0;
1331 long no_of_bytes = 0;
1332 char etim_result[256] = {0};
1333
1334 // First we need to digitize, to get the correct values for the
1335 // waveform data. This is a pain in the ass.
1336 agilent_scope_channel_str(chan, source);
1337 std::snprintf(cmd, sizeof(cmd), ":WAV:SOURCE %s", source);
1338 vxi11_send(clink, cmd);
1339 vxi11_send(clink, ":DIG");
1340
1341 /* Now find the info we need to calculate the number of points */
1342 hinterval = vxi11_obtain_double_value(clink, ":WAV:XINC?", timeout);
1343 time_range = vxi11_obtain_double_value(clink, ":TIM:RANGE?");
1344
1345 /* Are we in equivalent time (ETIM) mode? If so, the value of ACQ:SRAT will
1346 * be meaningless, and there's a different formula */
1347 vxi11_send_and_receive(clink, ":ACQ:MODE?", etim_result, sizeof(etim_result), VXI11_READ_TIMEOUT);
1348 /* Equivalent time (ETIM) mode: */
1349 if (std::strncmp("ETIM", etim_result, 4) == 0) {
1350 no_of_bytes = static_cast<long>(2 * ((time_range / hinterval) + 0.5));
1351 } else {
1352 srat = vxi11_obtain_double_value(clink, ":ACQ:SRAT?");
1353
1354 no_of_bytes = static_cast<long>(2 * (((time_range - (1 / srat)) / hinterval) + 1) + 0.5);
1355 /* 2x because 2 bytes per data point
1356 * +0.5 to round up when casting as a long
1357 * -(1/srat) and +1 so that both raw data, and interpolated (sinx/x) data works */
1358 }
1359 return no_of_bytes;
1360}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_IsBigEndianMachine()
Determines whether the current machine uses big-endian byte ordering.