After designing a "least square optimal" FIR filter I wanted to find the group/phase delay, defined as: $P(\omega) \triangleq - \frac{\Theta(\omega)}{\omega}$ (Depending on the literature group and phase are used inconsistently). In scipy I've done this as follows:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
# Filter design parameters
fs = 25.0 # Hz
desired = (1, 1, 0, 0) # Ideal filter "bands"
bands = (0, 1, 5, 12.5) # Frequency at band edges
taps = 15 # Filter order.
# Create LS optimal filter
fir_coeffs = signal.firls(taps, bands, desired, fs=fs)
normfreq, response = signal.freqz(fir_coeffs)
freq = 0.5*fs*normfreq/np.pi #Convert form rad/sample to Hz
# Find grp delay
normgrpfreq, grpdly = signal.group_delay((fir_coeffs,1), w=512, whole=False)
grpfreq = 0.5*fs*normgrpfreq/np.pi #Convert form rad/sample to Hz
zero, pole, gain = signal.tf2zpk(fir_coeffs, 1)
fig, axs = plt.subplots(3)
axs[0].set(title='Amplitude Response')
axs[0].set_xlabel('Frequency (Hz)')
axs[0].set_yscale("log")
axs[0].set_ylabel('Magnitude (DB)')
axs[0].grid(True)
axs[0].plot(freq, np.abs(response)) # Plot with log y axis
axs[1].set(title='Phase Response')
axs[1].set_xlabel('Frequency (Hz)')
axs[1].set_ylabel('Phase (°)')
axs[1].grid(True)
axs[1].plot(freq, np.angle(response)*180/np.pi) # Plot with log y axis
axs[2].set(title='Phase Delay')
axs[2].set_xlabel('Frequency (Hz)')
axs[2].set_ylabel('Delay (s)')
axs[2].grid(True)
axs[2].plot(grpfreq, grpdly/fs) # Plot with log y axis
fig.tight_layout()
resulting in:
The calculated delay seems wrong. If we limit ourselves to looking at the approximately linear part of the phase plot (0 to ~2 Hz) I would expect the delay in this period to be close to a constant $(120/2)* (\pi/ 180)\approx 1\ \mathrm{samples}$.
Am I misunderstanding something or doing something wrong?
EDIT: Further more when I convert the system to a minimum phase system, thereby ensuring minimum group delay the group delay seem to increase to roughly two samples, which doesn’t make sense either..
fir_coeffs =signal.minimum_phase(fir_coeffs, method='homomorphic', n_fft=None)
grpdly
. $\endgroup$