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.
 
 
 
 

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