Constrained Least-Squares Linear-Phase (CLSLP) FIR Filter Synthesis
Introduction 1
The Constrained Least-Squares Linear-Phase (CLSLP) FIR Filter algorithm is a computationally efficient filter synthesis technique distributed here in the form of C++ source code. CLSLP is not a universal acronym however and is used here as a convenient moniker and as the name of the associated software package. CLSLP LPFIR (Linear-Phase FIR) filters are, as the name implies, linear phase. This means that CLSLP phase response is essentially a constant time delay of (N-1)/2 samples where N is the number of filter coefficients. It's easy to show that cascaded linear-phase FIR filters are themselves linear phase so this quality is preserved even in arbitrarily large filtering networks when this discipline is employed. With the inherent speed of the CLSLP Filter Synthesis Algorithm exotic processing schemes such as adaptive filtering systems are practical even on modest processing platforms.
Examples of CLSLP Algorithm Use
The following software snippets should illustrate use of this software by example. This download includes a full source-code build and test in subdirectory CLSLP/ as well as source-code builds for the examples used in the documentation. This code, as it's written in C++20, should run on most major operating systems. Additionally, if the CPU supports the SIMD instruction set, then the release mode (-O3) build should employ it if possible.
Example 1
Example 1 is a 501-coefficient, real-valued, linear-phase FIR (LPFIR) filter synthesized by CLSLP in 1.65 ms, or about 3.30 μS/coef, on a standard desktop PC. The C++ commands used to synthesize this real-valued CLSLP filter are
#include "CLSLP.h"
CLSLP Filt;
Filt.SetGain(1);
// Optional Call. Gain is set to '1' by default. All
amplitudes multiplied by this quantity.
// AddSymmetric is for Real-Valued Filter Coefficients ([0,.5] mod
1.) Normalized Hz
// Add
is for Complex-Valued Filter Coefficients ([0,1.) mod 1.) Normalized
Hz
// Type
F0 F1 A0 A1 AuxMult
Filt.AddSymmetric( CLSLP::eLin_AbsErr, .0, .1,
0.00, 0.00, 1
);
Filt.AddSymmetric( CLSLP::eExp_AbsErr, .1, .4,
1e-4, 1.00, 1
);
Filt.AddSymmetric( CLSLP::eLin_AbsErr, .4, .5,
0.00, 0.00, 1
);
// A common reason why a filter solution cannot be determined is
that the frequency interval from 0 to 1 is not
// completely covered by the optimization statements
(above). This can be inconvenient during development
// so a simple work around is to add the following
statement to your code.
This statement draws all frequencies
// to zero in with a low weight as
default.
Filt.Add(
CLSLP::eLin_AbsErr, 0.00, 1.00, 0.00, 0.00, 1e-16 );
// Calculate the FIR Filter Coefficientsvector<complex<double>>
OutCoefs(501);
// The real-valued filter
coefficients are the real part of OutCoefs below. The imaginary
parts should be near zero.
vector<complex<double>>
OutCoefs(501);
const bool Valid = Filt.GenFilter( 501,
OutCoefs ); // 'Valid'
flag true if valid solution found
The amplitude response of this filter is
Example 2
Example 2 is a 1024-coefficient, complex-valued, linear-phase FIR (LPFIR) filter synthesized by CLSLP in 6.01 ms, or about 5.87 μS/coef, on a standard desktop PC. The C++ commands used to synthesize this CLSLP filter are
#include "CLSLP.h"
CLSLP Filt;
Filt.SetGain(1);
// Optional Call. Gain is set to '1' by default. All
amplitudes multiplied by this quantity.
// AddSymmetric is for Real-Valued Filter Coefficients ([0,.5] mod
1.) Normalized Hz
// Add
is for Complex-Valued Filter Coefficients ([0,1.) mod 1.) Normalized
Hz
//
Type F0 F1
A0 A1 AuxMult
Filt.Add( CLSLP::eExp_AbsErr, -0.50, -0.20, 0.00, 0.00, 1.0 );
Filt.Add( CLSLP::eExp_AbsErr, -0.20, -0.19, 1.00, 1.00, 1.0 );
Filt.Add( CLSLP::eExp_AbsErr, -0.19, 0.10, 0.00, 0.00, 1.0 );
Filt.Add( CLSLP::eExp_AbsErr , 0.10, 0.20, 1.00, 1.00, 1.0 );
Filt.Add( CLSLP::eExp_AbsErr, 0.20, 0.40, 0.00, 0.00, 1.0 );
Filt.Add( CLSLP::eExp_AbsErr, 0.40, 0.50, 0.00, 0.00, 1.0 );
// A common reason why a filter solution cannot be determined is
that the frequency interval from 0 to 1 is not
// completely covered by the optimization statements
(above). This can be inconvenient during development
// so a simple work around is to add the following
statement to your code.
This statement draws all frequencies
// to zero in with a low weight
as default.
Filt.Add(
CLSLP::eLin_AbsErr, 0.00, 1.00, 0.00, 0.00, 1e-16 );
// Calculate the FIR Filter Coefficients
vector<complex<double>>
OutCoefs(1024);
// The real-valued filter
coefficients are the real part of OutCoefs below. The imaginary
parts should be near zero.
const bool Valid = Filt.GenFilter(
1024, OutCoefs );
// 'Valid' flag true if valid solution found
The amplitude response of this filter is
Example 3
Example 3 is a 2049-coefficient, complex-valued, linear-phase FIR (LPFIR) filter synthesized by CLSLP in 21.31 ms, or about 11.85 μS/coef, on a standard desktop PC. The C++ commands used to synthesize this CLSLP filter are
#include "CLSLP.h"
CLSLP Filt;
Filt.SetGain(1);
// Optional Call. Gain is set to '1' by default. All
amplitudes multiplied by this quantity.
// AddSymmetric is for Real-Valued Filter Coefficients ([0,.5]
mod 1.) Normalized Hz
// Add
is for Complex-Valued Filter Coefficients ([0,1.) mod 1.)
Normalized Hz
//
Type F0
F1 A0 A1 AuxMult
Filt.Add(
CLSLP::eLin_AbsErr, 0.00, 0.10, 0.00, 0.00, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.10, 0.20, 1.00, 1e5, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.20, 0.30, 0.00, 0.00, 1.00 );
Filt.Add( CLSLP::eExp_AbsErr, 0.30, 0.40, 1e5, 1.00, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.40, 0.50, 0.00, 0.00, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.50, 0.60, 0.00, 0.00, 1.00 );
Filt.Add( CLSLP::eLin_RelErr, 0.60, 0.70, 1.00, 1e5, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.70, 0.80, 0.00, 0.00, 1.00 );
Filt.Add( CLSLP::eExp_RelErr, 0.80, 0.90, 1e5, 1.00, 1.00 );
Filt.Add( CLSLP::eLin_AbsErr, 0.90, 1.00, 0.00, 0.00, 1.00 );
// A common reason why a filter solution cannot be determined is
that the frequency interval from 0 to 1 is not
// completely covered by the optimization statements
(above). This can be inconvenient during development
// so a simple work around is to add the following
statement to your code.
This statement draws all frequencies
// to zero in with a low weight
as default.
Filt.Add(
CLSLP::eLin_AbsErr, 0.00, 1.00, 0.00, 0.00, 1e-16 );
// Calculate the FIR Filter Coefficients
vector<complex<double>>
OutCoefs(2049);
// The real-valued filter
coefficients are the real part of OutCoefs below. The imaginary
parts should be near zero.
bool Valid = Filt.GenFilter( 2049, OutCoefs );
// 'Valid' flag true if valid solution found
The amplitude response of this filter is
CLSLP Software Download
The zip file below contains a sample filter synthesis example as well as all the source code to embed CLSLP in your own design. This software is written in the C++20 language and should run on any major operating system (new C++ feature). A standard CMAKE build scheme is used. Sample Linux command files are included if you need a refresher on CMAKE builds. This download should have everything you need to embed this algorithm for your own application. All this code is released on a Standard BSD 3-Clause License.
— No Binary Files — Source Code and Linux Build Scripts —
>> Download CLSLP C++ CLSLP-20240928-1409 <<
NOTE: A common mistake in using the CLSLP algorithm is leaving frequency intervals undefined. The CLSLP algorithm is fast though at the expense of testing for errors such as undefined frequency intervals. A safe workaround for this problem is to add a statement such as the one below to your list of filter segments.
Filt.Add( CLSLP::eLin_AbsErr, 0.00, 1.00, 0.00, 0.00, 1e-16 ); // Draw all frequencies down to zero in with a low weight
This will draw all frequency intervals toward zero with a low weight making zero the default value. Check out the documentation below. Best of luck!
The Original Asilomar Paper on CLSLP Algorithm [most math detail]
During the conversion of the MATLAB code to C++, an error was made in converting the indices from MATLAB's 1-based to the C++ 0-based numbering. Change 'n' to '(n-1)' in example equations (30), (33), (37), (39), (41), and (43).
Additional Documentation
Documentation for CLSLP is provided in either of the two documents below. The first file is a basic PDF file describing the algorithm in general in general terms
CLSLP FIR Filter Synthesis.pdf
This second document contains the same information as the first document plus additional material: the published paper on the technique (math details), annotated C++ listings, as well as filter synthesis examples
CLSLP FIR Filter Synthesis Software and Docs.pdf