- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <unistd.h>
- #include <getopt.h>
- #include <stdint.h>
- #include <string.h>
- #include <syslog.h>
- #include <err.h>
- #include <linux/serial.h>
- #include <sys/signal.h>
- #include "serial-util.h"
- #include "gpib.h"
- #include "zoom.h"
- #include "math.h"
- #include <pthread.h>
- #include <ctype.h>
- #include "mt19937ar.h"
-
- #define info(x...) fprintf(stderr,x)
-
- static void dctest(int zoom, int gpib);
-
- int g_quit = 0;
- static void handle_sig(int sig) { g_quit = 1; }
-
- int main(int argc, char *argv[])
- {
- char *zoomdev=strdup("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6007wc5-if00-port0");
- char *gpibdev=strdup("/dev/serial/by-id/usb-Prologix_Prologix_GPIB-USB_Controller_PXQQY20G-if00-port0");
- int rate=500000;
- unsigned long seed = 1337;
- int getopt_index;
- int zoom, gpib;
-
- static struct option long_opts[] = {
- { "zoom-device", required_argument, NULL, 'Z' },
- { "gpib-device", required_argument, NULL, 'G' },
- { "rate", required_argument, NULL, 'r' },
- { "seed", required_argument, NULL, 's' },
- { "help", no_argument, NULL, 'h' },
- { 0, 0, 0, 0}
- };
- int help=0;
- char c;
-
- while ((c = getopt_long(argc, argv, "Z:G:r:s:h?",
- long_opts, &getopt_index)) != -1) {
- switch(c)
- {
- case 'Z':
- free(zoomdev);
- zoomdev = strdup(optarg);
- break;
- case 'G':
- free(gpibdev);
- gpibdev = strdup(optarg);
- break;
- case 'r':
- rate = atoi(optarg);
- if(rate == 0)
- errx(1, "invalid rate: %s", optarg);
- break;
- case 's':
- seed = atol(optarg);
- if(seed == 0)
- errx(1, "invalid seed: %s", optarg);
- break;
- case 'h':
- case '?':
- default:
- help = 1;
- break;
- }
- }
-
- if (help) {
- fprintf(stderr, "Zoom Nilm DC Test Tool\n");
- fprintf(stderr, "usage: %s [options]\n\n", *argv);
- fprintf(stderr, " -Z, --zoom-device %-9s zoom NILM serial port\n", "/dev/xxx");
- fprintf(stderr, " -G, --gpib-device %-9s GPIB serial port\n", "/dev/xxx");
- fprintf(stderr, " -r, --rate %-16d baud rate\n", rate);
- fprintf(stderr, " -s, --seed %-16ld random seed\n", seed);
- fprintf(stderr, " -h, --help this help\n");
- return 1;
- }
-
- signal(SIGINT, handle_sig);
-
- info("Initializing twister with seed %ld\n", seed);
- init_genrand(seed);
-
- /* open devices */
- info("Opening Zoom NILM device %s\n", zoomdev);
- if ((zoom = serial_open(zoomdev, rate)) == -1)
- err(1, "failed to open zoom device %s", zoomdev);
-
- info("Opening GPIB device %s\n", gpibdev);
- if ((gpib = serial_open(gpibdev, 9600)) == -1)
- err(1, "failed to open gpib device %s", gpibdev);
-
- /* do the dc test */
- dctest(zoom, gpib);
-
- close(zoom);
- close(gpib);
- return 0;
- }
-
- struct keithley_t {
- double desired;
- double actual;
- int stable;
- };
-
- struct threadinfo_t {
- int quit_flag;
- int fd;
- pthread_mutex_t mutex;
- float calibration;
- struct keithley_t k;
- };
-
- int process_adc_dac(const uint8_t *buf, struct threadinfo_t *ti)
- {
- uint16_t dac, tmp;
- int16_t adc;
- int overflow;
- double idesired, iactual;
- double calib;
- int stable;
-
- /* data OK? */
- if ((buf[0] & 0xC0) != 0) return 0;
-
- /* extract */
- overflow = (buf[0] & 0x10) ? 1 : 0;
- tmp = ((buf[0] & 0x0F) << 8) | buf[1];
-
- /* sign-extend ADC value */
- if (tmp & 0x0800)
- tmp |= 0xF000;
- else
- tmp &= ~0xF000;
- adc = (int16_t)tmp;
-
- dac = (buf[2] << 8) | buf[3];
-
- /* get locked data */
- pthread_mutex_lock(&ti->mutex);
- idesired = ti->k.desired;
- iactual = ti->k.actual;
- stable = ti->k.stable;
- calib = ti->calibration;
- pthread_mutex_unlock(&ti->mutex);
-
- /* write it out */
- printf("%d %.12f %.12f %.8f %5d %5d %d\n",
- stable,
- idesired,
- iactual,
- calib,
- dac,
- adc,
- overflow);
-
- return 1;
- }
-
- static int process_calibration(const uint8_t *buf, struct threadinfo_t *ti)
- {
- float f = *(float *)buf;
- pthread_mutex_lock(&ti->mutex);
- ti->calibration = f;
- pthread_mutex_unlock(&ti->mutex);
- info("New calibration value: %.8f\n", f);
- return 1;
- }
-
- static int process(const uint8_t *buf, int len, struct threadinfo_t *ti)
- {
- int n = 0;
-
- /* Process blocks */
- retry:
- for (; (n + 5) <= len; buf += 5, n += 5) {
- int ok = 0;
- switch (buf[0]) {
- case 0xA0:
- if (process_adc_dac(buf + 1, ti)) ok = 1;
- break;
- case 0xA1:
- if (process_calibration(buf + 1, ti)) ok = 1;
- break;
- default:
- break;
- }
- if (!ok) {
- /* badly formed data; eat one byte and retry */
- info("throwing away 0x%02x '%c'\n", buf[0], isprint(buf[0]) ? buf[0] : '.');
- buf++;
- n++;
- goto retry;
- }
- }
-
- return n;
- }
-
- static void *read_data(void *arg)
- {
- struct threadinfo_t *ti = (struct threadinfo_t *)arg;
- char buf[1024];
- int len;
-
- /* read data in a loop. Use saferead_timeout here so we can
- notice quit_flag before too long. */
- len = 0;
- while (!ti->quit_flag) {
- int processed, n;
- n = saferead_timeout(ti->fd,
- buf + len,
- sizeof(buf) - len,
- 1000);
- if (n < 0)
- err(1, "read");
- if (n == 0)
- continue;
- len += n;
- processed = process((uint8_t *) buf, len, ti);
- memmove(buf, buf + processed, len - processed);
- len -= processed;
- }
- info("read thread quitting\n");
- return NULL;
- }
-
- /* change keithley and update keithley_t structure with locking */
- static int keithley_change(int gpib, double desired, struct threadinfo_t *ti)
- {
- double actual;
-
- pthread_mutex_lock(&ti->mutex);
- ti->k.stable = 0;
- ti->k.desired = desired;
- pthread_mutex_unlock(&ti->mutex);
-
- if (keithley_current(gpib, desired) < 0)
- return -1;
-
- actual = keithley_read(gpib);
- if (isnan(actual))
- return -1;
-
- pthread_mutex_lock(&ti->mutex);
- ti->k.actual = actual;
- ti->k.stable = 1;
- pthread_mutex_unlock(&ti->mutex);
- return 0;
- }
-
- static double genrand(double min, double max)
- {
- double x;
- x = genrand_real1(); /* [0, 1] */
- x *= (max - min); /* [0, max-min] */
- x += min; /* [min, max] */
- return x;
- }
-
- static void dctest(int zoom, int gpib)
- {
- pthread_t thread;
- struct threadinfo_t ti;
- double tmp;
- int i;
-
- /* Do a calibration with Keithley off */
- info("Triggering calibration\n");
- zoomrun_trigger_calibrate(zoom);
- usleep(500000);
-
- /* Init Keithley */
- info("Initializing GPIB\n");
- if (gpib_init(gpib) < 0) { info("failed\n"); goto out1; }
-
- info("Initializing Keithley\n");
- if (gpib_addr(gpib, 24) < 0) { info("failed\n"); goto out1; }
- if (keithley_init(gpib) < 0) { info("failed\n"); goto out2; }
- if (keithley_current(gpib, 0) < 0) { info("failed\n"); goto out2; }
- if (isnan(keithley_read(gpib))) { info("failed\n"); goto out2; }
-
- /* Start the thread that reads and dumps data */
- info("Spawning thread\n");
- if (pthread_mutex_init(&ti.mutex, NULL) != 0) {
- info("failed\n");
- goto out2;
- }
- ti.calibration = 0.0;
- ti.k.desired = 0;
- ti.k.actual = 0;
- ti.k.stable = 0;
- ti.quit_flag = 0;
- ti.fd = zoom;
- drain_timeout(zoom, 0);
- if (pthread_create(&thread, NULL, read_data, &ti) != 0) {
- info("failed\n");
- goto out2;
- }
-
- /* Do another calibration now, and verify it works */
- info("Triggering calibration\n");
- pthread_mutex_lock(&ti.mutex);
- ti.calibration = 0.0;
- pthread_mutex_unlock(&ti.mutex);
- if (keithley_change(gpib, 0.0, &ti) < 0) { info("failed\n"); goto out3; }
- zoomrun_trigger_calibrate(zoom);
- for (i = 0; i < 100; i++) {
- usleep(50000);
- pthread_mutex_lock(&ti.mutex);
- tmp = ti.calibration;
- pthread_mutex_unlock(&ti.mutex);
- if (tmp != 0.0)
- break;
- }
- if (tmp == 0.0) {
- info("Invalid calibration data: is zoom NILM working?\n");
- goto out3;
- }
-
- info("Running\n");
- /* Change Keithley values */
- while (!g_quit) {
-
- #define DELAY 100000 /* after keithley settles, in us */
- #define STEPS 10 /* how many small steps to take */
-
- #define K_MIN -0.5 /* keithley min, amps */
- #define K_MAX 0.5 /* keithley max, amps */
- #define STEPSIZE 0.03 /* max size of small step, in amps */
-
- /* Choose any value within the Keithley range */
- double desired = genrand(K_MIN, K_MAX);
- info("Big step: %.12f\n", desired);
- if (keithley_change(gpib, desired, &ti) < 0) {
- info("error setting keithley\n");
- break;
- }
- usleep(DELAY);
-
- /* Choose a few more values nearby */
- for (i = 0; i < STEPS && !g_quit; i++) {
- desired += genrand(-STEPSIZE/2, STEPSIZE/2);
- if (desired < -1.0)
- desired = -1.0;
- if (desired > 1.0)
- desired = 1.0;
- if (keithley_change(gpib, desired, &ti) < 0) {
- info("error setting keithley\n");
- break;
- }
- usleep(DELAY);
- }
-
- /* one extra delay just to separate things a bit */
- usleep(DELAY);
- }
- printf("main thread quitting\n");
-
- out3:
- ti.quit_flag = 1;
- pthread_join(thread, NULL);
- out2:
- keithley_off(gpib);
- out1:
- return;
- }
-
|