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