{"id":19554121,"url":"https://github.com/berndporr/fir1","last_synced_at":"2025-07-27T22:40:06.287Z","repository":{"id":8787697,"uuid":"10478032","full_name":"berndporr/fir1","owner":"berndporr","description":"FIR \u0026 LMS filter implementation in C++ with Python \u0026 JAVA wrappers","archived":false,"fork":false,"pushed_at":"2024-04-15T13:38:50.000Z","size":6922,"stargazers_count":79,"open_issues_count":1,"forks_count":23,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-14T23:41:25.806Z","etag":null,"topics":["c-plus-plus","cmake","coefficients","digital-signal-processing","filter","fir","java","jni-android","jni-java","jni-wrapper","learning-rate","least-squares","leastsquares","library","lms","python-extension","python-library","python3","signal-processing"],"latest_commit_sha":null,"homepage":"http://www.youtube.com/channel/UCf-VdHm0OyV_TKD5BU9yIXw","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"qinwf/awesome-R","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/berndporr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-06-04T12:28:57.000Z","updated_at":"2025-04-10T17:21:47.000Z","dependencies_parsed_at":"2024-04-15T13:55:30.512Z","dependency_job_id":null,"html_url":"https://github.com/berndporr/fir1","commit_stats":{"total_commits":182,"total_committers":4,"mean_commits":45.5,"dds":"0.18681318681318682","last_synced_commit":"9d9416a84e7443605e6a24d276dcb85ceb87a17d"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berndporr%2Ffir1","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berndporr%2Ffir1/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berndporr%2Ffir1/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berndporr%2Ffir1/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/berndporr","download_url":"https://codeload.github.com/berndporr/fir1/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252887415,"owners_count":21819894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-plus-plus","cmake","coefficients","digital-signal-processing","filter","fir","java","jni-android","jni-java","jni-wrapper","learning-rate","least-squares","leastsquares","library","lms","python-extension","python-library","python3","signal-processing"],"created_at":"2024-11-11T04:25:59.737Z","updated_at":"2025-05-07T13:34:13.667Z","avatar_url":"https://github.com/berndporr.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FIR1\n\n![alt tag](fir.png)\n\nAn efficient finite impulse response (FIR) filter class\nin C++ and Python wrapper.\n\nThe FIR filter class offers also adaptive filtering\nusing the least mean square (LMS) or normalised least mean\nsquare (NLMS) algorithm.\n\n\n## Installation\n\n### Packages for Ubuntu LTS\n\nAdd this repository to your package manager:\n```\nsudo add-apt-repository ppa:berndporr/dsp\nsudo apt-get update\nsudo apt install fir1\nsudo apt install fir1-dev\n```\nThis adds `fir1-dev` and `fir1` to your package list.\nThe demo files are in `/usr/share/doc/fir1-dev`. Copy them into a working\ndirectory, type `gunzip *.gz`, `cmake .` and `make`.\n\n### Linux / Unix / MACOSX: compilation from source\n\nThe build system is `cmake`. Install the library with\nthe standard sequence:\n```\ncmake .\nmake\nsudo make install\nsudo ldconfig\n```\nor for debugging run cmake with:\n```cmake -DCMAKE_BUILD_TYPE=\"Debug\" .```\nBy default optimised release libraries are generated.\n\n### Windows\n\nUnder windows only the static library is generated which\nshould be used for your code development.\n\nFor example for Visual Studio 2019 you write:\n```\ncmake -G \"Visual Studio 16 2019\" -A x64 .\n```\nand then start Visual C++ and compile it. Usually\nyou want to compile both the release and debug\nlibraries because they are not compatible to each\nother under Windows.\n\n### Python\n\n#### Installation from the python package index (PyPi)\n\nWindows / Linux / Mac\n```\npip install fir1\n```\nunder Windows it might be just `pip` for python3.\n\n\n#### Installation from source\n\nWindows / Linux / Mac: make sure that you have swig and a C++ compiler installed. Then type:\n```\npython setup.py install\n```\n\n\n## How to use it\n\n### cmake\n\nAdd to your `CMakeLists.txt` either\n\n```\ntarget_link_libraries(myexecutable fir)\n```\nfor the dynamic library or\n\n```\ntarget_link_libraries(myexecutable fir_static)\n```\nfor the statically linked library.\n\nYou can also use `find_package(fir)`.\n\n### Generating the FIR filter coefficients\n\nSet the coefficients either with a C floating point array or\nwith a text file containing the coefficients. The text file or\nthe floating point array with the\ncoefficients can easily be generated by Python or OCTAVE/MATLAB:\n\n#### Python\nUse the `firwin` command to generate the coefficients:\n```\n# Sampling rate\nfs = 1000\n# bandstop between 45 and 55 Hz:\nf1 = 45\nf2 = 55\nb = signal.firwin(999,[f1/fs*2,f2/fs*2])\n```\n\n#### octave/MATLAB:\n```\noctave:1\u003e h=fir1(100,0.1);\n```\nwhich creates the coefficients of a lowpass filter with 100 taps\nand normalised cutoff 0.1 to Nyquist.\n\n### Initialisation\n\n#### C++ floating point FIR filter:\n```\nFir1 fir(\"h.dat\");\n```\nor import the coefficients as a const double array:\n```\nFir1 fir(coefficients)\n```\nthere is also an option to import a non-const array (for example\ngenerated with the ifft) and using std::vector. You can also\ncreate a moving average filter by initialising all coefficients\nwith a constant value:\n```\nFir1 moving_average(100,1.0/100);\n```\n\n#### Python\n```\nf = fir1.Fir1(coeff)\n```\n\n### Realtime filtering\n\n#### C++ double:\n```\ndouble b = fir.filter(a);\n```\n\n#### Python\n```\nb = f.filter(a)\n```\n### Utility methods\n\nThese functions are the same in C++ and Python:\n\n+ `getTaps()` returns the length of the FIR filter kernel.\n+ `reset()` sets all delay lines to zero.\n+ `zeroCoeff()` sets all coefficients to zero.\n\nRetreiving the coefficients/kernel from the FIR filter is different depending on the\nlanguage used:\n\n#### C++\n+ `void getCoeff(double* target, unsigned length) const` copies the FIR kernel into the\ngiven C array of `double`s with length `length`.\n\n   If `length` exceeds the length of the filter kernel, the result is zero-padded to fill\nthe given array.\n\n   If `length` is smaller than the filter kernel, a `std::out_of_range` exception is thrown.\n   \n+ `std::vector\u003cdouble\u003e getCoeffVector() const` returns a copy of the filter kernel.\n\n#### Python\n+ `getCoeff(n : int) -\u003e numpy.array` as per the C++ method, following the zero-padding\nand exception-throwing behaviour of the C++. The returned array will have `n` elements.\n+ `getCoeff() -\u003e numpy.array` additional to the C++ methods, this returns an numpy array\nwhich is a copy of the filter kernel. This is probably the default use case in Python.\n\n\n## LMS algorithm\n\n![alt tag](fir_lms.png)\n\nThe least mean square algorithm adjusts the FIR coefficients h_m\nwith the help of an error signal e(n):\n```\nh_m(n+1) = h_m(n) + learning_rate * h_m(n) * e(n)\n```\nusing the function `lms_update(e)` while performing\nthe filtering with `filter()`.\n\n### How to use the LMS filter\n\n- Construct the Fir filter with all coefficients set to zero: `Fir1(nCoeff)`\n- Set the learning_rate with the method `setLearningRate(learning_rate)`.\n- Provide the input signal `x` to the FIR filter and use its standard `filter` method to filter it.\n- Define your error which needs to be minimised: `e = d - y`\n- Feed the error back into the filter with the method `lms_update(e)`.\n\nThe `lmsdemo` in the demo directory makes this concept much clearer how to remove\nartefacts with this method.\n\n![alt tag](lms.png)\n\nThe above plot shows the filter in action which removes 50Hz noise with the adaptive\nfilter. Learning is very fast and the learning rate here is deliberately kept low to\nshow how it works.\n\n### Stability\n\nThe FIR filter itself is stable but the error signal changes the filter coefficients which\nin turn change the error and so on. There is a rule of thumb that the learning rate\nshould be less than the \"tap power\" of the input signal which is just the sum of all\nsquared values held in the different taps:\n```\nlearning_rate \u003c 1/getTapInputPower()\n```\nThat allows an adaptive learning rate which is called \"normalised LMS\". From\nmy experiments that works in theory but in practise the realtime value of getTapInputPower()\ncan make the algorithm easily unstable because it might suggest infinite learning rates\nand can fluctuate wildly. A better approach is to keep the learning rate constant and rather\ncontrol the power of the input signal by, for example, normalising the input signal or limiting\nit.\n\nSee the demo below which removes 50Hz from an ECG which uses a normalised 50Hz signal\nwhich guarantees stability by design.\n\n\n### Python\n\nThe commands under JAVA and Python are identical to C++.\n\n\n## Demos\nDemo programs are in the \"demo\" directory which show how to use the\nfilter.\n1. `firdemo` sends an impulse into the filter and you should see the impulse\nresponse at its output.\n2. `lmsdemo` filters out 50Hz noise from an ECG with the help of\nadaptive filtering by using the 50Hz powerline frequency as the input\nto the filter. This can be replaced by any reference artefact signal\nor signal which is correlated with the artefact.\n3. filter_ecg.py performs the filtering of an ECG in python using\nthe fir1 python module which in turn calls internally the C++ functions.\n\n## C++ documentation\n\nThe doxygen generated documentation can be found here:\n\n - Online: http://berndporr.github.io/fir1/index.html\n - PDF: https://github.com/berndporr/fir1/tree/master/docs/pdf\n\n## Unit tests\nUnder C++ just run `make test` or `ctest`.\n\n\n## Credits\n\nThis library has been adapted form Graeme Hattan's original C code.\n\nEnjoy!\n\nBernd Porr \u0026 Nick Bailey\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberndporr%2Ffir1","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fberndporr%2Ffir1","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberndporr%2Ffir1/lists"}