├── .gitignore ├── LICENSE ├── README.md ├── examples ├── determining_gain │ └── determining_gain.ino ├── moving_average │ └── moving_average.ino └── multiple_filters │ └── multiple_filters.ino ├── library.properties └── src ├── FIR.h └── FIR.tpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 John Leeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FIR Filter Arduino Library 2 | 3 | [![Say Thanks](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/leemangeo) 4 | 5 | A flexible FIR filter for the Arduino or other CPP micro. This was inspired by 6 | [Sebastian Nilsson's](https://github.com/sebnil) 7 | [FIR library](https://github.com/sebnil/FIR-filter-Arduino-Library) 8 | , but is a more generalized implementation that can be used with multiple data 9 | types. There is also an extensive example gallery for those not familiar with 10 | class templates or FIR filtering. 11 | 12 | This library does not calculate the filter coefficients for you, but the online 13 | [T-Filter](http://t-filter.engineerjs.com) tool does an excellent job. 14 | 15 | ## Installation 16 | Checkout the [Arduino library installation page](https://www.arduino.cc/en/Guide/Libraries) for details on how to install this library. 17 | 18 | ## Examples 19 | * **[determining_gain](examples/determining_gain)** - Demonstrates how to 20 | determine the appropriate gain for the filter. 21 | * **[moving_average](examples/moving_average)** - The FIR filter can be used as 22 | an efficient moving average filter. 23 | * **[multiple_filters](examples/multiple_filters)** - Shows how to construct and 24 | use multiples filters in a single sketch. 25 | 26 | ## License 27 | This software is licensed under the [MIT license](LICENSE). If you find the 28 | code useful, feel free to drop a note and 29 | [say thanks](https://saythanks.io/to/leemangeo) or consider using 30 | [Leeman Geophysical LLC](https://www.leemangeophysical.com) for your next 31 | project! 32 | -------------------------------------------------------------------------------- /examples/determining_gain/determining_gain.ino: -------------------------------------------------------------------------------- 1 | // Gain Setting Example 2 | // Demonstrates the filter response with unity input to 3 | // get the appropriate value for the filter gain setting. 4 | 5 | #include 6 | 7 | // Make an instance of the FIR filter. In this example we'll use 8 | // floating point values and a 13 element filter. 9 | FIR fir; 10 | 11 | void setup() { 12 | Serial.begin(115200); // Start a serial port 13 | 14 | // Use an online tool to get these such as http://t-filter.engineerjs.com 15 | // This filter rolls off after 2 Hz for a 10 Hz sampling rate. 16 | long coef[13] = { 660, 470, -1980, -3830, 504, 10027, 15214, 17 | 10027, 504, -3830, -1980, 470, 660}; 18 | 19 | // Set the coefficients 20 | fir.setFilterCoeffs(coef); 21 | 22 | // The filter automatically determines the gain 23 | Serial.print("Automatically calculated gain: "); 24 | Serial.println(fir.getGain()); 25 | 26 | // Set the gain to 1 to find the actual gain. 27 | // After running this sketch you'll see the gain 28 | // value sould be 26916. 29 | Serial.println("Setting gain to 1 to show how to manually determine gain"); 30 | long gain = 1; 31 | fir.setGain(gain); 32 | } 33 | 34 | void loop() { 35 | // Need to run at least the length of the filter. 36 | for (float i=0; i < 14; i++) { 37 | Serial.print("Iteration "); 38 | Serial.print(i+1); 39 | Serial.print(" -> "); 40 | Serial.println(fir.processReading(1)); // Input all unity values 41 | } 42 | 43 | while (true) {}; // Spin forever 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/moving_average/moving_average.ino: -------------------------------------------------------------------------------- 1 | // Moving Average Example 2 | // Shows how to use an FIR filter as a moving average on a simple 3 | // set of data that can be easily verified by hand. 4 | 5 | #include 6 | 7 | // Make an instance of the FIR filter. In this example we'll use 8 | // floating point values and an 8 element filter. For a moving average 9 | // that means an 8 point moving average. 10 | FIR fir; 11 | 12 | void setup() { 13 | Serial.begin(115200); // Start a serial port 14 | 15 | // For a moving average we want all of the coefficients to be unity. 16 | float coef[8] = { 1., 1., 1., 1., 1., 1., 1., 1.}; 17 | 18 | // Set the coefficients 19 | fir.setFilterCoeffs(coef); 20 | 21 | Serial.print("Gain set: "); 22 | Serial.println(fir.getGain()); 23 | } 24 | 25 | void loop() { 26 | // Calculate the moving average for a time series with the elements. 27 | // 0, 1, 2, ...., 13, 14, 15 28 | for (float i=0; i < 16; i++) { 29 | Serial.println(fir.processReading(i)); 30 | } 31 | 32 | while (true) {}; // Spin forever 33 | } 34 | 35 | -------------------------------------------------------------------------------- /examples/multiple_filters/multiple_filters.ino: -------------------------------------------------------------------------------- 1 | // Multiple Filters Example 2 | // Show how to make multiple filter objects in a single sketch. 3 | 4 | #include 5 | 6 | // Make two instances of the FIR filter. The first is a 13 element float 7 | // and the second is a 10 point float. The 13 element filter is a 2 Hz low-pass 8 | // for a 10 SPS signal and the 10 element is a moving average over one second. 9 | 10 | FIR fir_lp; 11 | FIR fir_avg; 12 | 13 | // Some data that is a 0.2 Hz sine wave with a 0.3 Hz sine wave noise on it. 14 | float data[51] = { 0.2, -0.01757378, 0.25261 , 0.50501472, 0.28169386, 15 | 0.73591371, 0.67214444, 0.63916107, 1.04340099, 0.75089683, 16 | 0.97104406, 1.1070092 , 0.79944834, 1.15681282, 0.95424177, 17 | 0.83291634, 1.1026499 , 0.68138434, 0.80743739, 0.79725188, 18 | 0.39288974, 0.65097057, 0.32531128, 0.14508086, 0.32099195, 19 | -0.17069916, -0.07172355, -0.14896219, -0.55850617, -0.30387694, 20 | -0.64596943, -0.77404044, -0.57958674, -1.0231335 , -0.8365037 , 21 | -0.86670651, -1.16875508, -0.81455928, -1.07313216, -1.05899594, 22 | -0.76797161, -1.09233578, -0.76336743, -0.70354745, -0.86712553, 23 | -0.40092413, -0.57401086, -0.4319664 , -0.07473948, -0.32007654, 24 | 0.0936594}; 25 | 26 | void setup() { 27 | Serial.begin(115200); // Start a serial port 28 | 29 | // Use an online tool to get these such as http://t-filter.engineerjs.com 30 | // This filter rolls off after 2 Hz for a 10 Hz sampling rate. 31 | float coef_lp[13] = { 660, 470, -1980, -3830, 504, 10027, 15214, 32 | 10027, 504, -3830, -1980, 470, 660}; 33 | 34 | // For a moving average we use all ones as coefficients. 35 | float coef_avg[10] = {1., 1., 1., 1., 1., 1., 1., 1., 1., 1.}; 36 | 37 | // Set the coefficients 38 | fir_lp.setFilterCoeffs(coef_lp); 39 | fir_avg.setFilterCoeffs(coef_avg); 40 | 41 | // Set the gain 42 | Serial.print("Low Pass Filter Gain: "); 43 | Serial.println(fir_lp.getGain()); 44 | Serial.print("Moving Average Filter Gain: "); 45 | Serial.println(fir_avg.getGain()); 46 | } 47 | 48 | void loop() { 49 | // Run through our simulated data in "real time" and compare the outputs. 50 | // You can paste the output in your favorite graphing program if you want 51 | // to see how the different filters modify the output. 52 | 53 | Serial.println("Input, Moving_Avg, Lowpass"); 54 | 55 | for (int i=0; i < 51; i++) { 56 | Serial.print(data[i]); 57 | Serial.print(", "); 58 | Serial.print(fir_avg.processReading(data[i])); 59 | Serial.print(", "); 60 | Serial.println(fir_lp.processReading(data[i])); 61 | delay(100); // Simulate real-time 10 Hz data collection 62 | } 63 | 64 | while (true) {}; // Spin forever 65 | } 66 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=FIR filter 2 | version=0.1.1 3 | author=Leeman Geophysical LLC 4 | maintainer=John R. Leeman 5 | sentence=FIR filter library for the Arduino. 6 | paragraph=Flexible FIR filter library. Multiple data types accepted. Based upon the work of Sebastian Nilsson (sebnil). 7 | category=Data Processing 8 | url=https://github.com/LeemanGeophysicalLLC/FIR_Filter_Arduino_Library 9 | architectures=* 10 | dot_a_linkage=false 11 | -------------------------------------------------------------------------------- /src/FIR.h: -------------------------------------------------------------------------------- 1 | #ifndef FIR_h 2 | #define FIR_h 3 | 4 | template 5 | class FIR { 6 | public: 7 | FIR(); 8 | void setGain(T newgain); 9 | T getGain(); 10 | void setFilterCoeffs(T *coeffs); 11 | T processReading(T newval); 12 | 13 | private: 14 | T values[ntaps]; 15 | T fir_coeffs[ntaps]; 16 | T gain; 17 | int k; 18 | }; 19 | 20 | #include "FIR.tpp" 21 | #endif 22 | -------------------------------------------------------------------------------- /src/FIR.tpp: -------------------------------------------------------------------------------- 1 | template 2 | FIR::FIR() { 3 | k = 0; 4 | memset(values, 0, sizeof(values)); 5 | } 6 | 7 | template 8 | void FIR::setGain(T newgain) { 9 | gain = newgain; 10 | } 11 | 12 | template 13 | T FIR::getGain() { 14 | return gain; 15 | } 16 | 17 | template 18 | void FIR::setFilterCoeffs(T *coeffs) { 19 | memcpy(&fir_coeffs, coeffs, sizeof(fir_coeffs)); 20 | 21 | // Automatically calculate the gain needed by sending unity inputs for at 22 | // least the legnth of the filter. 23 | T input = 1; 24 | T new_gain; 25 | gain = 1; 26 | for (int i=0; i::processReading(input); 28 | } 29 | 30 | // Cleanup by zeroing out the data and index for the filter 31 | k = 0; 32 | for (int i=0; i::setGain(new_gain); // Set the new gain 37 | } 38 | 39 | template 40 | T FIR::processReading(T newval){ 41 | T output = 0; 42 | values[k] = newval; 43 | 44 | for (int i=0; i