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