一、位同步&帧同步原理
伪距=信号收发时间差×光速。得到伪距,最重要是找到发送时间,故而需要同步。
码环能找到当前信号在一个CA码周期中的位置,但是一个数据比特有20个CA码,所以还必须要确定它是在第几个码周期中——位同步。
若相干积分1ms,也即是输出代表比特电平宽1ms(其实就是每个CA码为1ms),但正常的数据是1bit宽度为20ms,故要把20个电平(CA码)合成1个比特,因此要找到哪20个是一组——位同步。
【知道20个一组≈知道频率;不知道哪里开始数20个≈不知道相位】
1. 帧同步原理
根据卫星导航电文格式,每个子帧的第一个字是遥测字,而遥测字的首8个比特是值固定为10001011的同步码(Preamble),只需要搜索数据找到Preamble即可将比特正确拼接为字,即实现帧同步。载波环解调得到的数据可能存在180°相位模糊,所以实际上只要搜索到10001011或者其反相就可以。
但是因为导航数据比特有可能恰好随机拼成预导序列,为了避免错判,在找到preamble后还需要再进行奇偶校验来进一步确认。(实际上这里还可以继续加第三第四重保险,不过现在暂时不考虑这么复杂的情况)
2. 帧同步代码
帧同步主要通过findPreamble.m来实现:
(1)准备工作
设定搜索的起始偏移量,说明搜索开始的地方是哪里。创建矩阵firstSubFrame 用于存储每个通道找到的第一个子帧位置索引(因为子帧的开头是遥测字,遥测字的开头是同步码,所以找到同步码=找到了遥测字=找到了子帧)。
定义Preamble的比特值= [1 -1 -1 -1 1 -1 1 1];然后将它扩展为与样本速率匹配的长度,也就是复制20份,以便进行相关运算。
preamble_ms = kron(preamble_bits, ones(1, 20));
(这里最初以为是把preamble整体重复20次,后来分析应该是将preamble的每个比特值重复20次,之后下一个值再重复20次,例如[1 , -1]就变成[1,1,1,1,1…,-1,-1,-1,-1…])
解释:preamble表面上是8个±1数值,但其实每个数都是1个比特,而一个比特包含20个数据单元,也就是20ms,所以对于一个“1”,它应该扩展成“111111111111...”,才能和导航电文数据匹配上,进行相关操作。
(2)循环搜索同步码
对于可以处理的通道:
从searchStartOffset位置开始,对通道的I_P(即长度为36000ms的导航数据-这里在跟踪中初始化是zeros(1,settings.msToProcess)而msToProcess=36000)进行判决,阈值为0,输出±1的电平值;
计算导航数据和preamble序列的互相关值,来找同步码的位置,对于相关结果中大于阈值的位置,将其存储在index变量中。
(3)拼接
对于每一个找到的位置, 首先计算它与所有找到的位置的偏移,如果偏移为6000,表示在当前位置之前有一个完整的子帧,也就是先把一看就不对的结果排除掉。【解释:每个子帧前8比特是preamble,所以如果找到的位置正确的话,两个位置之间应该正好差一个子帧,也就是6000才对】 然后需要取遥测字和交接字以及上1子帧最后两个比特用于奇偶校验。
6000的解释:1子帧=10字,1字=30比特,1比特=20数据单元,所以1个子帧=6000个1ms数据单元
对于取出来的这些数据单元,先将其拼成20行62列的矩阵,再对每列进行求和,得到的向量中每个元素就是一个比特的值,因此得到62比特。
矩阵尺寸的解释:根据上面列出的换算法则,遥测+交接2个字 = 2*30*20 = 1200个数据单位,最后2个比特 = 2*20 = 40,1200+40=1240=20*62
然后进行判决,阈值为0,就得到电平为±1的一系列比特值。
最后检查子帧的导航消息的校验和是否正确,需要调用navPartyChk函数。如果正确,说明找对了, 就将index信息存储到firstSubFrame中,作为子帧起始点。如果无法找到有效的同步码,需要从activeChnList列表中移除当前通道,并提示错误信息。
二、MATLAB实现
下面的代码是帧同步:
searchStartOffset = 0; %搜索起始偏移量,说明搜索开始的地方是哪里
% 创建矩阵 用于存储每个通道找到的第一个子帧
firstSubFrame = zeros(1, settings.numberOfChannels);
% 定义Preamble的比特值
preamble_bits = [1 -1 -1 -1 1 -1 1 1];
%将预导序列扩展为与样本速率匹配的长度,以便进行相关运算。
preamble_ms = kron(preamble_bits, ones(1, 20));
%找到可以处理的通道号,也就是在前面已经成功捕获和跟踪的通道
activeChnList = find([trackResults.status] ~= '-');
for channelNr = activeChnList %开始循环
% 从searchStartOffset位置开始,取出通道的I_P数据。
bits = trackResults(channelNr).I_P(1 + searchStartOffset : end);
% 这两个就是个判决的过程,阈值为0
bits(bits > 0) = 1;
bits(bits <= 0) = -1;
% 计算位移相关性,找到预导序列的位置,因为相关性反映相似程度
tlmXcorrResult = xcorr(bits, preamble_ms);
% 清除索引变量
clear index
clear index2
% 计算相关性结果的长度:
xcorrLength = (length(tlmXcorrResult) + 1) /2;
% 找到相关性结果中大于阈值的位置,并将其存储在index变量中:
index = find(...
abs(tlmXcorrResult(xcorrLength : xcorrLength * 2 - 1)) > 153)' + ...
searchStartOffset;
% 对于找到的位置,执行下面的循环
for i = 1:size(index)
index2 = index - index(i); %:计算与当前位置的偏移
% 如果存在一个偏移为6000的位置(表示找到了一个完整的子帧)
if (~isempty(find(index2 == 6000)))
% 从信号中提取完整子帧的数据:
bits = trackResults(channelNr).I_P(index(i)-40 : ...
index(i) + 20 * 60 -1)';
% 将数据重新形状为20行,每行包含一个比特:
bits = reshape(bits, 20, (size(bits, 1) / 20));
bits = sum(bits); % 对每列进行求和,以得到每个比特的值
% 判决:
bits(bits > 0) = 1;
bits(bits <= 0) = -1;
% 检查子帧的导航消息的校验和是否正确:
if (navPartyChk(bits(1:32)) ~= 0) && ...
(navPartyChk(bits(31:62)) ~= 0)
% 将找到的第一个子帧的索引存储到firstSubFrame中
firstSubFrame(channelNr) = index(i);
break;
end
end
end
% 如果无法找到有效的预导序列,从活跃通道列表中移除该通道,并打印提示信息
if firstSubFrame(channelNr) == 0
activeChnList = setdiff(activeChnList, channelNr);
disp(['Could not find valid preambles in channel ', ...
num2str(channelNr),'!']);
end
end