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.
 
 
 

837 lines
21 KiB

  1. /*
  2. * Labjack Tools
  3. * Copyright (c) 2003-2007 Jim Paris <jim@jtan.com>
  4. *
  5. * This is free software; you can redistribute it and/or modify it and
  6. * it is provided under the terms of version 2 of the GNU General Public
  7. * License as published by the Free Software Foundation; see COPYING.
  8. */
  9. #include <stdint.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <errno.h>
  14. #include <sys/time.h>
  15. #include <time.h>
  16. #include <sys/stat.h>
  17. #include <signal.h>
  18. #include <unistd.h>
  19. #include "debug.h"
  20. #include "ue9.h"
  21. #include "ue9error.h"
  22. #include "nerdjack.h"
  23. #include "opt.h"
  24. #include "version.h"
  25. #include "compat.h"
  26. #include "ethstream.h"
  27. #include "example.inc"
  28. #define DEFAULT_HOST "192.168.1.209"
  29. #define UE9_COMMAND_PORT 52360
  30. #define UE9_DATA_PORT 52361
  31. #define MAX_CHANNELS 256
  32. struct callbackInfo {
  33. struct ue9Calibration calib;
  34. int convert;
  35. int maxlines;
  36. };
  37. struct options opt[] = {
  38. {'a', "address", "string", "host/address of device (192.168.1.209)"},
  39. {'n', "numchannels", "n", "sample the first N ADC channels (2)"},
  40. {'C', "channels", "a,b,c", "sample channels a, b, and c"},
  41. {'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
  42. {'L', "labjack", NULL, "Force LabJack device"},
  43. {'t', "timers", "a[:A],b[:B]", "set LabJack timer modes a,b and optional values A,B"},
  44. {'T', "timerdivisor", "n", "set LabJack timer divisor to n"},
  45. {'N', "nerdjack", NULL, "Force NerdJack device"},
  46. {'d', "detect", NULL, "Detect NerdJack IP address"},
  47. {'R', "range", "a,b",
  48. "Set range on NerdJack for channels 0-5,6-11 to either 5 or 10 (10,10)"},
  49. {'g', "gain", "a,b,c", "Set Labjack AIN channel gains: 0,1,2,4,8 in -C channel order"},
  50. {'o', "oneshot", NULL, "don't retry in case of errors"},
  51. {'f', "forceretry", NULL, "retry no matter what happens"},
  52. {'c', "convert", NULL, "convert output to volts/temperature"},
  53. {'H', "converthex", NULL, "convert output to hex"},
  54. {'m', "showmem", NULL, "output memory stats with data (NJ only)"},
  55. {'l', "lines", "num", "if set, output this many lines and quit"},
  56. {'h', "help", NULL, "this help"},
  57. {'v', "verbose", NULL, "be verbose"},
  58. {'V', "version", NULL, "show version number and exit"},
  59. {'i', "info", NULL, "get info from device (NJ only)"},
  60. {'X', "examples", NULL, "show ethstream examples and exit"},
  61. {0, NULL, NULL, NULL}
  62. };
  63. int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
  64. int *channel_list, int channel_count,
  65. int *timer_mode_list, int *timer_value_list,
  66. int timer_mode_count, int timer_divisor,
  67. int *gain_list, int gain_count,
  68. int convert, int maxlines);
  69. int nerdDoStream(const char *address, int *channel_list, int channel_count,
  70. int precision, unsigned long period, int convert, int lines,
  71. int showmem);
  72. int data_callback(int channels, int *channel_list, int gain_count, int *gain_list,
  73. uint16_t * data, void *context);
  74. int columns_left = 0;
  75. ////////EXTRA GLOBAL VARS///////////
  76. // for clean shutdown //
  77. // added by John Donnal 2015 //
  78. ////////////////////////////////////
  79. int fd_cmd, fd_data;
  80. int ue9_running = 0; //flag if labjack is currently streaming data
  81. void handle_sig(int sig)
  82. {
  83. while (columns_left--) {
  84. printf(" 0");
  85. }
  86. /******************************************************
  87. * added by John Donnal 2015 *
  88. * Close out connection to LabJack, firmware glitches *
  89. * if the stream is not closed correctly *
  90. ******************************************************/
  91. if(ue9_running==1){
  92. printf("Performing clean shutdown of LabJack\n");
  93. ue9_stream_stop(fd_cmd);
  94. ue9_buffer_flush(fd_cmd);
  95. ue9_close(fd_data);
  96. ue9_close(fd_cmd);
  97. }
  98. /******************************************************/
  99. fflush(stdout);
  100. exit(0);
  101. }
  102. int main(int argc, char *argv[])
  103. {
  104. int optind;
  105. char *optarg, *endp;
  106. char c;
  107. int tmp, i;
  108. FILE *help = stderr;
  109. char *address = strdup(DEFAULT_HOST);
  110. double desired_rate = 8000.0;
  111. int lines = 0;
  112. double actual_rate;
  113. int oneshot = 0;
  114. int forceretry = 0;
  115. int convert = CONVERT_DEC;
  116. int showmem = 0;
  117. int inform = 0;
  118. uint8_t scanconfig;
  119. uint16_t scaninterval;
  120. int timer_mode_list[UE9_TIMERS];
  121. int timer_value_list[UE9_TIMERS];
  122. int timer_mode_count = 0;
  123. int timer_divisor = 1;
  124. int gain_list[MAX_CHANNELS];
  125. int gain_count = 0;
  126. int channel_list[MAX_CHANNELS];
  127. int channel_count = 0;
  128. int nerdjack = 0;
  129. int labjack = 0;
  130. int detect = 0;
  131. int precision = 0;
  132. int addressSpecified = 0;
  133. int donerdjack = 0;
  134. unsigned long period = NERDJACK_CLOCK_RATE / desired_rate;
  135. /* Parse arguments */
  136. opt_init(&optind);
  137. while ((c = opt_parse(argc, argv, &optind, &optarg, opt)) != 0) {
  138. switch (c) {
  139. case 'a':
  140. free(address);
  141. address = strdup(optarg);
  142. addressSpecified = 1;
  143. break;
  144. case 'n':
  145. channel_count = 0;
  146. tmp = strtol(optarg, &endp, 0);
  147. if (*endp || tmp < 1 || tmp > MAX_CHANNELS) {
  148. info("bad number of channels: %s\n", optarg);
  149. goto printhelp;
  150. }
  151. for (i = 0; i < tmp; i++)
  152. channel_list[channel_count++] = i;
  153. break;
  154. case 'C':
  155. channel_count = 0;
  156. do {
  157. tmp = strtol(optarg, &endp, 0);
  158. if (*endp != '\0' && *endp != ',') {
  159. info("bad channel number: %s\n",
  160. optarg);
  161. goto printhelp;
  162. }
  163. //We do not want to overflow channel_list, so we need the check here
  164. //The rest of the sanity checking can come later after we know
  165. //whether this is a
  166. //LabJack or a NerdJack
  167. if (channel_count >= MAX_CHANNELS) {
  168. info("error: too many channels specified\n");
  169. goto printhelp;
  170. }
  171. channel_list[channel_count++] = tmp;
  172. optarg = endp + 1;
  173. }
  174. while (*endp);
  175. break;
  176. case 'g': /* labjack only */
  177. gain_count = 0;
  178. do {
  179. tmp = strtol(optarg, &endp, 0);
  180. if (*endp != '\0' && *endp != ',') {
  181. info("bad gain number: %s\n",
  182. optarg);
  183. goto printhelp;
  184. }
  185. if (gain_count >= MAX_CHANNELS) {
  186. info("error: too many gains specified\n");
  187. goto printhelp;
  188. }
  189. if (!(tmp == 0 || tmp == 1 || tmp == 2 || tmp == 3 || tmp == 8)) {
  190. info("error: invalid gain specified\n");
  191. goto printhelp;
  192. }
  193. gain_list[gain_count++] = tmp;
  194. optarg = endp + 1;
  195. }
  196. while (*endp);
  197. break;
  198. case 't': /* labjack only */
  199. timer_mode_count = 0;
  200. do {
  201. /* get mode */
  202. tmp = strtol(optarg, &endp, 0);
  203. if (*endp != '\0' && *endp != ',' && *endp != ':') {
  204. info("bad timer mode: %s\n", optarg);
  205. goto printhelp;
  206. }
  207. if (timer_mode_count >= UE9_TIMERS) {
  208. info("error: too many timers specified\n");
  209. goto printhelp;
  210. }
  211. timer_mode_list[timer_mode_count] = tmp;
  212. /* get optional value */
  213. if (*endp == ':') {
  214. optarg = endp + 1;
  215. tmp = strtol(optarg, &endp, 0);
  216. if (*endp != '\0' && *endp != ',') {
  217. info("bad timer value: %s\n", optarg);
  218. goto printhelp;
  219. }
  220. timer_value_list[timer_mode_count] = tmp;
  221. } else {
  222. timer_value_list[timer_mode_count] = 0;
  223. }
  224. timer_mode_count++;
  225. optarg = endp + 1;
  226. }
  227. while (*endp);
  228. break;
  229. case 'T': /* labjack only */
  230. timer_divisor = strtod(optarg, &endp);
  231. if (*endp || timer_divisor < 0 || timer_divisor > 255) {
  232. info("bad timer divisor: %s\n", optarg);
  233. goto printhelp;
  234. }
  235. break;
  236. case 'r':
  237. desired_rate = strtod(optarg, &endp);
  238. if (*endp || desired_rate <= 0) {
  239. info("bad rate: %s\n", optarg);
  240. goto printhelp;
  241. }
  242. break;
  243. case 'l':
  244. lines = strtol(optarg, &endp, 0);
  245. if (*endp || lines <= 0) {
  246. info("bad number of lines: %s\n", optarg);
  247. goto printhelp;
  248. }
  249. break;
  250. case 'R':
  251. tmp = strtol(optarg, &endp, 0);
  252. if (*endp != ',') {
  253. info("bad range number: %s\n", optarg);
  254. goto printhelp;
  255. }
  256. if (tmp != 5 && tmp != 10) {
  257. info("valid choices for range are 5 or 10\n");
  258. goto printhelp;
  259. }
  260. if (tmp == 5)
  261. precision = precision + 1;
  262. optarg = endp + 1;
  263. if (*endp == '\0') {
  264. info("Range needs two numbers, one for channels 0-5 and another for 6-11\n");
  265. goto printhelp;
  266. }
  267. tmp = strtol(optarg, &endp, 0);
  268. if (*endp != '\0') {
  269. info("Range needs only two numbers, one for channels 0-5 and another for 6-11\n");
  270. goto printhelp;
  271. }
  272. if (tmp != 5 && tmp != 10) {
  273. info("valid choices for range are 5 or 10\n");
  274. goto printhelp;
  275. }
  276. if (tmp == 5)
  277. precision = precision + 2;
  278. break;
  279. case 'N':
  280. nerdjack++;
  281. break;
  282. case 'L':
  283. labjack++;
  284. break;
  285. case 'd':
  286. detect++;
  287. break;
  288. case 'o':
  289. oneshot++;
  290. break;
  291. case 'f':
  292. forceretry++;
  293. break;
  294. case 'c':
  295. if (convert != 0) {
  296. info("specify only one conversion type\n");
  297. goto printhelp;
  298. }
  299. convert = CONVERT_VOLTS;
  300. break;
  301. case 'H':
  302. if (convert != 0) {
  303. info("specify only one conversion type\n");
  304. goto printhelp;
  305. }
  306. convert = CONVERT_HEX;
  307. break;
  308. case 'm':
  309. showmem++;
  310. case 'v':
  311. verb_count++;
  312. break;
  313. case 'X':
  314. printf("%s", examplestring);
  315. return 0;
  316. break;
  317. case 'V':
  318. printf("ethstream " VERSION "\n");
  319. printf("Written by Jim Paris <jim@jtan.com>\n");
  320. printf("and John Donnal <jdonnal@mit.edu>\n");
  321. printf("and Zachary Clifford <zacharyc@mit.edu>.\n");
  322. printf("This program comes with no warranty and is "
  323. "provided under the GPLv2.\n");
  324. return 0;
  325. break;
  326. case 'i':
  327. inform++;
  328. break;
  329. case 'h':
  330. help = stdout;
  331. default:
  332. printhelp:
  333. fprintf(help, "Usage: %s [options]\n", *argv);
  334. opt_help(opt, help);
  335. fprintf(help, "Read data from the specified Labjack UE9"
  336. " via Ethernet. See README for details.\n");
  337. return (help == stdout) ? 0 : 1;
  338. }
  339. }
  340. if (detect && labjack) {
  341. info("The LabJack does not support autodetection\n");
  342. goto printhelp;
  343. }
  344. if (detect && !nerdjack) {
  345. info("Only the NerdJack supports autodetection - assuming -N option\n");
  346. nerdjack = 1;
  347. }
  348. if (detect && addressSpecified) {
  349. info("Autodetection and specifying address are mutually exclusive\n");
  350. goto printhelp;
  351. }
  352. if (nerdjack && labjack) {
  353. info("Nerdjack and Labjack options are mutually exclusive\n");
  354. goto printhelp;
  355. }
  356. donerdjack = nerdjack;
  357. //First if no options were supplied try the Nerdjack
  358. //The second time through, donerdjack will be true and this will not fire
  359. if (!nerdjack && !labjack) {
  360. info("No device specified...Defaulting to Nerdjack\n");
  361. donerdjack = 1;
  362. }
  363. doneparse:
  364. if (inform) {
  365. //We just want information from NerdJack
  366. if (!detect) {
  367. if (nerd_get_version(address) < 0) {
  368. info("Could not find NerdJack at specified address\n");
  369. } else {
  370. return 0;
  371. }
  372. }
  373. info("Autodetecting NerdJack address\n");
  374. free(address);
  375. if (nerdjack_detect(address) < 0) {
  376. info("Error with autodetection\n");
  377. goto printhelp;
  378. } else {
  379. info("Found NerdJack at address: %s\n", address);
  380. if (nerd_get_version(address) < 0) {
  381. info("Error getting NerdJack version\n");
  382. goto printhelp;
  383. }
  384. return 0;
  385. }
  386. }
  387. if (donerdjack) {
  388. if (channel_count > NERDJACK_CHANNELS) {
  389. info("Too many channels for NerdJack\n");
  390. goto printhelp;
  391. }
  392. for (i = 0; i < channel_count; i++) {
  393. if (channel_list[i] >= NERDJACK_CHANNELS) {
  394. info("Channel is out of NerdJack range: %d\n",
  395. channel_list[i]);
  396. goto printhelp;
  397. }
  398. }
  399. } else {
  400. if (channel_count > UE9_MAX_CHANNEL_COUNT) {
  401. info("Too many channels for LabJack\n");
  402. goto printhelp;
  403. }
  404. for (i = 0; i < channel_count; i++) {
  405. if (channel_list[i] > UE9_MAX_CHANNEL) {
  406. info("Channel is out of LabJack range: %d\n",
  407. channel_list[i]);
  408. goto printhelp;
  409. }
  410. }
  411. }
  412. /* Timer requires Labjack */
  413. if (timer_mode_count && !labjack) {
  414. info("Can't use timers on NerdJack\n");
  415. goto printhelp;
  416. }
  417. /* Individual Analog Channel Gain Set requires Labjack*/
  418. if (gain_count && !labjack) {
  419. info("Can't use Individual Gain Set on NerdJack\n");
  420. goto printhelp;
  421. }
  422. if (optind < argc) {
  423. info("error: too many arguments (%s)\n\n", argv[optind]);
  424. goto printhelp;
  425. }
  426. if (forceretry && oneshot) {
  427. info("forceretry and oneshot options are mutually exclusive\n");
  428. goto printhelp;
  429. }
  430. /* Two channels if none specified */
  431. if (channel_count == 0) {
  432. channel_list[channel_count++] = 0;
  433. channel_list[channel_count++] = 1;
  434. }
  435. if (verb_count) {
  436. info("Scanning channels:");
  437. for (i = 0; i < channel_count; i++)
  438. info_no_timestamp(" AIN%d", channel_list[i]);
  439. info_no_timestamp("\n");
  440. }
  441. /* Figure out actual rate. */
  442. if (donerdjack) {
  443. if (nerdjack_choose_scan(desired_rate, &actual_rate, &period) <
  444. 0) {
  445. info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
  446. }
  447. } else {
  448. if (ue9_choose_scan(desired_rate, &actual_rate,
  449. &scanconfig, &scaninterval) < 0) {
  450. info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
  451. }
  452. }
  453. if ((desired_rate != actual_rate) || verb_count) {
  454. info("Actual scanrate is %lf Hz\n", actual_rate);
  455. info("Period is %ld\n", period);
  456. }
  457. if (verb_count && lines) {
  458. info("Stopping capture after %d lines\n", lines);
  459. }
  460. signal(SIGINT, handle_sig);
  461. signal(SIGTERM, handle_sig);
  462. #ifdef SIGPIPE /* not on Windows */
  463. /* Ignore SIGPIPE so I/O errors to the network device won't kill the process */
  464. signal(SIGPIPE, SIG_IGN);
  465. #endif
  466. if (detect) {
  467. info("Autodetecting NerdJack address\n");
  468. free(address);
  469. if (nerdjack_detect(address) < 0) {
  470. info("Error with autodetection\n");
  471. goto printhelp;
  472. } else {
  473. info("Found NerdJack at address: %s\n", address);
  474. }
  475. }
  476. for (;;) {
  477. int ret;
  478. if (donerdjack) {
  479. ret =
  480. nerdDoStream(address, channel_list, channel_count,
  481. precision, period, convert, lines,
  482. showmem);
  483. verb("nerdDoStream returned %d\n", ret);
  484. } else {
  485. ret = doStream(address, scanconfig, scaninterval,
  486. channel_list, channel_count,
  487. timer_mode_list, timer_value_list,
  488. timer_mode_count, timer_divisor,
  489. gain_list, gain_count,
  490. convert, lines);
  491. verb("doStream returned %d\n", ret);
  492. }
  493. if (oneshot)
  494. break;
  495. if (ret == 0)
  496. break;
  497. //Neither options specified at command line and first time through.
  498. //Try LabJack
  499. if (ret == -ENOTCONN && donerdjack && !labjack && !nerdjack) {
  500. info("Could not connect NerdJack...Trying LabJack\n");
  501. donerdjack = 0;
  502. goto doneparse;
  503. }
  504. //Neither option supplied, no address, and second time through.
  505. //Try autodetection
  506. if (ret == -ENOTCONN && !donerdjack && !labjack && !nerdjack
  507. && !addressSpecified) {
  508. info("Could not connect LabJack...Trying to autodetect Nerdjack\n");
  509. detect = 1;
  510. donerdjack = 1;
  511. goto doneparse;
  512. }
  513. if (ret == -ENOTCONN && nerdjack && !detect
  514. && !addressSpecified) {
  515. info("Could not reach NerdJack...Trying to autodetect\n");
  516. detect = 1;
  517. goto doneparse;
  518. }
  519. if (ret == -ENOTCONN && !forceretry) {
  520. info("Initial connection failed, giving up\n");
  521. break;
  522. }
  523. if (ret == -EAGAIN || ret == -ENOTCONN) {
  524. /* Some transient error. Wait a tiny bit, then retry */
  525. info("Retrying in 5 secs.\n");
  526. sleep(5);
  527. } else {
  528. info("Retrying now.\n");
  529. }
  530. }
  531. debug("Done loop\n");
  532. return 0;
  533. }
  534. int
  535. nerdDoStream(const char *address, int *channel_list, int channel_count,
  536. int precision, unsigned long period, int convert, int lines,
  537. int showmem)
  538. {
  539. int retval = -EAGAIN;
  540. int fd_data;
  541. static int first_call = 1;
  542. static int started = 0;
  543. static int wasreset = 0;
  544. getPacket command;
  545. static unsigned short currentcount = 0;
  546. tryagain:
  547. //If this is the first time, set up acquisition
  548. //Otherwise try to resume the previous one
  549. if (started == 0) {
  550. if (nerd_generate_command
  551. (&command, channel_list, channel_count, precision,
  552. period) < 0) {
  553. info("Failed to create configuration command\n");
  554. goto out;
  555. }
  556. if (nerd_send_command(address, "STOP", 4) < 0) {
  557. if (first_call) {
  558. retval = -ENOTCONN;
  559. if (verb_count)
  560. info("Failed to send STOP command\n");
  561. } else {
  562. info("Failed to send STOP command\n");
  563. }
  564. goto out;
  565. }
  566. if (nerd_send_command(address, &command, sizeof(command)) < 0) {
  567. info("Failed to send GET command\n");
  568. goto out;
  569. }
  570. } else {
  571. //If we had a transmission in progress, send a command to resume from there
  572. char cmdbuf[10];
  573. sprintf(cmdbuf, "SETC%05hd", currentcount);
  574. retval = nerd_send_command(address, cmdbuf, strlen(cmdbuf));
  575. if (retval == -4) {
  576. info("NerdJack was reset\n");
  577. //Assume we have not started yet, reset on this side.
  578. //If this routine is retried, start over
  579. printf("# NerdJack was reset here\n");
  580. currentcount = 0;
  581. started = 0;
  582. wasreset = 1;
  583. goto tryagain;
  584. } else if (retval < 0) {
  585. info("Failed to send SETC command\n");
  586. goto out;
  587. }
  588. }
  589. //The transmission has begun
  590. started = 1;
  591. /* Open connection */
  592. fd_data = nerd_open(address, NERDJACK_DATA_PORT);
  593. if (fd_data < 0) {
  594. info("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
  595. goto out;
  596. }
  597. retval = nerd_data_stream
  598. (fd_data, channel_count, channel_list, precision, convert, lines,
  599. showmem, &currentcount, period, wasreset);
  600. wasreset = 0;
  601. if (retval == -3) {
  602. retval = 0;
  603. }
  604. if (retval < 0) {
  605. info("Failed to open data stream\n");
  606. goto out1;
  607. }
  608. info("Stream finished\n");
  609. retval = 0;
  610. out1:
  611. nerd_close_conn(fd_data);
  612. out:
  613. //We've tried communicating, so this is not the first call anymore
  614. first_call = 0;
  615. return retval;
  616. }
  617. int
  618. doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
  619. int *channel_list, int channel_count,
  620. int *timer_mode_list, int *timer_value_list,
  621. int timer_mode_count, int timer_divisor,
  622. int *gain_list, int gain_count,
  623. int convert, int lines)
  624. {
  625. int retval = -EAGAIN;
  626. // int fd_cmd, fd_data; *these are now globals so sighandler can use them*
  627. int ret;
  628. static int first_call = 1;
  629. struct callbackInfo ci = {
  630. .convert = convert,
  631. .maxlines = lines,
  632. };
  633. /* Open command connection. If this fails, and this is the
  634. first attempt, return a different error code so we give up. */
  635. fd_cmd = ue9_open(address, UE9_COMMAND_PORT);
  636. if (fd_cmd < 0) {
  637. info("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
  638. if (first_call)
  639. retval = -ENOTCONN;
  640. goto out;
  641. }
  642. first_call = 0;
  643. /* Make sure nothing is left over from a previous stream */
  644. if (ue9_stream_stop(fd_cmd) == 0)
  645. verb("Stopped previous stream.\n");
  646. ue9_buffer_flush(fd_cmd);
  647. /* Open data connection */
  648. fd_data = ue9_open(address, UE9_DATA_PORT);
  649. if (fd_data < 0) {
  650. info("Connect failed: %s:%d\n", address, UE9_DATA_PORT);
  651. goto out1;
  652. }
  653. /* Get calibration */
  654. if (ue9_get_calibration(fd_cmd, &ci.calib) < 0) {
  655. info("Failed to get device calibration\n");
  656. goto out2;
  657. }
  658. /* Set timer configuration */
  659. if (timer_mode_count &&
  660. ue9_timer_config(fd_cmd, timer_mode_list, timer_value_list,
  661. timer_mode_count, timer_divisor) < 0) {
  662. info("Failed to set timer configuration\n");
  663. goto out2;
  664. }
  665. if (gain_count) {
  666. /* Set stream configuration */
  667. if (ue9_streamconfig(fd_cmd, channel_list, channel_count,
  668. scanconfig, scaninterval,
  669. gain_list, gain_count) < 0) {
  670. info("Failed to set stream configuration\n");
  671. goto out2;
  672. }
  673. } else {
  674. /* Set stream configuration */
  675. if (ue9_streamconfig_simple(fd_cmd, channel_list, channel_count,
  676. scanconfig, scaninterval,
  677. UE9_BIPOLAR_GAIN1) < 0) {
  678. info("Failed to set stream configuration\n");
  679. goto out2;
  680. }
  681. }
  682. /* Start stream */
  683. if (ue9_stream_start(fd_cmd) < 0) {
  684. info("Failed to start stream\n");
  685. goto out2;
  686. }
  687. /* Stream data */
  688. ue9_running = 1;
  689. ret =
  690. ue9_stream_data(fd_data, channel_count, channel_list, gain_count, gain_list, data_callback, (void *)&ci);
  691. if (ret < 0) {
  692. info("Data stream failed with error %d\n", ret);
  693. goto out3;
  694. }
  695. info("Stream finished\n");
  696. retval = 0;
  697. out3:
  698. /* Stop stream and clean up */
  699. ue9_stream_stop(fd_cmd);
  700. ue9_buffer_flush(fd_cmd);
  701. out2:
  702. ue9_close(fd_data);
  703. out1:
  704. ue9_close(fd_cmd);
  705. out:
  706. ue9_running = 0;
  707. return retval;
  708. }
  709. int data_callback(int channels, int *channel_list, int gain_count, int *gain_list, uint16_t * data, void *context)
  710. {
  711. int i;
  712. struct callbackInfo *ci = (struct callbackInfo *)context;
  713. static int lines = 0;
  714. columns_left = channels;
  715. for (i = 0; i < channels; i++) {
  716. if (ci->convert == CONVERT_VOLTS &&
  717. channel_list[i] <= UE9_MAX_ANALOG_CHANNEL) {
  718. /* CONVERT_VOLTS */
  719. if (i < gain_count)
  720. {
  721. if (printf("%lf", ue9_binary_to_analog(
  722. &ci->calib, gain_list[i],
  723. 12, data[i])) < 0)
  724. goto bad;
  725. } else {
  726. if (printf("%lf", ue9_binary_to_analog(
  727. &ci->calib, 0,
  728. 12, data[i])) < 0)
  729. goto bad;
  730. }
  731. } else if (ci->convert == CONVERT_VOLTS &&
  732. (channel_list[i] == 141 || channel_list[i] == 133)) {
  733. /* CONVERT_VOLTS but output temperature */
  734. if (printf("%lf", ue9_binary_to_temperature(
  735. &ci->calib, data[i])) < 0)
  736. goto bad;
  737. } else if (ci->convert == CONVERT_HEX) {
  738. /* CONVERT_HEX */
  739. if (printf("%04X", data[i]) < 0)
  740. goto bad;
  741. } else {
  742. /* CONVERT_DEC */
  743. if (printf("%d", data[i]) < 0)
  744. goto bad;
  745. }
  746. columns_left--;
  747. if (i < (channels - 1)) {
  748. if (ci->convert != CONVERT_HEX && putchar(' ') < 0)
  749. goto bad;
  750. } else {
  751. if (putchar('\n') < 0)
  752. goto bad;
  753. lines++;
  754. if (ci->maxlines && lines >= ci->maxlines)
  755. return -1;
  756. }
  757. }
  758. return 0;
  759. bad:
  760. info("Output error (disk full?)\n");
  761. return -3;
  762. }