1 /*
2 * Some of this code is taken from:
3 * http://www.home.agilent.com/upload/cmc_upload/All/sockets.c
4 * and is copyright Copyright (C) 2007 Agilent Technologies
5 * and was heavily modified.
6 *
7 * The modifications and remaining code is:
8 * Copyright (C) 2012-2013 Analog Devices Inc.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19
20 * The GNU General Public License is available at
21 * http://www.gnu.org/copyleft/gpl.html.
22 */
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include <arpa/inet.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <inttypes.h>
36 #include <math.h>
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
39 #include <stdarg.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <string.h>
43 #include <sys/dirent.h>
44 #include <syslog.h>
45 #include <sys/select.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <sys/types.h>
50 #include <termios.h>
51 #include <unistd.h>
52 #include <time.h>
53
54 #include <gtk/gtk.h>
55 #include <gtkdatabox.h>
56 #include <gtkdatabox_grid.h>
57 #include <gtkdatabox_points.h>
58 #include <gtkdatabox_lines.h>
59
60 #include "../osc.h"
61 #include "../osc_plugin.h"
62 #include "../config.h"
63
64 #define THIS_DRIVER "SCPI"
65
66 struct scpi_instrument {
67 /* selection */
68 bool serial;
69 bool network;
70 char *id_regex;
71 char *model;
72 char *response;
73
74 /* network based instrument */
75 char *ip_address;
76 in_port_t main_port;
77 int main_socket;
78 in_port_t control_port;
79 int control_socket;
80
81 /* serial based instrument */
82 char *tty_path;
83 int ttyfd;
84 int gpib_addr;
85
86 };
87
88 struct mag_seek {
89 struct scpi_instrument *scpi;
90 double target_lvl;
91 double min_lvl;
92 double max_lvl;
93 double dBm;
94 };
95
96 static struct scpi_instrument signal_generator;
97 static struct scpi_instrument spectrum_analyzer;
98 static struct scpi_instrument prog_counter;;
99 static struct scpi_instrument *current_instrument = NULL;
100
101 static char *supported_spectrum_analyzers[] = {
102 "Rohde&Schwarz,FSEA 20,839161/004,3.40.2",
103 "Rohde&Schwarz,FSEA 30,827765/004,3.30",
104 NULL
105 };
106
107 #define HAMEG_HM8123 "HAMEG Instruments,HM8123,5.12"
108 #define AGILENT_53131A "HEWLETT-PACKARD,53131A,0,3944"
109 #define FLUKE_PM6681 "PHILIPS, PM6681, 0, MAIN V1.09 26 JAN 2001 / GPIB V1.13 26 JAN 2001"
110
111 static char *supported_counters[] = {
112 HAMEG_HM8123,
113 AGILENT_53131A,
114 FLUKE_PM6681,
115 NULL
116 };
117
118 #define SOCKETS_BUFFER_SIZE 1024
119 #define SOCKETS_TIMEOUT 2
120
121 #define DEFAULT_SCPI_IP_ADDR "192.168.0.1"
122 #define DEFAULT_SCPI_IP_PORT 5025
123 #define DEFAULT_SCPI_TTY "/dev/ttyUSB0"
124 #define DEFAULT_SCPI_GPIB 16
125
126 #define TTY_RAW_MODE
127
128 #if 1
129 #define print_output_sys fprintf
130 #define print_output_scpi(x) fprintf(stdout, "SCPI: %s\n", x)
131 #else
132 #define print_output_sys {do { } while (0);}
133 #define print_output_scpi(x) {do { } while (0);}
134 #endif
135
136 /*
137 * Network communications functions
138 */
139
140 /* Wait for data to become available */
network_waitfordata(int MySocket)141 static int network_waitfordata(int MySocket)
142 {
143 fd_set MyFDSet;
144 struct timeval tv;
145 int retval;
146
147 /* Initialize fd_set structure */
148 FD_ZERO(&MyFDSet);
149
150 /* Add socket to "watch list" */
151 FD_SET(MySocket, &MyFDSet);
152
153 /* Set Timeout */
154 tv.tv_sec = SOCKETS_TIMEOUT;
155 tv.tv_usec = 0;
156
157 /* Wait for change */
158 retval = select(MySocket+1, &MyFDSet, NULL, NULL, &tv);
159
160 /* Interpret return value */
161 if(retval == -1) {
162 printf("Error: Problem with select (%i)...\n", errno);
163 perror(__func__);
164 exit(1);
165 }
166
167 /* 0 = timeout, 1 = socket status has changed */
168 return retval;
169 }
170
scpi_network_read(struct scpi_instrument * scpi)171 static int scpi_network_read(struct scpi_instrument *scpi)
172 {
173 int actual;
174
175 /* Wait for data to become available */
176 if (network_waitfordata(scpi->control_socket) == 0) {
177 scpi->response[0] = 0;
178 return 0;
179 }
180
181 /* Read data */
182
183 actual = recv(scpi->control_socket, scpi->response,
184 SOCKETS_BUFFER_SIZE, 0);
185 if (actual == -1) {
186 printf("Error: Unable to receive data (%i)...\n", errno);
187 perror(__func__);
188 exit(1);
189 } else {
190 scpi->response[actual]=0;
191 }
192
193 return actual;
194 }
195
196 /* Turn NOdelay on */
network_setnodelay(int MySocket)197 static void network_setnodelay(int MySocket)
198 {
199 int StateNODELAY = 1;
200 int ret;
201
202 ret = setsockopt(MySocket, IPPROTO_TCP, TCP_NODELAY,
203 (void *)&StateNODELAY, sizeof StateNODELAY);
204
205 if (ret == -1) {
206 printf("Error: Unable to set NODELAY option (%i)...\n", errno);
207 perror("sockets");
208 exit(1);
209 }
210 return;
211 }
212
213 static int __attribute__ ((warn_unused_result))
network_connect(struct scpi_instrument * scpi)214 network_connect(struct scpi_instrument *scpi)
215 {
216 struct sockaddr_in MyAddress, MyControlAddress;
217 int status;
218 struct timeval timeout;
219 char buf[128];
220
221 timeout.tv_sec = SOCKETS_TIMEOUT;
222 timeout.tv_usec = 0;
223
224 /* Create socket (allocate resources) - IPv4, TCP */
225 scpi->main_socket = socket(PF_INET, SOCK_STREAM, 0);
226
227 if (scpi->main_socket == -1) {
228 printf("Error: Unable to create socket (%i)...\n", errno);
229 return -1;
230 }
231
232 /* set Receive and Transmit Timeout, so connect doesn't take so long to fail */
233 status = setsockopt(scpi->main_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
234 if (status < 0)
235 perror("setsockopt failed\n");
236
237 status = setsockopt(scpi->main_socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout));
238 if (status < 0)
239 perror("setsockopt failed\n");
240
241 /* Establish TCP connection */
242 memset(&MyAddress, 0, sizeof(struct sockaddr_in));
243 MyAddress.sin_family = PF_INET;
244 MyAddress.sin_port = htons(scpi->main_port);
245 MyAddress.sin_addr.s_addr = inet_addr(scpi->ip_address);
246
247 status = connect(scpi->main_socket, (struct sockaddr *)&MyAddress, sizeof(struct sockaddr_in));
248 if(status == -1) {
249 printf("Error: Unable to establish connection to ip:%s (%i)...\n",
250 scpi->ip_address, errno);
251 return -1;
252 }
253
254 /* Minimize latency by setting TCP_NODELAY option */
255 network_setnodelay(scpi->main_socket);
256
257 /* Ask for control port */
258 sprintf(buf, "SYST:COMM:TCPIP:CONTROL?\n");
259 status = send(scpi->main_socket, buf, strlen(buf), 0);
260 if (status == -1)
261 return -1;
262
263 if (scpi_network_read((scpi)) == 0) {
264 scpi->control_socket = scpi->main_socket;
265 return 0;
266 }
267
268 sscanf(scpi->response, "%" SCNd16, &scpi->control_port);
269
270 /* Create socket for control port */
271 scpi->control_socket = socket(PF_INET, SOCK_STREAM, 0);
272 if(scpi->control_socket == -1) {
273 printf("Error: Unable to create control port socket (%i)...\n",errno);
274 return -1;
275 }
276
277 /* Establish TCP connection to control port */
278 memset(&MyControlAddress, 0, sizeof(struct sockaddr_in));
279 MyControlAddress.sin_family = PF_INET;
280 MyControlAddress.sin_port = htons(scpi->control_port);
281 MyControlAddress.sin_addr.s_addr = inet_addr(scpi->ip_address);
282
283 status = connect(scpi->control_socket, (struct sockaddr *) &MyControlAddress, sizeof(struct sockaddr_in));
284 if(status == -1) {
285 printf("Error: Unable to establish connection to control port (%i)...\n",
286 errno);
287 return -1;
288 }
289
290 return 0;
291 }
292
293 /*
294 * tty functions
295 */
296
tty_read(struct scpi_instrument * scpi)297 static int tty_read(struct scpi_instrument *scpi)
298 {
299 #ifdef TTY_RAW_MODE
300 int n, i, end = 0;
301 int byte_count = 0;
302
303 /* Number of seconds before signaling a tty read timeout. */
304 struct timespec ts_current, ts_end;
305 clock_gettime(CLOCK_MONOTONIC, &ts_current);
306 ts_end.tv_sec = ts_current.tv_sec + SOCKETS_TIMEOUT;
307 ts_end.tv_nsec = ts_current.tv_nsec;
308
309 do {
310 n = read(scpi->ttyfd, (char *)scpi->response + byte_count,
311 SOCKETS_BUFFER_SIZE - byte_count);
312 if (n >= 0) {
313 byte_count += n;
314 for (i = 0; i < byte_count; i++)
315 /* Handle line feeds or carriage returns */
316 if (scpi->response[i] == 0x0A || scpi->response[i] == 0x0D) {
317 end = 1;
318 scpi->response[i + 1] = 0;
319 }
320 } else {
321 /* Raw mode does non-blocking I/O by default so try to read data
322 * until some exists.
323 */
324 if (errno == EAGAIN) {
325 if (timespeccmp(&ts_current, &ts_end, >) != 0) {
326 fprintf(stderr, "SCPI: reading from TTY timed out\n");
327 return -ETIMEDOUT;
328 }
329 clock_gettime(CLOCK_MONOTONIC, &ts_current);
330 continue;
331 } else {
332 print_output_sys(stderr, "%s: Can't read from TTY device: %s %s (%d)\n",
333 __func__, scpi->tty_path, strerror(errno), errno);
334 }
335 }
336 } while (byte_count < SOCKETS_BUFFER_SIZE && (end == 0));
337
338 return byte_count;
339 #else
340 int ret = read(scpi->ttyfd, (char *)scpi->response,
341 SOCKETS_BUFFER_SIZE);
342 if (ret >= 0)
343 scpi->response[ret] = 0;
344
345 return ret;
346 #endif
347 }
348
349
tty_connect(struct scpi_instrument * scpi)350 static int tty_connect(struct scpi_instrument *scpi)
351 {
352 struct termios ti;
353 char strbuf[250];
354 ssize_t status;
355
356 scpi->ttyfd = open(scpi->tty_path, O_RDWR | O_NOCTTY);
357 if (scpi->ttyfd < 0) {
358 print_output_sys(stderr, "%s: Can't open serial port: %s %s (%d)\n",
359 __func__, scpi->tty_path, strerror(errno), errno);
360
361 return -1;
362 }
363
364 tcflush(scpi->ttyfd, TCIOFLUSH);
365
366 if (tcgetattr(scpi->ttyfd, &ti) < 0) {
367 print_output_sys(stderr, strbuf, "%s: Can't get port settings: %s (%d)\n", __func__, strerror(errno), errno);
368 close(scpi->ttyfd);
369 scpi->ttyfd = -1;
370 return -1;
371 }
372
373 #ifdef TTY_RAW_MODE
374 cfmakeraw(&ti);
375 ti.c_cc[VMIN] = 1;
376 ti.c_cc[VTIME] = 0;
377 #else
378 ti.c_cflag |= ICANON;
379 #endif
380
381 ti.c_cflag |= CLOCAL;
382 ti.c_cflag &= ~CRTSCTS;
383 ti.c_cflag &= ~PARENB;
384 ti.c_cflag &= ~PARODD;
385 ti.c_cflag &= ~CSIZE;
386 ti.c_cflag |= CS8;
387 ti.c_cflag &= ~CSTOPB;
388
389 ti.c_oflag &= ~OPOST;
390 ti.c_iflag = ti.c_lflag = 0;
391
392 cfsetospeed(&ti, B19200); /* We don't need that for USB-GPIB */
393
394 if (tcsetattr(scpi->ttyfd, TCSANOW, &ti) < 0) {
395 print_output_sys(stderr, strbuf, "%s: Can't change port settings: %s (%d)\n",
396 __func__, strerror(errno), errno);
397 close(scpi->ttyfd);
398 scpi->ttyfd = -1;
399 return -1;
400 }
401
402 tcflush(scpi->ttyfd, TCIOFLUSH);
403
404 #ifdef TTY_RAW_MODE
405 if (fcntl(scpi->ttyfd, F_SETFL, fcntl(scpi->ttyfd, F_GETFL, 0) | O_NONBLOCK) < 0) {
406 print_output_sys(stderr, "%s: Can't set non blocking mode: %s (%d)\n",
407 __func__, strerror(errno), errno);
408 close(scpi->ttyfd);
409 scpi->ttyfd = -1;
410 return -1;
411 }
412 #endif
413
414 print_output_sys(stdout, "%s: GPIB tty: connecting to %s\n", __func__, scpi->tty_path);
415
416 /* set up controller and GPIB address */
417 sprintf(strbuf, "++mode 1\n");
418 status = write(scpi->ttyfd, strbuf, strlen(strbuf));
419 if (status < 0)
420 return -1;
421
422 sprintf(strbuf, "++addr %d\n", scpi->gpib_addr);
423 status = write(scpi->ttyfd, strbuf, strlen(strbuf));
424 if (status < 0)
425 return -1;
426
427 sprintf(strbuf, "++auto 1\n");
428 status = write(scpi->ttyfd, strbuf, strlen(strbuf));
429 if (status < 0)
430 return -1;
431
432 return 0;
433 }
434
435
436 /* Main SCPI functions */
437
438 /*
439 * writes count bytes from the buffer (buf) to the
440 * scpi instrument referred to by the descriptor *scpi.
441 *
442 * On success, the number of bytes written is returned
443 * (zero indicates nothing was written).
444 * On error, -1 is returned
445 */
scpi_write(struct scpi_instrument * scpi,const void * buf,size_t count)446 static ssize_t scpi_write(struct scpi_instrument *scpi, const void *buf, size_t count)
447 {
448 ssize_t retval = -1;
449
450 if (!scpi->network && !scpi->serial)
451 return -ENOENT;
452
453 if (scpi->network && !scpi->control_socket)
454 return -ENXIO;
455 else if (scpi->network) {
456 retval = send(scpi->control_socket, buf, count, 00);
457 if (retval == (ssize_t)count && memchr(buf, '?', count)) {
458 memset(scpi->response, 0, SOCKETS_BUFFER_SIZE);
459 scpi_network_read(scpi);
460 }
461 }
462
463 if (scpi->serial && scpi->ttyfd < 0)
464 return -ENXIO;
465 else if (scpi->serial) {
466 retval = write(scpi->ttyfd, buf, count);
467 if (retval == (ssize_t)count) {
468 if (memrchr(buf, '?', count)) {
469 memset(scpi->response, 0, SOCKETS_BUFFER_SIZE);
470 retval = tty_read(scpi);
471 }
472 } else {
473 fprintf(stderr, "SCPI:%s tty didn't write the entire buffer\n", __func__);
474 return -EIO;
475 }
476
477 tcflush(scpi->ttyfd, TCIOFLUSH);
478 }
479
480 return retval;
481
482 }
483
484 #define MAX_STR_SIZE 256
485
scpi_fprintf(struct scpi_instrument * scpi,const char * str,...)486 static ssize_t scpi_fprintf(struct scpi_instrument *scpi, const char *str, ...)
487 {
488 va_list args;
489 char buf[MAX_STR_SIZE];
490 ssize_t retval = -1;
491 int len;
492
493 va_start(args, str);
494 len = vsnprintf(buf, MAX_STR_SIZE, str, args);
495 va_end(args);
496
497 if (len > -1 && len < MAX_STR_SIZE)
498 retval = scpi_write(scpi, buf, strlen(buf));
499
500 return retval;
501 }
502
503 /*
504 static ssize_t scpi_read(struct scpi_instrument *scpi)
505 {
506 ssize_t retval;
507
508 if (!scpi->network && !scpi->serial)
509 return -ENOENT;
510
511 if (scpi->network && !scpi->control_socket)
512 return -ENXIO;
513 else {
514 retval = scpi_network_read(scpi);
515 }
516
517 return retval;
518 }
519 */
520
scpi_connect(struct scpi_instrument * scpi)521 static int scpi_connect(struct scpi_instrument *scpi)
522 {
523 int ret;
524
525 if(scpi->network) {
526 ret = network_connect(scpi);
527 if (ret != 0)
528 return ret;
529 if (scpi->control_socket != scpi->main_socket) {
530 scpi_fprintf(scpi, "DCL\n");
531 if (strlen(scpi->response)) {
532 if (!strcmp(scpi->response, "DCL\n"))
533 printf("Warning : %s DCL response: %s\n", __func__, scpi->response);
534 }
535 }
536 } else if (scpi->serial) {
537 tty_connect(scpi);
538 } else {
539 printf("misconfigured SCPI data structure\n");
540 return -1;
541 }
542
543 ret = scpi_fprintf(current_instrument, "*CLS;*RST;*IDN?\r\n");
544 scpi->model = strdup(scpi->response);
545 if (!strstr(scpi->model, scpi->id_regex)) {
546 printf("instrument doesn't match regex\n");
547 printf("\twanted : '%s'\n", scpi->id_regex);
548 printf("\treceived : '%s'\n", scpi->response);
549 return -1;
550 }
551 if (ret > 0)
552 printf("Instrument ID: %s\n", scpi->model);
553
554 return ret < 0 ? ret : 0;
555 }
556
557 /* Spectrum Analyzer commands */
558
scpi_rx_connected(void)559 bool scpi_rx_connected(void)
560 {
561 return (spectrum_analyzer.ttyfd != 0 || spectrum_analyzer.control_port != 0);
562 }
563
scpi_rx_trigger_sweep(void)564 void scpi_rx_trigger_sweep(void)
565 {
566 scpi_fprintf(&spectrum_analyzer, "INIT:IMM;*WAI\n");
567 }
568
scpi_rx_set_center_frequency(unsigned long long fcent_hz)569 void scpi_rx_set_center_frequency(unsigned long long fcent_hz)
570 {
571 scpi_fprintf(&spectrum_analyzer, ":FREQ:CENT %llu;*WAI\n", fcent_hz);
572 }
573
scpi_rx_set_span_frequency(unsigned long long fspan_hz)574 void scpi_rx_set_span_frequency(unsigned long long fspan_hz)
575 {
576 scpi_fprintf(&spectrum_analyzer, ":FREQ:SPAN %llu;*WAI\n", fspan_hz);
577 }
578
scpi_rx_set_bandwith(unsigned int res_bw_khz,unsigned int vid_bw_khz)579 void scpi_rx_set_bandwith(unsigned int res_bw_khz, unsigned int vid_bw_khz)
580 {
581 scpi_fprintf(&spectrum_analyzer, ":BAND %dkHz;*WAI\n", res_bw_khz);
582 if (!vid_bw_khz)
583 scpi_fprintf(&spectrum_analyzer, ":BAND:VID %dkHz;*WAI\n", res_bw_khz);
584 else
585 scpi_fprintf(&spectrum_analyzer, ":BAND:VID %dkHz;*WAI\n", vid_bw_khz);
586 }
587
scpi_rx_set_bandwith_auto(double ratio)588 void scpi_rx_set_bandwith_auto(double ratio)
589 {
590 scpi_fprintf(&spectrum_analyzer, ":BAND:AUTO ON;*WAI\n");
591 scpi_fprintf(&spectrum_analyzer, ":BAND:RAT %f;*WAI\n", ratio);
592
593 }
594
scpi_rx_setup(void)595 void scpi_rx_setup(void)
596 {
597 static time_t rx_cal_time = 0;
598
599 scpi_fprintf(&spectrum_analyzer, ":DISP:TRACE:Y:RLEVEL %d DBM\n", 10);
600 /* Turn averaging off */
601 scpi_fprintf(&spectrum_analyzer, ":AVER OFF\n");
602 /* Turn off the markers */
603 scpi_fprintf(&spectrum_analyzer, ":DISPLAY:MARK: AOFF\n");
604
605 if (rx_cal_time < time(NULL) && 0) {
606 scpi_fprintf(&spectrum_analyzer, ":CAL:SHOR?\n");
607 /* Wait an hour */
608 rx_cal_time = time(NULL) + (60 * 60);
609 }
610
611 /* trigger source is external (continuous mode off) */
612 scpi_fprintf(&spectrum_analyzer, ":INIT:CONT OFF\n");
613 scpi_rx_trigger_sweep();
614 /* trigger source is internal (continuous mode on) */
615 //scpi_fprintf(&spectrum_analyzer, ":INIT:CONT ON\n");
616 }
617
scpi_rx_set_averaging(int average)618 void scpi_rx_set_averaging(int average)
619 {
620 scpi_fprintf(&spectrum_analyzer, ":AVER:TYPE SCAL\n");
621 scpi_fprintf(&spectrum_analyzer, ":AVER:COUNT %i\n", average);
622 }
623
scpi_rx_set_marker_freq(unsigned int marker,unsigned long long freq)624 int scpi_rx_set_marker_freq(unsigned int marker, unsigned long long freq)
625 {
626 scpi_fprintf(&spectrum_analyzer, "CALC:MARK%d:X %llu;*WAI\n", marker, freq);
627 return scpi_fprintf(&spectrum_analyzer, "CALC:MARK%d:STAT ON;*WAI\n", marker);
628 }
629
scpi_rx_get_marker_level(unsigned marker,bool wait,double * lvl)630 int scpi_rx_get_marker_level(unsigned marker, bool wait, double *lvl)
631 {
632 int ret;
633
634 if (wait)
635 scpi_fprintf(&spectrum_analyzer, "INIT:IMM;*WAI\n");
636
637 scpi_fprintf(&spectrum_analyzer, "CALC:MARK%d:Y?\n", marker);
638 ret = sscanf(spectrum_analyzer.response, "%lf", lvl);
639
640 if (ret == 1)
641 return 0;
642
643 return -1;
644 }
645
scpi_rx_get_marker_freq(unsigned int marker,bool wait,double * lvl)646 int scpi_rx_get_marker_freq(unsigned int marker, bool wait, double *lvl)
647 {
648 int ret;
649
650 if (wait)
651 scpi_fprintf(&spectrum_analyzer, "INIT:IMM;*WAI\n");
652
653 /* scpi_fprintf("CALC:MARK%d:COUNT ON\n", marker); */
654 scpi_fprintf(&spectrum_analyzer, "CALC:MARK%d:COUNT:FREQ?\n", marker);
655 ret = sscanf(spectrum_analyzer.response, "%lf", lvl);
656
657 if (ret == 1)
658 return 0;
659
660 return -1;
661 }
662
663 /* SIGNAL Generator Functions */
tx_freq_set_Hz(struct scpi_instrument * scpi,unsigned long long freq)664 static int tx_freq_set_Hz(struct scpi_instrument *scpi, unsigned long long freq)
665 {
666 return scpi_fprintf(scpi, ":FREQ:CW %llu;*WAI\n", freq);
667 }
668
669 /* Enable a signal generator's output. */
tx_output_set(struct scpi_instrument * scpi,unsigned on)670 static int tx_output_set(struct scpi_instrument *scpi, unsigned on)
671 {
672 return scpi_fprintf(scpi, ":OUTPut %s;*WAI\n", on ? "ON" : "OFF");
673 }
674
675 /* Set the power level for an instrument in dBm. */
tx_mag_set_dBm(struct scpi_instrument * scpi,double lvl)676 static int tx_mag_set_dBm(struct scpi_instrument *scpi, double lvl)
677 {
678 return scpi_fprintf(scpi, ":POW %f DBM;*WAI\n", lvl);
679 }
680
681 /* Retrieve the current power level for an instrument in dBm. */
tx_mag_get_dBm(struct scpi_instrument * scpi,double * lvl)682 static int tx_mag_get_dBm(struct scpi_instrument *scpi, double *lvl)
683 {
684 int ret = 0;
685
686 scpi_fprintf(scpi, ":POW?\n");
687 ret = sscanf(scpi->response, "%lf", lvl);
688
689 if (ret == 1)
690 return 0;
691
692 return -1;
693 }
694
695 #if 0
696 /* Query a given instrument for errors. */
697 static int scpi_query_errors(struct scpi_instrument *scpi)
698 {
699 int ret = 0;
700 gchar **error_status = NULL;
701
702 scpi_fprintf(scpi, ":SYST:ERR:ALL?\n");
703 error_status = g_strsplit(scpi->response, ",", 0);
704 ret = atoi(error_status[0]);
705
706 if (ret != 0)
707 printf("SCPI error %i: %s\n", ret, error_status[1]);
708
709 g_strfreev(error_status);
710 return ret;
711 }
712 #endif
713
714 /* Retrieve the plot markers related to a certain device. */
get_markers(const char * device_ref,struct marker_type * markers)715 static int get_markers(const char *device_ref, struct marker_type *markers)
716 {
717 OscPlot *fft_plot = plugin_find_plot_with_domain(FFT_PLOT);
718 int ret = 0;
719
720 do {
721 ret = plugin_data_capture_of_plot(fft_plot, device_ref, NULL, &markers);
722 } while (ret == -EBUSY);
723 return ret;
724 }
725
726 /* Perform a binary search for a given magnitude in dBm when driving an input
727 * signal into the AD9625.
728 */
tx_mag_seek_dBm(struct mag_seek * mag_seek)729 static int tx_mag_seek_dBm(struct mag_seek *mag_seek)
730 {
731 int ret = 0;
732 double dBm = 0;
733 double difference = 1;
734 struct marker_type *markers;
735 const char *device_ref;
736
737 device_ref = plugin_get_device_by_reference("axi-ad9625-hpc");
738 markers = g_malloc(sizeof(struct marker_type) * (MAX_MARKERS + 2));
739
740 while ((fabs(difference) > 0.01) && (dBm <= mag_seek->max_lvl)) {
741 tx_mag_set_dBm(mag_seek->scpi, dBm);
742 /* ret = scpi_query_errors(mag_seek->scpi); */
743 sleep(1);
744 get_markers(device_ref, markers);
745 difference = mag_seek->target_lvl - markers[0].y;
746 dBm += difference / 2;
747 }
748
749 if ((dBm < mag_seek->min_lvl) || (dBm > mag_seek->max_lvl))
750 ret = 1;
751
752 g_free(markers);
753 mag_seek->dBm = dBm;
754 return ret;
755 }
756
757 /* Programmable Counter functions */
scpi_counter_connected(void)758 static bool scpi_counter_connected(void)
759 {
760 int i;
761
762 if (current_instrument && strlen(current_instrument->model)) {
763 for (i = 0; supported_counters[i] != NULL; i++) {
764 if (supported_counters[i] &&
765 strstr(current_instrument->model, supported_counters[i])) {
766 return true;
767 }
768 }
769 }
770 return false;
771 }
772
773 /* Connect to a supported programmable counter device.
774 * Returns 0 when able to connect to a supported device, otherwise -1.
775 */
scpi_connect_counter(void)776 int scpi_connect_counter(void)
777 {
778 int i = 0, ret = -1;
779 unsigned int tty_node;
780
781 static char *hameg_inputs[] = {
782 "FRA",
783 "FRB",
784 "FRC",
785 NULL
786 };
787
788 current_instrument = &prog_counter;
789 current_instrument->serial = true;
790 current_instrument->id_regex = "";
791 current_instrument->response[0] = 0;
792
793 /* Iterate over tty dev nodes, trying to connect to a supported device. */
794 for (tty_node = 0; tty_node <= 9; tty_node++) {
795 current_instrument->tty_path[strlen(current_instrument->tty_path)-1] = (char)(tty_node + '0');
796 if (access(current_instrument->tty_path, R_OK | W_OK) != -1 &&
797 scpi_connect(current_instrument) == 0 &&
798 scpi_counter_connected()) {
799 if (strstr(current_instrument->model, HAMEG_HM8123)) {
800 /* Select the correct input. */
801 do {
802 if (hameg_inputs[i] == NULL)
803 break;
804 scpi_fprintf(current_instrument, "%s\r\n", hameg_inputs[i++]);
805 sleep(1);
806 scpi_fprintf(current_instrument, "XMT?\r\n");
807 sleep(1);
808 } while (strstr(current_instrument->response, "Not Available"));
809 } else if (strstr(current_instrument->model, AGILENT_53131A)) {
810 /* reset the counter */
811 scpi_fprintf(current_instrument, "*RST\r\n");
812 scpi_fprintf(current_instrument, "*CLS\r\n");
813 scpi_fprintf(current_instrument, "*SRE 0\r\n");
814 scpi_fprintf(current_instrument, "*ESE 0\r\n");
815 scpi_fprintf(current_instrument, ":STAT:PRES\r\n");
816 scpi_fprintf(current_instrument, ":DISP:CALC:MATH:STAT OFF\r\n");
817 scpi_fprintf(current_instrument, ":TRAC SCALE, 1.000000\r\n");
818 /* perform queries on channel 1 and return ascii formatted data */
819 scpi_fprintf(current_instrument, ":FUNC 'FREQ 1'\r\n");
820 scpi_fprintf(current_instrument, ":FORM:DATA ASCII\r\n");
821 }
822 ret = 0;
823 break;
824 }
825 }
826
827 return ret;
828 }
829
830
831 /* Retrieve the current frequency from supported frequency counter devices. */
scpi_counter_get_freq(double * freq,double * target_freq)832 int scpi_counter_get_freq(double *freq, double *target_freq)
833 {
834 int ret = -1;
835 double scale = 1.0;
836 gchar **freq_tokens = NULL;
837 gchar *freq_str = NULL;
838
839 /* Query instrument for the current measured frequency. */
840 if (strstr(current_instrument->model, HAMEG_HM8123)) {
841 ret = scpi_fprintf(current_instrument, "XMT?\r\n");
842 if (ret < 0)
843 return ret;
844
845 /* Output is usually of the form "value scale" where scale is often "GHz". */
846 freq_tokens = g_strsplit(current_instrument->response, " ", 2);
847 if (freq_tokens[0] != NULL)
848 freq_str = strdup(freq_tokens[0]);
849
850 /* Scale the value returned from the device to Hz */
851 if (strstr(freq_tokens[1], "GHz"))
852 scale = pow(10.0, 9);
853 else if (strstr(freq_tokens[1], "MHz"))
854 scale = pow(10.0, 6);
855 else if (strstr(freq_tokens[1], "KHz"))
856 scale = pow(10.0, 3);
857
858 g_strfreev(freq_tokens);
859 } else if (strstr(current_instrument->model, AGILENT_53131A) ||
860 strstr(current_instrument->model, FLUKE_PM6681)) {
861 /* Output is in scientific E notation, Hz scale by default. */
862 if (target_freq)
863 ret = scpi_fprintf(current_instrument, ":MEASURE:FREQ? %E HZ, 1 HZ\r\n", *target_freq);
864 else
865 ret = scpi_fprintf(current_instrument, ":MEASURE:FREQ?\r\n");
866 if (ret < 0)
867 return ret;
868
869 freq_str = strdup(current_instrument->response);
870 /* Re-enable continuous output otherwise we leave the display in a
871 * stopped state.
872 */
873 scpi_fprintf(current_instrument, ":INIT:CONT ON\r\n");
874 } else {
875 /* No supported device attached */
876 return -ENODEV;
877 }
878
879 ret = sscanf(freq_str, "%lf", freq);
880
881 g_free(freq_str);
882
883 if (ret == 1) {
884 *freq *= scale;
885 return 0;
886 }
887 return -1;
888 }
889
890 /*
891 * Save/Restore stuff
892 */
893
mag_input_seek(const char * value)894 static int mag_input_seek(const char *value)
895 {
896 int ret;
897 GThread *thr;
898 struct mag_seek *mag_seek;
899 gchar **mag_min_max = g_strsplit(value, " ", 0);
900
901 mag_seek = g_malloc(sizeof(struct mag_seek));
902 mag_seek->scpi = &signal_generator;
903 mag_seek->target_lvl = atof(mag_min_max[0]);
904 mag_seek->min_lvl = atof(mag_min_max[1]);
905 mag_seek->max_lvl = atof(mag_min_max[2]);
906 mag_seek->dBm = 0;
907
908 thr = g_thread_new("Mag_seek_thread",
909 (void *) &tx_mag_seek_dBm, mag_seek);
910
911 while (mag_seek->dBm == 0)
912 gtk_main_iteration();
913
914 ret = (int) (uintptr_t) g_thread_join(thr);
915 g_free(mag_seek);
916 g_strfreev(mag_min_max);
917 return ret;
918 }
919
scpi_handle(struct osc_plugin * plugin,int line,const char * attrib,const char * value)920 static int scpi_handle(struct osc_plugin *plugin, int line, const char *attrib, const char *value)
921 {
922 if (!strncmp(attrib, "rx.", sizeof("rx.") - 1))
923 current_instrument = &spectrum_analyzer;
924 else if (!strncmp(attrib, "tx.", sizeof("tx.") - 1))
925 current_instrument = &signal_generator;
926 else
927 return -EINVAL;
928
929 attrib += sizeof("rx.") - 1;
930
931 if (MATCH_ATTRIB("serial")) {
932 current_instrument->serial = !!atoi(value);
933 } else if (MATCH_ATTRIB("network")) {
934 current_instrument->network = !!atoi(value);
935 } else if (MATCH_ATTRIB("id_regex")) {
936 if (current_instrument->id_regex)
937 free(current_instrument->id_regex);
938 current_instrument->id_regex = strdup(value);
939 } else if (MATCH_ATTRIB("ip_addr")) {
940 if (current_instrument->ip_address)
941 free(current_instrument->ip_address);
942 current_instrument->ip_address = strdup(value);
943 } else if (MATCH_ATTRIB("tty_path")) {
944 if (current_instrument->tty_path)
945 free(current_instrument->tty_path);
946 current_instrument->tty_path = strdup(value);
947 } else if (MATCH_ATTRIB("gpib_addr")) {
948 current_instrument->gpib_addr = atoi(value);
949 } else if (MATCH_ATTRIB("connect")) {
950 if (atoi(value) == 1)
951 return scpi_connect(current_instrument);
952 } else if (current_instrument == &signal_generator) {
953 if (MATCH_ATTRIB("freq")) {
954 tx_freq_set_Hz(current_instrument, atoll(value));
955 } else if (MATCH_ATTRIB("mag")) {
956 tx_mag_set_dBm(current_instrument, atof(value));
957 } else if (MATCH_ATTRIB("mag_input_seek")) {
958 return mag_input_seek(value);
959 } else if (MATCH_ATTRIB("log.dBm")) {
960 double lvl;
961 FILE *f;
962
963 tx_mag_get_dBm(current_instrument, &lvl);
964
965 f = fopen(value, "a");
966 if (!f)
967 return -errno;
968
969 fprintf(f, "%.2lf\n", lvl);
970 fclose(f);
971 } else if (MATCH_ATTRIB("on")) {
972 tx_output_set(current_instrument, atoi(value));
973 } else {
974 return -EINVAL;
975 }
976 } else { /* current_instrument == &spectrum_analyzer */
977 if (MATCH_ATTRIB("setup")) {
978 scpi_rx_setup();
979 } else if (MATCH_ATTRIB("center")) {
980 scpi_rx_set_center_frequency(atoll(value));
981 } else if (MATCH_ATTRIB("span")) {
982 scpi_rx_set_span_frequency(atoll(value));
983 } else if (!strncmp(attrib, "marker", sizeof("marker") - 1)) {
984 int i = atoi(&attrib[sizeof("marker") - 1]);
985 long long j = atoll(value);
986
987 if (i && j)
988 scpi_rx_set_marker_freq(i, j);
989 else
990 printf("problems with %s = %s\n",
991 attrib, value);
992 } else if (!strncmp(attrib, "log.marker",
993 sizeof("log.marker") - 1)) {
994 int i;
995
996 i = atoi(&attrib[sizeof("log.marker") - 1]);
997 if (i) {
998 double lvl;
999 FILE *f;
1000
1001 scpi_rx_get_marker_level(i, true, &lvl);
1002 f = fopen(value, "a");
1003 if (!f)
1004 return -errno;
1005
1006 fprintf (f, "%f, ", lvl);
1007 fclose (f);
1008 }
1009 } else if (!strncmp(attrib, "test.marker",
1010 sizeof("test.marker") - 1)) {
1011 int i;
1012
1013 i = atoi(&attrib[sizeof("test.marker") - 1]);
1014 if (i) {
1015 gchar **min_max;
1016 double lvl;
1017
1018 scpi_rx_get_marker_level(i, true, &lvl);
1019 min_max = g_strsplit(value, " ", 0);
1020 if (lvl >= atof(min_max[1]) &&
1021 lvl <= atof(min_max[0])) {
1022 printf("Marker%i (%f) didn't match requirements (%f - %f)\n",
1023 i, lvl, atof(min_max[0]),
1024 atof(min_max[1]));
1025 return -EINVAL;
1026 }
1027 }
1028 } else {
1029 return -EINVAL;
1030 }
1031 }
1032
1033 return 0;
1034 }
1035
1036 /*
1037 * All the GUI/Glade stuff
1038 */
1039
1040 static GtkWidget *scpi_radio_conf;
1041 static GtkWidget *scpi_none_radio, *scpi_tty_radio, *scpi_net_radio;
1042 static GtkWidget *scpi_output, *scpi_regex;
1043 static GtkWidget *scpi_ip_addr;
1044 static GtkWidget *scpi_serial_tty, *scpi_gpib_addr;
1045 static GtkWidget *scpi_id;
1046
1047 static char *cmd_to_send = NULL;
1048
1049 #define NO_CONNECT 0
1050 #define NETWORK_CONNECT 1
1051 #define TTY_CONNECT 2
1052
1053 #define SCPI_NONE 0
1054 #define SCPI_TX 1
1055 #define SCPI_RX 2
1056
load_instrument(struct scpi_instrument * scpi)1057 static void load_instrument (struct scpi_instrument *scpi)
1058 {
1059 char tmp[128];
1060
1061 current_instrument = scpi;
1062
1063 if (!current_instrument->serial && !current_instrument->network) {
1064 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scpi_none_radio), TRUE);
1065 } else if (current_instrument->serial) {
1066 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scpi_tty_radio), TRUE);
1067 } else if (current_instrument->network) {
1068 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scpi_net_radio), TRUE);
1069 }
1070
1071 if (current_instrument->ip_address && strlen(current_instrument->ip_address)) {
1072 if (current_instrument->main_port &&
1073 current_instrument->main_port != DEFAULT_SCPI_IP_PORT)
1074 sprintf(tmp, "%s:%i", current_instrument->ip_address, current_instrument->main_port);
1075 else
1076 sprintf(tmp, "%s", current_instrument->ip_address);
1077
1078 gtk_entry_set_text(GTK_ENTRY(scpi_ip_addr), (const gchar *)tmp);
1079 }
1080
1081 if (current_instrument->tty_path && strlen(current_instrument->tty_path))
1082 gtk_entry_set_text(GTK_ENTRY(scpi_serial_tty), (const gchar *)current_instrument->tty_path);
1083
1084 sprintf(tmp, "%i", current_instrument->gpib_addr);
1085 gtk_entry_set_text(GTK_ENTRY(scpi_gpib_addr), (const gchar *)tmp);
1086
1087 }
1088
instrument_type_cb(GtkComboBox * box)1089 static void instrument_type_cb (GtkComboBox *box)
1090 {
1091 gint item;
1092
1093 item = gtk_combo_box_get_active(box);
1094 switch (item) {
1095 case SCPI_NONE:
1096 current_instrument = NULL;
1097 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scpi_none_radio), TRUE);
1098 gtk_widget_hide(scpi_radio_conf);
1099 gtk_widget_hide(scpi_output);
1100 break;
1101 case SCPI_TX:
1102 load_instrument(&signal_generator);
1103 gtk_widget_show(scpi_radio_conf);
1104 if (current_instrument->control_socket)
1105 gtk_widget_show(scpi_output);
1106 else
1107 gtk_widget_hide(scpi_output);
1108 break;
1109 case SCPI_RX:
1110 load_instrument(&spectrum_analyzer);
1111 gtk_widget_show(scpi_radio_conf);
1112 if (current_instrument->ttyfd)
1113 gtk_widget_show(scpi_output);
1114 else
1115 gtk_widget_hide(scpi_output);
1116 break;
1117 default: {
1118 gchar *box_text =
1119 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(box));
1120 printf("Unknown selection in %s:%s: %s\n",
1121 __FILE__, __func__, box_text);
1122 g_free(box_text);
1123 break;
1124 }
1125 }
1126 }
1127
scpi_text_entry_cb(GtkEntry * box,int data)1128 static void scpi_text_entry_cb (GtkEntry *box, int data)
1129 {
1130 GdkColor green = {
1131 .red = 0,
1132 .green = 0xFFFF,
1133 .blue = 0,
1134 };
1135 GdkColor red = {
1136 .red = 0xFFFF,
1137 .green = 0,
1138 .blue = 0,
1139 };
1140
1141 switch (data) {
1142 case 0:
1143 if (current_instrument->ip_address)
1144 free(current_instrument->ip_address);
1145
1146 current_instrument->ip_address = strdup(gtk_entry_get_text(box));
1147 break;
1148 case 1:
1149 if (current_instrument->tty_path)
1150 free(current_instrument->tty_path);
1151
1152 current_instrument->tty_path = strdup(gtk_entry_get_text(box));
1153 break;
1154 case 2:
1155 current_instrument->gpib_addr = atoi(gtk_entry_get_text(box));
1156 break;
1157 case 3:
1158 if (current_instrument->id_regex)
1159 free(current_instrument->id_regex);
1160
1161 current_instrument->id_regex = strdup(gtk_entry_get_text(box));
1162
1163 if (strstr(gtk_label_get_text(GTK_LABEL(scpi_id)), current_instrument->id_regex))
1164 gtk_widget_modify_text(GTK_WIDGET(box), GTK_STATE_NORMAL, &green);
1165 else
1166 gtk_widget_modify_text(GTK_WIDGET(box), GTK_STATE_NORMAL, &red);
1167 break;
1168 case 4:
1169 if (cmd_to_send)
1170 free(cmd_to_send);
1171 cmd_to_send = strdup(gtk_entry_get_text(box));
1172 break;
1173 default:
1174 printf("Unknown selection in %s:%s: %i\n",
1175 __FILE__, __func__, data);
1176 break;
1177 }
1178 }
1179
init_scpi_device(struct scpi_instrument * device)1180 static void init_scpi_device(struct scpi_instrument *device)
1181 {
1182 memset(device, 0, sizeof(struct scpi_instrument));
1183 device->ip_address = strdup(DEFAULT_SCPI_IP_ADDR);
1184 device->main_port = DEFAULT_SCPI_IP_PORT;
1185 device->tty_path = strdup(DEFAULT_SCPI_TTY);
1186 device->gpib_addr = DEFAULT_SCPI_GPIB;
1187 device->response = malloc(SOCKETS_BUFFER_SIZE);
1188 if (!device->response) {
1189 printf("%s:%s: malloc fail\n", __FILE__, __func__);
1190 exit (-1);
1191 }
1192 memset(device->response, 0, SOCKETS_BUFFER_SIZE);
1193 }
1194
connect_clicked_cb(void)1195 static void connect_clicked_cb(void)
1196 {
1197 int i, ret = -1;
1198
1199 if(current_instrument->network && current_instrument->ip_address) {
1200 if (!current_instrument->main_port)
1201 current_instrument->main_port = DEFAULT_SCPI_IP_PORT;
1202
1203 ret = network_connect(current_instrument);
1204
1205 }
1206
1207 if(current_instrument->serial && current_instrument->tty_path) {
1208 ret = tty_connect(current_instrument);
1209 }
1210
1211 if (ret == 0) {
1212 scpi_fprintf(current_instrument, "*CLS;*RST;*IDN?\r\n");
1213 if (strlen(current_instrument->response)) {
1214 current_instrument->model = current_instrument->response;
1215 gtk_label_set_text(GTK_LABEL(scpi_id), current_instrument->response);
1216 for (i = 0; supported_spectrum_analyzers[i] != NULL; i++) {
1217 if (supported_spectrum_analyzers[i] &&
1218 !strcmp(supported_spectrum_analyzers[i], current_instrument->response)) {
1219 gtk_label_set_text(GTK_LABEL(scpi_regex), current_instrument->response);
1220 break;
1221 }
1222 }
1223 }
1224
1225 if (current_instrument->id_regex)
1226 gtk_entry_set_text(GTK_ENTRY(scpi_regex), current_instrument->id_regex);
1227 else
1228 gtk_entry_set_text(GTK_ENTRY(scpi_regex), "");
1229 g_signal_emit_by_name(scpi_regex, "changed");
1230
1231 gtk_widget_show(scpi_output);
1232 }
1233
1234 }
1235
scpi_radio_cb(GtkRadioButton * button,int data)1236 static void scpi_radio_cb (GtkRadioButton *button, int data)
1237 {
1238 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
1239 return;
1240
1241 switch (data) {
1242 case NO_CONNECT:
1243 if (current_instrument) {
1244 current_instrument->serial = FALSE;
1245 current_instrument->network = FALSE;
1246 }
1247 break;
1248 case NETWORK_CONNECT:
1249 current_instrument->serial = FALSE;
1250 current_instrument->network = TRUE;
1251 if (current_instrument->control_socket)
1252 gtk_widget_show(scpi_output);
1253 else
1254 gtk_widget_hide(scpi_output);
1255 break;
1256 case TTY_CONNECT:
1257 current_instrument->serial = TRUE;
1258 current_instrument->network = FALSE;
1259 if (current_instrument->ttyfd)
1260 gtk_widget_show(scpi_output);
1261 else
1262 gtk_widget_hide(scpi_output);
1263 break;
1264 default:
1265 printf("Unknown selection in %s:%s\n", __FILE__, __func__);
1266 break;
1267 }
1268 }
1269
scpi_cmd_cb(GtkButton * button,GtkEntry * box)1270 static void scpi_cmd_cb (GtkButton *button, GtkEntry *box)
1271 {
1272 const char *buf = gtk_entry_get_text(box);
1273
1274 if (!buf || !strlen(buf))
1275 return;
1276
1277 current_instrument->response[0] = 0;
1278 scpi_fprintf(current_instrument, "%s\r\n", buf);
1279
1280 printf("sent: '%s'\n", buf);
1281 if (current_instrument->response)
1282 printf("received: '%s'\n", current_instrument->response);
1283 }
1284
1285 /*
1286 * Main function
1287 */
scpi_init(struct osc_plugin * plugin,GtkWidget * notebook,const char * ini_fn)1288 static GtkWidget * scpi_init(struct osc_plugin *plugin, GtkWidget *notebook, const char *ini_fn)
1289 {
1290 GtkBuilder *builder;
1291 GtkWidget *scpi_panel;
1292 GtkWidget *instrument_type, *connect;
1293 GtkWidget *scpi_cmd;
1294 GtkWidget *scpi_play;
1295 GtkWidget *tty_conf, *network_conf;
1296
1297 init_scpi_device(&signal_generator);
1298 init_scpi_device(&spectrum_analyzer);
1299 init_scpi_device(&prog_counter);
1300
1301 builder = gtk_builder_new();
1302
1303 if (osc_load_glade_file(builder, "scpi") < 0)
1304 return NULL;
1305
1306 scpi_panel = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_panel"));
1307 network_conf = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_network_conf"));
1308 tty_conf = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_tty_conf"));
1309
1310 scpi_none_radio = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_connect_none"));
1311 scpi_net_radio = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_network"));
1312 scpi_tty_radio = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_tty"));
1313
1314 scpi_radio_conf = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_radio_conf"));
1315 instrument_type = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_type"));
1316 connect = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_connect"));
1317 scpi_output = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_output"));
1318 scpi_ip_addr = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_ip_addr"));
1319 scpi_gpib_addr = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_gpib_addr"));
1320 scpi_serial_tty = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_serial_tty"));
1321 scpi_id = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_id"));
1322 scpi_regex = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_regex"));
1323 scpi_cmd = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_cmd"));
1324 scpi_play = GTK_WIDGET(gtk_builder_get_object(builder, "scpi_play"));
1325
1326 gtk_widget_show_all(scpi_panel);
1327
1328 g_object_bind_property(scpi_net_radio, "active", network_conf, "visible", 0);
1329 g_signal_connect(scpi_net_radio, "toggled", G_CALLBACK(scpi_radio_cb), (gpointer) 1);
1330
1331 g_object_bind_property(scpi_tty_radio, "active", tty_conf, "visible", 0);
1332 g_signal_connect(scpi_tty_radio, "toggled", G_CALLBACK(scpi_radio_cb), (gpointer) 2);
1333
1334 g_object_bind_property(scpi_none_radio, "active", connect, "visible", G_BINDING_INVERT_BOOLEAN);
1335 g_object_bind_property(scpi_none_radio, "active", scpi_output, "visible", G_BINDING_INVERT_BOOLEAN);
1336
1337 g_signal_connect(scpi_none_radio, "toggled", G_CALLBACK(scpi_radio_cb), (gpointer) 0);
1338
1339 g_signal_connect(instrument_type, "changed",
1340 G_CALLBACK(instrument_type_cb), (gpointer) instrument_type);
1341
1342 g_signal_connect(scpi_ip_addr, "changed",
1343 G_CALLBACK(scpi_text_entry_cb), (gpointer) 0);
1344
1345 g_signal_connect(scpi_serial_tty, "changed",
1346 G_CALLBACK(scpi_text_entry_cb), (gpointer) 1);
1347
1348 g_signal_connect(scpi_gpib_addr, "changed",
1349 G_CALLBACK(scpi_text_entry_cb), (gpointer) 2);
1350
1351 g_signal_connect(scpi_regex, "changed",
1352 G_CALLBACK(scpi_text_entry_cb), (gpointer) 3);
1353
1354 g_signal_connect(scpi_cmd, "changed",
1355 G_CALLBACK(scpi_text_entry_cb), (gpointer) 4);
1356
1357 g_signal_connect(connect, "clicked",
1358 G_CALLBACK(connect_clicked_cb), NULL);
1359
1360 g_signal_connect(scpi_play, "clicked",
1361 G_CALLBACK(scpi_cmd_cb), scpi_cmd);
1362
1363 gtk_combo_box_set_active(GTK_COMBO_BOX(instrument_type), 0);
1364
1365 gtk_widget_hide(network_conf);
1366 gtk_widget_hide(tty_conf);
1367 gtk_widget_hide(scpi_output);
1368 gtk_widget_hide(connect);
1369
1370 return scpi_panel;
1371 }
1372
scpi_save_profile(const struct osc_plugin * plugin,const char * ini_fn)1373 static void scpi_save_profile(const struct osc_plugin *plugin, const char *ini_fn)
1374 {
1375 FILE *f = fopen(ini_fn, "a");
1376 if (!f)
1377 return;
1378
1379 fprintf(f,
1380 "\n[" THIS_DRIVER "]\n"
1381 "tx.serial = %i\n"
1382 "tx.network = %i\n"
1383 "tx.id_regex = %s\n"
1384 "tx.ip_addr = %s\n"
1385 "tx.tty_path = %s\n"
1386 "tx.gpib_addr = %i\n"
1387 "rx.serial = %i\n"
1388 "rx.network = %i\n"
1389 "rx.id_regex = %s\n"
1390 "rx.ip_addr = %s\n"
1391 "rx.tty_path = %s\n"
1392 "rx.gpib_addr = %i\n",
1393 signal_generator.serial,
1394 signal_generator.network,
1395 signal_generator.id_regex,
1396 signal_generator.ip_address,
1397 signal_generator.tty_path,
1398 signal_generator.gpib_addr,
1399 spectrum_analyzer.serial,
1400 spectrum_analyzer.network,
1401 spectrum_analyzer.id_regex,
1402 spectrum_analyzer.ip_address,
1403 spectrum_analyzer.tty_path,
1404 spectrum_analyzer.gpib_addr);
1405 fclose(f);
1406 }
1407
scpi_destroy(struct osc_plugin * plugin,const char * ini_fn)1408 static void scpi_destroy(struct osc_plugin *plugin, const char *ini_fn)
1409 {
1410 if (ini_fn)
1411 scpi_save_profile(NULL, ini_fn);
1412
1413 if (current_instrument) {
1414 if (current_instrument->model)
1415 free(current_instrument->model);
1416 if (current_instrument->ip_address)
1417 free(current_instrument->ip_address);
1418 if (current_instrument->tty_path)
1419 free(current_instrument->tty_path);
1420 }
1421 }
1422
1423 /* This is normally used for test, and the GUI is used for
1424 * setting up the test infrastructure
1425 */
scpi_identify(const struct osc_plugin * plugin)1426 static bool scpi_identify(const struct osc_plugin *plugin)
1427 {
1428 /*
1429 * Always return false
1430 * If you want this to load, you are required to set the
1431 * 'OSC_FORCE_PLUGIN' environmental variable
1432 */
1433 return false;
1434 }
1435
1436 struct osc_plugin plugin = {
1437 .name = THIS_DRIVER,
1438 .identify = scpi_identify,
1439 .init = scpi_init,
1440 .handle_item = scpi_handle,
1441 .save_profile = scpi_save_profile,
1442 .destroy = scpi_destroy,
1443 };
1444