**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 );

// 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

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

// 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

// 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

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**