main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. /*
  2. voltlogger_oscilloscope
  3. Copyright (C) 2015 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <assert.h>
  16. #include <stdio.h>
  17. #include <stdint.h>
  18. #include <stdlib.h> /* free() */
  19. #include <string.h>
  20. #include <unistd.h>
  21. //#include <fcntl.h>
  22. #include <gtk/gtk.h>
  23. #include <pthread.h>
  24. #include <errno.h>
  25. #include "configuration.h"
  26. #include "binary.h"
  27. #include "malloc.h"
  28. FILE *sensor;
  29. FILE *dump;
  30. #define HISTORY_SIZE (1 << 20)
  31. #define MAX_REAL_CHANNELS 17
  32. #define MAX_MATH_CHANNELS 3
  33. #define Y_BITS 12
  34. #define AVG_MAX_ITERATIONS 10240
  35. //#define DOTTED_LINE
  36. #define GLADE_PATH "oscilloscope.glade"
  37. typedef struct {
  38. uint64_t timestamp;
  39. uint32_t value[MAX_REAL_CHANNELS];
  40. } history_t;
  41. int running = 1;
  42. GtkBuilder *builder;
  43. history_t *history;
  44. uint32_t history_length = 0;
  45. uint64_t ts_global = 0;
  46. pthread_mutex_t history_mutex;
  47. enum trigger_mode {
  48. TG_NONE = 0,
  49. TG_RISE,
  50. TG_FALL,
  51. };
  52. double avg_timestamp_diff = 0;
  53. uint64_t avg_count = 0;
  54. double x_userdiv = 2.3E-3;
  55. double x_useroffset = 0;
  56. double y_userscale [MAX_REAL_CHANNELS + MAX_MATH_CHANNELS];
  57. double y_useroffset[MAX_REAL_CHANNELS + MAX_MATH_CHANNELS];
  58. char chanenabled[MAX_REAL_CHANNELS + MAX_MATH_CHANNELS];
  59. int trigger_channel = 9;
  60. int trigger_start_mode = TG_RISE;
  61. int trigger_start_y = 2048;
  62. int trigger_end_mode = TG_RISE;
  63. int trigger_end_y = 2048;
  64. int channelsNum = 15;
  65. int mathChannelsNum = 0;
  66. double line_colors[MAX_REAL_CHANNELS + MAX_MATH_CHANNELS][3] = {{1}};
  67. void
  68. sensor_open()
  69. {
  70. sensor = stdin;
  71. return;
  72. }
  73. int
  74. sensor_fetch(history_t *p)
  75. {
  76. uint16_t ts_local;
  77. uint16_t ts_local_old;
  78. ts_local = get_uint16(sensor);
  79. ts_local_old = ts_global & 0xffff;
  80. if (ts_local <= ts_local_old) {
  81. ts_global += 1<<16;
  82. }
  83. ts_global = (ts_global & ~0xffff) | ts_local;
  84. p->timestamp = ts_global;
  85. int i = 0;
  86. while (i < channelsNum)
  87. p->value[i++] = get_uint16(sensor);
  88. return 1;
  89. }
  90. void
  91. sensor_close()
  92. {
  93. return;
  94. }
  95. void
  96. dump_open(char *dumppath, char tailonly)
  97. {
  98. dump = stdin;
  99. if (dumppath == NULL)
  100. return;
  101. if (*dumppath == 0)
  102. return;
  103. if (!strcmp(dumppath, "-"))
  104. return;
  105. dump = fopen(dumppath, "r");
  106. if (dump == NULL) {
  107. fprintf(stderr, "Cannot open file \"%s\": %s", dumppath, strerror(errno));
  108. abort ();
  109. }
  110. if (tailonly) {
  111. fseek(dump, 0, SEEK_END);
  112. } else {
  113. fseek(dump, 0, SEEK_SET);
  114. }
  115. //fprintf(stderr, "Pos: %li\n", ftell(dump));
  116. return;
  117. }
  118. int
  119. dump_fetch(history_t *p)
  120. {
  121. uint64_t ts_parse;
  122. uint64_t ts_device;
  123. // uint32_t value;
  124. ts_parse = get_uint64(dump);
  125. while (ts_parse < 1437900000000000000 || ts_parse > 1537900000000000000) {
  126. printf("dump_fetch() correction 0: %lu %li\n", ts_parse, ftell(dump));
  127. get_uint8(dump);
  128. ts_parse = get_uint64(dump);
  129. }
  130. ts_device = get_uint64(dump);
  131. int i = 0;
  132. while (i < channelsNum) {
  133. p->value[i] = get_uint32(dump);
  134. while (p->value[i] < 0 || p->value[i] > (1 << Y_BITS)) {
  135. printf("dump_fetch() correction 1: p->value[%i] == %u\n", i, p->value[i]);
  136. p->value[i] = get_uint32(dump);
  137. }
  138. i++;
  139. }
  140. (void)ts_parse;
  141. p->timestamp = ts_device;
  142. //fprintf(stderr, "%u\n", p->value[0]);
  143. //sleep(1);
  144. return 1;
  145. }
  146. void
  147. dump_close()
  148. {
  149. return;
  150. }
  151. void
  152. history_flush()
  153. {
  154. //sleep(3600);
  155. pthread_mutex_lock(&history_mutex);
  156. memcpy(history, &history[HISTORY_SIZE], sizeof(*history)*HISTORY_SIZE);
  157. history_length = HISTORY_SIZE;
  158. printf("history_flush()\n");
  159. pthread_mutex_unlock(&history_mutex);
  160. return;
  161. }
  162. void *
  163. history_fetcher(void *arg)
  164. {
  165. //fprintf(stderr, "history_fetcher\n");
  166. while (running) {
  167. //sensor_fetch(&history[0][ history_length[0]++ ]);
  168. dump_fetch(&history[ history_length++ ]);
  169. //printf("%lu; %u\n", history[ history_length-1].timestamp, history[ history_length-1].value[0]);
  170. if (history_length >= HISTORY_SIZE * 2)
  171. history_flush();
  172. }
  173. return NULL;
  174. }
  175. void
  176. arrange_widgets()
  177. {
  178. char offsetwidgetname[] = "offset_chanXX";
  179. char scalewidgetname[] = "scale_chanXX";
  180. int chan = 0;
  181. while (chan < MAX_REAL_CHANNELS + MAX_MATH_CHANNELS) {
  182. if (chan < 10) {
  183. offsetwidgetname[11] = chan + '0';
  184. offsetwidgetname[12] = 0;
  185. scalewidgetname [10] = chan + '0';
  186. scalewidgetname [11] = 0;
  187. } else {
  188. offsetwidgetname[11] = chan/10 + '0';
  189. offsetwidgetname[12] = chan%10 + '0';
  190. scalewidgetname [10] = chan/10 + '0';
  191. scalewidgetname [11] = chan%10 + '0';
  192. }
  193. GtkWidget *offset = GTK_WIDGET ( gtk_builder_get_object(builder, offsetwidgetname) );
  194. GtkWidget *scale = GTK_WIDGET ( gtk_builder_get_object(builder, scalewidgetname) );
  195. if (offset != NULL) {
  196. if (chanenabled[chan])
  197. gtk_widget_show(offset);
  198. else
  199. gtk_widget_hide(offset);
  200. }
  201. if (scale != NULL) {
  202. if (chanenabled[chan])
  203. gtk_widget_show(scale);
  204. else
  205. gtk_widget_hide(scale);
  206. }
  207. chan++;
  208. }
  209. return;
  210. }
  211. void
  212. cb_chanenable_toggled (
  213. GtkToggleButton *button,
  214. gpointer user_data)
  215. {
  216. char *chanenabled = user_data;
  217. *chanenabled = !*chanenabled;
  218. arrange_widgets();
  219. }
  220. void
  221. cb_scale_changed (
  222. GtkRange *range,
  223. gpointer user_data)
  224. {
  225. double *scale_value = user_data;
  226. *scale_value = gtk_range_get_value (range);
  227. return;
  228. }
  229. void
  230. cb_offset_changed (
  231. GtkRange *range,
  232. gpointer user_data)
  233. {
  234. double *offset_value = user_data;
  235. *offset_value = gtk_range_get_value (range);
  236. return;
  237. }
  238. void
  239. cb_resize (
  240. GtkWindow *window,
  241. GdkEvent *event,
  242. gpointer data)
  243. {
  244. int width, height;
  245. char offsetwidgetname[] = "offset_chanXX";
  246. width = event->configure.width;
  247. height = event->configure.height;
  248. GtkWidget *offset;
  249. int chan = 0;
  250. while (chan < MAX_REAL_CHANNELS + MAX_MATH_CHANNELS) {
  251. if (chan >= 10) {
  252. offsetwidgetname[11] = chan/10 + '0';
  253. offsetwidgetname[12] = chan%10 + '0';
  254. } else {
  255. offsetwidgetname[11] = chan + '0';
  256. offsetwidgetname[12] = 0;
  257. }
  258. chan++;
  259. offset = GTK_WIDGET ( gtk_builder_get_object(builder, offsetwidgetname) );
  260. if (offset == NULL)
  261. continue;
  262. gtk_widget_set_size_request (offset, 10, 200);
  263. }
  264. printf("%d, %d\n", width, height);
  265. }
  266. static gboolean
  267. cb_draw (GtkWidget *area,
  268. cairo_t *cr,
  269. gpointer *data)
  270. {
  271. int width, height;
  272. printf("cb_draw\n");
  273. GdkWindow *areaGdkWindow = gtk_widget_get_window(area);
  274. assert (areaGdkWindow != NULL);
  275. //gtk_window_get_size (gtk_widget_get_window(area), &width, &height);
  276. width = gdk_window_get_width (areaGdkWindow);
  277. height = gdk_window_get_height (areaGdkWindow);
  278. //fprintf(stderr, "%i %i\n", width, height);
  279. cairo_rectangle(cr, 0, 0, width, height);
  280. cairo_set_source_rgb(cr, 0, 0, 0);
  281. cairo_fill(cr);
  282. cairo_set_line_width (cr, 2);
  283. int history_end = history_length-2;
  284. if (history_end >= (double)HISTORY_SIZE*x_userdiv) {
  285. //printf("%u %u\n", HISTORY_SIZE, history_end);
  286. //cairo_set_source_rgba (cr, 0, 0, 0.3, 0.8);
  287. //cairo_set_source_rgba (cr, 0.8, 0.8, 1, 0.8);
  288. int x;
  289. int y;
  290. pthread_mutex_lock(&history_mutex);
  291. printf("cb_draw: lock()-ed\n");
  292. //int history_start = history_end - HISTORY_SIZE;
  293. int history_start_initial = history_end - (double)HISTORY_SIZE*x_userdiv;
  294. int history_start = history_start_initial;
  295. if (history_start == 0)
  296. history_start+=10;
  297. //printf("h: %u %u\n", history_start, history_end);
  298. char found;
  299. found = 0;
  300. while (history_start < history_end && !found) {
  301. history_t *cur = &history[history_start];
  302. history_t *prev = &history[history_start-10];
  303. //printf("1 %u %u\n", cur->value, prev->value);
  304. switch (trigger_start_mode) {
  305. case TG_FALL:
  306. found = (cur->value[trigger_channel] >= trigger_start_y && prev->value[trigger_channel] < trigger_start_y);
  307. break;
  308. case TG_RISE:
  309. found = (cur->value[trigger_channel] < trigger_start_y && prev->value[trigger_channel] >= trigger_start_y);
  310. break;
  311. }
  312. if (found)
  313. break;
  314. history_start++;
  315. }
  316. if (history_start >= history_end) {
  317. printf("Unable to sync start\n");
  318. history_start = history_start_initial;
  319. }
  320. uint64_t timestamp_start = history[history_start].timestamp;
  321. uint64_t timestamp_end;
  322. uint64_t timestamp_diff;
  323. do {
  324. int history_end_initial = history_end;
  325. history_end-=10;
  326. found = 0;
  327. while (history_start < history_end && !found) {
  328. history_t *cur = &history[history_end];
  329. history_t *prev = &history[history_end+10];
  330. //printf("2 %u %u\n", cur->value, prev->value);
  331. switch (trigger_end_mode) {
  332. case TG_RISE:
  333. found = (cur->value[trigger_channel] >= trigger_end_y && prev->value[trigger_channel] < trigger_end_y);
  334. break;
  335. case TG_FALL:
  336. found = (cur->value[trigger_channel] < trigger_end_y && prev->value[trigger_channel] >= trigger_end_y);
  337. break;
  338. }
  339. if (found)
  340. break;
  341. history_end--;
  342. }
  343. if (history_start >= history_end) {
  344. history_end = history_end_initial;
  345. printf("Unable to sync end; %u %u\n", history_start, history_end);
  346. break;
  347. }
  348. //printf("H: %u %u\n", history_start, history_end);
  349. timestamp_start = history[history_start].timestamp;
  350. timestamp_end = history[history_end ].timestamp;
  351. timestamp_diff = timestamp_end - timestamp_start;
  352. if (avg_count < AVG_MAX_ITERATIONS) {
  353. avg_timestamp_diff = (double)((avg_timestamp_diff * avg_count) + timestamp_diff) / (avg_count + 1);
  354. avg_count++;
  355. }
  356. //printf("timestamp_diff = ((%lf * %i) + %lu) / %i\n", avg_timestamp_diff, avg_count, timestamp_diff, avg_count+1);
  357. if (timestamp_diff > 32767) {
  358. printf("%lu %lu %u(%u) %u(%u) %u %u\n", timestamp_start, timestamp_end, history_start, history_start_initial, history_end, history_end_initial, history[history_start].value[0], history[history_end].value[0]);
  359. }
  360. if (timestamp_diff == 0) {
  361. printf("%lu %lu %u %u %u %u\n", timestamp_start, timestamp_end, history_start, history_end, history[history_start].value[0], history[history_end].value[0]);
  362. }
  363. assert (timestamp_diff > 0);
  364. } while ((timestamp_diff > avg_timestamp_diff*1.1) && avg_count > 10);
  365. printf("timestamp_diff == %lu (%lf, %lu)\n", timestamp_diff, avg_timestamp_diff, avg_count);
  366. double x_scale = (double)width / (timestamp_end - timestamp_start);
  367. double y_scale = (double)height / (1 << Y_BITS);
  368. int chan = 0;
  369. while (chan < channelsNum) {
  370. if (!chanenabled[chan]) {
  371. chan++;
  372. continue;
  373. }
  374. int history_cur = history_start;
  375. cairo_set_source_rgba (cr, line_colors[chan][0], line_colors[chan][1], line_colors[chan][2], 0.8);
  376. cairo_move_to(cr, -1, height/2);
  377. while (history_cur < history_end) {
  378. history_t *p = &history[ history_cur++ ];
  379. x = (double)x_useroffset*width + (double)x_scale * (double)(p->timestamp - timestamp_start);
  380. y = (double)height/2 + (double)y_useroffset[chan]*y_userscale[chan]*height - (double)y_scale * y_userscale[chan] * (double) p->value[chan];
  381. #ifdef DOTTED_LINE
  382. cairo_move_to(cr, x-1, y);
  383. #endif
  384. cairo_line_to(cr, x, y);
  385. }
  386. cairo_stroke(cr);
  387. chan++;
  388. }
  389. chan = 0;
  390. while (chan < mathChannelsNum) {
  391. int history_cur = history_start;
  392. cairo_set_source_rgba (cr, line_colors[MAX_REAL_CHANNELS + chan][0], line_colors[MAX_REAL_CHANNELS + chan][1], line_colors[MAX_REAL_CHANNELS + chan][2], 0.5);
  393. cairo_move_to(cr, -1, height/2);
  394. while (history_cur < history_end) {
  395. history_t *p = &history[ history_cur++ ];
  396. x = (double)x_useroffset*width + (double)x_scale * (double)(p->timestamp - timestamp_start);
  397. y = (double)height/2 + (double)y_useroffset[chan]*y_userscale[chan]*height - (double)y_scale * y_userscale[chan] * (double) p->value[chan*2] * (p->value[chan*2] + 1);
  398. cairo_line_to(cr, x, y);
  399. //printf("xy: %u (%lu %e) %u (%u %e %e)\n", x, p->timestamp - timestamp_start, x_scale, y, p->value, y_scale, y_userscale);
  400. }
  401. cairo_stroke(cr);
  402. chan++;
  403. }
  404. pthread_mutex_unlock(&history_mutex);
  405. }
  406. //cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
  407. cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
  408. cairo_move_to(cr, 0, height/2);
  409. cairo_line_to(cr, width, height/2);
  410. cairo_stroke(cr);
  411. return TRUE;
  412. }
  413. int
  414. main (int argc,
  415. char **argv)
  416. {
  417. pthread_t thread_fetcher;
  418. char *dumppath = NULL;
  419. char tailonly = 0;
  420. //sensor_open();
  421. history = xcalloc(HISTORY_SIZE * 2 + 1, sizeof(history_t));
  422. GtkWidget *main_window,
  423. *button,
  424. *area;
  425. gtk_init (&argc, &argv);
  426. // Parsing arguments
  427. char c;
  428. while ((c = getopt (argc, argv, "i:tfC:M:")) != -1) {
  429. char *arg;
  430. arg = optarg;
  431. switch (c)
  432. {
  433. case 'i':
  434. dumppath = arg;
  435. break;
  436. case 't':
  437. tailonly = 1;
  438. break;
  439. case 'C':
  440. channelsNum = atoi(arg);
  441. break;
  442. case 'M':
  443. mathChannelsNum = atoi(arg);
  444. break;
  445. default:
  446. abort ();
  447. }
  448. }
  449. assert ( channelsNum < MAX_REAL_CHANNELS );
  450. assert ( mathChannelsNum < MAX_MATH_CHANNELS );
  451. dump_open(dumppath, tailonly);
  452. if (pthread_create(&thread_fetcher, NULL, history_fetcher, NULL)) {
  453. fprintf(stderr, "Error creating thread\n");
  454. return 1;
  455. }
  456. builder = gtk_builder_new();
  457. GError *gerr = NULL;
  458. if ( gtk_builder_add_from_file (builder, GLADE_PATH, &gerr) <= 0 ) {
  459. fprintf(stderr, "Cannot parse file \""GLADE_PATH"\": %s\n", gerr->message);
  460. return 3;
  461. }
  462. main_window = GTK_WIDGET ( gtk_builder_get_object (builder, "mainwindow") );
  463. assert ( main_window != NULL );
  464. //main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  465. //gtk_window_set_default_size (GTK_WINDOW (main_window), 800, 600);
  466. g_signal_connect (main_window, "destroy", gtk_main_quit, NULL);
  467. //button = gtk_button_new_from_stock (GTK_STOCK_REFRESH);
  468. button = GTK_WIDGET ( gtk_builder_get_object(builder, "refresh") );
  469. assert (button != NULL);
  470. area = GTK_WIDGET ( gtk_builder_get_object(builder, "oscillogram") );
  471. assert (area != NULL);
  472. g_signal_connect (area, "draw", G_CALLBACK (cb_draw), NULL);
  473. //g_signal_connect (area, "resize", G_CALLBACK (cb_resize), main_window);
  474. g_signal_connect (area, "configure-event", G_CALLBACK(cb_resize), NULL);
  475. g_signal_connect_swapped (button, "clicked",
  476. G_CALLBACK (gtk_widget_queue_draw), area);
  477. gtk_widget_show_all (main_window);
  478. line_colors[0][0] = 1;
  479. line_colors[0][1] = 0;
  480. line_colors[0][2] = 0;
  481. line_colors[1][0] = 0;
  482. line_colors[1][1] = 1;
  483. line_colors[1][2] = 0;
  484. line_colors[2][0] = 0;
  485. line_colors[2][1] = 0;
  486. line_colors[2][2] = 1;
  487. line_colors[3][0] = 1;
  488. line_colors[3][1] = 1;
  489. line_colors[3][2] = 0;
  490. line_colors[4][0] = 1;
  491. line_colors[4][1] = 0;
  492. line_colors[4][2] = 1;
  493. line_colors[5][0] = 0;
  494. line_colors[5][1] = 1;
  495. line_colors[5][2] = 1;
  496. line_colors[6][0] = 0.5;
  497. line_colors[6][1] = 0.5;
  498. line_colors[6][2] = 0.5;
  499. {
  500. int i = 7;
  501. while (i < MAX_REAL_CHANNELS + MAX_MATH_CHANNELS) {
  502. line_colors[i][0] = 1;
  503. line_colors[i][1] = 1;
  504. line_colors[i][2] = 1;
  505. i++;
  506. }
  507. }
  508. #if MAX_REAL_CHANNELS + MAX_MATH_CHANNELS < 7
  509. #error MAX_REAL_CHANNELS + MAX_MATH_CHANNELS < 7
  510. #endif
  511. {
  512. char offsetwidgetname[] = "offset_chanXX";
  513. char scalewidgetname[] = "scale_chanXX";
  514. char enablewidgetname[] = "enable_chanXX";
  515. int chan = 0;
  516. while (chan < MAX_REAL_CHANNELS + MAX_MATH_CHANNELS) {
  517. y_userscale[chan] = 1;
  518. y_useroffset[chan] = 0.497;
  519. if (chan < 10) {
  520. offsetwidgetname[11] = chan + '0';
  521. offsetwidgetname[12] = 0;
  522. scalewidgetname [10] = chan + '0';
  523. scalewidgetname [11] = 0;
  524. enablewidgetname[11] = chan + '0';
  525. enablewidgetname[12] = 0;
  526. } else {
  527. offsetwidgetname[11] = chan/10 + '0';
  528. offsetwidgetname[12] = chan%10 + '0';
  529. scalewidgetname [10] = chan/10 + '0';
  530. scalewidgetname [11] = chan%10 + '0';
  531. enablewidgetname[11] = chan/10 + '0';
  532. enablewidgetname[12] = chan%10 + '0';
  533. }
  534. GtkRange *offset = GTK_RANGE ( gtk_builder_get_object(builder, offsetwidgetname) );
  535. GtkRange *scale = GTK_RANGE ( gtk_builder_get_object(builder, scalewidgetname) );
  536. GtkToggleButton *enable = GTK_TOGGLE_BUTTON ( gtk_builder_get_object(builder, enablewidgetname) );
  537. if (offset != NULL) {
  538. gtk_range_set_range(offset, -1 , 1);
  539. gtk_range_set_value(offset, 0.14);
  540. gtk_range_set_increments(offset, 0.05, 0.5);
  541. g_signal_connect (offset, "value-changed", G_CALLBACK (cb_offset_changed), &y_useroffset[chan]);
  542. }
  543. if (scale != NULL) {
  544. gtk_range_set_range(scale, 1 , 10);
  545. gtk_range_set_value(scale, 1);
  546. g_signal_connect (scale, "value-changed", G_CALLBACK (cb_scale_changed), &y_userscale[chan]);
  547. }
  548. if (enable != NULL) {
  549. chanenabled[chan] = 1;
  550. gtk_toggle_button_set_active(enable, TRUE);
  551. g_signal_connect (enable, "toggled", G_CALLBACK (cb_chanenable_toggled), &chanenabled[chan]);
  552. }
  553. chan++;
  554. }
  555. }
  556. arrange_widgets();
  557. //gtk_main ();
  558. while (running) {
  559. printf("update %p\n", area);
  560. gtk_widget_queue_draw(area);
  561. while (gtk_events_pending ())
  562. gtk_main_iteration();
  563. usleep(AUTOUPDATE_USECS);
  564. }
  565. running = 0;
  566. if (pthread_join(thread_fetcher, NULL)) {
  567. fprintf(stderr, "Error joining thread\n");
  568. return 2;
  569. }
  570. // sensor_close();
  571. dump_close();
  572. free(history);
  573. return 0;
  574. }