gtk2 sam7fc telemetrie application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
9.9KB

  1. /***************************************************************************
  2. * Copyright (C) 04/2008 by Olaf Rempel *
  3. * razzor@kopf-tisch.de *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; version 2 of the License *
  8. * *
  9. * This program is distributed in the hope that it will be useful, *
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  12. * GNU General Public License for more details. *
  13. * *
  14. * You should have received a copy of the GNU General Public License *
  15. * along with this program; if not, write to the *
  16. * Free Software Foundation, Inc., *
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. ***************************************************************************/
  19. #include <sys/time.h>
  20. #include <time.h>
  21. #include <gtk/gtk.h>
  22. #include <gtkdatabox.h>
  23. #include <gtkdatabox_grid.h>
  24. #include <gtkdatabox_lines.h>
  25. #include "tdc_variable.h"
  26. #include "gui_graph_tab.h"
  27. #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
  28. #define HISTORY 1000
  29. static unsigned int used_colors;
  30. static GdkColor colors[] = {
  31. { .red = 0x0000, .green = 0x0000, .blue = 0xFF00, },
  32. { .red = 0xFF00, .green = 0x0000, .blue = 0x0000, },
  33. { .red = 0x0000, .green = 0xDF00, .blue = 0x0000, },
  34. { .red = 0x0000, .green = 0xFF00, .blue = 0xFF00, },
  35. { .red = 0xFF00, .green = 0x0000, .blue = 0xFF00, },
  36. { .red = 0xA500, .green = 0x2A00, .blue = 0x2A00, },
  37. { .red = 0xFF00, .green = 0xA500, .blue = 0x0000, },
  38. { .red = 0x8000, .green = 0x8000, .blue = 0x8000, },
  39. };
  40. #define MOD_CONTINUOUS 0x0001
  41. #define MOD_SINGLESHOT 0x0002
  42. #define MOD_RUNNING 0x0004
  43. #define MOD_STOPPED 0x0008
  44. static int graph_mode = MOD_CONTINUOUS | MOD_RUNNING;
  45. static struct timeval base;
  46. static GtkWidget *box;
  47. static GtkWidget *legend;
  48. static GtkWidget *yscale_min, *yscale_max;
  49. static GtkWidget *start_button, *mode_button;
  50. static GList *graphlist;
  51. struct xygraph {
  52. GtkDataboxGraph *graph;
  53. struct tdc_var *var;
  54. gfloat xarr[HISTORY];
  55. gfloat yarr[HISTORY];
  56. GdkColor *color;
  57. int last_index;
  58. };
  59. static GdkColor * get_color(void)
  60. {
  61. int i;
  62. for (i = 0; i < ARRAY_SIZE(colors); i++) {
  63. if (used_colors & (1 << i))
  64. continue;
  65. used_colors |= (1 << i);
  66. return colors +i;
  67. }
  68. return NULL;
  69. }
  70. static void free_color(GdkColor *color)
  71. {
  72. int i;
  73. for (i = 0; i < ARRAY_SIZE(colors); i++)
  74. if (color == colors +i)
  75. used_colors &= ~(1 << i);
  76. }
  77. static void update_legend_cb(gpointer data, gpointer user_data)
  78. {
  79. struct xygraph *graph = (struct xygraph *)data;
  80. GString *tmp = (GString *)user_data;
  81. g_string_append_printf(tmp,"<span foreground=\"#%02x%02x%02x\">%s</span>\n",
  82. (graph->color->red >> 8) & 0xFF,
  83. (graph->color->green >> 8) & 0xFF,
  84. (graph->color->blue >> 8) & 0xFF,
  85. graph->var->name);
  86. }
  87. static void update_legend(void)
  88. {
  89. GString *tmp;
  90. tmp = g_string_new("");
  91. g_list_foreach(graphlist, &update_legend_cb, tmp);
  92. /* strip last \n */
  93. tmp->str[tmp->len -1] = 0x00;
  94. gtk_label_set_markup(GTK_LABEL(legend), tmp->str);
  95. g_string_free(tmp, TRUE);
  96. }
  97. static void rescale_graphs(gboolean reset)
  98. {
  99. static GtkDataboxValue min;
  100. static GtkDataboxValue max;
  101. GtkDataboxValue tmp_min, tmp_max;
  102. if (gtk_databox_calculate_extrema(GTK_DATABOX(box), &tmp_min, &tmp_max) < 0)
  103. return;
  104. /* swap y: min/max (high values -> higher on screen) */
  105. if (!reset) {
  106. min.x = MIN(min.x, tmp_min.x);
  107. max.x = MAX(max.x, tmp_max.x);
  108. min.y = MAX(min.y, tmp_max.y);
  109. max.y = MIN(max.y, tmp_min.y);
  110. } else {
  111. min.x = tmp_min.x;
  112. max.x = tmp_max.x;
  113. min.y = tmp_max.y;
  114. max.y = tmp_min.y;
  115. }
  116. char tmp[64];
  117. snprintf(tmp, sizeof(tmp), "y-Min: %0.0lf", max.y);
  118. gtk_label_set_text(GTK_LABEL(yscale_min), tmp);
  119. snprintf(tmp, sizeof(tmp), "y-Max: %0.0lf", min.y);
  120. gtk_label_set_text(GTK_LABEL(yscale_max), tmp);
  121. gtk_databox_set_canvas(GTK_DATABOX(box), min, max);
  122. }
  123. static void rescale_button_cb(GtkWidget *widget, gpointer data)
  124. {
  125. rescale_graphs(TRUE);
  126. }
  127. static void update_button_labels(void)
  128. {
  129. if (graph_mode & MOD_SINGLESHOT)
  130. gtk_button_set_label(GTK_BUTTON(mode_button), "Single Shot");
  131. else if (graph_mode & MOD_CONTINUOUS)
  132. gtk_button_set_label(GTK_BUTTON(mode_button), "Continuous");
  133. if (graph_mode & MOD_RUNNING)
  134. gtk_button_set_label(GTK_BUTTON(start_button), "Halt");
  135. else if (graph_mode & MOD_STOPPED)
  136. gtk_button_set_label(GTK_BUTTON(start_button), "Start");
  137. }
  138. static void mode_button_cb(GtkWidget *widget, gpointer data)
  139. {
  140. if (graph_mode & MOD_SINGLESHOT)
  141. graph_mode = (graph_mode & ~MOD_SINGLESHOT) | MOD_CONTINUOUS;
  142. else if (graph_mode & MOD_CONTINUOUS)
  143. graph_mode = (graph_mode & ~MOD_CONTINUOUS) | MOD_SINGLESHOT;
  144. update_button_labels();
  145. }
  146. static void start_button_cb(GtkWidget *widget, gpointer data)
  147. {
  148. if (graph_mode & MOD_STOPPED) {
  149. graph_mode = (graph_mode & ~MOD_STOPPED) | MOD_RUNNING;
  150. gettimeofday(&base, NULL);
  151. } else if (graph_mode & MOD_RUNNING) {
  152. graph_mode = (graph_mode & ~MOD_RUNNING) | MOD_STOPPED;
  153. }
  154. update_button_labels();
  155. }
  156. gint gui_graphtab_init(GtkNotebook *notebook)
  157. {
  158. GtkWidget *table = gtk_table_new(5, 6, FALSE);
  159. box = gtk_databox_new();
  160. gtk_table_attach(GTK_TABLE(table), box, 1, 4, 1, 2,
  161. GTK_FILL | GTK_EXPAND | GTK_SHRINK,
  162. GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
  163. GtkWidget *scrollbar = gtk_hscrollbar_new(gtk_databox_get_hadjustment(GTK_DATABOX(box)));
  164. gtk_table_attach(GTK_TABLE(table), scrollbar, 1, 4, 2, 3,
  165. GTK_FILL | GTK_EXPAND | GTK_SHRINK,
  166. GTK_FILL, 0, 0);
  167. scrollbar = gtk_vscrollbar_new(gtk_databox_get_vadjustment(GTK_DATABOX(box)));
  168. gtk_table_attach(GTK_TABLE(table), scrollbar, 4, 5, 1, 2,
  169. GTK_FILL,
  170. GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
  171. GtkWidget *ruler = gtk_hruler_new();
  172. gtk_table_attach(GTK_TABLE(table), ruler, 1, 4, 0, 1,
  173. GTK_FILL | GTK_EXPAND | GTK_SHRINK,
  174. GTK_FILL, 0, 0);
  175. gtk_databox_set_hruler(GTK_DATABOX(box), GTK_RULER(ruler));
  176. ruler = gtk_vruler_new();
  177. gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
  178. GTK_FILL,
  179. GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
  180. gtk_databox_set_vruler(GTK_DATABOX(box), GTK_RULER(ruler));
  181. GdkColor color = { .red = 0xC000, .green = 0xC000, .blue = 0xC000, };
  182. GtkDataboxGraph *grid = gtk_databox_grid_new(10, 10, &color, 1);
  183. gtk_databox_graph_add(GTK_DATABOX(box), grid);
  184. legend = gtk_label_new(NULL);
  185. gtk_table_attach(GTK_TABLE(table), legend, 1, 2, 3, 6, 0, 0, 5, 5);
  186. yscale_max = gtk_label_new("y-Max:");
  187. gtk_table_attach(GTK_TABLE(table), yscale_max, 2, 3, 3, 4, 0, 0, 5, 5);
  188. yscale_min = gtk_label_new("y-Min:");
  189. gtk_table_attach(GTK_TABLE(table), yscale_min, 2, 3, 4, 5, 0, 0, 5, 5);
  190. GtkWidget *rescale_button = gtk_button_new_with_label("Autoscale");
  191. gtk_table_attach(GTK_TABLE(table), rescale_button, 2, 3, 5, 6, 0, 0, 10, 10);
  192. g_signal_connect(G_OBJECT(rescale_button), "clicked", G_CALLBACK(rescale_button_cb), NULL);
  193. start_button = gtk_button_new_with_label("Start");
  194. gtk_table_attach(GTK_TABLE(table), start_button, 3, 4, 4, 5, 0, 0, 10, 10);
  195. g_signal_connect(G_OBJECT(start_button), "clicked", G_CALLBACK(start_button_cb), NULL);
  196. mode_button = gtk_button_new_with_label("Continuous");
  197. gtk_table_attach(GTK_TABLE(table), mode_button, 3, 4, 5, 6, 0, 0, 10, 10);
  198. g_signal_connect(G_OBJECT(mode_button), "clicked", G_CALLBACK(mode_button_cb), NULL);
  199. update_button_labels();
  200. GtkWidget *label = gtk_label_new(" Graph ");
  201. return gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table, label);
  202. }
  203. int gui_graphtab_add_var(struct tdc_var *var)
  204. {
  205. GdkColor *color = get_color();
  206. if (color == NULL)
  207. return -1;
  208. struct xygraph *graph = g_malloc0(sizeof(struct xygraph));
  209. graph->graph = gtk_databox_lines_new(HISTORY, graph->xarr, graph->yarr, color, 1);
  210. graph->var = var;
  211. graph->color = color;
  212. int i;
  213. for (i = 0; i < HISTORY; i++) {
  214. graph->xarr[i] = i;
  215. graph->yarr[i] = 0;
  216. }
  217. var->privdata_graphtab = graph;
  218. graphlist = g_list_append(graphlist, graph);
  219. gtk_databox_graph_add(GTK_DATABOX(box), graph->graph);
  220. update_legend();
  221. return 0;
  222. }
  223. void gui_graphtab_remove_var(struct tdc_var *var)
  224. {
  225. struct xygraph *graph = (struct xygraph *)var->privdata_graphtab;
  226. var->privdata_graphtab = NULL;
  227. gtk_databox_graph_remove(GTK_DATABOX(box), graph->graph);
  228. free_color(graph->color);
  229. graphlist = g_list_remove(graphlist, graph);
  230. update_legend();
  231. g_free(graph);
  232. }
  233. static int calc_index(struct timeval *base, struct timeval *now, int step)
  234. {
  235. struct timeval diff;
  236. diff.tv_sec = now->tv_sec - base->tv_sec;
  237. diff.tv_usec = now->tv_usec - base->tv_usec;
  238. diff.tv_usec += diff.tv_sec * 1000000;
  239. diff.tv_usec /= 1000;
  240. return diff.tv_usec / step;
  241. }
  242. void gui_graphtab_update_var(struct tdc_var *var)
  243. {
  244. static struct timeval update;
  245. struct xygraph *graph = (struct xygraph *)var->privdata_graphtab;
  246. if (graph_mode & MOD_STOPPED)
  247. return;
  248. struct timeval now;
  249. gettimeofday(&now, NULL);
  250. int i = calc_index(&base, &now, 10);
  251. if (i < 0 || i >= HISTORY) {
  252. base.tv_sec = now.tv_sec;
  253. base.tv_usec = now.tv_usec;
  254. i = 0;
  255. if (graph_mode & MOD_SINGLESHOT) {
  256. graph_mode = (graph_mode & ~MOD_RUNNING) | MOD_STOPPED;
  257. update_button_labels();
  258. }
  259. }
  260. float last_value = graph->yarr[graph->last_index];
  261. while (graph->last_index != i) {
  262. graph->last_index++;
  263. if (graph->last_index == HISTORY)
  264. graph->last_index = 0;
  265. graph->yarr[graph->last_index] = last_value;
  266. }
  267. graph->yarr[i] = tdcvar_get_double(var);
  268. if (calc_index(&update, &now, 100) != 0) {
  269. update.tv_sec = now.tv_sec;
  270. update.tv_usec = now.tv_usec;
  271. rescale_graphs(FALSE);
  272. }
  273. }