diff options
Diffstat (limited to 'src/measurement/extech.cpp')
-rw-r--r-- | src/measurement/extech.cpp | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/measurement/extech.cpp b/src/measurement/extech.cpp new file mode 100644 index 0000000..867aca7 --- /dev/null +++ b/src/measurement/extech.cpp @@ -0,0 +1,354 @@ +/* + * extech - Program for controlling the extech Device + * This file is part of PowerTOP + * + * Based on earlier client by Patrick Mochel for Wattsup Pro device + * Copyright (c) 2005 Patrick Mochel + * Copyright (c) 2006 Intel Corporation + * Copyright (c) 2011 Intel Corporation + * + * Authors: + * Patrick Mochel + * Venkatesh Pallipadi + * Arjan van de Ven + * + * Thanks to Rajiv Kapoor for finding out the DTR, RTS line bits issue below + * Without that this program would never work. + * + * + * This program file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file named COPYING; if not, write to the + * Free Software Foundation, Inc, + * 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * or just google for it. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <termios.h> +#include <ctype.h> +#include <getopt.h> +#include <time.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <sys/stat.h> + +#include "measurement.h" +#include "extech.h" +#include "../lib.h" +#include <iostream> +#include <fstream> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +using namespace std; + +struct packet { + char buf[256]; + char op[32]; + double watts; + int len; +}; + + +static int open_device(const char *device_name) +{ + struct stat s; + int ret; + + ret = stat(device_name, &s); + if (ret < 0) + return -1; + + if (!S_ISCHR(s.st_mode)) + return -1; + + ret = access(device_name, R_OK | W_OK); + if (ret) + return -1; + + ret = open(device_name, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (ret < 0) + return -1; + + return ret; +} + + +static int setup_serial_device(int dev_fd) +{ + struct termios t; + int ret; + int flgs; + + ret = tcgetattr(dev_fd, &t); + if (ret) + return ret; + + cfmakeraw(&t); + cfsetispeed(&t, B9600); + cfsetospeed(&t, B9600); + tcflush(dev_fd, TCIFLUSH); + + t.c_iflag = IGNPAR; + t.c_cflag = B9600 | CS8 | CREAD | CLOCAL; + t.c_oflag = 0; + t.c_lflag = 0; + t.c_cc[VMIN] = 2; + t.c_cc[VTIME] = 0; + + t.c_iflag &= ~(IXON | IXOFF | IXANY); + t.c_oflag &= ~(IXON | IXOFF | IXANY); + + ret = tcsetattr(dev_fd, TCSANOW, &t); + + if (ret) + return ret; + + /* + * Root caused by Rajiv Kapoor. Without this extech reads + * will fail + */ + + /* get DTR and RTS line bits settings */ + ioctl(dev_fd, TIOCMGET, &flgs); + + /* set DTR to 1 and RTS to 0 */ + flgs |= TIOCM_DTR; + flgs &= ~TIOCM_RTS; + ioctl(dev_fd, TIOCMSET, &flgs); + + return 0; +} + + +static unsigned int decode_extech_value(unsigned char byt3, unsigned char byt4, char *a) +{ + unsigned int input = ((unsigned int)byt4 << 8) + byt3; + unsigned int i; + unsigned int idx; + unsigned char revnum[] = { 0x0, 0x8, 0x4, 0xc, + 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, + 0x3, 0xb, 0x7, 0xf}; + unsigned char revdec[] = { 0x0, 0x2, 0x1, 0x3}; + + unsigned int digit_map[] = {0x2, 0x3c, 0x3c0, 0x3c00}; + unsigned int digit_shift[] = {1, 2, 6, 10}; + + unsigned int sign; + unsigned int decimal; + + /* this is basically BCD encoded floating point... but kinda weird */ + + decimal = (input & 0xc000) >> 14; + decimal = revdec[decimal]; + + sign = input & 0x1; + + idx = 0; + if (sign) + a[idx++] = '+'; + else + a[idx++] = '-'; + + /* first digit is only one or zero */ + a[idx] = '0'; + if ((input & digit_map[0]) >> digit_shift[0]) + a[idx] += 1; + + idx++; + /* Reverse the remaining three digits and store in the array */ + for (i = 1; i < 4; i++) { + int dig = ((input & digit_map[i]) >> digit_shift[i]); + dig = revnum[dig]; + if (dig > 0xa) + goto error_exit; + + a[idx++] = '0' + dig; + } + + /* Fit the decimal point where appropriate */ + for (i = 0; i < decimal; i++) + a[idx - i] = a[idx - i - 1]; + + a[idx - decimal] = '.'; + a[++idx] = '0'; + a[++idx] = '\0'; + + return 0; +error_exit: + return -1; +} + +static int parse_packet(struct packet * p) +{ + int i; + int ret; + + p->buf[p->len] = '\0'; + + /* + * First character in 5 character block should be '02' + * Fifth character in 5 character block should be '03' + */ + for (i = 0; i < 4; i++) { + if (p->buf[i * 0] != 2 || p->buf[i * 0 + 4] != 3) { + printf("Invalid packet\n"); + return -1; + } + } + + for (i = 0; i < 1; i++) { + ret = decode_extech_value(p->buf[5 * i + 2], + p->buf[5 * i + 3], + &(p->op[8 * i])); + if (ret) { + printf("Invalid packet, conversion failed\n"); + return -1; + } + p->watts = strtod( &(p->op[8 * i]), NULL); + } + return 0; +} + + +static double extech_read(int fd) +{ + struct packet p; + fd_set read_fd; + struct timeval tv; + int ret; + + if (fd < 0) + return 0.0; + + FD_ZERO(&read_fd); + FD_SET(fd, &read_fd); + + tv.tv_sec = 0; + tv.tv_usec = 500000; + + memset(&p, 0, sizeof(p)); + + ret = select(fd + 1, &read_fd, NULL, NULL, &tv); + if (ret <= 0) + return -1; + + ret = read(fd, &p.buf, 250); + if (ret < 0) + return ret; + p.len = ret; + + if (!parse_packet(&p)) + return p.watts; + + return -1000.0; +} + +extech_power_meter::extech_power_meter(const char *extech_name) +{ + rate = 0.0; + pt_strcpy(dev_name, extech_name); + int ret; + + fd = open_device(dev_name); + if (fd < 0) + return; + + ret = setup_serial_device(fd); + if (ret) { + close(fd); + fd = -1; + return; + } +} + + +void extech_power_meter::measure(void) +{ + /* trigger the extech to send data */ + if(write(fd, " ", 1) == -1) + printf("Error: %s\n", strerror(errno)); + + rate = extech_read(fd); + +} + +void extech_power_meter::sample(void) +{ + ssize_t ret; + struct timespec tv; + + tv.tv_sec = 0; + tv.tv_nsec = 200000000; + while (!end_thread) { + nanosleep(&tv, NULL); + /* trigger the extech to send data */ + ret = write(fd, " ", 1); + if (ret < 0) + continue; + + sum += extech_read(fd); + samples++; + + } +} + +extern "C" +{ + void* thread_proc(void *arg) + { + class extech_power_meter *parent; + parent = (class extech_power_meter*)arg; + parent->sample(); + return 0; + } +} + +void extech_power_meter::end_measurement(void) +{ + end_thread = 1; + pthread_join( thread, NULL); + if (samples){ + rate = sum / samples; + } + else + measure(); +} + +void extech_power_meter::start_measurement(void) +{ + end_thread = 0; + sum = samples = 0; + + if (pthread_create(&thread, NULL, thread_proc, this)) + fprintf(stderr, "ERROR: extech measurement thread creation failed\n"); + +} + + +double extech_power_meter::power(void) +{ + return rate; +} |