There are 2 types of Chebychev filters: Type 1 (the one that is covered in this article) is a filter type that allows some ripple ε in the pass band and therefore reaches a very steep slope towards the stop band. The transfer function of a Chebychev low pass filter of 4. Order looks like:
(Type 2 is the filter type that has some ripple in the stop band)
This steep slope is achieved by the transfer function
With TN(x) as a Chebyshev polynomial (see Integration by the use of Legendre or Chebychev polynomials) and ε as the ripple factor.
The Chebychev polynomial can be expressed as
Now, to implement a digital filter the above formulation must be transformed into a form like:
And therefore Chebychev first calculates the poles of his transfer function
They are there where
This is a complex equation and it has the solution
And with this the transfer function becomes
With V as an amplification vactor that has to be determined as next step. To do this we have to distinguish between the cases N is odd and N is even.
If N is even and we insert the Chebychev polynomial of an even order we get the transfer function
If s = 0 this equation is
But the transfer function we got with our poles gives for s = 0
That means V must be
If N is even.
If N is odd we have
And if s = 0
And so
So, if N is even the final transfer function becomes
and if N is odd
This part with the amplification is often missing in the literature. There they describe an algorithm which is not really correct and finally feed everything into MATLAB…And MATLAB just solves the problem
In the literature there is often a form like
Used for the transfer function. That comes from the fact that the poles are always conjugate complex. The poles are lying on the left side of an ellipse like
and we always have conjugate complex pairs. Her for instance for N = 4
and these pairs are multiplied together like
and so
If N is odd we get a little different situation like
One pole is pure real here and we get
and
For both even N and odd N.
This finally implemented in a c# function is
public void CalcChebychev(int order, double t)
{
int i = 1;
double[] poly = new double[3];
double[] poly2 = new double[2];
double nu = Math.Log(1.0 / e + Math.Sqrt(1.0 / e * e + 1.0)) / order;
double sigma;
double omega;
a_s = new double[1];
a_s[0] = 1.0;
b_s = 1.0;
// The denominator of the transfer function
if (order % 2 == 0)
{
poly[0] = 1.0;
for (i = 0; i < order / 2; i++)
{
sigma = Math.Sinh(nu) * Math.Sin(Math.PI * (2.0 * i + 1.0) / 2.0 / order);
omega = Math.Cosh(nu) * Math.Cos(Math.PI * (2.0 * i + 1.0) / 2.0 / order);
poly[1] = 2.0 * sigma;
poly[2] = sigma * sigma + omega * omega;
a_s = Poly.Mult(a_s, poly);
// the enumerator part
b_s = b_s * sigma * sigma + omega * omega;
}
}
else
{
poly[0] = 1.0;
for (i = 0; i < (order + 1) / 2; i++)
{
sigma = -Math.Sinh(nu) * Math.Sin(Math.PI * (2.0 * i + 1.0) / 2.0 / order);
omega = Math.Cosh(nu) * Math.Cos(Math.PI * (2.0 * i + 1.0) / 2.0 / order);
if (i < (order) / 2)
{
poly[1] = -2.0 * sigma;
poly[2] = sigma * sigma + omega * omega;
a_s = Poly.Mult(a_s, poly);
// the enumerator part
b_s = b_s * sigma * sigma + omega * omega;
}
else
{
poly2[0] = 1;
poly2[1] = -sigma;
a_s = Poly.Mult(a_s, poly2);
// the enumerator part
b_s = b_s * sigma;
}
}
}
// the amplification in the enumerator
if (order % 2 == 0)
b_s = -b_s * Math.Sqrt(1.0 + e * e);
else
b_s = -b_s;
}
This function calculates the transfer function in the Laplace domain and puts it into the array a_s for the denominator and into the double value b_s for the enumerator. An important fact is here that these parameters are independent on any frequency. Here only the ripple ε and the order N of the Chebychev polynomial matter. The frequencies get into the scene when the transfer function is transformed into the z domain.
This transformation into the z domain is done the same way I did it in Digital filter design, by a bilinear transformation with
and
with fc = cut off frequency of the filter and fs = sampling frequency.
The transfer function in the z domain becomes
with fc = cut off frequency of the filter and fs = sampling frequency.
The function to transform is more or less the same as I used it in the Bessel filter . Only the initialisation of the enumerator is a bit different as the input is not just 1 for this.
public void TransformToZPlane()
{
int i, j;
List<double[]> aa = new List<double[]>();
for (i = 0; i <= order; i++)
{
aa.Add(new double[] { 1.0, -1.0 });
}
double[] tempA = { 1.0, 1.0 };
tempA[0] = 1;
tempA[1] = 1;
b_z = Poly.Power(tempA, order);
b_z = Poly.Mult(b_z, b_s);
tempA[1] = 1;
for (i = 0; i <= order; i++)
{
double[] tempEl = aa.ElementAt(i);
tempEl = Poly.Mult(Poly.Power(tempA, i), Poly.Power(tempEl, order - i));
tempEl = Poly.Mult(tempEl, a_s[i] * Math.Pow(2.0 / tc, order - i));
aa.RemoveAt(i);
aa.Insert(i, tempEl);
}
for (i = 0; i <= order; i++)
{
a_z[i] = 0;
for (j = 0; j <= order; j++)
a_z[i] = a_z[i] + aa.ElementAt(j)[i];
}
for (i =0; i < b_z.Length; i++)
{
b_z[i] = b_z[i] / a_z[0];
}
for (i = a_z.Length-1; i >= 0; i--)
{
a_z[i] = a_z[i] / a_z[0];
}
}
I initialize the filter like:
t = 2.0 * Math.PI * fc / fs;
TChebychev cheb = new TChebychev(order, t, 0.2);
cheb.CalcChebychev(order, t);
cheb.TransformToZPlane();
For a ripple of 0.2 dB and get the denominator and enumerator polynomial in
cheb.a_z;
cheb.b_z;
With these parameters I get with fs = 10 kHz and fc = 300 Hz and order = 4 the transfer functionn
It has a really steep slope. That looks quite cool. With higher order it get’s even steeper. But the higher the order the more critical the ripple becomes. The Chebychev filter is quite sensitive on too big ripples. If the order is bigger than 4 the ripple should be smaller than 0.1 dB else the algorithm goes crazy
High pass filter
The transformation of the low pass filter into a high pass filter is done by a so called low-pass to high-pass transformation. That just means to replace s by 1/s in the transfer function like
That is
or without compound fraction
In the implementation that means I just have to switch the direction of the elements of my denominator polynomial and add the SN in the enumerator. This can be done in the transformation from Laplace to z domain.
public void TransformToZPlane(bool bHighPass)
{
int i, j;
double[] tempA = { 1.0, 1.0 };
tempA[0] = 1;
if (bHighPass)
{
tempA[1] = -1;
b_z = Poly.Power(tempA, order);
b_z = Poly.Mult(b_z, Math.Pow(2.0 / tc, order));
double[] temp = new double[a_z.Length];
for (i = 0; i < a_s.Length; i++)
temp[i] = a_s[a_s.Length - 1 - i];
for (i = 0; i < a_s.Length; i++)
a_s[i] = temp[i];
}
else
{
tempA[1] = 1;
b_z = Poly.Power(tempA, order);
}
b_z = Poly.Mult(b_z, b_s);
tempA[1] = 1;
With this small modification the filter can work as high pass filter as well and shows a transfer function
That’s it
The demo project consists of one main window. It processes a short sample signal (red curve) and displays the filtered signal (blue curve) the cut off frequency, sampling frequency and signal frequency can be set and in the left upper corner of the graphic is a checkbox where high or low pass behaviour can be selected.
A online solver in JavaScript, that returns the filter parameters, can be found on Chebychev filter