前言
卫星导航信号的捕获是实现接收机的第一步重要工作,也是我花费时间最长的一步,因为要实现很多对我来说从0到1的工作,我需要验证从DSP平台到ZYNQ的转换,还有从ALTERO到XILINX的转变。前前后后大约花了一年多的时间,当然中间有几个月并没有碰它。本篇从原理到实现过程做一个大致的记录。
捕获原理
我拿的的资料全部是干货,并没有只言片语的解释,我的师傅名义上在实验室做博士后,实际上却找了份工作跑了,而我的导师并不是从事相关方向的研究,明确表示无法对技术上给我具体的指导。所以我大部分理论知识都是自学,主要有两本书“Understanding GPS
Principles and Applications” 与谢刚的《GPS原理与接收机设计》。对于捕获我只知道有串行和并行两种,当时的书上也没有介绍匹配滤波算法,知道多年以后我才知道我们用的方法叫PMF-FFT即短时相关FFT,是一种硬件接收机常用的方法。
GPS信号简介
GPS L1信号由测距码和导航电文组成,测距码为C/A码是一种伪随机码,具有良好的自相关性,因此适合捕获。C/A码速率为1.023MHz,长度1023,所以周期为1ms。不同的卫星具有不同的PRN码用以区别。导航电文速率为50Hz,所以C/A码20ms可能反转一次。GPS信号通过BPSK的方式调制到载波上(L1:1575.42MHz)。由于卫星的相对运动,收到的信号频率会因为多普勒效应而变化,所以信号的捕获是一个二维搜索的过程,即码相位寻找和频率搜索。
图1 GPS信号结构
图2 扩频调制信号
PMF-FFT捕获原理
以下两段转自知乎:PMF-FFT捕获算法
具体我也不懂,我只知道把1ms数据分成20段,分别做相关累加,得到20个数据结果,然后补零凑够64个数送进FFT模块。
捕获的实现
上一节讲了一大段短时相关傅里叶变换捕获的原理(大概算是讲了吧),这一节介绍一下在ZYNQ这个平台上的实现过程。
信号的捕获是本地生成的信号跟接收到的卫星信号同步的过程,需要在FPGA中生成一个本地C/A码和本地载波,与接收到信号相乘、累加,从而得到结果。ARM处理器用于控制生成本地信号和相关结果分析,两者配合实现信号的捕获。
我最初的目标是实现信号的自主捕获,即让接收机自动不断地搜索每一颗卫星信号。这一步需要两个任务跟中断的配合才能做到,如果只实现某颗特定卫星的捕获则容易得多,我决定从最基本的开始,以验证方法本身的有效性。
虽然我在前面的提到已经通过MAX2769芯片得到了GPS信号,并且通过matlab验证了信号的有效性,毕竟不方便,一方面不知道具体哪颗卫星有信号,另一方面总不能一直在阳台上调试。于是我想到了一个办法:本地生成一路伪随机信号作为输入,然后跟自己做相关运算。即使这样,我也花了两周的时间才把他捕获到。
FPGA的主要工作
FPGA主要用于产生本地信号,做相关运算、对运算的结果作FFT,最后产生中断信号通知ARM分析数据。流程清楚、代码也比较容易理解。我制作了两项工作,FFT模块的例化及控制和RAM的例化及读写。
inv_i <= '1'; --进行FFT计算
master_source_dav <= '1'; --接收FFT输出结果的模块总是准备好
data_real_in <= SXT(RAM1_q(7 downto 0), 16) when (Zero_Padding = '0' and RAM_Full = "01") else --当RAM1满时,且不进行补零,则取RAM1的输出
SXT(RAM2_q(7 downto 0), 16) when (Zero_Padding = '0' and RAM_Full = "10") else --当RAM1满时,且不进行补零,则取RAM2的输出
(others=>'0'); --否则补零
data_imag_in <= SXT(RAM1_q(15 downto 8), 16) when (Zero_Padding = '0' and RAM_Full = "01") else
SXT(RAM2_q(15 downto 8), 16) when (Zero_Padding = '0' and RAM_Full = "10") else
(others=>'0');
s_axis_data_tdata<= data_imag_in & data_real_in;
my_FFT : xfft_0
PORT MAP (
aclk => clk,
aresetn => rstn,
s_axis_config_tdata => "00000001",--"0000000"& inv_i,
s_axis_config_tvalid => inv_i,
s_axis_config_tready => open ,
s_axis_data_tdata => s_axis_data_tdata, --data_real_in & data_imag_in ,
s_axis_data_tvalid => master_sink_dav,
s_axis_data_tready => master_sink_ena ,
s_axis_data_tlast => master_sink_eop,
m_axis_data_tdata => m_axis_data_tdata,--fft_imag_out & fft_real_out,
m_axis_data_tvalid => master_source_ena,
m_axis_data_tready => master_source_dav,
m_axis_data_tlast => master_source_eop,
event_frame_started => open ,
event_tlast_unexpected => open ,
event_tlast_missing => open ,
event_status_channel_halt => open ,
event_data_in_channel_halt => open ,
event_data_out_channel_halt => open
);
-- m_axis_data_tdata<=fft_imag_out & fft_real_out;
fft_imag_out <= m_axis_data_tdata(46)& m_axis_data_tdata(38 downto 24);
fft_real_out <= m_axis_data_tdata(22)& m_axis_data_tdata(14 downto 0 );
Output_RAM : blk_mem_gen_1
PORT MAP (
clka => clk,
wea => out_wren,
addra => out_wraddr,
dina => out_data,
clkb => clk,
addrb => out_rdaddr,
doutb => out_q
);
其他的则是现成的,直接拿来用就好了。
软件流程
当时软件看了很久,一直没有头绪,我只知道具体的实现过程,因为读了很多遍,却一直没有搞清楚它到底是怎么工作的。我根本不知道什么叫做操作系统,更不知道任务切换流程,总之多年来一直很迷茫。。。
师傅说他是在中断下执行的,main函数并不干什么活儿,根据常理我觉得接收机上来应该先捕获,我顺着捕获函数开始寻找,捕获一颗卫星,捕获GPS卫星,捕获所有卫星,捕获策略……貌似就是它了。
于是我在一些工具额辅助下终于理清了信号捕获的脉络,通道配置才是起点,开机检测通道状态,如果一颗卫星也没有被跟踪,则执行开机盲补,放在一个任务里面,开机盲补填充捕获卫星序列,通过消息邮箱逐个发送捕获开始通知给捕获任务,捕获任务再通过另一个消息邮箱返回捕获结果,如果成功则设置通道状态为准备跟踪,然后捕获下一刻卫星,所有的卫星搜素完成之后,任务挂起,等到30s再进行下一轮捕获,如此往复。
用自己产生的信号就不用这么麻烦了,直接在初始化完成之后主函数中精准的捕获该卫星,打印的结果表明,相关峰值达到38000,证明代码正确的运行起来了。接下来直接捕获天上的信号,一轮下来往往能捕获到5颗以上卫星信号,符合预期。
总结
记录下来,似乎只是轻描淡写几段话而已,而过程的艰辛也只有自己清楚,这还是在前人成功的基础上复现,可见要实现一些成果还是很难的。
捕获完成之后,就是最重要的跟踪了,我一直觉得只要实现了捕获跟踪剩下的只是数学运算的问题了。