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