Cookie Consent by Free Privacy Policy Generator CLSLP Tools
WEJC: Embedded Engineering Solutions [WEJC.COM]

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).

Constrained Least-Squares Design and Characterization of Affine Phase Complex FIR Filters (CLSLP).pdf

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