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.
 
 
 

644 lines
14 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. /* ljstream: Stream data from the first N (1-14) analog inputs.
  10. Resolution is set to 12-bit and all channels are in bipolar (-5 to
  11. +5V) mode.
  12. */
  13. #include <stdint.h>
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <errno.h>
  18. #include <sys/time.h>
  19. #include <time.h>
  20. #include <sys/stat.h>
  21. #include <signal.h>
  22. #include <unistd.h>
  23. #include "debug.h"
  24. #include "ue9.h"
  25. #include "ue9error.h"
  26. #include "nerdjack.h"
  27. #include "opt.h"
  28. #include "version.h"
  29. #include "compat.h"
  30. #include "ethstream.h"
  31. #define DEFAULT_HOST "192.168.1.209"
  32. #define UE9_COMMAND_PORT 52360
  33. #define UE9_DATA_PORT 52361
  34. struct callbackInfo
  35. {
  36. struct ue9Calibration calib;
  37. int convert;
  38. int maxlines;
  39. };
  40. struct options opt[] = {
  41. {'a', "address", "string", "host/address of UE9 (192.168.1.209)"},
  42. {'n', "numchannels", "n", "sample the first N ADC channels (2)"},
  43. {'N', "nerdjack", NULL, "Use NerdJack device instead"},
  44. {'d', "detect", NULL, "Detect NerdJack IP address"},
  45. {'p', "precision", "0-3",
  46. "Set precision on NerdJack (0 - max range, 1 - max precision)"},
  47. {'C', "channels", "a,b,c", "sample channels a, b, and c"},
  48. {'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
  49. {'o', "oneshot", NULL, "don't retry in case of errors"},
  50. {'f', "forceretry", NULL, "retry no matter what happens"},
  51. {'c', "convert", NULL, "convert output to volts"},
  52. {'H', "converthex", NULL, "convert output to hex"},
  53. {'m', "showmem", NULL, "output memory stats with data (NJ only)"},
  54. {'l', "lines", "num", "if set, output this many lines and quit"},
  55. {'h', "help", NULL, "this help"},
  56. {'v', "verbose", NULL, "be verbose"},
  57. {'V', "version", NULL, "show version number and exit"},
  58. {0, NULL, NULL, NULL}
  59. };
  60. int doStream (const char *address, uint8_t scanconfig, uint16_t scaninterval,
  61. int *channel_list, int channel_count, int convert,
  62. int maxlines);
  63. int nerdDoStream (const char *address, int *channel_list, int channel_count,
  64. int precision, unsigned long period, int convert, int lines,
  65. int showmem);
  66. int data_callback (int channels, uint16_t * data, void *context);
  67. int columns_left = 0;
  68. void
  69. handle_sig (int sig)
  70. {
  71. while (columns_left--)
  72. {
  73. printf (" 0");
  74. }
  75. fflush (stdout);
  76. exit (0);
  77. }
  78. int
  79. main (int argc, char *argv[])
  80. {
  81. int optind;
  82. char *optarg, *endp;
  83. char c;
  84. int tmp, i;
  85. FILE *help = stderr;
  86. char *address = strdup (DEFAULT_HOST);
  87. double desired_rate = 8000.0;
  88. int lines = 0;
  89. double actual_rate;
  90. int oneshot = 0;
  91. int forceretry = 0;
  92. int convert = CONVERT_DEC;
  93. int showmem = 0;
  94. uint8_t scanconfig;
  95. uint16_t scaninterval;
  96. #if UE9_CHANNELS > NERDJACK_CHANNELS
  97. int channel_list[UE9_CHANNELS];
  98. #else
  99. int channel_list[NERDJACK_CHANNELS];
  100. #endif
  101. int channel_count = 0;
  102. int nerdjack = 0;
  103. int detect = 0;
  104. int precision = 0;
  105. unsigned long period = NERDJACK_CLOCK_RATE / desired_rate;
  106. /* Parse arguments */
  107. opt_init (&optind);
  108. while ((c = opt_parse (argc, argv, &optind, &optarg, opt)) != 0)
  109. {
  110. switch (c)
  111. {
  112. case 'a':
  113. free (address);
  114. address = strdup (optarg);
  115. break;
  116. case 'n':
  117. channel_count = 0;
  118. tmp = strtol (optarg, &endp, 0);
  119. if (*endp || tmp < 1 || tmp > UE9_CHANNELS)
  120. {
  121. info ("bad number of channels: %s\n", optarg);
  122. goto printhelp;
  123. }
  124. for (i = 0; i < tmp; i++)
  125. channel_list[channel_count++] = i;
  126. break;
  127. case 'C':
  128. channel_count = 0;
  129. do
  130. {
  131. tmp = strtol (optarg, &endp, 0);
  132. if (*endp != '\0' && *endp != ',')
  133. {
  134. info ("bad channel number: %s\n", optarg);
  135. goto printhelp;
  136. }
  137. //We do not want to overflow channel_list, so we need the check here
  138. //The rest of the sanity checking can come later after we know
  139. //whether this is a
  140. //LabJack or a NerdJack
  141. #if UE9_CHANNELS > NERDJACK_CHANNELS
  142. if (channel_count >= UE9_CHANNELS)
  143. {
  144. #else
  145. if (channel_count >= NERDJACK_CHANNELS)
  146. {
  147. #endif
  148. info ("error: too many channels specified\n");
  149. goto printhelp;
  150. }
  151. channel_list[channel_count++] = tmp;
  152. optarg = endp + 1;
  153. }
  154. while (*endp);
  155. break;
  156. case 'r':
  157. desired_rate = strtod (optarg, &endp);
  158. if (*endp || desired_rate <= 0)
  159. {
  160. info ("bad rate: %s\n", optarg);
  161. goto printhelp;
  162. }
  163. break;
  164. case 'l':
  165. lines = strtol (optarg, &endp, 0);
  166. if (*endp || lines <= 0)
  167. {
  168. info ("bad number of lines: %s\n", optarg);
  169. goto printhelp;
  170. }
  171. break;
  172. case 'p':
  173. tmp = strtol (optarg, &endp, 0);
  174. if (tmp <= 3 && tmp >= 0)
  175. {
  176. precision = tmp;
  177. }
  178. else
  179. {
  180. info ("Bad argument to p: %s\n", optarg);
  181. goto printhelp;
  182. }
  183. break;
  184. case 'N':
  185. nerdjack++;
  186. break;
  187. case 'd':
  188. detect++;
  189. break;
  190. case 'o':
  191. oneshot++;
  192. break;
  193. case 'f':
  194. forceretry++;
  195. break;
  196. case 'c':
  197. if (convert != 0)
  198. {
  199. info ("specify only one conversion type\n");
  200. goto printhelp;
  201. }
  202. convert = CONVERT_VOLTS;
  203. break;
  204. case 'H':
  205. if (convert != 0)
  206. {
  207. info ("specify only one conversion type\n");
  208. goto printhelp;
  209. }
  210. convert = CONVERT_HEX;
  211. break;
  212. case 'm':
  213. showmem++;
  214. case 'v':
  215. verb_count++;
  216. break;
  217. case 'V':
  218. printf ("etherstream " VERSION "\n");
  219. printf ("Written by Jim Paris <jim@jtan.com>\n");
  220. printf ("and Zachary Clifford <zacharyc@mit.edu>\n");
  221. printf ("This program comes with no warranty and is "
  222. "provided under the GPLv2.\n");
  223. return 0;
  224. break;
  225. case 'h':
  226. help = stdout;
  227. default:
  228. printhelp:
  229. fprintf (help, "Usage: %s [options]\n", *argv);
  230. opt_help (opt, help);
  231. fprintf (help, "Read data from the specified Labjack UE9"
  232. " via Ethernet. See README for details.\n");
  233. return (help == stdout) ? 0 : 1;
  234. }
  235. }
  236. doneparse:
  237. if (nerdjack)
  238. {
  239. if (channel_count > NERDJACK_CHANNELS)
  240. {
  241. info ("Too many channels for NerdJack\n");
  242. goto printhelp;
  243. }
  244. for (i = 0; i < channel_count; i++)
  245. {
  246. if (channel_list[i] >= NERDJACK_CHANNELS)
  247. {
  248. info ("Channel is out of NerdJack range: %d\n",
  249. channel_list[i]);
  250. goto printhelp;
  251. }
  252. }
  253. }
  254. else
  255. {
  256. if (channel_count > UE9_CHANNELS)
  257. {
  258. info ("Too many channels for LabJack\n");
  259. goto printhelp;
  260. }
  261. for (i = 0; i < channel_count; i++)
  262. {
  263. if (channel_list[i] >= UE9_CHANNELS)
  264. {
  265. info ("Channel is out of LabJack range: %d\n", channel_list[i]);
  266. goto printhelp;
  267. }
  268. }
  269. }
  270. if (optind < argc)
  271. {
  272. info ("error: too many arguments (%s)\n\n", argv[optind]);
  273. goto printhelp;
  274. }
  275. if (forceretry && oneshot)
  276. {
  277. info ("forceretry and oneshot options are mutually exclusive\n");
  278. goto printhelp;
  279. }
  280. /* Two channels if none specified */
  281. if (channel_count == 0)
  282. {
  283. channel_list[channel_count++] = 0;
  284. channel_list[channel_count++] = 1;
  285. }
  286. if (verb_count)
  287. {
  288. info ("Scanning channels:");
  289. for (i = 0; i < channel_count; i++)
  290. info (" AIN%d", channel_list[i]);
  291. info ("\n");
  292. }
  293. /* Figure out actual rate. */
  294. if (nerdjack)
  295. {
  296. if (nerdjack_choose_scan (desired_rate, &actual_rate, &period) < 0)
  297. {
  298. info ("error: can't achieve requested scan rate (%lf Hz)\n",
  299. desired_rate);
  300. }
  301. }
  302. else
  303. {
  304. if (ue9_choose_scan (desired_rate, &actual_rate,
  305. &scanconfig, &scaninterval) < 0)
  306. {
  307. info ("error: can't achieve requested scan rate (%lf Hz)\n",
  308. desired_rate);
  309. }
  310. }
  311. if ((desired_rate != actual_rate) || verb_count)
  312. {
  313. info ("Actual scanrate is %lf Hz\n", actual_rate);
  314. info ("Period is %ld\n", period);
  315. }
  316. if (verb_count && lines)
  317. {
  318. info ("Stopping capture after %d lines\n", lines);
  319. }
  320. signal (SIGINT, handle_sig);
  321. signal (SIGTERM, handle_sig);
  322. if (detect)
  323. {
  324. info ("Autodetecting NerdJack address\n");
  325. free (address);
  326. if (nerdjack_detect (address) < 0)
  327. {
  328. info ("Error with autodetection\n");
  329. }
  330. else
  331. {
  332. info ("Found NerdJack at address: %s\n", address);
  333. }
  334. }
  335. for (;;)
  336. {
  337. int ret;
  338. if (nerdjack)
  339. {
  340. ret =
  341. nerdDoStream (address, channel_list, channel_count, precision,
  342. period, convert, lines, showmem);
  343. verb ("nerdDoStream returned %d\n", ret);
  344. }
  345. else
  346. {
  347. ret = doStream (address, scanconfig, scaninterval,
  348. channel_list, channel_count, convert, lines);
  349. verb ("doStream returned %d\n", ret);
  350. }
  351. if (oneshot)
  352. break;
  353. if (ret == 0)
  354. break;
  355. if (ret == -ENOTCONN && !nerdjack)
  356. {
  357. info ("Could not connect LabJack...Trying NerdJack\n");
  358. nerdjack = 1;
  359. goto doneparse;
  360. }
  361. if (ret == -ENOTCONN && !forceretry)
  362. {
  363. info ("Initial connection failed, giving up\n");
  364. break;
  365. }
  366. if (ret == -EAGAIN || ret == -ENOTCONN)
  367. {
  368. /* Some transient error. Wait a tiny bit, then retry */
  369. info ("Retrying in 5 secs.\n");
  370. sleep (5);
  371. }
  372. else
  373. {
  374. info ("Retrying now.\n");
  375. }
  376. }
  377. debug ("Done loop\n");
  378. return 0;
  379. }
  380. int
  381. nerdDoStream (const char *address, int *channel_list, int channel_count,
  382. int precision, unsigned long period, int convert, int lines,
  383. int showmem)
  384. {
  385. int retval = -EAGAIN;
  386. int fd_data;
  387. static int first_call = 1;
  388. static int started = 0;
  389. static int wasreset = 0;
  390. getPacket command;
  391. static unsigned short currentcount = 0;
  392. //If this is the first time, set up acquisition
  393. //Otherwise try to resume the previous one
  394. if (started == 0)
  395. {
  396. if (nerd_generate_command
  397. (&command, channel_list, channel_count, precision, period) < 0)
  398. {
  399. info ("Failed to create configuration command\n");
  400. goto out;
  401. }
  402. if (nerd_send_command (address, "STOP", 4) < 0)
  403. {
  404. if (first_call)
  405. retval = -ENOTCONN;
  406. info ("Failed to send STOP command\n");
  407. goto out;
  408. }
  409. //We've tried communicating, so this is not the first call anymore
  410. first_call = 0;
  411. if (nerd_send_command (address, &command, sizeof (command)) < 0)
  412. {
  413. info ("Failed to send GET command\n");
  414. goto out;
  415. }
  416. } else {
  417. //If we had a transmission in progress, send a command to resume from there
  418. char cmdbuf[10];
  419. sprintf (cmdbuf, "SETC%05hd", currentcount);
  420. retval = nerd_send_command (address, cmdbuf, strlen (cmdbuf));
  421. if(retval == -4) {
  422. info("NerdJack was reset\n");
  423. //Assume we have not started yet, reset on this side.
  424. //If this routine is retried, start over
  425. printf("# NerdJack was reset here\n");
  426. currentcount = 0;
  427. started = 0;
  428. wasreset = 1;
  429. goto out;
  430. } else if(retval < 0) {
  431. info ("Failed to send SETC command\n");
  432. goto out;
  433. }
  434. }
  435. //The transmission has begun
  436. started = 1;
  437. /* Open connection */
  438. fd_data = nerd_open (address, NERDJACK_DATA_PORT);
  439. if (fd_data < 0)
  440. {
  441. info ("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
  442. goto out;
  443. }
  444. retval = nerd_data_stream
  445. (fd_data, channel_count, channel_list, precision, convert, lines,
  446. showmem, &currentcount, period, wasreset);
  447. wasreset = 0;
  448. if(retval == -3)
  449. {
  450. retval = 0;
  451. }
  452. if(retval < 0)
  453. {
  454. info ("Failed to open data stream\n");
  455. goto out1;
  456. }
  457. info ("Stream finished\n");
  458. retval = 0;
  459. out1:
  460. nerd_close_conn (fd_data);
  461. out:
  462. return retval;
  463. }
  464. int
  465. doStream (const char *address, uint8_t scanconfig, uint16_t scaninterval,
  466. int *channel_list, int channel_count, int convert, int lines)
  467. {
  468. int retval = -EAGAIN;
  469. int fd_cmd, fd_data;
  470. int ret;
  471. static int first_call = 1;
  472. struct callbackInfo ci = {
  473. .convert = convert,
  474. .maxlines = lines,
  475. };
  476. /* Open command connection. If this fails, and this is the
  477. first attempt, return a different error code so we give up. */
  478. fd_cmd = ue9_open (address, UE9_COMMAND_PORT);
  479. if (fd_cmd < 0)
  480. {
  481. info ("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
  482. if (first_call)
  483. retval = -ENOTCONN;
  484. goto out;
  485. }
  486. first_call = 0;
  487. /* Make sure nothing is left over from a previous stream */
  488. if (ue9_stream_stop (fd_cmd) == 0)
  489. verb ("Stopped previous stream.\n");
  490. ue9_buffer_flush (fd_cmd);
  491. /* Open data connection */
  492. fd_data = ue9_open (address, UE9_DATA_PORT);
  493. if (fd_data < 0)
  494. {
  495. info ("Connect failed: %s:%d\n", address, UE9_DATA_PORT);
  496. goto out1;
  497. }
  498. /* Get calibration */
  499. if (ue9_get_calibration (fd_cmd, &ci.calib) < 0)
  500. {
  501. info ("Failed to get device calibration\n");
  502. goto out2;
  503. }
  504. /* Set stream configuration */
  505. if (ue9_streamconfig_simple (fd_cmd, channel_list, channel_count,
  506. scanconfig, scaninterval,
  507. UE9_BIPOLAR_GAIN1) < 0)
  508. {
  509. info ("Failed to set stream configuration\n");
  510. goto out2;
  511. }
  512. /* Start stream */
  513. if (ue9_stream_start (fd_cmd) < 0)
  514. {
  515. info ("Failed to start stream\n");
  516. goto out2;
  517. }
  518. /* Stream data */
  519. ret = ue9_stream_data (fd_data, channel_count, data_callback, (void *) &ci);
  520. if (ret < 0)
  521. {
  522. info ("Data stream failed with error %d\n", ret);
  523. goto out3;
  524. }
  525. info ("Stream finished\n");
  526. retval = 0;
  527. out3:
  528. /* Stop stream and clean up */
  529. ue9_stream_stop (fd_cmd);
  530. ue9_buffer_flush (fd_cmd);
  531. out2:
  532. ue9_close (fd_data);
  533. out1:
  534. ue9_close (fd_cmd);
  535. out:
  536. return retval;
  537. }
  538. int
  539. data_callback (int channels, uint16_t * data, void *context)
  540. {
  541. int i;
  542. struct callbackInfo *ci = (struct callbackInfo *) context;
  543. static int lines = 0;
  544. columns_left = channels;
  545. for (i = 0; i < channels; i++)
  546. {
  547. switch (ci->convert)
  548. {
  549. case CONVERT_VOLTS:
  550. if (printf
  551. ("%lf",
  552. ue9_binary_to_analog (&ci->calib, UE9_BIPOLAR_GAIN1, 12,
  553. data[i])) < 0)
  554. goto bad;
  555. break;
  556. case CONVERT_HEX:
  557. if (printf ("%04X", data[i]) < 0)
  558. goto bad;
  559. break;
  560. default:
  561. case CONVERT_DEC:
  562. if (printf ("%d", data[i]) < 0)
  563. goto bad;
  564. break;
  565. }
  566. columns_left--;
  567. if (i < (channels - 1))
  568. {
  569. if (ci->convert != CONVERT_HEX && putchar (' ') < 0)
  570. goto bad;
  571. }
  572. else
  573. {
  574. if (putchar ('\n') < 0)
  575. goto bad;
  576. lines++;
  577. if (ci->maxlines && lines >= ci->maxlines)
  578. return -1;
  579. }
  580. }
  581. return 0;
  582. bad:
  583. info ("Output error (disk full?)\n");
  584. return -3;
  585. }