2020-05-18 08:53:34 -04:00
|
|
|
//
|
|
|
|
// FILE: Correlation.cpp
|
2021-01-29 06:31:58 -05:00
|
|
|
// AUTHOR: Rob Tillaart
|
2022-10-30 13:12:10 -04:00
|
|
|
// VERSION: 0.2.3
|
2020-05-18 08:53:34 -04:00
|
|
|
// PURPOSE: Arduino Library to determine correlation between X and Y dataset
|
|
|
|
//
|
2022-10-30 13:12:10 -04:00
|
|
|
// HISTORY: see cjhangelog.md
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
|
|
|
|
#include "Correlation.h"
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
Correlation::Correlation(uint8_t size)
|
2020-05-18 08:53:34 -04:00
|
|
|
{
|
2021-08-27 10:16:35 -04:00
|
|
|
_size = 20;
|
|
|
|
if (size > 0) _size = size;
|
2021-01-29 06:31:58 -05:00
|
|
|
_x = (float *) malloc(_size * sizeof(float));
|
|
|
|
_y = (float *) malloc(_size * sizeof(float));
|
2020-05-18 08:53:34 -04:00
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
Correlation::~Correlation()
|
|
|
|
{
|
2021-08-27 10:16:35 -04:00
|
|
|
if (_x) free(_x);
|
|
|
|
if (_y) free(_y);
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
void Correlation::clear()
|
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
_count = 0;
|
2021-12-14 10:39:48 -05:00
|
|
|
_index = 0;
|
2020-05-18 08:53:34 -04:00
|
|
|
_needRecalculate = true;
|
2021-01-29 06:31:58 -05:00
|
|
|
_runningMode = false;
|
|
|
|
_avgX = 0;
|
|
|
|
_avgY = 0;
|
|
|
|
_a = 0;
|
|
|
|
_b = 0;
|
2022-06-21 02:22:05 -04:00
|
|
|
_div_b = -1; // as 1/_b is undefined
|
2021-08-27 10:16:35 -04:00
|
|
|
_r = 0;
|
2021-01-29 06:31:58 -05:00
|
|
|
_sumErrorSquare = 0;
|
|
|
|
_sumXiYi = 0;
|
|
|
|
_sumXi2 = 0;
|
|
|
|
_sumYi2 = 0;
|
2021-08-27 10:16:35 -04:00
|
|
|
_doR2 = true;
|
|
|
|
_doE2 = true;
|
2020-05-18 08:53:34 -04:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
bool Correlation::add(float x, float y)
|
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
if ( (_count < _size) || _runningMode)
|
2020-05-18 08:53:34 -04:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
_x[_index] = x;
|
|
|
|
_y[_index] = y;
|
|
|
|
_index++;
|
|
|
|
if (_index >= _size) _index = 0;
|
2021-01-29 06:31:58 -05:00
|
|
|
if (_count < _size) _count++;
|
2020-05-18 08:53:34 -04:00
|
|
|
_needRecalculate = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2021-08-27 10:16:35 -04:00
|
|
|
bool Correlation::calculate(bool forced)
|
2020-05-18 08:53:34 -04:00
|
|
|
{
|
|
|
|
if (_count == 0) return false;
|
2021-08-27 10:16:35 -04:00
|
|
|
if (! (_needRecalculate || forced)) return true;
|
2020-05-18 08:53:34 -04:00
|
|
|
|
2022-06-21 02:22:05 -04:00
|
|
|
// CALC AVERAGE X, AVERAGE Y
|
2021-08-26 11:18:52 -04:00
|
|
|
float avgx = 0;
|
|
|
|
float avgy = 0;
|
2022-10-30 13:12:10 -04:00
|
|
|
float div_count = 1.0 / _count; // speed up averaging
|
2020-05-18 08:53:34 -04:00
|
|
|
for (uint8_t i = 0; i < _count; i++)
|
|
|
|
{
|
2021-08-26 11:18:52 -04:00
|
|
|
avgx += _x[i];
|
|
|
|
avgy += _y[i];
|
2020-05-18 08:53:34 -04:00
|
|
|
}
|
2022-06-21 02:22:05 -04:00
|
|
|
avgx *= div_count;
|
|
|
|
avgy *= div_count;
|
2021-08-27 10:16:35 -04:00
|
|
|
|
2021-08-26 11:18:52 -04:00
|
|
|
_avgX = avgx;
|
|
|
|
_avgY = avgy;
|
2020-05-18 08:53:34 -04:00
|
|
|
|
2022-06-21 02:22:05 -04:00
|
|
|
// CALC A and B ==> formula Y = A + B * X
|
2021-08-26 11:18:52 -04:00
|
|
|
float sumXiYi = 0;
|
|
|
|
float sumXi2 = 0;
|
|
|
|
float sumYi2 = 0;
|
2020-05-18 08:53:34 -04:00
|
|
|
for (uint8_t i = 0; i < _count; i++)
|
|
|
|
{
|
2021-08-26 11:18:52 -04:00
|
|
|
float xi = _x[i] - avgx;
|
|
|
|
float yi = _y[i] - avgy;
|
|
|
|
sumXiYi += (xi * yi);
|
|
|
|
sumXi2 += (xi * xi);
|
|
|
|
sumYi2 += (yi * yi);
|
2020-05-18 08:53:34 -04:00
|
|
|
}
|
2021-08-26 11:18:52 -04:00
|
|
|
float b = sumXiYi / sumXi2;
|
|
|
|
float a = avgy - b * avgx;
|
2021-08-27 10:16:35 -04:00
|
|
|
|
2021-08-26 11:18:52 -04:00
|
|
|
_a = a;
|
|
|
|
_b = b;
|
2022-06-21 02:22:05 -04:00
|
|
|
_div_b = 1.0 / b;
|
2021-08-27 10:16:35 -04:00
|
|
|
_sumXiYi = sumXiYi;
|
|
|
|
_sumXi2 = sumXi2;
|
|
|
|
_sumYi2 = sumYi2;
|
2021-08-26 11:18:52 -04:00
|
|
|
|
2021-08-27 10:16:35 -04:00
|
|
|
if (_doR2 == true)
|
|
|
|
{
|
2022-06-21 02:22:05 -04:00
|
|
|
// R is calculated instead of rSquared so we do not loose the sign.
|
|
|
|
// Rsquared from R is much faster than R from Rsquared.
|
2021-08-27 10:16:35 -04:00
|
|
|
_r = sumXiYi / sqrt(sumXi2 * sumYi2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_doE2 == true)
|
2020-05-18 08:53:34 -04:00
|
|
|
{
|
2021-08-27 10:16:35 -04:00
|
|
|
float sumErrorSquare = 0;
|
|
|
|
for (uint8_t i = 0; i < _count; i++)
|
|
|
|
{
|
|
|
|
float EY = a + b * _x[i];
|
|
|
|
float ei = _y[i] - EY;
|
|
|
|
sumErrorSquare += (ei * ei);
|
|
|
|
}
|
|
|
|
_sumErrorSquare = sumErrorSquare;
|
2020-05-18 08:53:34 -04:00
|
|
|
}
|
|
|
|
_needRecalculate = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
float Correlation::getEstimateY(float x)
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
if (_needRecalculate) calculate();
|
|
|
|
return _a + _b * x;
|
|
|
|
}
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
float Correlation::getEstimateX(float y)
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
if (_needRecalculate) calculate();
|
2022-06-21 02:22:05 -04:00
|
|
|
return (y - _a) * _div_b;
|
2020-05-18 08:53:34 -04:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
//
|
2022-06-21 02:22:05 -04:00
|
|
|
// STATISTICAL
|
2021-01-29 06:31:58 -05:00
|
|
|
//
|
|
|
|
float Correlation::getMaxX()
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
float rv = _x[0];
|
|
|
|
for (uint8_t i = 1; i < _count; i++)
|
|
|
|
{
|
|
|
|
if (_x[i] > rv) rv = _x[i];
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
float Correlation::getMinX()
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
float rv = _x[0];
|
|
|
|
for (uint8_t i = 1; i < _count; i++)
|
|
|
|
{
|
|
|
|
if (_x[i] < rv) rv = _x[i];
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
float Correlation::getMaxY()
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
float rv = _y[0];
|
|
|
|
for (uint8_t i = 1; i < _count; i++)
|
|
|
|
{
|
|
|
|
if (_y[i] > rv) rv = _y[i];
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
float Correlation::getMinY()
|
|
|
|
{
|
|
|
|
if (_count == 0) return NAN;
|
|
|
|
float rv = _y[0];
|
|
|
|
for (uint8_t i = 1; i < _count; i++)
|
|
|
|
{
|
|
|
|
if (_y[i] < rv) rv = _y[i];
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
//
|
2022-06-21 02:22:05 -04:00
|
|
|
// DEBUGGING - access to internal arrays.
|
2021-01-29 06:31:58 -05:00
|
|
|
//
|
2021-12-14 10:39:48 -05:00
|
|
|
bool Correlation::setXY(uint8_t index, float x, float y)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
if (index >= _count) return false;
|
|
|
|
_x[index] = x;
|
|
|
|
_y[index] = y;
|
2021-01-29 06:31:58 -05:00
|
|
|
_needRecalculate = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
|
|
|
bool Correlation::setX(uint8_t index, float x)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
if (index >= _count) return false;
|
|
|
|
_x[index] = x;
|
2021-01-29 06:31:58 -05:00
|
|
|
_needRecalculate = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
|
|
|
float Correlation::getX(uint8_t index)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
if (index >= _count) return NAN;
|
|
|
|
return _x[index];
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
|
|
|
bool Correlation::setY(uint8_t index, float y)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
if (index >= _count) return false;
|
|
|
|
_y[index] = y;
|
2021-01-29 06:31:58 -05:00
|
|
|
_needRecalculate = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
|
|
|
float Correlation::getY(uint8_t index)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2021-12-14 10:39:48 -05:00
|
|
|
if (index > _count) return NAN;
|
|
|
|
return _y[index];
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
|
2021-12-14 10:39:48 -05:00
|
|
|
|
2020-05-18 08:53:34 -04:00
|
|
|
// -- END OF FILE --
|
2021-12-14 10:39:48 -05:00
|
|
|
|