The Parks-McClellan algorithm


The Parks-McClellan algorithm is an approach based on the alternation theorem for the design of optimal FIR filters.

Here we test the algorithm as implemented in matlab to construct a low-pass filter with passband [0,7/40] and stopband [9/40,1/2]. We consider in the matlab code the cases delta=0.09, delta=0.017 and delta=0.004 that with the notation in my signal processing course corresponds to N=7, N=16 and N=24.

First of all let us see the graph of the approximated filter in comparison with the ideal low-pass filter with a linear behavior in the transition band.

pa_mc_1
delta=0.09,         N=7

pa_mc_2
delta=0.017,         N=16

pa_mc_3
delta=0.004,         N=24

According to the numerical data, the maximal errors in the passband and the stopband are 0.11076, 0.016955 and 0.0045000 which are close to the specified values of delta. The plots of the errors in these zones is

pa_mc_e1
delta=0.09,         N=7

pa_mc_e2
delta=0.017,         N=16

pa_mc_e3
delta=0.004,         N=24

This is in clear agreement with the expected equioscillation property.

If you are curious about the aspect of the graphs of the error including the transition band, here they are:

pa_mc_er1
delta=0.09,         N=7

pa_mc_er2
delta=0.017,         N=16

pa_mc_er3
delta=0.004,         N=24



The code

This is the matlab code that generates the data. Uncomment the lines with % to see some figures.

df = 0.025;
nuc = 0.2;
delta = 0.09;

passb = nuc-df;
stopb = nuc+df;
dpass = delta;
dstop = delta;
nusamp = 1;

[n,fo,ao,w] = firpmord([passb stopb],[1 0],[dpass dstop],nusamp);
b = firpm(n,fo,ao,w);
[h,w] = freqz(b,1,512);
%figure(1)
%plot(fo/2,ao,w/pi/2,abs(h));

kv = [0:511]';
newh = h.*exp(n*pi*i/1024*kv);
%figure(2)
%plot(fo/2,ao,w/pi/2,real(newh));
%figure(3)
%plot(w/pi/2,real(newh));
format shortE
%size(real(newh))

disp('------------------------------------------')
disp((n-1)/2)
delta
%real(newh)

The data generated putting in the matlab program the different values of delta was stored in files dpm1.txt, dpm2.txt, dpm3.txt and the figures above were generated with the following SAGE program:


for k in srange(1,4):
  with open('dpm'+str(k)+'.txt','r') as f:
    text = f.read()
    
    temp = text.split('\n')[:512]
    for m in srange(512):
      temp[m] = sage_eval( temp[m] )

    P = line( [(0,1),(7/40,1),(9/40,0), (1/2,0)], thickness=2, linestyle='--', color='red', zorder= 60)  
    P += list_plot([(m/1024,temp[m]) for m in srange(512)], plotjoined=True, thickness=4)
    P.fontsize(25)
    P.save('./pa_mc_'+str(k)+'.png')

    error = [(m/1024,1-temp[m]) for m in srange(180)]
    error += [( m/1024, 20*(9/40-m/1024)-temp[m] ) for m in srange(180, 231)]
    error += [( m/1024,0-temp[m] ) for m in srange(231, 512)]
    P = list_plot( error, plotjoined=True, thickness=4)
    P.fontsize(25)
    P.save('./pa_mc_er'+str(k)+'.png')
    
    error = [(m/1024,1-temp[m]) for m in srange(180)]
    P = list_plot( error, plotjoined=True, thickness=4)
    ma = 1-min(temp[:180])
    mi = 1-max(temp[:180])
    error = [( m/1024,0-temp[m] ) for m in srange(231, 512)]
    ma = max( ma, 0-min(temp[231:512]) )
    mi = min( mi, 0-max(temp[231:512]) )
    
    print k,'->',max(-mi,ma)
    P += list_plot( error, plotjoined=True, thickness=4)
    
    P += plot( ma+0*x,7/40,9/40, fill=mi+0*x, color='gray', fillcolor='gray', fillalpha=1)
#    P += polygon([(7/40,ma), (9/40,ma), (9/40,mi), (7/40,mi)], color='gray')
    
    P.fontsize(25)
    P.save('./pa_mc_e'+str(k)+'.png')