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.
 
 
 

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