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.
 
 
 

610 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. #include <errno.h>
  10. #include <stdint.h>
  11. #include <stdlib.h>
  12. #include <unistd.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <sys/types.h>
  16. #include <math.h>
  17. #include "netutil.h"
  18. #include "compat.h"
  19. #include "debug.h"
  20. #include "nerdjack.h"
  21. #include "util.h"
  22. #include "netutil.h"
  23. #include "ethstream.h"
  24. #define NERDJACK_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
  25. //Struct holding information about how channels should be reordered for output
  26. typedef struct
  27. {
  28. int numCopies;
  29. int *destlist;
  30. } deststruct;
  31. #define NERD_HEADER_SIZE 8
  32. typedef struct __attribute__ ((__packed__))
  33. {
  34. unsigned char headerone;
  35. unsigned char headertwo;
  36. unsigned short packetNumber;
  37. unsigned short adcused;
  38. unsigned short packetsready;
  39. signed short data[NERDJACK_NUM_SAMPLES];
  40. } dataPacket;
  41. /* Choose the best ScanConfig and ScanInterval parameters for the
  42. desired scanrate. Returns -1 if no valid config found */
  43. int
  44. nerdjack_choose_scan (double desired_rate, double *actual_rate,
  45. unsigned long *period)
  46. {
  47. //The ffffe is because of a silicon bug. The last bit is unusable in all
  48. //devices so far. It is worked around on the chip, but giving it exactly
  49. //0xfffff would cause the workaround code to roll over.
  50. *period = floor ((double) NERDJACK_CLOCK_RATE / desired_rate);
  51. if (*period > 0x0ffffe)
  52. {
  53. info ("Cannot sample that slowly\n");
  54. *actual_rate = (double) NERDJACK_CLOCK_RATE / (double) 0x0ffffe;
  55. *period = 0x0ffffe;
  56. return -1;
  57. }
  58. //Period holds the period register for the NerdJack, so it needs to be right
  59. *actual_rate = (double) NERDJACK_CLOCK_RATE / (double) *period;
  60. if (*actual_rate != desired_rate)
  61. {
  62. return -1;
  63. }
  64. return 0;
  65. }
  66. /* Perform autodetection. Returns 0 on success, -1 on error
  67. * Sets ipAddress to the detected address
  68. */
  69. int
  70. nerdjack_detect (char *ipAddress)
  71. {
  72. int32_t sock, receivesock;
  73. struct sockaddr_in sa, receiveaddr, sFromAddr;
  74. int bytes_sent, buffer_length;
  75. char buffer[200];
  76. char incomingData[10];
  77. unsigned int lFromLen;
  78. sprintf (buffer, "TEST");
  79. buffer_length = strlen (buffer) + 1;
  80. net_init ();
  81. sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  82. receivesock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  83. /* Set nonblocking */
  84. if (soblock (sock, 0) < 0)
  85. {
  86. verb ("can't set nonblocking\n");
  87. return -1;
  88. }
  89. /* Set nonblocking */
  90. if (soblock (receivesock, 0) < 0)
  91. {
  92. verb ("can't set nonblocking\n");
  93. return -1;
  94. }
  95. int opt = 1;
  96. setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (void *) &opt, sizeof (int));
  97. if ((-1 == sock) || (-1 == receivesock)) /* if socket failed to initialize, exit */
  98. {
  99. verb ("Error Creating Socket\n");
  100. return -1;
  101. }
  102. //Setup family for both sockets
  103. sa.sin_family = PF_INET;
  104. receiveaddr.sin_family = PF_INET;
  105. //Setup ports to send on DATA and receive on RECEIVE
  106. receiveaddr.sin_port = htons (NERDJACK_UDP_RECEIVE_PORT);
  107. sa.sin_port = htons (NERDJACK_DATA_PORT);
  108. //Receive from any IP address, Will send to broadcast
  109. receiveaddr.sin_addr.s_addr = INADDR_ANY;
  110. sa.sin_addr.s_addr = INADDR_BROADCAST;
  111. bind (receivesock, (struct sockaddr *) &receiveaddr,
  112. sizeof (struct sockaddr_in));
  113. bytes_sent =
  114. sendto (sock, buffer, buffer_length, 0, (struct sockaddr *) &sa,
  115. sizeof (struct sockaddr_in));
  116. if (bytes_sent < 0)
  117. {
  118. info ("Error sending packet: %s\n", strerror (errno));
  119. return -1;
  120. }
  121. lFromLen = sizeof (sFromAddr);
  122. if (0 >
  123. recvfrom_timeout (receivesock, incomingData, sizeof (incomingData), 0,
  124. (struct sockaddr *) &sFromAddr, &lFromLen,
  125. &(struct timeval)
  126. {
  127. .tv_sec = NERDJACK_TIMEOUT}))
  128. {
  129. return -1;
  130. }
  131. ipAddress = malloc (INET_ADDRSTRLEN);
  132. //It isn't ipv6 friendly, but inet_ntop isn't on Windows...
  133. strcpy (ipAddress, inet_ntoa (sFromAddr.sin_addr));
  134. close (sock); /* close the socket */
  135. close (receivesock);
  136. return 0;
  137. }
  138. /* Send the given command to address. The command should be something
  139. * of the specified length. This expects the NerdJack to reply with OK
  140. * or NO
  141. */
  142. int
  143. nerd_send_command (const char *address, void *command, int length)
  144. {
  145. int ret, fd_command;
  146. char buf[3];
  147. fd_command = nerd_open (address, NERDJACK_COMMAND_PORT);
  148. if (fd_command < 0)
  149. {
  150. info ("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT);
  151. return -2;
  152. }
  153. /* Send request */
  154. ret = send_all_timeout (fd_command, command, length, 0, &(struct timeval)
  155. {
  156. .tv_sec = NERDJACK_TIMEOUT});
  157. if (ret < 0 || ret != length)
  158. {
  159. verb ("short send %d\n", (int) ret);
  160. return -1;
  161. }
  162. ret = recv_all_timeout (fd_command, buf, 3, 0, &(struct timeval)
  163. {
  164. .tv_sec = NERDJACK_TIMEOUT});
  165. nerd_close_conn (fd_command);
  166. if (ret < 0 || ret != 3)
  167. {
  168. verb ("Error receiving OK for command\n");
  169. return -1;
  170. }
  171. if (0 != strcmp ("OK", buf))
  172. {
  173. verb ("Did not receive OK. Received %s\n", buf);
  174. return -4;
  175. }
  176. return 0;
  177. }
  178. /*
  179. * Initialize the channel structure to distill how data should be displayed
  180. */
  181. static void
  182. nerd_init_channels (deststruct * destination, int numChannels,
  183. int numChannelsSampled, int *channel_list)
  184. {
  185. int channelprocessing = 0;
  186. int currentalign = 0; //Index into sampled channels
  187. int i;
  188. int tempdestlist[NERDJACK_CHANNELS];
  189. //Clear out destination stuff
  190. for (i = 0; i < numChannelsSampled; i++)
  191. {
  192. destination[i].numCopies = 0;
  193. }
  194. for (channelprocessing = 0; channelprocessing < numChannelsSampled;
  195. channelprocessing++)
  196. {
  197. //Find out how many copies of each channel so we malloc the right things
  198. currentalign = 0;
  199. for (i = 0; i < numChannels; i++)
  200. {
  201. if (channelprocessing == channel_list[i])
  202. {
  203. tempdestlist[currentalign] = i;
  204. currentalign++;
  205. }
  206. }
  207. //If this channel is wanted, set it up.
  208. if (currentalign > 0)
  209. {
  210. destination[channelprocessing].numCopies = currentalign;
  211. destination[channelprocessing].destlist =
  212. malloc (destination[channelprocessing].numCopies * sizeof (int));
  213. memcpy (destination[channelprocessing].destlist, tempdestlist,
  214. destination[channelprocessing].numCopies * sizeof (int));
  215. }
  216. }
  217. return;
  218. }
  219. int
  220. nerd_data_stream (int data_fd, int numChannels, int *channel_list,
  221. int precision, int convert, int lines, int showmem,
  222. unsigned short *currentcount, unsigned int period,
  223. int wasreset)
  224. {
  225. //Variables that should persist across retries
  226. static dataPacket buf;
  227. static int linesleft = 0;
  228. static int linesdumped = 0;
  229. int index = 0;
  230. int charsprocessed = 0;
  231. int alignment = 0;
  232. signed short datapoint = 0;
  233. unsigned short dataline[NERDJACK_CHANNELS];
  234. long double voltline[NERDJACK_CHANNELS];
  235. int i;
  236. //unsigned long memused = 0;
  237. unsigned short packetsready = 0;
  238. unsigned short adcused = 0;
  239. unsigned short tempshort = 0;
  240. int charsread = 0;
  241. int numgroups = 0;
  242. long double volts;
  243. //The timeout should be the expected time plus two seconds
  244. //This permits slower speeds to work properly
  245. unsigned int expectedtimeout =
  246. (period * NERDJACK_NUM_SAMPLES / NERDJACK_CLOCK_RATE) + 2;
  247. //Check to see if we're trying to resume
  248. //Don't blow away linesleft in that case
  249. if (lines != 0 && linesleft == 0)
  250. {
  251. linesleft = lines;
  252. }
  253. if(wasreset) {
  254. linesdumped = 0;
  255. }
  256. int numChannelsSampled = channel_list[0] + 1;
  257. //The number sampled will be the highest channel requested plus 1
  258. //(i.e. channel 0 requested means 1 sampled)
  259. for (i = 0; i < numChannels; i++)
  260. {
  261. if (channel_list[i] + 1 > numChannelsSampled)
  262. numChannelsSampled = channel_list[i] + 1;
  263. }
  264. deststruct destination[numChannelsSampled];
  265. nerd_init_channels (destination, numChannels, numChannelsSampled,
  266. channel_list);
  267. //If this is the first time called, warn the user if we're too fast
  268. if (linesdumped == 0)
  269. {
  270. if (period < (numChannelsSampled * 100 + 300))
  271. {
  272. info ("You are sampling close to the limit of NerdJack\n");
  273. info ("Sample fewer channels or sample slower\n");
  274. }
  275. }
  276. //Now destination structure array is set as well as numDuplicates.
  277. int numGroups = NERDJACK_NUM_SAMPLES / numChannelsSampled;
  278. //Loop forever to grab data
  279. while ((charsread =
  280. recv_all_timeout (data_fd, &buf, NERDJACK_PACKET_SIZE, 0,
  281. &(struct timeval)
  282. {
  283. .tv_sec = expectedtimeout})))
  284. {
  285. if (charsread != NERDJACK_PACKET_SIZE)
  286. {
  287. //There was a problem getting data. Probably a closed
  288. //connection.
  289. info ("Packet timed out or was too short\n");
  290. return -2;
  291. }
  292. //First check the header info
  293. if (buf.headerone != 0xF0 || buf.headertwo != 0xAA)
  294. {
  295. info ("No Header info\n");
  296. return -1;
  297. }
  298. //Check counter info to make sure not out of order
  299. tempshort = ntohs (buf.packetNumber);
  300. if (tempshort != *currentcount)
  301. {
  302. info ("Count wrong. Expected %hd but got %hd\n", *currentcount,
  303. tempshort);
  304. return -1;
  305. }
  306. //Increment number of packets received
  307. *currentcount = *currentcount + 1;
  308. //Process the rest of the header and update the index value to be pointing after it
  309. charsprocessed = NERD_HEADER_SIZE;
  310. //memused = ntohl (buf.lwipmemoryused);
  311. adcused = ntohs (buf.adcused);
  312. packetsready = ntohs (buf.packetsready);
  313. alignment = 0;
  314. numgroups = 0;
  315. //if(memused) {
  316. // printf("#Packet lost\n");
  317. // continue;
  318. //}
  319. if (showmem)
  320. {
  321. printf ("%hd %hd\n", adcused, packetsready);
  322. continue;
  323. }
  324. index = 0;
  325. //While there is still more data in the packet, process it
  326. //use the destination structure to load the line before printing
  327. while (charsread > charsprocessed)
  328. {
  329. datapoint = ntohs (buf.data[index]);
  330. if (destination[alignment].numCopies != 0)
  331. {
  332. switch (convert)
  333. {
  334. case CONVERT_VOLTS:
  335. if (alignment <= 5)
  336. {
  337. volts =
  338. (long double) (datapoint / 32767.0) *
  339. ((precision & 0x01) ? 5.0 : 10.0);
  340. }
  341. else
  342. {
  343. volts =
  344. (long double) (datapoint / 32767.0) *
  345. ((precision & 0x02) ? 5.0 : 10.0);
  346. }
  347. for (i = 0; i < destination[alignment].numCopies; i++)
  348. {
  349. voltline[destination[alignment].destlist[i]] = volts;
  350. }
  351. break;
  352. default:
  353. case CONVERT_HEX:
  354. case CONVERT_DEC:
  355. for (i = 0; i < destination[alignment].numCopies; i++)
  356. {
  357. dataline[destination[alignment].destlist[i]] =
  358. (unsigned short) (datapoint - INT16_MIN);
  359. }
  360. break;
  361. }
  362. }
  363. //Each point is two bytes, so increment index and total bytes read
  364. charsprocessed++;
  365. charsprocessed++;
  366. index++;
  367. alignment++;
  368. //Since channel data is packed, we need to know when to insert a newline
  369. if (alignment == numChannelsSampled)
  370. {
  371. //We want to dump the first line because it's usually spurious
  372. if (linesdumped != 0)
  373. {
  374. switch (convert)
  375. {
  376. case CONVERT_VOLTS:
  377. for (i = 0; i < numChannels; i++)
  378. {
  379. if (printf ("%Lf ", voltline[i]) < 0)
  380. goto bad;
  381. }
  382. break;
  383. case CONVERT_HEX:
  384. for (i = 0; i < numChannels; i++)
  385. {
  386. if (printf ("%04hX", dataline[i]) < 0)
  387. goto bad;
  388. }
  389. break;
  390. default:
  391. case CONVERT_DEC:
  392. for (i = 0; i < numChannels; i++)
  393. {
  394. if (printf ("%hu ", dataline[i]) < 0)
  395. goto bad;
  396. }
  397. break;
  398. }
  399. if (printf ("\n") < 0)
  400. goto bad;
  401. }
  402. else
  403. {
  404. linesdumped = linesdumped + 1;
  405. if (lines != 0)
  406. {
  407. linesleft++;
  408. }
  409. }
  410. alignment = 0;
  411. numgroups++;
  412. if (lines != 0)
  413. {
  414. linesleft--;
  415. if (linesleft == 0)
  416. {
  417. return 0;
  418. }
  419. }
  420. //If numgroups so far is equal to the numGroups in a packet, this packet is done
  421. if (numgroups == numGroups)
  422. {
  423. break;
  424. }
  425. }
  426. }
  427. index = 0;
  428. charsprocessed = 0;
  429. }
  430. return 0;
  431. bad:
  432. info ("Output error (disk full?)\n");
  433. return -3;
  434. }
  435. /* Open a connection to the NerdJack */
  436. int
  437. nerd_open (const char *address, int port)
  438. {
  439. struct hostent *he;
  440. net_init ();
  441. int32_t i32SocketFD = socket (PF_INET, SOCK_STREAM, 0);
  442. if (-1 == i32SocketFD)
  443. {
  444. verb ("cannot create socket");
  445. return -1;
  446. }
  447. /* Set nonblocking */
  448. if (soblock (i32SocketFD, 0) < 0)
  449. {
  450. verb ("can't set nonblocking\n");
  451. return -1;
  452. }
  453. struct sockaddr_in stSockAddr;
  454. memset (&stSockAddr, 0, sizeof (stSockAddr));
  455. stSockAddr.sin_family = AF_INET;
  456. stSockAddr.sin_port = htons (port);
  457. he = gethostbyname (address);
  458. if (he == NULL)
  459. {
  460. verb ("gethostbyname(\"%s\") failed\n", address);
  461. return -1;
  462. }
  463. stSockAddr.sin_addr = *((struct in_addr *) he->h_addr);
  464. debug ("Resolved %s -> %s\n", address, inet_ntoa (stSockAddr.sin_addr));
  465. /* Connect */
  466. if (connect_timeout
  467. (i32SocketFD, (struct sockaddr *) &stSockAddr, sizeof (stSockAddr),
  468. &(struct timeval)
  469. {
  470. .tv_sec = NERDJACK_TIMEOUT}) < 0)
  471. {
  472. verb ("connection to %s:%d failed: %s\n",
  473. inet_ntoa (stSockAddr.sin_addr), port, compat_strerror (errno));
  474. return -1;
  475. }
  476. return i32SocketFD;
  477. }
  478. //Generate an appropriate sample initiation command
  479. int
  480. nerd_generate_command (getPacket * command, int *channel_list,
  481. int channel_count, int precision, unsigned long period)
  482. {
  483. short channelbit = 0;
  484. int i;
  485. int highestchannel = 0;
  486. for (i = 0; i < channel_count; i++)
  487. {
  488. if (channel_list[i] > highestchannel) {
  489. highestchannel = channel_list[i];
  490. }
  491. //channelbit = channelbit | (0x1 << channel_list[i]);
  492. }
  493. for( i = 0; i <= highestchannel; i++) {
  494. channelbit = channelbit | (0x01 << i);
  495. }
  496. command->word[0] = 'G';
  497. command->word[1] = 'E';
  498. command->word[2] = 'T';
  499. command->word[3] = 'D';
  500. command->channelbit = htons (channelbit);
  501. command->precision = precision;
  502. command->period = htonl (period);
  503. command->prescaler = 0;
  504. return 0;
  505. }
  506. int
  507. nerd_close_conn (int data_fd)
  508. {
  509. shutdown (data_fd, 2);
  510. close (data_fd);
  511. return 0;
  512. }