Note
Click here to download the full example code
Compare Estimators
In this tutorial we investigate different estimator implementation techniques and compare their performance.
8 import timeit
9 import matplotlib.pyplot as plt
10 import numpy as np
11 import cbadc
Analog System
We will commit to a leap-frog control-bounded analog system throughtout this tutorial.
20 # Determine system parameters
21 N = 6
22 M = N
23 beta = 6250
24 # Set control period
25 T = 1.0 / (2.0 * beta)
26 # Adjust the feedback to achieve a bandwidth corresponding to OSR.
27 OSR = 128
28 omega_3dB = 2 * np.pi / (T * OSR)
29
30 # Instantiate analog system.
31 beta_vec = beta * np.ones(N)
32 rho_vec = - omega_3dB ** 2 / beta * np.ones(N)
33 Gamma = np.diag(-beta_vec)
34 analog_system = cbadc.analog_system.LeapFrog(beta_vec, rho_vec, Gamma)
35
36 print(analog_system, "\n")
Out:
The analog system is parameterized as:
A =
[[ -60.23928467 -60.23928467 0. 0. 0.
0. ]
[6250. 0. -60.23928467 0. 0.
0. ]
[ 0. 6250. 0. -60.23928467 0.
0. ]
[ 0. 0. 6250. 0. -60.23928467
0. ]
[ 0. 0. 0. 6250. 0.
-60.23928467]
[ 0. 0. 0. 0. 6250.
0. ]],
B =
[[6250.]
[ 0.]
[ 0.]
[ 0.]
[ 0.]
[ 0.]],
CT =
[[1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 1.]],
Gamma =
[[-6250. 0. 0. 0. 0. 0.]
[ 0. -6250. 0. 0. 0. 0.]
[ 0. 0. -6250. 0. 0. 0.]
[ 0. 0. 0. -6250. 0. 0.]
[ 0. 0. 0. 0. -6250. 0.]
[ 0. 0. 0. 0. 0. -6250.]],
Gamma_tildeT =
[[ 1. -0. -0. -0. -0. -0.]
[-0. 1. -0. -0. -0. -0.]
[-0. -0. 1. -0. -0. -0.]
[-0. -0. -0. 1. -0. -0.]
[-0. -0. -0. -0. 1. -0.]
[-0. -0. -0. -0. -0. 1.]], and D=[[0.]
[0.]
[0.]
[0.]
[0.]
[0.]]
Analog Signal
We will also need an analog signal for conversion. In this tutorial we will use a Sinusodial signal.
45 # Set the peak amplitude.
46 amplitude = 1.0
47 # Choose the sinusodial frequency via an oversampling ratio (OSR).
48 frequency = 1.0 / (T * OSR * (1 << 0))
49
50 # We also specify a phase an offset these are hovewer optional.
51 phase = 0.0
52 offset = 0.0
53
54 # Instantiate the analog signal
55 analog_signal = cbadc.analog_signal.Sinusodial(amplitude, frequency, phase, offset)
56
57 print(analog_signal)
Out:
Sinusodial parameterized as:
amplitude = 1.0,
frequency = 97.65624999999999,
phase = 0.0,
and
offset = 0.0
Simulating
Each estimator will require an independent stream of control signals. Therefore, we will next instantiate several digital controls and simulators.
67 # Set simulation precision parameters
68 atol = 1e-6
69 rtol = 1e-12
70 max_step = T / 10.
71
72 # Instantiate digital controls. We will need four of them as we will compare
73 # four different estimators.
74 digital_control1 = cbadc.digital_control.DigitalControl(T, M)
75 digital_control2 = cbadc.digital_control.DigitalControl(T, M)
76 digital_control3 = cbadc.digital_control.DigitalControl(T, M)
77 digital_control4 = cbadc.digital_control.DigitalControl(T, M)
78 print(digital_control1)
79
80 # Instantiate simulators.
81 simulator1 = cbadc.simulator.StateSpaceSimulator(
82 analog_system,
83 digital_control1,
84 [analog_signal],
85 atol=atol,
86 rtol=rtol,
87 max_step=max_step
88 )
89 simulator2 = cbadc.simulator.StateSpaceSimulator(
90 analog_system,
91 digital_control2,
92 [analog_signal],
93 atol=atol,
94 rtol=rtol,
95 max_step=max_step
96 )
97 simulator3 = cbadc.simulator.StateSpaceSimulator(
98 analog_system,
99 digital_control3,
100 [analog_signal],
101 atol=atol,
102 rtol=rtol,
103 max_step=max_step
104 )
105 simulator4 = cbadc.simulator.StateSpaceSimulator(
106 analog_system,
107 digital_control4,
108 [analog_signal],
109 atol=atol,
110 rtol=rtol,
111 max_step=max_step
112 )
113 print(simulator1)
Out:
The Digital Control is parameterized as:
T = 8e-05,
M = 6, and next update at
t = 8e-05
t = 0.0, (current simulator time)
Ts = 8e-05,
t_stop = inf,
rtol = 1e-12,
atol = 1e-06, and
max_step = 8.000000000000001e-06
Default, Quadratic Complexity, Estimator
Next we instantiate the quadratic and default estimator
cbadc.digital_estimator.DigitalEstimator. Note that during its
construction, the corresponding filter coefficients of the system will be
computed. Therefore, this procedure could be computationally intense for a
analog system with a large analog state order or equivalently for large
number of independent digital controls.
126 # Set the bandwidth of the estimator
127 G_at_omega = np.linalg.norm(
128 analog_system.transfer_function_matrix(np.array([omega_3dB])))
129 eta2 = G_at_omega**2
130 print(f"eta2 = {eta2}, {10 * np.log10(eta2)} [dB]")
131
132 # Set the batch size
133 K1 = 1 << 14
134 K2 = 1 << 14
135
136 # Instantiate the digital estimator (this is where the filter coefficients are
137 # computed).
138 digital_estimator_batch = cbadc.digital_estimator.DigitalEstimator(
139 analog_system, digital_control1, eta2, K1, K2)
140 digital_estimator_batch(simulator1)
141
142 print(digital_estimator_batch, "\n")
Out:
eta2 = 1259410956005.0083, 121.00167467044352 [dB]
Digital estimator is parameterized as
eta2 = 1259410956005.01, 121 [dB],
Ts = 8e-05,
K1 = 16384,
K2 = 16384,
and
number_of_iterations = 9223372036854775808
Resulting in the filter coefficients
Af =
[[ 9.93992011e-01 -4.80368682e-03 1.15864448e-05 -1.85515926e-08
1.63410461e-10 -7.50223347e-11]
[ 4.98396397e-01 9.97593569e-01 -4.81334317e-03 1.16016953e-05
-1.11135428e-08 1.65593448e-08]
[ 1.24724256e-01 4.99398045e-01 9.97591782e-01 -4.81356428e-03
1.13768870e-05 1.77374670e-07]
[ 2.07981872e-02 1.24899305e-01 4.99395630e-01 9.97581755e-01
-4.83705939e-03 -1.55238302e-05]
[ 2.60019384e-03 2.08166882e-02 1.24867079e-01 4.99198307e-01
9.96748934e-01 -6.25618007e-03]
[ 2.57435177e-04 2.57297257e-03 2.05433050e-02 1.22849014e-01
4.88154288e-01 9.59742079e-01]],
Ab =
[[ 1.00362260e+00 4.82689704e-03 1.16236702e-05 1.86786149e-08
2.09634411e-10 -1.97644500e-10]
[-5.00804522e-01 9.97589700e-01 4.81334112e-03 1.15948304e-05
2.15247962e-08 1.18496522e-08]
[ 1.25125657e-01 -4.99397548e-01 9.97591713e-01 4.81382778e-03
1.07124083e-05 6.90493753e-07]
[-2.08483433e-02 1.24899069e-01 -4.99394396e-01 9.97575580e-01
4.85758001e-03 -5.16212805e-05]
[ 2.60500526e-03 -2.08147709e-02 1.24852463e-01 -4.99111003e-01
9.96381087e-01 7.05497103e-03]
[-2.56748688e-04 2.56174243e-03 -2.04480404e-02 1.22206039e-01
-4.84970386e-01 9.50489454e-01]],
Bf =
[[-4.98596881e-01 1.20236964e-03 -1.93231278e-06 2.31454333e-09
-3.97667688e-11 1.54595883e-11]
[-1.24749327e-01 -4.99598767e-01 1.20406083e-03 -1.93395884e-06
8.49726057e-10 -4.20727403e-09]
[-2.08007368e-02 -1.24924740e-01 -4.99598530e-01 1.20411261e-03
-1.87040237e-06 -4.10128618e-08]
[-2.60081858e-03 -2.08232546e-02 -1.24924376e-01 -4.99596513e-01
1.20951927e-03 5.19835323e-06]
[-2.60096754e-04 -2.60270056e-03 -2.08191933e-02 -1.24892521e-01
-4.99417729e-01 1.57454839e-03]
[-2.14752207e-05 -2.57642154e-04 -2.57316206e-03 -2.05457499e-02
-1.22909157e-01 -4.89999233e-01]],
Bb =
[[ 5.01005490e-01 1.20623894e-03 1.93696031e-06 2.34138942e-09
4.37598487e-11 -5.22665975e-11]
[-1.25150778e-01 4.99598283e-01 1.20406088e-03 1.93243952e-06
3.44418666e-09 2.83912996e-09]
[ 2.08509199e-02 -1.24924690e-01 4.99598518e-01 1.20416489e-03
1.72011597e-06 1.85227740e-07]
[-2.60583470e-03 2.08232276e-02 -1.24924193e-01 4.99595398e-01
1.21392827e-03 -1.44616071e-05]
[ 2.60496116e-04 -2.60249443e-03 2.08172912e-02 -1.24878394e-01
4.99342305e-01 1.77775324e-03]
[-2.14127285e-05 2.56542055e-04 -2.56194098e-03 2.04515506e-02
-1.22306845e-01 4.87667659e-01]],
and WT =
[[ 3.72587867e-02 3.59110826e-04 -3.04746438e-05 -2.93722946e-07
3.97693792e-08 3.78364448e-10]].
Visualize Estimator’s Transfer Function (Same for Both)
150 # Logspace frequencies
151 frequencies = np.logspace(-3, 0, 100)
152 omega = 4 * np.pi * beta * frequencies
153
154 # Compute NTF
155 ntf = digital_estimator_batch.noise_transfer_function(omega)
156 ntf_dB = 20 * np.log10(np.abs(ntf))
157
158 # Compute STF
159 stf = digital_estimator_batch.signal_transfer_function(omega)
160 stf_dB = 20 * np.log10(np.abs(stf.flatten()))
161
162 # Signal attenuation at the input signal frequency
163 stf_at_omega = digital_estimator_batch.signal_transfer_function(
164 np.array([2 * np.pi * frequency]))[0]
165
166 # Plot
167 plt.figure()
168 plt.semilogx(frequencies, stf_dB, label='$STF(\omega)$')
169 for n in range(N):
170 plt.semilogx(frequencies, ntf_dB[0, n, :], label=f"$|NTF_{n+1}(\omega)|$")
171 plt.semilogx(frequencies, 20 * np.log10(np.linalg.norm(
172 ntf[0, :, :], axis=0)), '--', label="$ || NTF(\omega) ||_2 $")
173
174 # Add labels and legends to figure
175 plt.legend()
176 plt.grid(which='both')
177 plt.title("Signal and noise transfer functions")
178 plt.xlabel("$\omega / (4 \pi \\beta ) $")
179 plt.ylabel("dB")
180 plt.xlim((frequencies[1], frequencies[-1]))
181 plt.gcf().tight_layout()
FIR Filter Estimator
Similarly as for the previous estimator the
cbadc.digital_estimator.FIRFilter is initalized. Additionally,
we visualize the decay of the \(\|\cdot\|_2\) norm of the corresponding
filter coefficients. This is an aid to determine if the lookahead and
lookback sizes L1 and L2 are set sufficiently large.
193 # Determine lookback
194 L1 = K2
195 # Determine lookahead
196 L2 = K2
197 digital_estimator_fir = cbadc.digital_estimator.FIRFilter(
198 analog_system, digital_control2, eta2, L1, L2)
199
200 print(digital_estimator_fir, "\n")
201
202 digital_estimator_fir(simulator2)
203
204 # Next visualize the decay of the resulting filter coefficients.
205 h_index = np.arange(-L1, L2)
206
207 impulse_response = np.abs(np.array(digital_estimator_fir.h[0, :, :])) ** 2
208 impulse_response_dB = 10 * np.log10(impulse_response)
209
210 fig, ax = plt.subplots(2)
211 for index in range(N):
212 ax[0].plot(h_index, impulse_response[:, index],
213 label=f"$h_{index + 1}[k]$")
214 ax[1].plot(h_index, impulse_response_dB[:, index],
215 label=f"$h_{index + 1}[k]$")
216 ax[0].legend()
217 fig.suptitle(f"For $\eta^2 = {10 * np.log10(eta2)}$ [dB]")
218 ax[1].set_xlabel("filter taps k")
219 ax[0].set_ylabel("$| h_\ell [k]|^2_2$")
220 ax[1].set_ylabel("$| h_\ell [k]|^2_2$ [dB]")
221 ax[0].set_xlim((-50, 50))
222 ax[0].grid(which='both')
223 ax[1].set_xlim((-50, 500))
224 ax[1].set_ylim((-200, 0))
225 ax[1].grid(which='both')
Out:
FIR estimator is parameterized as
eta2 = 1259410956005.01, 121 [dB],
Ts = 8e-05,
K1 = 16384,
K2 = 16384,
and
number_of_iterations = 9223372036854775808.
Resulting in the filter coefficients
h =
[[[-8.98888895e-23 3.86415093e-22 1.78156003e-24 -4.79446969e-24
7.27065590e-26 2.27668499e-26]
[-2.83409887e-22 3.83559844e-22 6.03912514e-24 -4.80331076e-24
3.78726684e-26 2.38832692e-26]
[-4.75669187e-22 3.77650777e-22 1.02704857e-23 -4.77413572e-24
2.56155334e-27 2.48161161e-26]
...
[-4.75669905e-22 -3.86819971e-22 2.90231506e-24 4.90109883e-24
9.58043250e-26 -2.39133320e-26]
[-2.83410643e-22 -3.89022950e-22 -1.40723095e-24 4.84795441e-24
1.30885020e-25 -2.23089598e-26]
[-8.98896788e-23 -3.88147790e-22 -5.68388014e-24 4.75685837e-24
1.64756362e-25 -2.05368240e-26]]].
IIR Filter Estimator
The IIR filter is closely related to the FIR filter with the exception
of an moving average computation.
See cbadc.digital_estimator.IIRFilter for more information.
236 # Determine lookahead
237 L2 = K2
238
239 digital_estimator_iir = cbadc.digital_estimator.IIRFilter(
240 analog_system, digital_control3, eta2, L2)
241
242 print(digital_estimator_iir, "\n")
243
244 digital_estimator_iir(simulator3)
Out:
IIR estimator is parameterized as
eta2 = 1259410956005.01, 121 [dB],
Ts = 8e-05,
K2 = 16384,
and
number_of_iterations = 9223372036854775808.
Resulting in the filter coefficients
Af =
[[ 9.93992011e-01 -4.80368682e-03 1.15864448e-05 -1.85515926e-08
1.63410461e-10 -7.50223347e-11]
[ 4.98396397e-01 9.97593569e-01 -4.81334317e-03 1.16016953e-05
-1.11135428e-08 1.65593448e-08]
[ 1.24724256e-01 4.99398045e-01 9.97591782e-01 -4.81356428e-03
1.13768870e-05 1.77374670e-07]
[ 2.07981872e-02 1.24899305e-01 4.99395630e-01 9.97581755e-01
-4.83705939e-03 -1.55238302e-05]
[ 2.60019384e-03 2.08166882e-02 1.24867079e-01 4.99198307e-01
9.96748934e-01 -6.25618007e-03]
[ 2.57435177e-04 2.57297257e-03 2.05433050e-02 1.22849014e-01
4.88154288e-01 9.59742079e-01]],
Bf =
[[-4.98596881e-01 1.20236964e-03 -1.93231278e-06 2.31454333e-09
-3.97667688e-11 1.54595883e-11]
[-1.24749327e-01 -4.99598767e-01 1.20406083e-03 -1.93395884e-06
8.49726057e-10 -4.20727403e-09]
[-2.08007368e-02 -1.24924740e-01 -4.99598530e-01 1.20411261e-03
-1.87040237e-06 -4.10128618e-08]
[-2.60081858e-03 -2.08232546e-02 -1.24924376e-01 -4.99596513e-01
1.20951927e-03 5.19835323e-06]
[-2.60096754e-04 -2.60270056e-03 -2.08191933e-02 -1.24892521e-01
-4.99417729e-01 1.57454839e-03]
[-2.14752207e-05 -2.57642154e-04 -2.57316206e-03 -2.05457499e-02
-1.22909157e-01 -4.89999233e-01]],WT =
[[ 3.72587867e-02 3.59110826e-04 -3.04746438e-05 -2.93722946e-07
3.97693792e-08 3.78364448e-10]],
and h =
[[[ 1.86212790e-02 2.28154967e-04 -1.46830068e-05 -1.87616529e-07
1.94061457e-08 2.52891376e-10]
[ 1.85726421e-02 3.24796863e-04 -1.32368982e-05 -2.64504812e-07
1.81534337e-08 3.75850026e-10]
[ 1.84756128e-02 4.20239297e-04 -1.12914286e-05 -3.32485461e-07
1.64896942e-08 4.90005466e-10]
...
[-4.75669905e-22 -3.86819971e-22 2.90231506e-24 4.90109883e-24
9.58043250e-26 -2.39133320e-26]
[-2.83410643e-22 -3.89022950e-22 -1.40723095e-24 4.84795441e-24
1.30885020e-25 -2.23089598e-26]
[-8.98896788e-23 -3.88147790e-22 -5.68388014e-24 4.75685837e-24
1.64756362e-25 -2.05368240e-26]]].
Parallel Estimator
Next we instantiate the parallel estimator
cbadc.digital_estimator.ParallelEstimator. The parallel estimator
resembles the default estimator but diagonalizes the filter coefficients
resulting in a more computationally more efficient filter that can be
parallelized into independent filter operations.
256 # Instantiate the digital estimator (this is where the filter coefficients are
257 # computed).
258 digital_estimator_parallel = cbadc.digital_estimator.ParallelEstimator(
259 analog_system, digital_control4, eta2, K1, K2)
260
261 digital_estimator_parallel(simulator4)
262 print(digital_estimator_parallel, "\n")
Out:
Parallel estimator is parameterized as
eta2 = 1259410956005.01, 121 [dB],
Ts = 8e-05,
K1 = 16384,
K2 = 16384,
and
number_of_iterations = 9223372036854775808
Resulting in the filter coefficients
f_a =
[0.99352853+0.08855482j 0.99352853-0.08855482j 0.99020742+0.06151822j
0.99020742-0.06151822j 0.98788911+0.02199108j 0.98788911-0.02199108j],
b_a =
[[-1.38136935e+03+2.51506293e+03j 4.26841429e+02+2.82595564e+02j
3.90945213e+01-5.02154026e+01j -4.61566859e+00-4.48169798e+00j
-4.43374620e-01+3.13952416e-01j 9.36956969e-03+3.68208127e-02j]
[-1.38136935e+03-2.51506293e+03j 4.26841429e+02-2.82595564e+02j
3.90945213e+01+5.02154026e+01j -4.61566859e+00+4.48169798e+00j
-4.43374620e-01-3.13952416e-01j 9.36956969e-03-3.68208127e-02j]
[ 3.02676362e+03-8.51639570e+03j -9.79746501e+02-5.92495175e+02j
-5.98609064e+01+3.01180862e+01j -6.65317488e+00+2.19446920e+00j
-4.09920461e-01+1.15069267e+00j 7.27422100e-02+8.92621035e-02j]
[ 3.02676362e+03+8.51639570e+03j -9.79746501e+02+5.92495175e+02j
-5.98609064e+01-3.01180862e+01j -6.65317488e+00-2.19446920e+00j
-4.09920461e-01-1.15069267e+00j 7.27422100e-02-8.92621035e-02j]
[ 1.64587846e+03-1.36561516e+04j -5.52732368e+02-5.30916615e+02j
-2.09658328e+01-1.19692787e+02j -1.11559461e+01-7.04141499e+00j
-7.81738548e-01-8.25159236e-01j -1.63333052e-01-5.22025352e-02j]
[ 1.64587846e+03+1.36561516e+04j -5.52732368e+02+5.30916615e+02j
-2.09658328e+01+1.19692787e+02j -1.11559461e+01+7.04141499e+00j
-7.81738548e-01+8.25159236e-01j -1.63333052e-01+5.22025352e-02j]],
f_b =
[[ 1.38153996e+03-2.51528946e+03j 4.53510548e+02+2.34143075e+02j
-3.06136063e+01+5.52005619e+01j -5.28796590e+00-3.46613061e+00j
3.47968904e-01-3.90570686e-01j 1.68823963e-02+3.00750056e-02j]
[ 1.38153996e+03+2.51528946e+03j 4.53510548e+02-2.34143075e+02j
-3.06136063e+01-5.52005619e+01j -5.28796590e+00+3.46613061e+00j
3.47968904e-01+3.90570686e-01j 1.68823963e-02-3.00750056e-02j]
[ 3.02777145e+03-8.51896570e+03j 1.03840481e+03+4.28469244e+02j
-4.04251316e+01+3.99688739e+01j 5.68846326e+00-1.51947402e+00j
-2.91086803e-01+1.11522405e+00j -7.93111153e-02-6.76031065e-02j]
[ 3.02777145e+03+8.51896570e+03j 1.03840481e+03-4.28469244e+02j
-4.04251316e+01-3.99688739e+01j 5.68846326e+00+1.51947402e+00j
-2.91086803e-01-1.11522405e+00j -7.93111153e-02+6.76031065e-02j]
[-1.64668442e+03+1.36624329e+04j -5.84727395e+02-2.67798132e+02j
1.00094661e+01+1.12047350e+02j -1.08624178e+01-4.81058892e+00j
5.69853789e-01+7.11277929e-01j -1.50265682e-01-3.75273959e-02j]
[-1.64668442e+03-1.36624329e+04j -5.84727395e+02+2.67798132e+02j
1.00094661e+01-1.12047350e+02j -1.08624178e+01+4.81058892e+00j
5.69853789e-01-7.11277929e-01j -1.50265682e-01+3.75273959e-02j]],
b_b =
[[-1.38136935e+03+2.51506293e+03j 4.26841429e+02+2.82595564e+02j
3.90945213e+01-5.02154026e+01j -4.61566859e+00-4.48169798e+00j
-4.43374620e-01+3.13952416e-01j 9.36956969e-03+3.68208127e-02j]
[-1.38136935e+03-2.51506293e+03j 4.26841429e+02-2.82595564e+02j
3.90945213e+01+5.02154026e+01j -4.61566859e+00+4.48169798e+00j
-4.43374620e-01-3.13952416e-01j 9.36956969e-03-3.68208127e-02j]
[ 3.02676362e+03-8.51639570e+03j -9.79746501e+02-5.92495175e+02j
-5.98609064e+01+3.01180862e+01j -6.65317488e+00+2.19446920e+00j
-4.09920461e-01+1.15069267e+00j 7.27422100e-02+8.92621035e-02j]
[ 3.02676362e+03+8.51639570e+03j -9.79746501e+02+5.92495175e+02j
-5.98609064e+01-3.01180862e+01j -6.65317488e+00-2.19446920e+00j
-4.09920461e-01-1.15069267e+00j 7.27422100e-02-8.92621035e-02j]
[ 1.64587846e+03-1.36561516e+04j -5.52732368e+02-5.30916615e+02j
-2.09658328e+01-1.19692787e+02j -1.11559461e+01-7.04141499e+00j
-7.81738548e-01-8.25159236e-01j -1.63333052e-01-5.22025352e-02j]
[ 1.64587846e+03+1.36561516e+04j -5.52732368e+02+5.30916615e+02j
-2.09658328e+01+1.19692787e+02j -1.11559461e+01+7.04141499e+00j
-7.81738548e-01+8.25159236e-01j -1.63333052e-01+5.22025352e-02j]],
f_w =
[[ 3.03885798e-07+2.89096208e-07j 3.03885798e-07-2.89096208e-07j
1.85353652e-07+3.27557818e-07j 1.85353652e-07-3.27557818e-07j
-6.07297807e-08-3.44886116e-07j -6.07297807e-08+3.44886116e-07j]],
and b_w =
[[-3.03911445e-07-2.89128776e-07j -3.03911445e-07+2.89128776e-07j
1.85407011e-07+3.27659636e-07j 1.85407011e-07-3.27659636e-07j
6.07565225e-08+3.45045111e-07j 6.07565225e-08-3.45045111e-07j]].
Estimating (Filtering)
Next we execute all simulation and estimation tasks by iterating over the estimators. Note that since no stop criteria is set for either the analog signal, the simulator, or the digital estimator this iteration could potentially continue until the default stop criteria of 2^63 iterations.
274 # Set simulation length
275 size = K2 << 4
276 u_hat_batch = np.zeros(size)
277 u_hat_fir = np.zeros(size)
278 u_hat_iir = np.zeros(size)
279 u_hat_parallel = np.zeros(size)
280 for index in range(size):
281 u_hat_batch[index] = next(digital_estimator_batch)
282 u_hat_fir[index] = next(digital_estimator_fir)
283 u_hat_iir[index] = next(digital_estimator_iir)
284 u_hat_parallel[index] = next(digital_estimator_parallel)
Visualizing Results
Finally, we summarize the comparision by visualizing the resulting estimate in both time and frequency domain.
293 t = np.arange(size)
294 # compensate the built in L1 delay of FIR filter.
295 t_fir = np.arange(-L1 + 1, size - L1 + 1)
296 t_iir = np.arange(-L1 + 1, size - L1 + 1)
297 u = np.zeros_like(u_hat_batch)
298 for index, tt in enumerate(t):
299 u[index] = analog_signal.evaluate(tt * T)
300 plt.plot(t, u_hat_batch, label="$\hat{u}(t)$ Batch")
301 plt.plot(t_fir, u_hat_fir, label="$\hat{u}(t)$ FIR")
302 plt.plot(t_iir, u_hat_iir, label="$\hat{u}(t)$ IIR")
303 plt.plot(t, u_hat_parallel, label="$\hat{u}(t)$ Parallel")
304 plt.plot(t, stf_at_omega * u, label="$\mathrm{STF}(2 \pi f_u) * u(t)$")
305 plt.xlabel('$t / T$')
306 plt.legend()
307 plt.title("Estimated input signal")
308 plt.grid(which='both')
309 plt.xlim((-100, 500))
310 plt.tight_layout()
311
312 plt.figure()
313 plt.plot(t, u_hat_batch, label="$\hat{u}(t)$ Batch")
314 plt.plot(t_fir, u_hat_fir, label="$\hat{u}(t)$ FIR")
315 plt.plot(t_iir, u_hat_iir, label="$\hat{u}(t)$ IIR")
316 plt.plot(t, u_hat_parallel, label="$\hat{u}(t)$ Parallel")
317 plt.plot(t, stf_at_omega * u, label="$\mathrm{STF}(2 \pi f_u) * u(t)$")
318 plt.xlabel('$t / T$')
319 plt.legend()
320 plt.title("Estimated input signal")
321 plt.grid(which='both')
322 plt.xlim((t_fir[-1] - 50, t_fir[-1]))
323 plt.tight_layout()
324
325 plt.figure()
326 plt.plot(t, u_hat_batch, label="$\hat{u}(t)$ Batch")
327 plt.plot(t_fir, u_hat_fir, label="$\hat{u}(t)$ FIR")
328 plt.plot(t_iir, u_hat_iir, label="$\hat{u}(t)$ IIR")
329 plt.plot(t, u_hat_parallel, label="$\hat{u}(t)$ Parallel")
330 plt.plot(t, stf_at_omega * u, label="$\mathrm{STF}(2 \pi f_u) * u(t)$")
331 plt.xlabel('$t / T$')
332 plt.legend()
333 plt.title("Estimated input signal")
334 plt.grid(which='both')
335 # plt.xlim((t_fir[0], t[-1]))
336 plt.xlim(((1 << 14) - 100, (1 << 14) + 100))
337 plt.tight_layout()
338
339 batch_error = stf_at_omega * u - u_hat_batch
340 fir_error = stf_at_omega * u[:(u.size - L1 + 1)] - u_hat_fir[(L1 - 1):]
341 iir_error = stf_at_omega * u[:(u.size - L1 + 1)] - u_hat_iir[(L1 - 1):]
342 parallel_error = stf_at_omega * u - u_hat_parallel
343 plt.figure()
344 plt.plot(t, batch_error,
345 label="$|\mathrm{STF}(2 \pi f_u) * u(t) - \hat{u}(t)|$ Batch")
346 plt.plot(t[:(u.size - L1 + 1)], fir_error,
347 label="$|\mathrm{STF}(2 \pi f_u) * u(t) - \hat{u}(t)|$ FIR")
348 plt.plot(t[:(u.size - L1 + 1)], iir_error,
349 label="$|\mathrm{STF}(2 \pi f_u) * u(t) - \hat{u}(t)|$ IIR")
350 plt.plot(t, parallel_error,
351 label="$|\mathrm{STF}(2 \pi f_u) * u(t) - \hat{u}(t)|$ Parallel")
352 plt.xlabel('$t / T$')
353 plt.xlim(((1 << 14) - 100, (1 << 14) + 100))
354 plt.ylim((-0.00001, 0.00001))
355 plt.legend()
356 plt.title("Estimation error")
357 plt.grid(which='both')
358 plt.tight_layout()
359
360
361 print(f"Average Batch Error: {np.linalg.norm(batch_error) / batch_error.size}")
362 print(f"Average FIR Error: {np.linalg.norm(fir_error) / fir_error.size}")
363 print(f"Average IIR Error: {np.linalg.norm(iir_error) / iir_error.size}")
364 print(
365 f"""Average Parallel Error: { np.linalg.norm(parallel_error)/
366 parallel_error.size}""")
367
368 plt.figure()
369 u_hat_batch_clipped = u_hat_batch[(K1 + K2):-K2]
370 u_hat_fir_clipped = u_hat_fir[(L1 + L2):]
371 u_hat_iir_clipped = u_hat_iir[(K1 + K2):-K2]
372 u_hat_parallel_clipped = u_hat_parallel[(K1 + K2):-K2]
373 u_clipped = stf_at_omega * u
374 f_batch, psd_batch = cbadc.utilities.compute_power_spectral_density(
375 u_hat_batch_clipped)
376 f_fir, psd_fir = cbadc.utilities.compute_power_spectral_density(
377 u_hat_fir_clipped)
378 f_iir, psd_iir = cbadc.utilities.compute_power_spectral_density(
379 u_hat_iir_clipped)
380 f_parallel, psd_parallel = cbadc.utilities.compute_power_spectral_density(
381 u_hat_parallel_clipped)
382 f_ref, psd_ref = cbadc.utilities.compute_power_spectral_density(u_clipped)
383 plt.semilogx(f_ref, 10 * np.log10(psd_ref),
384 label="$\mathrm{STF}(2 \pi f_u) * U(f)$")
385 plt.semilogx(f_batch, 10 * np.log10(psd_batch), label="$\hat{U}(f)$ Batch")
386 plt.semilogx(f_fir, 10 * np.log10(psd_fir), label="$\hat{U}(f)$ FIR")
387 plt.semilogx(f_iir, 10 * np.log10(psd_iir), label="$\hat{U}(f)$ IIR")
388 plt.semilogx(f_parallel, 10 * np.log10(psd_parallel),
389 label="$\hat{U}(f)$ Parallel")
390 plt.legend()
391 plt.ylim((-200, 50))
392 plt.xlim((f_fir[1], f_fir[-1]))
393 plt.xlabel('frequency [Hz]')
394 plt.ylabel('$ \mathrm{V}^2 \, / \, (1 \mathrm{Hz})$')
395 plt.grid(which='both')
396 plt.show()
Out:
Average Batch Error: 4.083356807183407e-06
Average FIR Error: 4.355562871483952e-06
Average IIR Error: 4.355562871483949e-06
Average Parallel Error: 4.083356808847717e-06
Compute Time
Compare the execution time of each estimator
406 def dummy_input_control_signal():
407 while True:
408 yield np.zeros(M, dtype=np.int8)
409
410
411 def iterate_number_of_times(iterator, number_of_times):
412 for _ in range(number_of_times):
413 _ = next(iterator)
414
415
416 digital_estimator_batch = cbadc.digital_estimator.DigitalEstimator(
417 analog_system,
418 digital_control1,
419 eta2,
420 K1,
421 K2)
422 digital_estimator_fir = cbadc.digital_estimator.FIRFilter(
423 analog_system,
424 digital_control2,
425 eta2,
426 L1,
427 L2)
428 digital_estimator_parallel = cbadc.digital_estimator.ParallelEstimator(
429 analog_system,
430 digital_control4,
431 eta2,
432 K1,
433 K2)
434 digital_estimator_iir = cbadc.digital_estimator.IIRFilter(
435 analog_system,
436 digital_control3,
437 eta2,
438 L2)
439
440 digital_estimator_batch(dummy_input_control_signal())
441 digital_estimator_fir(dummy_input_control_signal())
442 digital_estimator_parallel(dummy_input_control_signal())
443 digital_estimator_iir(dummy_input_control_signal())
444
445 length = 1 << 14
446 repetitions = 10
447
448 print("Digital Estimator:")
449 print(timeit.timeit(lambda: iterate_number_of_times(
450 digital_estimator_batch, length), number=repetitions), 'sec \n')
451
452 print("FIR Estimator:")
453 print(timeit.timeit(lambda: iterate_number_of_times(
454 digital_estimator_fir, length), number=repetitions), 'sec \n')
455
456 print("IIR Estimator:")
457 print(timeit.timeit(lambda: iterate_number_of_times(
458 digital_estimator_iir, length), number=repetitions), 'sec \n')
459
460 print("Parallel Estimator:")
461 print(timeit.timeit(lambda: iterate_number_of_times(
462 digital_estimator_parallel, length), number=repetitions), 'sec \n')
Out:
Digital Estimator:
5.485120426994399 sec
FIR Estimator:
37.11588862899225 sec
IIR Estimator:
36.34378033899702 sec
Parallel Estimator:
9.21785790399008 sec
Total running time of the script: ( 40 minutes 23.772 seconds)