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.
 
 
 
 

385 lines
8.5 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <errno.h>
  5. #include <unistd.h>
  6. #include <getopt.h>
  7. #include <stdint.h>
  8. #include <string.h>
  9. #include <syslog.h>
  10. #include <err.h>
  11. #include <linux/serial.h>
  12. #include <sys/signal.h>
  13. #include "serial-util.h"
  14. #include "gpib.h"
  15. #include "zoom.h"
  16. #include "math.h"
  17. #include <pthread.h>
  18. #include <ctype.h>
  19. #include "mt19937ar.h"
  20. #define info(x...) fprintf(stderr,x)
  21. static void dctest(int zoom, int gpib);
  22. int g_quit = 0;
  23. static void handle_sig(int sig) { g_quit = 1; }
  24. int main(int argc, char *argv[])
  25. {
  26. char *zoomdev=strdup("/dev/serial/by-id/usb-FTDI_"
  27. "FT232R_USB_UART_A6007wc5-if00-port0");
  28. char *gpibdev=strdup("/dev/serial/by-id/usb-Prologix_"
  29. "Prologix_GPIB-USB_Controller_PXQQY20G-if00-port0");
  30. int rate=500000;
  31. unsigned long seed = 1337;
  32. int getopt_index;
  33. int zoom, gpib;
  34. static struct option long_opts[] = {
  35. { "zoom-device", required_argument, NULL, 'Z' },
  36. { "gpib-device", required_argument, NULL, 'G' },
  37. { "rate", required_argument, NULL, 'r' },
  38. { "seed", required_argument, NULL, 's' },
  39. { "help", no_argument, NULL, 'h' },
  40. { 0, 0, 0, 0}
  41. };
  42. int help=0;
  43. char c;
  44. while ((c = getopt_long(argc, argv, "Z:G:r:s:h?",
  45. long_opts, &getopt_index)) != -1) {
  46. switch(c)
  47. {
  48. case 'Z':
  49. free(zoomdev);
  50. zoomdev = strdup(optarg);
  51. break;
  52. case 'G':
  53. free(gpibdev);
  54. gpibdev = strdup(optarg);
  55. break;
  56. case 'r':
  57. rate = atoi(optarg);
  58. if(rate == 0)
  59. errx(1, "invalid rate: %s", optarg);
  60. break;
  61. case 's':
  62. seed = atol(optarg);
  63. if(seed == 0)
  64. errx(1, "invalid seed: %s", optarg);
  65. break;
  66. case 'h':
  67. case '?':
  68. default:
  69. help = 1;
  70. break;
  71. }
  72. }
  73. if (help) {
  74. fprintf(stderr, "Zoom Nilm DC Test Tool\n");
  75. fprintf(stderr, "usage: %s [options]\n\n", *argv);
  76. fprintf(stderr, " -Z, --zoom-device %-9s "
  77. "zoom NILM serial port\n", "/dev/xxx");
  78. fprintf(stderr, " -G, --gpib-device %-9s "
  79. "GPIB serial port\n", "/dev/xxx");
  80. fprintf(stderr, " -r, --rate %-16d baud rate\n", rate);
  81. fprintf(stderr, " -s, --seed %-16ld random seed\n", seed);
  82. fprintf(stderr, " -h, --help this help\n");
  83. return 1;
  84. }
  85. signal(SIGINT, handle_sig);
  86. info("Initializing twister with seed %ld\n", seed);
  87. init_genrand(seed);
  88. /* open devices */
  89. info("Opening Zoom NILM device %s\n", zoomdev);
  90. if ((zoom = serial_open(zoomdev, rate)) == -1)
  91. err(1, "failed to open zoom device %s", zoomdev);
  92. info("Opening GPIB device %s\n", gpibdev);
  93. if ((gpib = serial_open(gpibdev, 9600)) == -1)
  94. err(1, "failed to open gpib device %s", gpibdev);
  95. /* do the dc test */
  96. dctest(zoom, gpib);
  97. close(zoom);
  98. close(gpib);
  99. return 0;
  100. }
  101. struct keithley_t {
  102. double desired;
  103. double actual;
  104. int stable;
  105. };
  106. struct threadinfo_t {
  107. int quit_flag;
  108. int fd;
  109. pthread_mutex_t mutex;
  110. float calibration;
  111. struct keithley_t k;
  112. };
  113. int process_adc_dac(const uint8_t *buf, struct threadinfo_t *ti)
  114. {
  115. uint16_t dac, tmp;
  116. int16_t adc;
  117. int overflow;
  118. double idesired, iactual;
  119. double calib;
  120. int stable;
  121. /* data OK? */
  122. if ((buf[0] & 0xC0) != 0) return 0;
  123. /* extract */
  124. overflow = (buf[0] & 0x10) ? 1 : 0;
  125. tmp = ((buf[0] & 0x0F) << 8) | buf[1];
  126. /* sign-extend ADC value */
  127. if (tmp & 0x0800)
  128. tmp |= 0xF000;
  129. else
  130. tmp &= ~0xF000;
  131. adc = (int16_t)tmp;
  132. dac = (buf[2] << 8) | buf[3];
  133. /* get locked data */
  134. pthread_mutex_lock(&ti->mutex);
  135. idesired = ti->k.desired;
  136. iactual = ti->k.actual;
  137. stable = ti->k.stable;
  138. calib = ti->calibration;
  139. pthread_mutex_unlock(&ti->mutex);
  140. /* write it out */
  141. printf("%d %.12f %.12f %.8f %5d %5d %d\n",
  142. stable,
  143. idesired,
  144. iactual,
  145. calib,
  146. dac,
  147. adc,
  148. overflow);
  149. return 1;
  150. }
  151. static int process_calibration(const uint8_t *buf, struct threadinfo_t *ti)
  152. {
  153. float f = *(float *)buf;
  154. pthread_mutex_lock(&ti->mutex);
  155. ti->calibration = f;
  156. pthread_mutex_unlock(&ti->mutex);
  157. info("New calibration value: %.8f\n", f);
  158. return 1;
  159. }
  160. static int process(const uint8_t *buf, int len, struct threadinfo_t *ti)
  161. {
  162. int n = 0;
  163. /* Process blocks */
  164. retry:
  165. for (; (n + 5) <= len; buf += 5, n += 5) {
  166. int ok = 0;
  167. switch (buf[0]) {
  168. case 0xA0:
  169. if (process_adc_dac(buf + 1, ti)) ok = 1;
  170. break;
  171. case 0xA1:
  172. if (process_calibration(buf + 1, ti)) ok = 1;
  173. break;
  174. default:
  175. break;
  176. }
  177. if (!ok) {
  178. /* badly formed data; eat one byte and retry */
  179. info("throwing away 0x%02x '%c'\n", buf[0],
  180. isprint(buf[0]) ? buf[0] : '.');
  181. buf++;
  182. n++;
  183. goto retry;
  184. }
  185. }
  186. return n;
  187. }
  188. static void *read_data(void *arg)
  189. {
  190. struct threadinfo_t *ti = (struct threadinfo_t *)arg;
  191. char buf[1024];
  192. int len;
  193. /* read data in a loop. Use saferead_timeout here so we can
  194. notice quit_flag before too long. */
  195. len = 0;
  196. while (!ti->quit_flag) {
  197. int processed, n;
  198. n = saferead_timeout(ti->fd,
  199. buf + len,
  200. sizeof(buf) - len,
  201. 1000);
  202. if (n < 0)
  203. err(1, "read");
  204. if (n == 0)
  205. continue;
  206. len += n;
  207. processed = process((uint8_t *) buf, len, ti);
  208. memmove(buf, buf + processed, len - processed);
  209. len -= processed;
  210. }
  211. info("read thread quitting\n");
  212. return NULL;
  213. }
  214. /* change keithley and update keithley_t structure with locking */
  215. static int keithley_change(int gpib, double desired, struct threadinfo_t *ti)
  216. {
  217. double actual;
  218. pthread_mutex_lock(&ti->mutex);
  219. ti->k.stable = 0;
  220. ti->k.desired = desired;
  221. pthread_mutex_unlock(&ti->mutex);
  222. if (keithley_current(gpib, desired) < 0)
  223. return -1;
  224. actual = keithley_read(gpib);
  225. if (isnan(actual))
  226. return -1;
  227. pthread_mutex_lock(&ti->mutex);
  228. ti->k.actual = actual;
  229. ti->k.stable = 1;
  230. pthread_mutex_unlock(&ti->mutex);
  231. return 0;
  232. }
  233. static double genrand(double min, double max)
  234. {
  235. double x;
  236. x = genrand_real1(); /* [0, 1] */
  237. x *= (max - min); /* [0, max-min] */
  238. x += min; /* [min, max] */
  239. return x;
  240. }
  241. static void dctest(int zoom, int gpib)
  242. {
  243. pthread_t thread;
  244. struct threadinfo_t ti;
  245. double tmp;
  246. int i;
  247. /* Do a calibration with Keithley off */
  248. info("Triggering calibration\n");
  249. zoomrun_trigger_calibrate(zoom);
  250. usleep(500000);
  251. /* Init Keithley */
  252. info("Initializing GPIB\n");
  253. if (gpib_init(gpib) < 0) { info("failed\n"); goto out1; }
  254. info("Initializing Keithley\n");
  255. if (gpib_addr(gpib, 24) < 0) { info("failed\n"); goto out1; }
  256. if (keithley_init(gpib) < 0) { info("failed\n"); goto out2; }
  257. if (keithley_current(gpib, 0) < 0) { info("failed\n"); goto out2; }
  258. if (isnan(keithley_read(gpib))) { info("failed\n"); goto out2; }
  259. /* Start the thread that reads and dumps data */
  260. info("Spawning thread\n");
  261. if (pthread_mutex_init(&ti.mutex, NULL) != 0) {
  262. info("failed\n");
  263. goto out2;
  264. }
  265. ti.calibration = 0.0;
  266. ti.k.desired = 0;
  267. ti.k.actual = 0;
  268. ti.k.stable = 0;
  269. ti.quit_flag = 0;
  270. ti.fd = zoom;
  271. drain_timeout(zoom, 0);
  272. if (pthread_create(&thread, NULL, read_data, &ti) != 0) {
  273. info("failed\n");
  274. goto out2;
  275. }
  276. /* Do another calibration now, and verify it works */
  277. info("Triggering calibration\n");
  278. pthread_mutex_lock(&ti.mutex);
  279. ti.calibration = 0.0;
  280. pthread_mutex_unlock(&ti.mutex);
  281. if (keithley_change(gpib, 0.0, &ti) < 0) { info("failed\n"); goto out3; }
  282. zoomrun_trigger_calibrate(zoom);
  283. for (i = 0; i < 100; i++) {
  284. usleep(50000);
  285. pthread_mutex_lock(&ti.mutex);
  286. tmp = ti.calibration;
  287. pthread_mutex_unlock(&ti.mutex);
  288. if (tmp != 0.0)
  289. break;
  290. }
  291. if (tmp == 0.0) {
  292. info("Invalid calibration data: is zoom NILM working?\n");
  293. goto out3;
  294. }
  295. info("Running\n");
  296. /* Change Keithley values */
  297. while (!g_quit) {
  298. #define DELAY 100000 /* after keithley settles, in us */
  299. #define STEPS 10 /* how many small steps to take */
  300. #define K_MIN -0.5 /* keithley min, amps */
  301. #define K_MAX 0.5 /* keithley max, amps */
  302. #define STEPSIZE 0.03 /* max size of small step, in amps */
  303. /* Choose any value within the Keithley range */
  304. double desired = genrand(K_MIN, K_MAX);
  305. info("Big step: %.12f\n", desired);
  306. if (keithley_change(gpib, desired, &ti) < 0) {
  307. info("error setting keithley\n");
  308. break;
  309. }
  310. usleep(DELAY);
  311. /* Choose a few more values nearby */
  312. for (i = 0; i < STEPS && !g_quit; i++) {
  313. desired += genrand(-STEPSIZE/2, STEPSIZE/2);
  314. if (desired < -1.0)
  315. desired = -1.0;
  316. if (desired > 1.0)
  317. desired = 1.0;
  318. if (keithley_change(gpib, desired, &ti) < 0) {
  319. info("error setting keithley\n");
  320. break;
  321. }
  322. usleep(DELAY);
  323. }
  324. /* one extra delay just to separate things a bit */
  325. usleep(DELAY);
  326. }
  327. info("main thread quitting\n");
  328. out3:
  329. ti.quit_flag = 1;
  330. pthread_join(thread, NULL);
  331. out2:
  332. keithley_off(gpib);
  333. out1:
  334. return;
  335. }