1 /**
2  * Copyright (C) 2012-2015 Analog Devices, Inc.
3  *
4  * Licensed under the GPL-2.
5  *
6  **/
7 #include <stdio.h>
8 
9 #include <gtk/gtk.h>
10 #include <gtkdatabox.h>
11 #include <glib.h>
12 #include <gtkdatabox_grid.h>
13 #include <gtkdatabox_points.h>
14 #include <gtkdatabox_lines.h>
15 #include <math.h>
16 #include <stdint.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <time.h>
25 
26 #include <ad9361.h>
27 
28 #include "../datatypes.h"
29 #include "../osc.h"
30 #include "../iio_widget.h"
31 #include "../libini2.h"
32 #include "../osc_plugin.h"
33 #include "../config.h"
34 #include "dac_data_manager.h"
35 
36 #define THIS_DRIVER "Spectrum Analyzer"
37 #define PHY_DEVICE "ad9361-phy"
38 #define CAP_DEVICE "cf-ad9361-lpc"
39 #define FIR_FILTER "61_44_28MHz.ftr"
40 #define ARRAY_SIZE(x) (!sizeof(x) ?: sizeof(x) / sizeof((x)[0]))
41 #define MHZ_TO_HZ(x) ((x) * 1000000)
42 #define MHZ_TO_KHZ(x) ((x) * 1000)
43 #define HZ_TO_MHZ(x) ((x) / 1E6)
44 
45 #define HANNING_ENBW 1.50
46 
47 enum receivers {
48 	RX1,
49 	RX2
50 };
51 
52 typedef struct _plugin_setup {
53 	double start_freq;
54 	double stop_freq;
55 	double resolution_bw;
56 	unsigned int fft_size;
57 	enum receivers rx;
58 	GSList *rx_profiles;
59 	unsigned int profile_count;
60 	unsigned int profile_slot;
61 } plugin_setup;
62 
63 typedef struct _fastlock_profile {
64 	unsigned index;
65 	long long frequency;
66 	char data[66];
67 } fastlock_profile;
68 
69 /* Plugin Global Variables */
70 static const double sweep_freq_step = 56; /* 56 MHz */
71 static const long long sampling_rate = 61440000; /* 61.44 MSPS */
72 
73 static struct iio_context *ctx;
74 static struct iio_device *dev, *cap;
75 static struct iio_channel *alt_ch0;
76 static struct iio_buffer *capture_buffer;
77 static bool is_2rx_2tx;
78 static char *rx_fastlock_store_name;
79 static char *rx_fastlock_save_name;
80 static GtkWidget *spectrum_window;
81 static plugin_setup psetup;
82 
83 /* Plugin Threads */
84 static GThread *freq_sweep_thread;
85 static GThread *capture_thread;
86 static GThread *fft_thread;
87 
88 /* Threads Synchronization */
89 static GCond profile_applied_cond,
90 		capture_done_cond,
91 		demux_done_cond,
92 		fft_done_cond;
93 static GMutex profile_applied_mutex,
94 		capture_done_mutex,
95 		demux_done_mutex,
96 		fft_done_mutex;
97 static bool profile_applied,
98 		capture_done,
99 		demux_done,
100 		fft_done;
101 static bool kill_sweep_thread,
102 		kill_capture_thread,
103 		kill_fft_thread;
104 
105 /* Control Widgets */
106 static GtkWidget *center_freq;
107 static GtkWidget *freq_bw;
108 static GtkWidget *available_RBWs;
109 static GtkWidget *receiver1;
110 static GtkWidget *start_button;
111 static GtkWidget *stop_button;
112 
113 /* Default Plugin Variables */
114 static gint this_page;
115 static GtkNotebook *nbook;
116 static GtkWidget *analyzer_panel;
117 static gboolean plugin_detached;
118 
119 #if DEBUG
120 GTimer *gtimer;
121 double loop_durations_sum;
122 unsigned long long loop_count;
123 #endif
124 
demux_sample(const struct iio_channel * chn,void * sample,size_t size,void * d)125 static ssize_t demux_sample(const struct iio_channel *chn,
126 		void *sample, size_t size, void *d)
127 {
128 	struct extra_info *info = iio_channel_get_data(chn);
129 	struct extra_dev_info *dev_info = iio_device_get_data(info->dev);
130 	const struct iio_data_format *format = iio_channel_get_data_format(chn);
131 
132 	/* Prevent buffer overflow */
133 	if ((unsigned long) info->offset == (unsigned long) dev_info->sample_count)
134 		return 0;
135 
136 	if (size == 1) {
137 		int8_t val;
138 		iio_channel_convert(chn, &val, sample);
139 		if (format->is_signed)
140 			*(info->data_ref + info->offset++) = (gfloat) val;
141 		else
142 			*(info->data_ref + info->offset++) = (gfloat) (uint8_t)val;
143 	} else if (size == 2) {
144 		int16_t val;
145 		iio_channel_convert(chn, &val, sample);
146 		if (format->is_signed)
147 			*(info->data_ref + info->offset++) = (gfloat) val;
148 		else
149 			*(info->data_ref + info->offset++) = (gfloat) (uint16_t)val;
150 	} else {
151 		int32_t val;
152 		iio_channel_convert(chn, &val, sample);
153 		if (format->is_signed)
154 			*(info->data_ref + info->offset++) = (gfloat) val;
155 		else
156 			*(info->data_ref + info->offset++) = (gfloat) (uint32_t)val;
157 	}
158 
159 	return size;
160 }
161 
device_set_rx_sampling_freq(struct iio_device * dev,long long freq_hz)162 static void device_set_rx_sampling_freq(struct iio_device *dev, long long freq_hz)
163 {
164 	struct iio_channel *ch0;
165 
166 	ch0 = iio_device_find_channel(dev, "voltage0", false);
167 	if (ch0)
168 		iio_channel_attr_write_longlong(ch0, "sampling_frequency", freq_hz);
169 	else
170 		fprintf(stderr, "Failed to retrieve iio channel in %s\n", __func__);
171 }
172 
device_get_rx_sampling_freq(struct iio_device * dev)173 static long long device_get_rx_sampling_freq(struct iio_device *dev)
174 {
175 	long long freq_hz = 0;
176 	struct iio_channel *ch0;
177 
178 	ch0 = iio_device_find_channel(dev, "voltage0", false);
179 	if (ch0)
180 		iio_channel_attr_read_longlong(ch0, "sampling_frequency", &freq_hz);
181 	else
182 		fprintf(stderr, "Failed to retrieve iio channel in %s\n", __func__);
183 
184 	return freq_hz;
185 }
186 
187 /* Generate available values for the Resolution Bandwidth.
188  * RBW = Sampling Rate / N FFT Bins
189  * In oscplot.c FFT sizes are: 32 <= N <= 65536
190  */
comboboxtext_rbw_fill(GtkComboBoxText * box,double sampling_freq)191 static void comboboxtext_rbw_fill(GtkComboBoxText *box, double sampling_freq)
192 {
193 	GtkListStore *liststore;
194 	unsigned fft_size;
195 	char buf[64];
196 
197 	g_return_if_fail(box);
198 
199 	liststore = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(box)));
200 	gtk_list_store_clear(liststore);
201 
202 	for (fft_size = 65536; fft_size >= 32; fft_size >>= 1) {
203 		snprintf(buf, sizeof(buf), "%.3f",
204 			MHZ_TO_KHZ(sampling_freq) / (double)fft_size);
205 		gtk_combo_box_text_append_text(box, buf);
206 	}
207 }
208 
209 #if DEBUG
log_before_sweep_starts(plugin_setup * setup)210 static void log_before_sweep_starts(plugin_setup *setup)
211 {
212 	FILE *fp;
213 	GSList *node;
214 
215 	fp = fopen("spectrum_setup_log.txt", "w");
216 	if (!fp) {
217 		fprintf(stderr, "Could not open/create spectrum_setup_log.txt "
218 				"file for writing\n");
219 		return;
220 	}
221 
222 	fprintf(fp, "Spectrum Setup Log File\n\n");
223 	fprintf(fp, "Profile count: %d\n", g_slist_length(setup->rx_profiles));
224 	for (node = setup->rx_profiles; node; node = g_slist_next(node)) {
225 		fastlock_profile *profile = node->data;
226 		fprintf(fp, "Index: %u\n", profile->index);
227 		fprintf(fp, "Frequency: %lld\n", profile->frequency);
228 		fprintf(fp, "Raw Data: %s\n", profile->data);
229 	}
230 
231 	fclose(fp);
232 }
233 #endif
234 
init_device_list(struct iio_context * ctx)235 static void init_device_list(struct iio_context *ctx)
236 {
237 	unsigned int i, j, num_devices;
238 
239 	num_devices = iio_context_get_devices_count(ctx);
240 
241 	for (i = 0; i < num_devices; i++) {
242 		struct iio_device *dev = iio_context_get_device(ctx, i);
243 		unsigned int nb_channels = iio_device_get_channels_count(dev);
244 		struct extra_dev_info *dev_info = calloc(1, sizeof(*dev_info));
245 		iio_device_set_data(dev, dev_info);
246 		dev_info->input_device = is_input_device(dev);
247 		dev_info->plugin_fft_corr = 20 * log10(1/sqrt(HANNING_ENBW));
248 
249 		for (j = 0; j < nb_channels; j++) {
250 			struct iio_channel *ch = iio_device_get_channel(dev, j);
251 			struct extra_info *info = calloc(1, sizeof(*info));
252 			info->dev = dev;
253 			iio_channel_set_data(ch, info);
254 		}
255 	}
256 }
257 
plugin_gather_user_setup(plugin_setup * setup)258 static bool plugin_gather_user_setup(plugin_setup *setup)
259 {
260 	double center, bw, start_freq, stop_freq;
261 	int rbw_index;
262 	bool data_is_new = false;
263 
264 	g_return_val_if_fail(setup, false);
265 
266 	center = gtk_spin_button_get_value(GTK_SPIN_BUTTON(center_freq));
267 	bw = gtk_spin_button_get_value(GTK_SPIN_BUTTON(freq_bw));
268 	rbw_index = gtk_combo_box_get_active(GTK_COMBO_BOX(available_RBWs));
269 	start_freq = center - bw / 2;
270 	stop_freq = center + bw / 2;
271 	setup->fft_size = 65536 >> rbw_index;
272 
273 	if ((setup->start_freq != start_freq) || (setup->stop_freq != stop_freq)) {
274 		setup->start_freq = start_freq;
275 		setup->stop_freq = stop_freq;
276 		data_is_new = true;
277 	}
278 
279 	if (!is_2rx_2tx) {
280 		setup->rx = RX1;
281 	} else {
282 		if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(receiver1)))
283 			setup->rx = RX1;
284 		else
285 			setup->rx = RX2;
286 	}
287 	setup->profile_slot = 1;
288 
289 	return data_is_new;
290 }
291 
build_profiles_for_entire_sweep(plugin_setup * setup)292 static void build_profiles_for_entire_sweep(plugin_setup *setup)
293 {
294 	double start, stop, step, f;
295 	fastlock_profile *profile;
296 	static unsigned char prev_alc = 0;
297 	unsigned char alc;
298 	char *last_byte;
299 	unsigned int i = 0;
300 
301 	g_return_if_fail(setup);
302 
303 	/* Clear any previous profiles */
304 	g_slist_free_full(setup->rx_profiles, (GDestroyNotify)free);
305 	setup->rx_profiles = NULL;
306 
307 	start = setup->start_freq + sweep_freq_step / 2;
308 	stop = setup->stop_freq;
309 	step = sweep_freq_step;
310 
311 	for (f = start; (f - sweep_freq_step / 2) < stop; f += step) {
312 		iio_channel_attr_write_longlong(alt_ch0, "frequency",
313 				(long long)MHZ_TO_HZ(f));
314 		iio_channel_attr_write_longlong(alt_ch0,
315 					rx_fastlock_store_name, 0);
316 		profile = malloc(sizeof(fastlock_profile));
317 		if (!profile)
318 			return;
319 		iio_channel_attr_read(alt_ch0, rx_fastlock_save_name,
320 					profile->data, sizeof(profile->data));
321 		profile->frequency = (long long)MHZ_TO_HZ(f);
322 		profile->index = i++;
323 		setup->rx_profiles = g_slist_prepend(setup->rx_profiles, profile);
324 
325 		/* Make sure two consecutive profiles do not have the same ALC.
326 		 * Disregard the LBS of the ALC when comparing.
327 		   More on: https://ez.analog.com/message/151702#151702 */
328 		last_byte = g_strrstr(profile->data, ",") + 1;
329 		alc = atoi(last_byte);
330 		if (abs(alc - prev_alc) < 2)
331 			alc += 2;
332 		prev_alc = alc;
333 		sprintf(last_byte, "%d", alc);
334 
335 	}
336 	setup->rx_profiles = g_slist_reverse(setup->rx_profiles);
337 	setup->profile_count = g_slist_length(setup->rx_profiles);
338 	#if DEBUG
339 	log_before_sweep_starts(setup);
340 	#endif
341 }
342 
configure_spectrum_window(plugin_setup * setup)343 static void configure_spectrum_window(plugin_setup *setup)
344 {
345 	unsigned int i;
346 	bool enable;
347 
348 	g_return_if_fail(setup);
349 
350 	osc_plot_spect_mode(OSC_PLOT(spectrum_window), true);
351 	osc_plot_set_domain(OSC_PLOT(spectrum_window), SPECTRUM_PLOT);
352 	osc_plot_set_sample_count(OSC_PLOT(spectrum_window), setup->fft_size);
353 	for (i = 0; i < iio_device_get_channels_count(cap); i++) {
354 		enable = i / 2 == setup->rx;
355 		osc_plot_set_channel_state(OSC_PLOT(spectrum_window), CAP_DEVICE,
356 			i, enable);
357 	}
358 	osc_plot_spect_set_len(OSC_PLOT(spectrum_window), setup->profile_count);
359 	osc_plot_spect_set_start_f(OSC_PLOT(spectrum_window), setup->start_freq);
360 	osc_plot_spect_set_filter_bw(OSC_PLOT(spectrum_window), sweep_freq_step);
361 	osc_plot_set_visible(OSC_PLOT(spectrum_window), true);
362 }
363 
364 static void spectrum_window_destroyed_cb(OscPlot *plot);
build_spectrum_window(plugin_setup * setup)365 static void build_spectrum_window(plugin_setup *setup)
366 {
367 	g_return_if_fail(setup);
368 
369 	spectrum_window = osc_plot_new(ctx);
370 	configure_spectrum_window(setup);
371 	g_signal_connect(spectrum_window, "osc-destroy-event",
372 			G_CALLBACK(spectrum_window_destroyed_cb), NULL);
373 }
374 
configure_data_capture(plugin_setup * setup)375 static bool configure_data_capture(plugin_setup *setup)
376 {
377 	struct iio_channel *chn;
378 	struct extra_info *info;
379 	struct extra_dev_info *dev_info;
380 	unsigned int i;
381 	long long rate;
382 
383 	g_return_val_if_fail(setup, false);
384 
385 	device_set_rx_sampling_freq(dev, sampling_rate);
386 	rate = device_get_rx_sampling_freq(cap);
387 	if (rate != sampling_rate) {
388 		fprintf(stderr, "Failed to set the rx sampling rate to %lld"
389 			"in %s\n", sampling_rate, __func__);
390 		return false;
391 	}
392 
393 	dev_info = iio_device_get_data(cap);
394 	dev_info->sample_count = setup->fft_size;
395 	dev_info->adc_freq = rate;
396 	if (dev_info->adc_freq >= 1000000) {
397 		dev_info->adc_scale = 'M';
398 		dev_info->adc_freq /= 1000000.0;
399 	} else if (dev_info->adc_freq >= 1000) {
400 		dev_info->adc_scale = 'k';
401 		dev_info->adc_freq /= 1000.0;
402 	} else if (dev_info->adc_freq >= 0) {
403 		dev_info->adc_scale = ' ';
404 	} else {
405 		dev_info->adc_scale = '?';
406 		dev_info->adc_freq = 0.0;
407 	}
408 
409 	for (i = 0; i < iio_device_get_channels_count(cap); i++) {
410 		chn = iio_device_get_channel(cap, i);
411 		info = iio_channel_get_data(chn);
412 		if (info->data_ref) {
413 			g_free(info->data_ref);
414 			info->data_ref = NULL;
415 		}
416 		if (i / 2 == setup->rx) {
417 			iio_channel_enable(chn);
418 			info->data_ref = (gfloat *) g_new0(gfloat, setup->fft_size);
419 		} else {
420 			iio_channel_disable(chn);
421 		}
422 	}
423 
424 	return true;
425 }
426 
capture_data_thread_func(plugin_setup * setup)427 static gpointer capture_data_thread_func(plugin_setup *setup)
428 {
429 	while (!kill_capture_thread) {
430 
431 		/* Clean iio buffer */
432 		if (capture_buffer) {
433 			iio_buffer_destroy(capture_buffer);
434 			capture_buffer = NULL;
435 		}
436 
437 		/* Capture new data */
438 		capture_buffer = iio_device_create_buffer(cap, setup->fft_size, false);
439 		if (!capture_buffer) {
440 			fprintf(stderr, "Could not create iio buffer in %s\n", __func__);
441 			break;
442 		}
443 
444 		/* Reset the data offset for all channels */
445 		unsigned int i;
446 		for (i = 0; i < iio_device_get_channels_count(cap); i++) {
447 			struct iio_channel *ch = iio_device_get_channel(cap, i);
448 			struct extra_info *info = iio_channel_get_data(ch);
449 			info->offset = 0;
450 		}
451 
452 		/* Get captured data */
453 		ssize_t ret = iio_buffer_refill(capture_buffer);
454 		if (ret < 0) {
455 			fprintf(stderr, "Error while refilling iio buffer: %s\n", strerror(-ret));
456 			break;
457 		}
458 
459 		/* Signal the "Frequency Sweep" thread that data capture has completed */
460 		g_mutex_lock(&capture_done_mutex);
461 		capture_done = true;
462 		g_cond_signal(&capture_done_cond);
463 		g_mutex_unlock(&capture_done_mutex);
464 
465 		/* Block until the "Do FFT" thread has finished doing a FFT*/
466 		g_mutex_lock(&fft_done_mutex);
467 		while (!fft_done)
468 			g_cond_wait(&fft_done_cond, &fft_done_mutex);
469 		fft_done = false;
470 		g_mutex_unlock(&fft_done_mutex);
471 		if (kill_capture_thread)
472 			break;
473 
474 		/* Demux captured data */
475 		ret /= iio_buffer_step(capture_buffer);
476 		if ((unsigned)ret >= setup->fft_size)
477 			iio_buffer_foreach_sample(capture_buffer, demux_sample, NULL);
478 
479 		/* Signal the "Do FFT" thread that data demux has completed */
480 		g_mutex_lock(&demux_done_mutex);
481 		demux_done = true;
482 		g_cond_signal(&demux_done_cond);
483 		g_mutex_unlock(&demux_done_mutex);
484 
485 		/* Block until the "Data Capture" thread has recalled a new profile */
486 		g_mutex_lock(&profile_applied_mutex);
487 		while (!profile_applied)
488 			g_cond_wait(&profile_applied_cond, &profile_applied_mutex);
489 		profile_applied = false;
490 		g_mutex_unlock(&profile_applied_mutex);
491 		if (kill_capture_thread)
492 			break;
493 	}
494 
495 	/* Wake-up the "Frequency Sweep" thread and kill it */
496 	kill_sweep_thread = true;
497 	g_mutex_lock(&capture_done_mutex);
498 	capture_done = true;
499 	g_cond_signal(&capture_done_cond);
500 	g_mutex_unlock(&capture_done_mutex);
501 
502 	g_thread_join(freq_sweep_thread);
503 
504 	return NULL;
505 }
506 
profile_load_thread_func(plugin_setup * setup)507 static gpointer profile_load_thread_func(plugin_setup *setup)
508 {
509 	GSList *node = g_slist_nth(setup->rx_profiles, 1);
510 	fastlock_profile *profile;
511 	ssize_t ret;
512 
513 	while (!kill_sweep_thread) {
514 
515 		/* Block until the "Capture" thread has finished a data capture */
516 		g_mutex_lock(&capture_done_mutex);
517 		while (!capture_done)
518 			g_cond_wait(&capture_done_cond, &capture_done_mutex);
519 		capture_done = false;
520 		g_mutex_unlock(&capture_done_mutex);
521 		if (kill_sweep_thread)
522 			break;
523 
524 		/* Recall profile at slot 0 or 1 (alternative) */
525 		ret = iio_channel_attr_write_longlong(alt_ch0,
526 			"fastlock_recall", setup->profile_slot);
527 		setup->profile_slot = (setup->profile_slot + 1) % 2;
528 		if (setup->profile_count == 1)
529 			setup->profile_slot = 0;
530 		if (ret < 0)
531 		fprintf(stderr, "Could not write to fastlock_recall"
532 			"attribute in %s\n", __func__);
533 
534 		/* Signal the "Data Capture" thread that a new profile has been applied */
535 		g_mutex_lock(&profile_applied_mutex);
536 		profile_applied = true;
537 		g_cond_signal(&profile_applied_cond);
538 		g_mutex_unlock(&profile_applied_mutex);
539 
540 		/* Move to the next fastlock profile */
541 		node = g_slist_next(node);
542 		if (!node) {
543 			node = setup->rx_profiles;
544 			#if DEBUG
545 			g_timer_stop(gtimer);
546 			loop_durations_sum += g_timer_elapsed(gtimer, NULL);
547 			loop_count++;
548 			g_timer_start(gtimer);
549 			#endif
550 		}
551 		profile = node->data;
552 		profile->data[0] = '0' + setup->profile_slot;
553 		ret = iio_channel_attr_write(alt_ch0, "fastlock_load",
554 				profile->data);
555 		if (ret < 0)
556 			fprintf(stderr, "Could not write to fastlock_load"
557 				"attribute in %s\n", __func__);
558 	}
559 
560 	/* Wake-up and kill "Do FFT" thread */
561 	kill_fft_thread = true;
562 	g_mutex_lock(&demux_done_mutex);
563 	demux_done = true;
564 	g_cond_signal(&demux_done_cond);
565 	g_mutex_unlock(&demux_done_mutex);
566 
567 	g_thread_join(fft_thread);
568 
569 	return NULL;
570 }
571 
do_fft_thread_func(plugin_setup * setup)572 static gpointer do_fft_thread_func(plugin_setup *setup)
573 {
574 	while (!kill_fft_thread) {
575 
576 		/* Block until the "Data Capture" thread finishes to demux data */
577 		g_mutex_lock(&demux_done_mutex);
578 		while (!demux_done)
579 			g_cond_wait(&demux_done_cond, &demux_done_mutex);
580 		demux_done = false;
581 		g_mutex_unlock(&demux_done_mutex);
582 		if (kill_fft_thread)
583 			break;
584 
585 		/* Tell the oscplot object to process the captured data, perform FFT
586 		 * and concatenate with the rest of the FFTs in order to build the spectrum */
587 		if (spectrum_window)
588 			osc_plot_data_update(OSC_PLOT(spectrum_window));
589 
590 		/* Signal the "Data Capture" thread that the FFT has finished */
591 		g_mutex_lock(&fft_done_mutex);
592 		fft_done = true;
593 		g_cond_signal(&fft_done_cond);
594 		g_mutex_unlock(&fft_done_mutex);
595 	}
596 
597 	return NULL;
598 }
599 
setup_before_sweep_start(plugin_setup * setup)600 static bool setup_before_sweep_start(plugin_setup *setup)
601 {
602 	GSList *node;
603 	fastlock_profile *profile;
604 	ssize_t ret;
605 	int i;
606 
607 	g_return_val_if_fail(setup, false);
608 
609 	/* Configure the FIR filter */
610 	FILE *fp;
611 	char *buf;
612 	ssize_t len;
613 
614 	fp = fopen("filters/"FIR_FILTER, "r");
615 	if (!fp)
616 		fp = fopen(OSC_FILTER_FILE_PATH"/"FIR_FILTER, "r");
617 	if (!fp) {
618 		fprintf(stderr, "Could not open file %s for reading in %s. %s\n",
619 			FIR_FILTER, __func__, strerror(errno));
620 		goto fail;
621 	}
622 
623 	fseek(fp, 0, SEEK_END);
624 	len = ftell(fp);
625 	buf = malloc(len);
626 	fseek(fp, 0, SEEK_SET);
627 	len = fread(buf, 1, len, fp);
628 	fclose(fp);
629 
630 	ret = iio_device_attr_write_raw(dev,
631 			"filter_fir_config", buf, len);
632 	if (ret < 0) {
633 		fprintf(stderr, "FIR filter config failed in %s. %s\n",
634 			__func__, strerror(ret));
635 		goto fail;
636 	}
637 	free(buf);
638 
639 	ret = ad9361_set_trx_fir_enable(dev, true);
640 	if (ret < 0) {
641 		fprintf(stderr, "a write to in_out_voltage_filter_fir_en failed"
642 			"in %s. %s\n", __func__, strerror(ret));
643 		goto fail;
644 	}
645 	/* Fill fastlock slots 0 and 1 */
646 	for (i = 0, node = setup->rx_profiles; i < 2 && node;
647 					i++, node = g_slist_next(node)) {
648 		profile = node->data;
649 		profile->data[0] = '0' + i;
650 
651 		ret = iio_channel_attr_write(alt_ch0, "fastlock_load",
652 				profile->data);
653 		if (ret < 0) {
654 			fprintf(stderr, "Could not write to fastlock_load"
655 				"attribute in %s. %s\n", __func__, strerror(ret));
656 			goto fail;
657 		}
658 	}
659 
660 	/* Recall profile at slot 0 */
661 	ret = iio_channel_attr_write_longlong(alt_ch0,
662 			"fastlock_recall", 0);
663 	if (ret < 0) {
664 		fprintf(stderr, "Could not write to fastlock_recall"
665 			"attribute in %s. %s\n", __func__, strerror(ret));
666 		goto fail;
667 	}
668 
669 	kill_capture_thread = false;
670 	kill_sweep_thread = false;
671 	kill_fft_thread = false;
672 
673 	capture_done = false;
674 	profile_applied = false;
675 	demux_done = false;
676 	fft_done = true;
677 
678 	return true;
679 
680 fail:
681 	return false;
682 }
683 
start_sweep_clicked(GtkButton * btn,gpointer data)684 static void start_sweep_clicked(GtkButton *btn, gpointer data)
685 {
686 	gtk_widget_set_sensitive(GTK_WIDGET(btn), false);
687 
688 #if DEBUG
689 	gtimer = g_timer_new();
690 	loop_durations_sum = 0;
691 	loop_count = 0;
692 #endif
693 
694 	/* This capture process and the capture process from osc.c are designed
695 	 * to access the same iio devices but they do it from different threads,
696 	 * thus should not run simultaneously. */
697 	plugin_osc_stop_all_plots();
698 
699 	if (plugin_gather_user_setup(&psetup))
700 		build_profiles_for_entire_sweep(&psetup);
701 	if (!configure_data_capture(&psetup))
702 		goto abort;
703 	if (!spectrum_window)
704 		build_spectrum_window(&psetup);
705 	else
706 		configure_spectrum_window(&psetup);
707 	osc_plot_draw_start(OSC_PLOT(spectrum_window));
708 
709 	if (!setup_before_sweep_start(&psetup))
710 		goto abort;
711 
712 	capture_thread = g_thread_new("Data Capture",
713 				(GThreadFunc)capture_data_thread_func, &psetup);
714 	freq_sweep_thread = g_thread_new("Frequency Sweep",
715 				(GThreadFunc)profile_load_thread_func, &psetup);
716 	fft_thread = g_thread_new("Do FFT",
717 				(GThreadFunc)do_fft_thread_func, &psetup);
718 
719 	gtk_widget_set_sensitive(GTK_WIDGET(stop_button), true);
720 
721 	return;
722 
723 abort:
724 	g_signal_emit_by_name(stop_button, "clicked", NULL);
725 	return;
726 }
727 
stop_sweep_clicked(GtkButton * btn,gpointer data)728 static void stop_sweep_clicked(GtkButton *btn, gpointer data)
729 {
730 	gtk_widget_set_sensitive(GTK_WIDGET(btn), false);
731 
732 	if (spectrum_window)
733 		osc_plot_draw_stop(OSC_PLOT(spectrum_window));
734 	if (capture_thread) {
735 		kill_capture_thread = true;
736 		g_thread_join(capture_thread);
737 		capture_thread = NULL;
738 	}
739 	if (capture_buffer) {
740 		iio_buffer_destroy(capture_buffer);
741 		capture_buffer = NULL;
742 	}
743 
744 	gtk_widget_set_sensitive(GTK_WIDGET(start_button), true);
745 #if DEBUG
746 fprintf(stderr, "Average Sweep Duration: %f\n", loop_durations_sum / loop_count);
747 #endif
748 }
749 
center_freq_changed(GtkSpinButton * btn,gpointer data)750 static void center_freq_changed(GtkSpinButton *btn, gpointer data)
751 {
752 	GtkSpinButton *bw_spin = GTK_SPIN_BUTTON(freq_bw);
753 	GtkAdjustment *bw_adj = gtk_spin_button_get_adjustment(bw_spin);
754 	double center = gtk_spin_button_get_value(btn);
755 	double bw = gtk_spin_button_get_value(GTK_SPIN_BUTTON(freq_bw));
756 	double upper, upper1, upper2 = 0;
757 
758 	upper1 = (center - 70) * 2;
759 	upper2 = (6000 - center) * 2;
760 	upper = (upper1 < upper2) ? upper1 : upper2;
761 	gtk_adjustment_set_upper(bw_adj, upper);
762 	if (bw > upper)
763 		gtk_spin_button_set_value(bw_spin, upper);
764 }
765 
spectrum_window_destroyed_cb(OscPlot * plot)766 static void spectrum_window_destroyed_cb(OscPlot *plot)
767 {
768 	stop_sweep_clicked(GTK_BUTTON(stop_button), NULL);
769 	spectrum_window = NULL;
770 }
771 
handle_external_request(struct osc_plugin * plugin,const char * request)772 static int handle_external_request (struct osc_plugin *plugin, const char *request)
773 {
774 	int ret = 0;
775 
776 	if (!strcmp(request, "Stop")) {
777 		gtk_button_clicked(GTK_BUTTON(stop_button));
778 		ret = 1;
779 	}
780 
781 	return ret;
782 }
783 
analyzer_init(struct osc_plugin * plugin,GtkWidget * notebook,const char * ini_fn)784 static GtkWidget * analyzer_init(struct osc_plugin *plugin, GtkWidget *notebook, const char *ini_fn)
785 {
786 	GtkBuilder *builder;
787 	struct iio_channel *ch1;
788 
789 	ctx = osc_create_context();
790 	if (!ctx)
791 		return NULL;
792 
793 	dev = iio_context_find_device(ctx, PHY_DEVICE);
794 	if (!dev)
795 		goto destroy_ctx;
796 	cap = iio_context_find_device(ctx, CAP_DEVICE);
797 	if (!cap)
798 		goto destroy_ctx;
799 	alt_ch0 = iio_device_find_channel(dev, "altvoltage0", true);
800 	if (!alt_ch0)
801 		goto destroy_ctx;
802 
803 	ch1 = iio_device_find_channel(dev, "voltage1", false);
804 	is_2rx_2tx = ch1 && iio_channel_find_attr(ch1, "hardwaregain");
805 
806 	init_device_list(ctx);
807 
808 	if (iio_channel_find_attr(alt_ch0, "fastlock_store"))
809 		rx_fastlock_store_name = "fastlock_store";
810 	else
811 		rx_fastlock_store_name = "RX_LO_fastlock_store";
812 	if (iio_channel_find_attr(alt_ch0, "fastlock_save"))
813 		rx_fastlock_save_name = "fastlock_save";
814 	else
815 		rx_fastlock_save_name = "RX_LO_fastlock_save";
816 
817 	builder = gtk_builder_new();
818 	nbook = GTK_NOTEBOOK(notebook);
819 
820 	if (osc_load_glade_file(builder, "spectrum_analyzer") < 0)
821 		goto destroy_ctx;
822 
823 	analyzer_panel = GTK_WIDGET(gtk_builder_get_object(builder,
824 				"spectrum_analyzer_panel"));
825 	center_freq = GTK_WIDGET(gtk_builder_get_object(builder,
826 				"spin_center_freq"));
827 	freq_bw = GTK_WIDGET(gtk_builder_get_object(builder,
828 				"spin_freq_bw"));
829 	available_RBWs = GTK_WIDGET(gtk_builder_get_object(builder,
830 				"cmb_available_rbw"));
831 	receiver1 = GTK_WIDGET(gtk_builder_get_object(builder,
832 				"radiobutton_rx1"));
833 	start_button = GTK_WIDGET(gtk_builder_get_object(builder,
834 				"start_sweep_btn"));
835 	stop_button = GTK_WIDGET(gtk_builder_get_object(builder,
836 				"stop_sweep_btn"));
837 
838 	/* Widgets initialization */
839 	gtk_spin_button_set_range(GTK_SPIN_BUTTON(center_freq),
840 		70 + sweep_freq_step / 2, 6000 - sweep_freq_step / 2);
841 	gtk_adjustment_set_lower(gtk_spin_button_get_adjustment(
842 		GTK_SPIN_BUTTON(freq_bw)), sweep_freq_step);
843 	comboboxtext_rbw_fill(GTK_COMBO_BOX_TEXT(available_RBWs),
844 				HZ_TO_MHZ(sampling_rate));
845 	gtk_combo_box_set_active(GTK_COMBO_BOX(available_RBWs), 6);
846 
847 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(receiver1), true);
848 	if (!is_2rx_2tx)
849 		gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder,
850 				"frame_receiver_selection")));
851 
852 	gtk_widget_set_sensitive(GTK_WIDGET(stop_button), false);
853 
854 	/* Connect signals */
855 	g_builder_connect_signal(builder, "start_sweep_btn", "clicked",
856 			G_CALLBACK(start_sweep_clicked), NULL);
857 	g_builder_connect_signal(builder, "stop_sweep_btn", "clicked",
858 			G_CALLBACK(stop_sweep_clicked), NULL);
859 	g_builder_connect_signal(builder, "spin_center_freq", "value-changed",
860 			G_CALLBACK(center_freq_changed), NULL);
861 	g_signal_connect_swapped(freq_bw, "value-changed",
862 			G_CALLBACK(center_freq_changed), center_freq);
863 
864 	return analyzer_panel;
865 
866 destroy_ctx:
867 	osc_destroy_context(ctx);
868 	return NULL;
869 }
870 
update_active_page(struct osc_plugin * plugin,gint active_page,gboolean is_detached)871 static void update_active_page(struct osc_plugin *plugin, gint active_page, gboolean is_detached)
872 {
873 	this_page = active_page;
874 	plugin_detached = is_detached;
875 }
876 
analyzer_get_preferred_size(const struct osc_plugin * plugin,int * width,int * height)877 static void analyzer_get_preferred_size(const struct osc_plugin *plugin, int *width, int *height)
878 {
879 	if (width)
880 		*width = 640;
881 	if (height)
882 		*height = 480;
883 }
884 
context_destroy(struct osc_plugin * plugin,const char * ini_fn)885 static void context_destroy(struct osc_plugin *plugin, const char *ini_fn)
886 {
887 	if (capture_buffer) {
888 		iio_buffer_destroy(capture_buffer);
889 		capture_buffer = NULL;
890 	}
891 	g_source_remove_by_user_data(ctx);
892 
893 	osc_destroy_context(ctx);
894 }
895 
896 struct osc_plugin plugin;
897 
analyzer_identify(const struct osc_plugin * plugin)898 static bool analyzer_identify(const struct osc_plugin *plugin)
899 {
900 	/* Use the OSC's IIO context just to detect the devices */
901 	struct iio_context *osc_ctx = get_context_from_osc();
902 
903 	return !!iio_context_find_device(osc_ctx, PHY_DEVICE) &&
904 		!!iio_context_find_device(osc_ctx, CAP_DEVICE);
905 }
906 
907 struct osc_plugin plugin = {
908 	.name = THIS_DRIVER,
909 	.identify = analyzer_identify,
910 	.init = analyzer_init,
911 	.handle_external_request = handle_external_request,
912 	.update_active_page = update_active_page,
913 	.get_preferred_size = analyzer_get_preferred_size,
914 	.destroy = context_destroy,
915 };
916