summaryrefslogtreecommitdiffstats
path: root/src/measurement/extech.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:00:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:00:20 +0000
commitfcb4cb5c3d0fec0fede160d565134d553d783fb2 (patch)
tree7be42535554ca6badc1847d83ef123f4dc3c5506 /src/measurement/extech.cpp
parentInitial commit. (diff)
downloadpowertop-fcb4cb5c3d0fec0fede160d565134d553d783fb2.tar.xz
powertop-fcb4cb5c3d0fec0fede160d565134d553d783fb2.zip
Adding upstream version 2.15.upstream/2.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/measurement/extech.cpp')
-rw-r--r--src/measurement/extech.cpp354
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;
+}