Skip to content

Commit

Permalink
ema filter
Browse files Browse the repository at this point in the history
  • Loading branch information
tiandahuang committed Jul 10, 2024
1 parent 2de9d59 commit 0ca98d7
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 25 deletions.
143 changes: 143 additions & 0 deletions Apps/Inc/EMAFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* Copyright (c) 2018-2022 UT Longhorn Racing Solar */
/*
* This file implements a EMA filter.
* This performs the operation y_t = α * x_t + (1 - α) * y_{t-1}
*
* In order to use it in another file, you must import it in
* a particular way.
*
* 1. Define your data type, like so
* #define EMA_FILTER_TYPE int
* 2. Define your filter cofficient, like so
* #define EMA_FILTER_ALPHA_NUMERATOR 1
* #define EMA_FILTER_ALPHA_DEMONINATOR 8
* 3. Define the number of channels in your filter, like so
* #define EMA_FILTER_CHANNELS (31)
* 3. Name your EMA filter
* #define EMA_FILTER_NAME my_filter
* 4. Import this file
* #include "EMAFilter.h"
*
* This file includes some defaults, but they might not work for
* your case!
*
* Also, this file undef's everything at the end, so you can import
* multiple times if you need.
*
* If EMA_FILTER_NAME == my_filter, then your new data structure will be
* called my_filter_t.
*
* NOTE: importantly, this does not currently support usage from
* header files. That is, all these types/functions are statically
* declared, so there cannot be a non-static fifo at the moment.
*/

// The header guard only guard the import,
// since this file can be imported multiple times
#ifndef EMA_FILTER_H
#define EMA_FILTER_H
#include <stdint.h>
#include <string.h>
#endif

// The type of the EMA filter
#ifndef EMA_FILTER_TYPE
#define EMA_FILTER_TYPE int32_t
#endif

// The alpha coefficient of the EMA filter, as a fraction
#ifndef EMA_FILTER_ALPHA_NUMERATOR
#define EMA_FILTER_ALPHA_NUMERATOR 1
#endif

#ifndef EMA_FILTER_ALPHA_DEMONINATOR
#define EMA_FILTER_ALPHA_DEMONINATOR 8
#endif

// The number of channels in the EMA filter
#ifndef EMA_FILTER_CHANNELS
#define EMA_FILTER_CHANNELS 1
#endif

// The name of the EMA filter (minus the _t)
#ifndef EMA_FILTER_NAME
#define EMA_FILTER_NAME define_your_filter_type
#endif

// Utility definitions
#define _CONCAT(A, B) A ## B
#define CONCAT(A, B) _CONCAT(A, B)

// Type names
#define EMA_FILTER_STRUCT_NAME CONCAT(EMA_FILTER_NAME, _s)
#define EMA_FILTER_TYPE_NAME CONCAT(EMA_FILTER_NAME, _t)

// The actual structure
typedef struct EMA_FILTER_STRUCT_NAME {
EMA_FILTER_TYPE prev[EMA_FILTER_CHANNELS];
} EMA_FILTER_TYPE_NAME;

// Define some names for our functions
#define INIT CONCAT(EMA_FILTER_NAME, _init)
#define GET CONCAT(EMA_FILTER_NAME, _get)
#define PUT CONCAT(EMA_FILTER_NAME, _put)

/**
* @brief Initialize a new EMA filter
*
* If the type of the filter is myfilter_t, then this function
* will be called myfilter_init().
*
* @param filter a pointer to the EMA filter to initialize
* @param init initial value
*/
static inline void __attribute__((unused))
INIT (EMA_FILTER_TYPE_NAME *filter, EMA_FILTER_TYPE init) {
for (uint32_t ch = 0; ch < EMA_FILTER_CHANNELS; ch++) {
filter->prev[ch] = init;
}
}

/**
* @brief update the EMA filter by giving it a new set of values for all channels
*
* @param filter a pointer to the EMA filter
* @param values a complete set of new values for all channels to add to the EMA filter
*
*/
static inline void __attribute__((unused))
PUT (EMA_FILTER_TYPE_NAME *filter, EMA_FILTER_TYPE *values) {
for (uint32_t ch = 0; ch < EMA_FILTER_CHANNELS; ch++) {
filter->prev[ch] =
( EMA_FILTER_ALPHA_NUMERATOR * values[ch]
+ (EMA_FILTER_ALPHA_DEMONINATOR - EMA_FILTER_ALPHA_NUMERATOR) * filter->prev[ch] )
/ EMA_FILTER_ALPHA_DEMONINATOR;
}
}

/**
* @brief get a complete set of filtered values for all channels
*
* @param filter a pointer to the EMA filter
* @param dest a pointer to a buffer to store all of the filtered values
*/
static inline void __attribute__((unused))
GET (EMA_FILTER_TYPE_NAME *filter, EMA_FILTER_TYPE *dest) {
memcpy(dest, filter->prev, sizeof(EMA_FILTER_TYPE) * EMA_FILTER_CHANNELS);
}


// undef everything, so this file can be included multiple times
#undef EMA_FILTER_TYPE
#undef EMA_FILTER_DEPTH
#undef EMA_FILTER_CHANNELS
#undef EMA_FILTER_NAME
#undef _CONCAT
#undef CONCAT
#undef EMA_FILTER_STRUCT_NAME
#undef EMA_FILTER_TYPE_NAME
#undef EMA
#undef INIT
#undef GET
#undef PUT

33 changes: 24 additions & 9 deletions Apps/Src/Temperature.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@
#define MEDIAN_FILTER_TYPE int32_t
#define MEDIAN_FILTER_DEPTH 3
#define MEDIAN_FILTER_CHANNELS MAX_TEMP_SENSORS
#define MEDIAN_FILTER_NAME TemperatureFilter
#define MEDIAN_FILTER_NAME TemperatureFilter1
#include "MedianFilter.h"
static TemperatureFilter_t TemperatureFilter;
static TemperatureFilter1_t TemperatureFilter1;

// ema filter
#define EMA_FILTER_TYPE int32_t
#define EMA_FILTER_ALPHA_NUMERATOR 1
#define EMA_FILTER_ALPHA_DEMONINATOR 4
#define EMA_FILTER_CHANNELS MAX_TEMP_SENSORS
#define EMA_FILTER_NAME TemperatureFilter2
#include "EMAFilter.h"
static TemperatureFilter2_t TemperatureFilter2;

// simulator bypasses ltc driver
#ifndef SIMULATION
Expand All @@ -31,8 +40,9 @@ static cell_asic *Minions;
// Holds the temperatures in Celsius (Fixed Point with .001 resolution) for each sensor on each board
static int32_t Temperatures[NUM_TEMPERATURE_SENSORS];

// Raw temperature values from the ADC -- this is a sparse array and will be packed down later.
static int32_t rawTemperatures[MAX_TEMP_SENSORS];
// Intermediate temperature values for filtering -- this is a sparse array and will be packed down later.
static int32_t TemperaturesMedFiltIn[MAX_TEMP_SENSORS];
static int32_t TemperaturesEMAFiltIn[MAX_TEMP_SENSORS];

// Holds the maximum measured temperature in the most recent batch of temperature measurements
static int32_t maxTemperature = 0;
Expand Down Expand Up @@ -75,7 +85,9 @@ void Temperature_Init(cell_asic *boards){

#endif
// set up the median filter with alternating temperatures of 1000 degrees and 0 degrees
TemperatureFilter_init(&TemperatureFilter, 0, 1000000);
TemperatureFilter1_init(&TemperatureFilter1, 0, 1000000);
// set up the ema filter with an initial safe value (35000)
TemperatureFilter2_init(&TemperatureFilter2, 25000);
}

/** Temperature_ChannelConfig
Expand Down Expand Up @@ -212,7 +224,7 @@ ErrorStatus Temperature_UpdateSingleChannel(uint8_t channel){
uint8_t sensor_idx = (board * MAX_TEMP_SENSORS_PER_MINION_BOARD) + channel;
if (channel < TemperatureSensorsCfg[board]) { // don't touch unused sensors
#ifndef SIMULATION
rawTemperatures[sensor_idx] = milliVoltToCelsius(Minions[board].aux.a_codes[0] / 10);
TemperaturesMedFiltIn[sensor_idx] = milliVoltToCelsius(Minions[board].aux.a_codes[0] / 10);
#else
// simulator expects the actual number of physical sensors (and not just mux channels on the PCB)
// therefore, we have to do some index crunching -- this is very inefficient but it's just for
Expand All @@ -224,7 +236,7 @@ ErrorStatus Temperature_UpdateSingleChannel(uint8_t channel){
uint8_t simulator_idx = sensor_idx - empty_sensors_so_far;

// populate nonvalid sensors with 0
rawTemperatures[sensor_idx] = (channel < TemperatureSensorsCfg[board]) ? Simulator_getTemperature(simulator_idx) : 0;
TemperaturesMedFiltIn[sensor_idx] = (channel < TemperatureSensorsCfg[board]) ? Simulator_getTemperature(simulator_idx) : 0;
#endif
}
}
Expand All @@ -233,7 +245,10 @@ ErrorStatus Temperature_UpdateSingleChannel(uint8_t channel){
#endif

// run median filter
TemperatureFilter_put(&TemperatureFilter, rawTemperatures);
TemperatureFilter1_put(&TemperatureFilter1, TemperaturesMedFiltIn);
TemperatureFilter1_get(&TemperatureFilter1, TemperaturesEMAFiltIn);
// run ema filter -- values will be retrieved in Temperature_UpdateAllMeasurements()
TemperatureFilter2_put(&TemperatureFilter2, TemperaturesEMAFiltIn);

return SUCCESS;
}
Expand All @@ -256,7 +271,7 @@ ErrorStatus Temperature_UpdateAllMeasurements(){

// update public temperatures with output of median filter
int32_t filteredTemperatures[MAX_TEMP_SENSORS];
TemperatureFilter_get(&TemperatureFilter, filteredTemperatures);
TemperatureFilter2_get(&TemperatureFilter2, filteredTemperatures);

// package raw voltage values into single array
for (uint8_t minion = 0, sensor = 0; minion < NUM_MINIONS; minion++){
Expand Down
33 changes: 24 additions & 9 deletions Apps/Src/Voltage.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@
#define MEDIAN_FILTER_TYPE uint16_t
#define MEDIAN_FILTER_DEPTH 3
#define MEDIAN_FILTER_CHANNELS NUM_BATTERY_MODULES
#define MEDIAN_FILTER_NAME VoltageFilter
#define MEDIAN_FILTER_NAME VoltageFilter1
#include "MedianFilter.h"
static VoltageFilter_t VoltageFilter;
static VoltageFilter1_t VoltageFilter1;

// ema filter
#define EMA_FILTER_TYPE uint16_t
#define EMA_FILTER_ALPHA_NUMERATOR 1
#define EMA_FILTER_ALPHA_DEMONINATOR 4
#define EMA_FILTER_CHANNELS NUM_BATTERY_MODULES
#define EMA_FILTER_NAME VoltageFilter2
#include "EMAFilter.h"
static VoltageFilter2_t VoltageFilter2;

static cell_asic *Minions;

Expand Down Expand Up @@ -72,8 +81,9 @@ void Voltage_Init(cell_asic *boards){
// release mutex
RTOS_BPS_MutexPost(&MinionsASIC_Mutex, OS_OPT_POST_NONE);
#endif
// Initialize median filter. There should be no modules with less than 0 volts or more than 5 volts
VoltageFilter_init(&VoltageFilter, 0, 50000);
// Initialize median + EMA filter with safe values. There should be no modules with less than 0 volts or more than 5 volts
VoltageFilter1_init(&VoltageFilter1, 0, 50000);
VoltageFilter2_init(&VoltageFilter2, 37000);
}


Expand All @@ -82,7 +92,8 @@ void Voltage_Init(cell_asic *boards){
* @param pointer to new voltage measurements
*/
void Voltage_UpdateMeasurements(void){
uint16_t rawVoltages[NUM_BATTERY_MODULES];
uint16_t volt_med_filt_in[NUM_BATTERY_MODULES];
uint16_t volt_ema_filt_in[NUM_BATTERY_MODULES];
#ifndef SIMULATION
// Start Cell ADC Measurements
wakeup_sleep(NUM_MINIONS);
Expand All @@ -97,7 +108,7 @@ void Voltage_UpdateMeasurements(void){
// package raw voltage values into single array
for (uint8_t minion = 0, module = 0; minion < NUM_MINIONS; minion++){
for (uint8_t tap = 0; tap < VoltageSensorsCfg[minion]; tap++) {
rawVoltages[module++] = Minions[minion].cells.c_codes[tap];
volt_med_filt_in[module++] = Minions[minion].cells.c_codes[tap];
}
}

Expand All @@ -106,16 +117,20 @@ void Voltage_UpdateMeasurements(void){
#else
// package raw voltage values into single array
for(uint8_t i = 0; i < NUM_BATTERY_MODULES; i++){
rawVoltages[i] = Simulator_getVoltage(i);
volt_med_filt[i] = Simulator_getVoltage(i);
}
#endif
// run median filter
VoltageFilter_put(&VoltageFilter, rawVoltages);
VoltageFilter1_put(&VoltageFilter1, volt_med_filt_in);
VoltageFilter1_get(&VoltageFilter1, volt_ema_filt_in);

// run ema filter
VoltageFilter2_put(&VoltageFilter2, volt_ema_filt_in);

// update public voltage values
RTOS_BPS_MutexPend(&Voltage_Mutex, OS_OPT_PEND_BLOCKING);

VoltageFilter_get(&VoltageFilter, Voltages);
VoltageFilter2_get(&VoltageFilter2, Voltages);

// calculate min, max, and pack voltage
uint32_t maxv = 0, minv = UINT32_MAX, totalv = 0;
Expand Down
14 changes: 7 additions & 7 deletions Config/Inc/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ typedef enum SafetyStatusOpt_e {
//--------------------------------------------------------------------------------
// Battery Pack layout
#ifndef NUM_BATTERY_MODULES
#define NUM_BATTERY_MODULES 32 // Number of battery modules
#define NUM_BATTERY_MODULES 31 // Number of battery modules
#endif

#ifndef MODULE_CELLS_IN_PARALLEL
Expand All @@ -47,7 +47,7 @@ typedef enum SafetyStatusOpt_e {
#endif

#ifndef NUM_TEMPERATURE_SENSORS
#define NUM_TEMPERATURE_SENSORS 32 // Number of temperature sensors
#define NUM_TEMPERATURE_SENSORS 31 // Number of temperature sensors
#endif

//--------------------------------------------------------------------------------
Expand All @@ -58,7 +58,7 @@ typedef enum SafetyStatusOpt_e {
#endif

#ifndef PER_MINION_BOARD_VOLT_SENSORS
#define PER_MINION_BOARD_VOLT_SENSORS {11, 10, 11} // Number of voltage sensors per minion board. Should be an array with same length as NUM_MINIONS
#define PER_MINION_BOARD_VOLT_SENSORS {11, 9, 11} // Number of voltage sensors per minion board. Should be an array with same length as NUM_MINIONS
#endif

#define MAX_VOLT_WIRES (MAX_VOLT_SENSORS_PER_MINION_BOARD * NUM_MINIONS)
Expand All @@ -71,7 +71,7 @@ typedef enum SafetyStatusOpt_e {
#endif

#ifndef PER_MINION_BOARD_TEMP_SENSORS
#define PER_MINION_BOARD_TEMP_SENSORS {16, 0, 16} // Number of voltage sensors per minion board. Should be an array with same length as NUM_MINIONS
#define PER_MINION_BOARD_TEMP_SENSORS {16, 0, 15} // Number of voltage sensors per minion board. Should be an array with same length as NUM_MINIONS
#endif

#define MAX_TEMP_SENSORS (MAX_TEMP_SENSORS_PER_MINION_BOARD * NUM_MINIONS)
Expand Down Expand Up @@ -107,11 +107,11 @@ _Static_assert((sizeof(TemperatureSensorsCfg)/sizeof(*TemperatureSensorsCfg)) ==
#endif

#ifndef MAX_VOLTAGE_LIMIT
#define MAX_VOLTAGE_LIMIT 4100 // Over voltage limit (milliVolts) (actual max: 4.2V)
#define MAX_VOLTAGE_LIMIT 4200 // Over voltage limit (milliVolts) (actual max: 4.2V)
#endif

#ifndef CHARGE_DISABLE_VOLTAGE
#define CHARGE_DISABLE_VOLTAGE 4000 // Voltage to stop charging at
#define CHARGE_DISABLE_VOLTAGE 4100 // Voltage to stop charging at
#endif

// make sure we don't enable charging if we're too close to the voltage limit
Expand All @@ -128,7 +128,7 @@ _Static_assert((sizeof(TemperatureSensorsCfg)/sizeof(*TemperatureSensorsCfg)) ==
#endif

#ifndef CHARGE_DISABLE_TEMPERATURE
#define CHARGE_DISABLE_TEMPERATURE 43000 // Temperature to stop charging at
#define CHARGE_DISABLE_TEMPERATURE 43500 // Temperature to stop charging at
#endif

// make sure we don't enable charging if we're too close to the temperature limit
Expand Down

0 comments on commit 0ca98d7

Please sign in to comment.