Hello,
I am trying to implement an FIR, using the Arduino FIR Library. I first designed the FIR filter in MATLAB's Filter Design and Analysis Toolbox (fdatool), then validated it in Simulink. I am now trying to implement it on an Arduino Due to filter real-time data.
The filter is a 48-tap FIR low-pass filter sampled at 20 Hz (I know its slow, but its apart of my application). I have a 20 Hz timer calling the function to calculate the FIR. Majority of the following code is just to generate a test waveform that contains 2 frequencies (aptly named 'FASTER' and 'SLOWER'). The FIR is designed to attenuate the FASTER frequency.
#define FASTER_FREQUENCY 75 // BPM
#define FASTER_AMPLITUDE 12
#define SLOWER_FREQUENCY 12 // BPM
#define SLOWER_AMPLITUDE 20
#define OFFSET 0
#include <stdio.h>
#include <FIR.h>
#define FILTERTAPS 48
FIR fir;
double FASTER_motion [] = {
0.0001234098, 0.0002226299, 0.0003936690, 0.0006823281, 0.0011592292, 0.0019304541, 0.0031511116, 0.0050417603, 0.0079070541, 0.0121551783, 0.0183156389, 0.0270518469, 0.0391638951, 0.0555762126, 0.0773047404, 0.1053992246, 0.1408584209, 0.1845195240, 0.2369277587, 0.2981972794, 0.3678794412, 0.4448580662, 0.5272924240, 0.6126263942, 0.6976763261, 0.7788007831, 0.8521437890, 0.9139311853, 0.9607894392, 0.9900498337, 1.0000000000, 0.9900498337, 0.9607894392, 0.9139311853, 0.8521437890, 0.7788007831, 0.6976763261, 0.6126263942, 0.5272924240, 0.4448580662, 0.3678794412, 0.2981972794, 0.2369277587, 0.1845195240, 0.1408584209, 0.1053992246, 0.0773047404, 0.0555762126, 0.0391638951, 0.0270518469, 0.0183156389, 0.0121551783, 0.0079070541, 0.0050417603, 0.0031511116, 0.0019304541, 0.0011592292, 0.0006823281, 0.0003936690, 0.0002226299, 0.0001234098
};
int FASTER_frequency(FASTER_FREQUENCY), FASTER_period(0), FASTER_amplitude(FASTER_AMPLITUDE), FASTER_index(0);
int FASTER_size = sizeof(FASTER_motion) / sizeof(FASTER_motion[0]);
boolean FASTER_state = false;
double SLOWER_motion [] = {
0.0019305, 0.0020976, 0.002278, 0.0024726, 0.0026822, 0.002908, 0.0031511, 0.0034126, 0.0036938, 0.0039958, 0.0043202, 0.0046684, 0.0050418, 0.005442, 0.0058707, 0.0063297, 0.0068208, 0.0073459, 0.0079071, 0.0085063, 0.0091459, 0.0098282, 0.010555, 0.01133, 0.012155, 0.013033, 0.013966, 0.014958, 0.016012, 0.01713, 0.018316, 0.019573, 0.020905, 0.022315, 0.023807, 0.025385, 0.027052, 0.028813, 0.030671, 0.032631, 0.034697, 0.036873, 0.039164, 0.041574, 0.044108, 0.046771, 0.049566, 0.0525, 0.055576, 0.0588, 0.062177, 0.06571, 0.069406, 0.073269, 0.077305, 0.081517, 0.085911, 0.090491, 0.095263, 0.10023, 0.1054, 0.11077, 0.11635, 0.12215, 0.12816, 0.1344, 0.14086, 0.14755, 0.15447, 0.16162, 0.16901, 0.17665, 0.18452, 0.19264, 0.201, 0.20961, 0.21847, 0.22757, 0.23693, 0.24653, 0.25638, 0.26647, 0.2768, 0.28738, 0.2982, 0.30925, 0.32053, 0.33204, 0.34377, 0.35572, 0.36788, 0.38024, 0.3928, 0.40555, 0.41849, 0.43159, 0.44486, 0.45828, 0.47184, 0.48554, 0.49935, 0.51327, 0.52729, 0.54139, 0.55556, 0.56978, 0.58404, 0.59833, 0.61263, 0.62691, 0.64118, 0.65541, 0.66958, 0.68367, 0.69768, 0.71157, 0.72534, 0.73897, 0.75243, 0.76572, 0.7788, 0.79167, 0.8043, 0.81669, 0.8288, 0.84062, 0.85214, 0.86334, 0.8742, 0.88471, 0.89484, 0.90459, 0.91393, 0.92286, 0.93136, 0.93941, 0.94701, 0.95414, 0.96079, 0.96695, 0.9726, 0.97775, 0.98238, 0.98648, 0.99005, 0.99308, 0.99557, 0.9975, 0.99889, 0.99972, 1, 0.99972, 0.99889, 0.9975, 0.99557, 0.99308, 0.99005, 0.98648, 0.98238, 0.97775, 0.9726, 0.96695, 0.96079, 0.95414, 0.94701, 0.93941, 0.93136, 0.92286, 0.91393, 0.90459, 0.89484, 0.88471, 0.8742, 0.86334, 0.85214, 0.84062, 0.8288, 0.81669, 0.8043, 0.79167, 0.7788, 0.76572, 0.75243, 0.73897, 0.72534, 0.71157, 0.69768, 0.68367, 0.66958, 0.65541, 0.64118, 0.62691, 0.61263, 0.59833, 0.58404, 0.56978, 0.55556, 0.54139, 0.52729, 0.51327, 0.49935, 0.48554, 0.47184, 0.45828, 0.44486, 0.43159, 0.41849, 0.40555, 0.3928, 0.38024, 0.36788, 0.35572, 0.34377, 0.33204, 0.32053, 0.30925, 0.2982, 0.28738, 0.2768, 0.26647, 0.25638, 0.24653, 0.23693, 0.22757, 0.21847, 0.20961, 0.201, 0.19264, 0.18452, 0.17665, 0.16901, 0.16162, 0.15447, 0.14755, 0.14086, 0.1344, 0.12816, 0.12215, 0.11635, 0.11077, 0.1054, 0.10023, 0.095263, 0.090491, 0.085911, 0.081517, 0.077305, 0.073269, 0.069406, 0.06571, 0.062177, 0.0588, 0.055576, 0.0525, 0.049566, 0.046771, 0.044108, 0.041574, 0.039164, 0.036873, 0.034697, 0.032631, 0.030671, 0.028813, 0.027052, 0.025385, 0.023807, 0.022315, 0.020905, 0.019573, 0.018316, 0.01713, 0.016012, 0.014958, 0.013966, 0.013033, 0.012155, 0.01133, 0.010555, 0.0098282, 0.0091459, 0.0085063, 0.0079071, 0.0073459, 0.0068208, 0.0063297, 0.0058707, 0.005442, 0.0050418, 0.0046684, 0.0043202, 0.0039958, 0.0036938, 0.0034126, 0.0031511, 0.002908, 0.0026822, 0.0024726, 0.002278, 0.0020976, 0.0019305
};
int SLOWER_frequency(SLOWER_FREQUENCY), SLOWER_period(0), SLOWER_amplitude(SLOWER_AMPLITUDE), SLOWER_index(0);
int SLOWER_size = sizeof(SLOWER_motion) / sizeof(SLOWER_motion[0]);
boolean SLOWER_state = false;
volatile boolean hz100_flag(false), hz20_flag(false);
long elapsed_time = 0;
int offset(OFFSET), scaling_factor(1);
/* FIR Variables */
double FIR_output(0), FIR_input(0);
float FIR_gain(1);
float FIR_coef[FILTERTAPS] = {
0.065712, -0.017099, -0.016271, -0.0161, -0.016267, -0.016425, -0.016262, -0.015493, -0.013866, -0.011182, -0.0073126, -0.0022071, 0.0041003, 0.011493, 0.019777, 0.028693, 0.037929, 0.04714, 0.055962, 0.064028, 0.070981, 0.076483, 0.080236, 0.082223, 0.082223, 0.080236, 0.076483, 0.070981, 0.064028, 0.055962, 0.04714, 0.037929, 0.028693, 0.019777, 0.011493, 0.0041003, -0.0022071, -0.0073126, -0.011182, -0.013866, -0.015493, -0.016262, -0.016425, -0.016267, -0.0161, -0.016271, -0.017099, 0.065712
};
void setup() {
Serial.begin(115200);
/* Initialize Timers */
startTimer(TC1, 0, TC3_IRQn, 100);
startTimer(TC1, 2, TC5_IRQn, 20);
/* Determining the period from BPM */
FASTER_period = roundUP(60 / (double)FASTER_frequency * 1000, 10);
SLOWER_period = roundUP(60 / (double)SLOWER_frequency * 1000, 10);
analogWriteResolution(12);
analogReadResolution(12);
/* Initialize FIR */
fir.setCoefficients(FIR_coef);
fir.setGain(FIR_gain);
}
void loop() {
/* Controlling indexing of waveforms */
if (elapsed_time % (int)FASTER_period == 0) {
FASTER_index = 0;
FASTER_state = true;
}
if (elapsed_time % (int)SLOWER_period == 0) {
SLOWER_index = 0;
SLOWER_state = true;
}
if (FASTER_index >= FASTER_size - 1)
FASTER_state = false;
if (SLOWER_index >= SLOWER_size - 1)
SLOWER_state = false;
/* Timer @ 100 Hz - indexing through waveform arrays */
if (hz100_flag) {
hz100_flag = false;
if (FASTER_state)
FASTER_index++;
if (SLOWER_state)
SLOWER_index++;
elapsed_time += 10;
}
FIR_input = scaling_factor * (FASTER_amplitude * FASTER_motion[FASTER_index] + SLOWER_amplitude * SLOWER_motion[SLOWER_index] + offset);
/* Timer @ 20 Hz - calculating FIR */
if (hz20_flag) {
FIR_output = fir.process(FIR_input);
serial_output();
hz20_flag = false;
}
}
Additional functions:
void serial_output() {
Serial.print(FIR_input); Serial.print('\t');
Serial.println(FIR_output);
}
int roundUP(int numToRound, int multiple) {
if (multiple == 0)
return numToRound;
int remainder = numToRound % multiple;
if (remainder == 0)
return numToRound;
return numToRound + multiple - remainder;
}
void TC3_Handler() {
TC_GetStatus(TC1, 0);
hz100_flag = true;
}
void TC5_Handler() {
TC_GetStatus(TC1, 2);
hz20_flag = true;
}
void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)irq);
TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
uint32_t rc = VARIANT_MCK / 128 / frequency; //128 because we selected TIMER_CLOCK4 above
TC_SetRA(tc, channel, rc / 2); //50% high, 50% low
TC_SetRC(tc, channel, rc);
TC_Start(tc, channel);
tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS;
tc->TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS;
NVIC_EnableIRQ(irq);
}
Here is the expected filtered output (Simulink) from the same input and this is what I am getting.
I am convinced am using this library incorrectly, I have some experience designing and testing discrete Fourier transforms in MATLAB then implementing them in Arduino - I have tested the FIR filter however this is the first time I am implementing it.
Any help would be much appreciated, thank you.
EDIT: Added entire working code.