Commit b3626c85 authored by James Pallister's avatar James Pallister

Updated python interface and tests, autotools works better

parent ede5afa9
SUBDIRS=src/host src/firmware
SUBDIRS=src/firmware
ACLOCAL_AMFLAGS= -I m4
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_PREREQ([2.68])
AC_INIT([stm32f4-energy-monitor], [1.0], [james.pallister@embecosm.com])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE
# gcc-arm-embedded requires these libraries for the tests, else they'll fail
AC_SUBST([LDFLAGS], ["-lc -lrdimon $LDFLAGS"])
AC_SUBST([CFLAGS], ["-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $CFLAGS"])
AC_PROG_LIBTOOL
LT_INIT
AC_CONFIG_SRCDIR([src/host/host_receiver.cpp])
AC_CONFIG_SRCDIR([src/firmware/])
AC_CONFIG_HEADERS([config.h])
AC_LANG([C++])
AC_PROG_CXX
AC_PROG_CC
AC_PROG_CC([arm-none-eabi-gcc])
# TODO check if a suitable arm compiler exists
# Check to see if it can compile code for cortex-m4f
# Check to see if libopencm3 is available
# Check for compiler flags.
AX_CXX_COMPILE_STDCXX_11([noext],[mandatory])
# Checks for libraries.
BOOST_REQUIRE([1.49.0])
BOOST_REGEX([n])
BOOST_THREADS([n])
BOOST_SYSTEM([n])
# Save libraries (don't want to compile energy_monitor with them)
save_LIBS="$LIBS"
AC_CHECK_LIB([readline], [main], [], [AC_MSG_ERROR([Cannot find readline library. Please install it.])])
AC_CHECK_LIB([usb-1.0], [main], [], [AC_MSG_ERROR([Cannot find usb-1.0 library. Please install it.])])
LIBS="$save_LIBS"
# Python checking adapted from gdb's configure.ac
# It's license, and original code can be obtained from
# http://ftp.gnu.org/gnu/gdb/gdb-7.6.tar.bz2
AC_DEFUN([AC_TRY_LIBPYTHON],
[
version=$1
define([have_libpython_var],$2)
new_CPPFLAGS=$3
new_LIBS=$4
save_CPPFLAGS=$CPPFLAGS
save_LIBS=$LIBS
CPPFLAGS="$CPPFLAGS $new_CPPFLAGS"
LIBS="$LIBS $new_LIBS"
case $version in
python3*)
BOOST_FIND_LIB([python3], [n], [boost/python.hpp],
[], [BOOST_PYTHON_MODULE(empty) {}])
;;
*)
BOOST_PYTHON([n])
;;
esac
found_usable_python=no
AC_MSG_CHECKING([for ${version}])
AC_LINK_IFELSE([AC_LANG_SOURCE([AC_LANG_PROGRAM(
[[
#include "Python.h"
]],
[[
Py_Initialize ();
]])])],
[have_libpython_var=${version}
found_usable_python=yes
PYTHON_CPPFLAGS=$new_CPPFLAGS
PYTHON_LIBS=$new_LIBS])
CPPFLAGS=$save_CPPFLAGS
LIBS=$save_LIBS
AC_MSG_RESULT([${found_usable_python}])
])
AC_ARG_WITH(python,
AS_HELP_STRING([--with-python=PYTHON], [also build host receiver as a python module (/path/to/python)]),
[],
[with_python=`which python`])
AC_ARG_WITH(python,
[AS_HELP_STRING([--without-python],[disable building of host receiver as a python module])],
[],
[with_python=no])
AC_MSG_CHECKING([whether to use python])
AC_MSG_RESULT([$with_python])
if test "${with_python}" = no; then
AC_MSG_WARN([python module will not be compiled])
have_libpython=no
else
python_bin="${with_python}"
if test ! -f "${python_bin}"; then
AC_MSG_ERROR(unable to find python program ${python_bin})
else
python_includes=`${python_bin}-config --includes`
if test $? != 0; then
AC_MSG_ERROR(failure running python-config --includes)
fi
python_libs=`${python_bin}-config --ldflags`
if test $? != 0; then
AC_MSG_ERROR(failure running python-config --ldflags)
fi
have_libpython=no
python_version=`echo " ${python_libs} " \
| sed -e 's,^.* -l\(python[[0-9]]*[[\.]]*[[0-9]]*\).*$,\1,'`
case "${python_version}" in
python*)
AC_TRY_LIBPYTHON(${python_version}, have_libpython,
${python_includes}, ${python_libs})
;;
*)
AC_MSG_ERROR([unable to determine python version from ${python_libs}])
;;
esac
if test "${have_libpython}" = no; then
AC_MSG_ERROR([no usable python found at ${with_python}])
fi
fi
fi
AC_SUBST(PYTHON_CPPFLAGS)
AC_SUBST(PYTHON_LIBS)
AM_CONDITIONAL([BUILD_PYMODULE], [test x$have_libpython != xno])
#AC_PROG_CC([arm-none-eabi-gcc])
AC_CHECK_LIB(opencm3_stm32f4, nvic_enable_irq)
AC_CONFIG_FILES([Makefile src/host/Makefile src/firmware/Makefile])
AC_CONFIG_FILES([Makefile src/firmware/Makefile])
AC_OUTPUT
bin_PROGRAMS=energy_monitor
energy_monitor_SOURCES=energy_monitor.c
energy_monitor_DEPENDENCIES=libopencm3_stm32f4.ld
CC= arm-none-eabi-gcc
AM_CFLAGS = -D STM32F4
AM_CFLAGS += -mcpu=cortex-m4 -mthumb
AM_CFLAGS += -mcpu=cortex-m4 -mthumb -O2
AM_CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
#AM_LDFLAGS = -mcpu=cortex-m4 -mthumb
#AM_LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
AM_LDFLAGS = -lopencm3_stm32f4 -T libopencm3_stm32f4.ld
AM_LDFLAGS = -lopencm3_stm32f4 -T $(srcdir)/libopencm3_stm32f4.ld
......@@ -101,7 +101,7 @@ void adc_setup();
// Power data ///////////////////////////////////////////////////////
#define NUM_BUFFERS 128
#define PWR_SAMPLES 64
#define PWR_SAMPLES 32
typedef struct {
uint64_t energy_accum;
......
bin_PROGRAMS=host_receiver
host_receiver_SOURCES=dataprocessor.cpp host_receiver.cpp helper.cpp libusbinterface.cpp
host_receiver_SOURCES+=dataprocessor.h helper.h libusbinterface.h
host_receiver_CPPFLAGS = $(BOOST_CPPFLAGS)
host_receiver_LDADD = $(BOOST_REGEX_LIBS) $(BOOST_THREAD_LIBS) $(BOOST_SYSTEM_LIBS)
host_receiver_LDFLAGS = $(BOOST_REGEX_LDFLAGS) $(BOOST_THREAD_LDFLAGS) $(BOOST_SYSTEM_LDFLAGS) -lusb-1.0 -lreadline
host_receiver_CXXFLAGS= -std=c++11
if BUILD_PYMODULE
lib_LTLIBRARIES=pyenergy.la
pyenergy_la_SOURCES=$(host_receiver_SOURCES) pymodule.cpp
pyenergy_la_CPPFLAGS = $(PYTHON_CPPFLAGS)
pyenergy_la_LIBADD = $(BOOST_PYTHON_LIBS) $(BOOST_PYTHON3_LIBS) $(PYTHON_LIBS) $(host_receiver_LDADD)
pyenergy_la_LDFLAGS = $(BOOST_PYTHON_LDFLAGS) $(BOOST_PYTHON3_LDFLAGS) -module -shared -avoid-version $(host_receiver_LDFLAGS)
endif
ACLOCAL_AMFLAGS=-Im4
AC_CHECK_LIB([readline], [main],, [AC_MSG_ERROR([Cannot find readline library. Please install it.])])
AC_CHECK_LIB([usb-1.0], [main],, [AC_MSG_ERROR([Cannot find usb-1.0 library. Please install it.])])
#include <stdio.h>
#include <boost/thread.hpp>
#include "dataprocessor.h"
#include <time.h>
#include "libusbinterface.h"
#include "helper.h"
#include <math.h>
#define DEFAULT_OUTPUT "output_results"
using namespace std;
using namespace boost;
using namespace boost::accumulators;
DataProcessor::DataProcessor(boost::mutex *m, std::queue<DataSet> *d)
{
mQueue = m;
dQueue = d;
status = RUNNING;
cur_time = 0;
doAccumulation = false;
last_tick = 0;
switched = false;
isEmpty = true;
resistor = 1.0;
gain = 50.0;
referenceVoltage = 3.0;
/* Don't set output. This should be done with openOutput. */
output = NULL;
}
DataProcessor::~DataProcessor()
{
DataProcessor::closeOutput();
}
void DataProcessor::operator()()
{
int t1, t2;
t1 = time(0);
while(status == RUNNING || !isEmpty)
{
getData();
if(status != RUNNING && isEmpty)
continue;
processData();
// printf("gda\n");
t2 = time(0);
if(switched && doAccumulation)
{
mt_start_output();
printf("Avg: %4.1lf Std:%3.1lf Min: %4d Max: %4d kS/s:%3.1lf\n",mean(last_data),
sqrt(variance(last_data)), (int)extract::min(last_data), (int)extract::max(last_data),
extract::count(last_data)/1000.);
mt_end_output();
t1 = t2;
switched = false;
}
}
}
void DataProcessor::getData()
{
do {
{
boost::mutex::scoped_lock lock(*mQueue);
if(!dQueue->empty())
{
isEmpty = false;
data = dQueue->front();
dQueue->pop();
return;
}
isEmpty = true;
}
sleep(1);
} while(status == RUNNING);
}
void DataProcessor::addDataItem(short val, unsigned long tstamp)
{
if(tstamp > last_tick + TIMER_SECOND_TICKS)
{
last_data = current_data;
// clear(current_data);
current_data = decltype(current_data)();
switched = true;
last_tick += TIMER_SECOND_TICKS;
}
current_data(val);
}
/*
This decodes the data that has been received. The data is 'compressed'. The
first byte of each 64 byte transfer is the timer period between sequential
samples. The remaining 63 bytes encodes 42 samples (2 samples / 3 bytes).
0 Timer period
3n+1 Low 8 bits of sample n*2
3n+2 Low 8 bits of sample n*2+1
3n+3 Low 4 bits : bits 11-8 of sample n*2
High 4 bits : bits 11-8 of sample n*2+1
*/
void DataProcessor::processData()
{
int i, c = 0;
short b1, b2;
short rate = 0;
float power;
if (data.type == COMMAND)
{
switch (data.data[0])
{
case LibusbInterface::START:
this->openOutput();
break;
case LibusbInterface::STOP:
this->closeOutput();
break;
}
}
else
{
for(i = 0; i < DATA_LEN; ++i, ++c)
{
if(i % 64 == 0)
{
rate = data.data[i];
// printf("%d\n", rate);
c = 2;
}
else if(c % 3 == 0)
{
b1 = data.data[i];
}
else if(c % 3 == 1)
{
b2 = data.data[i];
}
else
{
b1 |= (data.data[i]&0x0F) << 8;
b2 |= (data.data[i]&0xF0) << 4;
power = convertToPower(b1);
if (DataProcessor::openedFile())
{
fprintf(output, "%f %lu\n", power, cur_time);
}
addDataItem(power, cur_time);
cur_time += rate;
power = convertToPower(b2);
if (DataProcessor::openedFile())
{
fprintf(output, "%f %lu\n", power, cur_time);
}
addDataItem(power, cur_time);
cur_time += rate;
}
}
}
}
void DataProcessor::endSignal()
{
status = IDLE;
}
void DataProcessor::setAccumulation(bool sa)
{
doAccumulation = sa;
}
void DataProcessor::setResistor(float res)
{
resistor = res;
}
void DataProcessor::setReferenceVoltage(float v)
{
referenceVoltage = v;
}
void DataProcessor::setGain(float v)
{
gain = v;
}
float DataProcessor::convertToPower(float v)
{
return double(v) * 4096. / 4095. / 4095. * double(referenceVoltage) * double(referenceVoltage)
* 2. / double(gain) / double(resistor);
}
int DataProcessor::closeOutput()
{
if (status == RUNNING)
{
return 1;
}
int to_return = 0;
if (output)
{
fprintf(output,"\n");
to_return = fclose(output);
output = NULL;
}
return to_return;
}
int DataProcessor::openOutput()
{
openOutput(DEFAULT_OUTPUT);
}
int DataProcessor::openOutput(std::string output_loc)
{
int to_return = 0;
if (!output)
{
output = fopen(output_loc.c_str(), "w");
to_return = (output == NULL);
}
else
{
to_return = -1;
}
return to_return;
}
int DataProcessor::openedFile()
{
return (output != NULL);
}
#ifndef __DATAPROCESSOR_H__
#define __DATAPROCESSOR_H__
#include <queue>
#include <vector>
#include <boost/thread.hpp>
#include <boost/shared_array.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include "libusbinterface.h"
#define TIMER_SECOND_TICKS 84000000
namespace _ba = boost::accumulators;
class DataProcessor
{
public:
DataProcessor(boost::mutex *, std::queue<DataSet> *);
~DataProcessor();
void operator()();
void endSignal();
void setAccumulation(bool);
void setResistor(float);
void setReferenceVoltage(float);
void setGain(float);
int closeOutput();
int openOutput();
int openOutput(std::string output_loc);
int openedFile();
private:
enum RunningStatus {
RUNNING,
IDLE
};
boost::mutex *mQueue;
std::queue<DataSet> *dQueue;
DataSet data;
int status;
bool isEmpty;
void getData();
void processData();
FILE *output;
unsigned long cur_time;
bool doAccumulation;
// Circuit parameters that are needed to calculate power
float resistor;
float referenceVoltage;
float gain;
float convertToPower(float v);
void addDataItem(short, unsigned long);
_ba::accumulator_set<double, _ba::features<_ba::tag::variance, _ba::tag::min, _ba::tag::max> > last_data, current_data;
unsigned long last_tick;
bool switched;
};
#endif
#include "helper.h"
#include <cstring>
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <readline/readline.h>
using namespace std;
void mt_start_output()
{
int i;
if(rl_line_buffer && rl_prompt)
{
cout << "\r";
for(i = 0; i < strlen(rl_line_buffer) + strlen(rl_prompt); ++i)
cout << " ";
cout << "\r";
}
}
void mt_end_output()
{
raise(SIGWINCH);
}
#ifndef __HELPER_H__
#define __HELPER_H__
void mt_start_output();
void mt_end_output();
#endif
\ No newline at end of file
This diff is collapsed.
#ifndef __HOST_RECEIVER_H__
#define __HOST_RECEIVER_H__
#include "libusbinterface.h"
void cmd_connect();
void cmd_connect_to(std::string connect_to);
void cmd_getserial();
void cmd_setserial(std::string new_serial);
void cmd_setresistor(std::string new_resist);
void cmd_setrefvoltage(std::string new_voltage);
void cmd_setgain(std::string new_gain);
void cmd_trigger(std::string trigger);
void cmd_leds();
void cmd_start();
void cmd_start_with_file(std::string output_file);
void cmd_stop();
void cmd_power();
void cmd_power_set(std::string power);
void cmd_mode(std::string new_mode);
LibusbInterface::accumulated_data cmd_getenergy();
void cmd_help();
void cmd_exit();
bool cmd_is_running();
void processCommand(std::string input);
#endif /* __HOST_RECEIVER_H__ */
This diff is collapsed.
#ifndef __LIBUSBINTERFACE_H__
#define __LIBUSBINTERFACE_H__
#include <queue>
#include <boost/thread.hpp>
#include <boost/shared_array.hpp>
#include <string>
#include <vector>
#include <libusb-1.0/libusb.h>
#define DATA_LEN 2048
enum DataSetType {
ENERGY_DATA = 0,
COMMAND = 1
};
struct DataSet {
boost::shared_array<unsigned char> data;
DataSetType type;
};
class LibusbInterface
{
public:
LibusbInterface(boost::mutex *, std::queue<DataSet> *, unsigned idVendor, unsigned idProduct, std::string serialId);
~LibusbInterface();
void operator()();
void endSignal();
static std::vector<std::pair<std::string, std::string> > listDevices(unsigned idVendor, unsigned idProduct);
enum CommandType {
LED = 0,
START = 1,
STOP = 2,
SETSERIAL = 3,
SETTRIGGER = 4,
SETMODE = 5,
GETENERGY = 6
};
enum Mode {
NORMAL_ADC = 0,
DUAL_ADC = 1,
OVERSAMPLED_ADC = 2
};
void sendCommand(CommandType);
void setSerial(std::string);
void setTrigger(char, int);
void setMode(Mode);
bool isRunning();
/* Energy in joules:
= Vref^2 / gain / resistor / 4096^2 * 2
* tperiod * (2/168000000) * energy_accum
^ peripheral clk rate (timer cnt)
Elapsed time in seconds:
= elapsed_time * (2/168000000)
Peak power in watts
= Vref^2 / gain / resistor / 4096^2 * peak_power * 2
Peak voltage in volts
= peak_voltage / 4096 * Vref * 2
Peak current in amps
= peak_current / 4096 * Vref / gain / resistor
*/
struct accumulated_data {
uint64_t energy_accum;
uint64_t elapsed_time;
unsigned peak_power;
unsigned peak_voltage;
unsigned peak_current;
unsigned n_samples;
};
accumulated_data lastData;
bool cmdsEmpty();
private:
boost::mutex *mQueue;
boost::mutex cQueueMutex;
std::queue<DataSet> *dQueue;
// Attributes to look for in the USB devices
unsigned idProduct, idVendor;
std::string serialId;
unsigned char data_buf[DATA_LEN];
unsigned char interrupt_buf[64];
int total_len;
bool running;
bool open_device();
void close_device();
// Send data to the data processor thread
void send_data(DataSet);
int status;
libusb_device_handle *devh;
// Periodic bulk transfer of data from the device
struct libusb_transfer *energy_transfer;
static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer);
struct libusb_transfer *interrupt_transfer;
static void LIBUSB_CALL interrupt_callback(struct libusb_transfer *transfer);
// This class handles the sending on control information to the device
struct CommandData {
CommandType cmd;
std::string cmd_data;
};
bool sendMonitorCommand(CommandData cmd);
// Our command queue
std::queue<CommandData> cQueue;
};
#endif
#include <boost/python.hpp>
#include <libusb-1.0/libusb.h>
#include <readline/readline.h>
#include "host_receiver.h"
#include <sys/types.h>
#include <sys/syscall.h>
#include <unistd.h>
using namespace boost::python;
int r;
class ReleaseGIL {
public:
ReleaseGIL()
{
state = PyEval_SaveThread();
}
~ReleaseGIL()
{
PyEval_RestoreThread(state);
}
private:
PyThreadState *state;
};