From 10fde8e4f0fbaf96627e95c9ffab33b2e49c91a4 Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus <simon.ottenhaus@kit.edu> Date: Mon, 23 Jun 2014 14:12:35 +0200 Subject: [PATCH] Added Logging --- .gitignore | 3 +- .../WeissHapticSensorsUnitTest/startGui.sh | 8 +++ .../WeissHapticSensorsUnitTest/tactile.html | 54 +++++++++++++++++++ .../WeissHapticSensorsUnit/CMakeLists.txt | 4 +- .../WeissHapticSensor/AbstractInterface.cpp | 53 ++++++++++++++++-- .../WeissHapticSensor/AbstractInterface.h | 11 +++- .../WeissHapticSensor/BinaryLogger.cpp | 6 +++ .../drivers/WeissHapticSensor/BinaryLogger.h | 1 + .../drivers/WeissHapticSensor/CMakeLists.txt | 2 + .../WeissHapticSensor/SerialInterface.cpp | 25 ++------- .../WeissHapticSensor/SerialInterface.h | 8 ++- .../drivers/WeissHapticSensor/TextWriter.cpp | 20 +++++++ .../drivers/WeissHapticSensor/TextWriter.h | 21 ++++++++ .../WeissHapticSensor/WeissHapticSensor.cpp | 33 ++++++++++-- .../WeissHapticSensor/WeissHapticSensor.h | 6 +++ source/RobotAPI/units/HapticObserver.cpp | 12 +++-- source/RobotAPI/units/HapticObserver.h | 16 +++++- 17 files changed, 238 insertions(+), 45 deletions(-) create mode 100755 scenarios/WeissHapticSensorsUnitTest/startGui.sh create mode 100755 scenarios/WeissHapticSensorsUnitTest/tactile.html create mode 100644 source/RobotAPI/drivers/WeissHapticSensor/TextWriter.cpp create mode 100644 source/RobotAPI/drivers/WeissHapticSensor/TextWriter.h diff --git a/.gitignore b/.gitignore index 36b9b1d57..f0dad452b 100644 --- a/.gitignore +++ b/.gitignore @@ -45,5 +45,6 @@ data/dbdump/ # Generated Scenario Files scenarios/*/startScenario.sh scenarios/*/stopScenario.sh -scenarios/*/startGui.sh +scenarios/*/ttyACM*.js +scenarios/*/ttyACM*.log diff --git a/scenarios/WeissHapticSensorsUnitTest/startGui.sh b/scenarios/WeissHapticSensorsUnitTest/startGui.sh new file mode 100755 index 000000000..e2f4b07c6 --- /dev/null +++ b/scenarios/WeissHapticSensorsUnitTest/startGui.sh @@ -0,0 +1,8 @@ +export CORE_PATH=../../../Core +export GUI_PATH=../../../Gui + +export SCRIPT_PATH=$CORE_PATH/build/bin +export GUI_BIN_PATH=$GUI_PATH/build/bin + +# Gui +$SCRIPT_PATH/startApplication.sh $GUI_BIN_PATH/ArmarXGuiRun & diff --git a/scenarios/WeissHapticSensorsUnitTest/tactile.html b/scenarios/WeissHapticSensorsUnitTest/tactile.html new file mode 100755 index 000000000..c86869f07 --- /dev/null +++ b/scenarios/WeissHapticSensorsUnitTest/tactile.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="de-DE"> +<head> +<meta charset="utf-8"> +<title>Tactile Sensor Data Explorer</title> +<link rel="stylesheet" type="text/css" href="svg-export.css"/> +<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> +<script> +sensors = []; +</script> +<script src="ttyACM0.js"></script> +<script src="ttyACM1.js"></script> +<style type="text/css"> +#out { + font-size: 10px; + font-family: Verdana, sans; +} +</style> +</head> +<body> +<div id="buttons"></div> +<div id="out"></div> +<script> + +var displayData = function(data) { + var s = 40; + var str = '<div style="position: relative; width:' + (data[0].length * s) + 'px; height:' + (data.length * s) + 'px;">'; + for(var y = 0; y < data.length; y++) { + var row = data[y]; + + for(var x = 0; x < row.length; x++) { + str += '<div style="top:' + (y * s) + 'px; left:' + (x * s) + 'px; width: '+s+'px; height: '+s+'px; position: absolute; text-align:center; line-height:'+s+'px; ">' + row[x] + '</div>'; + } + } + str += '</div>'; + return str; +}; + +var pos = 0; +var sensor = sensors[0]; +var formatDate = function(date) { + return date.getYear() + "-" + ("00" + date.getMonth()).slice(-2) + "-" + ("00" + date.getDate()).slice(-2) + " " + ("00" + date.getHours()).slice(-2) + ":" + ("00" + date.getMinutes()).slice(-2) + ":" + ("00" + date.getSeconds()).slice(-2) + "." + ("000" + date.getMilliseconds()).slice(-3); +}; +var update = function() { + $("#out").html(sensor.device + ", tag=" + sensor.tag + "<br />Frame " + pos + "/" + (sensor.data.length - 1) + " t= " + formatDate(new Date(sensor.data[pos][0]/1000)) + displayData(sensor.data[pos][1])); +} +var changeFrameGen = function(delta) { return function() { pos = Math.max(0, Math.min(sensor.data.length - 1, pos + delta)); update(); }; }; +var buttonGen = function(text, delta) { return $("<button>"+text+"</button>").click(changeFrameGen(delta)); }; +$("#buttons").append("navigate: ").append(buttonGen("-100", -100)).append(buttonGen("-10", -10)).append(buttonGen("-1", -1)).append(buttonGen("+1", +1)).append(buttonGen("+10", +10)).append(buttonGen("+100", +100)); +update(); + +</script> +</body> +</html> \ No newline at end of file diff --git a/source/RobotAPI/applications/WeissHapticSensorsUnit/CMakeLists.txt b/source/RobotAPI/applications/WeissHapticSensorsUnit/CMakeLists.txt index d2d552863..e49b35907 100644 --- a/source/RobotAPI/applications/WeissHapticSensorsUnit/CMakeLists.txt +++ b/source/RobotAPI/applications/WeissHapticSensorsUnit/CMakeLists.txt @@ -8,8 +8,8 @@ if (Eigen3_FOUND) ${Eigen3_INCLUDE_DIR}) endif() -set(COMPONENT_LIBS ArmarXInterfaces ArmarXCore ArmarXCoreEigen3Variants WeissHapticSensor) +set(COMPONENT_LIBS ArmarXInterfaces ArmarXCore ArmarXCoreObservers ArmarXCoreEigen3Variants WeissHapticSensor RobotAPIUnits) -set(EXE_SOURCE main.cpp) +set(EXE_SOURCE main.cpp WeissHapticSensorsUnitApp.h) armarx_add_component_executable("${EXE_SOURCE}") diff --git a/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.cpp b/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.cpp index 973635b44..a81825709 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.cpp +++ b/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.cpp @@ -9,12 +9,39 @@ #include <stdexcept> -AbstractInterface::AbstractInterface() : - connected(false) { +AbstractInterface::AbstractInterface() + : connected(false), log(NULL) +{ + } AbstractInterface::~AbstractInterface() { - // TODO Auto-generated destructor stub + if(log != NULL) + { + delete log; + } +} + +int AbstractInterface::read(unsigned char *buf, unsigned int len) +{ + int res = readInternal(buf, len); + + if(log != NULL) + { + log->logRead(buf, res); + } + + return res; +} + +int AbstractInterface::write(unsigned char *buf, unsigned int len) +{ + if(log != NULL) + { + log->logWrite(buf, len); + } + + return writeInternal(buf, len); } @@ -57,6 +84,19 @@ void AbstractInterface::fireAndForgetCmd( unsigned char id, unsigned char *paylo } } +void AbstractInterface::startLogging(std::string file) +{ + log = new BinaryLogger(file); +} + +void AbstractInterface::logText(std::string message) +{ + if(log != NULL) + { + log->logText(message); + } +} + Response AbstractInterface::receiveWithoutChecks() { int res; @@ -124,6 +164,7 @@ int AbstractInterface::receive( msg_t *msg ) unsigned short checksum = 0x50f5; // Checksum over preamble (0xaa 0xaa 0xaa) unsigned int sync; + logText("read preamble"); // Syncing - necessary for compatibility with serial interface sync = 0; while( sync != MSG_PREAMBLE_LEN ) @@ -191,9 +232,11 @@ int AbstractInterface::receive( msg_t *msg ) checksum = Checksum::Update_crc16( msg->data.data(), msg->len + 2, checksum ); if ( checksum != 0 ) { + logText("CHECKSUM ERROR"); throw ChecksumErrorException("Checksum error"); } + logText("receive done."); return msg->len + 8; } @@ -203,6 +246,8 @@ int AbstractInterface::send(unsigned char id, unsigned int len, unsigned char *d unsigned short crc; int i, res; + logText("write preamble"); + // Preamble for ( i = 0; i < MSG_PREAMBLE_LEN; i++ ) header[i] = MSG_PREAMBLE_BYTE; @@ -233,6 +278,8 @@ int AbstractInterface::send(unsigned char id, unsigned int len, unsigned char *d delete[] buf; + logText("send done."); + return len + 8; } diff --git a/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.h b/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.h index 5c01b9fc5..f8d6c1b9a 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.h +++ b/source/RobotAPI/drivers/WeissHapticSensor/AbstractInterface.h @@ -3,6 +3,7 @@ #include <string> #include "Types.h" +#include "BinaryLogger.h" #define MSG_PREAMBLE_BYTE 0xaa @@ -29,8 +30,8 @@ public: virtual ~AbstractInterface(); virtual int open() = 0; virtual void close() = 0; - virtual int read( unsigned char *buf, unsigned int len) = 0; - virtual int write( unsigned char *buf, unsigned int len) = 0; + int read( unsigned char *buf, unsigned int len); + int write( unsigned char *buf, unsigned int len); bool IsConnected() const { return connected; } @@ -43,12 +44,18 @@ public: Response receiveWithoutChecks(); void fireAndForgetCmd( unsigned char id, unsigned char *payload, unsigned int len, bool pending ); + void startLogging(std::string file); + void logText(std::string message); protected: bool connected; + virtual int readInternal( unsigned char *buf, unsigned int len) = 0; + virtual int writeInternal( unsigned char *buf, unsigned int len) = 0; + private: friend std::ostream& operator<<(std::ostream&, const AbstractInterface&); + BinaryLogger* log; }; std::ostream& operator<<(std::ostream &strm, const AbstractInterface &a); diff --git a/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.cpp b/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.cpp index b3a494138..40c394097 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.cpp +++ b/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.cpp @@ -32,3 +32,9 @@ void BinaryLogger::logWrite(unsigned char *buf, unsigned int len) log << std::endl; log.flush(); } + +void BinaryLogger::logText(std::string message) +{ + log << message << std::endl; + log.flush(); +} diff --git a/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.h b/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.h index d16c85ca6..535786e85 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.h +++ b/source/RobotAPI/drivers/WeissHapticSensor/BinaryLogger.h @@ -11,6 +11,7 @@ public: void logRead(unsigned char *buf, unsigned int len); void logWrite(unsigned char *buf, unsigned int len); + void logText(std::string message); private: std::ofstream log; diff --git a/source/RobotAPI/drivers/WeissHapticSensor/CMakeLists.txt b/source/RobotAPI/drivers/WeissHapticSensor/CMakeLists.txt index 01d560fb8..60c8611f2 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/CMakeLists.txt +++ b/source/RobotAPI/drivers/WeissHapticSensor/CMakeLists.txt @@ -21,6 +21,7 @@ set(LIB_FILES WeissHapticSensor.cpp AbstractInterface.cpp BinaryLogger.cpp + TextWriter.cpp Checksum.cpp SerialInterface.cpp TactileSensor.cpp @@ -30,6 +31,7 @@ set(LIB_HEADERS WeissHapticSensor.h AbstractInterface.h BinaryLogger.h + TextWriter.h Checksum.h Response.h SerialInterface.h diff --git a/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.cpp b/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.cpp index b9bdfe5f0..77bc6b49f 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.cpp +++ b/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.cpp @@ -35,17 +35,13 @@ static inline tcflag_t __bitrate_to_flag( unsigned int bitrate ) SerialInterface::SerialInterface(const char *device, unsigned int bitrate) - : log(NULL) { this->device = device; this->bitrate = bitrate; } SerialInterface::~SerialInterface() { - if(log != NULL) - { - delete log; - } + } int SerialInterface::open() { @@ -122,7 +118,7 @@ void SerialInterface::close() connected = false; } -int SerialInterface::read( unsigned char *buf, unsigned int len) +int SerialInterface::readInternal( unsigned char *buf, unsigned int len) { int res; @@ -132,21 +128,11 @@ int SerialInterface::read( unsigned char *buf, unsigned int len) std::cerr << "Failed to read from serial device" << std::endl; } - if(log != NULL) - { - log->logRead(buf, res); - } - return res; } -int SerialInterface::write( unsigned char *buf, unsigned int len) +int SerialInterface::writeInternal( unsigned char *buf, unsigned int len) { - if(log != NULL) - { - log->logWrite(buf, len); - } - return( ::write( fd, (void *) buf, len ) ); } @@ -155,8 +141,3 @@ std::string SerialInterface::toString() const return str(boost::format("SerialInterface(connected=%1%, device=%2%, bitrate=%3%, fd=%4%)") % connected % device % bitrate % fd); } - -void SerialInterface::startLogging(std::string file) -{ - log = new BinaryLogger(file); -} diff --git a/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.h b/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.h index e5c9b396b..a2bb216ce 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.h +++ b/source/RobotAPI/drivers/WeissHapticSensor/SerialInterface.h @@ -3,7 +3,6 @@ #include "AbstractInterface.h" #include <iostream> -#include "BinaryLogger.h" class SerialInterface : public AbstractInterface { @@ -13,18 +12,17 @@ public: virtual int open(); virtual void close(); - virtual int read( unsigned char *, unsigned int ); - virtual int write( unsigned char *, unsigned int ); virtual std::string toString() const; - void startLogging(std::string file); +protected: + virtual int readInternal( unsigned char *, unsigned int ); + virtual int writeInternal( unsigned char *, unsigned int ); private: const char *device; unsigned int bitrate; int fd; - BinaryLogger* log; }; #endif // SERIALINTERFACE_H diff --git a/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.cpp b/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.cpp new file mode 100644 index 000000000..021bcc12c --- /dev/null +++ b/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.cpp @@ -0,0 +1,20 @@ +#include "TextWriter.h" + +using namespace armarx; + +TextWriter::TextWriter(std::string filename) +{ + this->file.open(filename.c_str()); +} + +TextWriter::~TextWriter() +{ + file.close(); +} + +void TextWriter::writeLine(std::string message) +{ + file << message; + file << std::endl; + file.flush(); +} diff --git a/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.h b/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.h new file mode 100644 index 000000000..d0a87537b --- /dev/null +++ b/source/RobotAPI/drivers/WeissHapticSensor/TextWriter.h @@ -0,0 +1,21 @@ +#ifndef TEXTWRITER_H +#define TEXTWRITER_H + +#include <fstream> + +namespace armarx +{ + class TextWriter + { + public: + TextWriter(std::string filename); + ~TextWriter(); + + void writeLine(std::string message); + + private: + std::ofstream file; + }; +} + +#endif diff --git a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.cpp b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.cpp index ba44fc167..4a5a731cd 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.cpp +++ b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.cpp @@ -1,7 +1,7 @@ #include "WeissHapticSensor.h" -#include <Core/util/variants/eigen3/MatrixVariant.h> -#include <Core/observers/variant/TimestampVariant.h> #include "TransmissionException.h" +#include <boost/regex.hpp> +#include <boost/format.hpp> using namespace armarx; @@ -9,17 +9,23 @@ WeissHapticSensor::WeissHapticSensor(std::string device) : device(device) { sensorTask = new RunningTask<WeissHapticSensor>(this, &WeissHapticSensor::frameAcquisitionTaskLoop); + boost::smatch match; + boost::regex_search( device, match, boost::regex("\\w+$") ); + this->deviceFileName = match[0]; } void WeissHapticSensor::connect() { + //cout << "Open Serial" << endl; this->interface.reset(new SerialInterface(device.c_str(), 115200)); - //interface->startLogging(this->deviceFileName + ".transmission.log"); + interface->startLogging(deviceFileName + ".transmission.log"); interface->open(); //cout << *interface << endl; this->sensor.reset(new TactileSensor(interface)); + jsWriter.reset(new TextWriter(deviceFileName + ".js")); + //cout << "Stop Periodic Frame Acquisition" << endl; sensor->stopPeriodicFrameAcquisition(); @@ -33,6 +39,12 @@ void WeissHapticSensor::connect() this->tag = sensor->getDeviceTag(); //cout << boost::format("Sensor Tag = %1%") % tag << endl; + jsWriter->writeLine(str(boost::format("%s = {};") % deviceFileName)); + jsWriter->writeLine(str(boost::format("sensors.push(%s);") % deviceFileName)); + jsWriter->writeLine(str(boost::format("%s.device = \"%s\";") % deviceFileName % device)); + jsWriter->writeLine(str(boost::format("%s.tag = \"%s\";") % deviceFileName % tag)); + jsWriter->writeLine(str(boost::format("%s.data = [];") % deviceFileName)); + //cout << "Tare Sensor Matrix" << endl; //sensor->tareSensorMatrix(1); @@ -90,14 +102,25 @@ void WeissHapticSensor::frameAcquisitionTaskLoop() (*matrix)(y, x) = val; } } - listenerPrx->reportSensorValues(device, tag, matrix, TimestampVariant::nowPtr()); + TimestampVariantPtr now = TimestampVariant::nowPtr(); + writeMatrixToJs(matrix, now); + listenerPrx->reportSensorValues(device, tag, matrix, now); } catch(ChecksumErrorException) { - ARMARX_WARNING << "Caught ChecksumErrorException, skipping frame"; + ARMARX_WARNING << "Caught ChecksumErrorException on " << device << ", skipping frame"; } } cout << this << ": stopPeriodicFrameAcquisition" << endl; sensor->stopPeriodicFrameAcquisition(); } + +void WeissHapticSensor::writeMatrixToJs(MatrixFloatPtr matrix, TimestampVariantPtr timestamp) +{ + //std::cout << "writeMatrixToJs" << std::endl; + if(jsWriter != NULL) + { + jsWriter->writeLine(str(boost::format("%s.data.push([%i, %s]);") % deviceFileName % timestamp->getTimestamp() % matrix->toJsonRowMajor())); + } +} diff --git a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.h b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.h index 5f4efb2db..acfaad733 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.h +++ b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticSensor.h @@ -7,6 +7,9 @@ #include "TactileSensor.h" #include <RobotAPI/interface/units/HapticUnit.h> #include <Core/util/variants/eigen3/VariantObjectFactories.h> +#include "TextWriter.h" +#include <Core/observers/variant/TimestampVariant.h> +#include <Core/util/variants/eigen3/MatrixVariant.h> namespace armarx { @@ -23,14 +26,17 @@ namespace armarx private: RunningTask<WeissHapticSensor>::pointer_type sensorTask; + boost::shared_ptr<TextWriter> jsWriter; void frameAcquisitionTaskLoop(); std::string device; + std::string deviceFileName; boost::shared_ptr<SerialInterface> interface; boost::shared_ptr<TactileSensor> sensor; bool connected; string tag; tac_matrix_info_t mi; HapticUnitListenerPrx listenerPrx; + void writeMatrixToJs(MatrixFloatPtr matrix, TimestampVariantPtr timestamp); }; } diff --git a/source/RobotAPI/units/HapticObserver.cpp b/source/RobotAPI/units/HapticObserver.cpp index cca4d8b0c..033532980 100644 --- a/source/RobotAPI/units/HapticObserver.cpp +++ b/source/RobotAPI/units/HapticObserver.cpp @@ -73,14 +73,18 @@ void HapticObserver::reportSensorValues(const std::string& device, const std::st setDataField(device, "mean", Variant(mean)); setDataField(device, "timestamp", timestampPtr); } - if(statistics.count(device) > 0) + /*if(statistics.count(device) > 0) { statistics.at(device).add(timestamp->timestamp); + HapticSampleStatistics stats = statistics.at(device); + long avg = stats.average(); + float rate = avg == 0 ? 0 : 1000000.0f / (float)avg; + setDataField(device, "rate", Variant(rate)); } else { statistics.insert(std::map<std::string,HapticSampleStatistics>::value_type(device, HapticSampleStatistics(100, timestamp->timestamp))); - } + }*/ updateChannel(device); } @@ -92,7 +96,7 @@ PropertyDefinitionsPtr HapticObserver::createPropertyDefinitions() void HapticObserver::updateStatistics() { - ScopedLock lock(dataMutex); + /*ScopedLock lock(dataMutex); //ARMARX_LOG << "updateStatistics"; long now = TimestampVariant::nowLong(); for (std::map<std::string, HapticSampleStatistics>::iterator it = statistics.begin(); it != statistics.end(); ++it) @@ -103,5 +107,5 @@ void HapticObserver::updateStatistics() float rate = avg == 0 ? 0 : 1000000.0f / (float)avg; setDataField(device, "rate", Variant(rate)); updateChannel(device); - } + }*/ } diff --git a/source/RobotAPI/units/HapticObserver.h b/source/RobotAPI/units/HapticObserver.h index 88e9d74b6..0ef0de014 100644 --- a/source/RobotAPI/units/HapticObserver.h +++ b/source/RobotAPI/units/HapticObserver.h @@ -69,7 +69,7 @@ namespace armarx lastTimestamp = timestamp; } - long average(long timestamp) + /*long average(long timestamp) { long sum = timestamp - lastTimestamp; for(std::vector<long>::iterator it = deltas.begin(); it != deltas.end(); ++it) @@ -77,6 +77,20 @@ namespace armarx sum += *it; } return sum / (deltas.size() + 1); + }*/ + + long average() + { + if(deltas.size() == 0) + { + return 0; + } + long sum = 0; + for(std::vector<long>::iterator it = deltas.begin(); it != deltas.end(); ++it) + { + sum += *it; + } + return sum / deltas.size(); } private: -- GitLab