Multi-stage interpolation.ΒΆ

Design a two-stage interpolation with a total interpolation factor of four.

import math

import matplotlib.pyplot as plt
import numpy as np
from mplsignal import freqz_fir

from fird.constraint import PiecewiseLinearConstraint
from fird.designer import FIRDesigner
from fird.objective import PiecewiseLinearObjective
from fird.util import expand

Obtimize stopband attentation, while keeping passband ripple below 0.01

total_objective = PiecewiseLinearObjective([0.3, 0.7, 0.8, 1], [0, 0, 0, 0])
total_constraint = PiecewiseLinearConstraint([0, 0.2], [1, 1], [0.01])

Design first stage filter, keep passband ripple to half

Plot the magnitude response of the first-stage filter.

fig, ax = plt.subplots()
freqz_fir(h1, ax=ax, style="magnitude")
axins = ax.inset_axes(
    [0.2, 0.6, 0.6, 0.3],
)
ax.set_ylim((-100, 5))
freqz_fir(h1, ax=axins, style="magnitude")
axins.set_xlim(0, 0.4 * np.pi)
axins.set_ylim(-0.1, 0.1)
axins.xaxis.label.set_visible(False)
axins.yaxis.label.set_visible(False)
ax.indicate_inset_zoom(axins, edgecolor="black")
fig.show()
multistageinterpolation

Design second-stage filter subject to an upsampled version of the first-stage filter. From now on, the total objective and constraint is used.

Minimum stopband attenuation: 72.4954747000503 dB

Plot the different filters and the total response

combined = np.convolve(upsampled_h1, h2)
fig, ax = plt.subplots()
freqz_fir(upsampled_h1, ax=ax, style="magnitude")
freqz_fir(h2, ax=ax, style="magnitude")
freqz_fir(combined, ax=ax, style="magnitude")
total_objective.plot(ax=ax, plot_db=True, plot_range=True)
ax.set_ylim((-140, 5))
axins = ax.inset_axes([0.1, 0.1, 0.6, 0.3])
freqz_fir(upsampled_h1, ax=axins, style="magnitude")
freqz_fir(h2, ax=axins, style="magnitude")
freqz_fir(combined, ax=axins, style="magnitude")
total_constraint.plot(ax=axins, plot_db=True)
axins.set_xlim(0, 0.2 * math.pi)
axins.set_ylim(-0.1, 0.1)
axins.xaxis.label.set_visible(False)
axins.yaxis.label.set_visible(False)
ax.indicate_inset_zoom(axins, edgecolor="black")
fig.show()
multistageinterpolation
/builds/oscgu95/fird/venv/lib/python3.13/site-packages/fird/util.py:58: RuntimeWarning: divide by zero encountered in log10
  return -20 * np.log10(np.abs(a))

Now redesign the first-stage filter subject to the second-stage filter. As the first-stage filter is periodic when considering the the final sample rate, the period argument is used.

Minimum stopband attenuation: 72.49532277396742 dB

Plot the new and old first-stage filters.

fig, ax = plt.subplots()
freqz_fir(h1_new, ax=ax, style="magnitude")
freqz_fir(h1, ax=ax, style="magnitude")
ax.set_ylim((-100, 5))
axins = ax.inset_axes([0.2, 0.6, 0.6, 0.3])
freqz_fir(h1_new, ax=axins, style="magnitude")
freqz_fir(h1, ax=axins, style="magnitude")
axins.set_xlim(0, 0.4 * math.pi)
axins.set_ylim(-0.1, 0.1)
axins.xaxis.label.set_visible(False)
axins.yaxis.label.set_visible(False)
ax.indicate_inset_zoom(axins, edgecolor="black")
fig.show()
multistageinterpolation

The new first-stage filter does not lead to an improved total response.

upsampled_h1_new = expand(h1_new, 2)
combined_new = np.convolve(upsampled_h1_new, h2)

fig, ax = plt.subplots()
freqz_fir(upsampled_h1_new, ax=ax, style="magnitude")
freqz_fir(h2, ax=ax, style="magnitude")
freqz_fir(combined_new, ax=ax, style="magnitude")
total_objective.plot(ax=ax, plot_db=True, plot_range=True)
ax.set_ylim((-140, 5))
axins = ax.inset_axes([0.1, 0.1, 0.6, 0.3])
freqz_fir(upsampled_h1_new, ax=axins, style="magnitude")
freqz_fir(h2, ax=axins, style="magnitude")
freqz_fir(combined_new, ax=axins, style="magnitude")
total_constraint.plot(ax=axins, plot_db=True)
axins.set_xlim(0, 0.2 * np.pi)
axins.set_ylim(-0.1, 0.1)
axins.xaxis.label.set_visible(False)
axins.yaxis.label.set_visible(False)
ax.indicate_inset_zoom(axins, edgecolor="black")
fig.show()
multistageinterpolation

Redesign second-stage filter subject to an upsampled version of the redesigned first-stage filter.

Minimum stopband attenuation: 73.25777146258964 dB

Plot the different filters and the total response.

combined = np.convolve(upsampled_h1_new, h2_new)
fig, ax = plt.subplots()
freqz_fir(upsampled_h1_new, ax=ax, style="magnitude")
freqz_fir(h2_new, ax=ax, style="magnitude")
freqz_fir(combined, ax=ax, style="magnitude")
total_objective.plot(ax=ax, plot_db=True, plot_range=True)
ax.set_ylim((-140, 5))
axins = ax.inset_axes([0.1, 0.1, 0.6, 0.3])
freqz_fir(upsampled_h1_new, ax=axins, style="magnitude")
freqz_fir(h2_new, ax=axins, style="magnitude")
freqz_fir(combined, ax=axins, style="magnitude")
total_constraint.plot(ax=axins, plot_db=True)
axins.set_xlim(0, 0.2 * math.pi)
axins.set_ylim(-0.1, 0.1)
axins.xaxis.label.set_visible(False)
axins.yaxis.label.set_visible(False)
ax.indicate_inset_zoom(axins, edgecolor="black")
fig.show()
multistageinterpolation

It may now be possible to obtain a slightly better second-stage filter and so on. However, as the combined filter is a non-convex problem, it may not converge to the global minimum.

Total running time of the script: (0 minutes 1.299 seconds)

Gallery generated by Sphinx-Gallery