def demonstrate_second_order_model_fitting(runs: int = 10000): rng = default_rng(1) # sensor/measurement system S0 = 0.124 uS0 = 0.001 delta = 0.01 udelta = 0.001 f0 = 36 uf0 = 0.5 # Monte Carlo for calculation of unc. assoc. with [real(H),imag(H)] MCS0 = rng.normal(loc=S0, scale=uS0, size=runs) MCd = rng.normal(loc=delta, scale=udelta, size=runs) MCf0 = rng.normal(loc=f0, scale=uf0, size=runs) f = np.linspace(0, 1.2 * f0, 30) HMC = sos_FreqResp(MCS0, MCd, MCf0, f) Hc = np.mean(HMC, dtype=complex, axis=1) H = np.r_[np.real(Hc), np.imag(Hc)] UH = np.cov(np.r_[np.real(HMC), np.imag(HMC)], rowvar=True) UH = make_semiposdef(UH) p, Up = fit_som(f, H, UH, scaling=1, MCruns=runs, verbose=True) plt.figure(1) plt.errorbar( range(1, 4), [p[0] * 10, p[1] * 10, p[2] / 10], np.sqrt(np.diag(Up)) * np.array([10, 10, 0.1]), fmt=".", ) plt.errorbar( np.arange(1, 4) + 0.2, [S0 * 10, delta * 10, f0 / 10], [uS0 * 10, udelta * 10, uf0 / 10], fmt=".", ) plt.xlim(0, 4) plt.xticks([1.1, 2.1, 3.1], [r"$S_0$", r"$\delta$", r"$f_0$"]) plt.xlabel("scaled model parameters") plt.show()
Ts = 1 / Fs # nominal system parameters fcut = 20e3 L = 100 b = kaiser_lowpass(L, fcut, Fs)[0] # uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range(runs): B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov(B, rowvar=0)) # simulate input and output signals time = np.arange(0, 499 * Ts, Ts) noise = 1e-3 x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) y, Uy = FIRuncFilter(x, noise, b, Ub) yMC, UyMC = MC.MC(x, noise, b, [1.0], Ub, runs=10000) yMC2, UyMC2 = MC.SMC(x, noise, b, [1.0], Ub, runs=10000) plt.figure(1) plt.cla() plt.plot(time, col_hstack([x, y])) plt.legend(('input', 'output'))
200) # frequencies at which to calculate frequency response HMC = np.zeros((runs, len(f)), dtype=complex) # initialise frequency response with zeros for k in range(runs): # actual Monte Carlo bc_, ac_ = sos_phys2filter( MCS0[k], MCd[k], MCf0[k]) # calculate analogue filter coefficients b_, a_ = dsp.bilinear(bc_, ac_, Fs) # transform to digital filter coefficients HMC[k, :] = dsp.freqz(b_, a_, 2 * np.pi * f / Fs)[1] # calculate DFT frequency response Hc = np.mean(HMC, dtype=complex, axis=0) # best estimate (complex-valued) H = np.r_[np.real(Hc), np.imag(Hc)] # best estimate in real, imag UH = np.cov(np.c_[np.real(HMC), np.imag(HMC)], rowvar=0) # covariance of real, imag UH = make_semiposdef(UH, verbose=True) # correct for numerical errors bF, UbF = model_est.invLSFIR_unc( H, UH, N, tau, f, Fs) # Calculation of FIR deconvolution filter and its assoc. unc. CbF = UbF / (np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis], (1, N + 1)) * np.tile(np.sqrt(np.diag(UbF))[:, np.newaxis].T, (N + 1, 1))) # correlation of filter coefficients # Deconvolution Step1: lowpass filter for noise attenuation fcut = f0 + 20e3 low_order = 100 # cut-off frequency and filter order blow, lshift = kaiser_lowpass(low_order, fcut, Fs) # FIR low pass filter coefficients shift = tau + lshift # delay of low-pass plus that of the FIR deconv filter # Deconvolution Step2: Application of deconvolution filter
Ts = 1 / Fs # sampling interval length (in s) # nominal system parameters fcut = 20e3 # low-pass filter cut-off frequency (6 dB) L = 100 # filter order b = kaiser_lowpass(L, fcut, Fs)[0] # uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range(runs): # Monte Carlo for filter coefficients of low-pass filter B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov(B, rowvar=False)) # covariance matrix of MC result # simulate input and output signals time = np.arange(0, 499 * Ts, Ts) # time values noise = 1e-5 # std of white noise x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) # input signal y, Uy = FIRuncFilter(x, noise, b, Ub, blow=b) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC(x, noise, b, np.ones(1), Ub, runs=1000, blow=b) # apply uncertain FIR filter (Monte Carlo) plt.figure(1) plt.cla() plt.plot(time, x, label="input") plt.plot(time, y, label="output")
uf0 = 0.5 f = np.linspace(0, f0 * 1.2, 20) # Monte Carlo for calculation of unc. assoc. with [real(H),imag(H)] runs = 10000 MCS0 = S0 + rst.randn(runs) * uS0 MCd = delta + rst.randn(runs) * udelta MCf0 = f0 + rst.randn(runs) * uf0 f = np.linspace(0, 1.2 * f0, 30) HMC = sos_FreqResp(MCS0, MCd, MCf0, f) Hc = np.mean(HMC, dtype=complex, axis=1) H = np.r_[np.real(Hc), np.imag(Hc)] UH = np.cov(np.r_[np.real(HMC), np.imag(HMC)], rowvar=True) UH = make_semiposdef(UH) p, Up, HMC2 = fit_sos(f, Hc, UH, scaling=1, MCruns=10000) figure(1) errorbar( range(1, 4), [p[0] * 10, p[1] * 10, p[2] / 10], np.sqrt(np.diag(Up)) * np.array([10, 10, 0.1]), fmt=".", ) errorbar( np.arange(1, 4) + 0.2, [S0 * 10, delta * 10, f0 / 10], [uS0 * 10, udelta * 10, uf0 / 10], fmt=".",
# nominal system parameter fcut = 20e3 # low pass filter cut-off frequency (in Hz) L = 6 # filter order b, a = dsp.butter(L, 2 * fcut / Fs, btype='lowpass') # nominal filter coefficients # uncertain knowledge: fcut between 19.8kHz and 20.2kHz runs = 1000 # number of Monte Carlo runs FC = fcut + (2 * np.random.rand(runs) - 1) * 0.2e3 # cut-off frequency range AB = np.zeros((runs, len(b) + len(a) - 1)) # filter coefficients for k in range(runs): # Monte Carlo for uncertainty of filter coefficients bb, aa = dsp.butter(L, 2 * FC[k] / Fs, btype='lowpass') AB[k, :] = np.hstack((aa[1:], bb)) Uab = make_semiposdef(np.cov(AB, rowvar=0)) # correct for numerical errors # Apply filter time = np.arange(0, 499 * Ts, Ts) # time values t0 = 100 * Ts t1 = 300 * Ts # left and right boundary of rect signal height = 0.9 # input signal height noise = 1e-3 # std of white noise x = rect(time, t0, t1, height, noise=noise) # input signal y, Uy = IIRuncFilter(x, noise, b, a, Uab) # apply IIR formula (GUM) yMC, UyMC = MC.SMC(x, noise, b, a, Uab, runs=10000) # apply sequential Monte Carlo method (GUM S1) plt.figure(1) plt.cla()
# sensor/measurement system S0 = 0.124; uS0= 0.001 delta = 0.01; udelta = 0.001 f0 = 36; uf0 = 0.5 f = np.linspace(0, f0*1.2, 20) # Monte Carlo for calculation of unc. assoc. with [real(H),imag(H)] runs = 10000 MCS0 = S0 + rst.randn(runs)*uS0 MCd = delta+ rst.randn(runs)*udelta MCf0 = f0 + rst.randn(runs)*uf0 f = np.linspace(0, 1.2*f0, 30) HMC = sos_FreqResp(MCS0, MCd, MCf0, f) Hc = np.mean(HMC,dtype=complex,axis=1) H = np.r_[np.real(Hc), np.imag(Hc)] UH= np.cov(np.r_[np.real(HMC),np.imag(HMC)],rowvar=True) UH= make_semiposdef(UH) p, Up, HMC2 = fit_sos(f, Hc, UH, scaling = 1, MCruns = 10000) figure(1) errorbar(range(1,4), [p[0]*10,p[1]*10,p[2]/10], np.sqrt(np.diag(Up))*np.array([10,10,0.1]), fmt=".") errorbar(np.arange(1,4)+0.2, [S0*10, delta*10, f0/10], [uS0*10, udelta*10, uf0/10], fmt=".") xlim(0,4) xticks([1.1,2.1,3.1],[r"$S_0$",r"$\delta$",r"$f_0$"]) xlabel("scaled model parameters") show()
Ts = 1 / Fs # sampling interval length (in s) # nominal system parameters fcut = 20e3 # low-pass filter cut-off frequency (6 dB) L = 100 # filter order b = kaiser_lowpass(L,fcut,Fs)[0] # uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2*np.random.rand(runs)-1)*0.5e3 B = np.zeros((runs,L+1)) for k in range(runs): # Monte Carlo for filter coefficients of low-pass filter B[k,:] = kaiser_lowpass(L,FC[k],Fs)[0] Ub = make_semiposdef(np.cov(B,rowvar=0)) # covariance matrix of MC result # simulate input and output signals time = np.arange(0,499*Ts,Ts) # time values noise = 1e-5 # std of white noise x = rect(time,100*Ts,250*Ts,1.0,noise=noise) # input signal y, Uy = FIRuncFilter(x, noise, b, Ub, blow=b) # apply uncertain FIR filter (GUM formula) yMC,UyMC = MC.MC(x,noise,b,[1.0],Ub,runs=1000,blow=b) # apply uncertain FIR filter (Monte Carlo) plt.figure(1); plt.cla() plt.plot(time, x, label="input") plt.plot(time, y, label="output") plt.xlabel("time / au") plt.ylabel("signal amplitude / au") plt.legend()
def monte_carlo( measurement_system, random_number_generator, sampling_freq, freqs, complex_freq_resp, reference_array_path, ): udelta = 0.1 * measurement_system["delta"] uS0 = 0.001 * measurement_system["S0"] uf0 = 0.01 * measurement_system["f0"] runs = 10000 MCS0 = random_number_generator.normal( loc=measurement_system["S0"], scale=uS0, size=runs ) MCd = random_number_generator.normal( loc=measurement_system["delta"], scale=udelta, size=runs ) MCf0 = random_number_generator.normal( loc=measurement_system["f0"], scale=uf0, size=runs ) HMC = np.empty((runs, len(freqs)), dtype=complex) for index, mcs0_mcd_mcf0 in enumerate(zip(MCS0, MCd, MCf0)): bc_, ac_ = sos_phys2filter(mcs0_mcd_mcf0[0], mcs0_mcd_mcf0[1], mcs0_mcd_mcf0[2]) b_, a_ = dsp.bilinear(bc_, ac_, sampling_freq) HMC[index, :] = dsp.freqz(b_, a_, 2 * np.pi * freqs / sampling_freq)[1] H = complex_2_real_imag(complex_freq_resp) assert_allclose( H, np.load( os.path.join(reference_array_path, "test_LSFIR_H.npz"), )["H"], ) uAbs = np.std(np.abs(HMC), axis=0) assert_allclose( uAbs, np.load( os.path.join(reference_array_path, "test_LSFIR_uAbs.npz"), )["uAbs"], rtol=3.5e-2, ) uPhas = np.std(np.angle(HMC), axis=0) assert_allclose( uPhas, np.load( os.path.join(reference_array_path, "test_LSFIR_uPhas.npz"), )["uPhas"], rtol=4.3e-2, ) UH = np.cov(np.hstack((np.real(HMC), np.imag(HMC))), rowvar=False) UH = make_semiposdef(UH) assert_allclose( UH, np.load( os.path.join(reference_array_path, "test_LSFIR_UH.npz"), )["UH"], atol=1, ) return {"H": H, "uAbs": uAbs, "uPhas": uPhas, "UH": UH}
# nominal system parameter fcut = 20e3 # low pass filter cut-off frequency (in Hz) L = 6 # filter order b,a = dsp.butter(L,2*fcut/Fs,btype='lowpass') # nominal filter coefficients # uncertain knowledge: fcut between 19.8kHz and 20.2kHz runs = 1000 # number of Monte Carlo runs FC = fcut + (2*np.random.rand(runs)-1)*0.2e3 # cut-off frequency range AB = np.zeros((runs,len(b)+len(a)-1)) # filter coefficients for k in range(runs): # Monte Carlo for uncertainty of filter coefficients bb,aa = dsp.butter(L,2*FC[k]/Fs,btype='lowpass') AB[k,:] = np.hstack((aa[1:],bb)) Uab = make_semiposdef(np.cov(AB,rowvar=0)) # correct for numerical errors # Apply filter time = np.arange(0,499*Ts,Ts) # time values t0 = 100*Ts; t1 = 300*Ts # left and right boundary of rect signal height = 0.9 # input signal height noise = 1e-3 # std of white noise x = rect(time,t0,t1,height,noise=noise) # input signal y,Uy = IIRuncFilter(x, noise, b, a, Uab) # apply IIR formula (GUM) yMC,UyMC = MC.SMC(x,noise,b,a,Uab,runs=10000) # apply sequential Monte Carlo method (GUM S1) plt.figure(1);plt.cla() plt.plot(time*1e3, x, label="input signal") plt.plot(time*1e3, y, label="output signal") plt.xlabel('time / ms',fontsize=22)
def conduct_validation_of_FIRuncFilter(): # parameters of simulated measurement Fs = 100e3 # sampling frequency (in Hz) Ts = 1 / Fs # sampling interval length (in s) # nominal system parameters fcut = 20e3 # low-pass filter cut-off frequency (6 dB) L = 100 # filter order b1 = kaiser_lowpass(L, fcut, Fs)[0] b2 = kaiser_lowpass(L - 20, fcut, Fs)[0] # uncertain knowledge: cutoff between 19.5kHz and 20.5kHz runs = 1000 FC = fcut + (2 * np.random.rand(runs) - 1) * 0.5e3 B = np.zeros((runs, L + 1)) for k in range( runs): # Monte Carlo for filter coefficients of low-pass filter B[k, :] = kaiser_lowpass(L, FC[k], Fs)[0] Ub = make_semiposdef(np.cov( B, rowvar=False)) # covariance matrix of MC result # simulate input and output signals nTime = 500 time = np.arange(nTime) * Ts # time values # different cases sigma_noise = 1e-2 # 1e-5 for kind in ["float", "corr", "diag"]: for blow in [None, b2]: print(kind, type(blow)) if kind == "float": # input signal + run methods x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=sigma_noise) Uy, UyMC, y, yMC = _conduct_FIRuncFilter_and_MonteCarlo( Ub, b1, blow, kind, runs, sigma_noise, x) elif kind == "corr": # get an instance of noise, the covariance and the covariance-matrix # with # the specified color color = "red" noise = power_law_noise(N=nTime, color_value=color, std=sigma_noise) Ux = power_law_acf(nTime, color_value=color, std=sigma_noise) # input signal x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) # build Ux_matrix from autocorrelation Ux Ux_matrix = toeplitz(trimOrPad(Ux, nTime)) # run methods y, Uy = FIRuncFilter( x, Ux, b1, Ub, blow=blow, kind=kind) # apply uncertain FIR filter (GUM formula) yMC, UyMC = MC( x, Ux_matrix, b1, np.ones(1), Ub, runs=runs, blow=blow) # apply uncertain FIR filter (Monte Carlo) elif kind == "diag": sigma_diag = sigma_noise * ( 1 + np.heaviside(np.arange(len(time)) - len(time) // 2.5, 0) ) # std doubles after half of the time noise = sigma_diag * white_gaussian(len(time)) # input signal + run methods x = rect(time, 100 * Ts, 250 * Ts, 1.0, noise=noise) Uy, UyMC, y, yMC = _conduct_FIRuncFilter_and_MonteCarlo( Ub, b1, blow, kind, runs, sigma_diag, x) # compare FIR and MC results plt.figure(1) plt.cla() plt.plot(time, x, label="input") plt.plot(time, y, label="output FIR direct") plt.plot(time, yMC, label="output FIR MC") plt.xlabel("time [s]") plt.ylabel("signal amplitude [1]") plt.legend() plt.figure(2) plt.cla() plt.plot(time, Uy, label="FIR formula") plt.plot(time, np.sqrt(np.diag(UyMC)), label="Monte Carlo") plt.xlabel("time [s]") plt.ylabel("signal uncertainty [1]") plt.legend() plt.show()
# Monte Carlo for calculation of unc. assoc. with [real(H),imag(H)] runs = 10000 MCf0 = f0 + rst.randn(runs)*uf0 # MC draws of resonance frequency MCS0 = S0 + rst.randn(runs)*uS0 # MC draws of static gain MCd = delta+ rst.randn(runs)*udelta # MC draws of damping f = np.linspace(0, 120e3, 200) # frequencies at which to calculate frequency response HMC = np.zeros((runs, len(f)),dtype=complex) # initialise frequency response with zeros for k in range(runs): # actual Monte Carlo bc_,ac_ = sos_phys2filter(MCS0[k], MCd[k], MCf0[k]) # calculate analogue filter coefficients b_,a_ = dsp.bilinear(bc_,ac_,Fs) # transform to digital filter coefficients HMC[k,:] = dsp.freqz(b_,a_,2*np.pi*f/Fs)[1] # calculate DFT frequency response Hc = np.mean(HMC,dtype=complex,axis=0) # best estimate (complex-valued) H = np.r_[np.real(Hc), np.imag(Hc)] # best estimate in real, imag UH= np.cov(np.c_[np.real(HMC),np.imag(HMC)],rowvar=0) # covariance of real, imag UH= make_semiposdef(UH, verbose=True) # correct for numerical errors bF, UbF = deconv.LSFIR_unc(H,UH,N,tau,f,Fs) # Calculation of FIR deconvolution filter and its assoc. unc. CbF = UbF/(np.tile(np.sqrt(np.diag(UbF))[:,np.newaxis],(1,N+1))* np.tile(np.sqrt(np.diag(UbF))[:,np.newaxis].T,(N+1,1))) # correlation of filter coefficients # Deconvolution Step1: lowpass filter for noise attenuation fcut = f0+20e3; low_order = 100 # cut-off frequency and filter order blow, lshift = kaiser_lowpass(low_order, fcut, Fs) # FIR low pass filter coefficients shift = tau + lshift # delay of low-pass plus that of the FIR deconv filter # Deconvolution Step2: Application of deconvolution filter xhat,Uxhat = FIRuncFilter(yn,noise,bF,UbF,shift,blow) # apply low-pass and FIR deconv filter # Plot of results