1 /**
2 * Copyright (C) 2012-2014 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
25 #include <ad9361.h>
26 #include <iio.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 "../eeprom.h"
35 #include "../fru.h"
36 #include "../iio_utils.h"
37 #include "block_diagram.h"
38 #include "dac_data_manager.h"
39 #include "fir_filter.h"
40 #include "scpi.h"
41
42 #define HANNING_ENBW 1.50
43
44 #define THIS_DRIVER "AD936X"
45 #define PHY_DEVICE "ad9361-phy"
46 #define DDS_DEVICE "cf-ad9361-dds-core-lpc"
47 #define CAP_DEVICE "cf-ad9361-lpc"
48 #define UDC_RX_DEVICE "adf4351-udc-rx-pmod"
49 #define UDC_TX_DEVICE "adf4351-udc-tx-pmod"
50
51 #define ARRAY_SIZE(x) (!sizeof(x) ?: sizeof(x) / sizeof((x)[0]))
52
53 #define MHZ_TO_HZ(x) ((x) * 1000000ul)
54
55 #define REFCLK_RATE 40000000
56
57 extern bool dma_valid_selection(const char *device, unsigned mask, unsigned channel_count);
58
59 static struct dac_data_manager *dac_tx_manager;
60
61 static bool is_2rx_2tx;
62 static bool has_udc_driver;
63 static bool can_update_widgets;
64 static bool tx_rssi_available;
65
66 static const gdouble mhz_scale = 1000000.0;
67 static const gdouble inv_scale = -1.0;
68
69 static const char *freq_name;
70
71 static volatile int auto_calibrate = 0;
72 static unsigned int dcxo_coarse_num, dcxo_fine_num;
73 struct tuning_param
74 {
75 double frequency;
76 int coarse;
77 int fine;
78 };
79
80 static struct iio_widget widgets[100];
81 static struct iio_widget *glb_widgets, *tx_widgets, *rx_widgets, *fpga_widgets;
82 static unsigned int rx1_gain, rx2_gain;
83 static unsigned int num_glb, num_tx, num_rx, num_fpga;
84 static unsigned int rx_lo, tx_lo;
85 static unsigned int rx_sample_freq, tx_sample_freq;
86 static double updn_freq_span;
87 static double updn_freq_mix_sign;
88 static char last_fir_filter[PATH_MAX];
89 static char *rx_fastlock_store_name, *rx_fastlock_recall_name;
90 static char *tx_fastlock_store_name, *tx_fastlock_recall_name;
91
92 static struct iio_context *ctx;
93 static struct iio_device *dev, *dds, *cap, *udc_rx, *udc_tx;
94
95 #define SECTION_GLOBAL 0
96 #define SECTION_TX 1
97 #define SECTION_RX 2
98 #define SECTION_FPGA 3
99 static GtkToggleToolButton *section_toggle[4];
100 static GtkWidget *section_setting[4];
101
102 /* Widgets for Global Settings */
103 static GtkWidget *ensm_mode;
104 static GtkWidget *ensm_mode_available;
105 static GtkWidget *calib_mode;
106 static GtkWidget *calib_mode_available;
107 static GtkWidget *trx_rate_governor;
108 static GtkWidget *trx_rate_governor_available;
109 static GtkWidget *filter_fir_config;
110 static GtkWidget *up_down_converter;
111 static GtkWidget *dcxo_cal_progressbar;
112 static GtkWidget *dcxo_cal_type;
113 static GtkWidget *dcxo_cal;
114 static GtkWidget *enable_auto_filter;
115 static GtkWidget *dcxo_cal_tab;
116
117 /* Widgets for Receive Settings */
118 static GtkWidget *rx_gain_control_rx1;
119 static GtkWidget *rx_gain_control_modes_rx1;
120 static GtkWidget *rf_port_select_rx;
121 static GtkWidget *rx_gain_control_rx2;
122 static GtkWidget *rx_gain_control_modes_rx2;
123 static GtkWidget *rx1_rssi;
124 static GtkWidget *rx2_rssi;
125 static GtkWidget *tx1_rssi;
126 static GtkWidget *tx2_rssi;
127 static GtkWidget *rx_path_rates;
128 static GtkWidget *tx_path_rates;
129 static GtkWidget *fir_filter_en_tx;
130 static GtkWidget *enable_fir_filter_rx;
131 static GtkWidget *enable_fir_filter_rx_tx;
132 static GtkWidget *disable_all_fir_filters;
133 static GtkWidget *rf_port_select_tx;
134 static GtkWidget *rx_fastlock_profile;
135 static GtkWidget *tx_fastlock_profile;
136 static GtkWidget *rx_phase_rotation[2];
137
138 static GtkWidget *fpga_tx_frequency_available;
139 static GtkWidget *fpga_rx_frequency_available;
140
141 static GtkWidget *sampling_freq_rx_decim;
142 static GtkWidget *sampling_freq_tx_inter;
143
144 static gint this_page;
145 static GtkNotebook *nbook;
146 static GtkWidget *fmcomms2_panel;
147 static gboolean plugin_detached;
148
149 static const char *fmcomms2_sr_attribs[] = {
150 PHY_DEVICE".trx_rate_governor",
151 PHY_DEVICE".dcxo_tune_coarse",
152 PHY_DEVICE".dcxo_tune_fine",
153 PHY_DEVICE".xo_correction",
154 PHY_DEVICE".ensm_mode",
155 PHY_DEVICE".in_voltage0_rf_port_select",
156 PHY_DEVICE".in_voltage0_gain_control_mode",
157 PHY_DEVICE".in_voltage0_hardwaregain",
158 PHY_DEVICE".in_voltage1_gain_control_mode",
159 PHY_DEVICE".in_voltage1_hardwaregain",
160 PHY_DEVICE".in_voltage_bb_dc_offset_tracking_en",
161 PHY_DEVICE".in_voltage_quadrature_tracking_en",
162 PHY_DEVICE".in_voltage_rf_dc_offset_tracking_en",
163 PHY_DEVICE".out_voltage0_rf_port_select",
164 PHY_DEVICE".out_altvoltage0_RX_LO_external",
165 PHY_DEVICE".out_altvoltage1_TX_LO_external",
166 PHY_DEVICE".out_altvoltage0_RX_LO_frequency",
167 PHY_DEVICE".out_altvoltage1_TX_LO_frequency",
168 PHY_DEVICE".out_voltage0_hardwaregain",
169 PHY_DEVICE".out_voltage1_hardwaregain",
170 PHY_DEVICE".out_voltage_sampling_frequency",
171 PHY_DEVICE".in_voltage_rf_bandwidth",
172 PHY_DEVICE".out_voltage_rf_bandwidth",
173 PHY_DEVICE".in_voltage_filter_fir_en",
174 PHY_DEVICE".out_voltage_filter_fir_en",
175 PHY_DEVICE".in_out_voltage_filter_fir_en",
176
177 DDS_DEVICE".out_altvoltage0_TX1_I_F1_frequency",
178 DDS_DEVICE".out_altvoltage0_TX1_I_F1_phase",
179 DDS_DEVICE".out_altvoltage0_TX1_I_F1_scale",
180 DDS_DEVICE".out_altvoltage1_TX1_I_F2_frequency",
181 DDS_DEVICE".out_altvoltage1_TX1_I_F2_phase",
182 DDS_DEVICE".out_altvoltage1_TX1_I_F2_scale",
183 DDS_DEVICE".out_altvoltage2_TX1_Q_F1_frequency",
184 DDS_DEVICE".out_altvoltage2_TX1_Q_F1_phase",
185 DDS_DEVICE".out_altvoltage2_TX1_Q_F1_scale",
186 DDS_DEVICE".out_altvoltage3_TX1_Q_F2_frequency",
187 DDS_DEVICE".out_altvoltage3_TX1_Q_F2_phase",
188 DDS_DEVICE".out_altvoltage3_TX1_Q_F2_scale",
189 DDS_DEVICE".out_altvoltage4_TX2_I_F1_frequency",
190 DDS_DEVICE".out_altvoltage4_TX2_I_F1_phase",
191 DDS_DEVICE".out_altvoltage4_TX2_I_F1_scale",
192 DDS_DEVICE".out_altvoltage5_TX2_I_F2_frequency",
193 DDS_DEVICE".out_altvoltage5_TX2_I_F2_phase",
194 DDS_DEVICE".out_altvoltage5_TX2_I_F2_scale",
195 DDS_DEVICE".out_altvoltage6_TX2_Q_F1_frequency",
196 DDS_DEVICE".out_altvoltage6_TX2_Q_F1_phase",
197 DDS_DEVICE".out_altvoltage6_TX2_Q_F1_scale",
198 DDS_DEVICE".out_altvoltage7_TX2_Q_F2_frequency",
199 DDS_DEVICE".out_altvoltage7_TX2_Q_F2_phase",
200 DDS_DEVICE".out_altvoltage7_TX2_Q_F2_scale",
201
202 UDC_RX_DEVICE".out_altvoltage0_frequency",
203 UDC_TX_DEVICE".out_altvoltage0_frequency",
204 };
205
206 static const char * fmcomms2_driver_attribs[] = {
207 "load_fir_filter_file",
208 "dds_mode_tx1",
209 "dds_mode_tx2",
210 "global_settings_show",
211 "tx_show",
212 "rx_show",
213 "fpga_show",
214 "up_down_converter",
215 "tx_channel_0",
216 "tx_channel_1",
217 "tx_channel_2",
218 "tx_channel_3",
219 "dac_buf_filename",
220 };
221
glb_settings_update_labels(void)222 static void glb_settings_update_labels(void)
223 {
224 float rates[6];
225 char tmp[160], buf[1024];
226 ssize_t ret;
227
228 ret = iio_device_attr_read(dev, "ensm_mode", buf, sizeof(buf));
229 if (ret > 0)
230 gtk_label_set_text(GTK_LABEL(ensm_mode), buf);
231 else
232 gtk_label_set_text(GTK_LABEL(ensm_mode), "<error>");
233
234 ret = iio_device_attr_read(dev, "calib_mode", buf, sizeof(buf));
235 if (ret > 0)
236 gtk_label_set_text(GTK_LABEL(calib_mode), buf);
237 else
238 gtk_label_set_text(GTK_LABEL(calib_mode), "<error>");
239
240 ret = iio_device_attr_read(dev, "trx_rate_governor", buf, sizeof(buf));
241 if (ret > 0)
242 gtk_label_set_text(GTK_LABEL(trx_rate_governor), buf);
243 else
244 gtk_label_set_text(GTK_LABEL(trx_rate_governor), "<error>");
245
246 ret = iio_channel_attr_read(
247 iio_device_find_channel(dev, "voltage0", false),
248 "gain_control_mode", buf, sizeof(buf));
249 if (ret > 0)
250 gtk_label_set_text(GTK_LABEL(rx_gain_control_rx1), buf);
251 else
252 gtk_label_set_text(GTK_LABEL(rx_gain_control_rx1), "<error>");
253
254 if (is_2rx_2tx) {
255 ret = iio_channel_attr_read(
256 iio_device_find_channel(dev, "voltage1", false),
257 "gain_control_mode", buf, sizeof(buf));
258 if (ret > 0)
259 gtk_label_set_text(GTK_LABEL(rx_gain_control_rx2), buf);
260 else
261 gtk_label_set_text(GTK_LABEL(rx_gain_control_rx2), "<error>");
262 }
263
264 ret = iio_device_attr_read(dev, "rx_path_rates", buf, sizeof(buf));
265 if (ret > 0) {
266 sscanf(buf, "BBPLL:%f ADC:%f R2:%f R1:%f RF:%f RXSAMP:%f",
267 &rates[0], &rates[1], &rates[2], &rates[3], &rates[4],
268 &rates[5]);
269 sprintf(tmp, "BBPLL: %4.3f ADC: %4.3f R2: %4.3f R1: %4.3f RF: %4.3f RXSAMP: %4.3f",
270 rates[0] / 1e6, rates[1] / 1e6, rates[2] / 1e6,
271 rates[3] / 1e6, rates[4] / 1e6, rates[5] / 1e6);
272
273 gtk_label_set_text(GTK_LABEL(rx_path_rates), tmp);
274 } else {
275 gtk_label_set_text(GTK_LABEL(rx_path_rates), "<error>");
276 }
277
278 ret = iio_device_attr_read(dev, "tx_path_rates", buf, sizeof(buf));
279 if (ret > 0) {
280 sscanf(buf, "BBPLL:%f DAC:%f T2:%f T1:%f TF:%f TXSAMP:%f",
281 &rates[0], &rates[1], &rates[2], &rates[3], &rates[4],
282 &rates[5]);
283 sprintf(tmp, "BBPLL: %4.3f DAC: %4.3f T2: %4.3f T1: %4.3f TF: %4.3f TXSAMP: %4.3f",
284 rates[0] / 1e6, rates[1] / 1e6, rates[2] / 1e6,
285 rates[3] / 1e6, rates[4] / 1e6, rates[5] / 1e6);
286
287 gtk_label_set_text(GTK_LABEL(tx_path_rates), tmp);
288 } else {
289 gtk_label_set_text(GTK_LABEL(tx_path_rates), "<error>");
290 }
291
292 iio_widget_update(&rx_widgets[rx1_gain]);
293 if (is_2rx_2tx)
294 iio_widget_update(&rx_widgets[rx2_gain]);
295 }
296
rf_port_select_rx_changed_cb(GtkComboBoxText * cmb,gpointer data)297 static void rf_port_select_rx_changed_cb(GtkComboBoxText *cmb, gpointer data)
298 {
299 gchar *port_name;
300 bool tx1 = false, tx2 = false;
301
302 port_name = gtk_combo_box_text_get_active_text(cmb);
303 if (!port_name)
304 return;
305
306 if (!strcmp(port_name, "TX_MONITOR1")) {
307 tx1 = true;
308 } else if (!strcmp(port_name, "TX_MONITOR2")) {
309 tx2 = true;
310 } else if (!strcmp(port_name, "TX_MONITOR1_2")) {
311 tx1 = tx2 = true;
312 }
313 gtk_widget_set_visible(tx1_rssi, tx1);
314 gtk_widget_set_visible(tx2_rssi, tx2);
315
316 g_free(port_name);
317 }
318
rx_freq_info_update(void)319 static void rx_freq_info_update(void)
320 {
321 double lo_freq;
322
323 if (cap)
324 rx_update_device_sampling_freq(CAP_DEVICE,
325 USE_INTERN_SAMPLING_FREQ);
326 lo_freq = mhz_scale * gtk_spin_button_get_value(
327 GTK_SPIN_BUTTON(rx_widgets[rx_lo].widget));
328 if (cap)
329 rx_update_channel_lo_freq(CAP_DEVICE, "all", lo_freq);
330 }
331
int_dec_freq_update(void)332 static void int_dec_freq_update(void)
333 {
334 struct iio_channel *ch;
335 double freq;
336
337 if (cap) {
338 ch = iio_device_find_channel(cap, "voltage0", false);
339 iio_channel_attr_read_double(ch, "sampling_frequency", &freq);
340 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sampling_freq_rx_decim), freq / mhz_scale);
341 }
342
343 if (dds) {
344 ch = iio_device_find_channel(dds, "voltage0", true);
345 iio_channel_attr_read_double(ch, "sampling_frequency", &freq);
346 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sampling_freq_tx_inter), freq / mhz_scale);
347 }
348 }
349
sample_frequency_changed_cb(void * data)350 static void sample_frequency_changed_cb(void *data)
351 {
352 glb_settings_update_labels();
353 rx_freq_info_update();
354 iio_update_widgets(fpga_widgets, num_fpga);
355 int_dec_freq_update();
356 }
357
get_gui_tx_sampling_freq(void)358 static double get_gui_tx_sampling_freq(void)
359 {
360 return gtk_spin_button_get_value(GTK_SPIN_BUTTON(tx_widgets[tx_sample_freq].widget));
361 }
362
363 static void filter_fir_update(void); /* forwrad declaration */
364
tx_sample_frequency_changed_cb(void * data)365 static void tx_sample_frequency_changed_cb(void *data)
366 {
367 double rate;
368 bool auto_fir;
369
370 /* Skip rx_sample_freq changed, since RX and TX rates are always the same */
371 if (!data)
372 return;
373
374 auto_fir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (enable_auto_filter));
375 rate = get_gui_tx_sampling_freq();
376
377 if (auto_fir) {
378 ad9361_set_bb_rate (dev, (unsigned long) (rate * 1000000));
379 gtk_widget_show(enable_fir_filter_rx_tx);
380 gtk_widget_show(disable_all_fir_filters);
381 filter_fir_update();
382 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filter_fir_config), "(None)");
383 } else {
384 iio_spin_button_save(&tx_widgets[tx_sample_freq]);
385 }
386 /* We've set the sampling freq. Now read back the value and update the widget. */
387 iio_widget_update(&tx_widgets[tx_sample_freq]);
388
389 dac_data_manager_freq_widgets_range_update(dac_tx_manager, rate / 2.0);
390 sample_frequency_changed_cb(NULL);
391 }
392
rssi_update_label(GtkWidget * label,const char * chn,bool is_tx)393 static void rssi_update_label(GtkWidget *label, const char *chn, bool is_tx)
394 {
395 char buf[1024];
396 int ret;
397
398 /* don't update if it is hidden (to quiet down SPI) */
399 if (!gtk_widget_is_drawable(GTK_WIDGET(label)))
400 return;
401
402 ret = iio_channel_attr_read(
403 iio_device_find_channel(dev, chn, is_tx),
404 "rssi", buf, sizeof(buf));
405 if (ret > 0)
406 gtk_label_set_text(GTK_LABEL(label), buf);
407 else
408 gtk_label_set_text(GTK_LABEL(label), "<error>");
409 }
410
rssi_update_labels(void)411 static void rssi_update_labels(void)
412 {
413 rssi_update_label(rx1_rssi, "voltage0", false);
414 if (tx_rssi_available)
415 rssi_update_label(tx1_rssi, "voltage0", true);
416 if (is_2rx_2tx) {
417 rssi_update_label(rx2_rssi, "voltage1", false);
418 if (tx_rssi_available)
419 rssi_update_label(tx2_rssi, "voltage1", true);
420 }
421 }
422
update_display(gpointer foo)423 static gboolean update_display(gpointer foo)
424 {
425 if (this_page == gtk_notebook_get_current_page(nbook) || plugin_detached) {
426 gchar *gain_mode;
427
428 rssi_update_labels();
429 gain_mode = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(rx_gain_control_modes_rx1));
430 if (gain_mode && strcmp(gain_mode, "manual"))
431 iio_widget_update(&rx_widgets[rx1_gain]);
432 g_free(gain_mode);
433
434 gain_mode = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(rx_gain_control_modes_rx2));
435 if (is_2rx_2tx && gain_mode && strcmp(gain_mode, "manual"))
436 iio_widget_update(&rx_widgets[rx2_gain]);
437 g_free(gain_mode);
438 }
439
440 return TRUE;
441 }
442
443 const double RX_CENTER_FREQ = 340; /* MHz */
444 const double TX_CENTER_FREQ = 370; /* MHz */
445
get_span_multiple_from(double frequency,double span)446 static double get_span_multiple_from(double frequency, double span)
447 {
448 double num = 0;
449
450 if (span <= 0)
451 return 0;
452
453 while (num <= frequency)
454 num += span;
455
456 return (num - span);
457 }
458
split_target_lo_freq(double target_freq,double * ext_pll,double * ad9361_lo,double span,const double center_freq)459 static int split_target_lo_freq(double target_freq, double *ext_pll, double *ad9361_lo,
460 double span, const double center_freq)
461 {
462 double small_freq, large_freq;
463
464 large_freq = get_span_multiple_from(target_freq, span);
465 small_freq = target_freq - large_freq;
466
467 *ad9361_lo = center_freq - small_freq * updn_freq_mix_sign;
468 *ext_pll = center_freq + large_freq * updn_freq_mix_sign;
469
470 return 0;
471 }
472
473 #define UPDN_RX 1
474 #define UPDN_TX 2
475
updn_converter_lo_freq_changed_cb(GtkSpinButton * button,int data)476 static void updn_converter_lo_freq_changed_cb(GtkSpinButton *button, int data)
477 {
478 struct iio_channel *ad9361_ch, *updn_ch;
479 double target_freq, ad9361_lo, updn_pll, center_freq;
480 int ret;
481
482 if (data == UPDN_RX) {
483 ad9361_ch = iio_device_find_channel(dev, "altvoltage0", true);
484 updn_ch = iio_device_find_channel(udc_rx, "altvoltage0", true);
485 center_freq = RX_CENTER_FREQ;
486 } else if (data == UPDN_TX) {
487 ad9361_ch = iio_device_find_channel(dev, "altvoltage1", true);
488 updn_ch = iio_device_find_channel(udc_tx, "altvoltage0", true);
489 center_freq = TX_CENTER_FREQ;
490 } else {
491 return;
492 }
493
494 target_freq = gtk_spin_button_get_value(button);
495 split_target_lo_freq(target_freq, &updn_pll, &ad9361_lo, updn_freq_span, center_freq);
496 ret = iio_channel_attr_write_longlong(ad9361_ch, freq_name, (long long)MHZ_TO_HZ(ad9361_lo));
497 if (ret < 0)
498 fprintf(stderr,"Write to %s attribute of %s device: %s\n",
499 freq_name, PHY_DEVICE, strerror(-ret));
500 ret = iio_channel_attr_write_longlong(updn_ch, "frequency", (long long)MHZ_TO_HZ(updn_pll));
501 if (ret < 0)
502 fprintf(stderr,"Write to %s attribute of %s device: %s\n",
503 "frequency", (UPDN_TX) ? UDC_TX_DEVICE : UDC_RX_DEVICE, strerror(-ret));
504 rx_freq_info_update();
505 }
506
507 #define UPDN_LO_FREQ_MIN 1 /* MHz */
508 #define UPDN_LO_FREQ_MAX 120 /* MHz */
509
up_down_converter_toggled_cb(GtkToggleButton * button,gpointer data)510 static void up_down_converter_toggled_cb(GtkToggleButton *button, gpointer data)
511 {
512 static gint rx_updn_hid, tx_updn_hid;
513 static gdouble lo_min, lo_max;
514 static void (*rx_lo_update_value)(struct iio_widget *, const char *, size_t);
515 static void (*tx_lo_update_value)(struct iio_widget *, const char *, size_t);
516
517 if (gtk_toggle_button_get_active(button)) {
518 iio_spin_button_progress_deactivate(&rx_widgets[rx_lo]);
519 iio_spin_button_progress_deactivate(&tx_widgets[tx_lo]);
520 rx_updn_hid = g_signal_connect(rx_widgets[rx_lo].widget, "value-changed",
521 G_CALLBACK(updn_converter_lo_freq_changed_cb), (gpointer)UPDN_RX);
522 tx_updn_hid = g_signal_connect(tx_widgets[tx_lo].widget, "value-changed",
523 G_CALLBACK(updn_converter_lo_freq_changed_cb), (gpointer)UPDN_TX);
524 gtk_spin_button_get_range(GTK_SPIN_BUTTON(rx_widgets[rx_lo].widget), &lo_min, &lo_max);
525 gtk_spin_button_set_range(GTK_SPIN_BUTTON(rx_widgets[rx_lo].widget), UPDN_LO_FREQ_MIN, UPDN_LO_FREQ_MAX);
526 gtk_spin_button_set_range(GTK_SPIN_BUTTON(tx_widgets[tx_lo].widget), UPDN_LO_FREQ_MIN, UPDN_LO_FREQ_MAX);
527 rx_lo_update_value = rx_widgets[rx_lo].update_value;
528 tx_lo_update_value = tx_widgets[tx_lo].update_value;
529 rx_widgets[rx_lo].update_value = NULL;
530 tx_widgets[tx_lo].update_value = NULL;
531 } else {
532 g_signal_handler_disconnect(rx_widgets[rx_lo].widget, rx_updn_hid);
533 g_signal_handler_disconnect(tx_widgets[tx_lo].widget, tx_updn_hid);
534 rx_widgets[rx_lo].update_value = rx_lo_update_value;
535 tx_widgets[tx_lo].update_value = tx_lo_update_value;
536 iio_spin_button_progress_activate(&rx_widgets[rx_lo]);
537 iio_spin_button_progress_activate(&tx_widgets[tx_lo]);
538 g_signal_emit_by_name(rx_widgets[rx_lo].widget,
539 "value-changed", NULL);
540 g_signal_emit_by_name(tx_widgets[tx_lo].widget,
541 "value-changed", NULL);
542 gtk_spin_button_set_range(GTK_SPIN_BUTTON(rx_widgets[rx_lo].widget), lo_min, lo_max);
543 gtk_spin_button_set_range(GTK_SPIN_BUTTON(tx_widgets[tx_lo].widget), lo_min, lo_max);
544 }
545 }
546
rx_phase_rotation_update()547 static void rx_phase_rotation_update()
548 {
549 struct iio_channel *out[4];
550 gdouble val[4];
551 int i, d = 0;
552
553 if (!cap)
554 return;
555
556 out[0] = iio_device_find_channel(cap, "voltage0", false);
557 out[1] = iio_device_find_channel(cap, "voltage1", false);
558
559 if (is_2rx_2tx) {
560 out[2] = iio_device_find_channel(cap, "voltage2", false);
561 out[3] = iio_device_find_channel(cap, "voltage3", false);
562 d = 2;
563 }
564
565 for (i = 0; i <= d; i += 2) {
566 iio_channel_attr_read_double(out[i], "calibscale", &val[0]);
567 iio_channel_attr_read_double(out[i], "calibphase", &val[1]);
568 iio_channel_attr_read_double(out[i + 1], "calibscale", &val[2]);
569 iio_channel_attr_read_double(out[i + 1], "calibphase", &val[3]);
570
571 val[0] = acos(val[0]) * 360.0 / (2.0 * M_PI);
572 val[1] = asin(-1.0 * val[1]) * 360.0 / (2.0 * M_PI);
573 val[2] = acos(val[2]) * 360.0 / (2.0 * M_PI);
574 val[3] = asin(val[3]) * 360.0 / (2.0 * M_PI);
575
576 if (val[1] < 0.0)
577 val[0] *= -1.0;
578 if (val[3] < 0.0)
579 val[2] *= -1.0;
580 if (val[1] < -90.0)
581 val[0] = (val[0] * -1.0) - 180.0;
582 if (val[3] < -90.0)
583 val[0] = (val[0] * -1.0) - 180.0;
584
585 if (fabs(val[0]) > 90.0) {
586 if (val[1] < 0.0)
587 val[1] = (val[1] * -1.0) - 180.0;
588 else
589 val[1] = 180 - val[1];
590 }
591 if (fabs(val[2]) > 90.0) {
592 if (val[3] < 0.0)
593 val[3] = (val[3] * -1.0) - 180.0;
594 else
595 val[3] = 180 - val[3];
596 }
597
598 if (round(val[0]) != round(val[1]) &&
599 round(val[0]) != round(val[2]) &&
600 round(val[0]) != round(val[3])) {
601 printf("error calculating phase rotations\n");
602 val[0] = 0.0;
603 } else
604 val[0] = (val[0] + val[1] + val[2] + val[3]) / 4.0;
605
606 gtk_spin_button_set_value(GTK_SPIN_BUTTON(rx_phase_rotation[i/2]), val[0]);
607 }
608 }
609
dcxo_widgets_update(void)610 static void dcxo_widgets_update(void)
611 {
612 char val[64];
613 int ret;
614
615 ret = iio_device_attr_read(dev, "dcxo_tune_coarse", val, sizeof(val));
616
617 if (ret < 0)
618 gtk_widget_hide(dcxo_cal_tab);
619 else
620 gtk_widget_show(dcxo_cal_tab);
621 }
622
int_dec_update_cb(GtkComboBox * cmb,gpointer data)623 static void int_dec_update_cb(GtkComboBox *cmb, gpointer data)
624 {
625 if (gtk_combo_box_get_active(cmb) > 0)
626 gtk_widget_show(GTK_WIDGET(data));
627 else
628 gtk_widget_hide(GTK_WIDGET(data));
629
630 int_dec_freq_update();
631 rx_freq_info_update();
632 }
633
int_dec_spin_update_cb(GtkSpinButton * spin,gpointer data)634 static void int_dec_spin_update_cb(GtkSpinButton *spin, gpointer data)
635 {
636 struct iio_channel *chn = data;
637 double fpga, freq, trx;
638
639 freq = mhz_scale * gtk_spin_button_get_value(spin);
640 iio_channel_attr_read_double(chn, "sampling_frequency", &fpga);
641 trx = mhz_scale * gtk_spin_button_get_value(
642 GTK_SPIN_BUTTON(tx_widgets[tx_sample_freq].widget));
643
644 gtk_spin_button_set_value(GTK_SPIN_BUTTON(tx_widgets[tx_sample_freq].widget),
645 (trx/fpga * freq) / mhz_scale);
646
647 }
648
update_widgets(void)649 static void update_widgets(void)
650 {
651 iio_update_widgets_of_device(widgets, num_glb + num_tx + num_rx, dev);
652 if (dds)
653 iio_update_widgets_of_device(fpga_widgets, num_fpga, dds);
654 if (cap)
655 iio_update_widgets_of_device(fpga_widgets, num_fpga, cap);
656 dac_data_manager_update_iio_widgets(dac_tx_manager);
657 dcxo_widgets_update();
658 }
659
filter_fir_update(void)660 static void filter_fir_update(void)
661 {
662 bool rx = false, tx = false, rxtx;
663 struct iio_channel *chn;
664 int stat;
665
666 ad9361_get_trx_fir_enable(dev, &stat);
667 rxtx = !!stat;
668
669 chn = iio_device_find_channel(dev, "voltage0", false);
670 if (chn)
671 iio_channel_attr_read_bool(chn, "filter_fir_en", &rx);
672 chn = iio_device_find_channel(dev, "voltage0", true);
673 if (chn)
674 iio_channel_attr_read_bool(chn, "filter_fir_en", &tx);
675
676 if (rxtx) {
677 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (enable_fir_filter_rx_tx), rxtx);
678 } else if (!rx && !tx) {
679 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (disable_all_fir_filters), true);
680 } else {
681 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (enable_fir_filter_rx), rx);
682 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (fir_filter_en_tx), tx);
683 }
684 }
685
filter_fir_enable(GtkToggleButton * button,gpointer data)686 static void filter_fir_enable(GtkToggleButton *button, gpointer data)
687 {
688 bool rx, tx, rxtx, disable;
689
690 if (gtk_toggle_button_get_active(button))
691 return;
692
693 rx = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (enable_fir_filter_rx));
694 tx = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (fir_filter_en_tx));
695 rxtx = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (enable_fir_filter_rx_tx));
696 disable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (disable_all_fir_filters));
697
698 if (rxtx || disable) {
699 ad9361_set_trx_fir_enable(dev, rxtx);
700 } else {
701 struct iio_channel *chn;
702 if (rx) {
703 chn = iio_device_find_channel(dev, "voltage0", true);
704 if (chn)
705 iio_channel_attr_write_bool(chn, "filter_fir_en", tx);
706
707 chn = iio_device_find_channel(dev, "voltage0", false);
708 if (chn)
709 iio_channel_attr_write_bool(chn, "filter_fir_en", rx);
710
711 }
712
713 if (tx) {
714 chn = iio_device_find_channel(dev, "voltage0", false);
715 if (chn)
716 iio_channel_attr_write_bool(chn, "filter_fir_en", rx);
717
718 chn = iio_device_find_channel(dev, "voltage0", true);
719 if (chn)
720 iio_channel_attr_write_bool(chn, "filter_fir_en", tx);
721
722 }
723 }
724
725 if (plugin_osc_running_state() == true) {
726 plugin_osc_stop_capture();
727 plugin_osc_start_capture();
728 }
729
730 filter_fir_update();
731 glb_settings_update_labels();
732 update_widgets();
733 rx_freq_info_update();
734 }
735
reload_button_clicked(GtkButton * btn,gpointer data)736 static void reload_button_clicked(GtkButton *btn, gpointer data)
737 {
738 update_widgets();
739
740 filter_fir_update();
741 rx_freq_info_update();
742 glb_settings_update_labels();
743 rssi_update_labels();
744 rx_phase_rotation_update();
745 }
746
747 #ifndef _WIN32
dcxo_cal_to_eeprom_clicked(GtkButton * btn,gpointer data)748 static int dcxo_cal_to_eeprom_clicked(GtkButton *btn, gpointer data)
749 {
750 unsigned coarse, fine;
751 char cmd[256];
752 const char *eeprom_path = find_eeprom(NULL);
753 FILE *fp = NULL;
754 const char *failure_msg = NULL;
755 int ret = 0;
756
757 if (!eeprom_path) {
758 failure_msg = "Can't find EEPROM file in the sysfs";
759 goto cleanup;
760 }
761
762 coarse = gtk_spin_button_get_value(GTK_SPIN_BUTTON(glb_widgets[dcxo_coarse_num].widget));
763 fine = gtk_spin_button_get_value(GTK_SPIN_BUTTON(glb_widgets[dcxo_fine_num].widget));
764 sprintf(cmd, "fru-dump -i \"%s\" -o \"%s\" -t %.02x%.04x 2>&1", eeprom_path,
765 eeprom_path, coarse, fine);
766 fp = popen(cmd, "r");
767
768 if (!fp || pclose(fp) != 0) {
769 failure_msg = "Error running fru-dump to write to EEPROM";
770 fprintf(stderr, "Error running fru-dump: %s\n", cmd);
771 goto cleanup;
772 }
773
774 cleanup:
775 if (failure_msg) {
776 GtkWidget *toplevel = gtk_widget_get_toplevel(fmcomms2_panel);
777 if (!gtk_widget_is_toplevel(toplevel))
778 toplevel = NULL;
779 GtkWidget *dcxo_cal_eeprom_fail = gtk_message_dialog_new(
780 GTK_WINDOW(toplevel),
781 GTK_DIALOG_MODAL,
782 GTK_MESSAGE_ERROR,
783 GTK_BUTTONS_CLOSE,
784 "%s", failure_msg);
785 gtk_window_set_title(GTK_WINDOW(dcxo_cal_eeprom_fail), "Save to EEPROM");
786 if (gtk_dialog_run(GTK_DIALOG(dcxo_cal_eeprom_fail)))
787 gtk_widget_destroy(dcxo_cal_eeprom_fail);
788 ret = -1;
789 }
790
791 g_free((void *)eeprom_path);
792
793 return ret;
794 }
795
dcxo_cal_from_eeprom_clicked(GtkButton * btn,gpointer data)796 static int dcxo_cal_from_eeprom_clicked(GtkButton *btn, gpointer data)
797 {
798 const char *eeprom_path = find_eeprom(NULL);
799 unsigned char *raw_eeprom = NULL;
800 struct FRU_DATA *fru = NULL;
801 FILE *fp;
802 size_t bytes;
803 const char *failure_msg = NULL;
804 char coarse_str[3], fine_str[5];
805 int coarse, fine, ret = 0;
806
807 if (!eeprom_path) {
808 failure_msg = "Can't find EEPROM file in the sysfs";
809 goto cleanup;
810 }
811
812 fp = fopen(eeprom_path, "rb");
813 if (!fp) {
814 failure_msg = "Can't open EEPROM file";
815 goto cleanup;
816 }
817
818 raw_eeprom = g_malloc(FAB_SIZE_FRU_EEPROM);
819 bytes = fread(raw_eeprom, 1, FAB_SIZE_FRU_EEPROM, fp);
820
821 /* FRU format specifies a 256 byte file size. */
822 if (ferror(fp) || bytes != FAB_SIZE_FRU_EEPROM) {
823 failure_msg = "Failed to read EEPROM file";
824 fclose(fp);
825 goto cleanup;
826 }
827 fclose(fp);
828
829 fru = parse_FRU(raw_eeprom);
830 if (!fru) {
831 failure_msg = "Failed to parse EEPROM";
832 goto cleanup;
833 }
834
835 /* The tuning parameters are stored as a single, concatenated hex string,
836 * with the first two characters being the coarse value and the last four
837 * characters being the fine value.
838 *
839 * Note that there are two header bytes that must be skipped first.
840 */
841 memcpy(coarse_str, &fru->Board_Area->custom[4][2], 2);
842 coarse_str[2] = '\0';
843 memcpy(fine_str, &fru->Board_Area->custom[4][4], 4);
844 fine_str[4] = '\0';
845
846 coarse = strtol(coarse_str, NULL, 16);
847 fine = strtol(fine_str, NULL, 16);
848 if (errno == ERANGE || errno == EINVAL) {
849 failure_msg = "Failed parsing coarse and/or fine values from EEPROM";
850 goto cleanup;
851 }
852
853 gtk_spin_button_set_value(GTK_SPIN_BUTTON(
854 glb_widgets[dcxo_coarse_num].widget), coarse);
855 gtk_spin_button_set_value(GTK_SPIN_BUTTON(
856 glb_widgets[dcxo_fine_num].widget), fine);
857
858 cleanup:
859 if (failure_msg) {
860 GtkWidget *toplevel = gtk_widget_get_toplevel(fmcomms2_panel);
861 if (!gtk_widget_is_toplevel(toplevel))
862 toplevel = NULL;
863 GtkWidget *dcxo_cal_eeprom_fail = gtk_message_dialog_new(
864 GTK_WINDOW(toplevel),
865 GTK_DIALOG_MODAL,
866 GTK_MESSAGE_ERROR,
867 GTK_BUTTONS_CLOSE,
868 "%s", failure_msg);
869 gtk_window_set_title(GTK_WINDOW(dcxo_cal_eeprom_fail), "Load from EEPROM");
870 if (gtk_dialog_run(GTK_DIALOG(dcxo_cal_eeprom_fail)))
871 gtk_widget_destroy(dcxo_cal_eeprom_fail);
872 ret = -1;
873 }
874
875 g_free((void *)eeprom_path);
876 g_free(raw_eeprom);
877 g_free(fru);
878
879 return ret;
880 }
881
xo_freq_to_eeprom(void)882 static int xo_freq_to_eeprom(void)
883 {
884 const char *eeprom_path = find_eeprom(NULL);
885 char cmd[256];
886 FILE *fp = NULL, *cmdfp = NULL;
887 const char *failure_msg = NULL;
888 double current_freq, target_freq;
889 int ret = 0;
890
891 if (!eeprom_path) {
892 failure_msg = "Can't find EEPROM file in the sysfs";
893 goto cleanup;
894 }
895
896 if (!strcmp(iio_context_get_name(ctx), "network")) {
897 target_freq = REFCLK_RATE;
898 } else if (!strcmp(iio_context_get_name(ctx), "local")) {
899 fp = fopen("/sys/kernel/debug/clk/ad9361_ext_refclk/clk_rate", "r");
900 if (!fp || fscanf(fp, "%lf", &target_freq) != 1) {
901 failure_msg = "Unable to read AD9361 reference clock rate from debugfs.";
902 if (fp)
903 fclose(fp);
904 goto cleanup;
905 }
906 if (fp) {
907 fclose(fp);
908 }
909 } else {
910 failure_msg = "AD9361 Reference clock rate missing from debugfs.";
911 goto cleanup;
912 }
913
914 if (scpi_connect_counter() != 0) {
915 failure_msg = "Failed to connect to Programmable Counter device.";
916 goto cleanup;
917 }
918
919 if (scpi_counter_get_freq(¤t_freq, &target_freq) != 0) {
920 failure_msg = "Error retrieving counter frequency. "
921 "Make sure the counter has the correct input attached.";
922 goto cleanup;
923 }
924
925 sprintf(cmd, "fru-dump -i \"%s\" -o \"%s\" -t %x 2>&1", eeprom_path,
926 eeprom_path, (unsigned int)current_freq);
927 cmdfp = popen(cmd, "r");
928
929 if (!cmdfp || pclose(cmdfp) != 0) {
930 failure_msg = "Error running fru-dump to write to EEPROM";
931 fprintf(stderr, "Error running fru-dump: %s\n", cmd);
932 goto cleanup;
933 }
934
935 cleanup:
936 if (failure_msg) {
937 fprintf(stderr, "SCPI failed: %s\n", failure_msg);
938 GtkWidget *toplevel = gtk_widget_get_toplevel(fmcomms2_panel);
939 if (!gtk_widget_is_toplevel(toplevel))
940 toplevel = NULL;
941 GtkWidget *dcxo_to_eeprom_fail = gtk_message_dialog_new(
942 GTK_WINDOW(toplevel),
943 GTK_DIALOG_MODAL,
944 GTK_MESSAGE_ERROR,
945 GTK_BUTTONS_CLOSE,
946 "%s", failure_msg);
947 gtk_window_set_title(GTK_WINDOW(dcxo_to_eeprom_fail), "Save to EEPROM");
948 if (gtk_dialog_run(GTK_DIALOG(dcxo_to_eeprom_fail)))
949 gtk_widget_destroy(dcxo_to_eeprom_fail);
950 ret = -1;
951 }
952
953 g_free((void *)eeprom_path);
954
955 return ret;
956 }
957
958 #endif /* _WIN32 */
959
dcxo_cal_clicked(GtkButton * btn,gpointer data)960 static int dcxo_cal_clicked(GtkButton *btn, gpointer data)
961 {
962 double current_freq, target_freq = 0, diff = 0, orig_diff = 0, prev_diff = 0;
963 int coarse = 0, fine = 4095, tune_step = 1, direction = 0, ret = 0;
964 GQueue *tuning_elems = NULL;
965 struct tuning_param *tuning_elem = NULL;
966 bool fine_tune = false;
967 char *failure_msg = NULL;
968
969 FILE *fp;
970
971 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
972 goto dcxo_cleanup;
973
974 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(dcxo_cal_type))) {
975 case 0: /* REFCLK */
976 /* Force the correct clock output mode. */
977 iio_device_debug_attr_write_longlong(dev, "adi,clk-output-mode-select", 1);
978 iio_device_debug_attr_write_longlong(dev, "initialize", 1);
979
980 if (!strcmp(iio_context_get_name(ctx), "network")) {
981 target_freq = REFCLK_RATE;
982 } else if (!strcmp(iio_context_get_name(ctx), "local")) {
983 fp = fopen("/sys/kernel/debug/clk/ad9361_ext_refclk/clk_rate", "r");
984 if (!fp || fscanf(fp, "%lf", &target_freq) != 1) {
985 failure_msg = "Unable to read AD9361 reference clock rate from debugfs.";
986 if (fp)
987 fclose(fp);
988 goto dcxo_cleanup;
989 }
990 if (fp)
991 fclose(fp);
992 } else {
993 failure_msg = "AD9361 Reference clock rate missing from debugfs.";
994 goto dcxo_cleanup;
995 }
996 break;
997 case 1: /* RF Output */
998 target_freq = mhz_scale * (
999 gtk_spin_button_get_value(GTK_SPIN_BUTTON(tx_widgets[tx_lo].widget)) +
1000 gtk_spin_button_get_value(GTK_SPIN_BUTTON(
1001 dac_data_manager_get_widget(dac_tx_manager, dac_data_manager_dds_tone(0, TONE_1, TONE_I), WIDGET_FREQUENCY))));
1002 break;
1003 case 2: /* RF Input */
1004 failure_msg = "RF Input is not supported yet for DCXO calibration.";
1005 goto dcxo_cleanup;
1006 break;
1007 default:
1008 failure_msg = "Unsupported calibration method selected.";
1009 goto dcxo_cleanup;
1010 }
1011
1012 if (scpi_connect_counter() != 0) {
1013 failure_msg = "Failed to connect to Programmable Counter device.";
1014 goto dcxo_cleanup;
1015 }
1016
1017 /* Alter toggle button text on start and disable user input for certain
1018 * widgets during calibration.
1019 */
1020 gtk_button_set_label(btn, "Stop calibration");
1021 gtk_widget_set_sensitive(dcxo_cal_type, FALSE);
1022 gtk_widget_set_sensitive(glb_widgets[dcxo_coarse_num].widget, FALSE);
1023 gtk_widget_set_sensitive(glb_widgets[dcxo_fine_num].widget, FALSE);
1024
1025 tuning_elems = g_queue_new();
1026 target_freq = roundf(target_freq);
1027
1028 while (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn))) {
1029 gtk_widget_show(dcxo_cal_progressbar);
1030 gtk_spin_button_set_value(GTK_SPIN_BUTTON(glb_widgets[dcxo_coarse_num].widget), coarse);
1031 gtk_spin_button_set_value(GTK_SPIN_BUTTON(glb_widgets[dcxo_fine_num].widget), fine);
1032 dcxo_widgets_update();
1033 while (gtk_events_pending())
1034 gtk_main_iteration();
1035
1036 /* Querying frequency counters via SCPI too quickly leads to failures. */
1037 sleep(1);
1038
1039 if (scpi_counter_get_freq(¤t_freq, &target_freq) != 0) {
1040 failure_msg = "Error retrieving counter frequency. "
1041 "Make sure the counter has the correct input attached.";
1042 goto dcxo_cleanup;
1043 }
1044
1045 /* Sometimes the frequency counter returns entirely wrong values that
1046 * are orders of magnitude off. In those cases we trigger a new
1047 * measurement request and hope the device returns the correct value
1048 * this time.
1049 */
1050 if (prev_diff != 0 && fabs(target_freq - current_freq) > 10 * fabs(prev_diff)) {
1051 fprintf(stderr, "Skipping likely erroneous response from SCPI device. "
1052 "Previous difference to target frequency was %lf Hz, possible bad value's "
1053 "difference is %lf Hz.\n", prev_diff, (target_freq - current_freq));
1054 continue;
1055 }
1056
1057 prev_diff = diff;
1058 diff = target_freq - current_freq;
1059
1060 /* Show progress towards the target frequency in relation to the
1061 * original frequency measurement.
1062 */
1063 if (orig_diff == 0) {
1064 orig_diff = fabs(diff);
1065 direction = (int)fabs(diff)/diff;
1066 } else {
1067 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(
1068 dcxo_cal_progressbar), (orig_diff-fabs(diff))/orig_diff);
1069 }
1070
1071 /* Store the past ten tuning value pairs and related frequencies. This
1072 * is used to determine the final values that are the closest to the
1073 * target frequency.
1074 */
1075 if (g_queue_get_length(tuning_elems) >= 10)
1076 g_queue_pop_head(tuning_elems);
1077 tuning_elem = g_new(struct tuning_param, 1);
1078 tuning_elem->frequency = current_freq;
1079 tuning_elem->coarse = coarse;
1080 tuning_elem->fine = fine;
1081 g_queue_push_tail(tuning_elems, tuning_elem);
1082
1083 if (fine_tune) {
1084 /* Stop once we go past our target frequency. */
1085 if (diff != 0) {
1086 if (direction < 0 && current_freq < target_freq)
1087 break;
1088 else if (direction > 0 && current_freq > target_freq)
1089 break;
1090 }
1091
1092 tune_step = (int)roundf(-1 * (diff / 2));
1093
1094 /* Force the next tuning step to be at least positive or negative 1. */
1095 if (tune_step == 0)
1096 tune_step = -1 * direction;
1097
1098 fine += tune_step;
1099 } else {
1100 /* Do a binary search for the closest approaching coarse tune value
1101 * in relation to the target frequency. When the difference to the
1102 * target frequency is below another coarse tune step, switch to
1103 * fine tuning.
1104 */
1105 if (tune_step != 0) {
1106 if (prev_diff != 0)
1107 tune_step = (int)nearbyintf(-1 * ((tune_step * diff) / fabs(diff - prev_diff)) / 2);
1108 coarse += tune_step;
1109 } else {
1110 fine_tune = true;
1111 }
1112 }
1113
1114 if (coarse < 0 || coarse > 63 || fine < 0 || fine > 8191) {
1115 failure_msg = "Outside of tuning bounds. Make sure you have the "
1116 "correct calibration method selected.\n";
1117 goto dcxo_cleanup;
1118 }
1119 }
1120
1121 /* Determine the median tuning value from the list of acceptable values.
1122 * Values are first removed from the beginning of the queue if they have a
1123 * higher difference to the target frequency in comparison to the last
1124 * added element.
1125 */
1126 if (g_queue_get_length(tuning_elems) > 1) {
1127 tuning_elem = g_queue_peek_tail(tuning_elems);
1128 diff = fabs(tuning_elem->frequency - target_freq);
1129
1130 while (g_queue_get_length(tuning_elems) > 1) {
1131 tuning_elem = g_queue_peek_head(tuning_elems);
1132 if (fabs(tuning_elem->frequency - target_freq) > diff)
1133 g_queue_pop_head(tuning_elems);
1134 else
1135 break;
1136 }
1137
1138 /* Set final tuning values using the median value of the remaining
1139 * range.
1140 */
1141 tuning_elem = g_queue_peek_nth(
1142 tuning_elems, ceil(g_queue_get_length(tuning_elems)/2));
1143 gtk_spin_button_set_value(GTK_SPIN_BUTTON(
1144 glb_widgets[dcxo_coarse_num].widget), tuning_elem->coarse);
1145 gtk_spin_button_set_value(GTK_SPIN_BUTTON(
1146 glb_widgets[dcxo_fine_num].widget), tuning_elem->fine);
1147 }
1148
1149 dcxo_cleanup:
1150 if (failure_msg) {
1151 GtkWidget *toplevel = gtk_widget_get_toplevel(fmcomms2_panel);
1152 if (!gtk_widget_is_toplevel(toplevel))
1153 toplevel = NULL;
1154 GtkWidget *dcxo_cal_dialog_done = gtk_message_dialog_new(
1155 GTK_WINDOW(toplevel),
1156 GTK_DIALOG_MODAL,
1157 GTK_MESSAGE_ERROR,
1158 GTK_BUTTONS_CLOSE,
1159 "%s", failure_msg);
1160 gtk_window_set_title(GTK_WINDOW(dcxo_cal_dialog_done), "DCXO calibration");
1161 if (gtk_dialog_run(GTK_DIALOG(dcxo_cal_dialog_done)))
1162 gtk_widget_destroy(dcxo_cal_dialog_done);
1163 ret = -1;
1164 }
1165
1166 gtk_widget_hide(dcxo_cal_progressbar);
1167
1168 /* reset calibration buttons */
1169 gtk_button_set_label(btn, "Calibrate DCXO");
1170 g_signal_handlers_block_by_func(btn, G_CALLBACK(dcxo_cal_clicked), NULL);
1171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), FALSE);
1172 g_signal_handlers_unblock_by_func(btn, G_CALLBACK(dcxo_cal_clicked), NULL);
1173
1174 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dcxo_cal_progressbar), 0);
1175 gtk_widget_set_sensitive(dcxo_cal_type, TRUE);
1176 gtk_widget_set_sensitive(glb_widgets[dcxo_coarse_num].widget, TRUE);
1177 gtk_widget_set_sensitive(glb_widgets[dcxo_fine_num].widget, TRUE);
1178
1179 auto_calibrate = 1;
1180
1181 if (tuning_elems)
1182 g_queue_free_full(tuning_elems, (GDestroyNotify)g_free);
1183
1184 return ret;
1185 }
1186
hide_section_cb(GtkToggleToolButton * btn,GtkWidget * section)1187 static void hide_section_cb(GtkToggleToolButton *btn, GtkWidget *section)
1188 {
1189 GtkWidget *toplevel;
1190
1191 if (gtk_toggle_tool_button_get_active(btn)) {
1192 g_object_set(GTK_OBJECT(btn), "stock-id", "gtk-go-down", NULL);
1193 gtk_widget_show(section);
1194 } else {
1195 g_object_set(GTK_OBJECT(btn), "stock-id", "gtk-go-up", NULL);
1196 gtk_widget_hide(section);
1197 toplevel = gtk_widget_get_toplevel(GTK_WIDGET(btn));
1198 if (gtk_widget_is_toplevel(toplevel))
1199 gtk_window_resize (GTK_WINDOW(toplevel), 1, 1);
1200 }
1201 }
1202
write_int(struct iio_channel * chn,const char * attr,int val)1203 static int write_int(struct iio_channel *chn, const char *attr, int val)
1204 {
1205 return iio_channel_attr_write_longlong(chn, attr, (long long) val);
1206 }
1207
fastlock_clicked(GtkButton * btn,gpointer data)1208 static void fastlock_clicked(GtkButton *btn, gpointer data)
1209 {
1210 int profile;
1211
1212 switch ((uintptr_t) data) {
1213 case 1: /* RX Store */
1214 iio_widget_save(&rx_widgets[rx_lo]);
1215 profile = gtk_combo_box_get_active(GTK_COMBO_BOX(rx_fastlock_profile));
1216 write_int(iio_device_find_channel(dev, "altvoltage0", true),
1217 rx_fastlock_store_name, profile);
1218 break;
1219 case 2: /* TX Store */
1220 iio_widget_save(&tx_widgets[tx_lo]);
1221 profile = gtk_combo_box_get_active(GTK_COMBO_BOX(tx_fastlock_profile));
1222 write_int(iio_device_find_channel(dev, "altvoltage1", true),
1223 tx_fastlock_store_name, profile);
1224 break;
1225 case 3: /* RX Recall */
1226 profile = gtk_combo_box_get_active(GTK_COMBO_BOX(rx_fastlock_profile));
1227 write_int(iio_device_find_channel(dev, "altvoltage0", true),
1228 rx_fastlock_recall_name, profile);
1229 iio_widget_update(&rx_widgets[rx_lo]);
1230 break;
1231 case 4: /* TX Recall */
1232 profile = gtk_combo_box_get_active(GTK_COMBO_BOX(tx_fastlock_profile));
1233 write_int(iio_device_find_channel(dev, "altvoltage1", true),
1234 tx_fastlock_recall_name, profile);
1235 iio_widget_update(&tx_widgets[tx_lo]);
1236 break;
1237 }
1238 }
1239
filter_fir_config_file_set_cb(GtkFileChooser * chooser,gpointer data)1240 static void filter_fir_config_file_set_cb (GtkFileChooser *chooser, gpointer data)
1241 {
1242 int ret;
1243 char *file_name = gtk_file_chooser_get_filename(chooser);
1244
1245 ret = load_fir_filter(file_name, dev, NULL, fmcomms2_panel, chooser,
1246 fir_filter_en_tx, enable_fir_filter_rx,
1247 enable_fir_filter_rx_tx, disable_all_fir_filters,
1248 last_fir_filter);
1249
1250 if (ret >= 0)
1251 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (enable_auto_filter), FALSE);
1252 }
1253
1254 static int compare_gain(const char *a, const char *b) __attribute__((unused));
compare_gain(const char * a,const char * b)1255 static int compare_gain(const char *a, const char *b)
1256 {
1257 double val_a, val_b;
1258 sscanf(a, "%lf", &val_a);
1259 sscanf(b, "%lf", &val_b);
1260
1261 if (val_a < val_b)
1262 return -1;
1263 else if(val_a > val_b)
1264 return 1;
1265 else
1266 return 0;
1267 }
1268
rx_phase_rotation_set(GtkSpinButton * spinbutton,gpointer user_data)1269 static void rx_phase_rotation_set(GtkSpinButton *spinbutton, gpointer user_data)
1270 {
1271 uintptr_t offset = (uintptr_t) user_data;
1272 struct iio_channel *out0, *out1;
1273 gdouble val, phase;
1274
1275 if (!cap)
1276 return;
1277
1278 val = gtk_spin_button_get_value(spinbutton);
1279
1280 phase = val * 2 * M_PI / 360.0;
1281
1282 if (offset == 2) {
1283 out0 = iio_device_find_channel(cap, "voltage2", false);
1284 out1 = iio_device_find_channel(cap, "voltage3", false);
1285 } else {
1286 out0 = iio_device_find_channel(cap, "voltage0", false);
1287 out1 = iio_device_find_channel(cap, "voltage1", false);
1288 }
1289
1290 if (out1 && out0) {
1291 iio_channel_attr_write_double(out0, "calibscale", (double) cos(phase));
1292 iio_channel_attr_write_double(out0, "calibphase", (double) (-1 * sin(phase)));
1293 iio_channel_attr_write_double(out1, "calibscale", (double) cos(phase));
1294 iio_channel_attr_write_double(out1, "calibphase", (double) sin(phase));
1295 }
1296 }
1297
1298 /* Check for a valid two channels combination (ch0->ch1, ch2->ch3, ...)
1299 *
1300 * struct iio_device *dev - the iio device that owns the channels
1301 * char* ch_name - output parameter: stores the names of to the
1302 * enabled channels, useful for reporting for which
1303 * channels the combination is valid or not.
1304 * Return 1 if the channel combination is valid and 0 otherwise.
1305 */
channel_combination_check(struct iio_device * dev,const char ** ch_names)1306 static int channel_combination_check(struct iio_device *dev, const char **ch_names)
1307 {
1308 bool consecutive_ch = FALSE;
1309 unsigned int i, k;
1310 GArray *channels = get_iio_channels_naturally_sorted(dev);
1311
1312 for (i = 0, k = 0; i < channels->len; ++i) {
1313 struct iio_channel *ch = g_array_index(channels, struct iio_channel *, i);
1314 struct extra_info *info = iio_channel_get_data(ch);
1315
1316 if (info->may_be_enabled) {
1317 const char *name = iio_channel_get_name(ch) ?: iio_channel_get_id(ch);
1318 ch_names[k++] = name;
1319
1320 if (i > 0) {
1321 struct extra_info *prev = iio_channel_get_data(g_array_index(channels, struct iio_channel *, i - 1));
1322
1323 if (prev->may_be_enabled) {
1324 consecutive_ch = TRUE;
1325 break;
1326 }
1327 }
1328 }
1329 }
1330 g_array_free(channels, FALSE);
1331
1332 if (!consecutive_ch)
1333 return 0;
1334
1335 if (!(i & 0x1))
1336 return 0;
1337
1338 return 1;
1339 }
1340
save_widget_value(GtkWidget * widget,struct iio_widget * iio_w)1341 static void save_widget_value(GtkWidget *widget, struct iio_widget *iio_w)
1342 {
1343 iio_w->save(iio_w);
1344 }
1345
make_widget_update_signal_based(struct iio_widget * widgets,unsigned int num_widgets)1346 static void make_widget_update_signal_based(struct iio_widget *widgets,
1347 unsigned int num_widgets)
1348 {
1349 char signal_name[25];
1350 unsigned int i;
1351
1352 for (i = 0; i < num_widgets; i++) {
1353 if (GTK_IS_CHECK_BUTTON(widgets[i].widget))
1354 sprintf(signal_name, "%s", "toggled");
1355 else if (GTK_IS_TOGGLE_BUTTON(widgets[i].widget))
1356 sprintf(signal_name, "%s", "toggled");
1357 else if (GTK_IS_SPIN_BUTTON(widgets[i].widget))
1358 sprintf(signal_name, "%s", "value-changed");
1359 else if (GTK_IS_COMBO_BOX_TEXT(widgets[i].widget))
1360 sprintf(signal_name, "%s", "changed");
1361 else
1362 printf("unhandled widget type, attribute: %s\n", widgets[i].attr_name);
1363
1364 if (GTK_IS_SPIN_BUTTON(widgets[i].widget) &&
1365 widgets[i].priv_progress != NULL) {
1366 iio_spin_button_progress_activate(&widgets[i]);
1367 } else {
1368 g_signal_connect(G_OBJECT(widgets[i].widget), signal_name, G_CALLBACK(save_widget_value), &widgets[i]);
1369 }
1370 }
1371 }
1372
handle_external_request(struct osc_plugin * plugin,const char * request)1373 static int handle_external_request (struct osc_plugin *plugin, const char *request)
1374 {
1375 int ret = 0;
1376
1377 if (!strcmp(request, "Reload Settings")) {
1378 reload_button_clicked(NULL, 0);
1379 ret = 1;
1380 }
1381
1382 return ret;
1383 }
1384
fmcomms2_handle_driver(struct osc_plugin * plugin,const char * attrib,const char * value)1385 static int fmcomms2_handle_driver(struct osc_plugin *plugin, const char *attrib, const char *value)
1386 {
1387 int ret = 0;
1388
1389 if (MATCH_ATTRIB("load_fir_filter_file")) {
1390 if (value[0]) {
1391 load_fir_filter(value, dev, NULL, fmcomms2_panel,
1392 GTK_FILE_CHOOSER(filter_fir_config),
1393 fir_filter_en_tx, enable_fir_filter_rx,
1394 enable_fir_filter_rx_tx,
1395 disable_all_fir_filters,
1396 last_fir_filter);
1397 }
1398 } else if (MATCH_ATTRIB("dds_mode_tx1")) {
1399 dac_data_manager_set_dds_mode(dac_tx_manager,
1400 DDS_DEVICE, 1, atoi(value));
1401 } else if (MATCH_ATTRIB("dds_mode_tx2")) {
1402 dac_data_manager_set_dds_mode(dac_tx_manager,
1403 DDS_DEVICE, 2, atoi(value));
1404 } else if (MATCH_ATTRIB("global_settings_show")) {
1405 gtk_toggle_tool_button_set_active(
1406 section_toggle[SECTION_GLOBAL], !!atoi(value));
1407 hide_section_cb(section_toggle[SECTION_GLOBAL],
1408 section_setting[SECTION_GLOBAL]);
1409 } else if (MATCH_ATTRIB("tx_show")) {
1410 gtk_toggle_tool_button_set_active(
1411 section_toggle[SECTION_TX], !!atoi(value));
1412 hide_section_cb(section_toggle[SECTION_TX],
1413 section_setting[SECTION_TX]);
1414 } else if (MATCH_ATTRIB("rx_show")) {
1415 gtk_toggle_tool_button_set_active(
1416 section_toggle[SECTION_RX], !!atoi(value));
1417 hide_section_cb(section_toggle[SECTION_RX],
1418 section_setting[SECTION_RX]);
1419 } else if (MATCH_ATTRIB("fpga_show")) {
1420 gtk_toggle_tool_button_set_active(
1421 section_toggle[SECTION_FPGA], !!atoi(value));
1422 hide_section_cb(section_toggle[SECTION_FPGA],
1423 section_setting[SECTION_FPGA]);
1424 } else if (MATCH_ATTRIB("up_down_converter")) {
1425 gtk_toggle_button_set_active(
1426 (GtkToggleButton *)up_down_converter, !!atoi(value));
1427 } else if (!strncmp(attrib, "tx_channel_", sizeof("tx_channel_") - 1)) {
1428 int tx = atoi(attrib + sizeof("tx_channel_") - 1);
1429 dac_data_manager_set_tx_channel_state(
1430 dac_tx_manager, tx, !!atoi(value));
1431 } else if (MATCH_ATTRIB("dac_buf_filename")) {
1432 dac_data_manager_set_buffer_chooser_filename(
1433 dac_tx_manager, value);
1434 #ifndef _WIN32
1435 } else if (MATCH_ATTRIB("dcxo_calibrate")) {
1436 /* calibration button needs to be active for the function to run */
1437 g_signal_handlers_block_by_func(
1438 GTK_TOGGLE_BUTTON(dcxo_cal), G_CALLBACK(dcxo_cal_clicked), NULL);
1439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dcxo_cal), TRUE);
1440 g_signal_handlers_unblock_by_func(
1441 GTK_TOGGLE_BUTTON(dcxo_cal), G_CALLBACK(dcxo_cal_clicked), NULL);
1442 ret = dcxo_cal_clicked(GTK_BUTTON(dcxo_cal), NULL);
1443 while (!auto_calibrate)
1444 gtk_main_iteration();
1445 } else if (MATCH_ATTRIB("dcxo_to_eeprom")) {
1446 ret = dcxo_cal_to_eeprom_clicked(NULL, NULL);
1447 } else if (MATCH_ATTRIB("xo_freq_to_eeprom")) {
1448 ret = xo_freq_to_eeprom();
1449 #endif /* _WIN32 */
1450 } else if (MATCH_ATTRIB("SYNC_RELOAD")) {
1451 if (can_update_widgets)
1452 reload_button_clicked(NULL, NULL);
1453 } else {
1454 return -EINVAL;
1455 }
1456
1457 return ret;
1458 }
1459
fmcomms2_handle(struct osc_plugin * plugin,int line,const char * attrib,const char * value)1460 static int fmcomms2_handle(struct osc_plugin *plugin, int line, const char *attrib, const char *value)
1461 {
1462 return osc_plugin_default_handle(ctx, line, attrib, value,
1463 fmcomms2_handle_driver, NULL);
1464 }
1465
load_profile(struct osc_plugin * plugin,const char * ini_fn)1466 static void load_profile(struct osc_plugin *plugin, const char *ini_fn)
1467 {
1468 struct iio_channel *ch;
1469 char *value;
1470 unsigned int i;
1471
1472 for (i = 0; i < ARRAY_SIZE(fmcomms2_driver_attribs); i++) {
1473 char *value = read_token_from_ini(ini_fn, THIS_DRIVER,
1474 fmcomms2_driver_attribs[i]);
1475 if (value) {
1476 fmcomms2_handle_driver(NULL,
1477 fmcomms2_driver_attribs[i], value);
1478 free(value);
1479 }
1480 }
1481
1482 /* The gain_control_mode iio attribute should be set prior to setting
1483 * hardwaregain iio attribute. This is neccessary due to the fact that
1484 * some control modes change the hardwaregain automatically. */
1485 ch = iio_device_find_channel(dev, "voltage0", false);
1486 value = read_token_from_ini(ini_fn, THIS_DRIVER,
1487 PHY_DEVICE".in_voltage0_gain_control_mode");
1488 if (ch && value) {
1489 iio_channel_attr_write(ch, "gain_control_mode", value);
1490 free(value);
1491 }
1492
1493 ch = iio_device_find_channel(dev, "voltage1", false);
1494 value = read_token_from_ini(ini_fn, THIS_DRIVER,
1495 PHY_DEVICE".in_voltage1_gain_control_mode");
1496 if (ch && value) {
1497 iio_channel_attr_write(ch, "gain_control_mode", value);
1498 free(value);
1499 }
1500
1501 update_from_ini(ini_fn, THIS_DRIVER, dev, fmcomms2_sr_attribs,
1502 ARRAY_SIZE(fmcomms2_sr_attribs));
1503 if (dds)
1504 update_from_ini(ini_fn, THIS_DRIVER, dds, fmcomms2_sr_attribs,
1505 ARRAY_SIZE(fmcomms2_sr_attribs));
1506 if (udc_rx)
1507 update_from_ini(ini_fn, THIS_DRIVER, udc_rx, fmcomms2_sr_attribs,
1508 ARRAY_SIZE(fmcomms2_sr_attribs));
1509 if (udc_tx)
1510 update_from_ini(ini_fn, THIS_DRIVER, udc_tx, fmcomms2_sr_attribs,
1511 ARRAY_SIZE(fmcomms2_sr_attribs));
1512
1513 if (can_update_widgets)
1514 reload_button_clicked(NULL, NULL);
1515 }
1516
fmcomms2_init(struct osc_plugin * plugin,GtkWidget * notebook,const char * ini_fn)1517 static GtkWidget * fmcomms2_init(struct osc_plugin *plugin, GtkWidget *notebook, const char *ini_fn)
1518 {
1519 GtkBuilder *builder;
1520 GtkWidget *dds_container;
1521 struct iio_channel *ch0, *ch1;
1522
1523 can_update_widgets = false;
1524
1525 ctx = osc_create_context();
1526 if (!ctx)
1527 return NULL;
1528
1529 dev = iio_context_find_device(ctx, PHY_DEVICE);
1530 dds = iio_context_find_device(ctx, DDS_DEVICE);
1531 cap = iio_context_find_device(ctx, CAP_DEVICE);
1532 udc_rx = iio_context_find_device(ctx, UDC_RX_DEVICE);
1533 udc_tx = iio_context_find_device(ctx, UDC_TX_DEVICE);
1534 has_udc_driver = (udc_rx && udc_tx);
1535
1536 ch0 = iio_device_find_channel(dev, "voltage0", false);
1537 ch1 = iio_device_find_channel(dev, "voltage1", false);
1538
1539 dac_tx_manager = dac_data_manager_new(dds, NULL, ctx);
1540
1541 const char *env_freq_span = getenv("OSC_UPDN_FREQ_SPAN");
1542 const char *env_freq_mix_sign = getenv("OSC_UPDN_FREQ_MIX_SIGN");
1543
1544 if(!env_freq_span) {
1545 updn_freq_span = 2;
1546 } else {
1547 errno = 0;
1548 updn_freq_span = g_strtod(env_freq_span, NULL);
1549 if (errno)
1550 updn_freq_span = 2;
1551 }
1552
1553 if(!env_freq_mix_sign) {
1554 updn_freq_mix_sign = 1;
1555 } else {
1556 if (!strncmp(env_freq_mix_sign, "-", 1))
1557 updn_freq_mix_sign = -1;
1558 else
1559 updn_freq_mix_sign = 1;
1560 }
1561
1562 builder = gtk_builder_new();
1563 nbook = GTK_NOTEBOOK(notebook);
1564
1565 if (osc_load_glade_file(builder, "fmcomms2") < 0) {
1566 osc_destroy_context(ctx);
1567 return NULL;
1568 }
1569
1570 is_2rx_2tx = ch1 && iio_channel_find_attr(ch1, "hardwaregain");
1571
1572 fmcomms2_panel = GTK_WIDGET(gtk_builder_get_object(builder, "fmcomms2_panel"));
1573
1574 /* Hide DCXO calibration support if the scpi plugin isn't loaded. */
1575 if (!scpi_connect_functions())
1576 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "dcxo_cal_grid")));
1577
1578 #ifndef _WIN32
1579 /* Disable EEPROM functionality if not running locally. */
1580 if (strcmp(iio_context_get_name(ctx), "local") != 0) {
1581 gtk_widget_set_sensitive(GTK_WIDGET(
1582 gtk_builder_get_object(builder, "dcxo_cal_to_eeprom")), FALSE);
1583 gtk_widget_set_sensitive(GTK_WIDGET(
1584 gtk_builder_get_object(builder, "dcxo_cal_from_eeprom")), FALSE);
1585 }
1586
1587 /* Disable saving to EEPROM if not running as root. */
1588 if (getuid() != 0) {
1589 gtk_widget_set_sensitive(GTK_WIDGET(
1590 gtk_builder_get_object(builder, "dcxo_cal_to_eeprom")), FALSE);
1591 }
1592 #endif
1593
1594 /* Global settings */
1595
1596 ensm_mode = GTK_WIDGET(gtk_builder_get_object(builder, "ensm_mode"));
1597 ensm_mode_available = GTK_WIDGET(gtk_builder_get_object(builder, "ensm_mode_available"));
1598 calib_mode = GTK_WIDGET(gtk_builder_get_object(builder, "calib_mode"));
1599 calib_mode_available = GTK_WIDGET(gtk_builder_get_object(builder, "calib_mode_available"));
1600 trx_rate_governor = GTK_WIDGET(gtk_builder_get_object(builder, "trx_rate_governor"));
1601 trx_rate_governor_available = GTK_WIDGET(gtk_builder_get_object(builder, "trx_rate_governor_available"));
1602 tx_path_rates = GTK_WIDGET(gtk_builder_get_object(builder, "label_tx_path"));
1603 rx_path_rates = GTK_WIDGET(gtk_builder_get_object(builder, "label_rx_path"));
1604 filter_fir_config = GTK_WIDGET(gtk_builder_get_object(builder, "filter_fir_config"));
1605 enable_fir_filter_rx = GTK_WIDGET(gtk_builder_get_object(builder, "enable_fir_filter_rx"));
1606 fir_filter_en_tx = GTK_WIDGET(gtk_builder_get_object(builder, "fir_filter_en_tx"));
1607 enable_fir_filter_rx_tx = GTK_WIDGET(gtk_builder_get_object(builder, "enable_fir_filter_tx_rx"));
1608 disable_all_fir_filters = GTK_WIDGET(gtk_builder_get_object(builder, "disable_all_fir_filters"));
1609 up_down_converter = GTK_WIDGET(gtk_builder_get_object(builder, "checkbox_up_down_converter"));
1610 dcxo_cal_progressbar = GTK_WIDGET(gtk_builder_get_object(builder, "dcxo_cal_progressbar"));
1611 dcxo_cal_type = GTK_WIDGET(gtk_builder_get_object(builder, "dcxo_cal_type"));
1612 dcxo_cal = GTK_WIDGET(gtk_builder_get_object(builder, "dcxo_cal"));
1613 enable_auto_filter = GTK_WIDGET(gtk_builder_get_object(builder, "enable_auto_filter"));
1614 dcxo_cal_tab = GTK_WIDGET(gtk_builder_get_object(builder, "dcxo_tab"));
1615
1616 section_toggle[SECTION_GLOBAL] = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "global_settings_toggle"));
1617 section_setting[SECTION_GLOBAL] = GTK_WIDGET(gtk_builder_get_object(builder, "global_settings"));
1618 section_toggle[SECTION_TX] = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "tx_toggle"));
1619 section_setting[SECTION_TX] = GTK_WIDGET(gtk_builder_get_object(builder, "tx_settings"));
1620 section_toggle[SECTION_RX] = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "rx_toggle"));
1621 section_setting[SECTION_RX] = GTK_WIDGET(gtk_builder_get_object(builder, "rx_settings"));
1622 section_toggle[SECTION_FPGA] = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "fpga_toggle"));
1623 section_setting[SECTION_FPGA] = GTK_WIDGET(gtk_builder_get_object(builder, "fpga_settings"));
1624
1625 /* Receive Chain */
1626
1627 rf_port_select_rx = GTK_WIDGET(gtk_builder_get_object(builder, "rf_port_select_rx"));
1628 rx_gain_control_rx1 = GTK_WIDGET(gtk_builder_get_object(builder, "gain_control_mode_rx1"));
1629 rx_gain_control_rx2 = GTK_WIDGET(gtk_builder_get_object(builder, "gain_control_mode_rx2"));
1630 rx_gain_control_modes_rx1 = GTK_WIDGET(gtk_builder_get_object(builder, "gain_control_mode_available_rx1"));
1631 rx_gain_control_modes_rx2 = GTK_WIDGET(gtk_builder_get_object(builder, "gain_control_mode_available_rx2"));
1632 rx1_rssi = GTK_WIDGET(gtk_builder_get_object(builder, "rssi_rx1"));
1633 rx2_rssi = GTK_WIDGET(gtk_builder_get_object(builder, "rssi_rx2"));
1634 rx_fastlock_profile = GTK_WIDGET(gtk_builder_get_object(builder, "rx_fastlock_profile"));
1635 fpga_rx_frequency_available = GTK_WIDGET(gtk_builder_get_object(builder, "fpga_rx_frequency_available"));
1636 sampling_freq_rx_decim = GTK_WIDGET(gtk_builder_get_object(builder, "sampling_freq_rx_decim"));
1637
1638 /* Transmit Chain */
1639
1640 rf_port_select_tx = GTK_WIDGET(gtk_builder_get_object(builder, "rf_port_select_tx"));
1641 tx_fastlock_profile = GTK_WIDGET(gtk_builder_get_object(builder, "tx_fastlock_profile"));
1642 tx1_rssi = GTK_WIDGET(gtk_builder_get_object(builder, "rssi_tx1"));
1643 tx2_rssi = GTK_WIDGET(gtk_builder_get_object(builder, "rssi_tx2"));
1644 fpga_tx_frequency_available = GTK_WIDGET(gtk_builder_get_object(builder, "fpga_tx_frequency_available"));
1645 sampling_freq_tx_inter = GTK_WIDGET(gtk_builder_get_object(builder, "sampling_freq_tx_inter"));
1646
1647 dds_container = GTK_WIDGET(gtk_builder_get_object(builder, "dds_transmit_block"));
1648
1649 if (dac_tx_manager)
1650 gtk_container_add(GTK_CONTAINER(dds_container),
1651 dac_data_manager_get_gui_container(dac_tx_manager));
1652 gtk_widget_show_all(dds_container);
1653
1654 rx_phase_rotation[0] = GTK_WIDGET(gtk_builder_get_object(builder, "rx1_phase_rotation"));
1655 rx_phase_rotation[1] = GTK_WIDGET(gtk_builder_get_object(builder, "rx2_phase_rotation"));
1656
1657 gtk_combo_box_set_active(GTK_COMBO_BOX(ensm_mode_available), 0);
1658 gtk_combo_box_set_active(GTK_COMBO_BOX(trx_rate_governor_available), 0);
1659 gtk_combo_box_set_active(GTK_COMBO_BOX(rx_gain_control_modes_rx1), 0);
1660 gtk_combo_box_set_active(GTK_COMBO_BOX(rx_gain_control_modes_rx2), 0);
1661 gtk_combo_box_set_active(GTK_COMBO_BOX(rf_port_select_rx), 0);
1662 gtk_combo_box_set_active(GTK_COMBO_BOX(rf_port_select_tx), 0);
1663 gtk_combo_box_set_active(GTK_COMBO_BOX(rx_fastlock_profile), 0);
1664 gtk_combo_box_set_active(GTK_COMBO_BOX(tx_fastlock_profile), 0);
1665 gtk_combo_box_set_active(GTK_COMBO_BOX(dcxo_cal_type), 0);
1666 gtk_combo_box_set_active(GTK_COMBO_BOX(fpga_rx_frequency_available), 0);
1667 gtk_combo_box_set_active(GTK_COMBO_BOX(fpga_tx_frequency_available), 0);
1668
1669 /* Bind the IIO device files to the GUI widgets */
1670
1671 glb_widgets = widgets;
1672
1673 /* Global settings */
1674 iio_combo_box_init(&glb_widgets[num_glb++],
1675 dev, NULL, "ensm_mode", "ensm_mode_available",
1676 ensm_mode_available, NULL);
1677 iio_combo_box_init(&glb_widgets[num_glb++],
1678 dev, NULL, "calib_mode", "calib_mode_available",
1679 calib_mode_available, NULL);
1680 iio_combo_box_init(&glb_widgets[num_glb++],
1681 dev, NULL, "trx_rate_governor", "trx_rate_governor_available",
1682 trx_rate_governor_available, NULL);
1683
1684 dcxo_coarse_num = num_glb;
1685 iio_spin_button_int_init_from_builder(&glb_widgets[num_glb++],
1686 dev, NULL, "dcxo_tune_coarse", builder, "dcxo_coarse_tune",
1687 0);
1688 dcxo_fine_num = num_glb;
1689 iio_spin_button_int_init_from_builder(&glb_widgets[num_glb++],
1690 dev, NULL, "dcxo_tune_fine", builder, "dcxo_fine_tune",
1691 0);
1692
1693 iio_spin_button_int_init_from_builder(&glb_widgets[num_glb++],
1694 dev, NULL, "xo_correction", builder, "xo_correction",
1695 0);
1696
1697 rx_widgets = &glb_widgets[num_glb];
1698
1699 /* Receive Chain */
1700
1701 iio_combo_box_init(&rx_widgets[num_rx++],
1702 dev, ch0, "gain_control_mode",
1703 "gain_control_mode_available",
1704 rx_gain_control_modes_rx1, NULL);
1705
1706 iio_combo_box_init(&rx_widgets[num_rx++],
1707 dev, ch0, "rf_port_select",
1708 "rf_port_select_available",
1709 rf_port_select_rx, NULL);
1710
1711 if (is_2rx_2tx)
1712 iio_combo_box_init(&rx_widgets[num_rx++],
1713 dev, ch1, "gain_control_mode",
1714 "gain_control_mode_available",
1715 rx_gain_control_modes_rx2, NULL);
1716 rx1_gain = num_rx;
1717 iio_spin_button_int_init_from_builder(&rx_widgets[num_rx++],
1718 dev, ch0, "hardwaregain", builder,
1719 "hardware_gain_rx1", NULL);
1720
1721 if (is_2rx_2tx) {
1722 rx2_gain = num_rx;
1723 iio_spin_button_int_init_from_builder(&rx_widgets[num_rx++],
1724 dev, ch1, "hardwaregain", builder,
1725 "hardware_gain_rx2", NULL);
1726 }
1727 rx_sample_freq = num_rx;
1728 iio_spin_button_int_init_from_builder(&rx_widgets[num_rx++],
1729 dev, ch0, "sampling_frequency", builder,
1730 "sampling_freq_rx", &mhz_scale);
1731 iio_spin_button_add_progress(&rx_widgets[num_rx - 1]);
1732
1733 iio_spin_button_int_init_from_builder(&rx_widgets[num_rx++],
1734 dev, ch0, "rf_bandwidth", builder, "rf_bandwidth_rx",
1735 &mhz_scale);
1736 iio_spin_button_add_progress(&rx_widgets[num_rx - 1]);
1737 rx_lo = num_rx;
1738
1739 ch1 = iio_device_find_channel(dev, "altvoltage0", true);
1740 if (iio_channel_find_attr(ch1, "frequency"))
1741 freq_name = "frequency";
1742 else
1743 freq_name = "RX_LO_frequency";
1744 iio_spin_button_s64_init_from_builder(&rx_widgets[num_rx++],
1745 dev, ch1, freq_name, builder,
1746 "rx_lo_freq", &mhz_scale);
1747 iio_spin_button_add_progress(&rx_widgets[num_rx - 1]);
1748
1749 iio_toggle_button_init_from_builder(&rx_widgets[num_rx++],
1750 dev, ch1, "external", builder,
1751 "rx_lo_external", 0);
1752
1753 iio_toggle_button_init_from_builder(&rx_widgets[num_rx++],
1754 dev, ch0, "quadrature_tracking_en", builder,
1755 "quad", 0);
1756 iio_toggle_button_init_from_builder(&rx_widgets[num_rx++],
1757 dev, ch0, "rf_dc_offset_tracking_en", builder,
1758 "rfdc", 0);
1759 iio_toggle_button_init_from_builder(&rx_widgets[num_rx++],
1760 dev, ch0, "bb_dc_offset_tracking_en", builder,
1761 "bbdc", 0);
1762
1763 ch0 = iio_device_find_channel(dev, "altvoltage0", true);
1764
1765 if (iio_channel_find_attr(ch0, "fastlock_store"))
1766 rx_fastlock_store_name = "fastlock_store";
1767 else
1768 rx_fastlock_store_name = "RX_LO_fastlock_store";
1769 if (iio_channel_find_attr(ch0, "fastlock_recall"))
1770 rx_fastlock_recall_name = "fastlock_recall";
1771 else
1772 rx_fastlock_recall_name = "RX_LO_fastlock_recall";
1773
1774 /* Transmit Chain */
1775
1776 tx_widgets = &rx_widgets[num_rx];
1777
1778 ch0 = iio_device_find_channel(dev, "voltage0", true);
1779 if (is_2rx_2tx)
1780 ch1 = iio_device_find_channel(dev, "voltage1", true);
1781
1782 tx_rssi_available = ch0 && iio_channel_find_attr(ch0, "rssi");
1783 if (is_2rx_2tx)
1784 tx_rssi_available = tx_rssi_available &&
1785 (ch1 && iio_channel_find_attr(ch1, "rssi"));
1786
1787 iio_combo_box_init(&tx_widgets[num_tx++],
1788 dev, ch0, "rf_port_select",
1789 "rf_port_select_available",
1790 rf_port_select_tx, NULL);
1791
1792 iio_spin_button_init_from_builder(&tx_widgets[num_tx++],
1793 dev, ch0, "hardwaregain", builder,
1794 "hardware_gain_tx1", &inv_scale);
1795
1796 if (is_2rx_2tx)
1797 iio_spin_button_init_from_builder(&tx_widgets[num_tx++],
1798 dev, ch1, "hardwaregain", builder,
1799 "hardware_gain_tx2", &inv_scale);
1800 tx_sample_freq = num_tx;
1801 iio_spin_button_int_init_from_builder(&tx_widgets[num_tx++],
1802 dev, ch0, "sampling_frequency", builder,
1803 "sampling_freq_tx", &mhz_scale);
1804 iio_spin_button_add_progress(&tx_widgets[num_tx - 1]);
1805 iio_spin_button_int_init_from_builder(&tx_widgets[num_tx++],
1806 dev, ch0, "rf_bandwidth", builder,
1807 "rf_bandwidth_tx", &mhz_scale);
1808 iio_spin_button_add_progress(&tx_widgets[num_tx - 1]);
1809
1810 tx_lo = num_tx;
1811 ch1 = iio_device_find_channel(dev, "altvoltage1", true);
1812
1813 if (iio_channel_find_attr(ch1, "frequency"))
1814 freq_name = "frequency";
1815 else
1816 freq_name = "TX_LO_frequency";
1817 iio_spin_button_s64_init_from_builder(&tx_widgets[num_tx++],
1818 dev, ch1, freq_name, builder, "tx_lo_freq", &mhz_scale);
1819 iio_spin_button_add_progress(&tx_widgets[num_tx - 1]);
1820
1821 iio_toggle_button_init_from_builder(&tx_widgets[num_tx++],
1822 dev, ch1, "external", builder,
1823 "tx_lo_external", 0);
1824
1825 /* FPGA widgets */
1826 fpga_widgets = &tx_widgets[num_tx];
1827
1828 if (dds) {
1829 ch0 = iio_device_find_channel(dds, "voltage0", true);
1830 } else {
1831 ch0 = NULL;
1832 }
1833 if (ch0 && iio_channel_find_attr(ch0, "sampling_frequency_available")) {
1834 iio_combo_box_init(&fpga_widgets[num_fpga++],
1835 dds, ch0, "sampling_frequency",
1836 "sampling_frequency_available",
1837 fpga_tx_frequency_available, NULL);
1838
1839 g_signal_connect_after(sampling_freq_tx_inter, "value-changed",
1840 G_CALLBACK(int_dec_spin_update_cb), ch0);
1841
1842
1843 } else {
1844 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder,
1845 "transmit_frame_dma_buf")));
1846
1847 gtk_widget_hide(sampling_freq_tx_inter);
1848 }
1849
1850 if (cap) {
1851 ch0 = iio_device_find_channel(cap, "voltage0", false);
1852 } else {
1853 ch0 = NULL;
1854 }
1855 if (ch0 && iio_channel_find_attr(ch0, "sampling_frequency_available")) {
1856 iio_combo_box_init(&fpga_widgets[num_fpga++],
1857 cap, ch0, "sampling_frequency",
1858 "sampling_frequency_available",
1859 fpga_rx_frequency_available, NULL);
1860
1861 g_signal_connect_after(sampling_freq_rx_decim, "value-changed",
1862 G_CALLBACK(int_dec_spin_update_cb), ch0);
1863
1864 } else {
1865 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder,
1866 "receive_frame_dma_buf")));
1867 gtk_widget_hide(sampling_freq_rx_decim);
1868
1869 }
1870
1871 ch1 = iio_device_find_channel(dev, "altvoltage1", true);
1872
1873 /* Widgets bindings */
1874 g_builder_bind_property(builder, "rssi_tx1", "visible",
1875 "label_rssi_tx1", "sensitive", G_BINDING_DEFAULT);
1876 g_builder_bind_property(builder, "rssi_tx2", "visible",
1877 "label_rssi_tx2", "sensitive", G_BINDING_DEFAULT);
1878 g_builder_bind_property(builder, "rx_lo_external", "active",
1879 "rx_fastlock_profile", "visible", G_BINDING_INVERT_BOOLEAN);
1880 g_builder_bind_property(builder, "rx_lo_external", "active",
1881 "rx_fastlock_label", "visible", G_BINDING_INVERT_BOOLEAN);
1882 g_builder_bind_property(builder, "rx_lo_external", "active",
1883 "rx_fastlock_actions", "visible", G_BINDING_INVERT_BOOLEAN);
1884 g_builder_bind_property(builder, "tx_lo_external", "active",
1885 "tx_fastlock_profile", "visible", G_BINDING_INVERT_BOOLEAN);
1886 g_builder_bind_property(builder, "tx_lo_external", "active",
1887 "tx_fastlock_label", "visible", G_BINDING_INVERT_BOOLEAN);
1888 g_builder_bind_property(builder, "tx_lo_external", "active",
1889 "tx_fastlock_actions", "visible", G_BINDING_INVERT_BOOLEAN);
1890
1891 if (ini_fn)
1892 load_profile(NULL, ini_fn);
1893
1894 /* Update all widgets with current values */
1895 printf("Updating widgets...\n");
1896 update_widgets();
1897 sample_frequency_changed_cb(NULL);
1898 printf("Updating FIR filter...\n");
1899 filter_fir_update();
1900 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(disable_all_fir_filters), true);
1901 glb_settings_update_labels();
1902 rssi_update_labels();
1903 dac_data_manager_freq_widgets_range_update(dac_tx_manager,
1904 get_gui_tx_sampling_freq() / 2.0);
1905 dac_data_manager_update_iio_widgets(dac_tx_manager);
1906
1907 /* Connect signals */
1908
1909 if (iio_channel_find_attr(ch1, "fastlock_store"))
1910 tx_fastlock_store_name = "fastlock_store";
1911 else
1912 tx_fastlock_store_name = "TX_LO_fastlock_store";
1913 if (iio_channel_find_attr(ch1, "fastlock_recall"))
1914 tx_fastlock_recall_name = "fastlock_recall";
1915 else
1916 tx_fastlock_recall_name = "TX_LO_fastlock_recall";
1917
1918
1919 g_builder_connect_signal(builder, "rx1_phase_rotation", "value-changed",
1920 G_CALLBACK(rx_phase_rotation_set), (gpointer *)0);
1921
1922 g_builder_connect_signal(builder, "rx2_phase_rotation", "value-changed",
1923 G_CALLBACK(rx_phase_rotation_set), (gpointer *)2);
1924
1925 g_builder_connect_signal(builder, "fmcomms2_settings_reload", "clicked",
1926 G_CALLBACK(reload_button_clicked), NULL);
1927
1928 g_builder_connect_signal(builder, "filter_fir_config", "file-set",
1929 G_CALLBACK(filter_fir_config_file_set_cb), NULL);
1930
1931 g_builder_connect_signal(builder, "dcxo_cal", "clicked",
1932 G_CALLBACK(dcxo_cal_clicked), NULL);
1933 #ifndef _WIN32
1934 g_builder_connect_signal(builder, "dcxo_cal_to_eeprom", "clicked",
1935 G_CALLBACK(dcxo_cal_to_eeprom_clicked), NULL);
1936 g_builder_connect_signal(builder, "dcxo_cal_from_eeprom", "clicked",
1937 G_CALLBACK(dcxo_cal_from_eeprom_clicked), NULL);
1938 #endif
1939
1940 g_builder_connect_signal(builder, "rx_fastlock_store", "clicked",
1941 G_CALLBACK(fastlock_clicked), (gpointer) 1);
1942 g_builder_connect_signal(builder, "tx_fastlock_store", "clicked",
1943 G_CALLBACK(fastlock_clicked), (gpointer) 2);
1944 g_builder_connect_signal(builder, "rx_fastlock_recall", "clicked",
1945 G_CALLBACK(fastlock_clicked), (gpointer) 3);
1946 g_builder_connect_signal(builder, "tx_fastlock_recall", "clicked",
1947 G_CALLBACK(fastlock_clicked), (gpointer) 4);
1948
1949 g_signal_connect_after(section_toggle[SECTION_GLOBAL], "clicked",
1950 G_CALLBACK(hide_section_cb), section_setting[SECTION_GLOBAL]);
1951
1952 g_signal_connect_after(section_toggle[SECTION_TX], "clicked",
1953 G_CALLBACK(hide_section_cb), section_setting[SECTION_TX]);
1954
1955 g_signal_connect_after(section_toggle[SECTION_RX], "clicked",
1956 G_CALLBACK(hide_section_cb), section_setting[SECTION_RX]);
1957
1958 g_signal_connect_after(section_toggle[SECTION_FPGA], "clicked",
1959 G_CALLBACK(hide_section_cb), section_setting[SECTION_FPGA]);
1960
1961 g_signal_connect_after(ensm_mode_available, "changed",
1962 G_CALLBACK(glb_settings_update_labels), NULL);
1963
1964 g_signal_connect_after(calib_mode_available, "changed",
1965 G_CALLBACK(glb_settings_update_labels), NULL);
1966
1967 g_signal_connect_after(trx_rate_governor_available, "changed",
1968 G_CALLBACK(glb_settings_update_labels), NULL);
1969
1970 g_signal_connect_after(rx_gain_control_modes_rx1, "changed",
1971 G_CALLBACK(glb_settings_update_labels), NULL);
1972 g_signal_connect_after(rx_gain_control_modes_rx2, "changed",
1973 G_CALLBACK(glb_settings_update_labels), NULL);
1974
1975 g_signal_connect_after(fpga_rx_frequency_available, "changed",
1976 G_CALLBACK(int_dec_update_cb), sampling_freq_rx_decim);
1977
1978 g_signal_connect_after(fpga_tx_frequency_available, "changed",
1979 G_CALLBACK(int_dec_update_cb), sampling_freq_tx_inter);
1980
1981
1982 if (tx_rssi_available)
1983 g_signal_connect(rf_port_select_rx, "changed",
1984 G_CALLBACK(rf_port_select_rx_changed_cb), NULL);
1985
1986 g_signal_connect_after(enable_fir_filter_rx, "toggled",
1987 G_CALLBACK(filter_fir_enable), NULL);
1988 g_signal_connect_after(fir_filter_en_tx, "toggled",
1989 G_CALLBACK(filter_fir_enable), NULL);
1990 g_signal_connect_after(enable_fir_filter_rx_tx, "toggled",
1991 G_CALLBACK(filter_fir_enable), NULL);
1992 g_signal_connect_after(disable_all_fir_filters, "toggled",
1993 G_CALLBACK(filter_fir_enable), NULL);
1994
1995 g_signal_connect(up_down_converter, "toggled",
1996 G_CALLBACK(up_down_converter_toggled_cb), NULL);
1997
1998 make_widget_update_signal_based(glb_widgets, num_glb);
1999 make_widget_update_signal_based(rx_widgets, num_rx);
2000 make_widget_update_signal_based(tx_widgets, num_tx);
2001 make_widget_update_signal_based(fpga_widgets, num_fpga);
2002
2003 iio_spin_button_set_on_complete_function(&rx_widgets[rx_sample_freq],
2004 tx_sample_frequency_changed_cb, (void *) FALSE);
2005 iio_spin_button_set_on_complete_function(&tx_widgets[tx_sample_freq],
2006 tx_sample_frequency_changed_cb, (void *) TRUE);
2007 iio_spin_button_set_on_complete_function(&rx_widgets[rx_lo],
2008 sample_frequency_changed_cb, NULL);
2009 iio_spin_button_set_on_complete_function(&tx_widgets[tx_lo],
2010 sample_frequency_changed_cb, NULL);
2011
2012 /* Things are saved in tx_sample_frequency_changed_cb() */
2013 iio_spin_button_skip_save_on_complete(&rx_widgets[rx_sample_freq], TRUE);
2014 iio_spin_button_skip_save_on_complete(&tx_widgets[tx_sample_freq], TRUE);
2015
2016 add_ch_setup_check_fct("cf-ad9361-lpc", channel_combination_check);
2017
2018 struct iio_device *adc_dev;
2019 struct extra_dev_info *adc_info;
2020
2021 adc_dev = iio_context_find_device(get_context_from_osc(), CAP_DEVICE);
2022 if (adc_dev) {
2023 adc_info = iio_device_get_data(adc_dev);
2024 if (adc_info) /* TO DO: use osc preferences instead */
2025 adc_info->plugin_fft_corr = 20 * log10(1/sqrt(HANNING_ENBW));
2026 }
2027
2028 block_diagram_init(builder, 2, "AD9361.svg", "AD_FMCOMM2S2_RevC.jpg");
2029
2030 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(filter_fir_config), OSC_FILTER_FILE_PATH);
2031 dac_data_manager_set_buffer_chooser_current_folder(dac_tx_manager, OSC_WAVEFORM_FILE_PATH);
2032
2033 if (!is_2rx_2tx) {
2034 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "frame_rx2")));
2035 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "frame_fpga_rx2")));
2036 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "table_hw_gain_tx2")));
2037 }
2038 if (!tx_rssi_available) {
2039 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "rssi_tx1")));
2040 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "rssi_tx2")));
2041 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "label_rssi_tx1")));
2042 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "label_rssi_tx2")));
2043 }
2044 gtk_widget_set_visible(up_down_converter, has_udc_driver);
2045
2046 if (!dac_tx_manager)
2047 gtk_widget_hide(gtk_widget_get_parent(section_setting[SECTION_FPGA]));
2048
2049 g_timeout_add(1000, (GSourceFunc) update_display, ctx);
2050 can_update_widgets = true;
2051
2052 return fmcomms2_panel;
2053 }
2054
update_active_page(struct osc_plugin * plugin,gint active_page,gboolean is_detached)2055 static void update_active_page(struct osc_plugin *plugin, gint active_page, gboolean is_detached)
2056 {
2057 this_page = active_page;
2058 plugin_detached = is_detached;
2059 }
2060
fmcomms2_get_preferred_size(const struct osc_plugin * plugin,int * width,int * height)2061 static void fmcomms2_get_preferred_size(const struct osc_plugin *plugin, int *width, int *height)
2062 {
2063 if (width)
2064 *width = 1100;
2065 if (height)
2066 *height = 800;
2067 }
2068
save_widgets_to_ini(FILE * f)2069 static void save_widgets_to_ini(FILE *f)
2070 {
2071 fprintf(f, "load_fir_filter_file = %s\n"
2072 "dds_mode_tx1 = %i\n"
2073 "dds_mode_tx2 = %i\n"
2074 "tx_channel_0 = %i\n"
2075 "tx_channel_1 = %i\n"
2076 "tx_channel_2 = %i\n"
2077 "tx_channel_3 = %i\n"
2078 "dac_buf_filename = %s\n"
2079 "up_down_converter = %i\n"
2080 "global_settings_show = %i\n"
2081 "tx_show = %i\n"
2082 "rx_show = %i\n"
2083 "fpga_show = %i\n",
2084 last_fir_filter,
2085 dac_data_manager_get_dds_mode(dac_tx_manager, DDS_DEVICE, 1),
2086 dac_data_manager_get_dds_mode(dac_tx_manager, DDS_DEVICE, 2),
2087 dac_data_manager_get_tx_channel_state(dac_tx_manager, 0),
2088 dac_data_manager_get_tx_channel_state(dac_tx_manager, 1),
2089 dac_data_manager_get_tx_channel_state(dac_tx_manager, 2),
2090 dac_data_manager_get_tx_channel_state(dac_tx_manager, 3),
2091 dac_data_manager_get_buffer_chooser_filename(dac_tx_manager),
2092 !!gtk_toggle_button_get_active((GtkToggleButton *)up_down_converter),
2093 !!gtk_toggle_tool_button_get_active(section_toggle[SECTION_GLOBAL]),
2094 !!gtk_toggle_tool_button_get_active(section_toggle[SECTION_TX]),
2095 !!gtk_toggle_tool_button_get_active(section_toggle[SECTION_RX]),
2096 !!gtk_toggle_tool_button_get_active(section_toggle[SECTION_FPGA]));
2097 }
2098
save_profile(const struct osc_plugin * plugin,const char * ini_fn)2099 static void save_profile(const struct osc_plugin *plugin, const char *ini_fn)
2100 {
2101 FILE *f = fopen(ini_fn, "a");
2102 if (f) {
2103 save_to_ini(f, THIS_DRIVER, dev, fmcomms2_sr_attribs,
2104 ARRAY_SIZE(fmcomms2_sr_attribs));
2105 if (dds)
2106 save_to_ini(f, NULL, dds, fmcomms2_sr_attribs,
2107 ARRAY_SIZE(fmcomms2_sr_attribs));
2108 if (udc_rx)
2109 save_to_ini(f, NULL, udc_rx, fmcomms2_sr_attribs,
2110 ARRAY_SIZE(fmcomms2_sr_attribs));
2111 if (udc_tx)
2112 save_to_ini(f, NULL, udc_tx, fmcomms2_sr_attribs,
2113 ARRAY_SIZE(fmcomms2_sr_attribs));
2114 save_widgets_to_ini(f);
2115 fclose(f);
2116 }
2117 }
2118
context_destroy(struct osc_plugin * plugin,const char * ini_fn)2119 static void context_destroy(struct osc_plugin *plugin, const char *ini_fn)
2120 {
2121 g_source_remove_by_user_data(ctx);
2122
2123 if (ini_fn)
2124 save_profile(NULL, ini_fn);
2125
2126 if (dac_tx_manager) {
2127 dac_data_manager_free(dac_tx_manager);
2128 dac_tx_manager = NULL;
2129 }
2130
2131 osc_destroy_context(ctx);
2132 }
2133
2134 struct osc_plugin plugin;
2135
fmcomms2_identify(const struct osc_plugin * plugin)2136 static bool fmcomms2_identify(const struct osc_plugin *plugin)
2137 {
2138 /* Use the OSC's IIO context just to detect the devices */
2139 struct iio_context *osc_ctx = get_context_from_osc();
2140
2141 if (!iio_context_find_device(osc_ctx, PHY_DEVICE))
2142 return false;
2143
2144 /* Check if FMComms5 is used */
2145 return !iio_context_find_device(osc_ctx, "ad9361-phy-B");
2146 }
2147
get_dac_dev_names(const struct osc_plugin * plugin)2148 GSList* get_dac_dev_names(const struct osc_plugin *plugin) {
2149 GSList *list = NULL;
2150
2151 list = g_slist_append (list, (gpointer) DDS_DEVICE);
2152
2153 return list;
2154 }
2155
2156 struct osc_plugin plugin = {
2157 .name = THIS_DRIVER,
2158 .identify = fmcomms2_identify,
2159 .init = fmcomms2_init,
2160 .handle_item = fmcomms2_handle,
2161 .handle_external_request = handle_external_request,
2162 .update_active_page = update_active_page,
2163 .get_preferred_size = fmcomms2_get_preferred_size,
2164 .save_profile = save_profile,
2165 .load_profile = load_profile,
2166 .destroy = context_destroy,
2167 .get_dac_dev_names = get_dac_dev_names,
2168 };
2169