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.

dctest.c 7.0 KiB

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