Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving Average Filter Implementation #43

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions AttitudeManager/Inc/IMUMovingAverageFilter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef IMU_MOVING_AVERAGE_FILTER_HPP
#define IMU_MOVING_AVERAGE_FILTER_HPP

#include "MovingAverage.hpp"

class IMUMovingAverageFilter {
public:
IMUMovingAverageFilter();
MovingAverageError_t Update();
MovingAverageError_t GetAverage(uint8_t windowSize, IMUData_t* FilteredIMUData);

private:
IMU* imuObj;
Window<float> magxVals;
Window<float> magyVals;
Window<float> magzVals;
Window<float> accxVals;
Window<float> accyVals;
Window<float> acczVals;
Window<float> gyrxVals;
Window<float> gyryVals;
Window<float> gyrzVals;
};

#endif // IMU_MOVING_AVERAGE_FILTER_HPP
27 changes: 27 additions & 0 deletions AttitudeManager/Inc/MovingAverage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef MOVING_AVERAGE_HPP
#define MOVING_AVERAGE_HPP

#include <stdint.h>

#define MAX_WINDOW_SIZE 50
#define MAX_POS_LIMIT MAX_WINDOW_SIZE * 1000000

enum MovingAverageError_t {
SUCCESS,
FAILURE,
OLD_VALUES,
};

template<typename T>
class Window {
private:
T window[MAX_WINDOW_SIZE];
uint8_t size;
uint32_t pos;
public:
Window();
void update(T newValue);
MovingAverageError_t getAverage(uint8_t n, T* result);
};

#endif // MOVING_AVERAGE_HPP
112 changes: 112 additions & 0 deletions AttitudeManager/Inc/imu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* IMU Sensor Functions and Part Number Selection.
* Available IMU driver(s): BMX160
* Author(s): Lucy Gong, Dhruv Rawat, Anthony Berbari
*/

#ifndef IMU_HPP
#define IMU_HPP

#include <cstdint>
#include "CommonDataTypes.hpp"
/***********************************************************************************************************************
* Definitions
**********************************************************************************************************************/

struct IMUData_t {

float magx, magy, magz; // TODO although the BMX 160 has a magnetometer, it seems to produce bizarre results.More investigation needs to be done. Figuring out what the RHall register does is likely a part of that.
float accx, accy, accz;
float gyrx, gyry, gyrz;
float temp;

bool isDataNew;
SensorErrorCodes sensorStatus; // 0 = SUCCESS, -1 = FAIL, 1 = BUSY
};

/***********************************************************************************************************************
* Prototypes
*********************************************************************************************************************/

class IMU {
public:
/**
* Begins a transaction with the IMU.
* This function is non blocking and returns right away, data will be stored inside the module as it arrives.
* To achieve synchronous data, this function must be called synchronously.
* */
virtual void Begin_Measuring() = 0;

/**
* Retrieves any data already received by the imu.
* If no new data is available, the appropriate flag will be set in the return struct.
* All contents of the return struct, apart from the isDataNew flag, are undefined unless isDataNew is set to 1.
* This function is non blocking and returns right away.
* @param[in] Data reference to the results struct.
* */
virtual void GetResult(IMUData_t& Data) = 0;
};

class BMX160: public IMU{
public:

/**
* This module is built as a singleton. Thus to access a BMX160 object, this function must be called.
* Only a single BMX160 object will ever be created and will be shared by all callers of this function.
* @return IMU reference to the singleton object.
* */
static IMU& getInstance();

/**
* Deletes the constructor to disallow users to instantiate objects.
* */
BMX160(const BMX160*) = delete;

void Begin_Measuring();
void GetResult(IMUData_t& Data);

private:

BMX160();

void SetAllPowerModesToNormal(void);
void ConfigAcc(void);
void ConfigGyro(void);
void ConfigMag(void);
void SetMagConfig(uint8_t regAddr, uint8_t data);
void PrepareMagForDataMode(void);

void Bmx160WriteReg(uint8_t reg, uint8_t val);
void Bmx160ReadReg(uint8_t const regAddr, uint8_t *pData, uint8_t len);

void Calibrate(void);

//Variables
uint8_t rawImuData[21];
IMUData_t ImuCalibration;
IMUData_t ImuCalibrationFinal;

};

/**
* Callback for exclusive use by the SPI ISR.
* Users of this module should never call this function.
* Any manipulation of this function by the higher level voids all guarantees made by this module.
* */
void ImuTxRxCallback();

#ifdef UNIT_TESTING

class TestIMU : public IMU {
public:
static TestIMU* GetInstance();

void Begin_Measuring();

void GetResult(IMUData_t& Data);

};

#endif

#endif
57 changes: 57 additions & 0 deletions AttitudeManager/Src/IMUMovingAverageFilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "IMUMovingAverageFilter.hpp"
#include "imu.hpp"
#include "MovingAverage.hpp"

IMUMovingAverageFilter::IMUMovingAverageFilter() {
imuObj = &BMX160::getInstance();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll let this pass for now, but want you to know that we'll need to change it to IMU::getInstance() eventually, the IMU type eventually needs to get defined in the model config, but it's not relevant atm and is an easy change for now.

magxVals = Window<float>();
magyVals = Window<float>();
magzVals = Window<float>();
accxVals = Window<float>();
accyVals = Window<float>();
acczVals = Window<float>();
gyrxVals = Window<float>();
gyryVals = Window<float>();
gyrzVals = Window<float>();
}

MovingAverageError_t IMUMovingAverageFilter::Update() {
IMUData_t currentIMUData;
imuObj->GetResult(currentIMUData);

if (!currentIMUData.isDataNew) {
// We could log here, ideally this case shouldn't occur and we should slow
// down the refresh rate if we encounter this often.
return OLD_VALUES;
}

magxVals.update(currentIMUData.magx);
magyVals.update(currentIMUData.magy);
magzVals.update(currentIMUData.magz);
accxVals.update(currentIMUData.accx);
accyVals.update(currentIMUData.accy);
acczVals.update(currentIMUData.accz);
gyrxVals.update(currentIMUData.gyrx);
gyryVals.update(currentIMUData.gyry);
gyrzVals.update(currentIMUData.gyrz);

return SUCCESS;
}

// Populates FilteredIMUData.
MovingAverageError_t IMUMovingAverageFilter::GetAverage(uint8_t windowSize, IMUData_t* FilteredIMUData) {

int checksum;

checksum += magxVals.getAverage(windowSize, &FilteredIMUData->magx);
checksum += magyVals.getAverage(windowSize, &FilteredIMUData->magy);
checksum += magzVals.getAverage(windowSize, &FilteredIMUData->magz);
checksum += accyVals.getAverage(windowSize, &FilteredIMUData->accx);
checksum += acczVals.getAverage(windowSize, &FilteredIMUData->accy);
checksum += accxVals.getAverage(windowSize, &FilteredIMUData->accz);
checksum += gyrxVals.getAverage(windowSize, &FilteredIMUData->gyrx);
checksum += gyryVals.getAverage(windowSize, &FilteredIMUData->gyry);
checksum += gyrzVals.getAverage(windowSize, &FilteredIMUData->gyrz);

return checksum == 0 ? FAILURE : SUCCESS;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still not sure if checksums are the best way to do this - seems like a lot of computations and I'm still not sure what we're looking for....

}
41 changes: 41 additions & 0 deletions AttitudeManager/Src/MovingAverage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "MovingAverage.hpp"
#include <string.h>

template<typename T>
Window<T>::Window() {
memset(window, 0, MAX_WINDOW_SIZE);
size = 0;
pos = 0;
}

template<typename T>
void Window<T>::update(T newValue) {
window[pos] = newValue;
pos++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can post-increment within your array access I believe:

https://stackoverflow.com/questions/34250975/increment-operator-inside-array


// If the window is not full yet, increase size.
if (size < MAX_WINDOW_SIZE) {
size++;
}

// Reset pos once in a while to ensure it doesn't get too big.
if (pos == MAX_POS_LIMIT) {
pos = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this reset your entire moving average occasionally?

}
}

template<typename T>
MovingAverageError_t Window<T>::getAverage(uint8_t n, T* result) {
if (n <= 0 || n > size) {
return FAILURE;
}

int sum = 0;
for (int i = pos; i > pos - n; i--) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see .....

what is "n" supposed to keep track of here?

Also - negative indices aren't really good style. You'd have to wrap it all the way around.

At that point - I would consider making a linkedlist data struct in common, and using a circular version of that? So you can always just go next next next next next and never worry about resetting your data?

uint8_t realIdx = pos % MAX_WINDOW_SIZE;
sum += window[realIdx];
}

*result = static_cast<T>(sum) / n;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea for the static cast is good - the data that IMU's deal with is almost always floating point decimal (i.e. 0.9g, 1.1g, 1.7137g, etc), so it might be worth forcing a float return.

Something about safety-critical applications and never "guessing" your return type. Search for "cast" in this doc: https://embeddedgurus.com/barr-code/category/coding-standards/page/2/ as a starting point

return SUCCESS;
}
Loading
Loading