main.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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_CHANNELS 1
  32. #define Y_BITS 12
  33. typedef struct {
  34. uint64_t timestamp;
  35. uint32_t value;
  36. } history_t;
  37. int running = 1;
  38. history_t *history [MAX_CHANNELS];
  39. uint32_t history_length[MAX_CHANNELS] = {0};
  40. uint64_t ts_global = 0;
  41. pthread_mutex_t history_mutex;
  42. enum trigger_mode {
  43. TG_RISE,
  44. TG_FALL,
  45. };
  46. double x_userdiv = 1.9E-3;
  47. double x_useroffset = 0;
  48. double y_userscale = 7;
  49. double y_useroffset = -0.17;
  50. int trigger_start_mode = TG_RISE;
  51. int trigger_start_y = 695;
  52. int trigger_end_mode = TG_RISE;
  53. int trigger_end_y = 695;
  54. void
  55. sensor_open()
  56. {
  57. sensor = stdin;
  58. return;
  59. }
  60. int
  61. sensor_fetch(history_t *p)
  62. {
  63. uint16_t ts_local;
  64. uint16_t ts_local_old;
  65. uint16_t value;
  66. ts_local = get_uint16(sensor);
  67. value = get_uint16(sensor);
  68. ts_local_old = ts_global & 0xffff;
  69. if (ts_local <= ts_local_old) {
  70. ts_global += 1<<16;
  71. }
  72. ts_global = (ts_global & ~0xffff) | ts_local;
  73. p->timestamp = ts_global;
  74. p->value = value;
  75. return 1;
  76. }
  77. void
  78. sensor_close()
  79. {
  80. return;
  81. }
  82. void
  83. dump_open(char *dumppath, char tailonly)
  84. {
  85. dump = stdin;
  86. if (dumppath == NULL)
  87. return;
  88. if (*dumppath == 0)
  89. return;
  90. if (!strcmp(dumppath, "-"))
  91. return;
  92. dump = fopen(dumppath, "r");
  93. if (dump == NULL) {
  94. fprintf(stderr, "Cannot open file \"%s\": %s", dumppath, strerror(errno));
  95. abort ();
  96. }
  97. if (tailonly) {
  98. fseek(dump, 0, SEEK_END);
  99. }
  100. return;
  101. }
  102. int
  103. dump_fetch(history_t *p)
  104. {
  105. uint64_t ts_parse;
  106. uint64_t ts_device;
  107. uint32_t value;
  108. ts_parse = get_uint64(dump);
  109. while (ts_parse < 1437900000000000000 || ts_parse > 1537900000000000000) {
  110. printf("dump_fetch() correction 0\n");
  111. get_uint8(dump);
  112. ts_parse = get_uint64(dump);
  113. }
  114. ts_device = get_uint64(dump);
  115. value = get_uint32(dump);
  116. while (value < 0 || value > 1024) {
  117. printf("dump_fetch() correction 1\n");
  118. value = get_uint32(dump);
  119. }
  120. (void)ts_parse;
  121. p->timestamp = ts_device;
  122. p->value = value;
  123. //printf("V: %lu %lu %u\n", ts_parse, ts_device, value);
  124. //sleep(1);
  125. return 1;
  126. }
  127. void
  128. dump_close()
  129. {
  130. return;
  131. }
  132. void
  133. history_flush()
  134. {
  135. //sleep(3600);
  136. pthread_mutex_lock(&history_mutex);
  137. memcpy(history[0], &history[0][HISTORY_SIZE], sizeof(*history[0])*HISTORY_SIZE);
  138. history_length[0] = HISTORY_SIZE;
  139. printf("history_flush()\n");
  140. pthread_mutex_unlock(&history_mutex);
  141. return;
  142. }
  143. void *
  144. history_fetcher(void *arg)
  145. {
  146. while (running) {
  147. //sensor_fetch(&history[0][ history_length[0]++ ]);
  148. dump_fetch(&history[0][ history_length[0]++ ]);
  149. // printf("%lu; %u\n", history[0][ history_length[0]-1].timestamp, history[0][ history_length[0]-1].value);
  150. if (history_length[0] >= HISTORY_SIZE * 2)
  151. history_flush();
  152. }
  153. return NULL;
  154. }
  155. static gboolean
  156. cb_expose (GtkWidget *area,
  157. GdkEventExpose *event,
  158. gpointer *data)
  159. {
  160. int width, height;
  161. cairo_t *cr;
  162. cr = gdk_cairo_create (event->window);
  163. width = gdk_window_get_width (event->window);
  164. height = gdk_window_get_height (event->window);
  165. cairo_rectangle(cr, 0, 0, width, height);
  166. cairo_set_source_rgb(cr, 0, 0, 0);
  167. cairo_fill(cr);
  168. cairo_set_line_width (cr, 2);
  169. int history_end = history_length[0]-2;
  170. if (history_end >= (double)HISTORY_SIZE*x_userdiv) {
  171. //printf("%u %u\n", HISTORY_SIZE, history_end);
  172. pthread_mutex_lock(&history_mutex);
  173. //cairo_set_source_rgba (cr, 0, 0, 0.3, 0.8);
  174. //cairo_set_source_rgba (cr, 0.8, 0.8, 1, 0.8);
  175. cairo_set_source_rgba (cr, 0.1, 1, 0.1, 0.8);
  176. cairo_move_to(cr, -1, height/2);
  177. int x;
  178. int y;
  179. //int history_start = history_end - HISTORY_SIZE;
  180. int history_start_initial = history_end - (double)HISTORY_SIZE*x_userdiv;
  181. int history_start = history_start_initial;
  182. if (history_start == 0)
  183. history_start++;
  184. //printf("h: %u %u\n", history_start, history_end);
  185. char found;
  186. found = 0;
  187. while (history_start < history_end && !found) {
  188. history_t *cur = &history[0][history_start];
  189. history_t *prev = &history[0][history_start-1];
  190. //printf("1 %u %u\n", cur->value, prev->value);
  191. switch (trigger_start_mode) {
  192. case TG_FALL:
  193. found = (cur->value >= trigger_start_y && prev->value < trigger_start_y);
  194. break;
  195. case TG_RISE:
  196. found = (cur->value < trigger_start_y && prev->value >= trigger_start_y);
  197. break;
  198. }
  199. if (found)
  200. break;
  201. history_start++;
  202. }
  203. if (history_start >= history_end) {
  204. printf("Unable to sync start\n");
  205. history_start = history_start_initial;
  206. }
  207. int history_end_initial = history_end;
  208. history_end--;
  209. found = 0;
  210. while (history_start < history_end && !found) {
  211. history_t *cur = &history[0][history_end];
  212. history_t *prev = &history[0][history_end+1];
  213. //printf("2 %u %u\n", cur->value, prev->value);
  214. switch (trigger_end_mode) {
  215. case TG_RISE:
  216. found = (cur->value >= trigger_end_y && prev->value < trigger_end_y);
  217. break;
  218. case TG_FALL:
  219. found = (cur->value < trigger_end_y && prev->value >= trigger_end_y);
  220. break;
  221. }
  222. if (found)
  223. break;
  224. history_end--;
  225. }
  226. if (history_start >= history_end) {
  227. printf("Unable to sync end\n");
  228. history_end = history_end_initial;
  229. }
  230. //printf("H: %u %u\n", history_start, history_end);
  231. uint64_t timestamp_start = history[0][history_start].timestamp;
  232. uint64_t timestamp_end = history[0][history_end ].timestamp;
  233. int history_cur = history_start;
  234. if (timestamp_start == timestamp_end) {
  235. printf("%lu %lu %u %u %u %u\n", timestamp_start, timestamp_end, history_start, history_end, history[0][history_start].value, history[0][history_end].value);
  236. }
  237. assert (timestamp_end != timestamp_start);
  238. double x_scale = (double)width / (timestamp_end - timestamp_start);
  239. double y_scale = (double)height / (1 << Y_BITS);
  240. while (history_cur < history_end) {
  241. history_t *p = &history[0][ history_cur++ ];
  242. x = (double)x_useroffset*width + (double)x_scale * (double)(p->timestamp - timestamp_start);
  243. y = (double)height/2 + (double)y_useroffset*y_userscale*height + (double)y_scale * y_userscale * (double) p->value;
  244. cairo_line_to(cr, x, y);
  245. //printf("xy: %u (%lu %e) %u (%u %e %e)\n", x, p->timestamp - timestamp_start, x_scale, y, p->value, y_scale, y_userscale);
  246. }
  247. cairo_stroke(cr);
  248. pthread_mutex_unlock(&history_mutex);
  249. }
  250. //cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
  251. cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
  252. cairo_move_to(cr, 0, height/2);
  253. cairo_line_to(cr, width, height/2);
  254. cairo_stroke(cr);
  255. cairo_destroy (cr);
  256. return TRUE;
  257. }
  258. void *
  259. update (void *arg)
  260. {
  261. GtkWidget *area = arg;
  262. while (running) {
  263. gtk_widget_queue_draw(area);
  264. usleep(AUTOUPDATE_USECS);
  265. }
  266. return NULL;
  267. }
  268. int
  269. main (int argc,
  270. char **argv)
  271. {
  272. int i;
  273. pthread_t thread_fetcher;
  274. pthread_t thread_autoupdate;
  275. char *dumppath = NULL;
  276. char tailonly = 0;
  277. //sensor_open();
  278. // Parsing arguments
  279. char c;
  280. while ((c = getopt (argc, argv, "i:tf")) != -1) {
  281. char *arg;
  282. arg = optarg;
  283. switch (c)
  284. {
  285. case 'i':
  286. dumppath = arg;
  287. break;
  288. case 't':
  289. tailonly = 1;
  290. break;
  291. default:
  292. abort ();
  293. }
  294. }
  295. i = 0;
  296. while (i < MAX_CHANNELS)
  297. history[i++] = xcalloc(HISTORY_SIZE * 2 + 1, sizeof(history_t));
  298. dump_open(dumppath, tailonly);
  299. if (pthread_create(&thread_fetcher, NULL, history_fetcher, NULL)) {
  300. fprintf(stderr, "Error creating thread\n");
  301. return 1;
  302. }
  303. GtkWidget *main_window,
  304. *vbox,
  305. *button,
  306. *area;
  307. gtk_init (&argc, &argv);
  308. main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  309. gtk_window_set_default_size (GTK_WINDOW (main_window), 800, 600);
  310. g_signal_connect (main_window, "destroy", gtk_main_quit, NULL);
  311. vbox = gtk_vbox_new (FALSE, 5);
  312. gtk_container_add (GTK_CONTAINER (main_window), vbox);
  313. button = gtk_button_new_from_stock (GTK_STOCK_REFRESH);
  314. gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  315. area = gtk_drawing_area_new ();
  316. g_signal_connect (area, "expose-event", G_CALLBACK (cb_expose), NULL);
  317. gtk_box_pack_start (GTK_BOX (vbox), area, TRUE, TRUE, 0);
  318. g_signal_connect_swapped (button, "clicked",
  319. G_CALLBACK (gtk_widget_queue_draw), area);
  320. gtk_widget_show_all (main_window);
  321. if (pthread_create(&thread_autoupdate, NULL, update, area)) {
  322. fprintf(stderr, "Error creating thread\n");
  323. return 1;
  324. }
  325. gtk_main ();
  326. running = 0;
  327. if (pthread_join(thread_autoupdate, NULL)) {
  328. fprintf(stderr, "Error joining thread\n");
  329. return 2;
  330. }
  331. if (pthread_join(thread_fetcher, NULL)) {
  332. fprintf(stderr, "Error joining thread\n");
  333. return 2;
  334. }
  335. // sensor_close();
  336. dump_close();
  337. i = 0;
  338. while (i < MAX_CHANNELS)
  339. free(history[i++]);
  340. return 0;
  341. }