前言

我们先讲讲锁相放大器的基本结构示于下方图 ,包括信号通道、参考通道、相敏检测器 PSD 和低通滤波器 LPF 等。 各个模块的基本功能描述如下:

python如何获得屏幕的放大系数_Python

  • 1、信号通道: 对信号输入进行放大及滤波, 将微弱信号放大到足以推动相敏检测器工作的电平, 并且要滤除部分干扰和噪声。
  • 2、参考输入: 一般是等幅正弦或者方波信号, 它可以是从外部输入的某种周期信号,也可以是系统内原先用于调制的载波信号或用于斩波的信号。
  • 3、参考通道: 对参考输入进行放大或衰减, 以适应相敏检测器对幅度的要求。参考通道的另一个重要功能是对参考输入进行相位锁定及移相等处理,从而产生同频正弦波与余弦波,以提供给相敏检波器进行乘法运算。相敏检波器(PSD): 以参考通道提供的基准正弦与余弦分量作为输入, 对经过信号通道放大滤波的 进行相敏检波(乘法运算), 从而实现检波。
  • 4、 如下方图所示频谱迁移过程。将 的频谱由 处迁移到 处,再经 LPF 滤除噪声, 其输出 对 的幅度和相位都敏感,这样就达到了既鉴幅又鉴相(相位及幅度的测量)的目的。
  • python如何获得屏幕的放大系数_matplotlib_02

  • 5、低通滤波器(LPF): 频带可以做得很窄, 所以可使锁相放大器达到较大的 SNIR。 锁相放大器的应用大致分为以下领域:测量噪声中的微弱信号:
  • 红外分光光度计,2次量子光分光分析,光声光谱仪,超导材料的评价,金属材料的张力试验,放大器的CMRR或IMRR测量。
  • 测量被测定体传输函数的微小变化:俄歇电子能谱,金属探测器,涡流探伤器。被测定体的矢量分析:RLC测量仪,电解-阻抗,电子束测量。

python实现模拟锁相放大器

首先引入需要用到的package,使用%matplotlib widget可以产生交互式的图片。

import numpy as np
from scipy.fftpack import fft
from scipy import signal as sg
from matplotlib import pyplot as plt

%matplotlib widget

matplotlib绘图

在本文中将会产生许多的图片,为了方便绘图编写一个展示信号的绘图函数。

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
    -------
    '''
    # canvas settings
    plt.close()
    if L!=H:
        plt.ylim(L, H)
    plt.title(title)
    plt.xlabel(xlable)
    plt.ylabel(ylable)

    plt.plot(x, y)
    plt.show()

绘画正弦波

采用正弦波作为输入信号。另外,参考信号、噪声信号等都需要用到正弦信号,所以第一步将编写一个产生给定幅度、频率和相位的正弦信号的函数。

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)

python如何获得屏幕的放大系数_Python_03

绘画方波

参考信号可以使用正弦波、方波等。在本实验中将使用方波,而方波的傅里叶级数为:

  

python如何获得屏幕的放大系数_python_04


所以我们可以利用上面的正弦波函数,产生一个近似的方波,方波的阶数(即K)越大近似效果越好,K=50时就有很好的效果。 (PS:此处方波均指占空比为50%,正负对称的方波)

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)

python如何获得屏幕的放大系数_Python_05


使用上述两个函数,可以叠加产生带有噪声的信号。

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)

python如何获得屏幕的放大系数_matplotlib_06

绘画频域分析

这些噪声在时域中难以分辨。不过信号与噪声不同,往往具有明显的频率特征,而噪声一般是与频率无关的,或者是在特定频率范围内的,所以在频域内可以很好的分辨出输入信号的各种成分。

使用快速傅里叶变换(FFT)从时域转化到频域,并且对振幅谱进行取半归一化。

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)   # magnitude
    pX = np.angle(X) # phase

    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)

python如何获得屏幕的放大系数_matplotlib_07

绘画高斯白噪声

上面的信号添加的噪声还是正弦信号,而现实中比较常见的噪声是白噪声。下面的函数可以根据输入信号产生一个特定信噪比的白噪声。

信噪比的定义是:

python如何获得屏幕的放大系数_正弦波_08


在本实验中,功率python如何获得屏幕的放大系数_python如何获得屏幕的放大系数_09可以用功python如何获得屏幕的放大系数_python_10`代替,因为信号和噪声的积分时间是相同的。而信号的波形是采样得到的,所以python如何获得屏幕的放大系数_python_11可以用python如何获得屏幕的放大系数_Python_12代替,于是有:


python如何获得屏幕的放大系数_正弦波_13

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()

python如何获得屏幕的放大系数_正弦波_14

模拟锁相放大器的工作过程

下面开始正式的模拟过程:

获得输入信号,输入信号由10Hz的目标信号和其他频率的正弦噪声以及白噪声组成

A、 增加正弦部分

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、增加白噪声部分

noise = wgn(signal, 10)
sn    = signal+noise
draw(x, sn)

python如何获得屏幕的放大系数_python如何获得屏幕的放大系数_15


C、 查看输入信号的频域信息

FFT(sn, 500)

python如何获得屏幕的放大系数_python_16


获得参考信号,方波频率与目标信号相同,为10Hz

x, y   = g_square_wave(K=100)
square = y
draw(x, square)

python如何获得屏幕的放大系数_正弦波_17


3、前置放大×2倍

sn = sn*2
draw(x, sn)

python如何获得屏幕的放大系数_matplotlib_18


4、输入信号进行带通滤波,除去/抑制部分噪声,增强锁相放大器的性能(动态储备和输出动态范围)

b, a = sg.butter(1, [0.02, 0.12], 'bandpass') # 5Hz - 30Hz
snq  = sg.filtfilt(b, a, sn)
draw(x, snq)

python如何获得屏幕的放大系数_正弦波_19


5、调谐放大输入信号×5,增大信噪比

b, a = sg.butter(1, [0.038, 0.042], 'bandpass') # 9.5Hz - 10.5Hz
sns  = sg.filtfilt(b, a, snq)
snq  = sns*4+snq
draw(x, snq)

python如何获得屏幕的放大系数_matplotlib_20


6、PSD(相敏检波器)处理输入与参考信号(本质为乘法器)

snqx = snq*square
draw(x, snqx)

python如何获得屏幕的放大系数_python_21

FFT(snqx, 500)

python如何获得屏幕的放大系数_Python_22


7、低通滤波,减少其他信号对直流分量的影响(等效噪声宽度很小)

b, a = sg.butter(3, 0.00004, 'lowpass') # 0 - 0.01Hz
sns  = sg.filtfilt(b, a, snqx)
draw(x, sns)

python如何获得屏幕的放大系数_matplotlib_23


8、直流放大×5

sns = sns*5
draw(x, sns)

python如何获得屏幕的放大系数_python如何获得屏幕的放大系数_24