准备
本篇笔记将使用python演示锁相放大器的基本原理。
首先引入需要用到的package,使用%matplotlib widget
可以产生交互式的图片。
1 2 3 4 5 6 import numpy as npfrom scipy.fftpack import fftfrom scipy import signal as sgfrom matplotlib import pyplot as plt%matplotlib widget
绘图
在本文中将会产生许多的图片,为了方便绘图编写一个展示信号的绘图函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def draw (x, y, title='' , xlable='time(s)' , ylable='amplitude' , L=-3.8 , H=3.8 ): ''' draw Parameters ---------- x : numpy array abscissa y : numpy array ordinate Returns ------- ''' plt.close() if L!=H: plt.ylim(L, H) plt.title(title) plt.xlabel(xlable) plt.ylabel(ylable) plt.plot(x, y) plt.show()
正弦波
在本实验中将采用正弦波作为输入信号。另外,参考信号、噪声信号等都需要用到正弦信号,所以第一步将编写一个产生给定幅度、频率和相位的正弦信号的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def g_sin (t=1 , amp=1 , f0=10 , fs=500 , phi=0 ): ''' generate sinusoid Parameters ---------- t : float time length of the generated sequence amp : float amplitude f0 : float frequency of sinusoid in Hz fs : float sampling rate per second phi : float initial phase in deg Returns ------- anonymous : list[numpy array, numpy array] [abscissa, a sinusoid signal] ''' T = 1 /fs N = t/T x = 2 *np.pi*np.arange(N)*T return [np.arange(0 , t, T), amp*np.sin(f0*x+phi*np.pi/180 )] x, y = g_sin() draw(x, y)
方波
参考信号可以使用正弦波、方波等。在本实验中将使用方波,而方波的傅里叶级数为:
f ( x ) = 4 π ∑ n = 0 ∞ s i n [ ( 2 n + 1 ) ω r t ] 2 n + 1 f(x)=\frac{4}{\pi}\sum\limits_{n=0}^{\infty}\frac{sin[(2n+1)\omega_rt]}{2n+1}
f ( x ) = π 4 n = 0 ∑ ∞ 2 n + 1 s i n [ ( 2 n + 1 ) ω r t ]
所以我们可以利用上面的正弦波函数,产生一个近似的方波,方波的阶数(即K)越大近似效果越好,K=50时就有很好的效果。 (PS:此处方波均指占空比为50%,正负对称的方波)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 def g_square_wave (t=1 , amp=1 , f0=10 , fs=500 , K=50 ): ''' generate square wave Parameters ---------- t : float time length of the generated sequence amp : float amplitude f0 : float frequency of sinusoid in Hz fs : float sampling rate per second K : int order of fourier series Returns ------- anonymous : list[numpy array, numpy array] [abscissa, a square wave] ''' T = 1 /fs N = t/T x = 2 *np.pi*np.arange(N)*T y = np.zeros(len (x)) k = 2 *np.arange(1 , K)-1 for i in range (len (x)): y[i] = amp*np.sum (np.sin(k*f0*x[i])/k) return [np.arange(0 , t, T), 4 *y/np.pi] x, y = g_square_wave() draw(x, y)
使用上述两个函数,可以叠加产生带有噪声的信号。
1 2 3 4 x, y1 = g_sin(amp=0.2 , f0=30 ) x, y2 = g_sin(amp=0.35 , f0=3 , phi=45 ) x, y3 = g_square_wave(amp=0.1 , f0=10 ) draw(x, y1+y2+y3)
频域分析
这些噪声在时域中难以分辨。不过信号与噪声不同,往往具有明显的频率特征,而噪声一般是与频率无关的,或者是在特定频率范围内的,所以在频域内可以很好的分辨出输入信号的各种成分。
使用快速傅里叶变换(FFT)从时域转化到频域,并且对振幅谱进行取半归一化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def FFT (s, fs ): ''' fast fourier transform Parameters ---------- s : numpy array signal fs : float sampling rate per second Returns ------- ''' X = fft(s) mX = np.abs (X) pX = np.angle(X) draw(np.arange(int (fs/2 )), mX[range (int (fs/2 ))]/fs, 'Input signal in frequency domain' , 'frequency(Hz)' , 'amplitude' , 0 , 0 ) FFT(y1+y2+y3, 500 )
高斯白噪声
上面的信号添加的噪声还是正弦信号,而现实中比较常见的噪声是白噪声。下面的函数可以根据输入信号产生一个特定信噪比的白噪声。
信噪比的定义是:
S N R ( d B ) = 10 l g P s i g n a l P n o i s e = 20 l g A s i g n a l A n o i s e SNR(dB)=10lg\frac{P_{signal}}{P_{noise}}=20lg\frac{A_{signal}}{A_{noise}}
S N R ( d B ) = 1 0 l g P n o i s e P s i g n a l = 2 0 l g A n o i s e A s i g n a l
在本实验中,功率P P P 可以用功W = ∫ P d t = C ∫ A 2 d t W=\int Pdt=C\int A^2dt W = ∫ P d t = C ∫ A 2 d t 代替,因为信号和噪声的积分时间是相同的。而信号的波形是采样得到的,所以W W W 可以用∑ x i 2 \sum x_i^2 ∑ x i 2 代替,于是有:
S N R = 10 l g ∑ s i 2 ∑ n i 2 ⟹ ∑ n i 2 = ∑ s i 2 1 0 S N R / 10 SNR=10lg\frac{\sum s_i^2}{\sum n_i^2} \Longrightarrow \sum n_i^2= \frac{\sum s_i^2}{10^{SNR/10}}
S N R = 1 0 l g ∑ n i 2 ∑ s i 2 ⟹ ∑ n i 2 = 1 0 S N R / 1 0 ∑ s i 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 def wgn (x, snr ): ''' generate Gauss white noise Parameters ---------- x : numpy array signal snr : float signal-to-noise ratio Returns ------- anonymous : numpy array Gauss white noise ''' snr = 10 **(snr/10.0 ) xpower = np.sum (x**2 )/len (x) npower = xpower / snr return np.random.randn(len (x)) * np.sqrt(npower) noise = wgn(np.sin(np.arange(0 , 1000000 )*0.1 ), 6 ) plt.close() plt.subplot(211 ) plt.hist(noise, bins=100 ) plt.subplot(212 ) plt.psd(noise) plt.show()
模拟
下面开始正式的模拟过程:
获得输入信号,输入信号由10Hz的目标信号和其他频率的正弦噪声以及白噪声组成
A. 增加正弦部分
1 2 3 4 5 x, y1 = g_sin(amp=0.1 ) x, y2 = g_sin(amp=0.5 , f0=50 ) x, y3 = g_sin(amp=0.8 , f0=3 ) x, y4 = g_sin(amp=0.3 , f0=200 ) signal = y1+y2+y3+y4
B. 增加白噪声部分
1 2 3 noise = wgn(signal, 10 ) sn = signal+noise draw(x, sn)
C. 查看输入信号的频域信息
获得参考信号,方波频率与目标信号相同,为10Hz
1 2 3 x, y = g_square_wave(K=100 ) square = y draw(x, square)
前置放大×2倍
输入信号进行带通滤波,除去/抑制部分噪声,增强锁相放大器的性能(动态储备和输出动态范围)
1 2 3 b, a = sg.butter(1 , [0.02 , 0.12 ], 'bandpass' ) snq = sg.filtfilt(b, a, sn) draw(x, snq)
调谐放大输入信号×5,增大信噪比
1 2 3 4 b, a = sg.butter(1 , [0.038 , 0.042 ], 'bandpass' ) sns = sg.filtfilt(b, a, snq) snq = sns*4 +snq draw(x, snq)
PSD(相敏检波器)处理输入与参考信号(本质为乘法器)
1 2 snqx = snq*square draw(x, snqx)
低通滤波,减少其他信号对直流分量的影响(等效噪声宽度很小)
1 2 3 b, a = sg.butter(3 , 0.00004 , 'lowpass' ) sns = sg.filtfilt(b, a, snqx) draw(x, sns)
直流放大×5
1 2 sns = sns*5 draw(x, sns)