1 /**
2 * Copyright (C) 2012-2013 Analog Devices, Inc.
3 *
4 * Licensed under the GPL-2.
5 *
6 **/
7 #include <stdio.h>
8 #include <gtk/gtk.h>
9 #include <gtkdatabox.h>
10 #include <gtkdatabox_grid.h>
11 #include <gtkdatabox_points.h>
12 #include <gtkdatabox_lines.h>
13 #include <gtkdatabox_markers.h>
14 #include <errno.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 #include <math.h>
18 #include <matio.h>
19 #include <sys/time.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <dirent.h>
23
24 #include <complex.h>
25 #include <fftw3.h>
26 #include <iio.h>
27
28 #include "osc.h"
29 #include "oscplot.h"
30 #include "config.h"
31 #include "iio_widget.h"
32 #include "datatypes.h"
33 #include "osc_plugin.h"
34 #include "math_expression_generator.h"
35 #include "iio_utils.h"
36
37 /* add backwards compat for <matio-1.5.0 */
38 #if MATIO_MAJOR_VERSION == 1 && MATIO_MINOR_VERSION < 5
39 typedef int mat_dim;
40 #else
41 typedef size_t mat_dim;
42 #endif
43
44 /* timersub, macros are _BSD_SOURCE, and aren't included in windows */
45 #ifndef timersub
46 #define timersub(a, b, result) \
47 do { \
48 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
49 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
50 if ((result)->tv_usec < 0) { \
51 --(result)->tv_sec; \
52 (result)->tv_usec += 1000000; \
53 } \
54 } while (0)
55 #endif /* timersub */
56
57 extern void *find_setup_check_fct_by_devname(const char *dev_name);
58
59 static int (*plugin_setup_validation_fct)(struct iio_device *, const char **) = NULL;
60 static unsigned object_count = 0;
61
62 static void create_plot (OscPlot *plot);
63 static void plot_setup(OscPlot *plot);
64 static void capture_button_clicked_cb (GtkToggleToolButton *btn, gpointer data);
65 static void single_shot_clicked_cb (GtkToggleToolButton *btn, gpointer data);
66 static void update_grid(OscPlot *plot, gfloat min, gfloat max);
67 static void add_grid(OscPlot *plot);
68 static void rescale_databox(OscPlotPrivate *priv, GtkDatabox *box, gfloat border);
69 static bool call_all_transform_functions(OscPlotPrivate *priv);
70 static void capture_start(OscPlotPrivate *priv);
71 static void plot_profile_save(OscPlot *plot, char *filename);
72 static void transform_add_plot_markers(OscPlot *plot, Transform *transform);
73 static void osc_plot_finalize(GObject *object);
74 static void osc_plot_dispose(GObject *object);
75 static void save_as(OscPlot *plot, const char *filename, int type);
76 static void treeview_expand_update(OscPlot *plot);
77 static void treeview_icon_color_update(OscPlot *plot);
78 static int enabled_channels_of_device(GtkTreeView *treeview, const char *name, unsigned *enabled_mask);
79 static int enabled_channels_count(OscPlot *plot);
80 static int num_of_channels_of_device(GtkTreeView *treeview, const char *name);
81 static gboolean get_iter_by_name(GtkTreeView *tree, GtkTreeIter *iter, const char *dev_name, const char *ch_name);
82 static void set_marker_labels (OscPlot *plot, gchar *buf, enum marker_types type);
83 static void channel_color_icon_set_color(GdkPixbuf *pb, GdkColor *color);
84 static int comboboxtext_set_active_by_string(GtkComboBox *combo_box, const char *name);
85 static int comboboxtext_get_active_text_as_int(GtkComboBoxText* combobox);
86 static gboolean check_valid_setup(OscPlot *plot);
87 static int device_find_by_name(struct iio_context *ctx, const char *name);
88 static int channel_find_by_name(struct iio_context *ctx, int device_index, const char *name);
89 static void device_rx_info_update(OscPlotPrivate *priv);
90 static gdouble prefix2scale (char adc_scale);
91 static struct iio_device * transform_get_device_parent(Transform *transform);
92 static gboolean tree_get_selected_row_iter(GtkTreeView *treeview, GtkTreeIter *iter);
93 static void set_channel_shadow_of_enabled(gpointer data, gpointer user_data);
94 static gfloat * plot_channels_get_nth_data_ref(GSList *list, guint n);
95 static void transform_add_own_markers(OscPlot *plot, Transform *transform);
96 static void transform_remove_own_markers(Transform *transform);
97 static bool set_channel_state_in_tree_model(GtkTreeModel *model, GtkTreeIter* chn_iter, gboolean state);
98
99 /* IDs of signals */
100 enum {
101 CAPTURE_EVENT_SIGNAL,
102 DESTROY_EVENT_SIGNAL,
103 NEWPLOT_EVENT_SIGNAL,
104 LAST_SIGNAL
105 };
106
107 /* signals will be configured during class init */
108 static guint oscplot_signals[LAST_SIGNAL] = { 0 };
109
110 /* Columns of the device treestore */
111 enum {
112 ELEMENT_NAME,
113 IS_DEVICE,
114 IS_CHANNEL,
115 CHANNEL_TYPE,
116 DEVICE_SELECTABLE,
117 DEVICE_ACTIVE,
118 CHANNEL_ACTIVE,
119 ELEMENT_REFERENCE,
120 EXPANDED,
121 CHANNEL_SETTINGS,
122 CHANNEL_COLOR_ICON,
123 SENSITIVE,
124 PLOT_TYPE,
125 NUM_COL
126 };
127
128 /* Horizontal Scale Types */
129 enum {
130 HOR_SCALE_SAMPLES,
131 HOR_SCALE_TIME,
132 HOR_SCALE_NUM_OPTIONS
133 };
134
135 /* Types of channels that can be displayed on a plot */
136 enum {
137 PLOT_IIO_CHANNEL = 0,
138 PLOT_MATH_CHANNEL,
139 NUM_PLOT_CHANNELS_TYPES
140 };
141
142 #define MATH_CHANNELS_DEVICE "Math"
143
144 #define OSC_COLOR(r, g, b) { \
145 .red = (r) << 8, \
146 .green = (g) << 8, \
147 .blue = (b) << 8, \
148 }
149
150 static GdkColor color_graph[] = {
151 OSC_COLOR(138, 226, 52),
152 OSC_COLOR(239, 41, 41),
153 OSC_COLOR(114, 159, 207),
154 OSC_COLOR(252, 175, 62),
155 OSC_COLOR(211, 215, 208),
156 OSC_COLOR(252, 233, 79),
157 OSC_COLOR(173, 127, 168),
158 OSC_COLOR(233, 185, 110),
159
160 OSC_COLOR(115, 210, 22),
161 OSC_COLOR(204, 0, 0),
162 OSC_COLOR(52, 101, 164),
163 OSC_COLOR(245, 121, 0),
164 OSC_COLOR(186, 189, 182),
165 OSC_COLOR(237, 212, 0),
166 OSC_COLOR(117, 80, 123),
167 OSC_COLOR(193, 125, 17),
168 };
169
170 #define NUM_GRAPH_COLORS (sizeof(color_graph) / sizeof(color_graph[0]))
171
172 static GdkColor color_grid = {
173 .red = 51000,
174 .green = 51000,
175 .blue = 0,
176 };
177
178 static GdkColor color_background = {
179 .red = 0,
180 .green = 0,
181 .blue = 0,
182 };
183
184 static GdkColor color_marker = {
185 .red = 0xFFFF,
186 .green = 0,
187 .blue = 0xFFFF,
188 };
189
190 typedef struct channel_settings PlotChn;
191 typedef struct iio_channel_settings PlotIioChn;
192 typedef struct math_channel_settings PlotMathChn;
193
194 struct channel_settings {
195 unsigned type;
196 char *name;
197 char *parent_name;
198 struct iio_context *ctx;
199 GdkColor graph_color;
200
201 struct iio_device * (*get_iio_parent)(PlotChn *);
202 gfloat * (*get_data_ref)(PlotChn *);
203 void (*assert_used_iio_channels)(PlotChn *, bool);
204 void (*destroy)(PlotChn *);
205 };
206
207 struct iio_channel_settings {
208 PlotChn base;
209 struct iio_channel *iio_chn;
210 bool apply_inverse_funct;
211 bool apply_multiply_funct;
212 bool apply_add_funct;
213 double multiply_value;
214 double add_value;
215 };
216
217 struct math_channel_settings {
218 PlotChn base;
219 GSList *iio_channels;
220 gfloat ***iio_channels_data;
221 int num_channels;
222 char *iio_device_name;
223 char *txt_math_expression;
224 void (*math_expression)(float ***channels_data, float *out_data, unsigned long long chn_sample_cnt);
225 void *math_lib_handler;
226 float *data_ref;
227 };
228
229 /* Helpers */
230 #define TIME_SETTINGS(obj) ((struct _time_settings *)obj->settings)
231 #define FFT_SETTINGS(obj) ((struct _fft_settings *)obj->settings)
232 #define CONSTELLATION_SETTINGS(obj) ((struct _constellation_settings *)obj->settings)
233 #define XCORR_SETTINGS(obj) ((struct _cross_correlation_settings *)obj->settings)
234 #define FREQ_SPECTRUM_SETTINGS(obj) ((struct _freq_spectrum_settings *)obj->settings)
235 #define MATH_SETTINGS(obj) ((struct _math_settings *)obj->settings)
236
237 #define PLOT_CHN(obj) ((PlotChn *)obj)
238 #define PLOT_IIO_CHN(obj) ((PlotIioChn *)obj)
239 #define PLOT_MATH_CHN(obj) ((PlotMathChn *)obj)
240
241 struct int_and_plot {
242 int int_obj;
243 OscPlot *plot;
244 };
245
246 struct string_and_plot {
247 char *string_obj;
248 OscPlot *plot;
249 };
250
251 struct plot_geometry {
252 gint width;
253 gint height;
254 };
255
256 struct _OscPlotPrivate
257 {
258 GtkBuilder *builder;
259
260 int object_id;
261
262 /* Graphical User Interface */
263 GtkWidget *window;
264 GtkWidget *databox;
265 GtkWidget *capture_graph;
266 GtkWidget *capture_button;
267 GtkWidget *ss_button;
268 GtkWidget *channel_list_view;
269 GtkWidget *show_grid;
270 GtkWidget *plot_type;
271 GtkWidget *plot_domain;
272 GtkWidget *enable_auto_scale;
273 GtkWidget *hor_scale;
274 GtkWidget *hor_units;
275 GtkWidget *marker_label;
276 GtkWidget *devices_label;
277 GtkWidget *phase_label;
278 GtkWidget *saveas_button;
279 GtkWidget *saveas_dialog;
280 GtkWidget *saveas_type_dialog;
281 GtkWidget *title_edit_dialog;
282 GtkWidget *fullscreen_button;
283 GtkWidget *menu_fullscreen;
284 GtkWidget *menu_show_options;
285 GtkWidget *y_axis_max;
286 GtkWidget *y_axis_min;
287 GtkWidget *viewport_saveas_channels;
288 GtkWidget *saveas_channels_list;
289 GtkWidget *saveas_select_channel_message;
290 GtkWidget *device_combobox;
291 GtkWidget *sample_count_widget;
292 unsigned int sample_count;
293 GtkWidget *fft_size_widget;
294 GtkWidget *fft_win_widget;
295 GtkWidget *fft_avg_widget;
296 GtkWidget *fft_pwr_offset_widget;
297 GtkWidget *device_settings_menu;
298 GtkWidget *math_settings_menu;
299 GtkWidget *device_trigger_menuitem;
300 GtkWidget *math_menuitem;
301 GtkWidget *plot_trigger_menuitem;
302 GtkWidget *channel_settings_menu;
303 GtkWidget *math_channel_settings_menu;
304 GtkWidget *channel_expression_edit_menuitem;
305 GtkWidget *channel_iio_color_menuitem;
306 GtkWidget *channel_math_color_menuitem;
307 GtkWidget *channel_math_menuitem;
308 GtkWidget *channel_remove_menuitem;
309 GtkWidget *math_dialog;
310 GtkWidget *capture_options_box;
311 GtkWidget *saveas_settings_box;
312 GtkWidget *save_mat_scale;
313 GtkWidget *new_plot_button;
314 GtkWidget *cmb_saveas_type;
315 GtkWidget *math_expression_dialog;
316 GtkWidget *math_expression_textview;
317 GtkWidget *math_device_select;
318 GtkWidget *math_channel_name_entry;
319 GtkWidget *math_expr_error;
320
321 GtkTextBuffer* tbuf;
322 GtkTextBuffer* devices_buf;
323 GtkTextBuffer* phase_buf;
324 GtkTextBuffer* math_expression;
325
326 OscPlotPreferences *preferences;
327
328 struct iio_context *ctx;
329
330 unsigned int nb_input_devices;
331 unsigned int nb_plot_channels;
332
333 struct plot_geometry size;
334
335 int frame_counter;
336 double fps;
337 struct timeval last_update;
338
339 int last_hor_unit;
340
341 int do_a_rescale_flag;
342
343 gulong capture_button_hid;
344 gint deactivate_capture_btn_flag;
345
346 bool single_shot_mode;
347
348 /* A reference to the device holding the most recent created transform */
349 struct iio_device *current_device;
350
351 /* List of transforms for this plot */
352 TrList *transform_list;
353
354 /* Active transform type for this window */
355 int active_transform_type;
356
357 /* Transform currently holding the fft marker */
358 Transform *tr_with_marker;
359
360 /* Type of "Save As" currently selected*/
361 gint active_saveas_type;
362
363 /* The set of markers */
364 struct marker_type markers[MAX_MARKERS + 2];
365 struct marker_type *markers_copy;
366 enum marker_types marker_type;
367
368 /* Settings list of all channel */
369 GSList *ch_settings_list;
370
371 /* Databox data */
372 GtkDataboxGraph *grid;
373 gfloat gridy[25], gridx[25];
374
375 /* Spectrum mode - Parameters */
376 unsigned fft_count;
377 double start_freq;
378 double filter_bw;
379
380 gint line_thickness;
381
382 gint redraw_function;
383 gboolean stop_redraw;
384 gboolean redraw;
385
386 bool spectrum_data_ready;
387
388 gboolean fullscreen_state;
389
390 bool profile_loaded_scale;
391
392 bool save_as_png;
393
394 char *saveas_filename;
395
396 struct int_and_plot fix_marker;
397 struct string_and_plot add_mrk;
398 struct string_and_plot remove_mrk;
399 struct string_and_plot peak_mrk;
400 struct string_and_plot fix_mrk;
401 struct string_and_plot single_mrk;
402 struct string_and_plot dual_mrk;
403 struct string_and_plot image_mrk;
404 struct string_and_plot off_mrk;
405
406 gulong fixed_marker_hid;
407
408 gint plot_x_pos;
409 gint plot_y_pos;
410
411 gfloat plot_left;
412 gfloat plot_right;
413 gfloat plot_top;
414 gfloat plot_bottom;
415 int read_scale_params;
416
417 GMutex g_marker_copy_lock;
418
419 void (*quit_callback)(void *user_data);
420 void *qcb_user_data;
421 };
422
G_DEFINE_TYPE_WITH_PRIVATE(OscPlot,osc_plot,GTK_TYPE_WIDGET)423 G_DEFINE_TYPE_WITH_PRIVATE(OscPlot, osc_plot, GTK_TYPE_WIDGET)
424
425 static void osc_plot_class_init(OscPlotClass *klass)
426 {
427 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
428
429 gobject_class->dispose = osc_plot_dispose;
430 gobject_class->finalize = osc_plot_finalize;
431
432 oscplot_signals[CAPTURE_EVENT_SIGNAL] = g_signal_new("osc-capture-event",
433 G_TYPE_FROM_CLASS (klass),
434 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
435 G_STRUCT_OFFSET (OscPlotClass, capture_event),
436 NULL,
437 NULL,
438 g_cclosure_marshal_VOID__BOOLEAN,
439 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
440
441 oscplot_signals[DESTROY_EVENT_SIGNAL] = g_signal_new("osc-destroy-event",
442 G_TYPE_FROM_CLASS (klass),
443 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
444 G_STRUCT_OFFSET (OscPlotClass, destroy_event),
445 NULL,
446 NULL,
447 g_cclosure_marshal_VOID__VOID,
448 G_TYPE_NONE, 0);
449
450 oscplot_signals[NEWPLOT_EVENT_SIGNAL] = g_signal_new("osc-newplot-event",
451 G_TYPE_FROM_CLASS (klass),
452 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
453 G_STRUCT_OFFSET (OscPlotClass, newplot_event),
454 NULL,
455 NULL,
456 g_cclosure_marshal_VOID__POINTER,
457 G_TYPE_NONE, 1, G_TYPE_POINTER);
458 }
459
osc_plot_init(OscPlot * plot)460 static void osc_plot_init(OscPlot *plot)
461 {
462 plot->priv = osc_plot_get_instance_private (plot);
463 }
464
osc_plot_new(struct iio_context * ctx)465 GtkWidget *osc_plot_new(struct iio_context *ctx)
466 {
467 GtkWidget *plot;
468
469 plot = GTK_WIDGET(g_object_new(OSC_PLOT_TYPE, NULL));
470 OSC_PLOT(plot)->priv->ctx = ctx;
471 create_plot(OSC_PLOT(plot));
472
473 return plot;
474 }
475
osc_plot_new_with_pref(struct iio_context * ctx,OscPlotPreferences * pref)476 GtkWidget *osc_plot_new_with_pref(struct iio_context *ctx, OscPlotPreferences *pref)
477 {
478 GtkWidget *plot;
479
480 plot = GTK_WIDGET(g_object_new(OSC_PLOT_TYPE, NULL));
481 OSC_PLOT(plot)->priv->ctx = ctx;
482 OSC_PLOT(plot)->priv->preferences = pref;
483 create_plot(OSC_PLOT(plot));
484
485 return plot;
486 }
487
osc_plot_destroy(OscPlot * plot)488 void osc_plot_destroy (OscPlot *plot)
489 {
490 gtk_widget_destroy(plot->priv->window);
491 gtk_widget_destroy(GTK_WIDGET(plot));
492 }
493
osc_plot_reset_numbering(void)494 void osc_plot_reset_numbering (void)
495 {
496 object_count = 0;
497 }
498
osc_plot_set_visible(OscPlot * plot,bool visible)499 void osc_plot_set_visible (OscPlot *plot, bool visible)
500 {
501 gtk_widget_set_visible(plot->priv->window, visible);
502 }
503
osc_plot_get_buffer(OscPlot * plot)504 struct iio_buffer * osc_plot_get_buffer(OscPlot *plot)
505 {
506 struct extra_dev_info *dev_info;
507
508 dev_info = iio_device_get_data(plot->priv->current_device);
509 return dev_info->buffer;
510 }
511
osc_plot_data_update(OscPlot * plot)512 void osc_plot_data_update (OscPlot *plot)
513 {
514 if (call_all_transform_functions(plot->priv))
515 plot->priv->redraw = TRUE;
516
517 if (plot->priv->single_shot_mode) {
518 plot->priv->single_shot_mode = false;
519 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(plot->priv->capture_button), false);
520 }
521 }
522
is_frequency_transform(OscPlotPrivate * priv)523 static bool is_frequency_transform(OscPlotPrivate *priv)
524 {
525 return priv->active_transform_type == FFT_TRANSFORM ||
526 priv->active_transform_type == COMPLEX_FFT_TRANSFORM ||
527 priv->active_transform_type == FREQ_SPECTRUM_TRANSFORM;
528 }
529
osc_plot_update_rx_lbl(OscPlot * plot,bool initial_update)530 void osc_plot_update_rx_lbl(OscPlot *plot, bool initial_update)
531 {
532 OscPlotPrivate *priv = plot->priv;
533 TrList *tr_list = priv->transform_list;
534 struct extra_dev_info *dev_info = NULL;
535 char buf[20];
536 double corr;
537 int i;
538
539 device_rx_info_update(priv);
540
541 /* Skip rescaling graphs, updating labels and others if the redrawing is currently halted. */
542 if (priv->redraw_function <= 0 && !initial_update)
543 return;
544
545 if (is_frequency_transform(priv)) {
546 gfloat top, bottom, left, right;
547 gfloat padding;
548
549 /* In FFT mode we need to scale the x-axis according to the selected sampling frequency */
550 for (i = 0; i < tr_list->size; i++) {
551 if(!initial_update)
552 Transform_setup(tr_list->transforms[i]);
553 gtk_databox_graph_set_hide(tr_list->transforms[i]->graph, TRUE);
554 }
555
556 dev_info = iio_device_get_data(transform_get_device_parent(tr_list->transforms[i - 1]));
557 sprintf(buf, "%cHz", dev_info->adc_scale);
558 gtk_label_set_text(GTK_LABEL(priv->hor_scale), buf);
559
560 if (priv->active_transform_type == COMPLEX_FFT_TRANSFORM)
561 corr = dev_info->adc_freq / 2.0;
562 else
563 corr = 0;
564 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale)) && !initial_update)
565 return;
566 if (priv->profile_loaded_scale)
567 return;
568
569 update_grid(plot, -corr, dev_info->adc_freq / 2.0);
570 padding = (dev_info->adc_freq / 2.0 + corr) * 0.05;
571 gtk_databox_get_total_limits(GTK_DATABOX(priv->databox), &left, &right,
572 &top, &bottom);
573 gtk_databox_set_total_limits(GTK_DATABOX(priv->databox),
574 -corr - padding, dev_info->adc_freq / 2.0 + padding,
575 top, bottom);
576 } else {
577 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units))) {
578 case 0:
579 gtk_label_set_text(GTK_LABEL(priv->hor_scale), "Samples");
580 break;
581 case 1:
582 gtk_label_set_text(GTK_LABEL(priv->hor_scale), "µs");
583 break;
584 }
585 }
586 }
587
osc_plot_restart(OscPlot * plot)588 void osc_plot_restart (OscPlot *plot)
589 {
590 OscPlotPrivate *priv = plot->priv;
591
592 if (priv->redraw_function > 0)
593 {
594 priv->stop_redraw = TRUE;
595 plot_setup(plot);
596 add_grid(plot);
597 gtk_widget_queue_draw(priv->databox);
598 priv->frame_counter = 0;
599 priv->fps = 0.0;
600 gettimeofday(&(priv->last_update), NULL);
601 capture_start(priv);
602 }
603 }
604
osc_plot_running_state(OscPlot * plot)605 bool osc_plot_running_state (OscPlot *plot)
606 {
607 return !!plot->priv->redraw_function;
608 }
609
osc_plot_draw_start(OscPlot * plot)610 void osc_plot_draw_start (OscPlot *plot)
611 {
612 OscPlotPrivate *priv = plot->priv;
613
614 gtk_toggle_tool_button_set_active((GtkToggleToolButton *)priv->capture_button, TRUE);
615 }
616
osc_plot_draw_stop(OscPlot * plot)617 void osc_plot_draw_stop (OscPlot *plot)
618 {
619 OscPlotPrivate *priv = plot->priv;
620
621 gtk_toggle_tool_button_set_active((GtkToggleToolButton *)priv->capture_button, FALSE);
622 }
623
osc_plot_save_to_ini(OscPlot * plot,char * filename)624 void osc_plot_save_to_ini (OscPlot *plot, char *filename)
625 {
626 plot_profile_save(plot, filename);
627 }
628
osc_plot_save_as(OscPlot * plot,char * filename,int type)629 void osc_plot_save_as (OscPlot *plot, char *filename, int type)
630 {
631 save_as(plot, filename, type);
632 }
633
osc_plot_get_active_device(OscPlot * plot)634 const char * osc_plot_get_active_device (OscPlot *plot)
635 {
636 OscPlotPrivate *priv = plot->priv;
637 GtkTreeModel *model;
638 GtkTreeIter iter;
639 gboolean next_iter;
640 gboolean active;
641 struct iio_device *dev;
642
643 model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->channel_list_view));
644 next_iter = gtk_tree_model_get_iter_first(model, &iter);
645 while (next_iter) {
646 gtk_tree_model_get(model, &iter, ELEMENT_REFERENCE, &dev, DEVICE_ACTIVE, &active, -1);
647 if (active)
648 return iio_device_get_name(dev) ?:
649 iio_device_get_id(dev);
650 next_iter = gtk_tree_model_iter_next(model, &iter);
651 }
652
653 return NULL;
654 }
655
osc_plot_get_fft_avg(OscPlot * plot)656 int osc_plot_get_fft_avg (OscPlot *plot)
657 {
658 return gtk_spin_button_get_value(GTK_SPIN_BUTTON(plot->priv->fft_avg_widget));
659 }
660
osc_plot_get_marker_type(OscPlot * plot)661 int osc_plot_get_marker_type (OscPlot *plot)
662 {
663 return plot->priv->marker_type;
664 }
665
osc_plot_set_marker_type(OscPlot * plot,int mtype)666 void osc_plot_set_marker_type (OscPlot *plot, int mtype)
667 {
668 plot->priv->marker_type = mtype;
669 set_marker_labels(plot, NULL, mtype);
670 }
671
osc_plot_get_markers_copy(OscPlot * plot)672 void * osc_plot_get_markers_copy(OscPlot *plot)
673 {
674 return plot->priv->markers_copy;
675 }
676
osc_plot_set_markers_copy(OscPlot * plot,void * value)677 void osc_plot_set_markers_copy (OscPlot *plot, void *value)
678 {
679 plot->priv->markers_copy = value;
680 }
681
osc_plot_set_domain(OscPlot * plot,int domain)682 void osc_plot_set_domain (OscPlot *plot, int domain)
683 {
684 OscPlotPrivate *priv = plot->priv;
685
686 if (gtk_toggle_tool_button_get_active((GtkToggleToolButton *)priv->capture_button))
687 return;
688
689 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), domain);
690 }
691
osc_plot_get_plot_domain(OscPlot * plot)692 int osc_plot_get_plot_domain (OscPlot *plot)
693 {
694 return gtk_combo_box_get_active(GTK_COMBO_BOX(plot->priv->plot_domain));
695 }
696
osc_plot_get_marker_lock(OscPlot * plot)697 GMutex * osc_plot_get_marker_lock (OscPlot *plot)
698 {
699 return &plot->priv->g_marker_copy_lock;
700 }
701
osc_plot_set_sample_count(OscPlot * plot,gdouble count)702 bool osc_plot_set_sample_count (OscPlot *plot, gdouble count)
703 {
704 OscPlotPrivate *priv = plot->priv;
705 int ret;
706
707 if (gtk_toggle_tool_button_get_active((GtkToggleToolButton *)priv->capture_button))
708 return false;
709
710 if (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain)) == FFT_PLOT ||
711 gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain)) == SPECTRUM_PLOT) {
712 char s_count[32];
713 snprintf(s_count, sizeof(s_count), "%d", (int)count);
714 ret = comboboxtext_set_active_by_string(GTK_COMBO_BOX(priv->fft_size_widget), s_count);
715 priv->sample_count = (int)count;
716 } else {
717 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units))) {
718 case 0:
719 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), count);
720 priv->sample_count = (int)count;
721 break;
722 case 1:
723 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), count);
724 break;
725 }
726 ret = 1;
727 }
728
729 return (ret) ? true : false;
730 }
731
osc_plot_get_sample_count(OscPlot * plot)732 double osc_plot_get_sample_count (OscPlot *plot) {
733
734 OscPlotPrivate *priv = plot->priv;
735 int count;
736
737 if (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain)) == FFT_PLOT ||
738 gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain)) == SPECTRUM_PLOT)
739 count = comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));
740 else
741 count = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget));
742
743 return count;
744 }
745
osc_plot_set_channel_state(OscPlot * plot,const char * dev,unsigned int channel,bool state)746 void osc_plot_set_channel_state(OscPlot *plot, const char *dev, unsigned int channel, bool state)
747 {
748 OscPlotPrivate *priv;
749 struct iio_context *ctx;
750 struct iio_device *iio_dev;
751 struct iio_channel *iio_ch;
752
753 if (!plot || !dev)
754 return;
755
756 priv = plot->priv;
757 if (!priv)
758 return;
759
760 ctx = priv->ctx;
761 if (!ctx)
762 return;
763
764 if (gtk_toggle_tool_button_get_active((GtkToggleToolButton *)priv->capture_button))
765 return;
766
767 iio_dev = iio_context_find_device(ctx, dev);
768 if (!iio_dev || !is_input_device(iio_dev))
769 return;
770
771 if (channel >= iio_device_get_channels_count(iio_dev))
772 return;
773
774 iio_ch = iio_device_get_channel(iio_dev, channel);
775
776 if (!iio_ch)
777 return;
778
779 GtkTreeView *tree = GTK_TREE_VIEW(priv->channel_list_view);
780 GtkTreeModel *model = gtk_tree_view_get_model(tree);
781 GtkTreeIter ch_iter;
782 const char *ch;
783
784 ch = iio_channel_get_id(iio_ch);
785 get_iter_by_name(tree, &ch_iter, dev, ch);
786
787 set_channel_state_in_tree_model(model, &ch_iter, state);
788 check_valid_setup(plot);
789 }
790
osc_plot_xcorr_revert(OscPlot * plot,int revert)791 void osc_plot_xcorr_revert (OscPlot *plot, int revert)
792 {
793 TrList *tr_list = plot->priv->transform_list;
794 Transform *transform;
795 int i;
796
797 for (i = 0; i < tr_list->size; i++) {
798 transform = tr_list->transforms[i];
799 XCORR_SETTINGS(transform)->revert_xcorr = revert;
800 }
801 }
802
osc_plot_set_quit_callback(OscPlot * plot,void (* qcallback)(void * user_data),void * user_data)803 void osc_plot_set_quit_callback(OscPlot *plot,
804 void (*qcallback)(void *user_data), void *user_data)
805 {
806 g_return_if_fail(plot);
807 g_return_if_fail(qcallback);
808
809 plot->priv->quit_callback = qcallback;
810 plot->priv->qcb_user_data = user_data;
811 }
812
osc_plot_get_id(OscPlot * plot)813 int osc_plot_get_id(OscPlot *plot)
814 {
815 return plot->priv->object_id;
816 }
817
osc_plot_set_id(OscPlot * plot,int id)818 void osc_plot_set_id(OscPlot *plot, int id)
819 {
820 plot->priv->object_id = id;
821 }
822
osc_plot_spect_mode(OscPlot * plot,bool enable)823 void osc_plot_spect_mode(OscPlot *plot, bool enable)
824 {
825 OscPlotPrivate *priv = plot->priv;
826 GtkComboBox *cbox = GTK_COMBO_BOX(priv->plot_domain);
827
828 g_return_if_fail(plot);
829
830 if (enable) {
831 if (!comboboxtext_set_active_by_string(cbox, "Spectrum Mode"))
832 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cbox),
833 "Spectrum Mode");
834 gtk_widget_hide(priv->capture_button);
835 gtk_widget_hide(priv->ss_button);
836 } else {
837 GtkTreeIter iter;
838 GtkTreeModel *model;
839
840 model = gtk_combo_box_get_model(cbox);
841 if (gtk_tree_model_get_iter_from_string(model, &iter,
842 "Spectrum Mode")) {
843 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
844 }
845 gtk_widget_show(priv->capture_button);
846 gtk_widget_show(priv->ss_button);
847 }
848 }
849
osc_plot_spect_set_start_f(OscPlot * plot,double freq_mhz)850 void osc_plot_spect_set_start_f(OscPlot *plot, double freq_mhz)
851 {
852 g_return_if_fail(plot);
853
854 plot->priv->start_freq = freq_mhz;
855 }
856
osc_plot_spect_set_len(OscPlot * plot,unsigned fft_count)857 void osc_plot_spect_set_len(OscPlot *plot, unsigned fft_count)
858 {
859 g_return_if_fail(plot);
860
861 plot->priv->fft_count = fft_count;
862 }
863
osc_plot_spect_set_filter_bw(OscPlot * plot,double bw)864 void osc_plot_spect_set_filter_bw(OscPlot *plot, double bw)
865 {
866 g_return_if_fail(plot);
867
868 plot->priv->filter_bw = bw;
869 }
870
osc_plot_dispose(GObject * object)871 static void osc_plot_dispose(GObject *object)
872 {
873 G_OBJECT_CLASS(osc_plot_parent_class)->dispose(object);
874 }
875
osc_plot_finalize(GObject * object)876 static void osc_plot_finalize(GObject *object)
877 {
878 G_OBJECT_CLASS(osc_plot_parent_class)->finalize(object);
879 }
880
881 /* Ref:
882 * A Family of Cosine-Sum Windows for High-Resolution Measurements
883 * Hans-Helge Albrecht
884 * Physikalisch-Technische Bendesanstalt
885 * Acoustics, Speech, and Signal Processing, 2001. Proceedings. (ICASSP '01).
886 * 2001 IEEE International Conference on (Volume:5 )
887 * pgs. 3081-3084
888 *
889 * While this doesn't use any of his code - I did find the coeffients that were nicely
890 * typed in by Joe Henning as part of his MATLAB Window Utilities
891 * (https://www.mathworks.com/matlabcentral/fileexchange/46092-window-utilities)
892 *
893 */
window_function(gchar * win,int j,int n)894 static double window_function(gchar *win, int j, int n)
895 {
896 /* Strings need to match what is in glade */
897 if (!g_strcmp0(win, "Hanning")) {
898 double a = 2.0 * M_PI / (n - 1);
899 return 0.5 * (1.0 - cos(a * j));
900 } else if (!g_strcmp0(win, "Boxcar")) {
901 return 1.0;
902 } else if (!g_strcmp0(win, "Triangular")) {
903 double a = fabs(j - (n - 1)/ 2.0) / ((n - 1.0) / 2.0);
904 return 1.0 - a;
905 } else if (!g_strcmp0(win, "Welch")) {
906 double a = (j - (n - 1.0) / 2.0) / ((n - 1.0) / 2.0);
907 return 1.0 - (a * a);
908 } else if (!g_strcmp0(win, "Cosine")) {
909 double a = M_PI * j / (n - 1);
910 return sin(a);
911 } else if (!g_strcmp0(win, "Hamming")) {
912 double a0 = 0.5383553946707251, a1 = .4616446053292749;
913 return a0 - a1 * cos(j * 2.0 * M_PI / (n - 1));
914 } else if (!g_strcmp0(win, "Exact Blackman")) {
915 /* https://ieeexplore.ieee.org/document/940309 */
916 double a0 = 7938.0/18608.0, a1 = 9240.0/18608.0, a2 = 1430.0/18608.0;
917 double a = j * 2.0 * M_PI / (n - 1);
918 return a0 - a1 * cos(a) + a2 * cos(2.0 * a);
919 } else if (!g_strcmp0(win, "3 Term Cosine")) {
920 double a0 = 4.243800934609435e-1, a1 = 4.973406350967378e-1, a2 = 7.827927144231873e-2;
921 double a = j * 2.0 * M_PI / (n - 1);
922 return a0 - a1 * cos(a) + a2 * cos(2.0 * a);
923 } else if (!g_strcmp0(win, "4 Term Cosine")) {
924 double a0 = 3.635819267707608e-1, a1 = 4.891774371450171e-1, a2 = 1.365995139786921e-1,
925 a3 = 1.064112210553003e-2;
926 double a = j * 2.0 * M_PI / (n - 1);
927 return a0 - a1 * cos(a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a);
928 } else if (!g_strcmp0(win, "5 Term Cosine")) {
929 double a0 = 3.232153788877343e-1, a1 = 4.714921439576260e-1, a2 = 1.755341299601972e-1,
930 a3 = 2.849699010614994e-2, a4 = 1.261357088292677e-3;
931 double a = j * 2.0 * M_PI / (n - 1);
932 return a0 - a1 * cos(a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a) + a4 * cos(4.0 * a);
933 } else if (!g_strcmp0(win, "6 Term Cosine")) {
934 double a0 = 2.935578950102797e-1, a1 = 4.519357723474506e-1, a2 = 2.014164714263962e-1,
935 a3 = 4.792610922105837e-2, a4 = 5.026196426859393e-3, a5 = 1.375555679558877e-4;
936 double a = j * 2.0 * M_PI / (n - 1);
937 return a0 - a1 * cos(1.0 * a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a) + a4 * cos(4.0 * a) -
938 a5 * cos(5.0 * a);
939 } else if (!g_strcmp0(win, "7 Term Cosine")) {
940 double a0 = 2.712203605850388e-1, a1 = 4.334446123274422e-1, a2 = 2.180041228929303e-1,
941 a3 = 6.578534329560609e-2, a4 = 1.076186730534183e-2, a5 = 7.700127105808265e-4,
942 a6 = 1.368088305992921e-5;
943 double a = j * 2.0 * M_PI / (n - 1);
944 return a0 - a1 * cos(1.0 * a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a) + a4 * cos(4.0 * a) -
945 a5 * cos(5.0 * a) + a6 * cos(6.0 * a);
946 } else if (!g_strcmp0(win, "Blackman-Harris")) {
947 double a0 = 3.58750287312166e-1, a1 = 4.88290107472600e-1, a2 = 1.41279712970519e-1,
948 a3 = 1.16798922447150e-2;
949 double a = j * 2.0 * M_PI / (n - 1);
950 return a0 - a1 * cos(a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a);
951 } else if (!g_strcmp0(win, "Flat Top")) {
952 double a0 = 2.1557895e-1, a1 = 4.1663158e-1, a2 = 2.77263158e-1,
953 a3 = 8.3578947e-2, a4 = 6.947368e-3;
954 double a = j * 2.0 * M_PI / (n - 1);
955 return a0 - a1 * cos(a) + a2 * cos(2.0 * a) - a3 * cos(3.0 * a) + a4 * cos(4.0 * a);
956 }
957
958 printf("unknown window function\n");
959 return 0;
960 }
961
962 /* This equalized power, so full scale is always 0dBFS */
window_function_offset(gchar * win)963 static double window_function_offset(gchar *win)
964 {
965 /* Strings need to match what is in glade */
966 if (!g_strcmp0(win, "Hanning")) {
967 return 1.77;
968 } else if (!g_strcmp0(win, "Boxcar")) {
969 return -4.25;
970 } else if (!g_strcmp0(win, "Triangular")) {
971 return 1.77;
972 } else if (!g_strcmp0(win, "Welch")) {
973 return -0.73;
974 } else if (!g_strcmp0(win, "Cosine")) {
975 return -0.33;
976 } else if (!g_strcmp0(win, "Hamming")) {
977 return 1.13;
978 } else if (!g_strcmp0(win, "Exact Blackman")) {
979 return 3.15;
980 } else if (!g_strcmp0(win, "3 Term Cosine")) {
981 return 3.19;
982 } else if (!g_strcmp0(win, "4 Term Cosine")) {
983 return 4.54;
984 } else if (!g_strcmp0(win, "5 Term Cosine")) {
985 return 5.56;
986 } else if (!g_strcmp0(win, "6 Term Cosine")) {
987 return 6.39;
988 } else if (!g_strcmp0(win, "7 Term Cosine")) {
989 return 7.08;
990 } else if (!g_strcmp0(win, "Blackman-Harris")) {
991 return 4.65;
992 } else if (!g_strcmp0(win, "Flat Top")) {
993 return 9.08;
994 }
995 printf("missed\n");
996 return 0;
997 }
998
do_fft(Transform * tr)999 static void do_fft(Transform *tr)
1000 {
1001 struct _fft_settings *settings = tr->settings;
1002 struct _fft_alg_data *fft = &settings->fft_alg_data;
1003 struct marker_type *markers = settings->markers;
1004 enum marker_types marker_type = MARKER_OFF;
1005 gfloat *in_data = settings->real_source;
1006 gfloat *in_data_c;
1007 gfloat *out_data = tr->y_axis;
1008 gfloat *X = tr->x_axis;
1009 int fft_size = settings->fft_size;
1010 int i, j, k;
1011 int cnt;
1012 gfloat mag;
1013 double avg, pwr_offset;
1014 int maxX[MAX_MARKERS + 1];
1015 gfloat maxY[MAX_MARKERS + 1];
1016 gfloat plugin_fft_corr;
1017
1018 if (settings->marker_type)
1019 marker_type = *((enum marker_types *)settings->marker_type);
1020
1021 if ((fft->cached_fft_size == -1) || (fft->cached_fft_size != fft_size) ||
1022 (fft->cached_num_active_channels != fft->num_active_channels)) {
1023
1024 if (fft->cached_fft_size != -1) {
1025 fftw_destroy_plan(fft->plan_forward);
1026 fftw_free(fft->win);
1027 fftw_free(fft->out);
1028 if (fft->in != NULL)
1029 fftw_free(fft->in);
1030 if (fft->in_c != NULL)
1031 fftw_free(fft->in_c);
1032 fft->in_c = NULL;
1033 fft->in = NULL;
1034 }
1035
1036 fft->win = fftw_malloc(sizeof(double) * fft_size);
1037 if (fft->num_active_channels == 2) {
1038 fft->m = fft_size;
1039 fft->in_c = fftw_malloc(sizeof(fftw_complex) * fft_size);
1040 fft->in = NULL;
1041 fft->out = fftw_malloc(sizeof(fftw_complex) * (fft->m + 1));
1042 fft->plan_forward = fftw_plan_dft_1d(fft_size, fft->in_c, fft->out, FFTW_FORWARD, FFTW_ESTIMATE);
1043 } else {
1044 fft->m = fft_size / 2;
1045 fft->out = fftw_malloc(sizeof(fftw_complex) * (fft->m + 1));
1046 fft->in_c = NULL;
1047 fft->in = fftw_malloc(sizeof(double) * fft_size);
1048 fft->plan_forward = fftw_plan_dft_r2c_1d(fft_size, fft->in, fft->out, FFTW_ESTIMATE);
1049 }
1050
1051 for (i = 0; i < fft_size; i ++)
1052 fft->win[i] = window_function(settings->fft_win, i, fft_size);
1053
1054 fft->cached_fft_size = fft_size;
1055 fft->cached_num_active_channels = fft->num_active_channels;
1056 }
1057
1058 if (fft->num_active_channels == 2) {
1059 in_data_c = settings->imag_source;
1060 for (cnt = 0, i = 0; cnt < fft_size; cnt++) {
1061 /* normalization and scaling see fft_corr */
1062 fft->in_c[cnt] = in_data[i] * fft->win[cnt] + I * in_data_c[i] * fft->win[cnt];
1063 i++;
1064 }
1065 } else {
1066 for (cnt = 0, i = 0; i < fft_size; i++) {
1067 /* normalization and scaling see fft_corr */
1068 fft->in[cnt] = in_data[i] * fft->win[cnt];
1069 cnt++;
1070 }
1071 }
1072
1073 struct iio_device *iio_dev = transform_get_device_parent(tr);
1074 struct extra_dev_info *dev_info = iio_device_get_data(iio_dev);
1075 plugin_fft_corr = dev_info->plugin_fft_corr;
1076
1077 fftw_execute(fft->plan_forward);
1078 avg = (double)settings->fft_avg;
1079 if (avg && avg != 128 )
1080 avg = 1.0f / avg;
1081
1082 pwr_offset = settings->fft_pwr_off + window_function_offset(settings->fft_win);
1083
1084 for (j = 0; j <= MAX_MARKERS; j++) {
1085 maxX[j] = 0;
1086 maxY[j] = -200.0f;
1087 }
1088
1089 for (i = 0; i < fft->m; ++i) {
1090 if (fft->num_active_channels == 2) {
1091 if (i < (fft->m / 2))
1092 j = i + (fft->m / 2);
1093 else
1094 j = i - (fft->m / 2);
1095 } else {
1096 j = i;
1097 }
1098
1099 if (creal(fft->out[j]) == 0 && cimag(fft->out[j]) == 0)
1100 fft->out[j] = FLT_MIN + I * FLT_MIN;
1101
1102 mag = 10 * log10((creal(fft->out[j]) * creal(fft->out[j]) +
1103 cimag(fft->out[j]) * cimag(fft->out[j])) / ((unsigned long long)fft->m * fft->m)) +
1104 fft->fft_corr + pwr_offset + plugin_fft_corr;
1105 /* it's better for performance to have separate loops,
1106 * rather than do these tests inside the loop, but it makes
1107 * the code harder to understand... Oh well...
1108 ***/
1109 if (out_data[i] == FLT_MAX) {
1110 /* Don't average the first iteration */
1111 out_data[i] = mag;
1112 } else if (!avg) {
1113 /* keep peaks */
1114 if (out_data[i] <= mag)
1115 out_data[i] = mag;
1116 } else if (avg == 128) {
1117 /* keep min */
1118 if (out_data[i] >= mag)
1119 out_data[i] = mag;
1120 } else {
1121 /* do an average */
1122 out_data[i] = ((1 - avg) * out_data[i]) + (avg * mag);
1123 }
1124 if (!settings->markers || i < 2)
1125 continue;
1126 if (MAX_MARKERS && (marker_type == MARKER_PEAK ||
1127 marker_type == MARKER_ONE_TONE ||
1128 marker_type == MARKER_IMAGE)) {
1129 if (i <= 2) {
1130 maxX[0] = 0;
1131 maxY[0] = out_data[0];
1132 } else {
1133 for (j = 0; j <= MAX_MARKERS && markers[j].active; j++) {
1134 if ((out_data[i - 1] > maxY[j]) &&
1135 ((!((out_data[i - 2] > out_data[i - 1]) &&
1136 (out_data[i - 1] > out_data[i]))) &&
1137 (!((out_data[i - 2] < out_data[i - 1]) &&
1138 (out_data[i - 1] < out_data[i]))))) {
1139 if (marker_type == MARKER_PEAK) {
1140 for (k = MAX_MARKERS; k > j; k--) {
1141 maxY[k] = maxY[k - 1];
1142 maxX[k] = maxX[k - 1];
1143 }
1144 }
1145 maxY[j] = out_data[i - 1];
1146 maxX[j] = i - 1;
1147 break;
1148 }
1149 }
1150 }
1151 }
1152 }
1153
1154 if (!settings->markers)
1155 return;
1156
1157 int m = fft->m;
1158
1159 if ((marker_type == MARKER_ONE_TONE || marker_type == MARKER_IMAGE) &&
1160 ((fft->num_active_channels == 1 && maxX[0] == 0) ||
1161 (fft->num_active_channels == 2 && maxX[0] == m/2))) {
1162 unsigned int max_tmp;
1163
1164 max_tmp = maxX[1];
1165 maxX[1] = maxX[0];
1166 maxX[0] = max_tmp;
1167 }
1168
1169 if (MAX_MARKERS && marker_type != MARKER_OFF) {
1170 for (j = 0; j <= MAX_MARKERS && markers[j].active; j++) {
1171 if (marker_type == MARKER_PEAK) {
1172 markers[j].x = (gfloat)X[maxX[j]];
1173 markers[j].y = (gfloat)out_data[maxX[j]];
1174 markers[j].bin = maxX[j];
1175 } else if (marker_type == MARKER_FIXED) {
1176 markers[j].x = (gfloat)X[markers[j].bin];
1177 markers[j].y = (gfloat)out_data[markers[j].bin];
1178 } else if (marker_type == MARKER_ONE_TONE) {
1179 /* assume peak is the tone */
1180 if (j == 0) {
1181 markers[j].bin = maxX[j];
1182 i = 1;
1183 } else if (j == 1) {
1184 /* keep DC */
1185 if (tr->type_id == COMPLEX_FFT_TRANSFORM)
1186 markers[j].bin = m / 2;
1187 else
1188 markers[j].bin = 0;
1189 } else {
1190 /* where should the spurs be? */
1191 i++;
1192 if (tr->type_id == COMPLEX_FFT_TRANSFORM) {
1193 markers[j].bin = markers[0].bin * i;
1194 if (i % 2 == 0)
1195 markers[j].bin += m / 2;
1196 markers[j].bin %= m;
1197 } else {
1198 markers[j].bin = (markers[0].bin * i) % (2 * m);
1199 /* Mirror the even Nyquist zones */
1200 if (markers[j].bin > m)
1201 markers[j].bin = 2 * m - markers[j].bin;
1202 }
1203 }
1204 /* make sure we don't need to nudge things one way or the other */
1205 k = markers[j].bin;
1206 while (out_data[k] < out_data[k + 1]) {
1207 k++;
1208 }
1209
1210 while (markers[j].bin != 0 &&
1211 out_data[markers[j].bin] < out_data[markers[j].bin - 1]) {
1212 markers[j].bin--;
1213 }
1214
1215 if (out_data[k] > out_data[markers[j].bin])
1216 markers[j].bin = k;
1217
1218 markers[j].x = (gfloat)X[markers[j].bin];
1219 markers[j].y = (gfloat)out_data[markers[j].bin];
1220 } else if (marker_type == MARKER_IMAGE) {
1221 /* keep DC, fundamental, and image
1222 * num_active_channels always needs to be 2 for images */
1223 if (j == 0) {
1224 /* Fundamental */
1225 markers[j].bin = maxX[j];
1226 } else if (j == 1) {
1227 /* DC */
1228 markers[j].bin = m / 2;
1229 } else if (j == 2) {
1230 /* Image */
1231 markers[j].bin = m / 2 - (markers[0].bin - m/2);
1232 } else
1233 continue;
1234 markers[j].x = (gfloat)X[markers[j].bin];
1235 markers[j].y = (gfloat)out_data[markers[j].bin];
1236
1237 }
1238 if (fft->num_active_channels == 2) {
1239 markers[j].vector = I * settings->imag_source[markers[j].bin] +
1240 in_data[markers[j].bin];
1241 } else {
1242 markers[j].vector = 0 + I * 0;
1243 }
1244 }
1245 if (settings->markers_copy && *settings->markers_copy) {
1246 memcpy(*settings->markers_copy, settings->markers,
1247 sizeof(struct marker_type) * MAX_MARKERS);
1248 *settings->markers_copy = NULL;
1249 g_mutex_unlock(settings->marker_lock);
1250 }
1251 }
1252 }
1253
do_fft_for_spectrum(Transform * tr)1254 static void do_fft_for_spectrum(Transform *tr)
1255 {
1256 struct _freq_spectrum_settings *settings = tr->settings;
1257 struct _fft_alg_data *fft = &settings->ffts_alg_data[settings->fft_index];
1258 struct marker_type *markers = settings->markers;
1259 enum marker_types marker_type = MARKER_OFF;
1260 int fft_clip_size = settings->fft_upper_clipping_limit -
1261 settings->fft_lower_clipping_limit;
1262 gfloat *in_data = settings->real_source;
1263 gfloat *in_data_c = settings->imag_source;
1264 gfloat *out_data = tr->y_axis + (settings->fft_index * fft_clip_size);
1265 int fft_size = settings->fft_size;
1266 int i, j, k, m;
1267 int cnt;
1268 gfloat mag;
1269 double avg, pwr_offset;
1270 gfloat plugin_fft_corr;
1271 unsigned int *maxX = settings->maxXaxis;
1272 gfloat *maxY = settings->maxYaxis;
1273
1274 if (settings->marker_type)
1275 marker_type = *((enum marker_types *)settings->marker_type);
1276
1277 if ((fft->cached_fft_size == -1) || (fft->cached_fft_size != fft_size) ||
1278 (fft->cached_num_active_channels != fft->num_active_channels)) {
1279
1280 if (fft->cached_fft_size != -1) {
1281 fftw_destroy_plan(fft->plan_forward);
1282 fftw_free(fft->win);
1283 fftw_free(fft->out);
1284 if (fft->in != NULL)
1285 fftw_free(fft->in);
1286 if (fft->in_c != NULL)
1287 fftw_free(fft->in_c);
1288 fft->in_c = NULL;
1289 fft->in = NULL;
1290 }
1291
1292 fft->win = fftw_malloc(sizeof(double) * fft_size);
1293 fft->m = fft_size;
1294 fft->in_c = fftw_malloc(sizeof(fftw_complex) * fft_size);
1295 fft->in = NULL;
1296 fft->out = fftw_malloc(sizeof(fftw_complex) * (fft->m + 1));
1297 fft->plan_forward = fftw_plan_dft_1d(fft_size, fft->in_c, fft->out, FFTW_FORWARD, FFTW_ESTIMATE);
1298
1299 for (i = 0; i < fft_size; i ++)
1300 fft->win[i] = window_function(settings->fft_win, i, fft_size);
1301
1302 fft->cached_fft_size = fft_size;
1303 fft->cached_num_active_channels = fft->num_active_channels;
1304 }
1305
1306 for (cnt = 0, i = 0; cnt < fft_size; cnt++) {
1307 /* normalization and scaling see fft_corr */
1308 fft->in_c[cnt] = in_data[i] * fft->win[cnt] + I * in_data_c[i] * fft->win[cnt];
1309 i++;
1310 }
1311
1312 struct iio_device *iio_dev = transform_get_device_parent(tr);
1313 struct extra_dev_info *dev_info = iio_device_get_data(iio_dev);
1314 plugin_fft_corr = dev_info->plugin_fft_corr;
1315
1316 fftw_execute(fft->plan_forward);
1317 avg = (double)settings->fft_avg;
1318 if (avg && avg != 128 )
1319 avg = 1.0f / avg;
1320
1321 pwr_offset = settings->fft_pwr_off;
1322
1323 for (i = 0, k = 0; i < fft->m; ++i) {
1324 if ((unsigned)i < settings->fft_lower_clipping_limit || (unsigned)i >= settings->fft_upper_clipping_limit)
1325 continue;
1326 if (i < (fft->m / 2))
1327 j = i + (fft->m / 2);
1328 else
1329 j = i - (fft->m / 2);
1330
1331 if (creal(fft->out[j]) == 0 && cimag(fft->out[j]) == 0)
1332 fft->out[j] = FLT_MIN + I * FLT_MIN;
1333
1334 mag = 10 * log10((creal(fft->out[j]) * creal(fft->out[j]) +
1335 cimag(fft->out[j]) * cimag(fft->out[j])) / ((unsigned long long)fft->m * fft->m)) +
1336 settings->fft_corr + pwr_offset + plugin_fft_corr;
1337 /* it's better for performance to have separate loops,
1338 * rather than do these tests inside the loop, but it makes
1339 * the code harder to understand... Oh well...
1340 ***/
1341 if (out_data[k] == FLT_MAX) {
1342 /* Don't average the first iteration */
1343 out_data[k] = mag;
1344 } else if (!avg) {
1345 /* keep peaks */
1346 if (out_data[k] <= mag)
1347 out_data[k] = mag;
1348 } else if (avg == 128) {
1349 /* keep min */
1350 if (out_data[k] >= mag)
1351 out_data[k] = mag;
1352 } else {
1353 /* do an average */
1354 out_data[k] = ((1 - avg) * out_data[k]) + (avg * mag);
1355 }
1356
1357 if (MAX_MARKERS && marker_type == MARKER_PEAK) {
1358 if (settings->fft_index == 0 && k <= 2) {
1359 maxX[0] = 0;
1360 maxY[0] = out_data[0];
1361 } else {
1362 for (j = 0; j <= MAX_MARKERS && markers[j].active; j++) {
1363 if ((*(out_data + k - 1) > maxY[j]) &&
1364 ((!((*(out_data + k - 2) > *(out_data + k - 1)) &&
1365 (*(out_data + k - 1) > *(out_data + k)))) &&
1366 (!((*(out_data + k - 2) < *(out_data + k - 1)) &&
1367 (*(out_data + k - 1) < *(out_data + k)))))) {
1368
1369 for (m = MAX_MARKERS; m > j; m--) {
1370 maxY[m] = maxY[m - 1];
1371 maxX[m] = maxX[m - 1];
1372 }
1373 maxY[j] = *(out_data + k - 1);
1374 maxX[j] = k + (settings->fft_index * fft_clip_size) - 1;
1375 break;
1376 }
1377 }
1378 }
1379 }
1380
1381 k++;
1382 }
1383 }
1384
1385 /* sections of the xcorr function are borrowed (under the GPL) from
1386 * http://blog.dmaggot.org/2010/06/cross-correlation-using-fftw3/
1387 * which is copyright 2010 David E. Narváez
1388 */
xcorr(fftw_complex * signala,fftw_complex * signalb,fftw_complex * result,int N,double avg)1389 static void xcorr(fftw_complex *signala, fftw_complex *signalb, fftw_complex *result, int N, double avg)
1390 {
1391 fftw_complex * signala_ext = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (2 * N - 1));
1392 fftw_complex * signalb_ext = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (2 * N - 1));
1393 fftw_complex * outa = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (2 * N - 1));
1394 fftw_complex * outb = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (2 * N - 1));
1395 fftw_complex * out = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (2 * N - 1));
1396 fftw_complex scale;
1397 fftw_complex *cross;
1398
1399 int i;
1400 double peak_a = 0.0, peak_b = 0.0;
1401
1402 if (!signala_ext || !signalb_ext || !outa || !outb || !out)
1403 return;
1404
1405 if (avg > 1)
1406 cross = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * (2 * N));
1407 else
1408 cross = result;
1409
1410 fftw_plan pa = fftw_plan_dft_1d(2 * N - 1, signala_ext, outa, FFTW_FORWARD, FFTW_ESTIMATE);
1411 fftw_plan pb = fftw_plan_dft_1d(2 * N - 1, signalb_ext, outb, FFTW_FORWARD, FFTW_ESTIMATE);
1412 fftw_plan px = fftw_plan_dft_1d(2 * N - 1, out, cross, FFTW_BACKWARD, FFTW_ESTIMATE);
1413
1414 //zeropadding
1415 memset(signala_ext, 0, sizeof(fftw_complex) * (N - 1));
1416 memcpy(signala_ext + (N - 1), signala, sizeof(fftw_complex) * N);
1417 memcpy(signalb_ext, signalb, sizeof(fftw_complex) * N);
1418 memset(signalb_ext + N, 0, sizeof(fftw_complex) * (N - 1));
1419
1420 /* find the peaks of the time domain, for normalization */
1421 for (i = 0; i < N; i++) {
1422 if (peak_a < cabs(signala[i]))
1423 peak_a = cabs(signala[i]);
1424
1425 if (peak_b < cabs(signalb[i]))
1426 peak_b = cabs(signalb[i]);
1427 }
1428
1429 /* Move the two signals into the fourier domain */
1430 fftw_execute(pa);
1431 fftw_execute(pb);
1432
1433 /* Compute the dot product, and scale them */
1434 scale = (2 * N -1) * peak_a * peak_b * 2;
1435 for (i = 0; i < 2 * N - 1; i++)
1436 out[i] = outa[i] * conj(outb[i]) / scale;
1437
1438 /* Inverse FFT on the dot product */
1439 fftw_execute(px);
1440
1441 fftw_destroy_plan(pa);
1442 fftw_destroy_plan(pb);
1443 fftw_destroy_plan(px);
1444
1445 fftw_free(signala_ext);
1446 fftw_free(signalb_ext);
1447 fftw_free(out);
1448 fftw_free(outa);
1449 fftw_free(outb);
1450
1451 if(avg > 1) {
1452 if (result[0] == FLT_MAX) {
1453 for (i = 0; i < 2 * N -1; i++)
1454 result[i] = cross[i];
1455 } else {
1456 for (i = 0; i < 2 * N -1; i++)
1457 result[i] = (result[i] * (avg - 1) + cross[i]) / avg;
1458 }
1459 fftw_free(cross);
1460 }
1461
1462 fftw_cleanup();
1463
1464 return;
1465 }
1466
time_transform_function(Transform * tr,gboolean init_transform)1467 bool time_transform_function(Transform *tr, gboolean init_transform)
1468 {
1469 struct _time_settings *settings = tr->settings;
1470 unsigned axis_length = settings->num_samples;
1471 gfloat *in_data;
1472 unsigned int i;
1473
1474 if (init_transform) {
1475
1476 /* Set the sources of the transfrom */
1477 settings->data_source = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1478
1479 /* Initialize axis */
1480 Transform_resize_x_axis(tr, axis_length);
1481 for (i = 0; i < axis_length; i++) {
1482 if (settings->max_x_axis && settings->max_x_axis != 0)
1483 tr->x_axis[i] = (gfloat)(i * settings->max_x_axis)/axis_length;
1484 else
1485 tr->x_axis[i] = i;
1486 }
1487 tr->y_axis_size = axis_length;
1488
1489 if (settings->apply_inverse_funct ||
1490 settings->apply_multiply_funct ||
1491 settings->apply_add_funct) {
1492 Transform_resize_y_axis(tr, tr->y_axis_size);
1493 } else {
1494 tr->y_axis = settings->data_source;
1495 }
1496
1497 return true;
1498 }
1499
1500 if (tr->plot_channels_type == PLOT_MATH_CHANNEL) {
1501 PlotMathChn *m = tr->plot_channels->data;
1502 m->math_expression(m->iio_channels_data,
1503 m->data_ref, settings->num_samples);
1504 } else if (tr->plot_channels_type == PLOT_IIO_CHANNEL) {
1505 if (!settings->apply_inverse_funct &&
1506 !settings->apply_multiply_funct &&
1507 !settings->apply_add_funct)
1508 return true;
1509
1510 in_data = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1511 if (!in_data)
1512 return false;
1513
1514 for (i = 0; i < tr->y_axis_size; i++) {
1515 if (settings->apply_inverse_funct) {
1516 if (in_data[i] != 0)
1517 tr->y_axis[i] = 1 / in_data[i];
1518 else
1519 tr->y_axis[i] = 65535;
1520 } else {
1521 tr->y_axis[i] = in_data[i];
1522 }
1523 if (settings->apply_multiply_funct)
1524 tr->y_axis[i] *= settings->multiply_value;
1525 if (settings->apply_add_funct)
1526 tr->y_axis[i] += settings->add_value;
1527 }
1528 }
1529
1530 return true;
1531 }
1532
cross_correlation_transform_function(Transform * tr,gboolean init_transform)1533 bool cross_correlation_transform_function(Transform *tr, gboolean init_transform)
1534 {
1535 struct _cross_correlation_settings *settings = tr->settings;
1536 unsigned axis_length = settings->num_samples;
1537 gfloat *i_0, *q_0;
1538 gfloat *i_1, *q_1;
1539 unsigned int i;
1540
1541 if (init_transform) {
1542 /* Set the sources of the transfrom */
1543 settings->i0_source = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1544 settings->q0_source = plot_channels_get_nth_data_ref(tr->plot_channels, 1);
1545 settings->i1_source = plot_channels_get_nth_data_ref(tr->plot_channels, 2);
1546 settings->q1_source = plot_channels_get_nth_data_ref(tr->plot_channels, 3);
1547
1548 /* Initialize axis */
1549 if (settings->signal_a) {
1550 fftw_free(settings->signal_a);
1551 settings->signal_a = NULL;
1552 }
1553 if (settings->signal_b) {
1554 fftw_free(settings->signal_b);
1555 settings->signal_b = NULL;
1556 }
1557 if (settings->xcorr_data) {
1558 fftw_free(settings->xcorr_data);
1559 settings->xcorr_data = NULL;
1560 }
1561 settings->signal_a = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * axis_length);
1562 settings->signal_b = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * axis_length);
1563 settings->xcorr_data = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * axis_length * 2);
1564 settings->xcorr_data[0] = FLT_MAX;
1565
1566 Transform_resize_x_axis(tr, 2 * axis_length);
1567 Transform_resize_y_axis(tr, 2 * axis_length);
1568 for (i = 0; i < 2 * axis_length - 1; i++) {
1569 tr->x_axis[i] = ((i - (gfloat)axis_length + 1) * settings->max_x_axis)/(gfloat)axis_length;
1570 tr->y_axis[i] = 0;
1571 }
1572 tr->y_axis_size = 2 * axis_length - 1;
1573
1574 return true;
1575 }
1576
1577 GSList *node;
1578
1579 if (tr->plot_channels_type == PLOT_MATH_CHANNEL)
1580 for (node = tr->plot_channels; node; node = g_slist_next(node)) {
1581 PlotMathChn *m = node->data;
1582 m->math_expression(m->iio_channels_data,
1583 m->data_ref, settings->num_samples);
1584 }
1585
1586 i_0 = settings->i0_source;
1587 q_0 = settings->q0_source;
1588 i_1 = settings->i1_source;
1589 q_1 = settings->q1_source;
1590
1591 for (i = 0; i < axis_length; i++) {
1592 settings->signal_a[i] = q_0[i] + I * i_0[i];
1593 settings->signal_b[i] = q_1[i] + I * i_1[i];
1594 }
1595
1596 if (settings->revert_xcorr)
1597 xcorr(settings->signal_b, settings->signal_a, settings->xcorr_data, axis_length, (double)settings->avg);
1598 else
1599 xcorr(settings->signal_a, settings->signal_b, settings->xcorr_data, axis_length, (double)settings->avg);
1600
1601 gfloat *out_data = tr->y_axis;
1602 gfloat *X = tr->x_axis;
1603 struct marker_type *markers = settings->markers;
1604 enum marker_types marker_type = MARKER_OFF;
1605 unsigned int maxX[MAX_MARKERS + 1];
1606 gfloat maxY[MAX_MARKERS + 1];
1607 int j, k;
1608
1609 if (settings->marker_type)
1610 marker_type = *((enum marker_types *)settings->marker_type);
1611
1612 for (j = 0; j <= MAX_MARKERS; j++) {
1613 maxX[j] = 0;
1614 maxY[j] = -200.0f;
1615 }
1616
1617 /* find the peaks */
1618 for (i = 0; i < 2 * axis_length - 1; i++) {
1619 tr->y_axis[i] = 2 * creal(settings->xcorr_data[i]) / (gfloat)axis_length;
1620 if (!settings->markers)
1621 continue;
1622
1623 if (MAX_MARKERS && marker_type == MARKER_PEAK) {
1624 if (i <= 2) {
1625 maxX[0] = 0;
1626 maxY[0] = out_data[0];
1627 } else {
1628 for (j = 0; j <= MAX_MARKERS && markers[j].active; j++) {
1629 if ((fabs(out_data[i - 1]) > maxY[j]) &&
1630 ((!((out_data[i - 2] > out_data[i - 1]) &&
1631 (out_data[i - 1] > out_data[i]))) &&
1632 (!((out_data[i - 2] < out_data[i - 1]) &&
1633 (out_data[i - 1] < out_data[i]))))) {
1634 if (marker_type == MARKER_PEAK) {
1635 for (k = MAX_MARKERS; k > j; k--) {
1636 maxY[k] = maxY[k - 1];
1637 maxX[k] = maxX[k - 1];
1638 }
1639 }
1640 maxY[j] = fabs(out_data[i - 1]);
1641 maxX[j] = i - 1;
1642 break;
1643 }
1644 }
1645 }
1646 }
1647 }
1648
1649 if (!settings->markers)
1650 return true;
1651
1652 /* now we know where the peaks are, we estimate the actual peaks,
1653 * by quadratic interpolation of existing spectral peaks, which is explained:
1654 * https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
1655 * written by Julius Orion Smith III.
1656 */
1657 if (MAX_MARKERS && marker_type != MARKER_OFF) {
1658 for (j = 0; j <= MAX_MARKERS && markers[j].active; j++)
1659 if (marker_type == MARKER_PEAK) {
1660 /* If we don't have the alpha or the gamma peaks, we can't continue */
1661 if (maxX[j] < 1 || maxX[j] > 2 * axis_length - 1) {
1662 markers[j].x = 0;
1663 markers[j].y = 0;
1664 continue;
1665 }
1666 /* sync'ed with the pictures in the url above:
1667 * alpha = (gfloat)out_data[maxX[j] - 1];
1668 * gamma = (gfloat)out_data[maxX[j] + 1];
1669 * beta = (gfloat)out_data[maxX[j]];
1670 */
1671 markers[j].x = (gfloat)((out_data[maxX[j] - 1] - out_data[maxX[j] + 1]) /
1672 (2 * (out_data[maxX[j] - 1] - 2 * out_data[maxX[j]] +
1673 out_data[maxX[j] + 1])));
1674 markers[j].y = (gfloat)(out_data[maxX[j]] - (out_data[maxX[j] - 1] - out_data[maxX[j] + 1]) *
1675 markers[j].x / 4);
1676 markers[j].x += (gfloat)X[maxX[j]];
1677 markers[j].bin = maxX[j];
1678 }
1679 if (settings->markers_copy && *settings->markers_copy) {
1680 memcpy(*settings->markers_copy, settings->markers,
1681 sizeof(struct marker_type) * MAX_MARKERS);
1682 *settings->markers_copy = NULL;
1683 g_mutex_unlock(settings->marker_lock);
1684 }
1685 }
1686
1687 return true;
1688 }
1689
freq_spectrum_transform_function(Transform * tr,gboolean init_transform)1690 bool freq_spectrum_transform_function(Transform *tr, gboolean init_transform)
1691 {
1692 struct iio_channel *chn;
1693 struct _freq_spectrum_settings *settings = tr->settings;
1694 unsigned i, j, k, axis_length, fft_size, bits_used;
1695 int ret;
1696 double sampling_freq;
1697 bool complete_transform = false;
1698
1699 if (init_transform) {
1700 fft_size = settings->fft_size;
1701 chn = PLOT_IIO_CHN(tr->plot_channels->data)->iio_chn;
1702 ret = iio_channel_attr_read_double(chn, "sampling_frequency",
1703 &sampling_freq);
1704 if (ret < 0)
1705 return false;
1706 sampling_freq /= 1000000; /* Hz to MHz*/
1707
1708 bits_used = iio_channel_get_data_format(chn)->bits;
1709
1710 if (!bits_used)
1711 return false;
1712
1713 /* Compute FFT normalization and scaling offset */
1714 settings->fft_corr = 20 * log10(2.0 / (1ULL << (bits_used - 1)));
1715
1716 settings->fft_lower_clipping_limit = (fft_size / 2) - (settings->filter_bandwidth * fft_size) / (2 * sampling_freq);
1717 settings->fft_upper_clipping_limit = (fft_size / 2) + (settings->filter_bandwidth * fft_size) / (2 * sampling_freq);
1718
1719 settings->real_source = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1720 settings->imag_source = plot_channels_get_nth_data_ref(tr->plot_channels, 1);
1721
1722 axis_length = (settings->fft_upper_clipping_limit - settings->fft_lower_clipping_limit) * settings->fft_count;
1723 Transform_resize_x_axis(tr, axis_length);
1724 Transform_resize_y_axis(tr, axis_length);
1725
1726 for (i = 0, k = 0; i < settings->fft_count; i++) {
1727 for (j = 0; j < fft_size; j++) {
1728 if (j >= settings->fft_lower_clipping_limit && j < settings->fft_upper_clipping_limit) {
1729 tr->x_axis[k] = (j * sampling_freq / settings->fft_size - sampling_freq / 2) + settings->freq_sweep_start + (settings->filter_bandwidth) * i;
1730 tr->y_axis[k] = FLT_MAX;
1731 k++;
1732 }
1733 }
1734 }
1735
1736 for (i = 0; i <= MAX_MARKERS; i++) {
1737 settings->maxXaxis[i] = 0;
1738 settings->maxYaxis[i] = -200.0f;
1739 }
1740
1741 return true;
1742 }
1743
1744 do_fft_for_spectrum(tr);
1745 settings->fft_index++;
1746
1747 if (settings->fft_index == settings->fft_count) {
1748 settings->fft_index = 0;
1749 complete_transform = true;
1750
1751 if (MAX_MARKERS && *settings->marker_type != MARKER_OFF) {
1752 for (j = 0; j <= MAX_MARKERS && settings->markers[j].active; j++)
1753 if (*settings->marker_type == MARKER_PEAK) {
1754 settings->markers[j].x = (gfloat)tr->x_axis[settings->maxXaxis[j]];
1755 settings->markers[j].y = (gfloat)tr->y_axis[settings->maxXaxis[j]];
1756 settings->markers[j].bin = settings->maxXaxis[j];
1757 }
1758 if (*settings->markers_copy) {
1759 memcpy(*settings->markers_copy, settings->markers,
1760 sizeof(struct marker_type) * MAX_MARKERS);
1761 *settings->markers_copy = NULL;
1762 g_mutex_unlock(settings->marker_lock);
1763 }
1764 }
1765
1766 for (i = 0; i <= MAX_MARKERS; i++) {
1767 settings->maxXaxis[i] = 0;
1768 settings->maxYaxis[i] = -200.0f;
1769 }
1770 }
1771
1772 return complete_transform;
1773 }
1774
fft_transform_function(Transform * tr,gboolean init_transform)1775 bool fft_transform_function(Transform *tr, gboolean init_transform)
1776 {
1777 struct iio_device *dev;
1778 struct extra_dev_info *dev_info;
1779 struct _fft_settings *settings = tr->settings;
1780 unsigned num_samples;
1781 int axis_length;
1782 unsigned int bits_used;
1783 double corr;
1784 int i;
1785
1786 if (init_transform) {
1787 /* Set the sources of the transfrom */
1788 settings->real_source = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1789 if (g_slist_length(tr->plot_channels) > 1)
1790 settings->imag_source = plot_channels_get_nth_data_ref(tr->plot_channels, 1);
1791
1792 /* Initialize axis */
1793 dev = transform_get_device_parent(tr);
1794 if (!dev)
1795 return false;
1796 dev_info = iio_device_get_data(dev);
1797 num_samples = dev_info->sample_count;
1798 if (dev_info->channel_trigger_enabled)
1799 num_samples /= 2;
1800
1801 PlotChn *chn = (PlotChn *)tr->plot_channels->data;
1802 struct iio_channel *iio_chn = NULL;
1803
1804 bits_used = 0;
1805 if (chn->type == PLOT_IIO_CHANNEL) {
1806 iio_chn = PLOT_IIO_CHN(chn)->iio_chn;
1807 } else if (PLOT_CHN(chn)->type == PLOT_MATH_CHANNEL) {
1808 if (PLOT_MATH_CHN(chn)->iio_channels) {
1809 iio_chn = (struct iio_channel *)
1810 PLOT_MATH_CHN(chn)->iio_channels->data;
1811 }
1812 }
1813 if (iio_chn)
1814 bits_used = iio_channel_get_data_format(iio_chn)->bits;
1815
1816 if (!bits_used)
1817 return false;
1818 axis_length = settings->fft_size * settings->fft_alg_data.num_active_channels / 2;
1819 Transform_resize_x_axis(tr, axis_length);
1820 Transform_resize_y_axis(tr, axis_length);
1821 tr->y_axis_size = axis_length;
1822 if (settings->fft_alg_data.num_active_channels == 2)
1823 corr = dev_info->adc_freq / 2.0;
1824 else
1825 corr = 0;
1826 for (i = 0; i < axis_length; i++) {
1827 tr->x_axis[i] = i * dev_info->adc_freq / num_samples - corr;
1828 tr->y_axis[i] = FLT_MAX;
1829 }
1830
1831 /* Compute FFT normalization and scaling offset */
1832 settings->fft_alg_data.fft_corr = 20 * log10(2.0 / (1ULL << (bits_used - 1)));
1833
1834 /* Make sure that previous positions of markers are not out of bonds */
1835 if (settings->markers)
1836 for (i = 0; i <= MAX_MARKERS; i++)
1837 if (settings->markers[i].bin >= axis_length)
1838 settings->markers[i].bin = 0;
1839
1840 return true;
1841 }
1842
1843 GSList *node;
1844
1845 if (tr->plot_channels_type == PLOT_MATH_CHANNEL)
1846 for (node = tr->plot_channels; node; node = g_slist_next(node)) {
1847 PlotMathChn *m = node->data;
1848 m->math_expression(m->iio_channels_data,
1849 m->data_ref, settings->fft_size);
1850 }
1851 do_fft(tr);
1852
1853 return true;
1854 }
1855
constellation_transform_function(Transform * tr,gboolean init_transform)1856 bool constellation_transform_function(Transform *tr, gboolean init_transform)
1857 {
1858 struct _constellation_settings *settings = tr->settings;
1859 unsigned axis_length = settings->num_samples;
1860
1861 if (init_transform) {
1862 /* Set the sources of the transfrom */
1863 settings->x_source = plot_channels_get_nth_data_ref(tr->plot_channels, 0);
1864 settings->y_source = plot_channels_get_nth_data_ref(tr->plot_channels, 1);
1865
1866 /* Initialize axis */
1867 tr->x_axis_size = axis_length;
1868 tr->y_axis_size = axis_length;
1869 tr->x_axis = settings->x_source;
1870 tr->y_axis = settings->y_source;
1871
1872 return true;
1873 }
1874
1875 GSList *node;
1876
1877 if (tr->plot_channels_type == PLOT_MATH_CHANNEL)
1878 for (node = tr->plot_channels; node; node = g_slist_next(node)) {
1879 PlotMathChn *m = node->data;
1880 m->math_expression(m->iio_channels_data,
1881 m->data_ref, settings->num_samples);
1882 }
1883
1884 return true;
1885 }
1886
1887
1888 /* Plot iio channel definitions */
1889
1890 static struct iio_device * plot_iio_channel_get_iio_parent(PlotChn *obj);
1891 static gfloat* plot_iio_channel_get_data_ref(PlotChn *obj);
1892 static void plot_iio_channel_assert_channels(PlotChn *obj, bool assert);
1893 static void plot_iio_channel_destroy(PlotChn *obj);
1894
plot_iio_channel_new(struct iio_context * ctx)1895 static PlotIioChn * plot_iio_channel_new(struct iio_context *ctx)
1896 {
1897 PlotIioChn *obj;
1898
1899 obj = calloc(sizeof(PlotIioChn), 1);
1900 if (!obj) {
1901 fprintf(stderr, "Error in %s: %s", __func__, strerror(errno));
1902 return NULL;
1903 }
1904
1905 obj->base.type = PLOT_IIO_CHANNEL;
1906 obj->base.ctx = ctx;
1907 obj->base.get_iio_parent = *plot_iio_channel_get_iio_parent;
1908 obj->base.get_data_ref = *plot_iio_channel_get_data_ref;
1909 obj->base.assert_used_iio_channels = *plot_iio_channel_assert_channels;
1910 obj->base.destroy = *plot_iio_channel_destroy;
1911
1912 return obj;
1913 }
1914
plot_iio_channel_get_iio_parent(PlotChn * obj)1915 static struct iio_device *plot_iio_channel_get_iio_parent(PlotChn *obj)
1916 {
1917 PlotIioChn *this = (PlotIioChn *)obj;
1918 struct iio_device *iio_dev = NULL;
1919 struct extra_info *ch_info;
1920
1921 if (this && this->iio_chn) {
1922 ch_info = iio_channel_get_data(this->iio_chn);
1923 if (ch_info)
1924 iio_dev = ch_info->dev;
1925 }
1926
1927 return iio_dev;
1928 }
1929
plot_iio_channel_get_data_ref(PlotChn * obj)1930 static gfloat* plot_iio_channel_get_data_ref(PlotChn *obj)
1931 {
1932 PlotIioChn *this = (PlotIioChn *)obj;
1933 gfloat *ref = NULL;
1934
1935 if (this && this->iio_chn) {
1936 struct extra_info *ch_info;
1937
1938 ch_info = iio_channel_get_data(this->iio_chn);
1939 if (ch_info)
1940 ref = ch_info->data_ref;
1941 }
1942
1943 return ref;
1944 }
1945
plot_iio_channel_assert_channels(PlotChn * obj,bool assert)1946 static void plot_iio_channel_assert_channels(PlotChn *obj, bool assert)
1947 {
1948 PlotIioChn *this = (PlotIioChn *)obj;
1949
1950 if (this && this->iio_chn)
1951 set_channel_shadow_of_enabled(this->iio_chn,
1952 (gpointer)assert);
1953 }
1954
plot_iio_channel_destroy(PlotChn * obj)1955 static void plot_iio_channel_destroy(PlotChn *obj)
1956 {
1957 PlotIioChn *this = (PlotIioChn *)obj;
1958
1959 if (!this)
1960 return;
1961
1962 if (this->base.name)
1963 g_free(this->base.name);
1964
1965 if (this->base.parent_name)
1966 g_free(this->base.parent_name);
1967
1968 free(this);
1969 }
1970
1971 /* Plot math channel definitions */
1972
1973 static struct iio_device * plot_math_channel_get_iio_parent(PlotChn *obj);
1974 static gfloat * plot_math_channel_get_data_ref(PlotChn *obj);
1975 static void plot_math_channel_assert_channels(PlotChn *obj, bool assert);
1976 static void plot_math_channel_destroy(PlotChn *obj);
1977
plot_math_channel_new(struct iio_context * ctx)1978 static PlotMathChn * plot_math_channel_new(struct iio_context *ctx)
1979 {
1980 PlotMathChn *obj;
1981
1982 obj = calloc(sizeof(PlotMathChn), 1);
1983 if (!obj) {
1984 fprintf(stderr, "Error in %s: %s", __func__, strerror(errno));
1985 return NULL;
1986 }
1987
1988 obj->base.type = PLOT_MATH_CHANNEL;
1989 obj->base.ctx = ctx;
1990 obj->base.get_iio_parent = *plot_math_channel_get_iio_parent;
1991 obj->base.get_data_ref = *plot_math_channel_get_data_ref;
1992 obj->base.assert_used_iio_channels = *plot_math_channel_assert_channels;
1993 obj->base.destroy = *plot_math_channel_destroy;
1994
1995 return obj;
1996 }
1997
plot_math_channel_get_iio_parent(PlotChn * obj)1998 static struct iio_device *plot_math_channel_get_iio_parent(PlotChn *obj)
1999 {
2000 PlotMathChn *this = (PlotMathChn *)obj;
2001 struct iio_device *iio_dev = NULL;
2002
2003 if (this && this->iio_device_name) {
2004 iio_dev = iio_context_find_device(this->base.ctx,
2005 this->iio_device_name);
2006 }
2007
2008 return iio_dev;
2009 }
2010
plot_math_channel_get_data_ref(PlotChn * obj)2011 static gfloat * plot_math_channel_get_data_ref(PlotChn *obj)
2012 {
2013 PlotMathChn *this = (PlotMathChn *)obj;
2014 gfloat *ref = NULL;
2015
2016 if (this)
2017 ref = this->data_ref;
2018
2019 return ref;
2020 }
2021
plot_math_channel_assert_channels(PlotChn * obj,bool assert)2022 static void plot_math_channel_assert_channels(PlotChn *obj, bool assert)
2023 {
2024 PlotMathChn *this = (PlotMathChn *)obj;
2025
2026 if (this && this->iio_channels)
2027 g_slist_foreach(this->iio_channels,
2028 set_channel_shadow_of_enabled,
2029 (gpointer)assert);
2030 }
2031
plot_math_channel_destroy(PlotChn * obj)2032 static void plot_math_channel_destroy(PlotChn *obj)
2033 {
2034 PlotMathChn *this = (PlotMathChn *)obj;
2035
2036 if (!this)
2037 return;
2038
2039 if (this->base.name)
2040 g_free(this->base.name);
2041
2042 if (this->base.parent_name)
2043 g_free(this->base.parent_name);
2044
2045 if (this->iio_channels)
2046 g_slist_free(this->iio_channels);
2047
2048 if (this->iio_channels_data)
2049 free(this->iio_channels_data);
2050
2051 if (this->iio_device_name)
2052 g_free(this->iio_device_name);
2053
2054 if (this->txt_math_expression)
2055 g_free(this->txt_math_expression);
2056
2057 math_expression_close_lib_handler(this->math_lib_handler);
2058
2059 free(this);
2060 }
2061
2062
2063
expand_iter(OscPlot * plot,GtkTreeIter * iter,gboolean expand)2064 static void expand_iter(OscPlot *plot, GtkTreeIter *iter, gboolean expand)
2065 {
2066 GtkTreeView *tree = GTK_TREE_VIEW(plot->priv->channel_list_view);
2067 GtkTreeModel *model;
2068 GtkTreePath *path;
2069
2070 model = gtk_tree_view_get_model(tree);
2071 path = gtk_tree_model_get_path(model, iter);
2072 if (expand)
2073 gtk_tree_view_expand_row(tree, path, FALSE);
2074 else
2075 gtk_tree_view_collapse_row(tree, path);
2076 }
2077
foreach_device_iter(GtkTreeView * treeview,void (* tree_iter_func)(GtkTreeModel * model,GtkTreeIter * iter,void * user_data),void * user_data)2078 static void foreach_device_iter(GtkTreeView *treeview,
2079 void (*tree_iter_func)(GtkTreeModel *model, GtkTreeIter *iter, void *user_data),
2080 void *user_data)
2081 {
2082 GtkTreeIter iter;
2083 GtkTreeModel *model;
2084 gboolean next_iter;
2085
2086 model = gtk_tree_view_get_model(treeview);
2087 if (!gtk_tree_model_get_iter_first(model, &iter))
2088 return;
2089
2090 next_iter = true;
2091 while (next_iter) {
2092 tree_iter_func(model, &iter, user_data);
2093 next_iter = gtk_tree_model_iter_next(model, &iter);
2094 }
2095 }
2096
foreach_channel_iter_of_device(GtkTreeView * treeview,const char * name,void (* tree_iter_func)(GtkTreeModel * model,GtkTreeIter * data,void * user_data),void * user_data)2097 static void foreach_channel_iter_of_device(GtkTreeView *treeview, const char *name,
2098 void (*tree_iter_func)(GtkTreeModel *model, GtkTreeIter *data, void *user_data),
2099 void *user_data)
2100 {
2101 GtkTreeIter iter;
2102 GtkTreeIter child_iter;
2103 GtkTreeModel *model;
2104 gboolean next_iter = true;
2105 gboolean next_child_iter;
2106 char *str_device;
2107
2108 model = gtk_tree_view_get_model(treeview);
2109 if (!gtk_tree_model_get_iter_first(model, &iter))
2110 return;
2111
2112 do {
2113 gtk_tree_model_get(model, &iter, ELEMENT_NAME, &str_device, -1);
2114 if (!strcmp(name, str_device))
2115 break;
2116 next_iter = gtk_tree_model_iter_next(model, &iter);
2117 } while (next_iter);
2118 if (!next_iter)
2119 return;
2120
2121 next_child_iter = gtk_tree_model_iter_children(model, &child_iter, &iter);
2122 while (next_child_iter) {
2123 tree_iter_func(model, &child_iter, user_data);
2124 next_child_iter = gtk_tree_model_iter_next(model, &child_iter);
2125 }
2126 }
2127
set_may_be_enabled_bit(GtkTreeModel * model,GtkTreeIter * iter,void * user_data)2128 static void set_may_be_enabled_bit(GtkTreeModel *model,
2129 GtkTreeIter *iter, void *user_data)
2130 {
2131 gboolean enabled;
2132 struct iio_channel *chn;
2133 struct extra_info *info;
2134
2135 gtk_tree_model_get(model, iter, ELEMENT_REFERENCE, &chn,
2136 CHANNEL_ACTIVE, &enabled, -1);
2137 info = iio_channel_get_data(chn);
2138 info->may_be_enabled = enabled;
2139 }
2140
check_valid_setup_of_device(OscPlot * plot,const char * name)2141 static gboolean check_valid_setup_of_device(OscPlot *plot, const char *name)
2142 {
2143 OscPlotPrivate *priv = plot->priv;
2144 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
2145 int plot_type;
2146 int num_enabled;
2147 struct iio_device *dev;
2148 unsigned int nb_channels = num_of_channels_of_device(treeview, name);
2149 unsigned enabled_channels_mask;
2150
2151 GtkTreeModel *model;
2152 GtkTreeIter iter;
2153 gboolean device_enabled;
2154
2155 plot_type = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain));
2156
2157 model = gtk_tree_view_get_model(treeview);
2158 get_iter_by_name(treeview, &iter, name, NULL);
2159 gtk_tree_model_get(model, &iter,
2160 ELEMENT_REFERENCE, &dev,
2161 DEVICE_ACTIVE, &device_enabled, -1);
2162 if (!device_enabled && plot_type != TIME_PLOT)
2163 return true;
2164
2165 num_enabled = enabled_channels_of_device(treeview, name, &enabled_channels_mask);
2166
2167 /* Basic validation rules */
2168 if (plot_type == FFT_PLOT) {
2169 if (num_enabled != 4 && num_enabled != 2 && num_enabled != 1) {
2170 gtk_widget_set_tooltip_text(priv->capture_button,
2171 "FFT needs 4 or 2 or less channels");
2172 return false;
2173 }
2174 } else if (plot_type == XY_PLOT) {
2175 if (num_enabled != 2) {
2176 gtk_widget_set_tooltip_text(priv->capture_button,
2177 "Constellation requires only 2 channels");
2178 return false;
2179 }
2180 } else if (plot_type == TIME_PLOT) {
2181 if (enabled_channels_count(plot) == 0) {
2182 gtk_widget_set_tooltip_text(priv->capture_button,
2183 "Time Domain needs at least one channel");
2184 return false;
2185 } else if (dev && !dma_valid_selection(name, enabled_channels_mask | global_enabled_channels_mask(dev), nb_channels)) {
2186 gtk_widget_set_tooltip_text(priv->capture_button,
2187 "Channel selection not supported");
2188 return false;
2189 }
2190 } else if (plot_type == XCORR_PLOT) {
2191 if (enabled_channels_count(plot) != 4) {
2192 gtk_widget_set_tooltip_text(priv->capture_button,
2193 "Correlation requires 4 channels");
2194 return false;
2195 }
2196 }
2197
2198 /* No additional checking is needed for non iio devices */
2199 if (!dev)
2200 return TRUE;
2201
2202 char warning_text[100];
2203
2204 /* Check if devices that need a trigger have one and it's configured */
2205 const struct iio_device *trigger;
2206 int ret;
2207
2208 ret = osc_iio_device_get_trigger(dev, &trigger);
2209 if (ret == 0 && trigger == NULL && num_enabled > 0) {
2210 snprintf(warning_text, sizeof(warning_text),
2211 "Device %s needs an impulse generator", name);
2212 gtk_widget_set_tooltip_text(priv->capture_button, warning_text);
2213 return false;
2214 }
2215
2216 /* Additional validation rules provided by the plugin of the device */
2217 if (num_enabled != 2 || plot_type == TIME_PLOT)
2218 return true;
2219
2220 bool valid_comb;
2221 const char *ch_names[2];
2222
2223 plugin_setup_validation_fct = find_setup_check_fct_by_devname(name);
2224 if (plugin_setup_validation_fct) {
2225 foreach_channel_iter_of_device(GTK_TREE_VIEW(priv->channel_list_view),
2226 name, *set_may_be_enabled_bit, NULL);
2227 valid_comb = (*plugin_setup_validation_fct)(dev, ch_names);
2228 if (!valid_comb) {
2229 snprintf(warning_text, sizeof(warning_text),
2230 "Combination between %s and %s is invalid", ch_names[0], ch_names[1]);
2231 gtk_widget_set_tooltip_text(priv->capture_button, warning_text);
2232 return false;
2233 }
2234 }
2235
2236 if (num_enabled && plot_type == FFT_PLOT && !gtk_toggle_tool_button_get_active((GtkToggleToolButton *)priv->capture_button)) {
2237 GtkListStore *liststore;
2238 int i, j, k = 0, m = 0;
2239 char buf[256];
2240
2241 j = comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));
2242 liststore = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(priv->fft_size_widget)));
2243 gtk_list_store_clear(liststore);
2244
2245 i = 4194304;
2246 /* make sure we don't exceed DMA, 2^22 bytes (not samples) */
2247 while (i >= 64) {
2248 if (i * num_enabled * 2 <= 4194304) {
2249 sprintf(buf, "%i", i);
2250 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(priv->fft_size_widget), buf);
2251
2252 if (i == j)
2253 m = k;
2254 k++;
2255 }
2256 i = i / 2;
2257 }
2258 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->fft_size_widget), m);
2259 }
2260
2261 return true;
2262 }
2263
check_valid_setup_of_all_devices(OscPlot * plot)2264 static gboolean check_valid_setup_of_all_devices(OscPlot *plot)
2265 {
2266 OscPlotPrivate *priv = plot->priv;
2267 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
2268 GtkTreeModel *model;
2269 GtkTreeIter iter;
2270 gboolean next_iter;
2271 gchar *dev_name;
2272 gboolean valid;
2273
2274 model = gtk_tree_view_get_model(treeview);
2275 next_iter = gtk_tree_model_get_iter_first(model, &iter);
2276 while (next_iter) {
2277 gtk_tree_model_get(model, &iter, ELEMENT_NAME, &dev_name, -1);
2278 valid = check_valid_setup_of_device(plot, dev_name);
2279 g_free(dev_name);
2280 if (!valid)
2281 return false;
2282 next_iter = gtk_tree_model_iter_next(model, &iter);
2283 }
2284
2285 return true;
2286 }
2287
check_valid_setup(OscPlot * plot)2288 static gboolean check_valid_setup(OscPlot *plot)
2289 {
2290 OscPlotPrivate *priv = plot->priv;
2291
2292 if (!check_valid_setup_of_all_devices(plot))
2293 goto capture_button_err;
2294
2295 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(priv->capture_button)))
2296 g_object_set(priv->capture_button, "stock-id", "gtk-stop", NULL);
2297 else
2298 g_object_set(priv->capture_button, "stock-id", "gtk-media-play", NULL);
2299 gtk_widget_set_tooltip_text(priv->capture_button, "Capture / Stop");
2300
2301 g_object_set(priv->ss_button, "stock-id", "gtk-media-next", NULL);
2302 gtk_widget_set_tooltip_text(priv->ss_button, "Single Shot Capture");
2303
2304 if (!priv->capture_button_hid) {
2305 priv->capture_button_hid = g_signal_connect(priv->capture_button, "toggled",
2306 G_CALLBACK(capture_button_clicked_cb), plot);
2307 priv->deactivate_capture_btn_flag = 0;
2308 }
2309
2310 return true;
2311
2312 capture_button_err:
2313 g_object_set(priv->capture_button, "stock-id", "gtk-dialog-warning", NULL);
2314 g_object_set(priv->ss_button, "stock-id", "gtk-dialog-warning", NULL);
2315 if (priv->capture_button_hid) {
2316 g_signal_handler_disconnect(priv->capture_button, priv->capture_button_hid);
2317 priv->deactivate_capture_btn_flag = 1;
2318 }
2319 priv->capture_button_hid = 0;
2320
2321 return false;
2322 }
2323
plot_channel_check_name_exists(OscPlot * plot,const char * name,PlotChn * struct_to_skip)2324 static bool plot_channel_check_name_exists(OscPlot *plot, const char *name,
2325 PlotChn *struct_to_skip)
2326 {
2327 OscPlotPrivate *priv = plot->priv;
2328 GSList *node;
2329 PlotChn *settings;
2330 bool name_exists;
2331
2332 name_exists = false;
2333 for (node = priv->ch_settings_list; node; node = g_slist_next(node)) {
2334 settings = node->data;
2335 if (settings == struct_to_skip)
2336 continue;
2337 if (!settings->name) {
2338 fprintf(stderr, "Error in %s: Channel name is null", __func__);
2339 continue;
2340 }
2341 if (!strcmp(name, settings->name)) {
2342 name_exists = true;
2343 break;
2344 }
2345 }
2346
2347 return name_exists;
2348 }
2349
plot_get_sample_count_of_device(OscPlot * plot,const char * device)2350 static int plot_get_sample_count_of_device(OscPlot *plot, const char *device)
2351 {
2352 OscPlotPrivate *priv;
2353 struct iio_context *ctx;
2354 struct iio_device *iio_dev;
2355 struct extra_dev_info *dev_info;
2356 gdouble freq;
2357 int count = -1;
2358
2359 if (!plot || !device)
2360 return count;
2361
2362 priv = plot->priv;
2363 if (!priv)
2364 return count;
2365
2366 ctx = priv->ctx;
2367 if (!ctx)
2368 return count;
2369
2370 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units))) {
2371 case 0:
2372 count = (int)osc_plot_get_sample_count(plot);
2373 break;
2374 case 1:
2375 iio_dev = iio_context_find_device(ctx, device);
2376 if (!iio_dev)
2377 break;
2378
2379 dev_info = iio_device_get_data(iio_dev);
2380 if (!dev_info)
2381 break;
2382
2383 freq = dev_info->adc_freq * prefix2scale(dev_info->adc_scale);
2384 count = (int)round((gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget)) *
2385 freq) / pow(10.0, 6));
2386 break;
2387 }
2388
2389 return count;
2390 }
2391
plot_get_sample_count_for_transform(OscPlot * plot,Transform * transform)2392 static int plot_get_sample_count_for_transform(OscPlot *plot, Transform *transform)
2393 {
2394 OscPlotPrivate *priv = plot->priv;
2395 struct iio_device *iio_dev = transform_get_device_parent(transform);
2396
2397 if (!iio_dev)
2398 iio_dev = priv->current_device;
2399
2400 return plot_get_sample_count_of_device(plot,
2401 iio_device_get_name(iio_dev) ?:
2402 iio_device_get_id(iio_dev));
2403 }
2404
notebook_info_set_page_visibility(GtkNotebook * nb,int page,bool visbl)2405 static void notebook_info_set_page_visibility(GtkNotebook *nb, int page, bool visbl)
2406 {
2407 GtkWidget *wpage = gtk_notebook_get_nth_page(nb, page);
2408 gtk_widget_set_visible(wpage, visbl);
2409 }
2410
transform_get_device_parent(Transform * transform)2411 static struct iio_device * transform_get_device_parent(Transform *transform)
2412 {
2413 struct iio_device *iio_dev = NULL;
2414 PlotChn *plot_ch;
2415
2416 if (!transform || !transform->plot_channels)
2417 return NULL;
2418
2419 plot_ch = transform->plot_channels->data;
2420 if (plot_ch)
2421 iio_dev = plot_ch->get_iio_parent(plot_ch);
2422
2423 return iio_dev;
2424 }
2425
update_transform_settings(OscPlot * plot,Transform * transform)2426 static void update_transform_settings(OscPlot *plot, Transform *transform)
2427 {
2428 OscPlotPrivate *priv = plot->priv;
2429 unsigned i;
2430 int plot_type;
2431
2432 plot_type = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain));
2433 if (plot_type == FFT_PLOT) {
2434 FFT_SETTINGS(transform)->fft_size = comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));
2435 FFT_SETTINGS(transform)->fft_win = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->fft_win_widget));
2436 FFT_SETTINGS(transform)->fft_avg = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_avg_widget));
2437 FFT_SETTINGS(transform)->fft_pwr_off = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_pwr_offset_widget));
2438 FFT_SETTINGS(transform)->fft_alg_data.cached_fft_size = -1;
2439 FFT_SETTINGS(transform)->fft_alg_data.cached_num_active_channels = -1;
2440 FFT_SETTINGS(transform)->fft_alg_data.num_active_channels = g_slist_length(transform->plot_channels);
2441 FFT_SETTINGS(transform)->markers = NULL;
2442 FFT_SETTINGS(transform)->markers_copy = NULL;
2443 FFT_SETTINGS(transform)->marker_lock = NULL;
2444 FFT_SETTINGS(transform)->marker_type = NULL;
2445 } else if (plot_type == TIME_PLOT) {
2446 int dev_samples = plot_get_sample_count_for_transform(plot, transform);
2447 if (dev_samples < 0)
2448 return;
2449
2450 TIME_SETTINGS(transform)->num_samples = dev_samples;
2451 if (PLOT_CHN(transform->plot_channels->data)->type == PLOT_IIO_CHANNEL) {
2452 PlotIioChn *set;
2453
2454 set = (PlotIioChn *)transform->plot_channels->data;
2455 TIME_SETTINGS(transform)->apply_inverse_funct = set->apply_inverse_funct;
2456 TIME_SETTINGS(transform)->apply_multiply_funct = set->apply_multiply_funct;
2457 TIME_SETTINGS(transform)->apply_add_funct = set->apply_add_funct;
2458 TIME_SETTINGS(transform)->multiply_value = set->multiply_value;
2459 TIME_SETTINGS(transform)->add_value = set->add_value;
2460 TIME_SETTINGS(transform)->max_x_axis = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget));
2461 }
2462 } else if (plot_type == XY_PLOT){
2463 CONSTELLATION_SETTINGS(transform)->num_samples = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget));
2464 } else if (plot_type == XCORR_PLOT){
2465 int dev_samples = plot_get_sample_count_for_transform(plot, transform);
2466 if (dev_samples < 0)
2467 return;
2468
2469 XCORR_SETTINGS(transform)->num_samples = dev_samples;
2470 XCORR_SETTINGS(transform)->avg = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_avg_widget));
2471 XCORR_SETTINGS(transform)->revert_xcorr = 0;
2472 XCORR_SETTINGS(transform)->signal_a = NULL;
2473 XCORR_SETTINGS(transform)->signal_b = NULL;
2474 XCORR_SETTINGS(transform)->xcorr_data = NULL;
2475 XCORR_SETTINGS(transform)->markers = NULL;
2476 XCORR_SETTINGS(transform)->markers_copy = NULL;
2477 XCORR_SETTINGS(transform)->marker_lock = NULL;
2478 XCORR_SETTINGS(transform)->marker_type = NULL;
2479 XCORR_SETTINGS(transform)->max_x_axis = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget));
2480 } else if (plot_type == SPECTRUM_PLOT) {
2481 FREQ_SPECTRUM_SETTINGS(transform)->ffts_alg_data = calloc(sizeof(struct _fft_alg_data), priv->fft_count);
2482 FREQ_SPECTRUM_SETTINGS(transform)->fft_count = priv->fft_count;
2483 FREQ_SPECTRUM_SETTINGS(transform)->freq_sweep_start = priv->start_freq + priv->filter_bw / 2;
2484 FREQ_SPECTRUM_SETTINGS(transform)->filter_bandwidth = priv->filter_bw;
2485 FREQ_SPECTRUM_SETTINGS(transform)->fft_size = comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));
2486 FREQ_SPECTRUM_SETTINGS(transform)->fft_avg = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_avg_widget));
2487 FREQ_SPECTRUM_SETTINGS(transform)->fft_pwr_off = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_pwr_offset_widget));
2488 FREQ_SPECTRUM_SETTINGS(transform)->maxXaxis = malloc(sizeof(unsigned int) * (MAX_MARKERS + 1));
2489 FREQ_SPECTRUM_SETTINGS(transform)->maxYaxis = malloc(sizeof(unsigned int) * (MAX_MARKERS + 1));
2490 for (i = 0; i < priv->fft_count; i++) {
2491 FREQ_SPECTRUM_SETTINGS(transform)->ffts_alg_data[i].cached_fft_size = -1;
2492 FREQ_SPECTRUM_SETTINGS(transform)->ffts_alg_data[i].cached_num_active_channels = -1;
2493 FREQ_SPECTRUM_SETTINGS(transform)->ffts_alg_data[i].num_active_channels = g_slist_length(transform->plot_channels);
2494 }
2495 }
2496 }
2497
add_transform_to_list(OscPlot * plot,int tr_type,GSList * channels)2498 static Transform* add_transform_to_list(OscPlot *plot, int tr_type, GSList *channels)
2499 {
2500 OscPlotPrivate *priv = plot->priv;
2501 Transform *transform;
2502 struct _time_settings *time_settings;
2503 struct _fft_settings *fft_settings;
2504 struct _constellation_settings *constellation_settings;
2505 struct _cross_correlation_settings *xcross_settings;
2506 struct _freq_spectrum_settings *freq_spectrum_settings;
2507 GSList *node;
2508
2509 transform = Transform_new(tr_type);
2510 transform->graph_color = &color_graph[priv->transform_list->size];
2511 transform->plot_channels = g_slist_copy(channels);
2512 transform->plot_channels_type = PLOT_CHN(channels->data)->type;
2513
2514 /* Enable iio channels used by the transform */
2515 for (node = channels; node; node = g_slist_next(node)) {
2516 PlotChn *plot_ch;
2517
2518 plot_ch = node->data;
2519 if (plot_ch)
2520 plot_ch->assert_used_iio_channels(plot_ch, true);
2521 }
2522
2523 switch (tr_type) {
2524 case TIME_TRANSFORM:
2525 Transform_attach_function(transform, time_transform_function);
2526 time_settings = (struct _time_settings *)calloc(sizeof(struct _time_settings), 1);
2527 Transform_attach_settings(transform, time_settings);
2528 transform->graph_color = &PLOT_CHN(channels->data)->graph_color;
2529 break;
2530 case FFT_TRANSFORM:
2531 Transform_attach_function(transform, fft_transform_function);
2532 fft_settings = (struct _fft_settings *)calloc(sizeof(struct _fft_settings), 1);
2533 Transform_attach_settings(transform, fft_settings);
2534 break;
2535 case CONSTELLATION_TRANSFORM:
2536 Transform_attach_function(transform, constellation_transform_function);
2537 constellation_settings = (struct _constellation_settings *)calloc(sizeof(struct _constellation_settings), 1);
2538 Transform_attach_settings(transform, constellation_settings);
2539 break;
2540 case COMPLEX_FFT_TRANSFORM:
2541 Transform_attach_function(transform, fft_transform_function);
2542 fft_settings = (struct _fft_settings *)calloc(sizeof(struct _fft_settings), 1);
2543 Transform_attach_settings(transform, fft_settings);
2544 break;
2545 case CROSS_CORRELATION_TRANSFORM:
2546 Transform_attach_function(transform, cross_correlation_transform_function);
2547 xcross_settings = (struct _cross_correlation_settings *)calloc(sizeof(struct _cross_correlation_settings), 1);
2548 Transform_attach_settings(transform, xcross_settings);
2549 break;
2550 case FREQ_SPECTRUM_TRANSFORM:
2551 Transform_attach_function(transform, freq_spectrum_transform_function);
2552 freq_spectrum_settings = (struct _freq_spectrum_settings *)calloc(sizeof(struct _freq_spectrum_settings), 1);
2553 Transform_attach_settings(transform, freq_spectrum_settings);
2554 break;
2555 default:
2556 fprintf(stderr, "Invalid transform\n");
2557 return NULL;
2558 }
2559 TrList_add_transform(priv->transform_list, transform);
2560 update_transform_settings(plot, transform);
2561
2562 priv->active_transform_type = tr_type;
2563 priv->current_device = transform_get_device_parent(transform);
2564
2565 return transform;
2566 }
2567
iio_channels_get_data(struct iio_context * ctx,const char * device_name)2568 static gfloat *** iio_channels_get_data(struct iio_context *ctx,
2569 const char *device_name)
2570 {
2571 gfloat ***data;
2572 struct iio_device *iio_dev;
2573 struct iio_channel *iio_chn;
2574 int nb_channels;
2575 struct extra_info *ch_info;
2576 int i;
2577
2578 if (!device_name)
2579 return NULL;
2580
2581 iio_dev = iio_context_find_device(ctx, device_name);
2582 if (!iio_dev) {
2583 fprintf(stderr, "Could not find device %s in %s\n",
2584 device_name, __func__);
2585 return NULL;
2586 }
2587 nb_channels = iio_device_get_channels_count(iio_dev);
2588 data = malloc(sizeof(double**) * nb_channels);
2589 for (i = 0; i < nb_channels; i++) {
2590 iio_chn = iio_device_get_channel(iio_dev, i);
2591 ch_info = iio_channel_get_data(iio_chn);
2592 data[i] = &ch_info->data_ref;
2593 }
2594
2595 return data;
2596 }
2597
set_channel_shadow_of_enabled(gpointer data,gpointer user_data)2598 static void set_channel_shadow_of_enabled(gpointer data, gpointer user_data)
2599 {
2600 struct iio_channel *chn = data;
2601 struct extra_info *ch_info;
2602
2603 if (!chn)
2604 return;
2605
2606 ch_info = iio_channel_get_data(chn);
2607 ch_info->shadow_of_enabled += ((bool)user_data) ? 1 : -1;
2608 }
2609
remove_transform_from_list(OscPlot * plot,Transform * tr)2610 static void remove_transform_from_list(OscPlot *plot, Transform *tr)
2611 {
2612 OscPlotPrivate *priv = plot->priv;
2613 TrList *list = priv->transform_list;
2614
2615 if (tr->has_the_marker)
2616 priv->tr_with_marker = NULL;
2617
2618 transform_remove_own_markers(tr);
2619 if (tr->type_id == FREQ_SPECTRUM_TRANSFORM) {
2620 free(FREQ_SPECTRUM_SETTINGS(tr)->ffts_alg_data);
2621 free(FREQ_SPECTRUM_SETTINGS(tr)->maxXaxis);
2622 free(FREQ_SPECTRUM_SETTINGS(tr)->maxYaxis);
2623 }
2624 TrList_remove_transform(list, tr);
2625 Transform_destroy(tr);
2626 if (list->size == 0) {
2627 priv->active_transform_type = NO_TRANSFORM_TYPE;
2628 }
2629 }
2630
markers_init(OscPlot * plot)2631 static void markers_init(OscPlot *plot)
2632 {
2633 OscPlotPrivate *priv = plot->priv;
2634 GtkDatabox *databox = GTK_DATABOX(plot->priv->databox);
2635 struct marker_type *markers = priv->markers;
2636 const char *empty_text = " ";
2637 char buf[10];
2638 int i;
2639
2640 /* Clear marker information text box */
2641 if (priv->tbuf)
2642 gtk_text_buffer_set_text(priv->tbuf, empty_text, -1);
2643
2644 priv->markers_copy = NULL;
2645
2646 /* Don't go any further with the init when in TIME or XY domains*/
2647 if (priv->active_transform_type == TIME_TRANSFORM ||
2648 priv->active_transform_type == CONSTELLATION_TRANSFORM)
2649 return;
2650
2651 /* Ensure that Marker Image is applied only to Complex FFT Transforms */
2652 if (priv->active_transform_type == FFT_TRANSFORM && priv->marker_type == MARKER_IMAGE)
2653 priv->marker_type = MARKER_OFF;
2654
2655 for (i = 0; i <= MAX_MARKERS; i++) {
2656 markers[i].x = 0.0f;
2657 markers[i].y = 0.0f;
2658 if (markers[i].graph)
2659 g_object_unref(markers[i].graph);
2660 markers[i].graph = gtk_databox_markers_new(1, &markers[i].x, &markers[i].y, &color_marker,
2661 10, GTK_DATABOX_MARKERS_TRIANGLE);
2662 gtk_databox_graph_add(databox, markers[i].graph);
2663 gtk_databox_graph_set_hide(markers[i].graph, true);
2664 sprintf(buf, "?%i", i);
2665 gtk_databox_markers_set_label(GTK_DATABOX_MARKERS(markers[i].graph),
2666 0, GTK_DATABOX_MARKERS_TEXT_N, buf, FALSE);
2667 if (priv->marker_type == MARKER_OFF)
2668 gtk_databox_graph_set_hide(markers[i].graph, TRUE);
2669 else
2670 gtk_databox_graph_set_hide(markers[i].graph, !markers[i].active);
2671 }
2672 if (priv->marker_type != MARKER_OFF)
2673 set_marker_labels(plot, NULL, priv->marker_type);
2674 }
2675
transform_add_plot_markers(OscPlot * plot,Transform * transform)2676 static void transform_add_plot_markers(OscPlot *plot, Transform *transform)
2677 {
2678 OscPlotPrivate *priv = plot->priv;
2679
2680 transform->has_the_marker = true;
2681 priv->tr_with_marker = transform;
2682 if (priv->active_transform_type == FFT_TRANSFORM ||
2683 priv->active_transform_type == COMPLEX_FFT_TRANSFORM) {
2684 FFT_SETTINGS(transform)->markers = priv->markers;
2685 FFT_SETTINGS(transform)->markers_copy = &priv->markers_copy;
2686 FFT_SETTINGS(transform)->marker_type = &priv->marker_type;
2687 FFT_SETTINGS(transform)->marker_lock = &priv->g_marker_copy_lock;
2688 } else if (priv->active_transform_type == CROSS_CORRELATION_TRANSFORM) {
2689 XCORR_SETTINGS(transform)->markers = priv->markers;
2690 XCORR_SETTINGS(transform)->markers_copy = &priv->markers_copy;
2691 XCORR_SETTINGS(transform)->marker_type = &priv->marker_type;
2692 XCORR_SETTINGS(transform)->marker_lock = &priv->g_marker_copy_lock;
2693 } else if (priv->active_transform_type == FREQ_SPECTRUM_TRANSFORM) {
2694 FREQ_SPECTRUM_SETTINGS(transform)->markers = priv->markers;
2695 FREQ_SPECTRUM_SETTINGS(transform)->markers_copy = &priv->markers_copy;
2696 FREQ_SPECTRUM_SETTINGS(transform)->marker_type = &priv->marker_type;
2697 FREQ_SPECTRUM_SETTINGS(transform)->marker_lock = &priv->g_marker_copy_lock;
2698 }
2699 }
2700
transform_add_own_markers(OscPlot * plot,Transform * transform)2701 static void transform_add_own_markers(OscPlot *plot, Transform *transform)
2702 {
2703 OscPlotPrivate *priv = plot->priv;
2704 struct marker_type *markers;
2705 int i;
2706
2707 markers = calloc(sizeof(struct marker_type), MAX_MARKERS + 2);
2708 if (!markers) {
2709 fprintf(stderr,
2710 "Error: could not alloc memory for markers in %s\n",
2711 __func__);
2712 return;
2713 }
2714
2715 for (i = 0; i < MAX_MARKERS; i++)
2716 markers[i].active = (i <= 4);
2717
2718 if (transform->type_id == FFT_TRANSFORM ||
2719 transform->type_id == COMPLEX_FFT_TRANSFORM) {
2720 FFT_SETTINGS(transform)->markers = markers;
2721 FFT_SETTINGS(transform)->marker_type = FFT_SETTINGS(
2722 priv->tr_with_marker)->marker_type;
2723 } else if (transform->type_id == CROSS_CORRELATION_TRANSFORM) {
2724 XCORR_SETTINGS(transform)->markers = markers;
2725 XCORR_SETTINGS(transform)->marker_type = XCORR_SETTINGS(
2726 priv->tr_with_marker)->marker_type;
2727 }
2728 }
2729
transform_remove_own_markers(Transform * transform)2730 static void transform_remove_own_markers(Transform *transform)
2731 {
2732 struct marker_type *markers;
2733
2734 if (transform->has_the_marker)
2735 return;
2736
2737 if (transform->type_id == FFT_TRANSFORM ||
2738 transform->type_id == COMPLEX_FFT_TRANSFORM) {
2739 markers = FFT_SETTINGS(transform)->markers;
2740 } else if (transform->type_id == CROSS_CORRELATION_TRANSFORM) {
2741 markers = XCORR_SETTINGS(transform)->markers;
2742 } else {
2743 return;
2744 }
2745
2746 if (markers)
2747 free(markers);
2748 }
2749
plot_channels_update(OscPlot * plot)2750 static void plot_channels_update(OscPlot *plot)
2751 {
2752 OscPlotPrivate *priv;
2753 GSList *node;
2754 PlotChn *ch;
2755 PlotMathChn *mch;
2756 int num_samples;
2757
2758 g_return_if_fail(plot);
2759
2760 priv = plot->priv;
2761 for (node = priv->ch_settings_list;
2762 node; node = g_slist_next(node)) {
2763 ch = node->data;
2764 if (ch->type != PLOT_MATH_CHANNEL)
2765 continue;
2766
2767 mch = (PlotMathChn *)ch;
2768 num_samples = plot_get_sample_count_of_device(plot,
2769 mch->iio_device_name);
2770 if (num_samples < 0)
2771 num_samples = osc_plot_get_sample_count(plot);
2772 mch->data_ref = realloc(mch->data_ref,
2773 sizeof(gfloat) * num_samples);
2774 }
2775 }
2776
collect_parameters_from_plot(OscPlot * plot)2777 static void collect_parameters_from_plot(OscPlot *plot)
2778 {
2779 OscPlotPrivate *priv = plot->priv;
2780 struct iio_context *ctx = priv->ctx;
2781 struct plot_params *prms;
2782 GSList *list;
2783 unsigned int i;
2784
2785 for (i = 0; i < iio_context_get_devices_count(ctx); i++) {
2786 struct iio_device *dev = iio_context_get_device(ctx, i);
2787 struct extra_dev_info *info = iio_device_get_data(dev);
2788 const char *dev_name = iio_device_get_name(dev) ?: iio_device_get_id(dev);
2789
2790 if (info->input_device == false)
2791 continue;
2792
2793 prms = malloc(sizeof(struct plot_params));
2794 prms->plot_id = priv->object_id;
2795 prms->sample_count = plot_get_sample_count_of_device(plot, dev_name);
2796 list = info->plots_sample_counts;
2797 list = g_slist_prepend(list, prms);
2798 info->plots_sample_counts = list;
2799 }
2800 }
2801
dispose_parameters_from_plot(OscPlot * plot)2802 static void dispose_parameters_from_plot(OscPlot *plot)
2803 {
2804 OscPlotPrivate *priv = plot->priv;
2805 struct iio_context *ctx = priv->ctx;
2806 struct plot_params *prms;
2807 GSList *node;
2808 GSList *del_link = NULL;
2809 unsigned int i;
2810
2811 for (i = 0; i < iio_context_get_devices_count(ctx); i++) {
2812 struct iio_device *dev = iio_context_get_device(ctx, i);
2813 struct extra_dev_info *info = iio_device_get_data(dev);
2814 GSList *list = info->plots_sample_counts;
2815
2816 if (info->input_device == false)
2817 continue;
2818
2819 for (node = list; node; node = g_slist_next(node)) {
2820 prms = node->data;
2821 if (prms->plot_id == priv->object_id) {
2822 del_link = node;
2823 break;
2824 }
2825 }
2826 if (del_link) {
2827 list = g_slist_delete_link(list, del_link);
2828 info->plots_sample_counts = list;
2829 }
2830 }
2831 }
2832
prefix2scale(char adc_scale)2833 static gdouble prefix2scale (char adc_scale)
2834 {
2835 switch (adc_scale) {
2836 case 'M':
2837 return 1000000.0;
2838 case 'k':
2839 return 1000.0;
2840 default:
2841 return 1.0;
2842 break;
2843 }
2844 }
2845
markers_phase_diff_show(OscPlotPrivate * priv)2846 static void markers_phase_diff_show(OscPlotPrivate *priv)
2847 {
2848 static float avg[MAX_MARKERS] = {NAN};
2849
2850 GtkTextIter iter;
2851 char text[256];
2852 int m;
2853 struct marker_type *trA_markers;
2854 struct marker_type *trB_markers;
2855 float angle_diff, lead_lag;
2856 float filter, angle;
2857
2858 gtk_text_buffer_set_text(priv->phase_buf, "", -1);
2859 gtk_text_buffer_get_iter_at_line(priv->phase_buf, &iter, 1);
2860
2861 if (priv->active_transform_type == COMPLEX_FFT_TRANSFORM &&
2862 priv->transform_list->size == 2) {
2863 trA_markers = FFT_SETTINGS(
2864 priv->transform_list->transforms[0])->markers;
2865 trB_markers = FFT_SETTINGS(
2866 priv->transform_list->transforms[1])->markers;
2867
2868 filter = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_avg_widget));
2869 if (!filter)
2870 filter = 1;
2871 filter = 1.0 / filter;
2872
2873 if (MAX_MARKERS && priv->marker_type != MARKER_OFF) {
2874 for (m = 0; m < MAX_MARKERS &&
2875 trA_markers[m].active; m++) {
2876
2877 /* find out the quadrant
2878 * since carg() returns something from [-pi, +pi], use that.
2879 * this handles reflex angles up to [-2*pi, +2*pi]
2880 */
2881 lead_lag = (cargf(trA_markers[m].vector) - cargf(trB_markers[m].vector)) * 180 / M_PI;
2882
2883 /* [-2*pi, +2*pi] is kind of silly
2884 * move things to [-pi, +pi]
2885 */
2886 if (lead_lag > 180)
2887 lead_lag = lead_lag - 360;
2888 if (lead_lag < -180)
2889 lead_lag = 360 + lead_lag;
2890
2891 if (isnan(avg[m]))
2892 avg[m] = lead_lag;
2893
2894 /* Cosine law, answers are [0, +pi] */
2895 if (cabsf(trA_markers[m].vector) == 0.0 || cabsf(trB_markers[m].vector) == 0.0) {
2896 /* divide by 0 is nan */
2897 angle_diff = lead_lag;
2898 } else {
2899 angle_diff = acosf((crealf(trA_markers[m].vector) * crealf(trB_markers[m].vector) +
2900 cimagf(trA_markers[m].vector) * cimagf(trB_markers[m].vector)) /
2901 (cabsf(trA_markers[m].vector) * cabsf(trB_markers[m].vector))) * 180 / M_PI;
2902 }
2903
2904 /* put back into the correct quadrant */
2905 if (lead_lag < 0)
2906 angle_diff *= -1.0;
2907
2908 if (lead_lag > 180.0)
2909 angle_diff = 360.0 - angle_diff;
2910
2911 avg[m] = ((1 - filter) * avg[m]) + (filter * angle_diff);
2912
2913 angle = avg[m];
2914 if (angle > 180.0)
2915 angle -= 360.0;
2916 if (angle < -180.0)
2917 angle += 360.0;
2918
2919 trA_markers[m].angle = angle;
2920 trB_markers[m].angle = angle;
2921
2922 snprintf(text, sizeof(text),
2923 "%s: %02.3f° @ %2.3f %cHz %c",
2924 trA_markers[m].label,
2925 angle,
2926 /* lo_freq / markers_scale */ trA_markers[m].x,
2927 /*dev_info->adc_scale */ 'M',
2928 m != (MAX_MARKERS - 1) ? '\n' : '\0');
2929
2930 gtk_text_buffer_insert(priv->phase_buf,
2931 &iter, text, -1);
2932 }
2933 } else {
2934 gtk_text_buffer_set_text(priv->phase_buf,
2935 "No markers active", 17);
2936 }
2937 }
2938 }
2939
draw_marker_values(OscPlotPrivate * priv,Transform * tr)2940 static void draw_marker_values(OscPlotPrivate *priv, Transform *tr)
2941 {
2942 struct iio_device *iio_dev;
2943 struct extra_dev_info *dev_info;
2944 struct marker_type *markers;
2945 GtkTextIter iter;
2946 char text[256];
2947 int markers_scale;
2948 double lo_freq;
2949 int m;
2950
2951 if (tr->type_id == CROSS_CORRELATION_TRANSFORM)
2952 markers = XCORR_SETTINGS(tr)->markers;
2953 else if (tr->type_id == FREQ_SPECTRUM_TRANSFORM)
2954 markers = FREQ_SPECTRUM_SETTINGS(tr)->markers;
2955 else if(tr->type_id == FFT_TRANSFORM)
2956 markers = FFT_SETTINGS(tr)->markers;
2957 else if(tr->type_id == COMPLEX_FFT_TRANSFORM)
2958 markers = FFT_SETTINGS(tr)->markers;
2959 else
2960 return;
2961
2962 if (priv->tbuf == NULL) {
2963 priv->tbuf = gtk_text_buffer_new(NULL);
2964 gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->marker_label), priv->tbuf);
2965 }
2966 iio_dev = transform_get_device_parent(tr);
2967 if (!iio_dev) {
2968 fprintf(stderr,
2969 "Error: Could not find iio device parent for the given transform.%s\n",
2970 __func__);
2971 return;
2972 }
2973 dev_info = iio_device_get_data(iio_dev);
2974
2975 /* Get the LO frequency stored by a iio channel which is used by
2976 * this transform. All channels should have the same lo freq. */
2977 lo_freq = 0.0;
2978 if (tr->plot_channels && g_slist_length(tr->plot_channels)) {
2979 PlotChn *p = PLOT_CHN(tr->plot_channels->data);
2980 if (p->type == PLOT_IIO_CHANNEL) {
2981 struct iio_channel *ch;
2982 struct extra_info *ch_info;
2983 ch = PLOT_IIO_CHN(p)->iio_chn;
2984 if (ch) {
2985 ch_info = iio_channel_get_data(ch);
2986 if (ch_info)
2987 lo_freq = ch_info->lo_freq;
2988 }
2989 }
2990 }
2991
2992 markers_scale = prefix2scale(dev_info->adc_scale);
2993
2994 if (MAX_MARKERS && priv->marker_type != MARKER_OFF) {
2995 for (m = 0; m <= MAX_MARKERS && markers[m].active; m++) {
2996 if (tr->type_id == FFT_TRANSFORM || tr->type_id == COMPLEX_FFT_TRANSFORM) {
2997 sprintf(text, "%s: %2.2f dBFS @ %2.3f %cHz%c",
2998 markers[m].label, markers[m].y,
2999 lo_freq / markers_scale + markers[m].x,
3000 dev_info->adc_scale,
3001 m != MAX_MARKERS ? '\n' : '\0');
3002 } else if (tr->type_id == CROSS_CORRELATION_TRANSFORM) {
3003 sprintf(text, "M%i: %1.6f @ %2.3f%c", m, markers[m].y, markers[m].x,
3004 m != MAX_MARKERS ? '\n' : '\0');
3005 } else if (tr->type_id == FREQ_SPECTRUM_TRANSFORM) {
3006 sprintf(text, "M%i: %1.6f @ %2.3f%c", m, markers[m].y, markers[m].x,
3007 m != MAX_MARKERS ? '\n' : '\0');
3008 }
3009
3010 if (m == 0) {
3011 gtk_text_buffer_set_text(priv->tbuf, text, -1);
3012 gtk_text_buffer_get_iter_at_line(priv->tbuf, &iter, 1);
3013 } else {
3014 gtk_text_buffer_insert(priv->tbuf, &iter, text, -1);
3015 }
3016 }
3017 } else {
3018 gtk_text_buffer_set_text(priv->tbuf, "No markers active", 17);
3019 }
3020 }
3021
device_rx_info_update(OscPlotPrivate * priv)3022 static void device_rx_info_update(OscPlotPrivate *priv)
3023 {
3024 GtkTextIter iter;
3025 char text[256];
3026 unsigned int i, num_devices = 0;
3027
3028 gtk_text_buffer_set_text(priv->devices_buf, "", -1);
3029 gtk_text_buffer_get_iter_at_line(priv->devices_buf, &iter, 1);
3030
3031 if (priv->ctx)
3032 num_devices = iio_context_get_devices_count(priv->ctx);
3033
3034 for (i = 0; i < num_devices; i++) {
3035 struct iio_device *dev = iio_context_get_device(priv->ctx, i);
3036 const char *name = iio_device_get_name(dev) ?: iio_device_get_id(dev);
3037 struct extra_dev_info *dev_info = iio_device_get_data(dev);
3038 double freq, percent, seconds;
3039 char freq_prefix, sec_prefix;
3040
3041 if (dev_info->input_device == false)
3042 continue;
3043
3044 freq = dev_info->adc_freq * prefix2scale(dev_info->adc_scale);
3045 freq = freq / comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));;
3046 seconds = 1 / freq;
3047 percent = seconds * priv->fps * 100.0;
3048 if (freq > 1e6) {
3049 freq = freq / 1e6;
3050 freq_prefix = 'M';
3051 } else if (freq > 1e3) {
3052 freq = freq / 1e3;
3053 freq_prefix = 'k';
3054 } else
3055 freq_prefix = ' ';
3056 if (seconds < 1e-6) {
3057 seconds = seconds * 1e9;
3058 sec_prefix = 'n';
3059 } else if (seconds < 1e-3) {
3060 seconds = seconds * 1e6;
3061 sec_prefix = 'u';
3062 } else {
3063 seconds = seconds * 1e3;
3064 sec_prefix = 'm';
3065 }
3066
3067 snprintf(text, sizeof(text), "%s:\n\tSampleRate: %3.2f %cSPS\n"
3068 "\tHz/Bin: %3.2f %cHz\n"
3069 "\tSweep: %3.2f %cs (%2.2f%%)\n"
3070 "\tFPS: %2.2f\n",
3071 name, dev_info->adc_freq, dev_info->adc_scale,
3072 freq, freq_prefix, seconds, sec_prefix, percent, priv->fps);
3073 gtk_text_buffer_insert(priv->devices_buf, &iter, text, -1);
3074 }
3075 }
3076
call_all_transform_functions(OscPlotPrivate * priv)3077 static bool call_all_transform_functions(OscPlotPrivate *priv)
3078 {
3079 TrList *tr_list = priv->transform_list;
3080 Transform *tr;
3081 bool valid = true;
3082 bool tr_valid;
3083 int i = 0;
3084
3085 if (priv->redraw_function <= 0)
3086 return false;
3087
3088 for (; i < tr_list->size; i++) {
3089 tr = tr_list->transforms[i];
3090 tr_valid = Transform_update_output(tr);
3091 if (tr_valid)
3092 gtk_databox_graph_set_hide(tr->graph, FALSE);
3093 valid &= tr_valid;
3094 }
3095
3096 return valid;
3097 }
3098
enabled_channels_of_device(GtkTreeView * treeview,const char * name,unsigned * enabled_mask)3099 static int enabled_channels_of_device(GtkTreeView *treeview, const char *name, unsigned *enabled_mask)
3100 {
3101 GtkTreeIter iter;
3102 GtkTreeIter child_iter;
3103 gboolean next_child_iter;
3104 char *str_device;
3105 gboolean enabled;
3106 int num_enabled = 0;
3107 int ch_pos = 0;
3108
3109 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
3110 gboolean next_iter = gtk_tree_model_get_iter_first(model, &iter);
3111
3112 if (enabled_mask)
3113 *enabled_mask = 0;
3114
3115 while (next_iter) {
3116 if (!gtk_tree_model_iter_children(model, &child_iter, &iter)) {
3117 next_iter = gtk_tree_model_iter_next(model, &iter);
3118 continue;
3119 }
3120 gtk_tree_model_get(model, &iter, ELEMENT_NAME, &str_device, -1);
3121 if (!strcmp(name, str_device)) {
3122 next_child_iter = true;
3123 while (next_child_iter) {
3124 gtk_tree_model_get(model, &child_iter, CHANNEL_ACTIVE, &enabled, -1);
3125 if (enabled) {
3126 num_enabled++;
3127 if (enabled_mask)
3128 *enabled_mask |= 1 << ch_pos;
3129 }
3130 ch_pos++;
3131 next_child_iter = gtk_tree_model_iter_next(model, &child_iter);
3132 }
3133 }
3134 next_iter = gtk_tree_model_iter_next(model, &iter);
3135 }
3136
3137 return num_enabled;
3138 }
3139
num_of_channels_of_device(GtkTreeView * treeview,const char * name)3140 static int num_of_channels_of_device(GtkTreeView *treeview, const char *name)
3141 {
3142 GtkTreeModel *model;
3143 GtkTreeIter iter;
3144 gboolean dev_exists;
3145
3146 model = gtk_tree_view_get_model(treeview);
3147 dev_exists = get_iter_by_name(treeview, &iter, name, NULL);
3148 if (!dev_exists)
3149 return 0;
3150
3151 return gtk_tree_model_iter_n_children(model, &iter);
3152 }
3153
enabled_channels_count(OscPlot * plot)3154 static int enabled_channels_count(OscPlot *plot)
3155 {
3156 OscPlotPrivate *priv = plot->priv;
3157 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3158 GtkTreeModel *model;
3159 GtkTreeIter iter;
3160 gboolean next_iter;
3161 gchar *dev_name;
3162 int count = 0;
3163
3164 model = gtk_tree_view_get_model(treeview);
3165 next_iter = gtk_tree_model_get_iter_first(model, &iter);
3166 while (next_iter) {
3167 gtk_tree_model_get(model, &iter, ELEMENT_NAME, &dev_name, -1);
3168 count += enabled_channels_of_device(treeview, dev_name, NULL);
3169 g_free(dev_name);
3170 next_iter = gtk_tree_model_iter_next(model, &iter);
3171 }
3172
3173 return count;
3174 }
3175
plot_channels_get_nth_data_ref(GSList * list,guint n)3176 static gfloat * plot_channels_get_nth_data_ref(GSList *list, guint n)
3177 {
3178 GSList *nth_node;
3179 gfloat *data = NULL;
3180
3181 if (!list) {
3182 fprintf(stderr, "Invalid list argument.");
3183 goto end;
3184 }
3185
3186 nth_node = g_slist_nth(list, n);
3187 if (!nth_node || !nth_node->data) {
3188 fprintf(stderr, "Element at index %d does not exist.", n);
3189 goto end;
3190 }
3191
3192 PlotChn *plot_ch = nth_node->data;
3193
3194 data = plot_ch->get_data_ref(plot_ch);
3195
3196 end:
3197 if (!data)
3198 fprintf(stderr, "Could not find data reference in %s\n",
3199 __func__);
3200
3201 return data;
3202 }
3203
3204 struct ch_tr_params {
3205 OscPlot *plot;
3206 int enabled_channels;
3207 GSList *ch_settings;
3208 };
3209
channels_transform_assignment(GtkTreeModel * model,GtkTreeIter * iter,void * user_data)3210 static void channels_transform_assignment(GtkTreeModel *model,
3211 GtkTreeIter *iter, void *user_data)
3212 {
3213 struct ch_tr_params *prm = user_data;
3214 OscPlot *plot = prm->plot;
3215 OscPlotPrivate *priv = plot->priv;
3216 PlotChn *settings;
3217 Transform *transform = NULL;
3218 gboolean enabled;
3219 int num_added_chs;
3220
3221 gtk_tree_model_get(model, iter,
3222 CHANNEL_ACTIVE, &enabled,
3223 CHANNEL_SETTINGS, &settings,
3224 -1);
3225 if (!enabled)
3226 return;
3227
3228 prm->ch_settings = g_slist_prepend(prm->ch_settings, settings);
3229 num_added_chs = g_slist_length(prm->ch_settings);
3230
3231 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain))) {
3232 case TIME_PLOT:
3233 transform = add_transform_to_list(plot, TIME_TRANSFORM, prm->ch_settings);
3234 break;
3235 case FFT_PLOT:
3236 if (prm->enabled_channels == 1) {
3237 transform = add_transform_to_list(plot, FFT_TRANSFORM, prm->ch_settings);
3238 } else if ((prm->enabled_channels == 2 || prm->enabled_channels == 4) && num_added_chs == 2) {
3239 if (plugin_installed("FMComms6")) {
3240 transform = add_transform_to_list(plot, COMPLEX_FFT_TRANSFORM, prm->ch_settings);
3241 } else {
3242 prm->ch_settings = g_slist_reverse(prm->ch_settings);
3243 transform = add_transform_to_list(plot, COMPLEX_FFT_TRANSFORM, prm->ch_settings);
3244 }
3245 }
3246 break;
3247 case XY_PLOT:
3248 if (prm->enabled_channels == 2 && num_added_chs == 2) {
3249 prm->ch_settings = g_slist_reverse(prm->ch_settings);
3250 transform = add_transform_to_list(plot, CONSTELLATION_TRANSFORM, prm->ch_settings);
3251 }
3252 break;
3253 case XCORR_PLOT:
3254 if (prm->enabled_channels == 4 && num_added_chs == 4) {
3255 prm->ch_settings = g_slist_reverse(prm->ch_settings);
3256 transform = add_transform_to_list(plot, CROSS_CORRELATION_TRANSFORM, prm->ch_settings);
3257 }
3258 break;
3259 case SPECTRUM_PLOT:
3260 if (prm->enabled_channels == 2 && num_added_chs == 2) {
3261 prm->ch_settings = g_slist_reverse(prm->ch_settings);
3262 transform = add_transform_to_list(plot, FREQ_SPECTRUM_TRANSFORM, prm->ch_settings);
3263 }
3264 break;
3265 default:
3266 break;
3267 }
3268 if (transform && prm->ch_settings) {
3269 g_slist_free(prm->ch_settings);
3270 prm->ch_settings = NULL;
3271 }
3272 }
3273
devices_transform_assignment(OscPlot * plot)3274 static void devices_transform_assignment(OscPlot *plot)
3275 {
3276 OscPlotPrivate *priv = plot->priv;
3277 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3278 GtkTreeModel *model;
3279 GtkTreeIter iter;
3280 gboolean next_iter;
3281 gchar *dev_name;
3282 struct ch_tr_params prm;
3283
3284 prm.plot = plot;
3285 prm.enabled_channels = enabled_channels_count(plot);
3286 prm.ch_settings = NULL;
3287
3288 model = gtk_tree_view_get_model(treeview);
3289 next_iter = gtk_tree_model_get_iter_first(model, &iter);
3290 while (next_iter) {
3291 gtk_tree_model_get(model, &iter,
3292 ELEMENT_NAME, &dev_name, -1);
3293 foreach_channel_iter_of_device(treeview, dev_name,
3294 *channels_transform_assignment, &prm);
3295 g_free(dev_name);
3296 next_iter = gtk_tree_model_iter_next(model, &iter);
3297 }
3298 }
3299
deassert_used_channels(OscPlot * plot)3300 static void deassert_used_channels(OscPlot *plot)
3301 {
3302 OscPlotPrivate *priv = plot->priv;
3303 Transform *tr;
3304 GSList *node;
3305 int i;
3306
3307 for (i = 0; i < priv->transform_list->size; i++) {
3308 tr = priv->transform_list->transforms[i];
3309 /* Disable iio channels used by the transform */
3310 for (node = tr->plot_channels; node; node = g_slist_next(node)) {
3311 PlotChn *plot_ch;
3312
3313 plot_ch = node->data;
3314 if (plot_ch)
3315 plot_ch->assert_used_iio_channels(plot_ch, false);
3316 }
3317 }
3318 }
3319
remove_all_transforms(OscPlot * plot)3320 static void remove_all_transforms(OscPlot *plot)
3321 {
3322 OscPlotPrivate *priv = plot->priv;
3323
3324 while (priv->transform_list->size)
3325 remove_transform_from_list(plot, priv->transform_list->transforms[0]);
3326 }
3327
auto_scale_databox(OscPlotPrivate * priv,GtkDatabox * box)3328 static void auto_scale_databox(OscPlotPrivate *priv, GtkDatabox *box)
3329 {
3330 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale)))
3331 return;
3332
3333 /* Auto scale every 10 seconds */
3334 if ((priv->frame_counter == 0) || (priv->do_a_rescale_flag == 1)) {
3335 priv->do_a_rescale_flag = 0;
3336 rescale_databox(priv, box, 0.05);
3337 }
3338 }
3339
fps_counter(OscPlotPrivate * priv)3340 static void fps_counter(OscPlotPrivate *priv)
3341 {
3342 struct timeval now, diff;
3343
3344 priv->frame_counter++;
3345 if (gettimeofday(&now, NULL) == -1) {
3346 printf("err with gettimeofdate()\n");
3347 return;
3348 }
3349 if (!priv->fps) {
3350 priv->last_update.tv_sec = now.tv_sec;
3351 priv->last_update.tv_usec = now.tv_usec;
3352 priv->fps = -1.0;
3353 priv->frame_counter = 0;
3354 return;
3355 }
3356
3357 timersub(&now, &priv->last_update, &diff);
3358
3359 if (diff.tv_sec >= 5 || priv->fps == -1.0) {
3360 double tmp = priv->frame_counter / (diff.tv_sec + diff.tv_usec / 1000000.0);
3361 priv->fps = tmp;
3362 priv->frame_counter = 0;
3363 priv->last_update.tv_sec = now.tv_sec;
3364 priv->last_update.tv_usec = now.tv_usec;
3365 device_rx_info_update(priv);
3366 }
3367 }
3368
plot_redraw(OscPlotPrivate * priv)3369 static gboolean plot_redraw(OscPlotPrivate *priv)
3370 {
3371 TrList *tr_list = priv->transform_list;
3372 Transform *tr;
3373 bool show_diff_phase = false;
3374 int i;
3375
3376 if (!GTK_IS_DATABOX(priv->databox))
3377 return FALSE;
3378
3379 if (priv->redraw) {
3380 auto_scale_databox(priv, GTK_DATABOX(priv->databox));
3381 gtk_widget_queue_draw(priv->databox);
3382 fps_counter(priv);
3383 for (i = 0; i < tr_list->size; i++) {
3384 tr = tr_list->transforms[i];
3385 if (tr->has_the_marker) {
3386
3387 show_diff_phase = true;
3388 draw_marker_values(priv, tr);
3389 }
3390 }
3391 if (show_diff_phase)
3392 markers_phase_diff_show(priv);
3393 }
3394 if (priv->stop_redraw == TRUE)
3395 priv->redraw_function = 0;
3396
3397 priv->redraw = FALSE;
3398 return !priv->stop_redraw;
3399 }
3400
capture_start(OscPlotPrivate * priv)3401 static void capture_start(OscPlotPrivate *priv)
3402 {
3403 if (priv->redraw_function) {
3404 priv->stop_redraw = FALSE;
3405 } else {
3406 priv->stop_redraw = FALSE;
3407 priv->redraw_function = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, (GSourceFunc) plot_redraw, priv, NULL);
3408 }
3409 }
3410
plot_setup(OscPlot * plot)3411 static void plot_setup(OscPlot *plot)
3412 {
3413 OscPlotPrivate *priv = plot->priv;
3414 TrList *tr_list = priv->transform_list;
3415 Transform *transform;
3416 gfloat *transform_x_axis;
3417 gfloat *transform_y_axis;
3418 unsigned int max_x_axis = 0;
3419 GtkDataboxGraph *graph;
3420 int i;
3421
3422 gtk_databox_graph_remove_all(GTK_DATABOX(priv->databox));
3423 markers_init(plot);
3424 for (i = 0; i < tr_list->size; i++) {
3425 transform = tr_list->transforms[i];
3426 Transform_setup(transform);
3427 transform_x_axis = Transform_get_x_axis_ref(transform);
3428 transform_y_axis = Transform_get_y_axis_ref(transform);
3429
3430 gchar *plot_type_str = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->plot_type));
3431 if (strcmp(plot_type_str, "Lines") &&
3432 !is_frequency_transform(priv)) {
3433 graph = gtk_databox_points_new(transform->y_axis_size,
3434 transform_x_axis, transform_y_axis,
3435 transform->graph_color, 3);
3436 } else {
3437 graph = gtk_databox_lines_new(transform->y_axis_size,
3438 transform_x_axis, transform_y_axis,
3439 transform->graph_color, priv->line_thickness);
3440 }
3441 g_free(plot_type_str);
3442
3443 transform->graph = graph;
3444
3445 if (transform->x_axis_size > max_x_axis)
3446 max_x_axis = transform->x_axis_size;
3447
3448 if (is_frequency_transform(priv) ||
3449 priv->active_transform_type == CROSS_CORRELATION_TRANSFORM) {
3450 if (i == 0)
3451 transform_add_plot_markers(plot, transform);
3452 else
3453 transform_add_own_markers(plot, transform);
3454 }
3455
3456 gtk_databox_graph_set_hide(graph, TRUE);
3457 gtk_databox_graph_add(GTK_DATABOX(priv->databox), graph);
3458 }
3459 if (!priv->profile_loaded_scale) {
3460 if (priv->active_transform_type == TIME_TRANSFORM &&
3461 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale)))
3462 gtk_databox_set_total_limits(GTK_DATABOX(priv->databox), 0.0, max_x_axis,
3463 (int)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->y_axis_max))),
3464 (int)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->y_axis_min))));
3465 else if (priv->active_transform_type == CONSTELLATION_TRANSFORM &&
3466 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale)))
3467 gtk_databox_set_total_limits(GTK_DATABOX(priv->databox), -1000.0, 1000.0, 1000, -1000);
3468 else if (priv->active_transform_type == FREQ_SPECTRUM_TRANSFORM) {
3469 double end_freq = priv->start_freq + priv->filter_bw * priv->fft_count;
3470 double width = end_freq - priv->start_freq;
3471 gtk_databox_set_total_limits(GTK_DATABOX(priv->databox),
3472 priv->start_freq - 0.05 * width, end_freq + 0.05 * width,
3473 0.0, -100.0);
3474 }
3475 }
3476
3477 osc_plot_update_rx_lbl(plot, INITIAL_UPDATE);
3478
3479 bool show_phase_info = false;
3480 if (priv->active_transform_type == COMPLEX_FFT_TRANSFORM &&
3481 priv->transform_list->size == 2) {
3482 show_phase_info = true;
3483 }
3484 notebook_info_set_page_visibility(GTK_NOTEBOOK(
3485 gtk_builder_get_object(priv->builder, "notebook_info")),
3486 2, show_phase_info);
3487 }
3488
single_shot_clicked_cb(GtkToggleToolButton * btn,gpointer data)3489 static void single_shot_clicked_cb(GtkToggleToolButton *btn, gpointer data)
3490 {
3491 OscPlot *plot = data;
3492 OscPlotPrivate *priv = plot->priv;
3493
3494 priv->single_shot_mode = true;
3495 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->capture_button), true);
3496 }
3497
comboboxtext_input_devices_fill(struct iio_context * iio_ctx,GtkComboBoxText * box)3498 static bool comboboxtext_input_devices_fill(struct iio_context *iio_ctx, GtkComboBoxText *box)
3499 {
3500 unsigned int i, num_devices = 0;
3501
3502 if (!box) {
3503 fprintf(stderr, "Error: invalid parameters in %s\n", __func__);
3504 return false;
3505 }
3506 if (iio_ctx)
3507 num_devices = iio_context_get_devices_count(iio_ctx);
3508
3509 for (i = 0; i < num_devices; i++) {
3510 struct iio_device *dev = iio_context_get_device(iio_ctx, i);
3511 struct extra_dev_info *dev_info = iio_device_get_data(dev);
3512 const char *name;
3513
3514 if (dev_info->input_device == false)
3515 continue;
3516
3517 name = iio_device_get_name(dev) ?: iio_device_get_id(dev);
3518 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(box), name);
3519 }
3520
3521 return true;
3522 }
3523
capture_button_clicked_cb(GtkToggleToolButton * btn,gpointer data)3524 static void capture_button_clicked_cb(GtkToggleToolButton *btn, gpointer data)
3525 {
3526 OscPlot *plot = data;
3527 OscPlotPrivate *priv = plot->priv;
3528 gboolean button_state;
3529
3530 if (!check_valid_setup(plot))
3531 return;
3532
3533 button_state = gtk_toggle_tool_button_get_active(btn);
3534
3535 if (button_state) {
3536 gtk_widget_set_tooltip_text(GTK_WIDGET(btn), "Capture / Stop");
3537 plot_channels_update(plot);
3538 collect_parameters_from_plot(plot);
3539 remove_all_transforms(plot);
3540 devices_transform_assignment(plot);
3541
3542 g_mutex_trylock(&priv->g_marker_copy_lock);
3543
3544 g_signal_emit(plot, oscplot_signals[CAPTURE_EVENT_SIGNAL], 0, button_state);
3545
3546 plot_setup(plot);
3547
3548 add_grid(plot);
3549 gtk_widget_queue_draw(priv->databox);
3550 priv->frame_counter = 0;
3551 priv->fps = 0.0;
3552 gettimeofday(&(priv->last_update), NULL);
3553 capture_start(priv);
3554 } else {
3555 priv->stop_redraw = TRUE;
3556 dispose_parameters_from_plot(plot);
3557 deassert_used_channels(plot);
3558
3559 g_mutex_trylock(&priv->g_marker_copy_lock);
3560 g_mutex_unlock(&priv->g_marker_copy_lock);
3561
3562 g_signal_emit(plot, oscplot_signals[CAPTURE_EVENT_SIGNAL], 0, button_state);
3563 }
3564 }
3565
new_plot_button_clicked_cb(GtkToolButton * btn,OscPlot * plot)3566 static void new_plot_button_clicked_cb(GtkToolButton *btn, OscPlot *plot)
3567 {
3568 OscPlot *new_plot;
3569
3570 new_plot = OSC_PLOT(osc_plot_new_with_pref(plot->priv->ctx, plot->priv->preferences));
3571 osc_plot_set_visible(new_plot, true);
3572 g_signal_emit(plot, oscplot_signals[NEWPLOT_EVENT_SIGNAL], 0, new_plot);
3573 }
3574
iter_children_sensitivity_update(GtkTreeModel * model,GtkTreeIter * iter,void * data)3575 static void iter_children_sensitivity_update(GtkTreeModel *model, GtkTreeIter *iter, void *data)
3576 {
3577 GtkTreeIter child;
3578 gboolean next_iter;
3579 gboolean state;
3580 gboolean *forced_state = data;
3581
3582 gtk_tree_model_get(model, iter, DEVICE_ACTIVE, &state, -1);
3583 if (!gtk_tree_model_iter_children(model, &child, iter))
3584 return;
3585
3586 if (forced_state)
3587 state = *forced_state;
3588
3589 next_iter = true;
3590 while (next_iter) {
3591 struct iio_channel *chn;
3592 gtk_tree_model_get(model, &child, ELEMENT_REFERENCE, &chn, -1);
3593 struct extra_info *info = iio_channel_get_data(chn);
3594 if (info) {
3595 if (info->constraints & CONSTR_CHN_UNTOGGLEABLE) {
3596 next_iter = gtk_tree_model_iter_next(model, &child);
3597 continue;
3598 }
3599 }
3600
3601 gtk_tree_store_set(GTK_TREE_STORE(model), &child, SENSITIVE, state, -1);
3602 if (state == false)
3603 gtk_tree_store_set(GTK_TREE_STORE(model), &child, CHANNEL_ACTIVE, state, -1);
3604 next_iter = gtk_tree_model_iter_next(model, &child);
3605 }
3606 }
3607
iter_children_plot_type_update(GtkTreeModel * model,GtkTreeIter * iter,void * data)3608 static void iter_children_plot_type_update(GtkTreeModel *model, GtkTreeIter *iter, void *data)
3609 {
3610 OscPlot *plot = data;
3611 OscPlotPrivate *priv = plot->priv;
3612 GtkTreeIter child;
3613 gboolean next_iter;
3614
3615 if (!gtk_tree_model_iter_children(model, &child, iter))
3616 return;
3617
3618 next_iter = true;
3619 while (next_iter) {
3620 gtk_tree_store_set(GTK_TREE_STORE(model), &child,
3621 PLOT_TYPE, gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain)), -1);
3622 next_iter = gtk_tree_model_iter_next(model, &child);
3623 }
3624 }
3625
iter_children_icon_color_update(GtkTreeModel * model,GtkTreeIter * iter,void * data)3626 static void iter_children_icon_color_update(GtkTreeModel *model, GtkTreeIter *iter, void *data)
3627 {
3628 GtkTreeIter child;
3629 gboolean next_iter;
3630 GdkPixbuf *icon;
3631 PlotChn *settings;
3632
3633 if (!gtk_tree_model_iter_children(model, &child, iter))
3634 return;
3635
3636 next_iter = true;
3637 while (next_iter) {
3638 gtk_tree_model_get(model, &child, CHANNEL_SETTINGS, &settings,
3639 CHANNEL_COLOR_ICON, &icon, -1);
3640 channel_color_icon_set_color(icon, &settings->graph_color);
3641
3642 next_iter = gtk_tree_model_iter_next(model, &child);
3643 }
3644 }
3645
device_toggled(GtkCellRendererToggle * renderer,gchar * pathStr,gpointer plot)3646 static void device_toggled(GtkCellRendererToggle* renderer, gchar* pathStr, gpointer plot)
3647 {
3648 OscPlotPrivate *priv = ((OscPlot *)plot)->priv;
3649 GtkTreePath* path = gtk_tree_path_new_from_string(pathStr);
3650 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3651 GtkTreeModel *model;
3652 GtkTreeIter iter;
3653 gboolean active;
3654
3655 model = gtk_tree_view_get_model(treeview);
3656 gtk_tree_model_get_iter(model, &iter, path);
3657 gtk_tree_model_get(model, &iter, DEVICE_ACTIVE, &active, -1);
3658 if (active)
3659 return;
3660 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, DEVICE_ACTIVE, true, -1);
3661
3662 /* When one device is enabled, disable the others */
3663 GtkTreePath *local_path;
3664 GtkTreeIter local_iter;
3665 gboolean next_iter;
3666
3667 gtk_tree_model_get_iter_first(model, &local_iter);
3668 next_iter = true;
3669 while (next_iter) {
3670 local_path = gtk_tree_model_get_path(model, &local_iter);
3671 if (gtk_tree_path_compare(local_path, path) != 0)
3672 gtk_tree_store_set(GTK_TREE_STORE(model), &local_iter, DEVICE_ACTIVE, false, -1);
3673 gtk_tree_path_free(local_path);
3674
3675 next_iter = gtk_tree_model_iter_next(model, &local_iter);
3676 }
3677
3678 foreach_device_iter(treeview, *iter_children_sensitivity_update, NULL);
3679 check_valid_setup(plot);
3680 }
3681
channel_toggled(GtkCellRendererToggle * renderer,gchar * pathStr,gpointer plot)3682 static void channel_toggled(GtkCellRendererToggle* renderer, gchar* pathStr, gpointer plot)
3683 {
3684 OscPlotPrivate *priv = ((OscPlot *)plot)->priv;
3685 GtkTreePath* path = gtk_tree_path_new_from_string(pathStr);
3686 GtkTreeModel *model;
3687 GtkTreeIter iter;
3688 gboolean active;
3689
3690 if (!gtk_cell_renderer_get_sensitive(GTK_CELL_RENDERER(renderer)))
3691 return;
3692
3693 model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->channel_list_view));
3694 gtk_tree_model_get_iter(model, &iter, path);
3695 gtk_tree_model_get(model, &iter, CHANNEL_ACTIVE, &active, -1);
3696 active = !active;
3697 set_channel_state_in_tree_model(model, &iter, active);
3698 gtk_tree_path_free(path);
3699
3700 check_valid_setup(plot);
3701 }
3702
color_icon_renderer_visibility(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)3703 static void color_icon_renderer_visibility(GtkTreeViewColumn *col,
3704 GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
3705 {
3706 gboolean is_channel;
3707 gint plot_type;
3708
3709 gtk_tree_model_get(model, iter, IS_CHANNEL, &is_channel,
3710 PLOT_TYPE, &plot_type, -1);
3711
3712 if (is_channel && plot_type == TIME_PLOT)
3713 gtk_cell_renderer_set_visible(cell, TRUE);
3714 else
3715 gtk_cell_renderer_set_visible(cell, FALSE);
3716 }
3717
create_channel_list_view(OscPlot * plot)3718 static void create_channel_list_view(OscPlot *plot)
3719 {
3720 OscPlotPrivate *priv = plot->priv;
3721 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3722 GtkTreeViewColumn *col;
3723 GtkCellRenderer *renderer_name;
3724 GtkCellRenderer *renderer_ch_toggle;
3725 GtkCellRenderer *renderer_dev_toggle;
3726 GtkCellRenderer *renderer_ch_color;
3727
3728 col = gtk_tree_view_column_new();
3729 gtk_tree_view_column_set_title(col, "Channels");
3730 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
3731
3732 renderer_name = gtk_cell_renderer_text_new();
3733 renderer_ch_toggle = gtk_cell_renderer_toggle_new();
3734 renderer_dev_toggle = gtk_cell_renderer_toggle_new();
3735 renderer_ch_color = gtk_cell_renderer_pixbuf_new();
3736
3737 gtk_tree_view_column_pack_end(col, renderer_ch_color, FALSE);
3738 gtk_tree_view_column_pack_end(col, renderer_name, FALSE);
3739 gtk_tree_view_column_pack_end(col, renderer_ch_toggle, FALSE);
3740 gtk_tree_view_column_pack_end(col, renderer_dev_toggle, FALSE);
3741
3742 gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(renderer_dev_toggle), TRUE);
3743
3744 gtk_tree_view_column_set_attributes(col, renderer_name,
3745 "text", ELEMENT_NAME,
3746 "sensitive", SENSITIVE,
3747 NULL);
3748 gtk_tree_view_column_set_attributes(col, renderer_ch_toggle,
3749 "visible", IS_CHANNEL,
3750 "active", CHANNEL_ACTIVE,
3751 "sensitive", SENSITIVE,
3752 NULL);
3753 gtk_tree_view_column_set_attributes(col, renderer_dev_toggle,
3754 "visible", DEVICE_SELECTABLE,
3755 "active", DEVICE_ACTIVE,
3756 "sensitive", SENSITIVE,
3757 NULL);
3758 gtk_tree_view_column_set_attributes(col, renderer_ch_color,
3759 "pixbuf", CHANNEL_COLOR_ICON,
3760 "sensitive", SENSITIVE,
3761 NULL);
3762 gtk_tree_view_column_set_cell_data_func(col, renderer_ch_color,
3763 *color_icon_renderer_visibility,
3764 NULL,
3765 NULL);
3766
3767 g_object_set(renderer_ch_color, "follow-state", FALSE, NULL);
3768 g_signal_connect(G_OBJECT(renderer_ch_toggle), "toggled", G_CALLBACK(channel_toggled), plot);
3769 g_signal_connect(G_OBJECT(renderer_dev_toggle), "toggled", G_CALLBACK(device_toggled), plot);
3770 }
3771
plot_channel_add_to_plot(OscPlot * plot,PlotChn * settings)3772 static void plot_channel_add_to_plot(OscPlot *plot, PlotChn *settings)
3773 {
3774 OscPlotPrivate *priv = plot->priv;
3775 GSList *list = priv->ch_settings_list;
3776 int index = priv->nb_plot_channels;
3777
3778 g_return_if_fail(settings);
3779
3780 /* Set a default color */
3781 settings->graph_color.red = color_graph[index % NUM_GRAPH_COLORS].red;
3782 settings->graph_color.green = color_graph[index % NUM_GRAPH_COLORS].green;
3783 settings->graph_color.blue = color_graph[index % NUM_GRAPH_COLORS].blue;
3784
3785 /* Add the settings to an internal list */
3786 list = g_slist_prepend(list, settings);
3787 priv->ch_settings_list = list;
3788 priv->nb_plot_channels++;
3789 }
3790
plot_channel_remove_from_plot(OscPlot * plot,PlotChn * chn)3791 static void plot_channel_remove_from_plot(OscPlot *plot,
3792 PlotChn *chn)
3793 {
3794 OscPlotPrivate *priv = plot->priv;
3795 GSList *node, *list;
3796
3797 /* Remove the plot channel from the internal list */
3798 list = priv->ch_settings_list;
3799 node = g_slist_find(list, chn);
3800 chn->destroy(chn);
3801 priv->ch_settings_list = g_slist_remove_link(list, node);
3802 priv->nb_plot_channels--;
3803 }
3804
channel_color_icon_new(OscPlot * plot)3805 static GdkPixbuf * channel_color_icon_new(OscPlot *plot)
3806 {
3807 DIR *d;
3808
3809 /* Check the local icons folder first */
3810 d = opendir("./icons");
3811 if (!d) {
3812 return gtk_image_get_pixbuf(GTK_IMAGE(gtk_image_new_from_file(OSC_GLADE_FILE_PATH"ch_color_icon.png")));
3813 } else {
3814 closedir(d);
3815 return gtk_image_get_pixbuf(GTK_IMAGE(gtk_image_new_from_file("icons/ch_color_icon.png")));
3816 }
3817 }
3818
channel_color_icon_set_color(GdkPixbuf * pb,GdkColor * color)3819 static void channel_color_icon_set_color(GdkPixbuf *pb, GdkColor *color)
3820 {
3821 guchar *pixel;
3822 int rowstride;
3823 int ht;
3824 int i, j;
3825 const char border = 2;
3826
3827 pixel = gdk_pixbuf_get_pixels(pb);
3828 ht = gdk_pixbuf_get_height(pb);
3829 rowstride = gdk_pixbuf_get_rowstride(pb);
3830
3831 for (i = border; i < ht - border; i++)
3832 for (j = border * 4; j < rowstride - border * 4; j += 4) {
3833 pixel[i * rowstride + j + 0] = color->red / 256;
3834 pixel[i * rowstride + j + 1] = color->green / 256;
3835 pixel[i * rowstride + j + 2] = color->blue / 256;
3836 pixel[i * rowstride + j + 3] = 255;
3837 }
3838 }
3839
show_channel(struct iio_channel * chn)3840 static bool show_channel(struct iio_channel *chn)
3841 {
3842 const char *id = iio_channel_get_id(chn);
3843
3844 if (iio_channel_is_output(chn) || !strcmp(id, "timestamp"))
3845 return false;
3846 else
3847 return iio_channel_is_scan_element(chn);
3848 }
3849
plot_channels_add_device(OscPlot * plot,const char * dev_name)3850 static void plot_channels_add_device(OscPlot *plot, const char *dev_name)
3851 {
3852 OscPlotPrivate *priv = plot->priv;
3853 struct iio_context *ctx = priv->ctx;
3854 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3855 GtkTreeStore *treestore;
3856 GtkTreeIter iter;
3857
3858 treestore = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
3859
3860 struct iio_device *iio_dev = NULL;
3861
3862 if (ctx)
3863 iio_dev = iio_context_find_device(ctx, dev_name);
3864
3865 gtk_tree_store_append(treestore, &iter, NULL);
3866 gtk_tree_store_set(treestore, &iter,
3867 ELEMENT_NAME, dev_name,
3868 IS_DEVICE, !!iio_dev,
3869 DEVICE_ACTIVE, !priv->nb_input_devices,
3870 ELEMENT_REFERENCE, iio_dev,
3871 SENSITIVE, TRUE,
3872 EXPANDED, TRUE,
3873 -1);
3874 }
3875
plot_channels_add_channel(OscPlot * plot,PlotChn * pchn)3876 static void plot_channels_add_channel(OscPlot *plot, PlotChn *pchn)
3877 {
3878 OscPlotPrivate *priv = plot->priv;
3879 struct iio_context *ctx = priv->ctx;
3880 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3881 GtkTreeStore *treestore;
3882 GtkTreeIter parent_iter, child_iter;
3883 GdkPixbuf *new_icon;
3884 GdkColor *icon_color;
3885 gboolean ret;
3886
3887 treestore = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
3888
3889 new_icon = channel_color_icon_new(plot);
3890 icon_color = &pchn->graph_color;
3891 channel_color_icon_set_color(new_icon, icon_color);
3892
3893 ret = get_iter_by_name(treeview, &parent_iter, pchn->parent_name, NULL);
3894 if (!ret) {
3895 fprintf(stderr,
3896 "Could not add %s channel to device %s. Device not found\n",
3897 pchn->name, pchn->parent_name);
3898 return;
3899 }
3900
3901 struct iio_device *iio_dev = NULL;
3902 struct iio_channel *iio_chn = NULL;
3903
3904 if (ctx && (iio_dev = iio_context_find_device(ctx, pchn->parent_name)))
3905 iio_chn = iio_device_find_channel(iio_dev, pchn->name, false);
3906
3907 bool sensitive = true;
3908 bool active = false;
3909
3910 /* Check if there are any channel constraints */
3911 struct extra_info *ch_info = iio_channel_get_data(iio_chn);
3912 if (ch_info) {
3913 active = (ch_info->constraints & CONSTR_CHN_INITIAL_ENABLED);
3914 sensitive = !(ch_info->constraints & CONSTR_CHN_UNTOGGLEABLE);
3915 }
3916
3917 gtk_tree_store_append(treestore, &child_iter, &parent_iter);
3918 gtk_tree_store_set(treestore, &child_iter,
3919 ELEMENT_NAME, pchn->name,
3920 IS_CHANNEL, TRUE,
3921 CHANNEL_TYPE, pchn->type,
3922 CHANNEL_ACTIVE, active,
3923 ELEMENT_REFERENCE, iio_chn,
3924 CHANNEL_SETTINGS, pchn,
3925 CHANNEL_COLOR_ICON, new_icon,
3926 SENSITIVE, sensitive,
3927 PLOT_TYPE, TIME_PLOT,
3928 -1);
3929 }
3930
plot_channels_remove_channel(OscPlot * plot,GtkTreeIter * iter)3931 static void plot_channels_remove_channel(OscPlot *plot, GtkTreeIter *iter)
3932 {
3933 OscPlotPrivate *priv = plot->priv;
3934 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
3935 GtkTreeModel *model;
3936 PlotChn *settings;
3937
3938 model = gtk_tree_view_get_model(treeview);
3939 gtk_tree_model_get(model, iter, CHANNEL_SETTINGS, &settings, -1);
3940 plot_channel_remove_from_plot(plot, settings);
3941 gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
3942 }
3943
device_list_treeview_init(OscPlot * plot)3944 static void device_list_treeview_init(OscPlot *plot)
3945 {
3946 OscPlotPrivate *priv = plot->priv;
3947 struct iio_context *ctx = priv->ctx;
3948 unsigned int i, j;
3949
3950 priv->nb_input_devices = 0;
3951 if (!ctx)
3952 goto math_channels;
3953 for (i = 0; i < iio_context_get_devices_count(ctx); i++) {
3954 struct iio_device *dev = iio_context_get_device(ctx, i);
3955 struct extra_dev_info *dev_info = iio_device_get_data(dev);
3956 const char *dev_name = iio_device_get_name(dev) ?:
3957 iio_device_get_id(dev);
3958
3959 if (dev_info->input_device == false)
3960 continue;
3961
3962 if (!priv->current_device)
3963 priv->current_device = dev;
3964
3965 plot_channels_add_device(plot, dev_name);
3966 priv->nb_input_devices++;
3967
3968 GArray *channels = get_iio_channels_naturally_sorted(dev);
3969
3970 for (j = 0; j < channels->len; ++j) {
3971 struct iio_channel *ch = g_array_index(channels,
3972 struct iio_channel *, j);
3973 if (!show_channel(ch))
3974 continue;
3975
3976 const char *chn_name = iio_channel_get_name(ch) ?:
3977 iio_channel_get_id(ch);
3978 PlotIioChn *pic;
3979
3980 pic = plot_iio_channel_new(priv->ctx);
3981 if (!pic) {
3982 fprintf(stderr, "Could not create an iio plot"
3983 "channel with name %s in function %s\n",
3984 chn_name, __func__);
3985 break;
3986 }
3987 plot_channel_add_to_plot(plot, PLOT_CHN(pic));
3988 pic->iio_chn = ch;
3989 pic->base.type = PLOT_IIO_CHANNEL;
3990 pic->base.name = g_strdup(chn_name);
3991 pic->base.parent_name = g_strdup(dev_name);
3992 plot_channels_add_channel(plot, PLOT_CHN(pic));
3993 }
3994 g_array_free(channels, FALSE);
3995 }
3996 math_channels:
3997 #ifdef linux
3998 plot_channels_add_device(plot, MATH_CHANNELS_DEVICE);
3999 priv->nb_input_devices++;
4000 #endif
4001 create_channel_list_view(plot);
4002 treeview_expand_update(plot);
4003 }
4004
saveas_device_changed_cb(GtkComboBoxText * box,OscPlot * plot)4005 static void saveas_device_changed_cb(GtkComboBoxText *box, OscPlot *plot)
4006 {
4007 OscPlotPrivate *priv = plot->priv;
4008 struct iio_context *ctx = priv->ctx;
4009 struct iio_device *dev;
4010 GtkWidget *parent;
4011 GtkWidget *ch_checkbtn;
4012 gchar *active_device;
4013 unsigned int i;
4014 int d;
4015
4016 parent = gtk_widget_get_parent(priv->saveas_channels_list);
4017 gtk_widget_destroy(priv->saveas_channels_list);
4018 priv->saveas_channels_list = gtk_vbox_new(FALSE, 0);
4019 gtk_box_pack_start(GTK_BOX(parent), priv->saveas_channels_list, FALSE, TRUE, 0);
4020
4021 active_device = gtk_combo_box_text_get_active_text(box);
4022 d = device_find_by_name(ctx, active_device);
4023 g_free(active_device);
4024 if (d < 0)
4025 return;
4026
4027 dev = iio_context_get_device(ctx, d);
4028
4029 GArray *channels = get_iio_channels_naturally_sorted(dev);
4030
4031 for (i = 0; i < channels->len; ++i) {
4032 struct iio_channel *chn = g_array_index(channels, struct iio_channel *, i);
4033 const char *name = iio_channel_get_name(chn) ?:
4034 iio_channel_get_id(chn);
4035 ch_checkbtn = gtk_check_button_new_with_label(name);
4036 gtk_box_pack_start(GTK_BOX(priv->saveas_channels_list), ch_checkbtn, FALSE, TRUE, 0);
4037 }
4038 g_array_free(channels, FALSE);
4039 gtk_widget_show_all(priv->saveas_channels_list);
4040 }
4041
saveas_channels_list_fill(OscPlot * plot)4042 static void saveas_channels_list_fill(OscPlot *plot)
4043 {
4044 OscPlotPrivate *priv = plot->priv;
4045 GtkWidget *ch_window;
4046 GtkWidget *vbox;
4047 unsigned int num_devices = 0;
4048 unsigned int i;
4049
4050 if (priv->ctx)
4051 num_devices = iio_context_get_devices_count(priv->ctx);
4052 ch_window = priv->viewport_saveas_channels;
4053 vbox = gtk_vbox_new(FALSE, 10);
4054 gtk_container_add(GTK_CONTAINER(ch_window), vbox);
4055 priv->device_combobox = gtk_combo_box_text_new();
4056 gtk_box_pack_start(GTK_BOX(vbox), priv->device_combobox, FALSE, TRUE, 0);
4057 priv->saveas_channels_list = gtk_vbox_new(FALSE, 0);
4058 gtk_box_pack_end(GTK_BOX(vbox), priv->saveas_channels_list, FALSE, TRUE, 0);
4059
4060 for (i = 0; i < num_devices; i++) {
4061 struct iio_device *dev = iio_context_get_device(priv->ctx, i);
4062 const char *name = iio_device_get_name(dev) ?:
4063 iio_device_get_id(dev);
4064 struct extra_dev_info *dev_info = iio_device_get_data(dev);
4065
4066 if (dev_info->input_device == false)
4067 continue;
4068
4069 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(priv->device_combobox), name);
4070 }
4071
4072 if (num_devices == 0)
4073 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(priv->device_combobox),
4074 "No Devices Available");
4075 g_signal_connect(priv->device_combobox, "changed",
4076 G_CALLBACK(saveas_device_changed_cb), (gpointer)plot);
4077 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->device_combobox), 0);
4078 gtk_widget_set_size_request(priv->viewport_saveas_channels, -1, 150);
4079 gtk_widget_show_all(vbox);
4080 }
4081
iio_chn_basenames_get(OscPlot * plot,const char * dev_name)4082 static GSList * iio_chn_basenames_get(OscPlot *plot, const char *dev_name)
4083 {
4084 struct iio_device *iio_dev;
4085 struct iio_channel *iio_chn;
4086 GSList *list = NULL;
4087 unsigned int i;
4088
4089 if (!dev_name)
4090 return NULL;
4091
4092 iio_dev = iio_context_find_device(plot->priv->ctx, dev_name);
4093 if (!iio_dev)
4094 return NULL;
4095
4096 unsigned int nb_channels = iio_device_get_channels_count(iio_dev);
4097
4098 for (i = 0; i < nb_channels; i++) {
4099 iio_chn = iio_device_get_channel(iio_dev, i);
4100 if (!show_channel(iio_chn))
4101 continue;
4102
4103 const char *chn_name = iio_channel_get_name(iio_chn) ?:
4104 iio_channel_get_id(iio_chn);
4105
4106 char *basename, *c;
4107
4108 basename = g_strdup_printf("%s", chn_name);
4109 for (c = basename; *c; c++) {
4110 if (g_ascii_isdigit(*c))
4111 *c = 0;
4112 }
4113
4114 if (list) {
4115 if (!g_slist_find_custom(list, basename, (GCompareFunc)strcmp)) {
4116 list = g_slist_append(list, basename);
4117 }
4118 } else {
4119 list = g_slist_append(list, basename);
4120 }
4121 }
4122
4123 return list;
4124 }
4125
capture_button_icon_transform(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer data)4126 static gboolean capture_button_icon_transform(GBinding *binding,
4127 const GValue *source_value, GValue *target_value, gpointer data)
4128 {
4129 if (((OscPlot *)data)->priv->deactivate_capture_btn_flag == 1)
4130 return FALSE;
4131
4132 if (g_value_get_boolean(source_value))
4133 g_value_set_static_string(target_value, "gtk-stop");
4134 else
4135 g_value_set_static_string(target_value, "gtk-media-play");
4136
4137 return TRUE;
4138 }
4139
4140 /*
4141 * Fill in an array, of about num times
4142 */
fill_axis(gfloat * buf,gfloat start,gfloat inc,int num)4143 static void fill_axis(gfloat *buf, gfloat start, gfloat inc, int num)
4144 {
4145 int i;
4146 gfloat val = start;
4147
4148 for (i = 0; i < num; i++) {
4149 buf[i] = val;
4150 val += inc;
4151 }
4152
4153 }
4154
update_grid(OscPlot * plot,gfloat left,gfloat right)4155 static void update_grid(OscPlot *plot, gfloat left, gfloat right)
4156 {
4157 OscPlotPrivate *priv = plot->priv;
4158
4159 if (priv->active_transform_type == FFT_TRANSFORM ||
4160 priv->active_transform_type == COMPLEX_FFT_TRANSFORM) {
4161 gfloat spacing;
4162
4163 spacing = ceil((right - left) / 130) * 10;
4164 if (spacing < 10)
4165 spacing = 10;
4166 fill_axis(priv->gridx, left, spacing, 14);
4167 fill_axis(priv->gridy, 10, -10, 25);
4168 }
4169 }
4170
add_grid(OscPlot * plot)4171 static void add_grid(OscPlot *plot)
4172 {
4173 OscPlotPrivate *priv = plot->priv;
4174
4175 /*
4176 This would be a better general solution, but it doesn't really work well
4177 gfloat left, right, top, bottom;
4178 int x, y;
4179
4180 gtk_databox_get_total_limits(GTK_DATABOX(databox), &left, &right, &top, &bottom);
4181 y = fill_axis(gridy, top, bottom, 20);
4182 x = fill_axis(gridx, left, right, 20);
4183 grid = gtk_databox_grid_array_new (y, x, gridy, gridx, &color_grid, 1);
4184 */
4185
4186 if (priv->active_transform_type == FFT_TRANSFORM ||
4187 priv->active_transform_type == COMPLEX_FFT_TRANSFORM) {
4188 priv->grid = gtk_databox_grid_array_new (25, 14, priv->gridy, priv->gridx, &color_grid, 1);
4189 } else if (priv->active_transform_type == CONSTELLATION_TRANSFORM) {
4190 fill_axis(priv->gridx, -80000, 10000, 18);
4191 fill_axis(priv->gridy, -80000, 10000, 18);
4192 priv->grid = gtk_databox_grid_array_new (18, 18, priv->gridy, priv->gridx, &color_grid, 1);
4193 } else if (priv->active_transform_type == TIME_TRANSFORM) {
4194 fill_axis(priv->gridx, 0, 100, 5);
4195 fill_axis(priv->gridy, -80000, 10000, 18);
4196 priv->grid = gtk_databox_grid_array_new (18, 5, priv->gridy, priv->gridx, &color_grid, 1);
4197 } else if (priv->active_transform_type == NO_TRANSFORM_TYPE) {
4198 gfloat left, right, top, bottom;
4199
4200 gtk_databox_get_total_limits(GTK_DATABOX(priv->databox), &left, &right, &top, &bottom);
4201 fill_axis(priv->gridy, top, bottom, 20);
4202 fill_axis(priv->gridx, left, right, 20);
4203 priv->grid = gtk_databox_grid_array_new (18, 5, priv->gridy, priv->gridx, &color_grid, 1);
4204 }
4205
4206 gtk_databox_graph_add(GTK_DATABOX(priv->databox), priv->grid);
4207 gtk_databox_graph_set_hide(priv->grid, !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->show_grid)));
4208 }
4209
show_grid_toggled(GtkToggleButton * btn,gpointer data)4210 static void show_grid_toggled(GtkToggleButton *btn, gpointer data)
4211 {
4212 OscPlot *plot = data;
4213 OscPlotPrivate *priv = plot->priv;
4214
4215 if (priv->grid) {
4216 gtk_databox_graph_set_hide(priv->grid, !gtk_toggle_button_get_active(btn));
4217 gtk_widget_queue_draw(priv->databox);
4218 }
4219 }
4220
rescale_databox(OscPlotPrivate * priv,GtkDatabox * box,gfloat border)4221 static void rescale_databox(OscPlotPrivate *priv, GtkDatabox *box, gfloat border)
4222 {
4223 bool fixed_aspect = (priv->active_transform_type == CONSTELLATION_TRANSFORM) ? TRUE : FALSE;
4224
4225 if (fixed_aspect) {
4226 gfloat min_x;
4227 gfloat max_x;
4228 gfloat min_y;
4229 gfloat max_y;
4230 gfloat width;
4231
4232 gint extrema_success = gtk_databox_calculate_extrema(box,
4233 &min_x, &max_x, &min_y, &max_y);
4234 if (extrema_success)
4235 return;
4236 if (min_x > min_y)
4237 min_x = min_y;
4238 if (max_x < max_y)
4239 max_x = max_y;
4240
4241 width = max_x - min_x;
4242 if (width == 0)
4243 width = max_x;
4244
4245 min_x -= border * width;
4246 max_x += border * width;
4247
4248 gtk_databox_set_total_limits(box, min_x, max_x, max_x, min_x);
4249
4250 } else if (priv->active_transform_type == FREQ_SPECTRUM_TRANSFORM) {
4251 gfloat min_x;
4252 gfloat max_x;
4253 gfloat min_y;
4254 gfloat max_y;
4255 gfloat width;
4256
4257 gint extrema_success = gtk_databox_calculate_extrema(box,
4258 &min_x, &max_x, &min_y, &max_y);
4259 if (extrema_success)
4260 return;
4261 if (min_x == 0) {
4262 min_x = priv->start_freq;
4263 }
4264 width = priv->filter_bw * priv->fft_count;
4265
4266 gtk_databox_set_total_limits(box, min_x - 0.05 * width,
4267 max_x + 0.05 * width, max_y, min_y);
4268 } else {
4269 gtk_databox_auto_rescale(box, border);
4270 }
4271 }
4272
zoom_fit(GtkButton * btn,gpointer data)4273 static void zoom_fit(GtkButton *btn, gpointer data)
4274 {
4275 OscPlot *plot = data;
4276 OscPlotPrivate *priv = plot->priv;
4277
4278 rescale_databox(priv, GTK_DATABOX(priv->databox), 0.05);
4279 }
4280
zoom_in(GtkButton * btn,gpointer data)4281 static void zoom_in(GtkButton *btn, gpointer data)
4282 {
4283 OscPlot *plot = data;
4284 OscPlotPrivate *priv = plot->priv;
4285 bool fixed_aspect = (priv->active_transform_type == CONSTELLATION_TRANSFORM) ? TRUE : FALSE;
4286 gfloat left, right, top, bottom;
4287 gfloat width, height;
4288
4289 gtk_databox_get_visible_limits(GTK_DATABOX(priv->databox), &left, &right, &top, &bottom);
4290 width = right - left;
4291 height = bottom - top;
4292 left += width * 0.25;
4293 right -= width * 0.25;
4294 top += height * 0.25;
4295 bottom -= height * 0.25;
4296
4297 if (fixed_aspect) {
4298 gfloat diff;
4299 width *= 0.5;
4300 height *= -0.5;
4301 if (height > width) {
4302 diff = width - height;
4303 left -= diff * 0.5;
4304 right += diff * 0.5;
4305 } else {
4306 diff = height - width;
4307 bottom += diff * 0.5;
4308 top -= diff * 0.5;
4309 }
4310 }
4311
4312 gtk_databox_set_visible_limits(GTK_DATABOX(priv->databox), left, right, top, bottom);
4313 }
4314
zoom_out(GtkButton * btn,gpointer data)4315 static void zoom_out(GtkButton *btn, gpointer data)
4316 {
4317 OscPlot *plot = data;
4318 OscPlotPrivate *priv = plot->priv;
4319 bool fixed_aspect = (priv->active_transform_type == CONSTELLATION_TRANSFORM) ? TRUE : FALSE;
4320 gfloat left, right, top, bottom;
4321 gfloat t_left, t_right, t_top, t_bottom;
4322 gfloat width, height;
4323
4324 gtk_databox_get_visible_limits(GTK_DATABOX(priv->databox), &left, &right, &top, &bottom);
4325 width = right - left;
4326 height = bottom - top;
4327 left -= width * 0.25;
4328 right += width * 0.25;
4329 top -= height * 0.25;
4330 bottom += height * 0.25;
4331
4332 gtk_databox_get_total_limits(GTK_DATABOX(priv->databox), &t_left, &t_right, &t_top, &t_bottom);
4333 if (left < right) {
4334 if (left < t_left)
4335 left = t_left;
4336 if (right > t_right)
4337 right = t_right;
4338 } else {
4339 if (left > t_left)
4340 left = t_left;
4341 if (right < t_right)
4342 right = t_right;
4343 }
4344
4345 if (top < bottom) {
4346 if (top < t_top)
4347 top = t_top;
4348 if (bottom > t_bottom)
4349 bottom = t_bottom;
4350 } else {
4351 if (top > t_top)
4352 top = t_top;
4353 if (bottom < t_bottom)
4354 bottom = t_bottom;
4355 }
4356
4357 if (fixed_aspect) {
4358 gfloat diff;
4359 width = right - left;
4360 height = top - bottom;
4361 if (height < width) {
4362 diff = width - height;
4363 bottom -= diff * 0.5;
4364 top += diff * 0.5;
4365 if (top < t_top) {
4366 bottom += t_top - top;
4367 top = t_top;
4368 }
4369 if (bottom > t_bottom) {
4370 top -= bottom - t_bottom;
4371 bottom = t_bottom;
4372 }
4373 } else {
4374 diff = height - width;
4375 left -= diff * 0.5;
4376 right += diff * 0.5;
4377 if (left < t_left) {
4378 right += t_left - left;
4379 left = t_left;
4380 }
4381 if (right > t_right) {
4382 left -= right - t_right;
4383 right = t_right;
4384 }
4385 }
4386 }
4387
4388 gtk_databox_set_visible_limits(GTK_DATABOX(priv->databox), left, right, top, bottom);
4389 }
4390
transform_csv_print(OscPlotPrivate * priv,FILE * fp,Transform * tr)4391 static void transform_csv_print(OscPlotPrivate *priv, FILE *fp, Transform *tr)
4392 {
4393 gfloat *tr_data;
4394 gfloat *tr_x_axis;
4395 unsigned int i;
4396 GSList *node;
4397 const char *id1 = NULL, *id2 = NULL;
4398
4399 switch (g_slist_length(tr->plot_channels)) {
4400 case 2:
4401 node = g_slist_nth(tr->plot_channels, 1);
4402 id2 = PLOT_CHN(node->data)->name;
4403 /* FALLTHROUGH */
4404 case 1:
4405 node = g_slist_nth(tr->plot_channels, 0);
4406 id1 = PLOT_CHN(node->data)->name;
4407 break;
4408 default:
4409 break;
4410 }
4411
4412 if (tr->type_id == TIME_TRANSFORM)
4413 fprintf(fp, "X Axis(Sample Index) Y Axis(%s)\n", id1);
4414 else if (tr->type_id == FFT_TRANSFORM)
4415 fprintf(fp, "X Axis(Frequency) Y Axis(FFT - %s)\n", id1);
4416 else if (tr->type_id == COMPLEX_FFT_TRANSFORM)
4417 fprintf(fp, "X Axis(Frequency) Y Axis(Complex FFT - %s, %s)\n", id1, id2);
4418 else if (tr->type_id == CONSTELLATION_TRANSFORM)
4419 fprintf(fp, "X Axis(%s) Y Axis(%s)\n", id2, id1);
4420
4421 tr_x_axis = Transform_get_x_axis_ref(tr);
4422 tr_data = Transform_get_y_axis_ref(tr);
4423
4424 if (tr_x_axis == NULL || tr_data == NULL) {
4425 fprintf(fp, "No data\n");
4426 return;
4427 }
4428
4429 for (i = 0; i < tr->x_axis_size; i++) {
4430 fprintf(fp, "%g, %g,\n", tr_x_axis[i], tr_data[i]);
4431 }
4432 fprintf(fp, "\n");
4433 }
4434
plot_destroyed(GtkWidget * object,OscPlot * plot)4435 static void plot_destroyed (GtkWidget *object, OscPlot *plot)
4436 {
4437 osc_plot_draw_stop(plot);
4438 g_slist_free_full(plot->priv->ch_settings_list, (GDestroyNotify)g_free);
4439 g_mutex_trylock(&plot->priv->g_marker_copy_lock);
4440 g_mutex_unlock(&plot->priv->g_marker_copy_lock);
4441
4442 g_signal_emit(plot, oscplot_signals[DESTROY_EVENT_SIGNAL], 0);
4443 }
4444
window_get_screenshot_pixbuf(GtkWidget * window)4445 static GdkPixbuf * window_get_screenshot_pixbuf(GtkWidget *window)
4446 {
4447 GdkWindow *gdk_w;
4448 gint width, height;
4449
4450 gdk_w = gtk_widget_get_window(window);
4451 width = gdk_window_get_width(gdk_w);
4452 height = gdk_window_get_height(gdk_w);
4453
4454 return gdk_pixbuf_get_from_drawable(NULL, GDK_DRAWABLE(gdk_w),
4455 gdk_colormap_get_system(), 0, 0, 0, 0, width, height);
4456 }
4457
screenshot_saveas_png(OscPlot * plot)4458 static void screenshot_saveas_png(OscPlot *plot)
4459 {
4460 OscPlotPrivate *priv = plot->priv;
4461 GdkPixbuf *pixbuf;
4462 char *filename;
4463 GError *err = NULL;
4464 gboolean ret = true;
4465
4466 filename = priv->saveas_filename;
4467 if (!filename) {
4468 fprintf(stderr, "error invalid filename");
4469 return;
4470 }
4471
4472 pixbuf = window_get_screenshot_pixbuf(priv->window);
4473 if (pixbuf)
4474 ret = gdk_pixbuf_save(pixbuf, filename, "png", &err, NULL);
4475 else
4476 fprintf(stderr,
4477 "error getting the pixbug of the Capture Plot window\n");
4478
4479
4480 if (!ret) {
4481 fprintf(stderr, "error creating %s\n", filename);
4482 if (err)
4483 fprintf(stderr, "error(%d):%s\n", err->code,
4484 err->message);
4485 }
4486
4487 return;
4488 }
4489
copy_channel_state_to_selection_channel(GtkTreeModel * model,GtkTreeIter * iter,void * user_data)4490 static void copy_channel_state_to_selection_channel(GtkTreeModel *model,
4491 GtkTreeIter *iter, void *user_data)
4492 {
4493 OscPlot *plot = user_data;
4494 OscPlotPrivate *priv = plot->priv;
4495 struct iio_channel *chn;
4496 gboolean active_state;
4497 GList *ch_checkbtns = NULL;
4498 GList *node;
4499 GtkToggleButton *btn;
4500 const char *ch_name;
4501 gchar *btn_label;
4502
4503 gtk_tree_model_get(model, iter, ELEMENT_REFERENCE, &chn, CHANNEL_ACTIVE, &active_state, -1);
4504 ch_name = iio_channel_get_name(chn) ?: iio_channel_get_id(chn);
4505
4506 /* Get user channel selection from GUI widgets */
4507 ch_checkbtns = gtk_container_get_children(GTK_CONTAINER(priv->saveas_channels_list));
4508 for (node = ch_checkbtns; node; node = g_list_next(node)) {
4509 btn = (GtkToggleButton *)node->data;
4510 g_object_get(btn, "label", &btn_label, NULL);
4511 if (!strcmp(ch_name, btn_label)) {
4512 gtk_toggle_button_set_active(btn, active_state);
4513 g_free(btn_label);
4514 break;
4515 }
4516 g_free(btn_label);
4517 }
4518 g_list_free(ch_checkbtns);
4519
4520 }
4521
channel_selection_set_default(OscPlot * plot)4522 static void channel_selection_set_default(OscPlot *plot)
4523 {
4524 OscPlotPrivate *priv = plot->priv;
4525 gchar *device_name;
4526 int d;
4527
4528 device_name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->device_combobox));
4529 d = device_find_by_name(priv->ctx, device_name);
4530 if (d >= 0)
4531 foreach_channel_iter_of_device(GTK_TREE_VIEW(priv->channel_list_view),
4532 device_name, *copy_channel_state_to_selection_channel, plot);
4533 g_free(device_name);
4534 }
4535
get_user_saveas_channel_selection(OscPlot * plot,unsigned int nb_channels)4536 static int * get_user_saveas_channel_selection(OscPlot *plot, unsigned int nb_channels)
4537 {
4538 OscPlotPrivate *priv = plot->priv;
4539 GList *ch_checkbtns;
4540 GList *node;
4541 GtkToggleButton *btn;
4542 int *mask;
4543
4544 /* Create masks for all channels */
4545 mask = malloc(sizeof(int) * nb_channels);
4546
4547 /* Get user channel selection from GUI widgets */
4548 ch_checkbtns = gtk_container_get_children(GTK_CONTAINER(priv->saveas_channels_list));
4549 for (node = ch_checkbtns; node; node = g_list_next(node)) {
4550 btn = (GtkToggleButton *)node->data;
4551
4552 int dev_index, ch_index;
4553 gchar *dev_name, *ch_name;
4554
4555 dev_name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->device_combobox));
4556 dev_index = device_find_by_name(priv->ctx, dev_name);
4557 g_free(dev_name);
4558 g_object_get(btn, "label", &ch_name, NULL);
4559 ch_index = channel_find_by_name(priv->ctx, dev_index, ch_name);
4560 if (ch_index < 0) {
4561 fprintf(stderr, "Cannot find channel %s\n", ch_name);
4562 g_free(ch_name);
4563 break;
4564 }
4565 g_free(ch_name);
4566 mask[ch_index] = !gtk_toggle_button_get_active(btn);
4567 }
4568 g_list_free(ch_checkbtns);
4569
4570 return mask;
4571 }
4572
4573 #define SAVE_AS_RAW_DATA 1
4574
saveas_dialog_show(GtkWidget * w,OscPlot * plot)4575 static void saveas_dialog_show(GtkWidget *w, OscPlot *plot)
4576 {
4577 OscPlotPrivate *priv = plot->priv;
4578
4579 gtk_file_chooser_set_action(GTK_FILE_CHOOSER (priv->saveas_dialog), GTK_FILE_CHOOSER_ACTION_SAVE);
4580 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(priv->saveas_dialog), TRUE);
4581
4582 if (!priv->saveas_filename) {
4583 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (priv->saveas_dialog), getenv("HOME"));
4584 } else {
4585 if (!gtk_file_chooser_set_filename(GTK_FILE_CHOOSER (priv->saveas_dialog), priv->saveas_filename))
4586 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (priv->saveas_dialog), getenv("HOME"));
4587 g_free(priv->saveas_filename);
4588 priv->saveas_filename = NULL;
4589 }
4590
4591 priv->active_saveas_type = SAVE_AS_RAW_DATA;
4592 channel_selection_set_default(plot);
4593 gtk_widget_show(priv->saveas_dialog);
4594 }
4595
save_as(OscPlot * plot,const char * filename,int type)4596 static void save_as(OscPlot *plot, const char *filename, int type)
4597 {
4598 OscPlotPrivate *priv = plot->priv;
4599 struct iio_context *ctx = priv->ctx;
4600 FILE *fp;
4601 mat_t *mat;
4602 matvar_t *matvar;
4603 struct iio_device *dev;
4604 struct extra_dev_info *dev_info;
4605 char tmp[100];
4606 mat_dim dims[2] = {-1, 1};
4607 double freq;
4608 char *name;
4609 gchar *active_device;
4610 int *save_channels_mask;
4611 int d;
4612 unsigned int nb_channels, i, j;
4613 const char *dev_name;
4614 unsigned int dev_sample_count;
4615
4616 name = malloc(strlen(filename) + 5);
4617 switch(type) {
4618 case SAVE_VSA:
4619 /* Save as Agilent VSA formatted file */
4620 if (!strncasecmp(&filename[strlen(filename)-4], ".txt", 4))
4621 strcpy(name, filename);
4622 else
4623 sprintf(name, "%s.txt", filename);
4624 fp = fopen(name, "w");
4625 if (!fp)
4626 break;
4627
4628 active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->device_combobox));
4629 d = device_find_by_name(ctx, active_device);
4630 g_free(active_device);
4631 if (d < 0)
4632 break;
4633 dev = iio_context_get_device(ctx, d);
4634 dev_info = iio_device_get_data(dev);
4635 nb_channels = iio_device_get_channels_count(dev);
4636
4637 /* Find which channel need to be saved */
4638 save_channels_mask = get_user_saveas_channel_selection(plot, nb_channels);
4639
4640 /* Make a VSA file header */
4641 fprintf(fp, "InputZoom\tTRUE\n");
4642 fprintf(fp, "InputCenter\t0\n");
4643 fprintf(fp, "InputRange\t1\n");
4644 fprintf(fp, "InputRefImped\t50\n");
4645 fprintf(fp, "XStart\t0\n");
4646 freq = dev_info->adc_freq * prefix2scale(dev_info->adc_scale);
4647 fprintf(fp, "XDelta\t%-.17f\n", 1.0/freq);
4648 fprintf(fp, "XDomain\t2\n");
4649 fprintf(fp, "XUnit\tSec\n");
4650 fprintf(fp, "YUnit\tV\n");
4651 fprintf(fp, "FreqValidMax\t%e\n", freq / 2);
4652 fprintf(fp, "FreqValidMin\t-%e\n", freq / 2);
4653 fprintf(fp, "Y\n");
4654
4655 dev_sample_count = dev_info->sample_count;
4656 if (dev_info->channel_trigger_enabled)
4657 dev_sample_count /= 2;
4658
4659 /* Start writing the samples */
4660 for (i = 0; i < dev_sample_count; i++) {
4661 for (j = 0; j < nb_channels; j++) {
4662 struct extra_info *info = iio_channel_get_data(iio_device_get_channel(dev, j));
4663 if (save_channels_mask[j] == 1)
4664 continue;
4665 fprintf(fp, "%g\t", info->data_ref[i]);
4666 }
4667 fprintf(fp, "\n");
4668 }
4669 fprintf(fp, "\n");
4670 fclose(fp);
4671 free(save_channels_mask);
4672
4673 break;
4674 case SAVE_CSV:
4675 /* save comma separated values (csv) */
4676 if (!strncasecmp(&filename[strlen(filename)-4], ".csv", 4))
4677 strcpy(name, filename);
4678 else
4679 sprintf(name, "%s.csv", filename);
4680 fp = fopen(name, "w");
4681 if (!fp)
4682 break;
4683 if (priv->active_saveas_type == SAVE_AS_RAW_DATA) {
4684 active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->device_combobox));
4685 d = device_find_by_name(ctx, active_device);
4686 g_free(active_device);
4687 if (d < 0)
4688 break;
4689
4690 dev = iio_context_get_device(ctx, d);
4691 dev_info = iio_device_get_data(dev);
4692 nb_channels = iio_device_get_channels_count(dev);
4693
4694 /* Find which channel need to be saved */
4695 save_channels_mask = get_user_saveas_channel_selection(plot, nb_channels);
4696
4697 dev_sample_count = dev_info->sample_count;
4698 if (dev_info->channel_trigger_enabled)
4699 dev_sample_count /= 2;
4700
4701 for (i = 0; i < dev_sample_count; i++) {
4702 for (j = 0; j < nb_channels; j++) {
4703 struct extra_info *info = iio_channel_get_data(iio_device_get_channel(dev, j));
4704 if (save_channels_mask[j] == 1)
4705 continue;
4706 fprintf(fp, "%g, ", info->data_ref[i]);
4707 }
4708 fprintf(fp, "\n");
4709 }
4710 fprintf(fp, "\n");
4711 free(save_channels_mask);
4712 } else {
4713 for (d = 0; d < priv->transform_list->size; d++) {
4714 transform_csv_print(priv, fp, priv->transform_list->transforms[d]);
4715 }
4716 }
4717 fprintf(fp, "\n");
4718 fclose(fp);
4719 break;
4720
4721 case SAVE_PNG:
4722 /* save png */
4723 if (!strncasecmp(&filename[strlen(filename)-4], ".png", 4))
4724 strcpy(name, filename);
4725 else
4726 sprintf(name, "%s.png", filename);
4727 priv->save_as_png = true;
4728
4729 break;
4730
4731 case SAVE_MAT:
4732 /* Matlab file
4733 * http://na-wiki.csc.kth.se/mediawiki/index.php/MatIO
4734 */
4735 if (!strncasecmp(&filename[strlen(filename)-4], ".mat", 4))
4736 strcpy(name, filename);
4737 else
4738 sprintf(name, "%s.mat", filename);
4739
4740 mat = Mat_Create(name, NULL);
4741 if (!mat) {
4742 fprintf(stderr, "Error creating MAT file %s: %s\n", name, strerror(errno));
4743 break;
4744 }
4745
4746 active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->device_combobox));
4747 d = device_find_by_name(ctx, active_device);
4748 g_free(active_device);
4749 if (d < 0)
4750 break;
4751
4752 dev = iio_context_get_device(ctx, d);
4753 dev_info = iio_device_get_data(dev);
4754 nb_channels = iio_device_get_channels_count(dev);
4755 dev_name = iio_device_get_name(dev) ?:
4756 iio_device_get_id(dev);
4757
4758 /* Find which channel need to be saved */
4759 save_channels_mask = get_user_saveas_channel_selection(plot, nb_channels);
4760
4761 dev_sample_count = dev_info->sample_count;
4762 if (dev_info->channel_trigger_enabled)
4763 dev_sample_count /= 2;
4764
4765 dims[0] = dev_sample_count;
4766 for (i = 0; i < nb_channels; i++) {
4767 struct iio_channel *chn = iio_device_get_channel(dev, i);
4768 const char *ch_name = iio_channel_get_name(chn) ?:
4769 iio_channel_get_id(chn);
4770 struct extra_info *info = iio_channel_get_data(chn);
4771 if (save_channels_mask[i] == 1)
4772 continue;
4773 sprintf(tmp, "%s_%s", dev_name, ch_name);
4774 g_strdelimit(tmp, "-", '_');
4775 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->save_mat_scale))) {
4776 matvar = Mat_VarCreate(tmp, MAT_C_SINGLE, MAT_T_SINGLE, 2, dims,
4777 info->data_ref, 0);
4778 } else {
4779 const struct iio_data_format* format = iio_channel_get_data_format(chn);
4780 gdouble *tmp_data;
4781 double k;
4782
4783 tmp_data = g_new(gdouble, dev_sample_count);
4784 if (format->is_signed)
4785 k = format->bits - 1;
4786 else
4787 k = format->bits;
4788 for (j = 0; j < dev_sample_count; j++) {
4789 tmp_data[j] = (gdouble)info->data_ref[j] /
4790 (pow(2.0, k));
4791 }
4792 matvar = Mat_VarCreate(tmp, MAT_C_DOUBLE, MAT_T_DOUBLE,
4793 2, dims, tmp_data, 0);
4794 g_free(tmp_data);
4795 }
4796
4797 if (!matvar)
4798 fprintf(stderr,
4799 "error creating matvar on channel %s\n",
4800 tmp);
4801 else {
4802 Mat_VarWrite(mat, matvar, 0);
4803 Mat_VarFree(matvar);
4804 }
4805 }
4806 free(save_channels_mask);
4807
4808 Mat_Close(mat);
4809 break;
4810
4811 default:
4812 fprintf(stderr, "SaveAs response: %i\n", type);
4813 }
4814
4815 if (priv->saveas_filename)
4816 g_free(priv->saveas_filename);
4817
4818 priv->saveas_filename = g_strdup(name);
4819 free(name);
4820 }
4821
cb_saveas_response(GtkDialog * dialog,gint response_id,OscPlot * plot)4822 void cb_saveas_response(GtkDialog *dialog, gint response_id, OscPlot *plot)
4823 {
4824 /* Save as Dialog */
4825 OscPlotPrivate *priv = plot->priv;
4826
4827 priv->saveas_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (priv->saveas_dialog));
4828
4829 if (response_id == GTK_RESPONSE_ACCEPT) {
4830 gint type = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->cmb_saveas_type));
4831 save_as(plot, priv->saveas_filename, type);
4832 }
4833
4834 gtk_widget_hide(priv->saveas_dialog);
4835
4836 if (priv->save_as_png) {
4837 int i = 0, timeout = 1000;
4838 while (gtk_events_pending() && i < timeout) {
4839 i++;
4840 gtk_main_iteration();
4841 }
4842 screenshot_saveas_png(plot);
4843 priv->save_as_png = false;
4844 }
4845 }
4846
enable_auto_scale_cb(GtkToggleButton * button,OscPlot * plot)4847 static void enable_auto_scale_cb(GtkToggleButton *button, OscPlot *plot)
4848 {
4849 OscPlotPrivate *priv = plot->priv;
4850 gfloat left, right, top, bottom;
4851
4852 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale))) {
4853 priv->do_a_rescale_flag = 1;
4854 gtk_widget_set_sensitive(plot->priv->y_axis_max, FALSE);
4855 gtk_widget_set_sensitive(plot->priv->y_axis_min, FALSE);
4856 } else {
4857 gtk_databox_get_visible_limits(GTK_DATABOX(plot->priv->databox),
4858 &left, &right, &top, &bottom);
4859 gtk_spin_button_set_value(GTK_SPIN_BUTTON(plot->priv->y_axis_max),
4860 top);
4861 gtk_widget_set_sensitive(plot->priv->y_axis_max, TRUE);
4862 gtk_spin_button_set_value(GTK_SPIN_BUTTON(plot->priv->y_axis_min),
4863 bottom);
4864 gtk_widget_set_sensitive(plot->priv->y_axis_min, TRUE);
4865 }
4866 }
4867
max_y_axis_cb(GtkSpinButton * btn,OscPlot * plot)4868 static void max_y_axis_cb(GtkSpinButton *btn, OscPlot *plot)
4869 {
4870 GtkDatabox *box;
4871 gfloat min_x;
4872 gfloat max_x;
4873 gfloat min_y;
4874 gfloat max_y;
4875
4876 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(plot->priv->enable_auto_scale)))
4877 return;
4878 box = GTK_DATABOX(plot->priv->databox);
4879 gtk_databox_get_total_limits(box, &min_x, &max_x, &max_y, &min_y);
4880 max_y = gtk_spin_button_get_value(btn);
4881 min_y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(plot->priv->y_axis_min));
4882 gtk_databox_set_total_limits(box, min_x, max_x, max_y, min_y);
4883 }
4884
min_y_axis_cb(GtkSpinButton * btn,OscPlot * plot)4885 static void min_y_axis_cb(GtkSpinButton *btn, OscPlot *plot)
4886 {
4887 GtkDatabox *box;
4888 gfloat min_x;
4889 gfloat max_x;
4890 gfloat min_y;
4891 gfloat max_y;
4892
4893 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(plot->priv->enable_auto_scale)))
4894 return;
4895 box = GTK_DATABOX(plot->priv->databox);
4896 gtk_databox_get_total_limits(box, &min_x, &max_x, &max_y, &min_y);
4897 min_y = gtk_spin_button_get_value(btn);
4898 max_y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(plot->priv->y_axis_max));
4899 gtk_databox_set_total_limits(box, min_x, max_x, max_y, min_y);
4900 }
4901
count_changed_cb(GtkSpinButton * box,OscPlot * plot)4902 static void count_changed_cb(GtkSpinButton *box, OscPlot *plot)
4903 {
4904 OscPlotPrivate *priv = plot->priv;
4905 struct extra_dev_info *dev_info;
4906 gdouble freq = 0;
4907
4908 if (priv->current_device) {
4909 dev_info = iio_device_get_data(priv->current_device);
4910 freq = dev_info->adc_freq * prefix2scale(dev_info->adc_scale);
4911 }
4912
4913 switch(gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units))) {
4914 case HOR_SCALE_SAMPLES:
4915 priv->sample_count = (int)gtk_spin_button_get_value(box);
4916 break;
4917 case HOR_SCALE_TIME:
4918 priv->sample_count = (int)round((gtk_spin_button_get_value(box) *
4919 freq) / pow(10.0, 6));
4920 break;
4921 }
4922 }
4923
units_changed_cb(GtkComboBoxText * box,OscPlot * plot)4924 static void units_changed_cb(GtkComboBoxText *box, OscPlot *plot)
4925 {
4926
4927 OscPlotPrivate *priv = plot->priv;
4928 struct extra_dev_info *dev_info;
4929 int tmp_int;
4930 gdouble freq = 0, tmp_d;
4931 GtkAdjustment *limits;
4932
4933 if (priv->current_device) {
4934 dev_info = iio_device_get_data(priv->current_device);
4935 freq = dev_info->adc_freq * prefix2scale(dev_info->adc_scale);
4936 }
4937
4938 g_signal_handlers_block_by_func(priv->sample_count_widget, G_CALLBACK(count_changed_cb), plot);
4939
4940 limits = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(priv->sample_count_widget));
4941
4942 tmp_int = gtk_combo_box_get_active(GTK_COMBO_BOX(box));
4943 switch(tmp_int) {
4944 case 0:
4945 gtk_label_set_text(GTK_LABEL(priv->hor_scale), "Samples");
4946 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(priv->sample_count_widget), 0);
4947 gtk_adjustment_set_lower(limits, 10.0);
4948 gtk_adjustment_set_upper(limits, MAX_SAMPLES);
4949 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), priv->sample_count);
4950 break;
4951 case 1:
4952 gtk_label_set_text(GTK_LABEL(priv->hor_scale), "µs");
4953 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(priv->sample_count_widget), 3);
4954 if (freq) {
4955 gtk_adjustment_set_lower(limits, 10.0 * pow(10.0, 6)/freq);
4956 gtk_adjustment_set_upper(limits, MAX_SAMPLES * pow(10.0, 6)/freq);
4957 tmp_d = (pow(10.0, 6)/freq) * priv->sample_count;
4958 tmp_d = round(tmp_d * 1000.0) / 1000.0;
4959 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), tmp_d);
4960 }
4961 break;
4962 }
4963
4964 g_signal_handlers_unblock_by_func(priv->sample_count_widget, G_CALLBACK(count_changed_cb), plot);
4965 }
4966
get_iter_by_name(GtkTreeView * tree,GtkTreeIter * iter,const char * dev_name,const char * ch_name)4967 static gboolean get_iter_by_name(GtkTreeView *tree, GtkTreeIter *iter,
4968 const char *dev_name, const char *ch_name)
4969 {
4970 GtkTreeModel *model;
4971 GtkTreeIter dev_iter;
4972 GtkTreeIter ch_iter;
4973 gboolean next_dev_iter;
4974 gboolean next_ch_iter;
4975 char *device;
4976 char *channel;
4977
4978 model = gtk_tree_view_get_model(tree);
4979 if (!gtk_tree_model_get_iter_first(model, &dev_iter))
4980 return FALSE;
4981 if (dev_name == NULL)
4982 return FALSE;
4983
4984 next_dev_iter = true;
4985 while (next_dev_iter) {
4986 gtk_tree_model_iter_children(model, &ch_iter, &dev_iter);
4987 gtk_tree_model_get(model, &dev_iter, ELEMENT_NAME, &device, -1);
4988 if (!strcmp(dev_name, device)) {
4989 g_free(device);
4990 if (ch_name == NULL) {
4991 *iter = dev_iter;
4992 return TRUE;
4993 }
4994 next_ch_iter = true;
4995 while (next_ch_iter) {
4996 gtk_tree_model_get(model, &ch_iter, ELEMENT_NAME, &channel, -1);
4997 if (!strcmp(ch_name, channel)) {
4998 g_free(channel);
4999 *iter = ch_iter;
5000 return TRUE;
5001 }
5002 next_ch_iter = gtk_tree_model_iter_next(model, &ch_iter);
5003 }
5004 }
5005 next_dev_iter = gtk_tree_model_iter_next(model, &dev_iter);
5006 }
5007
5008 return FALSE;
5009 }
5010
plot_profile_save(OscPlot * plot,char * filename)5011 static void plot_profile_save(OscPlot *plot, char *filename)
5012 {
5013 OscPlotPrivate *priv = plot->priv;
5014 GtkTreeView *tree = GTK_TREE_VIEW(priv->channel_list_view);
5015 GtkTreeModel *model;
5016 GtkTreeIter dev_iter;
5017 GtkTreeIter ch_iter;
5018 gboolean next_dev_iter;
5019 gboolean next_ch_iter;
5020 gboolean expanded;
5021 gboolean device_active;
5022 gboolean ch_enabled;
5023 PlotChn *csettings;
5024 FILE *fp;
5025
5026 model = gtk_tree_view_get_model(tree);
5027
5028 fp = fopen(filename, "a");
5029 if (!fp) {
5030 fprintf(stderr, "Failed to open %s : %s\n", filename, strerror(errno));
5031 return;
5032 }
5033 fprintf(fp, "\n[%s%d]\n", CAPTURE_INI_SECTION, priv->object_id);
5034
5035 int tmp_int;
5036 float tmp_float;
5037 gchar *tmp_string;
5038
5039 fprintf(fp, "domain=");
5040 tmp_int = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain));
5041 if (tmp_int == FFT_PLOT)
5042 fprintf(fp, "fft\n");
5043 else if (tmp_int == XY_PLOT)
5044 fprintf(fp, "constellation\n");
5045 else if (tmp_int == TIME_PLOT)
5046 fprintf(fp, "time\n");
5047 else if (tmp_int == XCORR_PLOT)
5048 fprintf(fp, "correlation\n");
5049 else
5050 fprintf(fp, "unknown\n");
5051
5052 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units))) {
5053 case HOR_SCALE_SAMPLES:
5054 fprintf(fp, "sample_count=%d\n",
5055 (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget)));
5056 break;
5057 case HOR_SCALE_TIME:
5058 fprintf(fp, "micro_seconds=%f\n",
5059 gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->sample_count_widget)));
5060 break;
5061 }
5062
5063 tmp_int = comboboxtext_get_active_text_as_int(GTK_COMBO_BOX_TEXT(priv->fft_size_widget));
5064 fprintf(fp, "fft_size=%d\n", tmp_int);
5065
5066 tmp_string = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->fft_win_widget));
5067 fprintf(fp, "fft_win=%s\n", tmp_string);
5068
5069 tmp_int = (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_avg_widget));
5070 fprintf(fp, "fft_avg=%d\n", tmp_int);
5071
5072 tmp_float = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->fft_pwr_offset_widget));
5073 fprintf(fp, "fft_pwr_offset=%f\n", tmp_float);
5074
5075 tmp_string = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->plot_type));
5076 fprintf(fp, "graph_type=%s\n", tmp_string);
5077 g_free(tmp_string);
5078
5079 tmp_int = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->show_grid));
5080 fprintf(fp, "show_grid=%d\n", tmp_int);
5081
5082 tmp_int = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale));
5083 fprintf(fp, "enable_auto_scale=%d\n", tmp_int);
5084
5085 tmp_float = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->y_axis_max));
5086 fprintf(fp, "user_y_axis_max=%f\n", tmp_float);
5087
5088 tmp_float = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->y_axis_min));
5089 fprintf(fp, "user_y_axis_min=%f\n", tmp_float);
5090
5091 gfloat left, right, top, bottom;
5092 gtk_databox_get_visible_limits(GTK_DATABOX(priv->databox), &left, &right, &top, &bottom);
5093 fprintf(fp, "x_axis_min=%f\n", left);
5094 fprintf(fp, "x_axis_max=%f\n", right);
5095 fprintf(fp, "y_axis_min=%f\n", bottom);
5096 fprintf(fp, "y_axis_max=%f\n", top);
5097
5098 fprintf(fp, "line_thickness = %i\n", priv->line_thickness);
5099
5100 fprintf(fp, "plot_title = %s\n", gtk_window_get_title(GTK_WINDOW(priv->window)));
5101
5102 fprintf(fp, "show_capture_options = %d\n", gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(priv->menu_show_options)));
5103
5104 gtk_window_get_size(GTK_WINDOW(priv->window), &priv->size.width, &priv->size.height);
5105 fprintf(fp, "plot_width = %d\n", priv->size.width);
5106 fprintf(fp, "plot_height = %d\n", priv->size.height);
5107
5108 gint x_pos, y_pos;
5109 gtk_window_get_position(GTK_WINDOW(priv->window), &x_pos, &y_pos);
5110 fprintf(fp, "plot_x_pos=%d\n", x_pos);
5111 fprintf(fp, "plot_y_pos=%d\n", y_pos);
5112
5113 next_dev_iter = gtk_tree_model_get_iter_first(model, &dev_iter);
5114 while (next_dev_iter) {
5115 struct iio_device *dev;
5116 char *name;
5117
5118 gtk_tree_model_get(model, &dev_iter,
5119 ELEMENT_REFERENCE, &dev,
5120 ELEMENT_NAME, &name,
5121 DEVICE_ACTIVE, &device_active,
5122 -1);
5123 /* TO DO: Remove this hack (the if-branch) that skips saving to
5124 * .ini file all settings of the Math device including its math
5125 * expressions. Implement a way to save and also load math
5126 * expressions. */
5127 if (!strncmp(name, "Math", strlen("Math"))) {
5128 g_free(name);
5129 next_dev_iter = gtk_tree_model_iter_next(model, &dev_iter);
5130 continue;
5131 }
5132
5133 expanded = gtk_tree_view_row_expanded(tree, gtk_tree_model_get_path(model, &dev_iter));
5134 fprintf(fp, "%s.expanded=%d\n", name, (expanded) ? 1 : 0);
5135 fprintf(fp, "%s.active=%d\n", name, (device_active) ? 1 : 0);
5136
5137 if (dev) {
5138 struct extra_dev_info *info = iio_device_get_data(dev);
5139 fprintf(fp, "%s.trigger_enabled=%i\n", name,
5140 info->channel_trigger_enabled);
5141 if (info->channel_trigger_enabled) {
5142 fprintf(fp, "%s.trigger_channel=%u\n", name,
5143 info->channel_trigger);
5144 fprintf(fp, "%s.trigger_falling_edge=%i\n", name,
5145 info->trigger_falling_edge);
5146 fprintf(fp, "%s.trigger_value=%f\n", name,
5147 info->trigger_value);
5148 }
5149 }
5150
5151 next_ch_iter = gtk_tree_model_iter_children(model, &ch_iter, &dev_iter);
5152 while (next_ch_iter) {
5153 struct iio_channel *ch;
5154 char *ch_name;
5155
5156 gtk_tree_model_get(model, &ch_iter,
5157 ELEMENT_REFERENCE, &ch,
5158 CHANNEL_ACTIVE, &ch_enabled,
5159 CHANNEL_SETTINGS, &csettings,
5160 -1);
5161 ch_name = csettings->name;
5162
5163 fprintf(fp, "%s.%s.enabled=%d\n", name, ch_name, (ch_enabled) ? 1 : 0);
5164 fprintf(fp, "%s.%s.color_red=%d\n", name, ch_name, csettings->graph_color.red);
5165 fprintf(fp, "%s.%s.color_green=%d\n", name, ch_name, csettings->graph_color.green);
5166 fprintf(fp, "%s.%s.color_blue=%d\n", name, ch_name, csettings->graph_color.blue);
5167 switch (csettings->type) {
5168 case PLOT_IIO_CHANNEL:
5169 fprintf(fp, "%s.%s.math_apply_inverse_funct=%d\n", name, ch_name, PLOT_IIO_CHN(csettings)->apply_inverse_funct);
5170 fprintf(fp, "%s.%s.math_apply_multiply_funct=%d\n", name, ch_name, PLOT_IIO_CHN(csettings)->apply_multiply_funct);
5171 fprintf(fp, "%s.%s.math_apply_add_funct=%d\n", name, ch_name, PLOT_IIO_CHN(csettings)->apply_add_funct);
5172 fprintf(fp, "%s.%s.math_multiply_value=%f\n", name, ch_name, PLOT_IIO_CHN(csettings)->multiply_value);
5173 fprintf(fp, "%s.%s.math_add_value=%f\n", name, ch_name, PLOT_IIO_CHN(csettings)->add_value);
5174 break;
5175 case PLOT_MATH_CHANNEL:
5176 break;
5177 }
5178 next_ch_iter = gtk_tree_model_iter_next(model, &ch_iter);
5179 }
5180 g_free(name);
5181 next_dev_iter = gtk_tree_model_iter_next(model, &dev_iter);
5182 }
5183
5184 if (priv->marker_type == MARKER_OFF)
5185 fprintf(fp, "marker_type = %s\n", OFF_MRK);
5186 else if (priv->marker_type == MARKER_PEAK)
5187 fprintf(fp, "marker_type = %s\n", PEAK_MRK);
5188 else if (priv->marker_type == MARKER_FIXED)
5189 fprintf(fp, "marker_type = %s\n", FIX_MRK);
5190 else if (priv->marker_type == MARKER_ONE_TONE)
5191 fprintf(fp, "marker_type = %s\n", SINGLE_MRK);
5192 else if (priv->marker_type == MARKER_TWO_TONE)
5193 fprintf(fp, "marker_type = %s\n", DUAL_MRK);
5194 else if (priv->marker_type == MARKER_IMAGE)
5195 fprintf(fp, "marker_type = %s\n", IMAGE_MRK);
5196
5197 for (tmp_int = 0; tmp_int <= MAX_MARKERS; tmp_int++) {
5198 if (priv->markers[tmp_int].active)
5199 fprintf(fp, "marker.%i = %i\n", tmp_int, priv->markers[tmp_int].bin);
5200 }
5201
5202 fprintf(fp, "capture_started=%d\n", (priv->redraw_function) ? 1 : 0);
5203 fclose(fp);
5204 }
5205
comboboxtext_set_active_by_string(GtkComboBox * combo_box,const char * name)5206 static int comboboxtext_set_active_by_string(GtkComboBox *combo_box, const char *name)
5207 {
5208 GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
5209 GtkTreeIter iter;
5210 gboolean has_iter;
5211 char *item;
5212
5213 has_iter = gtk_tree_model_get_iter_first(model, &iter);
5214 while (has_iter) {
5215 gtk_tree_model_get(model, &iter, 0, &item, -1);
5216 if (strcmp(name, item) == 0) {
5217 g_free(item);
5218 gtk_combo_box_set_active_iter(combo_box, &iter);
5219 return 1;
5220 }
5221 has_iter = gtk_tree_model_iter_next(model, &iter);
5222 }
5223
5224 return 0;
5225 }
5226
comboboxtext_get_active_text_as_int(GtkComboBoxText * combobox)5227 static int comboboxtext_get_active_text_as_int(GtkComboBoxText* combobox)
5228 {
5229 gchar *active_text;
5230 int value = 0;
5231
5232 active_text = gtk_combo_box_text_get_active_text(combobox);
5233 if (active_text) {
5234 value = atoi(active_text);
5235 g_free(active_text);
5236 }
5237
5238 return value;
5239 }
5240
5241 #define MATCH(s1, s2) strcmp(s1, s2) == 0
5242 #define MATCH_N(s1, s2, n) strncmp(s1, s2, n) == 0
5243 #define MATCH_SECT(s) strcmp(section, s) == 0
5244 #define MATCH_NAME(n) strcmp(name, n) == 0
5245 #define PLOT_ATTRIBUTE 0
5246 #define DEVICE 1
5247 #define CHANNEL 2
5248
device_find_by_name(struct iio_context * ctx,const char * name)5249 static int device_find_by_name(struct iio_context *ctx, const char *name)
5250 {
5251 unsigned int num_devices = 0;
5252 unsigned int i;
5253
5254 if (!name)
5255 return -1;
5256 if (ctx)
5257 num_devices = iio_context_get_devices_count(ctx);
5258
5259 for (i = 0; i < num_devices; i++) {
5260 struct iio_device *dev = iio_context_get_device(ctx, i);
5261 const char *id = iio_device_get_name(dev) ?:
5262 iio_device_get_id(dev);
5263 if (!strcmp(id, name))
5264 return i;
5265 }
5266 return -1;
5267 }
5268
channel_find_by_name(struct iio_context * ctx,int device_index,const char * name)5269 static int channel_find_by_name(struct iio_context *ctx, int device_index,
5270 const char *name)
5271 {
5272 struct iio_device *dev;
5273 unsigned int i, nb_channels = 0;
5274
5275 if (!ctx)
5276 return -1;
5277 if (!name)
5278 return -1;
5279
5280 dev = iio_context_get_device(ctx, device_index);
5281 if (dev)
5282 nb_channels = iio_device_get_channels_count(dev);
5283
5284 for (i = 0; i < nb_channels; i++) {
5285 struct iio_channel *chn = iio_device_get_channel(dev, i);
5286 const char *id = iio_channel_get_name(chn) ?:
5287 iio_channel_get_id(chn);
5288 if (!strcmp(id, name))
5289 return i;
5290 }
5291 return -1;
5292 }
5293
count_char_in_string(char c,const char * s)5294 static int count_char_in_string(char c, const char *s)
5295 {
5296 int i;
5297
5298 for (i = 0; s[i];)
5299 if (s[i] == c)
5300 i++;
5301 else
5302 s++;
5303
5304 return i;
5305 }
5306
osc_plot_ini_read_handler(OscPlot * plot,int line,const char * section,const char * name,const char * value)5307 int osc_plot_ini_read_handler (OscPlot *plot, int line, const char *section,
5308 const char *name, const char *value)
5309 {
5310 OscPlotPrivate *priv = plot->priv;
5311 struct iio_context *ctx = priv->ctx;
5312 GtkTreeView *tree = GTK_TREE_VIEW(priv->channel_list_view);
5313 GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(tree));
5314 GtkTreeIter dev_iter, ch_iter;
5315 int dev, ch;
5316 char *dev_name, *ch_name;
5317 char *dev_property, *ch_property;
5318 gboolean expanded;
5319 gboolean device_active;
5320 gboolean enabled;
5321 int elem_type;
5322 gchar **elems = NULL, **min_max = NULL;
5323 gfloat max_f, min_f;
5324 PlotChn *csettings;
5325 int ret = 0, i;
5326 FILE *fd;
5327 struct extra_dev_info *dev_info;
5328
5329 elem_type = count_char_in_string('.', name);
5330 switch(elem_type) {
5331 case PLOT_ATTRIBUTE:
5332 if (MATCH_NAME("capture_started")) {
5333 if (priv->redraw_function && atoi(value))
5334 goto handled;
5335 treeview_expand_update(plot);
5336 treeview_icon_color_update(plot);
5337 max_y_axis_cb(GTK_SPIN_BUTTON(plot->priv->y_axis_max), plot);
5338 min_y_axis_cb(GTK_SPIN_BUTTON(plot->priv->y_axis_min), plot);
5339 if (priv->read_scale_params == 4) {
5340 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_min), priv->plot_bottom);
5341 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_max), priv->plot_top);
5342 gtk_databox_set_total_limits(GTK_DATABOX(priv->databox), priv->plot_left,
5343 priv->plot_right, priv->plot_top, priv->plot_bottom);
5344 priv->read_scale_params = 0;
5345 }
5346 check_valid_setup(plot);
5347 priv->profile_loaded_scale = TRUE;
5348 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->capture_button), atoi(value));
5349 priv->profile_loaded_scale = FALSE;
5350 osc_plot_set_visible(plot, true);
5351 } else if (MATCH_NAME("destroy_plot")) {
5352 osc_plot_destroy(plot);
5353 } else if (MATCH_NAME("domain")) {
5354 if (!strcmp(value, "time"))
5355 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), TIME_PLOT);
5356 else if (!strcmp(value, "fft"))
5357 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), FFT_PLOT);
5358 else if (!strcmp(value, "constellation"))
5359 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), XY_PLOT);
5360 else if (!strcmp(value, "correlation"))
5361 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), XCORR_PLOT);
5362 else
5363 goto unhandled;
5364 } else if (MATCH_NAME("sample_count")) {
5365 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->hor_units), HOR_SCALE_SAMPLES);
5366 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), atof(value));
5367 } else if (MATCH_NAME("micro_seconds")) {
5368 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->hor_units), HOR_SCALE_TIME);
5369 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), atof(value));
5370 } else if (MATCH_NAME("fft_size")) {
5371 if (!comboboxtext_set_active_by_string(GTK_COMBO_BOX(priv->fft_size_widget), value)) {
5372 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(priv->fft_size_widget), value);
5373 if (!comboboxtext_set_active_by_string(GTK_COMBO_BOX(priv->fft_size_widget), value))
5374 goto unhandled;
5375 }
5376 } else if (MATCH_NAME("fft_win")) {
5377 if (!comboboxtext_set_active_by_string(GTK_COMBO_BOX(priv->fft_win_widget), value))
5378 goto unhandled;
5379 } else if (MATCH_NAME("fft_avg")) {
5380 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->fft_avg_widget), atoi(value));
5381 } else if (MATCH_NAME("fft_pwr_offset")) {
5382 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->fft_pwr_offset_widget), atof(value));
5383 } else if (MATCH_NAME("graph_type")) {
5384 if (!comboboxtext_set_active_by_string(GTK_COMBO_BOX(priv->plot_type), value))
5385 goto unhandled;
5386 } else if (MATCH_NAME("show_grid"))
5387 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->show_grid), atoi(value));
5388 else if (MATCH_NAME("enable_auto_scale")) {
5389 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->enable_auto_scale), atoi(value));
5390 if (atoi(value)) {
5391 gtk_widget_hide(priv->y_axis_max);
5392 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(priv->builder, "labelYMax")));
5393 gtk_widget_hide(priv->y_axis_min);
5394 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(priv->builder, "labelYMin")));
5395 }
5396 } else if (MATCH_NAME("user_y_axis_max"))
5397 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_max), atof(value));
5398 else if (MATCH_NAME("user_y_axis_min"))
5399 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_min), atof(value));
5400 else if (MATCH_NAME("x_axis_min")) {
5401 priv->plot_left = atof(value);
5402 priv->read_scale_params++;
5403 } else if (MATCH_NAME("x_axis_max")) {
5404 priv->plot_right = atof(value);
5405 priv->read_scale_params++;
5406 } else if (MATCH_NAME("y_axis_min")) {
5407 priv->plot_bottom = atof(value);
5408 priv->read_scale_params++;
5409 } else if (MATCH_NAME("y_axis_max")) {
5410 priv->plot_top = atof(value);
5411 priv->read_scale_params++;
5412 } else if (MATCH_NAME("plot_title")) {
5413 gtk_window_set_title(GTK_WINDOW(priv->window), value);
5414 } else if (MATCH_NAME("show_capture_options")) {
5415 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(priv->menu_show_options), atoi(value));
5416 } else if (MATCH_NAME("plot_width")) {
5417 priv->size.width = atoi(value);
5418 gtk_window_resize(GTK_WINDOW(priv->window), priv->size.width, priv->size.height);
5419 } else if (MATCH_NAME("plot_height")) {
5420 priv->size.height = atoi(value);
5421 gtk_window_resize(GTK_WINDOW(priv->window), priv->size.width, priv->size.height);
5422 } else if (MATCH_NAME("plot_x_pos")) {
5423 if (atoi(value)) {
5424 priv->plot_x_pos = atoi(value);
5425 move_gtk_window_on_screen(GTK_WINDOW(priv->window), priv->plot_x_pos, priv->plot_y_pos);
5426 }
5427 } else if (MATCH_NAME("plot_y_pos")) {
5428 if (atoi(value)) {
5429 priv->plot_y_pos = atoi(value);
5430 move_gtk_window_on_screen(GTK_WINDOW(priv->window), priv->plot_x_pos, priv->plot_y_pos);
5431 }
5432 } else if (MATCH_NAME("marker_type")) {
5433 if (!strncmp(value, PEAK_MRK, strlen(PEAK_MRK))) {
5434 printf("set to peak\n");
5435 osc_plot_set_marker_type(plot, MARKER_PEAK);
5436 } else if (!strncmp(value, FIX_MRK, strlen(FIX_MRK))) {
5437 printf("set to fixed\n");
5438 osc_plot_set_marker_type(plot, MARKER_FIXED);
5439 } else if (!strncmp(value, SINGLE_MRK, strlen(SINGLE_MRK))) {
5440 printf("set to single tone markers\n");
5441 osc_plot_set_marker_type(plot, MARKER_ONE_TONE);
5442 } else if (!strncmp(value, DUAL_MRK, strlen(DUAL_MRK))) {
5443 printf("set to two tone markers\n");
5444 osc_plot_set_marker_type(plot, MARKER_TWO_TONE);
5445 } else if (!strncmp(value, IMAGE_MRK, strlen(IMAGE_MRK))) {
5446 printf("set to image markers\n");
5447 osc_plot_set_marker_type(plot, MARKER_IMAGE);
5448 } else {
5449 printf("setting all off\n");
5450 osc_plot_set_marker_type(plot, MARKER_OFF);
5451 for (i = 0; i <= MAX_MARKERS; i++)
5452 priv->markers[i].active = FALSE;
5453 }
5454 } else if (MATCH_NAME("save_png")) {
5455 save_as(plot, value, SAVE_PNG);
5456 i = 0;
5457 while (gtk_events_pending() && i < 1000) {
5458 gtk_main_iteration();
5459 i++;
5460 }
5461 screenshot_saveas_png(plot);
5462 priv->save_as_png = false;
5463 } else if (MATCH_NAME("cycle")) {
5464 unsigned int msecs;
5465 sscanf(value, "%u", &msecs);
5466 osc_process_gtk_events(msecs);
5467 } else if (MATCH_NAME("save_markers")) {
5468 fd = osc_get_log_file(value);
5469 if (!fd)
5470 return 0;
5471
5472 for (i = 0; i <= MAX_MARKERS; i++) {
5473 if (priv->markers[i].active) {
5474 fprintf(fd, ", %f, %f", priv->markers[i].x, priv->markers[i].y);
5475 if (!isnan(priv->markers[i].angle))
5476 fprintf(fd, ", %f", priv->markers[i].angle);
5477 }
5478 }
5479 fprintf(fd, "\n");
5480 fclose(fd);
5481 } else if (MATCH_NAME("fru_connect")) {
5482 if (atoi(value) == 1) {
5483 i = fru_connect();
5484 if (i == GTK_RESPONSE_OK)
5485 ret = 0;
5486 else
5487 ret = -1;
5488 } else {
5489 ret = -1;
5490 }
5491 } else if (MATCH_NAME("line_thickness")) {
5492 if (atoi(value))
5493 priv->line_thickness = atoi(value);
5494 } else if (MATCH_NAME("quit") || MATCH_NAME("stop")) {
5495 application_quit();
5496 return 0;
5497 } else if (MATCH_NAME("echo")) {
5498 printf("echoing : '%s'\n", value);
5499 ret = 0;
5500 } else {
5501 goto unhandled;
5502 }
5503 break;
5504 case DEVICE:
5505 elems = g_strsplit(name, ".", DEVICE + 1);
5506 dev_name = elems[0];
5507 dev_property = elems[1];
5508
5509 /* Check for markers */
5510 if (MATCH(elems[0], "marker")) {
5511 i = atoi(elems[1]);
5512 priv->markers[i].bin = atoi(value);
5513 priv->markers[i].active = TRUE;
5514 break;
5515 } else if (MATCH(elems[0], "test")) {
5516 if (MATCH(elems[1], "message")) {
5517 create_blocking_popup(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK,
5518 "Profile status", value);
5519 break;
5520 } else {
5521 goto unhandled;
5522 }
5523 }
5524 /* TO DO: Remove this hack (the if-branch) that skips
5525 * loading the settings of a math device and implement a way
5526 * to load and save a math expression. */
5527 if (strncmp(dev_name, "Math", 4) == 0) {
5528 break;
5529 }
5530
5531 if (strncmp(dev_name, "Math", 4) == 0) {
5532 dev_info = NULL;
5533 } else {
5534 dev = device_find_by_name(ctx, dev_name);
5535 if (dev == -1)
5536 goto unhandled;
5537 dev_info = iio_device_get_data(iio_context_find_device(ctx, dev_name));
5538 }
5539
5540 if (MATCH(dev_property, "expanded")) {
5541 expanded = atoi(value);
5542 get_iter_by_name(tree, &dev_iter, dev_name, NULL);
5543 gtk_tree_store_set(store, &dev_iter, EXPANDED, expanded, -1);
5544 }else if (MATCH(dev_property, "active")) {
5545 device_active = atoi(value);
5546 get_iter_by_name(tree, &dev_iter, dev_name, NULL);
5547 gtk_tree_store_set(store, &dev_iter, DEVICE_ACTIVE, device_active, -1);
5548 } else if (MATCH(dev_property, "trigger_enabled")) {
5549 if (!dev_info)
5550 goto unhandled;
5551 dev_info->channel_trigger_enabled = !!atoi(value);
5552 } else if (MATCH(dev_property, "trigger_channel")) {
5553 if (!dev_info)
5554 goto unhandled;
5555 dev_info->channel_trigger = atoi(value);
5556 } else if (MATCH(dev_property, "trigger_falling_edge")) {
5557 if (!dev_info)
5558 goto unhandled;
5559 dev_info->trigger_falling_edge = !!atoi(value);
5560 } else if (MATCH(dev_property, "trigger_value")) {
5561 if (!dev_info)
5562 goto unhandled;
5563 dev_info->trigger_value = (float) atof(value);
5564 }
5565 break;
5566 case CHANNEL:
5567 elems = g_strsplit(name, ".", CHANNEL + 1);
5568 dev_name = elems[0];
5569 ch_name = elems[1];
5570 ch_property = elems[2];
5571
5572 if (g_str_has_prefix(ch_name, "in_"))
5573 ch_name = ch_name + strlen("in_");
5574
5575 if (MATCH(elems[0], "test")) {
5576 if (MATCH(elems[1], "marker")) {
5577 min_max = g_strsplit(value, " ", 0);
5578 min_f = atof(min_max[0]);
5579 max_f = atof(min_max[1]);
5580 i = atoi(elems[2]);
5581
5582 printf("Line %i: (test.marker.%i = %f %f): %f\n",
5583 line, i, min_f, max_f,
5584 priv->markers[i].y);
5585 if (priv->markers[i].active &&
5586 priv->markers[i].y >= min_f &&
5587 priv->markers[i].y <= max_f) {
5588 ret = 0;
5589 printf("Test passed.\n");
5590 } else {
5591 ret = -1;
5592 printf("*** Test failed! ***\n");
5593 create_blocking_popup(GTK_MESSAGE_ERROR,
5594 GTK_BUTTONS_CLOSE,
5595 "Test failure",
5596 "Test failed! Line: %i\n\n"
5597 "Test was: test.marker.%i = %f %f\n"
5598 "Value read = %f\n",
5599 line, i, min_f, max_f, priv->markers[i].y);
5600 }
5601 g_strfreev(min_max);
5602 } else {
5603 goto unhandled;
5604 }
5605 break;
5606 }
5607
5608 /* TO DO: Remove this hack (the if-branch) that skips
5609 * loading settings of channels of a math device and
5610 * implement a way to load and save a math expression. */
5611 if (strncmp(dev_name, "Math", 4) == 0)
5612 break;
5613
5614 dev = device_find_by_name(ctx, dev_name);
5615 if (dev == -1)
5616 goto unhandled;
5617 ch = channel_find_by_name(ctx, dev, ch_name);
5618 if (ch == -1)
5619 goto unhandled;
5620 if (MATCH(ch_property, "enabled")) {
5621 enabled = atoi(value);
5622 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5623 set_channel_state_in_tree_model(gtk_tree_view_get_model(tree), &ch_iter, enabled);
5624 } else if (MATCH(ch_property, "color_red")) {
5625 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5626 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5627 csettings->graph_color.red = atoi(value);
5628 } else if (MATCH(ch_property, "color_green")) {
5629 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5630 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5631 csettings->graph_color.green = atoi(value);
5632 } else if (MATCH(ch_property, "color_blue")) {
5633 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5634 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5635 csettings->graph_color.blue = atoi(value);
5636 } else if (MATCH(ch_property, "math_apply_inverse_funct")) {
5637 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5638 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5639 PLOT_IIO_CHN(csettings)->apply_inverse_funct = atoi(value);
5640 } else if (MATCH(ch_property, "math_apply_multiply_funct")) {
5641 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5642 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5643 PLOT_IIO_CHN(csettings)->apply_multiply_funct = atoi(value);
5644 } else if (MATCH(ch_property, "math_apply_add_funct")) {
5645 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5646 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5647 PLOT_IIO_CHN(csettings)->apply_add_funct = atoi(value);
5648 } else if (MATCH(ch_property, "math_multiply_value")) {
5649 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5650 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5651 PLOT_IIO_CHN(csettings)->multiply_value = atof(value);
5652 } else if (MATCH(ch_property, "math_add_value")) {
5653 get_iter_by_name(tree, &ch_iter, dev_name, ch_name);
5654 gtk_tree_model_get(gtk_tree_view_get_model(tree), &ch_iter, CHANNEL_SETTINGS, &csettings, -1);
5655 PLOT_IIO_CHN(csettings)->add_value = atof(value);
5656 }
5657 break;
5658 default:
5659 unhandled:
5660 create_blocking_popup(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
5661 "Unhandled attribute",
5662 "Unhandled attribute in section [%s], "
5663 "line %i:\n%s = %s\n",section, line, name, value);
5664 fprintf(stderr, "Unhandled tokens in section [%s], line: %i: "
5665 "%s = %s\n", section, line, name, value);
5666 ret = -1;
5667 break;
5668 }
5669 handled:
5670 if (elems != NULL)
5671 g_strfreev(elems);
5672
5673 return ret;
5674 }
5675
treeview_expand_update(OscPlot * plot)5676 static void treeview_expand_update(OscPlot *plot)
5677 {
5678 GtkTreeIter dev_iter;
5679 GtkTreeView *tree = GTK_TREE_VIEW(plot->priv->channel_list_view);
5680 GtkTreeModel *model;
5681 gboolean next_dev_iter;
5682 gboolean expanded;
5683
5684 model = gtk_tree_view_get_model(tree);
5685 next_dev_iter = gtk_tree_model_get_iter_first(model, &dev_iter);
5686 while (next_dev_iter) {
5687 gtk_tree_model_get(model, &dev_iter, EXPANDED, &expanded, -1);
5688 expand_iter(plot, &dev_iter, expanded);
5689 next_dev_iter = gtk_tree_model_iter_next(model, &dev_iter);
5690 }
5691 }
5692
treeview_icon_color_update(OscPlot * plot)5693 static void treeview_icon_color_update(OscPlot *plot)
5694 {
5695 foreach_device_iter(GTK_TREE_VIEW(plot->priv->channel_list_view),
5696 *iter_children_icon_color_update, NULL);
5697 }
5698
marker_set(OscPlot * plot,int i,char * buf,bool force)5699 static inline void marker_set(OscPlot *plot, int i, char *buf, bool force)
5700 {
5701 OscPlotPrivate *priv = plot->priv;
5702
5703 if (force)
5704 priv->markers[i].active = TRUE;
5705
5706 if (priv->markers[i].graph) {
5707 snprintf(priv->markers[i].label, sizeof(priv->markers[i].label), buf, i);
5708 gtk_databox_markers_set_label(GTK_DATABOX_MARKERS(priv->markers[i].graph), 0,
5709 GTK_DATABOX_MARKERS_TEXT_N, priv->markers[i].label, FALSE);
5710 gtk_databox_graph_set_hide(priv->markers[i].graph, !priv->markers[i].active);
5711 }
5712 }
5713
set_marker_labels(OscPlot * plot,gchar * buf,enum marker_types type)5714 static void set_marker_labels (OscPlot *plot, gchar *buf, enum marker_types type)
5715 {
5716 OscPlotPrivate *priv = plot->priv;
5717 int i;
5718
5719 if (!MAX_MARKERS)
5720 return;
5721
5722 if ((buf && !strcmp(buf, PEAK_MRK)) || type == MARKER_PEAK) {
5723 priv->marker_type = MARKER_PEAK;
5724 for (i = 0; i <= MAX_MARKERS; i++)
5725 marker_set(plot, i, "P%i", FALSE);
5726 return;
5727 } else if ((buf && !strcmp(buf, FIX_MRK)) || type == MARKER_FIXED) {
5728 priv->marker_type = MARKER_FIXED;
5729 for (i = 0; i <= MAX_MARKERS; i++)
5730 marker_set(plot, i, "F%i", FALSE);
5731 return;
5732 } else if ((buf && !strcmp(buf, SINGLE_MRK)) || type == MARKER_ONE_TONE) {
5733 priv->marker_type = MARKER_ONE_TONE;
5734 marker_set(plot, 0, "Fund", TRUE);
5735 marker_set(plot, 1, "DC", TRUE);
5736 for (i = 2; i <= MAX_MARKERS; i++)
5737 marker_set(plot, i, "%iH", FALSE);
5738 return;
5739 } else if ((buf && !strcmp(buf, DUAL_MRK)) || type == MARKER_TWO_TONE) {
5740 priv->marker_type = MARKER_TWO_TONE;
5741 return;
5742 } else if ((buf && !strcmp(buf, IMAGE_MRK)) || type == MARKER_IMAGE) {
5743 priv->marker_type = MARKER_IMAGE;
5744 marker_set(plot, 0, "Fund", TRUE);
5745 marker_set(plot, 1, "DC", TRUE);
5746 marker_set(plot, 2, "Image", TRUE);
5747 for (i = 3; i <= MAX_MARKERS; i++) {
5748 priv->markers[i].active = FALSE;
5749 if(priv->markers[i].graph)
5750 gtk_databox_graph_set_hide(priv->markers[i].graph, TRUE);
5751 }
5752 return;
5753 } else if ((buf && !strcmp(buf, OFF_MRK)) || type == MARKER_OFF) {
5754 priv->marker_type = MARKER_OFF;
5755 for (i = 0; i <= MAX_MARKERS; i++) {
5756 if (priv->markers[i].graph)
5757 gtk_databox_graph_set_hide(priv->markers[i].graph, TRUE);
5758 }
5759 return;
5760 } else if (buf && !strcmp(buf, REMOVE_MRK)) {
5761 for (i = MAX_MARKERS; i != 0; i--) {
5762 if (priv->markers[i].active) {
5763 priv->markers[i].active = FALSE;
5764 gtk_databox_graph_set_hide(priv->markers[i].graph, TRUE);
5765 break;
5766 }
5767 }
5768 return;
5769 } else if (buf && !strcmp(buf, ADD_MRK)) {
5770 for (i = 0; i <= MAX_MARKERS; i++) {
5771 if (!priv->markers[i].active) {
5772 priv->markers[i].active = TRUE;
5773 gtk_databox_graph_set_hide(priv->markers[i].graph, FALSE);
5774 break;
5775 }
5776 }
5777 return;
5778 }
5779
5780 fprintf(stderr, "unhandled event at %s : %s\n", __func__, buf ? buf : "<null>");
5781 }
5782
marker_menu(struct string_and_plot * string_data)5783 static void marker_menu (struct string_and_plot *string_data)
5784 {
5785 set_marker_labels(string_data->plot, string_data->string_obj, MARKER_NULL);
5786 }
5787
moved_fixed(GtkDatabox * box,GdkEventMotion * event,gpointer user_data)5788 static gint moved_fixed(GtkDatabox *box, GdkEventMotion *event, gpointer user_data)
5789 {
5790 struct int_and_plot *data = user_data;
5791 OscPlot *plot = data->plot;
5792 OscPlotPrivate *priv = plot->priv;
5793 int mark = data->int_obj;
5794 int max_size;
5795 gfloat *X = Transform_get_x_axis_ref(priv->tr_with_marker);
5796
5797 if (priv->active_transform_type == COMPLEX_FFT_TRANSFORM)
5798 max_size = priv->tr_with_marker->x_axis_size;
5799 else
5800 max_size = priv->tr_with_marker->x_axis_size / 2;
5801
5802 while((gfloat)X[priv->markers[mark].bin] < gtk_databox_pixel_to_value_x(box, event->x) &&
5803 priv->markers[mark].bin < max_size)
5804 priv->markers[mark].bin++;
5805
5806 while((gfloat)X[priv->markers[mark].bin] > gtk_databox_pixel_to_value_x(box, event->x) &&
5807 priv->markers[mark].bin > 0)
5808 priv->markers[mark].bin--;
5809
5810 return FALSE;
5811 }
5812
marker_button(GtkDatabox * box,GdkEventButton * event,gpointer data)5813 static gint marker_button(GtkDatabox *box, GdkEventButton *event, gpointer data)
5814 {
5815 OscPlot *plot = (OscPlot *)data;
5816 OscPlotPrivate *priv = plot->priv;
5817 gfloat x, y, dist;
5818 GtkWidget *popupmenu, *menuitem;
5819 gfloat left, right, top, bottom;
5820 int i, fix = -1;
5821 bool full = TRUE, empty = TRUE;
5822
5823 /* FFT? */
5824 if (!is_frequency_transform(priv) &&
5825 priv->active_transform_type != CROSS_CORRELATION_TRANSFORM)
5826 return FALSE;
5827
5828 /* Right button */
5829 if (event->button != 3)
5830 return FALSE;
5831
5832 /* things are running? */
5833 if (!priv->markers[0].graph)
5834 return FALSE;
5835
5836 if (event->type == GDK_BUTTON_RELEASE) {
5837 if (priv->fixed_marker_hid) {
5838 g_signal_handler_disconnect(box, priv->fixed_marker_hid);
5839 priv->fixed_marker_hid = 0;
5840 return TRUE;
5841 }
5842 return FALSE;
5843 }
5844
5845 x = gtk_databox_pixel_to_value_x(box, event->x);
5846 y = gtk_databox_pixel_to_value_y(box, event->y);
5847 gtk_databox_get_total_limits(box, &left, &right, &top, &bottom);
5848
5849 for (i = 0; i <= MAX_MARKERS; i++) {
5850 if (priv->marker_type == MARKER_FIXED) {
5851 /* sqrt of ((delta X / X range)^2 + (delta Y / Y range)^2 ) */
5852 dist = sqrtf(powf((x - priv->markers[i].x) / (right - left), 2.0) +
5853 powf((y - priv->markers[i].y) / (bottom - top), 2.0)) * 100;
5854 if (dist <= 2.0)
5855 fix = i;
5856 }
5857 if (!priv->markers[i].active)
5858 full = FALSE;
5859 else if (empty)
5860 empty = FALSE;
5861 }
5862
5863 priv->fix_marker.int_obj = fix;
5864 priv->fix_marker.plot = plot;
5865 if (fix != -1) {
5866 priv->fixed_marker_hid = g_signal_connect(box, "motion_notify_event",
5867 G_CALLBACK(moved_fixed), (gpointer) &priv->fix_marker);
5868 return TRUE;
5869 }
5870
5871 popupmenu = gtk_menu_new();
5872
5873 i = 0;
5874 if (!full && !(priv->marker_type == MARKER_OFF || priv->marker_type == MARKER_IMAGE)) {
5875 menuitem = gtk_menu_item_new_with_label(ADD_MRK);
5876 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5877 g_signal_connect_swapped(menuitem, "activate",
5878 G_CALLBACK(marker_menu), (gpointer) &priv->add_mrk);
5879 gtk_widget_show(menuitem);
5880 i++;
5881 }
5882
5883 if (!empty && !(priv->marker_type == MARKER_OFF || priv->marker_type == MARKER_IMAGE)) {
5884 menuitem = gtk_menu_item_new_with_label(REMOVE_MRK);
5885 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5886 g_signal_connect_swapped(menuitem, "activate",
5887 G_CALLBACK(marker_menu), (gpointer) &priv->remove_mrk);
5888 gtk_widget_show(menuitem);
5889 i++;
5890 }
5891
5892 if (!full || !empty) {
5893 menuitem = gtk_separator_menu_item_new();
5894 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5895 gtk_widget_show(menuitem);
5896 i++;
5897 }
5898
5899 menuitem = gtk_check_menu_item_new_with_label(PEAK_MRK);
5900 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5901 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5902 priv->marker_type == MARKER_PEAK);
5903 g_signal_connect_swapped(menuitem, "activate",
5904 G_CALLBACK(marker_menu), (gpointer) &priv->peak_mrk);
5905 gtk_widget_show(menuitem);
5906 i++;
5907
5908 if (priv->active_transform_type == CROSS_CORRELATION_TRANSFORM ||
5909 priv->active_transform_type == FREQ_SPECTRUM_TRANSFORM)
5910 goto skip_no_peak_markers;
5911
5912 menuitem = gtk_check_menu_item_new_with_label(FIX_MRK);
5913 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5914 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5915 priv->marker_type == MARKER_FIXED);
5916 g_signal_connect_swapped(menuitem, "activate",
5917 G_CALLBACK(marker_menu), (gpointer) &priv->fix_mrk);
5918 gtk_widget_show(menuitem);
5919 i++;
5920
5921 menuitem = gtk_check_menu_item_new_with_label(SINGLE_MRK);
5922 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5923 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5924 priv->marker_type == MARKER_ONE_TONE);
5925 g_signal_connect_swapped(menuitem, "activate",
5926 G_CALLBACK(marker_menu), (gpointer) &priv->single_mrk);
5927 gtk_widget_show(menuitem);
5928 i++;
5929
5930 /*
5931 menuitem = gtk_check_menu_item_new_with_label(DUAL_MRK);
5932 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5933 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5934 priv->marker_type == MARKER_TWO_TONE);
5935 g_signal_connect_swapped(menuitem, "activate",
5936 G_CALLBACK(marker_menu), (gpointer) &priv->dual_mrk);
5937 gtk_widget_show(menuitem);
5938 i++;
5939 */
5940
5941 if (priv->active_transform_type == COMPLEX_FFT_TRANSFORM) {
5942 menuitem = gtk_check_menu_item_new_with_label(IMAGE_MRK);
5943 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5944 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5945 priv->marker_type == MARKER_IMAGE);
5946 g_signal_connect_swapped(menuitem, "activate",
5947 G_CALLBACK(marker_menu), (gpointer) &priv->image_mrk);
5948 gtk_widget_show(menuitem);
5949 i++;
5950 }
5951
5952 skip_no_peak_markers:
5953
5954 if (priv->marker_type != MARKER_OFF) {
5955 menuitem = gtk_check_menu_item_new_with_label(OFF_MRK);
5956 gtk_menu_attach(GTK_MENU(popupmenu), menuitem, 0, 1, i, i + 1);
5957 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5958 priv->marker_type == MARKER_OFF);
5959 g_signal_connect_swapped(menuitem, "activate",
5960 G_CALLBACK(marker_menu), (gpointer) &priv->off_mrk);
5961 gtk_widget_show(menuitem);
5962 i++;
5963 }
5964
5965 gtk_menu_popup(GTK_MENU(popupmenu), NULL, NULL, NULL, NULL,
5966 event->button, event->time);
5967
5968 if (priv->marker_type == MARKER_FIXED)
5969 return TRUE;
5970
5971 return FALSE;
5972 }
5973
enable_tree_device_selection(OscPlot * plot,gboolean enable)5974 static void enable_tree_device_selection(OscPlot *plot, gboolean enable)
5975 {
5976 OscPlotPrivate *priv = plot->priv;
5977 GtkTreeModel *model;
5978 GtkTreeIter iter;
5979 gboolean next_iter;
5980
5981 model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->channel_list_view));
5982 if (!gtk_tree_model_get_iter_first(model, &iter))
5983 return;
5984
5985 next_iter = true;
5986 while (next_iter) {
5987 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, DEVICE_SELECTABLE, enable, -1);
5988 next_iter = gtk_tree_model_iter_next(model, &iter);
5989 }
5990 }
5991
plot_domain_changed_cb(GtkComboBox * box,OscPlot * plot)5992 static void plot_domain_changed_cb(GtkComboBox *box, OscPlot *plot)
5993 {
5994 OscPlotPrivate *priv = plot->priv;
5995 gboolean force_sensitive = true;
5996 gboolean enabled_plot_units;
5997 gint plot_type;
5998
5999 priv->marker_type = MARKER_OFF;
6000 check_valid_setup(plot);
6001
6002 plot_type = gtk_combo_box_get_active(box);
6003 foreach_device_iter(GTK_TREE_VIEW(priv->channel_list_view),
6004 *iter_children_plot_type_update, plot);
6005
6006 /* Allow horizontal units selection only for TIME plots */
6007 if (gtk_widget_is_sensitive(priv->hor_units))
6008 priv->last_hor_unit = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->hor_units));
6009 enabled_plot_units = (plot_type == TIME_PLOT) || (plot_type == XCORR_PLOT);
6010 gtk_widget_set_sensitive(priv->hor_units, enabled_plot_units);
6011 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->hor_units),
6012 enabled_plot_units ? priv->last_hor_unit : 0);
6013
6014 /* Allow only 1 active device for a FFT or XY plot */
6015 if (priv->nb_input_devices < 2)
6016 return;
6017 switch (plot_type) {
6018 case FFT_PLOT:
6019 case XY_PLOT:
6020 enable_tree_device_selection(plot, true);
6021 foreach_device_iter(GTK_TREE_VIEW(priv->channel_list_view),
6022 *iter_children_sensitivity_update, NULL);
6023 break;
6024 case TIME_PLOT:
6025 case XCORR_PLOT:
6026 enable_tree_device_selection(plot, false);
6027 foreach_device_iter(GTK_TREE_VIEW(priv->channel_list_view),
6028 *iter_children_sensitivity_update, &force_sensitive);
6029 break;
6030 };
6031 }
6032
domain_is_fft(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)6033 static gboolean domain_is_fft(GBinding *binding,
6034 const GValue *source_value, GValue *target_value, gpointer user_data)
6035 {
6036 g_value_set_boolean(target_value, g_value_get_int(source_value) == FFT_PLOT ||
6037 g_value_get_int(source_value) == SPECTRUM_PLOT);
6038 return TRUE;
6039 }
6040
domain_is_time(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)6041 static gboolean domain_is_time(GBinding *binding,
6042 const GValue *source_value, GValue *target_value, gpointer user_data)
6043 {
6044 g_value_set_boolean(target_value, g_value_get_int(source_value) != FFT_PLOT);
6045 return TRUE;
6046 }
6047
domain_is_xcorr_fft(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)6048 static gboolean domain_is_xcorr_fft(GBinding *binding,
6049 const GValue *source_value, GValue *target_value, gpointer user_data)
6050 {
6051 g_value_set_boolean(target_value, g_value_get_int(source_value) == FFT_PLOT ||
6052 g_value_get_int(source_value) == XCORR_PLOT ||
6053 g_value_get_int(source_value) == SPECTRUM_PLOT);
6054 return TRUE;
6055 }
6056
fft_avg_value_changed_cb(GtkSpinButton * button,OscPlot * plot)6057 static void fft_avg_value_changed_cb(GtkSpinButton *button, OscPlot *plot)
6058 {
6059 OscPlotPrivate *priv = plot->priv;
6060 int i, plot_type;
6061
6062 plot_type = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain));
6063
6064 for (i = 0; i < priv->transform_list->size; i++) {
6065 if (plot_type == FFT_PLOT)
6066 FFT_SETTINGS(priv->transform_list->transforms[i])->fft_avg = gtk_spin_button_get_value(button);
6067 else if (plot_type == XCORR_PLOT)
6068 XCORR_SETTINGS(priv->transform_list->transforms[i])->avg = gtk_spin_button_get_value(button);
6069 else if (plot_type == SPECTRUM_PLOT)
6070 FREQ_SPECTRUM_SETTINGS(priv->transform_list->transforms[i])->fft_avg = gtk_spin_button_get_value(button);
6071 }
6072 }
fft_pwr_offset_value_changed_cb(GtkSpinButton * button,OscPlot * plot)6073 static void fft_pwr_offset_value_changed_cb(GtkSpinButton *button, OscPlot *plot)
6074 {
6075 OscPlotPrivate *priv = plot->priv;
6076 int i, plot_type;
6077
6078 plot_type = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->plot_domain));
6079
6080 for (i = 0; i < priv->transform_list->size; i++) {
6081 if (plot_type == FFT_PLOT)
6082 FFT_SETTINGS(priv->transform_list->transforms[i])->fft_pwr_off = gtk_spin_button_get_value(button);
6083 else if (plot_type == SPECTRUM_PLOT)
6084 FREQ_SPECTRUM_SETTINGS(priv->transform_list->transforms[i])->fft_pwr_off = gtk_spin_button_get_value(button);
6085 }
6086 }
6087
tree_get_selected_row_iter(GtkTreeView * treeview,GtkTreeIter * iter)6088 static gboolean tree_get_selected_row_iter(GtkTreeView *treeview, GtkTreeIter *iter)
6089 {
6090 GtkTreeSelection *selection;
6091 GtkTreeModel *model;
6092 GList *row;
6093
6094 selection = gtk_tree_view_get_selection(treeview);
6095 model = gtk_tree_view_get_model(treeview);
6096 row = gtk_tree_selection_get_selected_rows(selection, &model);
6097 if (!row)
6098 return false;
6099 gtk_tree_model_get_iter(model, iter, row->data);
6100
6101 return true;
6102 }
6103
device_trigger_settings_cb(GtkMenuItem * menuitem,OscPlot * plot)6104 static void device_trigger_settings_cb(GtkMenuItem *menuitem, OscPlot *plot)
6105 {
6106 OscPlotPrivate *priv = plot->priv;
6107 struct iio_device *dev;
6108 GtkTreeView *treeview;
6109 GtkTreeModel *model;
6110 GtkTreeIter iter;
6111 gboolean selected;
6112
6113 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6114 model = gtk_tree_view_get_model(treeview);
6115 selected = tree_get_selected_row_iter(treeview, &iter);
6116 if (!selected)
6117 return;
6118 gtk_tree_model_get(model, &iter, ELEMENT_REFERENCE, &dev, -1);
6119 if (!dev) {
6120 fprintf(stderr, "Invalid reference of iio_device read from devicetree\n");
6121 return;
6122 }
6123 trigger_settings_for_device(priv->builder, iio_device_get_name(dev));
6124 check_valid_setup(plot);
6125 }
6126
channel_compare(gconstpointer a,gconstpointer b)6127 static gint channel_compare(gconstpointer a, gconstpointer b)
6128 {
6129 const char *a_name = iio_channel_get_name((struct iio_channel *)a) ?:
6130 iio_channel_get_id((struct iio_channel *)a);
6131 const char *b_name = iio_channel_get_name((struct iio_channel *)b) ?:
6132 iio_channel_get_id((struct iio_channel *)b);
6133
6134 return strcmp(a_name, b_name);
6135 }
6136
math_expression_get_iio_channel_list(const char * expression,struct iio_context * ctx,const char * device,bool * has_invalid_ch)6137 static GSList * math_expression_get_iio_channel_list(const char *expression, struct iio_context *ctx, const char *device, bool *has_invalid_ch)
6138 {
6139 GSList *chn_list = NULL;
6140 GRegex *regex;
6141 GMatchInfo *info;
6142 gchar *chn_name;
6143 struct iio_device *iio_dev;
6144 struct iio_channel *iio_chn;
6145 gboolean invalid_list = false, is_match;
6146
6147 if (!device || !(iio_dev = iio_context_find_device(ctx, device)))
6148 return NULL;
6149
6150 regex = g_regex_new("voltage[0-9]+", 0, 0, NULL);
6151 is_match = g_regex_match(regex, expression, 0, &info);
6152 if (!is_match) {
6153 invalid_list = true;
6154 } else {
6155 *has_invalid_ch = false;
6156 do {
6157 chn_name = g_match_info_fetch(info, 0);
6158 if (chn_name && (iio_chn = iio_device_find_channel(iio_dev, chn_name, false))) {
6159 if (!g_slist_find_custom(chn_list, iio_chn, channel_compare))
6160 chn_list = g_slist_prepend(chn_list, iio_chn);
6161 } else {
6162 invalid_list = true;
6163 *has_invalid_ch = true;
6164 }
6165 } while (g_match_info_next(info, NULL) && !invalid_list);
6166 }
6167 g_match_info_free(info);
6168 g_regex_unref(regex);
6169
6170 if (invalid_list) {
6171 if (chn_list)
6172 g_slist_free(chn_list);
6173 chn_list = NULL;
6174 }
6175
6176 return chn_list;
6177 }
6178
math_chooser_clear_key_pressed_cb(GtkButton * btn,OscPlot * plot)6179 static void math_chooser_clear_key_pressed_cb(GtkButton *btn, OscPlot *plot)
6180 {
6181 OscPlotPrivate *priv = plot->priv;
6182 GtkTextBuffer *tbuf = priv->math_expression;
6183
6184 gtk_text_buffer_set_text(tbuf, "", -1);
6185 gtk_widget_grab_focus(priv->math_expression_textview);
6186 }
6187
math_chooser_backspace_key_pressed_cb(GtkButton * btn,OscPlot * plot)6188 static void math_chooser_backspace_key_pressed_cb(GtkButton *btn, OscPlot *plot)
6189 {
6190 OscPlotPrivate *priv = plot->priv;
6191 GtkTextBuffer *tbuf = priv->math_expression;
6192 GtkTextMark *insert_mark;
6193 GtkTextIter insert_iter;
6194
6195 insert_mark = gtk_text_buffer_get_insert(tbuf);
6196 gtk_text_buffer_get_iter_at_mark(tbuf, &insert_iter, insert_mark);
6197 gtk_text_buffer_backspace(tbuf, &insert_iter, true, true);
6198 gtk_widget_grab_focus(priv->math_expression_textview);
6199 }
6200
math_chooser_fullscale_key_pressed_cb(GtkButton * btn,OscPlot * plot)6201 static void math_chooser_fullscale_key_pressed_cb(GtkButton *btn, OscPlot *plot)
6202 {
6203 OscPlotPrivate *priv = plot->priv;
6204 GtkTextBuffer *tbuf = priv->math_expression;
6205 struct iio_device *iio_dev;
6206 char key_val[128] = "0";
6207 gchar *device_name;
6208
6209 device_name = gtk_combo_box_text_get_active_text(
6210 GTK_COMBO_BOX_TEXT(priv->math_device_select));
6211 if (device_name) {
6212 iio_dev = iio_context_find_device(priv->ctx, device_name);
6213 g_free(device_name);
6214 if (iio_dev) {
6215 unsigned int i;
6216 struct iio_channel *iio_chn;
6217 const struct iio_data_format *format;
6218 int full_scale;
6219 for (i = 0; i < iio_device_get_channels_count(iio_dev); i++) {
6220 iio_chn = iio_device_get_channel(iio_dev, i);
6221 if (!iio_channel_is_scan_element(iio_chn))
6222 continue;
6223 format = iio_channel_get_data_format(iio_chn);
6224 full_scale = 2 << (format->bits - 1);
6225 if (format->is_signed)
6226 full_scale /= 2;
6227 snprintf(key_val, sizeof(key_val), "%d", full_scale);
6228 break;
6229 }
6230 }
6231 }
6232
6233 gtk_text_buffer_insert_at_cursor(tbuf, key_val, -1);
6234 gtk_widget_grab_focus(priv->math_expression_textview);
6235 }
6236
math_chooser_key_pressed_cb(GtkButton * btn,OscPlot * plot)6237 static void math_chooser_key_pressed_cb(GtkButton *btn, OscPlot *plot)
6238 {
6239 OscPlotPrivate *priv = plot->priv;
6240 GtkTextBuffer *tbuf = priv->math_expression;
6241 GtkTextMark *insert_mark;
6242 GtkTextIter insert_iter;
6243 const gchar *key_label;
6244
6245 key_label = gtk_button_get_label(btn);
6246 gtk_text_buffer_insert_at_cursor(tbuf, key_label, -1);
6247
6248 if (g_str_has_suffix(key_label, ")") && g_strrstr(key_label, "(")) {
6249 insert_mark = gtk_text_buffer_get_insert(tbuf);
6250 gtk_text_buffer_get_iter_at_mark(tbuf, &insert_iter, insert_mark);
6251 gtk_text_iter_backward_char(&insert_iter);
6252 gtk_text_buffer_place_cursor(tbuf, &insert_iter);
6253 }
6254
6255 gtk_widget_grab_focus(priv->math_expression_textview);
6256 }
6257
buttons_table_remove_child(GtkWidget * child,gpointer data)6258 static void buttons_table_remove_child(GtkWidget *child, gpointer data)
6259 {
6260 GtkContainer *container = GTK_CONTAINER(data);
6261
6262 gtk_container_remove(container, child);
6263 }
6264
math_device_cmb_changed_cb(GtkComboBoxText * box,OscPlot * plot)6265 static void math_device_cmb_changed_cb(GtkComboBoxText *box, OscPlot *plot)
6266 {
6267 char *device_name;
6268 const char *channel_name;
6269 struct iio_device *iio_dev;
6270 struct iio_channel *iio_chn;
6271 GtkWidget *button, *buttons_table;
6272 int row, col, sc;
6273 unsigned int i;
6274
6275 device_name = gtk_combo_box_text_get_active_text(box);
6276 if (!device_name)
6277 return;
6278
6279 iio_dev = iio_context_find_device(plot->priv->ctx, device_name);
6280 if (!iio_dev)
6281 goto end;
6282
6283 buttons_table = GTK_WIDGET(gtk_builder_get_object(plot->priv->builder,
6284 "table_channel_buttons"));
6285 gtk_container_foreach(GTK_CONTAINER(buttons_table),
6286 buttons_table_remove_child, buttons_table);
6287 for (i = 0, sc = 0; i < iio_device_get_channels_count(iio_dev); i++) {
6288 iio_chn = iio_device_get_channel(iio_dev, i);
6289
6290 if (iio_channel_is_scan_element(iio_chn)) {
6291 channel_name = iio_channel_get_name(iio_chn) ?:
6292 iio_channel_get_id(iio_chn);
6293 button = gtk_button_new_with_label(channel_name);
6294 row = sc % 4;
6295 col = sc / 4;
6296 gtk_table_attach_defaults(GTK_TABLE(buttons_table),
6297 button, col, col + 1, row, row + 1);
6298 sc++;
6299 }
6300 }
6301 GList *node;
6302
6303 for (node = gtk_container_get_children(GTK_CONTAINER(buttons_table)); node; node = g_list_next(node)) {
6304 g_signal_connect(node->data, "clicked", G_CALLBACK(math_chooser_key_pressed_cb), plot);
6305 }
6306
6307 gtk_widget_show_all(buttons_table);
6308 end:
6309 g_free(device_name);
6310 }
6311
math_expression_get_settings(OscPlot * plot,PlotMathChn * pmc)6312 static int math_expression_get_settings(OscPlot *plot, PlotMathChn *pmc)
6313 {
6314 OscPlotPrivate *priv = plot->priv;
6315 char *active_device;
6316 int ret;
6317 void *lhandler;
6318 math_function fn;
6319 GSList *channels = NULL;
6320 gchar *txt_math_expr;
6321 bool invalid_channels;
6322 const char *channel_name;
6323 char *expression_name;
6324
6325 math_device_cmb_changed_cb(GTK_COMBO_BOX_TEXT(priv->math_device_select), plot);
6326
6327 active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->math_device_select));
6328 if (!active_device) {
6329 fprintf(stderr, "Error: No device available in %s\n", __func__);
6330 return -1;
6331 }
6332
6333 if (pmc->txt_math_expression)
6334 gtk_text_buffer_set_text(priv->math_expression,
6335 pmc->txt_math_expression, -1);
6336 else
6337 gtk_text_buffer_set_text(priv->math_expression, "", -1);
6338
6339 if (pmc->base.name) {
6340 gtk_entry_set_text(GTK_ENTRY(priv->math_channel_name_entry),
6341 pmc->base.name);
6342 } else {
6343 expression_name = g_strdup_printf("expression %d",
6344 num_of_channels_of_device(GTK_TREE_VIEW(priv->channel_list_view),
6345 MATH_CHANNELS_DEVICE));
6346 gtk_entry_set_text(GTK_ENTRY(priv->math_channel_name_entry),
6347 expression_name);
6348 g_free(expression_name);
6349 }
6350
6351 gtk_widget_set_visible(priv->math_expr_error, false);
6352
6353 /* Get the math expression from user */
6354 do {
6355 ret = gtk_dialog_run(GTK_DIALOG(priv->math_expression_dialog));
6356 if (ret != GTK_RESPONSE_OK)
6357 break;
6358
6359 g_free(active_device);
6360 active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(priv->math_device_select));
6361
6362 channel_name = gtk_entry_get_text(GTK_ENTRY(priv->math_channel_name_entry));
6363 if (plot_channel_check_name_exists(plot, channel_name, PLOT_CHN(pmc)))
6364 channel_name = NULL;
6365
6366 /* Get the string of the math expression */
6367 GtkTextIter start;
6368 GtkTextIter end;
6369
6370 gtk_text_buffer_get_start_iter(priv->math_expression, &start);
6371 gtk_text_buffer_get_end_iter(priv->math_expression, &end);
6372 txt_math_expr = gtk_text_buffer_get_text(priv->math_expression, &start, &end, FALSE);
6373
6374 /* Find device channels used in the expression */
6375 channels = math_expression_get_iio_channel_list(txt_math_expr,
6376 priv->ctx, active_device, &invalid_channels);
6377
6378 /* Get the compiled math expression */
6379 GSList *basenames = iio_chn_basenames_get(plot, active_device);
6380 fn = math_expression_get_math_function(txt_math_expr, &lhandler, basenames);
6381 if (basenames) {
6382 g_slist_free_full(basenames, (GDestroyNotify)g_free);
6383 basenames = NULL;
6384 }
6385
6386 gtk_widget_set_visible(priv->math_expr_error, true);
6387 if (!fn)
6388 gtk_label_set_text(GTK_LABEL(priv->math_expr_error), "Invalid math expression.");
6389 else if (!channel_name)
6390 gtk_label_set_text(GTK_LABEL(priv->math_expr_error), "An expression with the same name already exists");
6391 else
6392 gtk_widget_set_visible(priv->math_expr_error, false);
6393 } while (!fn || !channel_name);
6394 gtk_widget_hide(priv->math_expression_dialog);
6395 if (ret != GTK_RESPONSE_OK) {
6396 g_free(active_device);
6397 return - 1;
6398 }
6399
6400 /* Store the settings of the new channel*/
6401 if (pmc->txt_math_expression)
6402 g_free(pmc->txt_math_expression);
6403 if (pmc->base.name)
6404 g_free(pmc->base.name);
6405 if (pmc->iio_device_name)
6406 g_free(pmc->iio_device_name);
6407 if (pmc->iio_channels)
6408 g_slist_free(pmc->iio_channels);
6409
6410 pmc->txt_math_expression = txt_math_expr;
6411 pmc->base.name = g_strdup(channel_name);
6412 pmc->iio_device_name = g_strdup(active_device);
6413 pmc->iio_channels = channels;
6414 pmc->math_expression = fn;
6415 pmc->math_lib_handler = lhandler;
6416 pmc->num_channels = g_slist_length(pmc->iio_channels);
6417 pmc->iio_channels_data = iio_channels_get_data(priv->ctx,
6418 pmc->iio_device_name);
6419
6420 g_free(active_device);
6421
6422 return 0;
6423 }
6424
new_math_channel_cb(GtkMenuItem * menuitem,OscPlot * plot)6425 static void new_math_channel_cb(GtkMenuItem *menuitem, OscPlot *plot)
6426 {
6427 int ret;
6428
6429 /* Build a new Math Channel */
6430 PlotMathChn *pmc;
6431
6432 pmc = plot_math_channel_new(plot->priv->ctx);
6433 if (!pmc)
6434 return;
6435
6436 plot_channel_add_to_plot(plot, PLOT_CHN(pmc));
6437 pmc->base.type = PLOT_MATH_CHANNEL;
6438 pmc->base.parent_name = g_strdup(MATH_CHANNELS_DEVICE);
6439
6440 ret = math_expression_get_settings(plot, pmc);
6441 if (ret < 0) {
6442 plot_channel_remove_from_plot(plot, PLOT_CHN(pmc));
6443 pmc = NULL;
6444 return;
6445 }
6446
6447 /* Create GUI for the new channel */
6448 plot_channels_add_channel(plot, PLOT_CHN(pmc));
6449
6450 treeview_expand_update(plot);
6451 }
6452
channel_edit_settings_cb(GtkMenuItem * menuitem,OscPlot * plot)6453 static void channel_edit_settings_cb(GtkMenuItem *menuitem, OscPlot *plot)
6454 {
6455 OscPlotPrivate *priv = plot->priv;
6456 GtkTreeView *treeview;
6457 GtkTreeModel *model;
6458 GtkTreeIter iter;
6459 gboolean selected;
6460
6461 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6462 model = gtk_tree_view_get_model(treeview);
6463 selected = tree_get_selected_row_iter(treeview, &iter);
6464 if (!selected)
6465 return;
6466
6467 /* Get Channel settings structure */
6468 PlotMathChn *settings;
6469
6470 gtk_tree_model_get(model, &iter, CHANNEL_SETTINGS, &settings, -1);
6471 math_expression_get_settings(plot, settings);
6472 gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
6473 ELEMENT_NAME, settings->base.name, -1);
6474 }
6475
plot_channel_remove_cb(GtkMenuItem * menuitem,OscPlot * plot)6476 static void plot_channel_remove_cb(GtkMenuItem *menuitem, OscPlot *plot)
6477 {
6478 OscPlotPrivate *priv = plot->priv;
6479 GtkTreeView *treeview;
6480 GtkTreeIter iter;
6481 gboolean selected;
6482
6483 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6484 selected = tree_get_selected_row_iter(treeview, &iter);
6485 if (!selected)
6486 return;
6487
6488 plot_channels_remove_channel(plot, &iter);
6489 check_valid_setup(plot);
6490 }
6491
plot_trigger_save_settings(OscPlotPrivate * priv,const struct iio_device * dev)6492 static void plot_trigger_save_settings(OscPlotPrivate *priv,
6493 const struct iio_device *dev)
6494 {
6495 struct extra_dev_info *dev_info = iio_device_get_data(dev);
6496 struct iio_channel *chn;
6497 GtkComboBoxText *box;
6498 GtkToggleButton *radio;
6499 GtkSpinButton *btn;
6500 gchar *active_channel;
6501
6502 radio = GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, "radio_enable_trigger"));
6503 dev_info->channel_trigger_enabled = gtk_toggle_button_get_active(radio);
6504
6505 if (!dev_info->channel_trigger_enabled)
6506 return;
6507
6508 box = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, "comboboxtext_trigger_channel"));
6509 active_channel = gtk_combo_box_text_get_active_text(box);
6510
6511 if (active_channel) {
6512 chn = iio_device_find_channel(dev, active_channel, false);
6513 dev_info->channel_trigger = iio_channel_get_index(chn);
6514 }
6515
6516 radio = GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, "radio_trigger_falling"));
6517 dev_info->trigger_falling_edge = gtk_toggle_button_get_active(radio);
6518
6519 btn = GTK_SPIN_BUTTON(gtk_builder_get_object(
6520 priv->builder, "spin_trigger_value"));
6521 dev_info->trigger_value = gtk_spin_button_get_value(btn);
6522
6523 if (active_channel)
6524 g_free(active_channel);
6525 }
6526
plot_trigger_settings_cb(GtkMenuItem * menuitem,OscPlot * plot)6527 static void plot_trigger_settings_cb(GtkMenuItem *menuitem, OscPlot *plot)
6528 {
6529 OscPlotPrivate *priv = plot->priv;
6530 struct iio_device *dev;
6531 struct extra_dev_info *dev_info;
6532 GtkDialog *dialog;
6533 GtkTreeView *treeview;
6534 GtkTreeModel *model;
6535 GtkComboBoxText *box;
6536 GtkWidget *item;
6537 GtkTreeIter iter, child_iter;
6538 GtkListStore *store;
6539 gboolean selected;
6540 bool box_has_channels = false;
6541 gchar *active_channel;
6542 unsigned cpt = 0;
6543
6544 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6545 model = gtk_tree_view_get_model(treeview);
6546 selected = tree_get_selected_row_iter(treeview, &iter);
6547 if (!selected)
6548 return;
6549 gtk_tree_model_get(model, &iter, ELEMENT_REFERENCE, &dev, -1);
6550 if (!dev) {
6551 fprintf(stderr, "Invalid reference of iio_device read from devicetree\n");
6552 return;
6553 }
6554
6555 dev_info = iio_device_get_data(dev);
6556
6557 box = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, "comboboxtext_trigger_channel"));
6558 gtk_combo_box_set_active(GTK_COMBO_BOX(box), dev_info->channel_trigger);
6559 active_channel = gtk_combo_box_text_get_active_text(box);
6560
6561 /* This code empties the GtkComboBoxText (as it may contain channels
6562 * that have been de-selected); the do-while loop will re-insert only
6563 * the names of the enabled channels. */
6564 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(box)));
6565 gtk_list_store_clear(store);
6566
6567 gtk_tree_model_iter_children(model, &child_iter, &iter);
6568 do {
6569 gboolean enabled;
6570 struct iio_channel *ch;
6571 const char *name;
6572
6573 gtk_tree_model_get(model, &child_iter,
6574 CHANNEL_ACTIVE, &enabled, -1);
6575 if (!enabled)
6576 continue;
6577
6578 gtk_tree_model_get(model, &child_iter,
6579 ELEMENT_REFERENCE, &ch, -1);
6580 name = iio_channel_get_name(ch) ?: iio_channel_get_id(ch);
6581 gtk_combo_box_text_append_text(box, name);
6582 box_has_channels = true;
6583
6584 if (active_channel && !strcmp(name, active_channel))
6585 dev_info->channel_trigger = cpt;
6586 cpt++;
6587 } while (gtk_tree_model_iter_next(model, &child_iter));
6588
6589 gtk_widget_set_sensitive(GTK_WIDGET(box), box_has_channels);
6590
6591 if (box_has_channels)
6592 gtk_combo_box_set_active(GTK_COMBO_BOX(box), dev_info->channel_trigger);
6593 else
6594 dev_info->channel_trigger_enabled = FALSE;
6595
6596 item = GTK_WIDGET(gtk_builder_get_object(priv->builder, "radio_disable_trigger"));
6597 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item),
6598 !dev_info->channel_trigger_enabled);
6599
6600 item = GTK_WIDGET(gtk_builder_get_object(priv->builder, "radio_enable_trigger"));
6601 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item),
6602 dev_info->channel_trigger_enabled);
6603 gtk_widget_set_sensitive(item, box_has_channels);
6604
6605 item = GTK_WIDGET(gtk_builder_get_object(priv->builder, "radio_trigger_falling"));
6606 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), dev_info->trigger_falling_edge);
6607
6608 item = GTK_WIDGET(gtk_builder_get_object(priv->builder, "spin_trigger_value"));
6609 gtk_spin_button_set_value(GTK_SPIN_BUTTON(item), dev_info->trigger_value);
6610
6611 dialog = GTK_DIALOG(gtk_builder_get_object(priv->builder, "channel_trigger_dialog"));
6612 switch (gtk_dialog_run(dialog)) {
6613 case GTK_RESPONSE_CANCEL:
6614 break;
6615 case GTK_RESPONSE_OK:
6616 plot_trigger_save_settings(priv, dev);
6617 default:
6618 break;
6619 }
6620
6621 if (active_channel)
6622 g_free(active_channel);
6623 gtk_widget_hide(GTK_WIDGET(dialog));
6624 }
6625
channel_color_settings_cb(GtkMenuItem * menuitem,OscPlot * plot)6626 static void channel_color_settings_cb(GtkMenuItem *menuitem, OscPlot *plot)
6627 {
6628 OscPlotPrivate *priv = plot->priv;
6629 PlotChn *settings;
6630 GtkWidget *color_dialog;
6631 GtkWidget *colorsel;
6632 GdkColor *color;
6633 GtkTreeView *treeview;
6634 GtkTreeModel *model;
6635 GtkTreeIter iter;
6636 GdkPixbuf *color_icon;
6637 gboolean selected;
6638 gint response;
6639
6640 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6641 model = gtk_tree_view_get_model(treeview);
6642 selected = tree_get_selected_row_iter(treeview, &iter);
6643 if (!selected)
6644 return;
6645 gtk_tree_model_get(model, &iter, CHANNEL_SETTINGS, &settings,
6646 CHANNEL_COLOR_ICON, &color_icon, -1);
6647 color = &settings->graph_color;
6648
6649 color_dialog = gtk_color_selection_dialog_new("Channel Graph Color Selection");
6650 colorsel = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog));
6651 gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), color);
6652 response = gtk_dialog_run(GTK_DIALOG(color_dialog));
6653 gtk_widget_hide(color_dialog);
6654 if (response != GTK_RESPONSE_OK)
6655 return;
6656
6657 gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(colorsel), color);
6658
6659 /* Change icon color */
6660 channel_color_icon_set_color(color_icon, color);
6661
6662 gtk_widget_destroy(color_dialog);
6663 }
6664
channel_math_settings_cb(GtkMenuItem * menuitem,OscPlot * plot)6665 static void channel_math_settings_cb(GtkMenuItem *menuitem, OscPlot *plot)
6666 {
6667 OscPlotPrivate *priv = plot->priv;
6668 GtkBuilder *builder = priv->builder;
6669 GtkWidget *widget;
6670 GtkTreeView *treeview;
6671 GtkTreeModel *model;
6672 GtkTreeIter iter;
6673 gboolean selected;
6674 int response;
6675 PlotIioChn *csettings;
6676
6677 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6678 model = gtk_tree_view_get_model(treeview);
6679 selected = tree_get_selected_row_iter(treeview, &iter);
6680 if (!selected)
6681 return;
6682 gtk_tree_model_get(model, &iter, CHANNEL_SETTINGS, &csettings, -1);
6683
6684 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_inverse_fct"));
6685 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), csettings->apply_inverse_funct);
6686 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_multiply"));
6687 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), csettings->apply_multiply_funct);
6688 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_add"));
6689 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), csettings->apply_add_funct);
6690 widget = GTK_WIDGET(gtk_builder_get_object(builder, "spinbutton_multiply_value"));
6691 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), csettings->multiply_value);
6692 widget = GTK_WIDGET(gtk_builder_get_object(builder, "spinbutton_add_to_value"));
6693 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), csettings->add_value);
6694
6695 response = gtk_dialog_run(GTK_DIALOG(priv->math_dialog));
6696 if (response == GTK_RESPONSE_OK) {
6697 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_inverse_fct"));
6698 csettings->apply_inverse_funct = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6699 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_multiply"));
6700 csettings->apply_multiply_funct = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6701 widget = GTK_WIDGET(gtk_builder_get_object(builder, "btn_add"));
6702 csettings->apply_add_funct = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6703 widget = GTK_WIDGET(gtk_builder_get_object(builder, "spinbutton_multiply_value"));
6704 csettings->multiply_value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
6705 widget = GTK_WIDGET(gtk_builder_get_object(builder, "spinbutton_add_to_value"));
6706 csettings->add_value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
6707 }
6708 gtk_widget_hide(priv->math_dialog);
6709 }
6710
right_click_menu_show(OscPlot * plot,GdkEventButton * event)6711 static gboolean right_click_menu_show(OscPlot *plot, GdkEventButton *event)
6712 {
6713 OscPlotPrivate *priv = plot->priv;
6714 GtkTreeView *treeview;
6715 GtkTreeModel *model;
6716 GtkTreeIter iter;
6717 GtkTreeSelection *selection;
6718 GtkTreePath *path;
6719 gboolean is_device = false;
6720 gboolean is_channel = false;
6721 gpointer ref;
6722 gchar *element_name;
6723 int channel_type;
6724 char name[1024];
6725
6726 treeview = GTK_TREE_VIEW(priv->channel_list_view);
6727 model = gtk_tree_view_get_model(treeview);
6728 selection = gtk_tree_view_get_selection(treeview);
6729
6730 /* Get tree path for row that was clicked */
6731 if (!gtk_tree_view_get_path_at_pos(treeview,
6732 (gint) event->x, (gint) event->y,
6733 &path, NULL, NULL, NULL))
6734 return false;
6735
6736 gtk_tree_selection_unselect_all(selection);
6737 gtk_tree_selection_select_path(selection, path);
6738
6739 gtk_tree_model_get_iter(model, &iter, path);
6740 gtk_tree_model_get(model, &iter,
6741 ELEMENT_NAME, &element_name,
6742 ELEMENT_REFERENCE, &ref,
6743 IS_DEVICE, &is_device,
6744 IS_CHANNEL, &is_channel,
6745 CHANNEL_TYPE, &channel_type,
6746 -1);
6747 snprintf(name, sizeof(name), "%s", element_name);
6748 g_free(element_name);
6749
6750 if (is_channel) {
6751 if (gtk_combo_box_get_active(GTK_COMBO_BOX(plot->priv->plot_domain)) != TIME_PLOT)
6752 return false;
6753
6754 switch(channel_type) {
6755 case PLOT_IIO_CHANNEL:
6756 gtk_menu_popup(GTK_MENU(priv->channel_settings_menu), NULL, NULL,
6757 NULL, NULL,
6758 (event != NULL) ? event->button : 0,
6759 gdk_event_get_time((GdkEvent*)event));
6760 return true;
6761 case PLOT_MATH_CHANNEL:
6762 gtk_menu_popup(GTK_MENU(priv->math_channel_settings_menu), NULL, NULL,
6763 NULL, NULL,
6764 (event != NULL) ? event->button : 0,
6765 gdk_event_get_time((GdkEvent*)event));
6766 return true;
6767 }
6768 }
6769
6770 if (is_device) {
6771 /* Check if device needs a trigger */
6772 struct iio_device *dev = ref;
6773 const struct iio_device *trigger;
6774 bool has_trigger;
6775 int ret;
6776
6777 ret = osc_iio_device_get_trigger(dev, &trigger);
6778 if (ret == 0)
6779 has_trigger = true;
6780 else
6781 has_trigger = false;
6782
6783 gtk_widget_set_sensitive(priv->device_trigger_menuitem,
6784 has_trigger);
6785
6786 gtk_menu_popup(GTK_MENU(priv->device_settings_menu),
6787 NULL, NULL, NULL, NULL,
6788 (event != NULL) ? event->button : 0,
6789 gdk_event_get_time((GdkEvent*)event));
6790 return true;
6791 }
6792
6793 if (!strncmp(name, MATH_CHANNELS_DEVICE, sizeof(MATH_CHANNELS_DEVICE))) {
6794 gtk_menu_popup(GTK_MENU(priv->math_settings_menu),
6795 NULL, NULL, NULL, NULL,
6796 (event != NULL) ? event->button : 0,
6797 gdk_event_get_time((GdkEvent*)event));
6798 return true;
6799 }
6800
6801 return false;
6802 }
6803
right_click_on_ch_list_cb(GtkTreeView * treeview,GdkEventButton * event,OscPlot * plot)6804 static gboolean right_click_on_ch_list_cb(GtkTreeView *treeview, GdkEventButton *event, OscPlot *plot)
6805 {
6806 /* single click with the right mouse button */
6807 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
6808 return right_click_menu_show(plot, event);
6809
6810 return false;
6811 }
6812
menu_quit_cb(GtkMenuItem * menuitem,OscPlot * plot)6813 static void menu_quit_cb(GtkMenuItem *menuitem, OscPlot *plot)
6814 {
6815 osc_plot_destroy(plot);
6816 }
6817
quit_callback_default_cb(GtkMenuItem * menuitem,OscPlot * plot)6818 static void quit_callback_default_cb(GtkMenuItem *menuitem, OscPlot *plot)
6819 {
6820 OscPlotPrivate *priv = plot->priv;
6821 if (priv->quit_callback)
6822 priv->quit_callback(priv->qcb_user_data);
6823 else
6824 fprintf(stderr, "Plot %d does not have a quit callback!\n",
6825 priv->object_id);
6826 }
6827
menu_title_edit_cb(GtkMenuItem * menuitem,OscPlot * plot)6828 static void menu_title_edit_cb(GtkMenuItem *menuitem, OscPlot *plot)
6829 {
6830 OscPlotPrivate *priv = plot->priv;
6831 GtkEntry *title_entry;
6832 const gchar *title;
6833 gint response;
6834
6835 response = gtk_dialog_run(GTK_DIALOG(priv->title_edit_dialog));
6836 if (response == GTK_RESPONSE_OK) {
6837 title_entry = GTK_ENTRY(gtk_builder_get_object(priv->builder, "title_entry"));
6838 title = gtk_entry_get_text(title_entry);
6839 gtk_window_set_title(GTK_WINDOW(priv->window), title);
6840 }
6841
6842 gtk_widget_hide(plot->priv->title_edit_dialog);
6843 }
6844
show_capture_options_toggled_cb(GtkCheckMenuItem * menu_item,OscPlot * plot)6845 static void show_capture_options_toggled_cb(GtkCheckMenuItem *menu_item, OscPlot *plot)
6846 {
6847 OscPlotPrivate *priv = plot->priv;
6848
6849 if (gtk_check_menu_item_get_active(menu_item)) {
6850 gtk_window_get_size(GTK_WINDOW(priv->window), &priv->size.width, &priv->size.height);
6851 gtk_widget_show(plot->priv->capture_options_box);
6852 } else {
6853 gtk_widget_hide(plot->priv->capture_options_box);
6854 gtk_window_resize(GTK_WINDOW(priv->window), priv->size.width, priv->size.height);
6855 }
6856 }
6857
fullscreen_changed_cb(GtkWidget * widget,OscPlot * plot)6858 static void fullscreen_changed_cb(GtkWidget *widget, OscPlot *plot)
6859 {
6860 OscPlotPrivate *priv = plot->priv;
6861 GtkWidget *img;
6862
6863 if (priv->fullscreen_state) {
6864 gtk_window_unfullscreen(GTK_WINDOW(priv->window));
6865 gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(priv->fullscreen_button), "gtk-fullscreen");
6866 gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menu_fullscreen), "Fullscreen");
6867 img = gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(priv->menu_fullscreen));
6868 gtk_image_set_from_stock(GTK_IMAGE(img), "gtk-fullscreen", GTK_ICON_SIZE_MENU);
6869 } else {
6870 gtk_window_fullscreen(GTK_WINDOW(priv->window));
6871 gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(priv->fullscreen_button), "gtk-leave-fullscreen");
6872 gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menu_fullscreen), "Leave Fullscreen");
6873 img = gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(priv->menu_fullscreen));
6874 gtk_image_set_from_stock(GTK_IMAGE(img), "gtk-leave-fullscreen", GTK_ICON_SIZE_MENU);
6875 }
6876 }
6877
window_state_event_cb(GtkWidget * widget,GdkEventWindowState * event,OscPlot * plot)6878 static gboolean window_state_event_cb(GtkWidget *widget, GdkEventWindowState *event, OscPlot *plot)
6879 {
6880 if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
6881 plot->priv->fullscreen_state = true;
6882 else
6883 plot->priv->fullscreen_state = false;
6884
6885 return FALSE;
6886 }
6887
capture_window_realize_cb(GtkWidget * widget,OscPlot * plot)6888 static void capture_window_realize_cb(GtkWidget *widget, OscPlot *plot)
6889 {
6890 gtk_window_get_size(GTK_WINDOW(plot->priv->window),
6891 &plot->priv->size.width, &plot->priv->size.height);
6892 }
6893
set_channel_state_in_tree_model(GtkTreeModel * model,GtkTreeIter * chn_iter,gboolean state)6894 static bool set_channel_state_in_tree_model(GtkTreeModel *model, GtkTreeIter* chn_iter, gboolean state)
6895 {
6896 gboolean sensitive;
6897
6898 gtk_tree_model_get(model, chn_iter, SENSITIVE, &sensitive, -1);
6899
6900 if (sensitive) {
6901 gtk_tree_store_set(GTK_TREE_STORE(model), chn_iter, CHANNEL_ACTIVE, state, -1);
6902 return true;
6903 }
6904
6905 return false;
6906 }
6907
set_channel_state_via_iter(GtkTreeModel * model,GtkTreeIter * iter,void * user_data)6908 static void set_channel_state_via_iter(GtkTreeModel *model,
6909 GtkTreeIter *iter, void *user_data)
6910 {
6911 gboolean enable_state = *(gboolean *)user_data;
6912 set_channel_state_in_tree_model(model, iter, enable_state);
6913 }
6914
enable_all_button_toggled_cb(GtkToggleButton * btn,OscPlot * plot)6915 static void enable_all_button_toggled_cb(GtkToggleButton *btn, OscPlot *plot)
6916 {
6917 OscPlotPrivate *priv = plot->priv;
6918 gboolean toggled = gtk_toggle_button_get_active(btn);
6919
6920 if (toggled) {
6921 gtk_button_set_label(GTK_BUTTON(btn), "Disable All");
6922 } else {
6923 gtk_button_set_label(GTK_BUTTON(btn), "Enable All");
6924 }
6925
6926 // Enable/disable by going through all channels of each device
6927 GtkTreeView *treeview = GTK_TREE_VIEW(priv->channel_list_view);
6928 GtkTreeModel *model;
6929 GtkTreeIter iter;
6930 gboolean next_iter;
6931 gchar *dev_name;
6932
6933 model = gtk_tree_view_get_model(treeview);
6934 next_iter = gtk_tree_model_get_iter_first(model, &iter);
6935 while (next_iter) {
6936 gtk_tree_model_get(model, &iter,
6937 ELEMENT_NAME, &dev_name, -1);
6938 foreach_channel_iter_of_device(treeview, dev_name,
6939 *set_channel_state_via_iter, &toggled);
6940 g_free(dev_name);
6941 next_iter = gtk_tree_model_iter_next(model, &iter);
6942 }
6943 check_valid_setup(plot);
6944 }
6945
create_plot(OscPlot * plot)6946 static void create_plot(OscPlot *plot)
6947 {
6948 OscPlotPrivate *priv = plot->priv;
6949
6950 GtkWidget *table;
6951 GtkWidget *tmp;
6952 GtkBuilder *builder;
6953 GtkTreeSelection *tree_selection;
6954 GtkDataboxRuler *ruler_y;
6955 GtkTreeStore *tree_store;
6956 char buf[50];
6957 int i;
6958
6959 /* Get the GUI from a glade file. */
6960 builder = gtk_builder_new();
6961 if (!gtk_builder_add_from_file(builder, "./glade/oscplot.glade", NULL))
6962 gtk_builder_add_from_file(builder, OSC_GLADE_FILE_PATH "oscplot.glade", NULL);
6963 else {
6964 GtkImage *logo;
6965 /* We are running locally, so load the local files */
6966 logo = GTK_IMAGE(gtk_builder_get_object(builder, "ADI_logo"));
6967 g_object_set(logo, "file","./icons/ADIlogo.png", NULL);
6968 }
6969
6970 priv->builder = builder;
6971 priv->window = GTK_WIDGET(gtk_builder_get_object(builder, "toplevel"));
6972 priv->capture_graph = GTK_WIDGET(gtk_builder_get_object(builder, "display_capture"));
6973 priv->capture_button = GTK_WIDGET(gtk_builder_get_object(builder, "capture_button"));
6974 priv->ss_button = GTK_WIDGET(gtk_builder_get_object(builder, "single_shot_button"));
6975 priv->channel_list_view = GTK_WIDGET(gtk_builder_get_object(builder, "channel_list_view"));
6976 priv->show_grid = GTK_WIDGET(gtk_builder_get_object(builder, "show_grid"));
6977 priv->plot_domain = GTK_WIDGET(gtk_builder_get_object(builder, "capture_domain"));
6978 priv->plot_type = GTK_WIDGET(gtk_builder_get_object(builder, "plot_type"));
6979 priv->enable_auto_scale = GTK_WIDGET(gtk_builder_get_object(builder, "auto_scale"));
6980 priv->hor_scale = GTK_WIDGET(gtk_builder_get_object(builder, "hor_scale"));
6981 priv->hor_units = GTK_WIDGET(gtk_builder_get_object(builder, "sample_count_units"));
6982 priv->marker_label = GTK_WIDGET(gtk_builder_get_object(builder, "marker_info"));
6983 priv->devices_label = GTK_WIDGET(gtk_builder_get_object(builder, "device_info"));
6984 priv->phase_label = GTK_WIDGET(gtk_builder_get_object(builder, "phase_info"));
6985 priv->saveas_button = GTK_WIDGET(gtk_builder_get_object(builder, "save_as"));
6986 priv->saveas_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "saveas_dialog"));
6987 priv->title_edit_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "dialog_plot_title_edit"));
6988 priv->fullscreen_button = GTK_WIDGET(gtk_builder_get_object(builder, "fullscreen"));
6989 priv->menu_fullscreen = GTK_WIDGET(gtk_builder_get_object(builder, "menuitem_fullscreen"));
6990 priv->menu_show_options = GTK_WIDGET(gtk_builder_get_object(builder, "menuitem_show_options"));
6991 priv->y_axis_max = GTK_WIDGET(gtk_builder_get_object(builder, "spin_Y_max"));
6992 priv->y_axis_min = GTK_WIDGET(gtk_builder_get_object(builder, "spin_Y_min"));
6993 priv->viewport_saveas_channels = GTK_WIDGET(gtk_builder_get_object(builder, "saveas_channels_container"));
6994 priv->saveas_select_channel_message = GTK_WIDGET(gtk_builder_get_object(builder, "hbox_ch_sel_label"));
6995 priv->sample_count_widget = GTK_WIDGET(gtk_builder_get_object(builder, "sample_count"));
6996 priv->fft_size_widget = GTK_WIDGET(gtk_builder_get_object(builder, "fft_size"));
6997 priv->fft_win_widget = GTK_WIDGET(gtk_builder_get_object(builder, "fft_win"));
6998 priv->fft_avg_widget = GTK_WIDGET(gtk_builder_get_object(builder, "fft_avg"));
6999 priv->fft_pwr_offset_widget = GTK_WIDGET(gtk_builder_get_object(builder, "pwr_offset"));
7000 priv->math_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "dialog_math_settings"));
7001 priv->capture_options_box = GTK_WIDGET(gtk_builder_get_object(builder, "box_capture_options"));
7002 priv->saveas_settings_box = GTK_WIDGET(gtk_builder_get_object(builder, "vbox_saveas_settings"));
7003 priv->save_mat_scale = GTK_WIDGET(gtk_builder_get_object(builder, "save_mat_scale"));
7004 priv->new_plot_button = GTK_WIDGET(gtk_builder_get_object(builder, "toolbutton_new_plot"));
7005 priv->cmb_saveas_type = GTK_WIDGET(gtk_builder_get_object(priv->builder, "save_formats"));
7006 priv->math_expression_dialog = GTK_WIDGET(gtk_builder_get_object(priv->builder, "math_expression_chooser"));
7007 priv->math_expression_textview = GTK_WIDGET(gtk_builder_get_object(priv->builder, "textview_math_expression"));
7008 priv->math_expression = GTK_TEXT_BUFFER(gtk_builder_get_object(priv->builder, "textbuffer_math_expression"));
7009 priv->math_channel_name_entry = GTK_WIDGET(gtk_builder_get_object(priv->builder, "entry_math_ch_name"));
7010 priv->math_expr_error = GTK_WIDGET(gtk_builder_get_object(priv->builder, "label_math_expr_invalid_msg"));
7011
7012 priv->tbuf = NULL;
7013 priv->ch_settings_list = NULL;
7014
7015 /* Count every object that is being created */
7016 object_count++;
7017 priv->object_id = object_count;
7018
7019 /* Set a different title for every plot */
7020 snprintf(buf, sizeof(buf), "ADI IIO Oscilloscope - Capture%d", priv->object_id);
7021 gtk_window_set_title(GTK_WINDOW(priv->window), buf);
7022
7023 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_domain), TIME_PLOT);
7024 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_type), 0);
7025 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->cmb_saveas_type), SAVE_CSV);
7026
7027 /* Create a empty list of transforms */
7028 plot->priv->transform_list = TrList_new();
7029
7030 /* No active transforms by default */
7031 plot->priv->active_transform_type = NO_TRANSFORM_TYPE;
7032
7033 /* Create a GtkDatabox widget along with scrollbars and rulers */
7034 gtk_databox_create_box_with_scrollbars_and_rulers(&priv->databox, &table,
7035 TRUE, TRUE, TRUE, TRUE);
7036 gtk_box_pack_start(GTK_BOX(priv->capture_graph), table, TRUE, TRUE, 0);
7037 gtk_widget_modify_bg(priv->databox, GTK_STATE_NORMAL, &color_background);
7038 gtk_widget_set_size_request(table, 320, 240);
7039 ruler_y = gtk_databox_get_ruler_y(GTK_DATABOX(priv->databox));
7040
7041 /* Create a Tree Store that holds information about devices */
7042 tree_store = gtk_tree_store_new(NUM_COL,
7043 G_TYPE_STRING, /* ELEMENT_NAME */
7044 G_TYPE_BOOLEAN, /* IS_DEVICE */
7045 G_TYPE_BOOLEAN, /* IS_CHANNEL */
7046 G_TYPE_INT, /* CHANNEL_TYPE */
7047 G_TYPE_BOOLEAN, /* DEVICE_SELECTABLE */
7048 G_TYPE_BOOLEAN, /* DEVICE_ACTIVE */
7049 G_TYPE_BOOLEAN, /* CHANNEL_ACTIVE */
7050 G_TYPE_POINTER, /* ELEMENT_REFERENCE */
7051 G_TYPE_BOOLEAN, /* EXPANDED */
7052 G_TYPE_POINTER, /* CHANNEL_SETTINGS */
7053 GDK_TYPE_PIXBUF, /* CHANNEL_COLOR_ICON */
7054 G_TYPE_INT , /* PLOT_TYPE */
7055 G_TYPE_BOOLEAN); /* SENSITIVE */
7056 gtk_tree_view_set_model((GtkTreeView *)priv->channel_list_view, (GtkTreeModel *)tree_store);
7057
7058 /* Create Device Settings Menu */
7059 GtkWidget *image;
7060 priv->device_settings_menu = gtk_menu_new();
7061 priv->device_trigger_menuitem = gtk_image_menu_item_new_with_label("Impulse Generator");
7062 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
7063 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->device_trigger_menuitem), image);
7064 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->device_trigger_menuitem), true);
7065 gtk_menu_shell_append(GTK_MENU_SHELL(priv->device_settings_menu),
7066 priv->device_trigger_menuitem);
7067
7068 priv->plot_trigger_menuitem = gtk_image_menu_item_new_with_label("Trigger settings");
7069 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
7070 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->plot_trigger_menuitem), image);
7071 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->plot_trigger_menuitem), true);
7072 gtk_menu_shell_append(GTK_MENU_SHELL(priv->device_settings_menu),
7073 priv->plot_trigger_menuitem);
7074 gtk_widget_show_all(priv->device_settings_menu);
7075
7076 priv->math_settings_menu = gtk_menu_new();
7077 priv->math_menuitem = gtk_image_menu_item_new_with_label("New Channel");
7078 image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
7079 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->math_menuitem), image);
7080 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->math_menuitem), true);
7081 gtk_menu_shell_append(GTK_MENU_SHELL(priv->math_settings_menu),
7082 priv->math_menuitem);
7083 gtk_widget_show_all(priv->math_settings_menu);
7084
7085 /* Create Channel Settings Menu */
7086 priv->channel_settings_menu = gtk_menu_new();
7087
7088 priv->channel_iio_color_menuitem = gtk_image_menu_item_new_with_label("Color Selection");
7089 image = gtk_image_new_from_stock(GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU);
7090 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->channel_iio_color_menuitem), image);
7091 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->channel_iio_color_menuitem), true);
7092 gtk_menu_shell_append(GTK_MENU_SHELL(priv->channel_settings_menu),
7093 priv->channel_iio_color_menuitem);
7094 priv->channel_math_menuitem = gtk_image_menu_item_new_with_label("Math Settings");
7095 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
7096 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->channel_math_menuitem), image);
7097 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->channel_math_menuitem), true);
7098 gtk_menu_shell_append(GTK_MENU_SHELL(priv->channel_settings_menu),
7099 priv->channel_math_menuitem);
7100 gtk_widget_show_all(priv->channel_settings_menu);
7101
7102 /* Create Math Channel Settings Menu */
7103 priv->math_channel_settings_menu = gtk_menu_new();
7104
7105 priv->channel_expression_edit_menuitem = gtk_image_menu_item_new_with_label("Edit Expression");
7106 image = gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
7107 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->channel_expression_edit_menuitem), image);
7108 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->channel_expression_edit_menuitem), true);
7109 gtk_menu_shell_append(GTK_MENU_SHELL(priv->math_channel_settings_menu),
7110 priv->channel_expression_edit_menuitem);
7111
7112 priv->channel_math_color_menuitem = gtk_image_menu_item_new_with_label("Color Selection");
7113 image = gtk_image_new_from_stock(GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU);
7114 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->channel_math_color_menuitem), image);
7115 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->channel_math_color_menuitem), true);
7116 gtk_menu_shell_append(GTK_MENU_SHELL(priv->math_channel_settings_menu),
7117 priv->channel_math_color_menuitem);
7118 priv->channel_remove_menuitem = gtk_image_menu_item_new_with_label("Remove");
7119 image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
7120 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(priv->channel_remove_menuitem), image);
7121 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(priv->channel_remove_menuitem), true);
7122 gtk_menu_shell_append(GTK_MENU_SHELL(priv->math_channel_settings_menu),
7123 priv->channel_remove_menuitem);
7124 gtk_widget_show_all(priv->math_channel_settings_menu);
7125
7126 gtk_box_pack_start(GTK_BOX(gtk_builder_get_object(builder, "buttons_separator_box")),
7127 gtk_vseparator_new(), FALSE, TRUE, 0);
7128
7129 /* Create application's treeviews */
7130 device_list_treeview_init(plot);
7131 saveas_channels_list_fill(plot);
7132
7133 /* Initialize text view for Devices Info */
7134 priv->devices_buf = gtk_text_buffer_new(NULL);
7135 gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->devices_label), priv->devices_buf);
7136
7137 /* Initialize text view for Phase Info */
7138 priv->phase_buf = gtk_text_buffer_new(NULL);
7139 gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->phase_label), priv->phase_buf);
7140
7141 /* Initialize Impulse Generators (triggers) dialog */
7142 trigger_dialog_init(builder);
7143
7144 /* Add a device chooser to the Math Expression Chooser */
7145 priv->math_device_select = GTK_WIDGET(gtk_builder_get_object(priv->builder, "cmb_math_device_chooser"));
7146 comboboxtext_input_devices_fill(priv->ctx, GTK_COMBO_BOX_TEXT(priv->math_device_select));
7147 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->math_device_select), 0);
7148
7149 notebook_info_set_page_visibility(GTK_NOTEBOOK(
7150 gtk_builder_get_object(priv->builder, "notebook_info")),
7151 2, false);
7152
7153 /* Connect Signals */
7154 g_signal_connect(G_OBJECT(priv->window), "destroy", G_CALLBACK(plot_destroyed), plot);
7155
7156 g_signal_connect(G_OBJECT(priv->window), "window-state-event",
7157 G_CALLBACK(window_state_event_cb), plot);
7158 g_signal_connect(G_OBJECT(priv->window), "realize",
7159 G_CALLBACK(capture_window_realize_cb), plot);
7160
7161 priv->capture_button_hid =
7162 g_signal_connect(priv->capture_button, "toggled",
7163 G_CALLBACK(capture_button_clicked_cb), plot);
7164 g_signal_connect(priv->ss_button, "clicked",
7165 G_CALLBACK(single_shot_clicked_cb), plot);
7166 g_signal_connect(priv->plot_domain, "changed",
7167 G_CALLBACK(plot_domain_changed_cb), plot);
7168 g_signal_connect(priv->saveas_button, "clicked",
7169 G_CALLBACK(saveas_dialog_show), plot);
7170 g_signal_connect(priv->saveas_dialog, "response",
7171 G_CALLBACK(cb_saveas_response), plot);
7172 g_signal_connect(priv->saveas_dialog, "delete-event",
7173 G_CALLBACK(gtk_widget_hide_on_delete), plot);
7174 g_signal_connect(priv->fullscreen_button, "clicked",
7175 G_CALLBACK(fullscreen_changed_cb), plot);
7176 g_signal_connect(priv->enable_auto_scale, "toggled",
7177 G_CALLBACK(enable_auto_scale_cb), plot);
7178 g_signal_connect(priv->y_axis_max, "value-changed",
7179 G_CALLBACK(max_y_axis_cb), plot);
7180 g_signal_connect(priv->y_axis_min, "value-changed",
7181 G_CALLBACK(min_y_axis_cb), plot);
7182 g_signal_connect(priv->fft_avg_widget, "value-changed",
7183 G_CALLBACK(fft_avg_value_changed_cb), plot);
7184 g_signal_connect(priv->fft_pwr_offset_widget, "value-changed",
7185 G_CALLBACK(fft_pwr_offset_value_changed_cb), plot);
7186 g_signal_connect(priv->new_plot_button, "clicked",
7187 G_CALLBACK(new_plot_button_clicked_cb), plot);
7188
7189 g_signal_connect(priv->channel_list_view, "button-press-event",
7190 G_CALLBACK(right_click_on_ch_list_cb), plot);
7191 g_signal_connect(priv->device_trigger_menuitem, "activate",
7192 G_CALLBACK(device_trigger_settings_cb), plot);
7193 g_signal_connect(priv->math_menuitem, "activate",
7194 G_CALLBACK(new_math_channel_cb), plot);
7195 g_signal_connect(priv->plot_trigger_menuitem, "activate",
7196 G_CALLBACK(plot_trigger_settings_cb), plot);
7197 g_signal_connect(priv->channel_iio_color_menuitem, "activate",
7198 G_CALLBACK(channel_color_settings_cb), plot);
7199 g_signal_connect(priv->channel_math_color_menuitem, "activate",
7200 G_CALLBACK(channel_color_settings_cb), plot);
7201 g_signal_connect(priv->channel_math_menuitem, "activate",
7202 G_CALLBACK(channel_math_settings_cb), plot);
7203 g_signal_connect(priv->channel_remove_menuitem, "activate",
7204 G_CALLBACK(plot_channel_remove_cb), plot);
7205 g_signal_connect(priv->channel_expression_edit_menuitem, "activate",
7206 G_CALLBACK(channel_edit_settings_cb), plot);
7207
7208 g_builder_connect_signal(builder, "zoom_in", "clicked",
7209 G_CALLBACK(zoom_in), plot);
7210 g_builder_connect_signal(builder, "zoom_out", "clicked",
7211 G_CALLBACK(zoom_out), plot);
7212 g_builder_connect_signal(builder, "zoom_fit", "clicked",
7213 G_CALLBACK(zoom_fit), plot);
7214 g_signal_connect(priv->show_grid, "toggled",
7215 G_CALLBACK(show_grid_toggled), plot);
7216
7217 g_signal_connect(GTK_DATABOX(priv->databox), "button_press_event",
7218 G_CALLBACK(marker_button), plot);
7219 g_signal_connect(GTK_DATABOX(priv->databox), "button_release_event",
7220 G_CALLBACK(marker_button), plot);
7221
7222 g_builder_connect_signal(builder, "menuitem_save_as", "activate",
7223 G_CALLBACK(saveas_dialog_show), plot);
7224
7225 g_builder_connect_signal(builder, "menuitem_close", "activate",
7226 G_CALLBACK(menu_quit_cb), plot);
7227
7228 g_builder_connect_signal(builder, "menuitem_quit", "activate",
7229 G_CALLBACK(quit_callback_default_cb), plot);
7230
7231 g_builder_connect_signal(builder, "menuitem_window_title", "activate",
7232 G_CALLBACK(menu_title_edit_cb), plot);
7233
7234 g_builder_connect_signal(builder, "menuitem_show_options", "toggled",
7235 G_CALLBACK(show_capture_options_toggled_cb), plot);
7236
7237 g_builder_connect_signal(builder, "menuitem_fullscreen", "activate",
7238 G_CALLBACK(fullscreen_changed_cb), plot);
7239
7240 g_builder_connect_signal(builder, "cmb_math_device_chooser", "changed",
7241 G_CALLBACK(math_device_cmb_changed_cb), plot);
7242
7243 g_builder_connect_signal(builder, "math_key_clear", "clicked",
7244 G_CALLBACK(math_chooser_clear_key_pressed_cb), plot);
7245 g_builder_connect_signal(builder, "math_key_backspace", "clicked",
7246 G_CALLBACK(math_chooser_backspace_key_pressed_cb), plot);
7247
7248 GtkWidget *math_table = GTK_WIDGET(gtk_builder_get_object(priv->builder, "table_math_chooser"));
7249 GtkWidget *key_fullscale = GTK_WIDGET(gtk_builder_get_object(priv->builder, "math_key_full_scale"));
7250 GList *node;
7251
7252 for (node = gtk_container_get_children(GTK_CONTAINER(math_table));
7253 node; node = g_list_next(node)) {
7254 g_signal_connect(node->data, "clicked",
7255 G_CALLBACK(math_chooser_key_pressed_cb), plot);
7256 }
7257 g_signal_handlers_disconnect_by_func(key_fullscale, math_chooser_key_pressed_cb, plot);
7258 g_signal_connect(key_fullscale, "clicked",
7259 G_CALLBACK(math_chooser_fullscale_key_pressed_cb), plot);
7260
7261 g_builder_connect_signal(builder, "togglebutton_enable_all", "toggled",
7262 G_CALLBACK(enable_all_button_toggled_cb), plot);
7263
7264 /* Create Bindings */
7265 g_object_bind_property_full(priv->capture_button, "active", priv->capture_button,
7266 "stock-id", 0, capture_button_icon_transform, NULL, plot, NULL);
7267 g_object_bind_property(priv->capture_button, "tooltip-text",
7268 priv->ss_button, "tooltip-text", G_BINDING_DEFAULT);
7269 g_object_bind_property(priv->y_axis_max, "value", ruler_y, "lower", G_BINDING_DEFAULT);
7270 g_object_bind_property(priv->y_axis_min, "value", ruler_y, "upper", G_BINDING_DEFAULT);
7271
7272 g_builder_bind_property(builder, "capture_button", "active",
7273 "channel_list_view", "sensitive", G_BINDING_INVERT_BOOLEAN);
7274 g_builder_bind_property(builder, "capture_button", "active",
7275 "capture_domain", "sensitive", G_BINDING_INVERT_BOOLEAN);
7276 g_builder_bind_property(builder, "capture_button", "active",
7277 "fft_size", "sensitive", G_BINDING_INVERT_BOOLEAN);
7278 g_builder_bind_property(builder, "capture_button", "active",
7279 "fft_win", "sensitive", G_BINDING_INVERT_BOOLEAN);
7280 g_builder_bind_property(builder, "capture_button", "active",
7281 "plot_type", "sensitive", G_BINDING_INVERT_BOOLEAN);
7282 g_builder_bind_property(builder, "capture_button", "active",
7283 "sample_count", "sensitive", G_BINDING_INVERT_BOOLEAN);
7284 g_builder_bind_property(builder, "capture_button", "active",
7285 "plot_units_container", "sensitive", G_BINDING_INVERT_BOOLEAN);
7286
7287 /* in autoscale mode, don't display the scales */
7288 g_builder_bind_property(builder, "auto_scale", "active",
7289 "labelYMax", "visible", G_BINDING_INVERT_BOOLEAN);
7290 g_builder_bind_property(builder, "auto_scale", "active",
7291 "spin_Y_max", "visible", G_BINDING_INVERT_BOOLEAN);
7292 g_builder_bind_property(builder, "auto_scale", "active",
7293 "labelYMin", "visible", G_BINDING_INVERT_BOOLEAN);
7294 g_builder_bind_property(builder, "auto_scale", "active",
7295 "spin_Y_min", "visible", G_BINDING_INVERT_BOOLEAN);
7296
7297 /* Bind the plot domain to the sensitivity of the sample count and
7298 * FFT size widgets */
7299 tmp = GTK_WIDGET(gtk_builder_get_object(builder, "fft_size_label"));
7300 g_object_bind_property_full(priv->plot_domain, "active", tmp, "visible",
7301 0, domain_is_fft, NULL, plot, NULL);
7302 g_object_bind_property_full(priv->plot_domain, "active", priv->fft_size_widget, "visible",
7303 0, domain_is_fft, NULL, NULL, NULL);
7304
7305 tmp = GTK_WIDGET(gtk_builder_get_object(builder, "fft_win_label"));
7306 g_object_bind_property_full(priv->plot_domain, "active", tmp, "visible",
7307 0, domain_is_fft, NULL, plot, NULL);
7308 g_object_bind_property_full(priv->plot_domain, "active", priv->fft_win_widget, "visible",
7309 0, domain_is_fft, NULL, NULL, NULL);
7310
7311 tmp = GTK_WIDGET(gtk_builder_get_object(builder, "fft_avg_label"));
7312 g_object_bind_property_full(priv->plot_domain, "active", tmp, "visible",
7313 0, domain_is_xcorr_fft, NULL, NULL, NULL);
7314 g_object_bind_property_full(priv->plot_domain, "active", priv->fft_avg_widget, "visible",
7315 0, domain_is_xcorr_fft, NULL, NULL, NULL);
7316
7317 tmp = GTK_WIDGET(gtk_builder_get_object(builder, "pwr_offset_label"));
7318 g_object_bind_property_full(priv->plot_domain, "active", tmp, "visible",
7319 0, domain_is_fft, NULL, NULL, NULL);
7320 g_object_bind_property_full(priv->plot_domain, "active", priv->fft_pwr_offset_widget, "visible",
7321 0, domain_is_fft, NULL, NULL, NULL);
7322
7323 g_object_bind_property_full(priv->plot_domain, "active", priv->hor_units, "visible",
7324 0, domain_is_time, NULL, NULL, NULL);
7325 g_signal_connect(priv->hor_units, "changed", G_CALLBACK(units_changed_cb), plot);
7326 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->hor_units), 0);
7327
7328 g_object_bind_property_full(priv->plot_domain, "active", priv->sample_count_widget, "visible",
7329 0, domain_is_time, NULL, NULL, NULL);
7330
7331 tmp = GTK_WIDGET(gtk_builder_get_object(builder, "plot_type_label"));
7332 g_object_bind_property_full(priv->plot_domain, "active", tmp, "visible",
7333 0, domain_is_time, NULL, NULL, NULL);
7334 g_object_bind_property_full(priv->plot_domain, "active", priv->plot_type, "visible",
7335 0, domain_is_time, NULL, NULL, NULL);
7336
7337 if (priv->preferences && priv->preferences->sample_count) {
7338 priv->sample_count = *priv->preferences->sample_count;
7339 } else {
7340 priv->sample_count = 400;
7341 }
7342 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->sample_count_widget), priv->sample_count);
7343 g_signal_connect(priv->sample_count_widget, "value-changed", G_CALLBACK(count_changed_cb), plot);
7344
7345 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->fft_size_widget), 2);
7346 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->fft_win_widget), 0);
7347 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->plot_type), 0);
7348 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_max), 1000);
7349 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->y_axis_min), -1000);
7350 tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->channel_list_view));
7351 gtk_tree_selection_set_mode(tree_selection, GTK_SELECTION_SINGLE);
7352 add_grid(plot);
7353 check_valid_setup(plot);
7354 g_mutex_init(&priv->g_marker_copy_lock);
7355 device_rx_info_update(priv);
7356
7357 if (MAX_MARKERS) {
7358 priv->marker_type = MARKER_OFF;
7359 for (i = 0; i < MAX_MARKERS; i++) {
7360 priv->markers[i].graph = NULL;
7361 priv->markers[i].active = (i <= 4);
7362 }
7363 }
7364 priv->add_mrk.plot = plot;
7365 priv->remove_mrk.plot = plot;
7366 priv->peak_mrk.plot = plot;
7367 priv->fix_mrk.plot = plot;
7368 priv->single_mrk.plot = plot;
7369 priv->dual_mrk.plot = plot;
7370 priv->image_mrk.plot = plot;
7371 priv->off_mrk.plot = plot;
7372 priv->add_mrk.string_obj = ADD_MRK;
7373 priv->remove_mrk.string_obj = REMOVE_MRK;
7374 priv->peak_mrk.string_obj = PEAK_MRK;
7375 priv->fix_mrk.string_obj = FIX_MRK;
7376 priv->single_mrk.string_obj = SINGLE_MRK;
7377 priv->dual_mrk.string_obj = DUAL_MRK;
7378 priv->image_mrk.string_obj = IMAGE_MRK;
7379 priv->off_mrk.string_obj = OFF_MRK;
7380
7381 priv->line_thickness = 1;
7382
7383 gtk_window_set_modal(GTK_WINDOW(priv->saveas_dialog), FALSE);
7384 gtk_widget_show_all(priv->capture_graph);
7385 }
7386