def test_estimation_with_circuit_simulator(): eta2 = 1e12 K1 = 1 << 10 K2 = 1 << 10 size = K2 << 2 window = 1000 size_2 = size // 2 window_2 = window // 2 left_w = size_2 - window_2 right_w = size_2 + window_2 analogSystem = AnalogSystem(A, B, CT, Gamma, Gamma_tildeT) analogSignals = [Sinusodial(amplitude, frequency, phase)] digitalControl1 = DigitalControl(Ts, M) digitalControl2 = DigitalControl(Ts, M) digitalControl3 = DigitalControl(Ts, M) digitalControl4 = DigitalControl(Ts, M) tf_abs = np.abs( analogSystem.transfer_function_matrix(np.array([2 * np.pi * frequency ]))) print(tf_abs, tf_abs.shape) simulator1 = StateSpaceSimulator(analogSystem, digitalControl1, analogSignals) simulator2 = StateSpaceSimulator(analogSystem, digitalControl2, analogSignals) simulator3 = StateSpaceSimulator(analogSystem, digitalControl3, analogSignals) simulator4 = StateSpaceSimulator(analogSystem, digitalControl4, analogSignals) estimator1 = DigitalEstimator(analogSystem, digitalControl1, eta2, K1, K2) estimator2 = ParallelEstimator(analogSystem, digitalControl2, eta2, K1, K2) estimator3 = FIRFilter(analogSystem, digitalControl3, eta2, K1, K2) estimator4 = IIRFilter(analogSystem, digitalControl4, eta2, K2) estimator1(simulator1) estimator2(simulator2) estimator3(simulator3) estimator4(simulator4) tf_1 = estimator1.signal_transfer_function( np.array([2 * np.pi * frequency]))[0] e1_array = np.zeros(size) e2_array = np.zeros(size) e3_array = np.zeros(size) e4_array = np.zeros(size) e1_error = 0 e2_error = 0 e3_error = 0 e4_error = 0 for index in range(size): e1 = estimator1.__next__() e2 = estimator2.__next__() e3 = estimator3.__next__() e4 = estimator4.__next__() e1_array[index] = e1 e2_array[index] = e2 e3_array[index] = e3 e4_array[index] = e4 t = index * Ts u = analogSignals[0].evaluate(t) u_lag = analogSignals[0].evaluate(t - estimator4.filter_lag() * Ts) if (index > left_w and index < right_w): print( f"Time: {t: 0.2f}, Input Signal: {u * tf_1}, e1: {e1}, e2: {e2}, e3: {e3}, e4: {e4}" ) e1_error += np.abs(e1 - u * tf_1)**2 e2_error += np.abs(e2 - u * tf_1)**2 e3_error += np.abs(e3 - u_lag * tf_1)**2 e4_error += np.abs(e4 - u_lag * tf_1)**2 e1_error /= window e2_error /= window e3_error /= window e4_error /= window print(f"""Digital estimator error: {e1_error}, {10 * np.log10(e1_error)} dB""") print(f"""Parallel estimator error: {e2_error}, {10 * np.log10(e2_error)} dB""") print(f"""FIR filter estimator error: {e3_error}, {10 * np.log10(e3_error)} dB""") print(f"""IIR filter estimator error: {e4_error}, {10 * np.log10(e4_error)} dB""") assert (np.allclose(e1_error, 0, rtol=1e-6, atol=1e-6)) assert (np.allclose(e2_error, 0, rtol=1e-6, atol=1e-6)) assert (np.allclose(e3_error, 0, rtol=1e-6, atol=1e-6)) assert (np.allclose(e4_error, 0, rtol=1e-6, atol=1e-6))
############################################################################### # Visualize Estimator's Transfer Function (Same for Both) # ------------------------------------------------------- # # Logspace frequencies frequencies = np.logspace(-3, 0, 100) omega = 4 * np.pi * beta * frequencies # Compute NTF ntf = digital_estimator_batch.noise_transfer_function(omega) ntf_dB = 20 * np.log10(np.abs(ntf)) # Compute STF stf = digital_estimator_batch.signal_transfer_function(omega) stf_dB = 20 * np.log10(np.abs(stf.flatten())) # Signal attenuation at the input signal frequency stf_at_omega = digital_estimator_batch.signal_transfer_function( np.array([2 * np.pi * frequency]))[0] # Plot plt.figure() plt.semilogx(frequencies, stf_dB, label='$STF(\omega)$') for n in range(N): plt.semilogx(frequencies, ntf_dB[0, n, :], label=f"$|NTF_{n+1}(\omega)|$") plt.semilogx(frequencies, 20 * np.log10(np.linalg.norm( ntf[0, :, :], axis=0)), '--', label="$ || NTF(\omega) ||_2 $") # Add labels and legends to figure
omega_3dB = (4 * np.pi * beta) / 100. eta2 = np.linalg.norm( analog_system.transfer_function_matrix(np.array([omega_3dB])).flatten())**2 # Instantiate estimator. digital_estimator = DigitalEstimator(analog_system, digital_control, eta2, K1=1) # Compute NTF ntf = digital_estimator.noise_transfer_function(omega) ntf_dB = 20 * np.log10(np.abs(ntf)) # Compute STF stf = digital_estimator.signal_transfer_function(omega) stf_dB = 20 * np.log10(np.abs(stf.flatten())) # Plot plt.figure() plt.semilogx(frequencies, stf_dB, label='$STF(\omega)$') for n in range(N): plt.semilogx(frequencies, ntf_dB[0, n, :], label=f"$|NTF_{n+1}(\omega)|$") plt.semilogx(frequencies, 20 * np.log10(np.linalg.norm(ntf[0, :, :], axis=0)), '--', label="$ || NTF(\omega) ||_2 $") # Add labels and legends to figure plt.legend() plt.grid(which='both')