先来讲讲主界面对应的程序,这样比较直观,能知道每个功能、按钮对应的变量名,以及背后的函数。
代码链接:https://github.com/rtlsdrblog/kerberossdr/blob/master/_GUI/hydra_main_window.py
先结合截图讲一下要用到的界面操作,注意我截图用的是老版本(白色背景)的,实际现在的代码是黑色背景了,里面有一些不一样的,比如对均匀圆阵的spacing factor的计算。
你可以先点一下start processing,可以看到画面会开始刷新。刷新率对应update rate。
先要设置center frequency也就是中心频点和sampleing frequency也就是采样率,中心频点就是你要找的目标信号的频率值,采样率如果是测向推荐使用1MHz,我习惯用0.9MHz。Gain是4个接收机增益,我习惯统一设置为28,你可以自己调节,但它必须设置在15.7以上否则无法同步。点击Set按钮,这样程序会把这些设置下发给设备。你可以选择DC compensation,它会提升测量效果,不是一定要选的。你可以勾选enable spectrum display来观察4个通道上收到的信号。看完了就可以关掉这个显示,可以降低CPU占用率。
接下来要开始校准了,我说的校准是针对DOA应用的,被动雷达的校准有点不一样。DOA的校准必须把天线拔下来换成50欧姆虚阻抗,并且必须做采样时间同步和相位同步。校准前要保证filter、FIR、Decimation都是默认值不改变。
接下来就可以切换到Sync页面了,这个校准只针对于你之前设置的中心频率、采样率、增益,如果你要修改这些值,必须重新校准。你可以勾选enable sync display、noise source on/off。这样就会出现下面的画面。
你可以看到ABS X Corr(互相关的模)上有3个尖峰,并且下方Sample Delay History(采样延迟历史)和Phase Diff History(相位差历史)上的值都不是0。这表示4个通道都没有同步。
这时候点击Sample Sync(采样同步),稍等一会,ABS X Corr上的3个尖就会重合到中间位置,并且Sample Delay History的3根曲线变为0 ,这说明现在4个通道都没有采样时间差了,Phase Diff History也会有变化,从杂乱无章变为3根恒定的直线,说明相位差还是存在但是变为了一个常数值,(参见下图黄色圈中的部分)。
这时候点一下Correct IQ(校准IQ),它会校准4个通道的相位差,这样Phase Diff History上的曲线都会变为0。这样采样时间同步和相位同步就完成了。校准完毕后要把Sync Display和Noise Source关掉,就是把当前界面上两个勾选去掉就行。注意每次重新上电,或者改变中心频点,采样率,增益都要重新做这个校准步骤。
接下来可以就可以测向了。先把天线接回去。然后回到Configuration and Spectrum页面。确保Enable Spectrum Display勾选上了,检查一下待测信号在频谱图上是否出现在中间位置。
接下来我们要改变Filter BW、FIR tap size和Decimation的值。FIlter BW代表对信号进行低通滤波的滤波器宽度,如果目标信号带宽25kHz,那么这里就设置为25,FIR tap size代表的是滤波器的抽头,数字越大滤波越明显,但是对计算机性能要求越高,一般设置为100,Decimation是降采样的比例,必须设置为2的n次方,最大是8,也就是说可选的是1,2,4,8。如果我们设置为4,频谱带宽就会缩减,相当于带宽除以4。
我们先设置Decimation,效果如下图,相当于频谱的中间部分放大了。
然后设置Filter BW和FIR tap size,如下图,旁边不感兴趣的信号就被过滤掉了。
这时候就可以把频谱显示关掉并切换到DOA estimation页面了。
先选择ULA(均匀线阵)还是UCA(均匀圆阵),Spacing里填写的值是根据天线间距和中心频率换算出来的,最佳值是0.33,但在0.1~0.5范围内都是可以的。然后选择一个DOA算法,效果最好的是MUSIC算法。这样你就能看到图像了,最高值对应的横坐标就是测出的角度。UCA可以分清平面内0~360度的角度,ULA只能分清0~180度的,分不清前后,但是ULA受多径效应影响比UCA小,如果用ULA,可以打开FB average模式,进一步减少多径影响,但是kerberos的安卓APP的定位功能只支持UCA,所以我一般使用UCA。
# KerberosSDR Python GUI
# Copyright (C) 2018-2019 Carl Laufer, Tamás Pető
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# -*- coding: utf-8 -*
import sys
import os
import time
import math
import pyqtgraph as pg
import pyqtgraph.exporters
import numpy as np
import scipy
from bottle import route, run, request, get, post, redirect, template, static_file
import threading
import subprocess
np.seterr(divide='ignore') #把除法产生的错误提示忽略掉
# Import Kerberos modules
currentPath = os.path.dirname(os.path.realpath(__file__)) #获取本文件的当前路径,然后去掉文件名
rootPath = os.path.dirname(currentPath) #再次去掉文件名返回目录名,得到kerberossdr根目录
receiverPath = os.path.join(rootPath, "_receiver") #根目录后加上下一层目录名
signalProcessorPath = os.path.join(rootPath, "_signalProcessing") #这样就得到另外程序的目录
sys.path.insert(0, receiverPath) #加入import的优先搜索
sys.path.insert(0, signalProcessorPath)
from hydra_receiver import ReceiverRTLSDR #从hydra_receiver中导入接收机对象
# Import graphical user interface packages
from PyQt4.QtGui import *
from PyQt4 import QtCore
from PyQt4 import QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
# Import packages for plotting
import matplotlib
matplotlib.use('Agg') # For Raspberry Pi compatiblity
from matplotlib import cm
#from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
#import matplotlib.pyplot as plt
#import matplotlib.patches as patches
from hydra_main_window_layout import Ui_MainWindow #导入UI布局
from hydra_signal_processor import SignalProcessor #导入信号处理对象
# Import the pyArgus module
#root_path = os.getcwd()
#pyargus_path = os.path.join(os.path.join(root_path, "pyArgus"), "pyArgus")
#sys.path.insert(0, pyargus_path)
#import directionEstimation as de
from pyargus import directionEstimation as de #测向算法对象
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__ (self,parent = None):
super(MainWindow, self).__init__(parent)
self.setupUi(self) #把界面上的控件都设置好了
#f = open('/dev/null', 'w')
#sys.stdout = f
self.tabWidget.setCurrentIndex(0) #默认显示0号选项卡
# Set pyqtgraph to use white background, black foreground
pg.setConfigOption('background', (61, 61, 61)) #设置函数窗口颜色
pg.setConfigOption('foreground', 'w')
pg.setConfigOption('imageAxisOrder', 'row-major')
#pg.setConfigOption('useOpenGL', True)
#pg.setConfigOption('useWeave', True)
# Spectrum display #频谱显示选项卡
self.win_spectrum = pg.GraphicsWindow(title="Quad Channel Spectrum") #名称
self.export_spectrum = pg.exporters.ImageExporter(self.win_spectrum.scene())
#在win_spectrum内部绘制4个窗口
self.plotWidget_spectrum_ch1 = self.win_spectrum.addPlot(title="Channel 1")
self.plotWidget_spectrum_ch2 = self.win_spectrum.addPlot(title="Channel 2")
self.win_spectrum.nextRow() #画完上面2个换行画下面两个
self.plotWidget_spectrum_ch3 = self.win_spectrum.addPlot(title="Channel 3")
self.plotWidget_spectrum_ch4 = self.win_spectrum.addPlot(title="Channel 4")
self.gridLayout_spectrum.addWidget(self.win_spectrum, 1, 1, 1, 1)
#把win_spectrum加入网格中
x = np.arange(1000)
y = np.random.normal(size=(4,1000))
self.spectrum_ch1_curve = self.plotWidget_spectrum_ch1.plot(x, y[0], clear=True, pen=(255, 199, 15))
self.spectrum_ch2_curve = self.plotWidget_spectrum_ch2.plot(x, y[1], clear=True, pen='r')
self.spectrum_ch3_curve = self.plotWidget_spectrum_ch3.plot(x, y[2], clear=True, pen='g')
self.spectrum_ch4_curve = self.plotWidget_spectrum_ch4.plot(x, y[3], clear=True, pen=(9, 237, 237))
#在4个窗口中绘制4条不同颜色的曲线,数据暂时是随机的
self.plotWidget_spectrum_ch1.setLabel("bottom", "Frequency [MHz]")
self.plotWidget_spectrum_ch1.setLabel("left", "Amplitude [dBm]")
self.plotWidget_spectrum_ch2.setLabel("bottom", "Frequency [MHz]")
self.plotWidget_spectrum_ch2.setLabel("left", "Amplitude [dBm]")
self.plotWidget_spectrum_ch3.setLabel("bottom", "Frequency [MHz]")
self.plotWidget_spectrum_ch3.setLabel("left", "Amplitude [dBm]")
self.plotWidget_spectrum_ch4.setLabel("bottom", "Frequency [MHz]")
self.plotWidget_spectrum_ch4.setLabel("left", "Amplitude [dBm]")
#在4个窗口中加入标记文本
#---> Sync display <--- #同步显示选项卡
# --> Delay #延迟
self.win_sync = pg.GraphicsWindow(title="Receiver Sync") #设置窗口名称
self.export_sync = pg.exporters.ImageExporter(self.win_sync.scene())
self.plotWidget_sync_absx = self.win_sync.addPlot(title="ABS X Corr")
#第一个图像是互相关结果的模
#self.plotWidget_sync_absx.setDownsampling(ds=4, mode='subsample')
#self.plotWidget_sync_normph = self.win_sync.addPlot(title="Normalized Phasors")
self.win_sync.nextRow() #换行
self.plotWidget_sync_sampd = self.win_sync.addPlot(title="Sample Delay History") #第二个图像是采样延迟历史
self.plotWidget_sync_phasediff = self.win_sync.addPlot(title="Phase Diff History") #第三个图像是相位差历史
self.gridLayout_sync.addWidget(self.win_sync, 1, 1, 1, 1)
x = np.arange(1000)
y = np.random.normal(size=(4,1000))
#---> DOA results display <--- #DOA结果显示选项卡
self.win_DOA = pg.GraphicsWindow(title="DOA Plot") #设置窗口名称
#Set up image exporter for web display #下面的exporter是导出给网页显示用的
self.export_DOA = pg.exporters.ImageExporter(self.win_DOA.scene())
self.plotWidget_DOA = self.win_DOA.addPlot(title="Direction of Arrival Estimation") #DOA函数绘制窗口
self.plotWidget_DOA.setLabel("bottom", "Incident Angle [deg]") #底部标签(角度)
self.plotWidget_DOA.setLabel("left", "Amplitude [dB]") #左边标签(幅度)
self.plotWidget_DOA.showGrid(x=True, alpha=0.25) #显示网格
self.gridLayout_DOA.addWidget(self.win_DOA, 1, 1, 1, 1)
self.DOA_res_fd = open("/ram/DOA_value.html","w") # DOA estimation result file descriptor #打开DOA结果显示的网页
# Junk data to just init plot legends
x = np.arange(1000)
y = np.random.normal(size=(4,1000))
self.plotWidget_DOA.addLegend()
self.plotWidget_DOA.plot(x, y[0], pen=pg.mkPen((255, 199, 15), width=2), name="Bartlett")
self.plotWidget_DOA.plot(x, y[1], pen=pg.mkPen('g', width=2), name="Capon")
self.plotWidget_DOA.plot(x, y[2], pen=pg.mkPen('r', width=2), name="MEM")
self.plotWidget_DOA.plot(x, y[3], pen=pg.mkPen((9, 237, 237), width=2), name="MUSIC") #绘制MUSIC算法对应曲线
#---> Passive radar results display <---
self.win_PR = pg.GraphicsWindow(title="Passive Radar")
self.export_PR = pg.exporters.ImageExporter(self.win_PR.scene())
self.plt_PR = self.win_PR.addPlot(Title="Range-Doppler Matrix")
self.plt_PR.setLabel("bottom", "Range")
self.plt_PR.setLabel("left", "Doppler (Speed)")
self.PR_interp_factor = 4
self.plt_PR.getAxis("bottom").setScale(1.0/self.PR_interp_factor)
self.plt_PR.getAxis("left").setScale(1.0/self.PR_interp_factor)
rand_mat = np.random.rand(50,50)
self.img_PR = pg.ImageView()
self.plt_PR.addItem(self.img_PR.imageItem)
self.img_PR.setImage(rand_mat)
self.img_PR.autoLevels()
self.img_PR.autoRange()
# Take the color map from matplotlib because it's nicer than pyqtgraph's
colormap = cm.get_cmap("jet")
colormap._init()
lut = (colormap._lut * 255).view(np.ndarray)
self.img_PR.imageItem.setLookupTable(lut)
#self.img_PR.setPredefinedGradient('spectrum')
self.gridLayout_RD.addWidget(self.win_PR, 1, 1, 1, 1)
# Connect pushbutton signals #把按钮和要触发的函数连起来
self.pushButton_close.clicked.connect(self.pb_close_clicked) #关闭界面
self.pushButton_proc_control.clicked.connect(self.pb_proc_control_clicked)
#数据处理按钮
self.pushButton_sync.clicked.connect(self.pb_sync_clicked) #同步按钮
self.pushButton_iq_calib.clicked.connect(self.pb_calibrate_iq_clicked)
#校准iq按钮
self.pushButton_del_sync_history.clicked.connect(self.pb_del_sync_history_clicked)
#删除历史按钮
self.pushButton_DOA_cal_90.clicked.connect(self.pb_calibrate_DOA_90_clicked)
self.pushButton_set_receiver_config.clicked.connect(self.pb_rec_reconfig_clicked)
#接收机设置按钮
self.stream_state = False
# Status and configuration tab control #选项卡控制
self.tabWidget.currentChanged.connect(self.tab_changed)
# Connect checkbox signals #把复选框和要触发的函数连起来
self.checkBox_en_uniform_gain.stateChanged.connect(self.pb_rec_reconfig_clicked)
#4个通道使用完全相等的增益
self.checkBox_en_sync_display.stateChanged.connect(self.set_sync_params)
#开启同步显示
self.checkBox_en_spectrum.stateChanged.connect(self.set_spectrum_params)
#显示频谱
self.checkBox_en_DOA.stateChanged.connect(self.set_DOA_params)
#开启DOA算法
self.checkBox_en_DOA_Bartlett.stateChanged.connect(self.set_DOA_params)
self.checkBox_en_DOA_Capon.stateChanged.connect(self.set_DOA_params)
self.checkBox_en_DOA_MEM.stateChanged.connect(self.set_DOA_params)
self.checkBox_en_DOA_MUSIC.stateChanged.connect(self.set_DOA_params)
self.checkBox_en_DOA_FB_avg.stateChanged.connect(self.set_DOA_params)
#选择某一种算法以及是否开FB_avg
self.checkBox_en_dc_compensation.stateChanged.connect(self.set_iq_preprocessing_params)
#开启直流分量补偿
self.checkBox_en_passive_radar.stateChanged.connect(self.set_PR_params)
#开启被动雷达
self.checkBox_en_td_filter.stateChanged.connect(self.set_PR_params)
self.checkBox_en_autodet.stateChanged.connect(self.set_PR_params)
self.checkBox_en_noise_source.stateChanged.connect(self.switch_noise_source)
#开启噪声源
# Connect spinbox signals #把微调框和对应函数连起来
self.doubleSpinBox_filterbw.valueChanged.connect(self.set_iq_preprocessing_params)
#滤波器带宽
self.spinBox_fir_tap_size.valueChanged.connect(self.set_iq_preprocessing_params)
#抽头大小
self.spinBox_decimation.valueChanged.connect(self.set_iq_preprocessing_params)
#降采样倍数
self.doubleSpinBox_DOA_d.valueChanged.connect(self.set_DOA_params)
#根据界面和注释调的代码这里本来填的是lamda,但是现在好像可以直接填写天线间距了
self.spinBox_DOA_sample_size.valueChanged.connect(self.set_DOA_params)
#这个决定了DOA采样长度,但是是2的指数幂
self.doubleSpinBox_center_freq.valueChanged.connect(self.set_DOA_params)
#中心频率
self.spinBox_td_filter_dimension.valueChanged.connect(self.set_PR_params)
self.doubleSpinBox_cc_det_max_range.valueChanged.connect(self.set_PR_params)
self.doubleSpinBox_cc_det_max_Doppler.valueChanged.connect(self.set_PR_params)
self.spinBox_ref_ch_select.valueChanged.connect(self.set_PR_params)
self.spinBox_surv_ch_select.valueChanged.connect(self.set_PR_params)
self.spinBox_cfar_est_win.valueChanged.connect(self.set_PR_params)
self.spinBox_cfar_guard_win.valueChanged.connect(self.set_PR_params)
self.doubleSpinBox_cfar_threshold.valueChanged.connect(self.set_PR_params)
self.spinBox_resync_time.valueChanged.connect(self.set_resync_time)
#设置重新同步的时间,现在这部分代码注释掉了
# Connect combobox signals #设置下拉选择框
self.comboBox_antenna_alignment.currentIndexChanged.connect(self.set_DOA_params)
#天线阵形
self.comboBox_cc_det_windowing.currentIndexChanged.connect(self.set_windowing_mode)
# Instantiate and configura Hydra modules #实例化接收机并配置
self.module_receiver = ReceiverRTLSDR() #接收机
self.module_receiver.block_size = int(sys.argv[1]) * 1024
#处理的块大小,这是单个通道的iq数据的长度
#是在run.sh脚本启动的时候得到的,对应于run.sh的BUFF_SIZE=256
#即处理的单通道的iq数据长度为256*1024
self.module_signal_processor = SignalProcessor(module_receiver=self.module_receiver)
#把当前的接收机给数据处理部分
self.module_signal_processor.signal_overdrive.connect(self.power_level_update)
#本来可以检测信号饱和,但是现在代码被注释掉了
self.module_signal_processor.signal_period.connect(self.period_time_update)
#信号处理周期计算完成后发给周期显示更新函数
self.module_signal_processor.signal_spectrum_ready.connect(self.spectrum_plot)
#频谱计算完成,告知频谱绘图函数
self.module_signal_processor.signal_sync_ready.connect(self.delay_plot)
#延迟计算完成,告知延迟绘图函数
self.module_signal_processor.signal_DOA_ready.connect(self.DOA_plot)
#DOA计算完成,告知DOA绘制函数
self.module_signal_processor.signal_PR_ready.connect(self.RD_plot)
# -> Set default confiration for the signal processing module
#把界面上的各种设置发送给信号处理模块
self.set_spectrum_params()
self.set_sync_params()
self.set_DOA_params()
self.set_windowing_mode()
self.DOA_time = time.time()
self.PR_time = time.time()
self.sync_time = time.time()
self.spectrum_time = time.time()
#用于更新界面的计时
#self.spectrum_plot()
#self.delay_plot()
#self.DOA_plot()
#self.RD_plot()
# Set default confiuration for the GUI components
self.set_default_configuration()
#把界面设置为默认,这个会把前面做的设置里的频谱和DOA都禁用掉,是否有冲突?
self.ip_addr = sys.argv[2]
threading.Thread(target=run, kwargs=dict(host=self.ip_addr, port=8080, quiet=True, debug=False, server='paste')).start()
#-----------------------------------------------------------------
#
#-----------------------------------------------------------------
def set_default_configuration(self):
self.power_level_update(0)
self.checkBox_en_spectrum.setChecked(False)#取消频谱显示复选框
self.checkBox_en_DOA.setChecked(False)#取消DOA计算复选框
def calculate_spacing(self):
ant_arrangement_index = self.comboBox_antenna_alignment.currentText()#阵型
ant_meters = self.doubleSpinBox_DOA_d.value()#线阵的天线间距 圆阵对角线长度
freq = self.doubleSpinBox_center_freq.value()#中心频率
wave_length = (299.79/freq)#波长
if ant_arrangement_index == "ULA":
ant_spacing = (ant_meters/wave_length)
#如果是线阵 所求lamda就是天线间距/波长
elif ant_arrangement_index == "UCA":
ant_spacing = ((ant_meters/wave_length)/math.sqrt(2))
#如果是圆阵 (对角线长度/波长)/根号2 得到了s' 老版本还要做修正新版本不需要了
return ant_spacing
def tab_changed(self):
tab_index = self.tabWidget.currentIndex() #选项卡切换
if tab_index == 0: # Spectrum tab
self.stackedWidget_config.setCurrentIndex(0)
elif tab_index == 1: # Sync tab
self.stackedWidget_config.setCurrentIndex(1)
elif tab_index == 2: # DOA tab
self.stackedWidget_config.setCurrentIndex(2)
elif tab_index == 3: # PR tab
self.stackedWidget_config.setCurrentIndex(3)
def set_sync_params(self): #根据界面上的同步复选框,使信号处理模块启用或禁用同步
if self.checkBox_en_sync_display.checkState():
self.module_signal_processor.en_sync = True
else:
self.module_signal_processor.en_sync = False
def set_spectrum_params(self): #根据界面上的频谱显示复选框,使信号处理模块启用或禁用频谱
if self.checkBox_en_spectrum.checkState():
self.module_signal_processor.en_spectrum = True
else:
self.module_signal_processor.en_spectrum = False
def pb_rec_reconfig_clicked(self):
center_freq = self.doubleSpinBox_center_freq.value() *10**6
#获得界面上的中心频率 (MHz就是10的6次方)
sample_rate = float(self.comboBox_sampling_freq.currentText()) *10**6
#获得界面上的采样率
#self.doubleSpinBox_sampling_freq.value()*10**6
gain = [0,0,0,0]
#4个接收机的增益数组声明
if self.checkBox_en_uniform_gain.checkState():
#如果勾选统一增益,就用4个相同的数值设置4个接收机
gain[0] = 10*float(self.comboBox_gain.currentText())
gain[1] = 10*float(self.comboBox_gain.currentText())
gain[2] = 10*float(self.comboBox_gain.currentText())
gain[3] = 10*float(self.comboBox_gain.currentText())
gain_index = self.comboBox_gain.currentIndex()
self.module_receiver.receiver_gain = 10*float(self.comboBox_gain.currentText())
form.comboBox_gain_2.setCurrentIndex(int(gain_index))
form.comboBox_gain_2.setEnabled(False)
self.module_receiver.receiver_gain_2 = 10*float(self.comboBox_gain.currentText())
form.comboBox_gain_3.setCurrentIndex(int(gain_index))
form.comboBox_gain_3.setEnabled(False)
self.module_receiver.receiver_gain_3 = 10*float(self.comboBox_gain.currentText())
form.comboBox_gain_4.setCurrentIndex(int(gain_index))
form.comboBox_gain_4.setEnabled(False)
self.module_receiver.receiver_gain_4 = 10*float(self.comboBox_gain.currentText())
else:
#否则就是用各自不同的增益值设置接收机
gain[0] = 10*float(self.comboBox_gain.currentText())
gain[1] = 10*float(self.comboBox_gain_2.currentText())
gain[2] = 10*float(self.comboBox_gain_3.currentText())
gain[3] = 10*float(self.comboBox_gain_4.currentText())
self.module_receiver.receiver_gain = 10*float(self.comboBox_gain.currentText())
form.comboBox_gain_2.setEnabled(True)
self.module_receiver.receiver_gain_2 = 10*float(self.comboBox_gain_2.currentText())
form.comboBox_gain_3.setEnabled(True)
self.module_receiver.receiver_gain_3 = 10*float(self.comboBox_gain_3.currentText())
form.comboBox_gain_4.setEnabled(True)
self.module_receiver.receiver_gain_4 = 10*float(self.comboBox_gain_4.currentText())
self.module_receiver.fs = float(self.comboBox_sampling_freq.currentText())*10**6
#对接收机硬件设置采样率
#self.doubleSpinBox_sampling_freq.value()*10**6
self.module_signal_processor.fs = self.module_receiver.fs/self.module_receiver.decimation_ratio
#信号处理模块的采样率是接收机硬件的采样率/降采样倍数
self.module_signal_processor.center_freq=self.doubleSpinBox_center_freq.value() *10**6
#信号处理模块获得中心频率
self.module_receiver.reconfigure_tuner(center_freq, sample_rate, gain)
#设置接收机的中心频率、采样率、增益
def switch_noise_source(self):
if self.checkBox_en_noise_source.checkState():
#根据界面上的噪声源复选框,使信号处理模块和接收机模块获取是否开启噪声源
self.module_signal_processor.noise_checked = True
self.module_receiver.switch_noise_source(1)
else:
self.module_signal_processor.noise_checked = False
self.module_receiver.switch_noise_source(0)
def set_iq_preprocessing_params(self):
"""
Update IQ preprocessing parameters
Callback function of:
-
"""
#这里设置的是iq预处理的参数
# Set DC compensations
if self.checkBox_en_dc_compensation.checkState():
#根据界面上直流抑制复选框,设置接收机是否要做这个操作
self.module_receiver.en_dc_compensation = True
else:
self.module_receiver.en_dc_compensation = False
# Set FIR filter parameters
#设置FIR滤波器参数
tap_size = self.spinBox_fir_tap_size.value()
bw = self.doubleSpinBox_filterbw.value() * 10**3 # ->[kHz]
接收机模块获得fir滤波器的抽头和带宽
self.module_receiver.set_fir_coeffs(tap_size, bw)
# Set Decimation
#设置降采样率倍数
self.module_receiver.decimation_ratio = self.spinBox_decimation.value()
#接收机模块获取降采样倍数
self.module_signal_processor.fs = self.module_receiver.fs/self.module_receiver.decimation_ratio
#信号处理模块获得采样率=接收机采样率/降采样倍数
def set_windowing_mode(self):
self.module_signal_processor.windowing_mode = int(self.comboBox_cc_det_windowing.currentIndex())
def set_DOA_params(self):
"""
Update DOA processing parameters
Callback function of:
-
"""
# Set DOA processing option
if self.checkBox_en_DOA.checkState():
#根据界面上的DOA复选框,启用信号处理模块的DOA算法
self.module_signal_processor.en_DOA_estimation = True
else:
self.module_signal_processor.en_DOA_estimation = False
#根据选择的DOA算法,信号处理模块会使用对应的函数来运算
if self.checkBox_en_DOA_Bartlett.checkState():
self.module_signal_processor.en_DOA_Bartlett = True
else:
self.module_signal_processor.en_DOA_Bartlett = False
if self.checkBox_en_DOA_Capon.checkState():
self.module_signal_processor.en_DOA_Capon = True
else:
self.module_signal_processor.en_DOA_Capon = False
if self.checkBox_en_DOA_MEM.checkState():
self.module_signal_processor.en_DOA_MEM = True
else:
self.module_signal_processor.en_DOA_MEM = False
if self.checkBox_en_DOA_MUSIC.checkState():
self.module_signal_processor.en_DOA_MUSIC = True
else:
self.module_signal_processor.en_DOA_MUSIC = False
#如果界面勾选了FB_avg,信号处理模块就会使用FB_avg
if self.checkBox_en_DOA_FB_avg.checkState():
self.module_signal_processor.en_DOA_FB_avg = True
else:
self.module_signal_processor.en_DOA_FB_avg = False
#self.module_signal_processor.DOA_inter_elem_space = self.doubleSpinBox_DOA_d.value()
#现在lamda不直接输入,而是填写天线间距,然后自动计算lamda
self.module_signal_processor.DOA_inter_elem_space = self.calculate_spacing()
#读入DOA天线阵形状
self.module_signal_processor.DOA_ant_alignment = self.comboBox_antenna_alignment.currentText()
#print(str(self.module_signal_processor.DOA_inter_elem_space) + "\n")
#如果是均匀圆阵,就把FB_avg禁用掉,否则启用
if self.module_signal_processor.DOA_ant_alignment == "UCA":
self.checkBox_en_DOA_FB_avg.setEnabled(False)
self.checkBox_en_DOA_FB_avg.setCheckState(False)
else:
self.checkBox_en_DOA_FB_avg.setEnabled(True)
#获取DOA运算的采样长度
self.module_signal_processor.DOA_sample_size = 2**self.spinBox_DOA_sample_size.value()
def set_PR_params(self):
if self.checkBox_en_passive_radar.checkState():
self.module_signal_processor.en_PR_processing = True
else:
self.module_signal_processor.en_PR_processing = False
if self.checkBox_en_td_filter.checkState():
self.module_signal_processor.en_td_filtering = True
else:
self.module_signal_processor.en_td_filtering = False
if self.checkBox_en_autodet.checkState():
self.module_signal_processor.en_PR_autodet = True
else:
self.module_signal_processor.en_PR_autodet = False
# Set CFAR parameters
self.module_signal_processor.cfar_win_params=[self.spinBox_cfar_est_win.value(), self.spinBox_cfar_est_win.value(), self.spinBox_cfar_guard_win.value(), self.spinBox_cfar_guard_win.value()]
self.module_signal_processor.cfar_threshold = self.doubleSpinBox_cfar_threshold.value()
# Set Time-domain clutter fitler parameters
self.module_signal_processor.td_filter_dimension = self.spinBox_td_filter_dimension.value()
# Set Cross-Correlation detector parameters
self.module_signal_processor.max_range = int(self.doubleSpinBox_cc_det_max_range.value())
self.module_signal_processor.max_Doppler = int(self.doubleSpinBox_cc_det_max_Doppler.value())
# General channel settings
self.module_signal_processor.ref_ch_id = self.spinBox_ref_ch_select.value()
self.module_signal_processor.surv_ch_id = self.spinBox_surv_ch_select.value()
def set_resync_time(self):
self.module_signal_processor.resync_time = self.spinBox_resync_time.value()
def pb_close_clicked(self):
#self.stop_streaming()
#self.module_receiver.module_state = "EXIT"
self.module_receiver.close() #关闭接收机模块
self.DOA_res_fd.close() #关闭DOA结果网页的文件符
self.close()
def pb_proc_control_clicked(self):
if self.pushButton_proc_control.text() == "Start processing":
self.pushButton_proc_control.setText("Stop processing")
#如果处理按钮被按下,文字要改变状态,并开始或关闭信号处理模块
self.module_signal_processor.start()
elif self.pushButton_proc_control.text() == "Stop processing":
self.pushButton_proc_control.setText("Start processing")
self.module_signal_processor.stop()
def pb_sync_clicked(self): #点击同步按钮,信号处理模块开启同步
#print("[ INFO ] Sync requested")
self.module_signal_processor.en_sample_offset_sync=True
def pb_calibrate_iq_clicked(self):#点击iq校准按钮,信号处理模块开启iq校准
#print("[ INFO ] IQ calibration requested")
self.module_signal_processor.en_calib_iq=True
def pb_calibrate_DOA_90_clicked(self):
#print("[ INFO ] DOA IQ calibration requested")
self.module_signal_processor.en_calib_DOA_90=True
def pb_del_sync_history_clicked(self):#点击清除历史,信号处理模块删除历史
self.module_signal_processor.delete_sync_history()
def power_level_update(self, over_drive_flag):
if over_drive_flag:
red_text = ""
red_text += "OVERDRIVE"
red_text += ("")
self.label_power_level.setText(red_text)
else:
green_text = ""
green_text += "OK"
green_text += ("")
self.label_power_level.setText(green_text)
def period_time_update(self, update_period): #显示刷新周期,根据长度使用不同单位
if update_period > 1:
self.label_update_rate.setText("%.1f s" %update_period)
else:
self.label_update_rate.setText("%.1f ms" %(update_period*1000))
def spectrum_plot(self): #绘制频谱
xw1 = self.module_signal_processor.spectrum[1,:] #获取信号处理模块的4个频谱
xw2 = self.module_signal_processor.spectrum[2,:]
xw3 = self.module_signal_processor.spectrum[3,:]
xw4 = self.module_signal_processor.spectrum[4,:]
freqs = self.module_signal_processor.spectrum[0,:] #获取频率范围
spectrum_dynamic_range = 10
self.spectrum_ch1_curve.setData(freqs, xw1) #绘制4个频谱函数
self.spectrum_ch2_curve.setData(freqs, xw2)
self.spectrum_ch3_curve.setData(freqs, xw3)
self.spectrum_ch4_curve.setData(freqs, xw4)
currentTime = time.time() #获取当前时间
if((currentTime - self.spectrum_time) > 0.5):
self.spectrum_time = currentTime #当前时间距上次刷新频谱超过0.5s就输出网页
self.export_spectrum.export('/ram/spectrum.jpg')
def delay_plot(self):
#绘制延迟图像
#从信号处理模块中获得3个互相关函数的数据,并计算模,再转换单位
xcorr12 = 10*np.log10(np.abs(self.module_signal_processor.xcorr[0,:]))
xcorr13 = 10*np.log10(np.abs(self.module_signal_processor.xcorr[1,:]))
xcorr14 = 10*np.log10(np.abs(self.module_signal_processor.xcorr[2,:]))
phasor12 =self.module_signal_processor.phasors[0,:]#这个现在没有用
phasor13 =self.module_signal_processor.phasors[1,:]
phasor14 =self.module_signal_processor.phasors[2,:]
N = np.size(xcorr12)//2 #互相关函数长度除以2并取整
xcorr12 -= np.max(xcorr12)
xcorr13 -= np.max(xcorr13)
xcorr14 -= np.max(xcorr14)
#互相关绘图最大值都保持在0
#phasor12 /= np.max(np.abs(phasor12))
#phasor13 /= np.max(np.abs(phasor13))
#phasor14 /= np.max(np.abs(phasor14))
M = 500
max_delay = np.max(np.abs(self.module_signal_processor.delay_log[:,-1]))
#[:,-1]表示最后一列,delay_log中每行都是表示每个通道相对0通道的延迟
#最后一列就是每个通道的最新的延迟值,它们可能提前或延后,求abs再求最大得到最大延迟
if max_delay+50 > M:
M=max_delay+50
#我们的目标是要绘制-500~500范围内的互相关函数,而不是整个互相关函数
#M基本上取500已经够了,如果3个延迟的最大值超过了这个范围,M就要取得更大,是最大延迟+50
delay_label = np.arange(-M,M+1,1)
#图像横轴范围
# if(xcorr12[0] != 0 and xcorr13[0] != 0 and xcorr14[0] != 0):
self.plotWidget_sync_absx.clear() #清除abs x图像
#绘图范围是比较小的,没必要把互相关函数全部画出来
#首先找到互相关函数的中间点,然后从中间开始往左取M个点,然后往右取M个点
#这就是[N-M:N+M+1]的由来
self.plotWidget_sync_absx.plot(delay_label, xcorr12[N-M:N+M+1], pen=(255, 199, 15))
self.plotWidget_sync_absx.plot(delay_label, xcorr13[N-M:N+M+1], pen='r')
self.plotWidget_sync_absx.plot(delay_label, xcorr14[N-M:N+M+1], pen='g')
# Plot delay history
#绘制延迟历史图像
self.plotWidget_sync_sampd.clear() #清除延迟历史图像
#delay_log存储的是每个通道的各个时刻的延迟,[0,:]是第0行所有列的
#就是0号通道相对参考通道的各个时刻延迟,绘制出来就是这个通道的延迟历史
self.plotWidget_sync_sampd.plot(self.module_signal_processor.delay_log[0,:], pen=(255, 199, 15))
self.plotWidget_sync_sampd.plot(self.module_signal_processor.delay_log[1,:], pen='r')
self.plotWidget_sync_sampd.plot(self.module_signal_processor.delay_log[2,:], pen='g')
# Plot phasors
# For averaged phasors
#self.plotWidget_sync_normph.clear()
#self.plotWidget_sync_normph.plot(np.cos(np.deg2rad(self.module_signal_processor.phase_log[0,:])), np.sin(np.deg2rad(self.module_signal_processor.phase_log[0,:])), pen=None, symbol='o', symbolBrush=(100,100,255,50))
#self.plotWidget_sync_normph.plot(np.cos(np.deg2rad(self.module_signal_processor.phase_log[0,:])), np.sin(np.deg2rad(self.module_signal_processor.phase_log[0,:])), pen=None, symbol='o', symbolBrush=(150,150,150,50))
#self.plotWidget_sync_normph.plot(np.cos(np.deg2rad(self.module_signal_processor.phase_log[0,:])), np.sin(np.deg2rad(self.module_signal_processor.phase_log[0,:])), pen=None, symbol='o', symbolBrush=(50,50,50,50))
# Plot phase history
#绘制相位差历史图像
self.plotWidget_sync_phasediff.clear()
self.plotWidget_sync_phasediff.plot(self.module_signal_processor.phase_log[0,:], pen=(255, 199, 15))
self.plotWidget_sync_phasediff.plot(self.module_signal_processor.phase_log[1,:], pen='r')
self.plotWidget_sync_phasediff.plot(self.module_signal_processor.phase_log[2,:], pen='g')
currentTime = time.time() #如果刷新时间超过0.5s就输出到网页
if((currentTime - self.sync_time) > 0.5):
self.sync_time = currentTime
self.export_sync.export('/ram/sync.jpg')
def DOA_plot_helper(self, DOA_data, incident_angles, log_scale_min=None, color=(255, 199, 15), legend=None):
DOA_data = np.divide(np.abs(DOA_data), np.max(np.abs(DOA_data))) # normalization #对DOA_data做归一化,除以了自身的最大值,这样最大值是1
if(log_scale_min != None):
DOA_data = 10*np.log10(DOA_data) #DOA_data转为dB
theta_index = 0
for theta in incident_angles:
if DOA_data[theta_index] < log_scale_min: #如果低于最小值就赋值
DOA_data[theta_index] = log_scale_min
theta_index += 1
plot = self.plotWidget_DOA.plot(incident_angles, DOA_data, pen=pg.mkPen(color, width=2)) #绘制处理过的DOA_data
return DOA_data
def DOA_plot(self):
#绘制DOA数据,获得theta,以及DOA结果
thetas = self.module_signal_processor.DOA_theta
Bartlett = self.module_signal_processor.DOA_Bartlett_res
Capon = self.module_signal_processor.DOA_Capon_res
MEM = self.module_signal_processor.DOA_MEM_res
MUSIC = self.module_signal_processor.DOA_MUSIC_res
DOA = 0
DOA_results = []
COMBINED = np.zeros_like(thetas, dtype=np.complex)
self.plotWidget_DOA.clear()
if self.module_signal_processor.en_DOA_Bartlett:
plt = self.DOA_plot_helper(Bartlett, thetas, log_scale_min = -50, color=(255, 199, 15))
COMBINED += np.divide(np.abs(Bartlett),np.max(np.abs(Bartlett)))
#de.DOA_plot(Bartlett, thetas, log_scale_min = -50, axes=self.axes_DOA)
DOA_results.append(thetas[np.argmax(Bartlett)])
if self.module_signal_processor.en_DOA_Capon:
self.DOA_plot_helper(Capon, thetas, log_scale_min = -50, color='g')
COMBINED += np.divide(np.abs(Capon),np.max(np.abs(Capon)))
#de.DOA_plot(Capon, thetas, log_scale_min = -50, axes=self.axes_DOA)
DOA_results.append(thetas[np.argmax(Capon)])
if self.module_signal_processor.en_DOA_MEM:
self.DOA_plot_helper(MEM, thetas, log_scale_min = -50, color='r')
COMBINED += np.divide(np.abs(MEM),np.max(np.abs(MEM)))
#de.DOA_plot(MEM, thetas, log_scale_min = -50, axes=self.axes_DOA)
DOA_results.append(thetas[np.argmax(MEM)])
if self.module_signal_processor.en_DOA_MUSIC:
#把MUSIC算法结果的纵坐标,横坐标,最小值,绘图颜色给DOA_plot_helper处理并绘图
self.DOA_plot_helper(MUSIC, thetas, log_scale_min = -50, color=(9, 237, 237))
COMBINED += np.divide(np.abs(MUSIC),np.max(np.abs(MUSIC)))
#de.DOA_plot(MUSIC, thetas, log_scale_min = -50, axes=self.axes_DOA)
DOA_results.append(thetas[np.argmax(MUSIC)])
#从MUSIC图像最高点的横坐标找到对应角度
#COMBINED_LOG = 10*np.log10(COMBINED)
if len(DOA_results) != 0:
# Combined Graph (beta) #如果选择MUSIC,COMBINED里存储的就是MUSIC的数据
COMBINED_LOG = self.DOA_plot_helper(COMBINED, thetas, log_scale_min = -50, color=(163, 64, 245))
#COMBINED_LOG就是MUSIC数据经过DOA_plot_helper处理了一下的数据
confidence = scipy.signal.find_peaks_cwt(COMBINED_LOG, np.arange(10,30), min_snr=1) #np.max(DOA_combined**2) / np.average(DOA_combined**2)
#confidence里存储的是MUSIC函数的几个波峰对应的横坐标值
maxIndex = confidence[np.argmax(COMBINED_LOG[confidence])]
#COMBINED_LOG[confidence]是几个横坐标对应几个波峰值
#np.argmax求出第几个波峰最高,然后求出第几个横坐标值对应最高波峰
#maxIndex里是这几个横坐标值里对应波峰最高的那个横坐标
confidence_sum = 0;
#print("Peaks: " + str(confidence))
#计算可信度,要把几个波峰都纳入计算
for val in confidence:
if(val != maxIndex and np.abs(COMBINED_LOG[val] - min(COMBINED_LOG)) > np.abs(min(COMBINED_LOG))*0.25):
#纳入计算的波峰,如果不是最高波峰,那么它的波峰值必须足够高,否则忽略掉
#print("Doing other peaks: " + str(val) + "combined value: " + str(COMBINED_LOG[val]))
confidence_sum += 1/(np.abs(COMBINED_LOG[val]))
#如果这种波峰不够大,或者波峰数量很多,那么confidence_sum就很大
elif val == maxIndex:
#print("Doing maxIndex peak: " + str(maxIndex) + "min combined: " + str(min(COMBINED_LOG)))
confidence_sum += 1/np.abs(min(COMBINED_LOG))
#如果MUSIC结果图的最小值不够小,取了绝对值就比较小
#再求倒数会造成confidence_sum比较大
#总之,如果MUSIC找不到明显波峰,这里的confidence_sum就比较大
# Get avg power level
#从频谱图中获得最大功率
max_power_level = np.max(self.module_signal_processor.spectrum[1,:])
#rms_power_level = np.sqrt(np.mean(self.module_signal_processor.spectrum[1,:]**2))
confidence_sum = 10/confidence_sum
#这里求了倒数,如果没明显波峰,confidence就比较小
#print("Max Power Level" + str(max_power_level))
#print("Confidence Sum: " + str(confidence_sum))
DOA_results = np.array(DOA_results)
# Convert measured DOAs to complex numbers
DOA_results_c = np.exp(1j*np.deg2rad(DOA_results))
# Average measured DOA angles
DOA_avg_c = np.average(DOA_results_c)
# Convert back to degree
DOA = np.rad2deg(np.angle(DOA_avg_c))
#由于DOA的角度值不停在求,这里要取平均值,平滑一些
#采用的方法是先求单位圆上此角度对应复数,再求复数的平均值,再转回角度
#复数运算要用弧度,所以转成弧度求完了再转回角度
# Update DOA results on the compass display
#在罗盘界面上更新DOA结果
#print("[ INFO ] Python GUI: DOA results :",DOA_results)
if DOA < 0:
DOA += 360
#如果DOA结果小于0就加上360度的周期,使得结果范围是0~360度
#DOA = 360 - DOA
DOA_str = str(int(DOA))
#把测向结果转为字符串
html_str = "\n"+DOA_str+" \n"+str(int(confidence_sum))+" \n"+str(np.maximum(0, max_power_level))+" \n"
#这里是输出到网页的字符串,包括DOA结果、可信度confidence、最大功率
#如果功率小于0就输出0
self.DOA_res_fd.seek(0)
#文件指针从头开始
self.DOA_res_fd.write(html_str)
self.DOA_res_fd.truncate()
#截断文件
#print("[ INFO ] Python GUI: DOA results writen:",html_str)
#self.DOA_res_fd.close()
currentTime = time.time()
if((currentTime - self.DOA_time) > 0.5):
self.DOA_time = currentTime
#如果距上次刷新超过0.5s就重新输出给网页doa画面
self.export_DOA.export('/ram/doa.jpg')
def RD_plot(self):
"""
Event type: Surveillance processor module has calculated the new range-Doppler matrix
callback description:
------------------
Plot the previously obtained range-Doppler matrix
"""
# If Automatic detection is disabled the range-Doppler matrix is plotted otherwise the matrix
if not self.checkBox_en_autodet.checkState():
# Set colormap TODO: Implement this properly
colormap = cm.get_cmap("jet")
colormap._init()
lut = (colormap._lut * 255).view(np.ndarray)
self.img_PR.imageItem.setLookupTable(lut)
CAFMatrix = self.module_signal_processor.RD_matrix
CAFMatrix = np.abs(CAFMatrix)
CAFDynRange = self.spinBox_rd_dyn_range.value()
#print("Max Val" + str(np.amax(CAFMatrix)))
#print("X-Size" + str(np.shape(CAFMatrix)[0]))
#print("Y-Size" + str(np.shape(CAFMatrix)[1]))
CAFMatrix = CAFMatrix / np.amax(CAFMatrix) # Noramlize with the maximum value
CAFMatrixLog = 20 * np.log10(CAFMatrix) # Change to logscale
CAFMatrixLog[CAFMatrixLog < -CAFDynRange] = -CAFDynRange
# for i in range(np.shape(CAFMatrix)[0]): # Remove extreme low values
# for j in range(np.shape(CAFMatrix)[1]):
# if CAFMatrixLog[i, j] < -CAFDynRange:
# CAFMatrixLog[i, j] = -CAFDynRange
# plot
#CAFPlot = self.axes_RD.imshow(CAFMatrixLog, interpolation='sinc', cmap='jet', origin='lower', aspect='auto')
plotPRImage = scipy.ndimage.zoom(CAFMatrixLog, self.PR_interp_factor, order=3)
self.img_PR.clear()
self.img_PR.setImage(plotPRImage)
else:
# Set colormap TODO: Implement this properly
colormap = cm.get_cmap("gray")
colormap._init()
lut = (colormap._lut * 255).view(np.ndarray)
self.img_PR.imageItem.setLookupTable(lut)
CAFMatrix = self.module_signal_processor.hit_matrix
plotPRImage = scipy.ndimage.zoom(CAFMatrix, self.PR_interp_factor, order=3)
self.img_PR.clear()
self.img_PR.setImage(plotPRImage)
currentTime = time.time()
if((currentTime - self.PR_time) > 0.5):
self.PR_time = currentTime
self.export_PR.export(toBytes=True).save('/ram/pr.jpg', quality=30)
# Set doppler speed Y-AXIS
max_Doppler = int(self.doubleSpinBox_cc_det_max_Doppler.value())
ay = self.plt_PR.getAxis('left')
matrix_ySize = np.shape(plotPRImage)[0]
ay.setTicks([[(0, -max_Doppler), (matrix_ySize * 0.25, -max_Doppler * 0.5), (matrix_ySize/2, 0), (matrix_ySize * 0.75, max_Doppler * 0.5), (matrix_ySize, max_Doppler)], []])
app = QApplication(sys.argv)
form = MainWindow()
form.show()
#这一段是实例化主窗口并显示,form就是主界面的实例化变量
def reboot_program():
form.module_receiver.close()
form.DOA_res_fd.close()
subprocess.call(['./run.sh'])
#重启程序,把接收机和DOA结果关掉,重新调用启动脚本
#@route('/static/:path#.+#', name='static')
#def static(path):
#return static_file(path, root='static')
@route('/static/', name='static')
def server_static(filepath):
return static_file(filepath, root='./static')
#获取静态网页
@get('/pr')
def pr():
en_pr = form.checkBox_en_passive_radar.checkState()
ref_ch = form.spinBox_ref_ch_select.value()
surv_ch = form.spinBox_surv_ch_select.value()
en_clutter = form.checkBox_en_td_filter.checkState()
filt_dim = form.spinBox_td_filter_dimension.value()
max_range = form.doubleSpinBox_cc_det_max_range.value()
max_doppler = form.doubleSpinBox_cc_det_max_Doppler.value()
windowing_mode = int(form.comboBox_cc_det_windowing.currentIndex())
dyn_range = form.spinBox_rd_dyn_range.value()
en_det = form.checkBox_en_autodet.checkState()
est_win = form.spinBox_cfar_est_win.value()
guard_win = form.spinBox_cfar_guard_win.value()
thresh_det = form.doubleSpinBox_cfar_threshold.value()
ip_addr = form.ip_addr
return template ('pr.tpl', {'en_pr':en_pr,
'ref_ch':ref_ch,
'surv_ch':surv_ch,
'en_clutter':en_clutter,
'filt_dim':filt_dim,
'max_range':max_range,
'max_doppler':max_doppler,
'windowing_mode':windowing_mode,
'dyn_range':dyn_range,
'en_det':en_det,
'est_win':est_win,
'guard_win':guard_win,
'thresh_det':thresh_det,
'ip_addr':ip_addr})
@post('/pr')
def do_pr():
en_pr = request.forms.get('en_pr')
form.checkBox_en_passive_radar.setChecked(True if en_pr=="on" else False)
ref_ch = request.forms.get('ref_ch')
form.spinBox_ref_ch_select.setProperty("value", ref_ch)
surv_ch = request.forms.get('surv_ch')
form.spinBox_surv_ch_select.setProperty("value", surv_ch)
en_clutter = request.forms.get('en_clutter')
form.checkBox_en_td_filter.setChecked(True if en_clutter=="on" else False)
filt_dim = request.forms.get('filt_dim')
form.spinBox_td_filter_dimension.setProperty("value", filt_dim)
max_range = request.forms.get('max_range')
form.doubleSpinBox_cc_det_max_range.setProperty("value", max_range)
max_doppler = request.forms.get('max_doppler')
form.doubleSpinBox_cc_det_max_Doppler.setProperty("value", max_doppler)
windowing_mode = request.forms.get('windowing_mode')
form.comboBox_cc_det_windowing.setCurrentIndex(int(windowing_mode))
dyn_range = request.forms.get('dyn_range')
form.spinBox_rd_dyn_range.setProperty("value", dyn_range)
en_det = request.forms.get('en_det')
form.checkBox_en_autodet.setChecked(True if en_det=="on" else False)
est_win = request.forms.get('est_win')
form.spinBox_cfar_est_win.setProperty("value", est_win)
guard_win = request.forms.get('guard_win')
form.spinBox_cfar_guard_win.setProperty("value", guard_win)
thresh_det = request.forms.get('thresh_det')
form.doubleSpinBox_cfar_threshold.setProperty("value", thresh_det)
form.set_PR_params()
return redirect('pr')
#@get和@post都是针对远程网页访问时做的回应
#远程发get过来就要获取信息,因此这里把信息准备好发过去
#远程发post过来是要做一些设置,这里接到设置后传送给其他有关模块
@get('/doa')
def doa():
#如果远端要get doa信息,就把所有跟doa有关的选项发出去
ant_arrangement_index = int(form.comboBox_antenna_alignment.currentIndex())
ant_meters = form.doubleSpinBox_DOA_d.value()
en_doa = form.checkBox_en_DOA.checkState()
en_bartlett = form.checkBox_en_DOA_Bartlett.checkState()
en_capon = form.checkBox_en_DOA_Capon.checkState()
en_MEM = form.checkBox_en_DOA_MEM.checkState()
en_MUSIC = form.checkBox_en_DOA_MUSIC.checkState()
en_fbavg = form.checkBox_en_DOA_FB_avg.checkState()
ip_addr = form.ip_addr
return template ('doa.tpl', {'ant_arrangement_index':ant_arrangement_index,
# 'ant_spacing':ant_spacing,
'ant_meters' :ant_meters,
'en_doa':en_doa,
'en_bartlett':en_bartlett,
'en_capon':en_capon,
'en_MEM':en_MEM,
'en_MUSIC':en_MUSIC,
'en_fbavg':en_fbavg,
'ip_addr':ip_addr})
@post('/doa')
def do_doa():
#如果远端post doa消息,就根据远端的要求做设置
ant_arrangement_index = request.forms.get('ant_arrangement')
form.comboBox_antenna_alignment.setCurrentIndex(int(ant_arrangement_index))
ant_spacing = request.forms.get('ant_spacing')
form.doubleSpinBox_DOA_d.setProperty("value", ant_spacing)
en_doa = request.forms.get('en_doa')
form.checkBox_en_DOA.setChecked(True if en_doa=="on" else False)
en_bartlett = request.forms.get('en_bartlett')
form.checkBox_en_DOA_Bartlett.setChecked(True if en_bartlett=="on" else False)
en_capon = request.forms.get('en_capon')
form.checkBox_en_DOA_Capon.setChecked(True if en_capon=="on" else False)
en_MEM = request.forms.get('en_MEM')
form.checkBox_en_DOA_MEM.setChecked(True if en_MEM=="on" else False)
en_MUSIC = request.forms.get('en_MUSIC')
form.checkBox_en_DOA_MUSIC.setChecked(True if en_MUSIC=="on" else False)
en_fbavg = request.forms.get('en_fbavg')
form.checkBox_en_DOA_FB_avg.setChecked(True if en_fbavg=="on" else False)
form.set_DOA_params()
#根据获得的信息,调用这个函数对信号处理模块进行对应设置
return redirect('doa')
#下面这些是跟sync有关的get和post
@get('/sync')
def sync():
en_sync = form.checkBox_en_sync_display.checkState()
en_noise = form.checkBox_en_noise_source.checkState()
ip_addr = form.ip_addr
return template ('sync.tpl', {'en_sync':en_sync,
'en_noise':en_noise,
'ip_addr':ip_addr})
@post('/sync')
def do_sync():
#如果远端post过来的是开启所有同步按钮,就把同步显示和噪声源都启用,若已启用再点一下是都关闭
if (request.POST.get('enable_all_sync') == 'enable_all_sync'):
current_sync = form.checkBox_en_sync_display.checkState()
current_noise = form.checkBox_en_noise_source.checkState()
if (current_sync == False) and (current_noise == False):
form.checkBox_en_sync_display.setChecked(True)
form.checkBox_en_noise_source.setChecked(True)
else:
form.checkBox_en_sync_display.setChecked(False)
form.checkBox_en_noise_source.setChecked(False)
form.switch_noise_source()
form.set_sync_params()
#根据设置的变化要调用这两个函数给信号处理模块和接收机模块
if (request.POST.get('update_sync') == 'update_sync'):
en_sync = request.forms.get('en_sync')
form.checkBox_en_sync_display.setChecked(True if en_sync=="on" else False)
#这是单独按下启用同步显示按钮
en_noise = request.forms.get('en_noise')
form.checkBox_en_noise_source.setChecked(True if en_noise=="on" else False)
#这是单独按下启用噪声按钮
form.switch_noise_source()
form.set_sync_params()
#同样要对信号处理模块和接收机模块进行设置
if (request.POST.get('del_hist') == 'del_hist'):
form.pb_del_sync_history_clicked()
#删除历史
if (request.POST.get('samp_sync') == 'samp_sync'):
form.pb_sync_clicked()
#采样时间同步按钮
if (request.POST.get('cal_iq') == 'cal_iq'):
form.pb_calibrate_iq_clicked()
#iq校准按钮
return redirect('sync')
#init是其他一些参数的初始化,包括中心频率、采样率、增益、直流抑制、滤波器带宽、抽头、降采样、ip
@get('/init')
def init():
center_freq = form.doubleSpinBox_center_freq.value()
samp_index = int(form.comboBox_sampling_freq.currentIndex())
uniform_gain = form.checkBox_en_uniform_gain.checkState()
gain_index = int(form.comboBox_gain.currentIndex())
gain_index_2 = int(form.comboBox_gain_2.currentIndex())
gain_index_3 = int(form.comboBox_gain_3.currentIndex())
gain_index_4 = int(form.comboBox_gain_4.currentIndex())
dc_comp = form.checkBox_en_dc_compensation.checkState()
filt_bw = form.doubleSpinBox_filterbw.value()
fir_size = form.spinBox_fir_tap_size.value()
decimation = form.spinBox_decimation.value()
ip_addr = form.ip_addr
return template ('init.tpl', {'center_freq':center_freq,
'samp_index':samp_index,
'uniform_gain':uniform_gain,
'gain_index':gain_index,
'gain_index_2':gain_index_2,
'gain_index_3':gain_index_3,
'gain_index_4':gain_index_4,
'dc_comp':dc_comp,
'filt_bw':filt_bw,
'fir_size':fir_size,
'decimation':decimation,
'ip_addr':ip_addr})
@post('/init') # or @route('/login', method='POST')
def do_init():
if (request.POST.get('rcv_params') == 'rcv_params'):
center_freq = request.forms.get('center_freq')
form.doubleSpinBox_center_freq.setProperty("value", center_freq)
#中心频率
samp_index = request.forms.get('samp_freq')
form.comboBox_sampling_freq.setCurrentIndex(int(samp_index))
#采样率
uniform_gain = request.forms.get('uniform_gain')
form.checkBox_en_uniform_gain.setChecked(True if uniform_gain=="on" else False)
#是否是统一增益
if uniform_gain == "on":
gain_index = request.forms.get('gain')
form.comboBox_gain.setCurrentIndex(int(gain_index))
gain_index_2 = request.forms.get('gain')
form.comboBox_gain_2.setCurrentIndex(int(gain_index))
gain_index_3 = request.forms.get('gain')
form.comboBox_gain_3.setCurrentIndex(int(gain_index))
gain_index_4 = request.forms.get('gain')
form.comboBox_gain_4.setCurrentIndex(int(gain_index))
else:
gain_index = request.forms.get('gain')
form.comboBox_gain.setCurrentIndex(int(gain_index))
gain_index_2 = request.forms.get('gain_2')
form.comboBox_gain_2.setCurrentIndex(int(gain_index_2))
gain_index_3 = request.forms.get('gain_3')
form.comboBox_gain_3.setCurrentIndex(int(gain_index_3))
gain_index_4 = request.forms.get('gain_4')
form.comboBox_gain_4.setCurrentIndex(int(gain_index_4))
form.pb_rec_reconfig_clicked()
#调用这个函数把这些设置发给信号处理模块和接收机模块
if (request.POST.get('iq_params') == 'iq_params'):
dc_comp = request.forms.get('dc_comp')
form.checkBox_en_dc_compensation.setChecked(True if dc_comp=="on" else False)
#直流抑制
filt_bw = request.forms.get('filt_bw')
form.doubleSpinBox_filterbw.setProperty("value", filt_bw)
#滤波器带宽
fir_size = request.forms.get('fir_size')
form.spinBox_fir_tap_size.setProperty("value", fir_size)
#抽头
decimation = request.forms.get('decimation')
form.spinBox_decimation.setProperty("value", decimation)
#降采样倍数
form.set_iq_preprocessing_params()
#iq预处理参数主要传给接收机模块,但是信号处理模块也要知道降采样后的采样率
if (request.POST.get('start') == 'start'):
form.module_signal_processor.start()
form.pushButton_proc_control.setText("Stop processing")
#点了Start processing按钮要打开信号处理模块的线程,并且文字变为stop processing
if (request.POST.get('stop') == 'stop'):
form.module_signal_processor.stop()
form.pushButton_proc_control.setText("Start processing")
#这是上面的相反操作,要停止信号处理模块的线程
if (request.POST.get('start_spec') == 'start_spec'):
form.checkBox_en_spectrum.setChecked(True)
form.set_spectrum_params()
#点了start spectrum按钮后这个函数会使信号处理模块开启频谱显示
if (request.POST.get('stop_spec') == 'stop_spec'):
form.checkBox_en_spectrum.setChecked(False)
form.set_spectrum_params()
#这是上面的相反操作,会停止频谱显示
if (request.POST.get('reboot') == 'reboot'):
reboot_program()
#reboot按钮会重启程序
return redirect('init')
@get('/stats')
def stats():
upd_rate = form.label_update_rate.text()
#获得刷新率
if(form.module_receiver.overdrive_detect_flag):
ovr_drv = "YES"
else:
ovr_drv = "NO"
#这里本来会显示接收机是否饱和但是实际代码被注释掉了
return template ('stats.tpl', {'upd_rate':upd_rate,
'ovr_drv':ovr_drv})
app.exec_()