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.
 
 
 

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