简介:这份资源是面向卫星导航、通信与信号处理方向的学习者和科研人员的一套GNSS软件接收机Matlab实现,基于SoftGNSS v3.0,可用于教学演示、算法验证与系统级仿真。内容覆盖数字信号处理、坐标系统与定位算法、卫星信号模拟、跟踪循环与搜索算法、多径效应抑制、用户界面与数据可视化以及代码效率优化等关键环节,帮助读者理解从信号捕获到定位解算的完整流程。压缩包共46个文件,以39个m脚本为核心,辅以txt说明、readme、fig界面文件与mat数据文件,整体约179KB,结构紧凑便于按模块查阅。目前已有164人学习下载。通过研读这些脚本与配套说明,读者可掌握捕获、跟踪、导航电文解析与最小二乘定位等实现思路,并在此基础上测试新的信号处理策略,为实际硬件接收机设计提供参考。
1. 从射频采样到定位解算:GNSS软件接收机Matlab到底能做什么
很多人第一次接触GNSS软件接收机Matlab,是因为手里有一段中频采样数据,却不知道怎么把它变成经纬度。硬件接收机把捕获、跟踪、解算全部封在黑匣子里,你只能看到最终输出,中间出了什么问题完全靠猜。软件接收机的价值就在于把这条链路全部摊开:从载波剥离、码相位搜索,到比特同步、帧解析,再到最小二乘定位,每一步你都能打断点、画图、改参数。
Matlab在这个场景里几乎是默认选择。它的矩阵运算天然适合做相关器,绘图能力让你一眼看出捕获峰是否尖锐、跟踪环路是否锁定,而不用像C语言那样写一堆调试输出。适合谁?做卫星导航课程设计的学生、需要验证新捕获算法的研究者、想从零理解GPS/BDS信号结构的工程师。前提是你得有一段可用的中频数据文件,以及Matlab的Signal Processing Toolbox和Communications Toolbox。
这一篇不讲教科书上的推导,只讲怎么把一条完整的软件接收机链路在Matlab里跑通,参数怎么设,哪里容易翻车。
2. 中频数据读进来之后:捕获、跟踪、解算三段链路的Matlab骨架
2.1 先搞清楚你的数据格式,别急着写捕获
拿到一段中频采样数据,第一件事不是打开Matlab写代码,而是确认三件事:采样率、中频频率、数据位宽。这三个参数错了,后面所有代码都是白写。常见的数据格式有几种:8位有符号整数、16位有符号整数、32位浮点。有些采集设备还会把I/Q两路交错存储,有些只存实数采样。
我一般会先写一个最小的读取脚本,把前几千个采样点画出来,看看波形是否合理。如果看到的是纯噪声,那可能是格式读错了;如果看到明显的正弦包络,说明中频频率和采样率大致对得上。
% read_if_data.m % 读取中频采样数据并做初步可视化检查 function [signal, fs, fc] = read_if_data(filename, format, fs, fc) % filename: 数据文件路径 % format: 'int8', 'int16', 'float32' % fs: 采样率 (Hz) % fc: 中频频率 (Hz) fid = fopen(filename, 'r'); if fid == -1 error('文件打开失败,检查路径'); end switch format case 'int8' raw = fread(fid, Inf, 'int8'); signal = double(raw); % 转双精度便于后续运算 case 'int16' raw = fread(fid, Inf, 'int16'); signal = double(raw); case 'float32' raw = fread(fid, Inf, 'float32'); signal = double(raw); otherwise error('不支持的格式: %s', format); end fclose(fid); % 画前2000个点的时域波形 figure; plot(signal(1:min(2000, length(signal)))); title('中频采样前2000点'); xlabel('采样点'); ylabel('幅度'); grid on; % 画频谱确认中频位置 N = min(8192, length(signal)); f = (-N/2:N/2-1) * (fs/N); S = fftshift(abs(fft(signal(1:N)))); figure; plot(f/1e6, 20*log10(S)); title('频谱'); xlabel('频率 (MHz)'); ylabel('幅度 (dB)'); grid on; end这段代码的逻辑很直接:按指定格式读入全部数据,转成double类型避免后续整数溢出,然后画时域波形和频谱。参数说明:fs必须和采集时设置的一致,差一点都不行;fc用来确认频谱峰值是否在预期位置。如果频谱峰值偏离你设定的fc超过几百kHz,要么是fs填错了,要么是数据里根本没有卫星信号。
2.2 捕获:用FFT并行码相位搜索找卫星
捕获的目的是找到可见卫星的码相位和载波多普勒频移。最常用的方法是基于FFT的并行码相位搜索,因为它比时域滑动相关快几个数量级。核心思路是把接收信号和本地C/A码分别做FFT,共轭相乘后做IFFT,得到的峰值位置就是码相位,峰值大小反映相关强度。
% acquisition.m % 基于FFT的并行码相位捕获 function [acq_result] = acquisition(signal, fs, fc, prn, code_phase_step) % signal: 输入中频信号(实数) % fs: 采样率 % fc: 中频频率 % prn: 卫星PRN号 % code_phase_step: 码相位搜索步长(通常取1) % 生成本地C/A码(1ms周期,1023码片) ca_code = generate_ca_code(prn); % 返回1023长度的±1序列 % 按采样率重采样C/A码 samples_per_code = round(fs / 1023); % 每个码片的采样点数 ca_code_upsampled = repelem(ca_code, samples_per_code); ca_code_upsampled = ca_code_upsampled(1:round(fs*1e-3)); % 截取1ms % 多普勒搜索范围 ±10kHz,步长500Hz doppler_bins = -10000:500:10000; num_dopplers = length(doppler_bins); % 取1ms数据 num_samples = round(fs * 1e-3); signal_1ms = signal(1:num_samples); % 预分配结果矩阵 acq_result = zeros(num_dopplers, num_samples); % 本地码的FFT(共轭) ca_fft_conj = conj(fft(ca_code_upsampled, num_samples)); for k = 1:num_dopplers % 生成多普勒频移后的本地载波 t = (0:num_samples-1)' / fs; carrier = exp(-1j * 2 * pi * (fc + doppler_bins(k)) * t); % 混频得到基带信号 baseband = signal_1ms .* carrier; % FFT并行码相位搜索 bb_fft = fft(baseband, num_samples); corr = ifft(bb_fft .* ca_fft_conj); acq_result(k, :) = abs(corr); end % 找全局最大值 [max_val, max_idx] = max(acq_result(:)); [doppler_idx, code_idx] = ind2sub(size(acq_result), max_idx); fprintf('PRN %d: 多普勒 = %d Hz, 码相位 = %d 采样点, 相关峰值 = %.2f\n', ... prn, doppler_bins(doppler_idx), code_idx, max_val); % 画捕获结果三维图 figure; [D, C] = meshgrid(1:num_samples, doppler_bins); mesh(C, D, acq_result); xlabel('码相位 (采样点)'); ylabel('多普勒 (Hz)'); zlabel('相关幅度'); title(sprintf('PRN %d 捕获结果', prn)); end逻辑说明:先把1023长度的C/A码按采样率上采样到1ms长度,然后对每个多普勒频点做混频和FFT相关。ca_fft_conj是本地码FFT的共轭,只算一次,循环里复用。acq_result的每一行对应一个多普勒频点,每一列对应一个码相位。峰值位置就是捕获结果。
参数说明:doppler_bins的范围一般取±10kHz,因为地面静态接收机的多普勒频移通常在±5kHz以内,但高动态场景要放宽到±20kHz。步长500Hz是折中,太大会漏掉峰值,太小计算量翻倍。samples_per_code必须是整数,如果fs/1023不是整数,需要做插值或者调整fs。常见做法是选fs为1023的整数倍,比如4.092MHz、8.184MHz、16.368MHz。
捕获门限怎么定?不能只看峰值绝对值,要看峰值和次峰的比值。我一般设一个经验值:峰值超过次峰3倍以上才认为捕获成功。如果所有卫星的相关峰都差不多平,说明数据里没有信号或者参数错了。
2.3 跟踪:锁相环和延迟锁定环的参数整定
捕获只给你一个粗略的码相位和多普勒估计,精度远远不够解算。跟踪环路要做两件事:用延迟锁定环(DLL)持续调整本地码相位,用锁相环(PLL)持续调整本地载波频率和相位。跟踪环路的设计直接决定你能不能解出导航电文。
% tracking.m % 标量跟踪环路:DLL + PLL function [nav_bits, track_results] = tracking(signal, fs, fc, prn, init_code_phase, init_doppler) % signal: 输入信号 % fs, fc: 采样率和中频 % prn: 卫星PRN % init_code_phase: 捕获得到的初始码相位 % init_doppler: 捕获得到的初始多普勒 % 环路参数 T = 1e-3; % 积分时间 1ms samples_per_ms = round(fs * T); code_freq = 1.023e6; % C/A码速率 samples_per_code = fs / 1023; % DLL带宽和阻尼 B_dll = 2; % Hz zeta = 0.707; wn_dll = B_dll / (zeta + 1/(4*zeta)); Kp_dll = 2 * zeta * wn_dll; Ki_dll = wn_dll^2; % PLL带宽和阻尼 B_pll = 25; % Hz wn_pll = B_pll / (zeta + 1/(4*zeta)); Kp_pll = 2 * zeta * wn_pll; Ki_pll = wn_pll^2; % 生成本地码 ca_code = generate_ca_code(prn); ca_upsampled = repelem(ca_code, round(samples_per_code)); % 初始化 code_phase = init_code_phase; doppler = init_doppler; carrier_phase = 0; dll_integ = 0; pll_integ = 0; num_ms = floor(length(signal) / samples_per_ms); nav_bits = zeros(1, num_ms); track_results = zeros(num_ms, 4); % [码相位误差, 载波相位误差, 多普勒, 相关幅值] for ms = 1:num_ms % 取当前1ms数据 idx_start = (ms-1) * samples_per_ms + 1; idx_end = ms * samples_per_ms; if idx_end > length(signal) break; end segment = signal(idx_start:idx_end); % 生成本地载波 t = (0:samples_per_ms-1)' / fs; carrier = exp(-1j * 2 * pi * (fc + doppler) * t + 1j * carrier_phase); % 混频 baseband = segment .* carrier; % 生成本地码的三个副本(超前、即时、滞后) code_idx = mod(round(code_phase) + (0:samples_per_ms-1), length(ca_upsampled)) + 1; code_prompt = ca_upsampled(code_idx); code_early = ca_upsampled(mod(code_idx - round(samples_per_code/2) - 1, length(ca_upsampled)) + 1); code_late = ca_upsampled(mod(code_idx + round(samples_per_code/2) - 1, length(ca_upsampled)) + 1); % 相关 I_p = sum(real(baseband) .* code_prompt); Q_p = sum(imag(baseband) .* code_prompt); I_e = sum(real(baseband) .* code_early); Q_e = sum(imag(baseband) .* code_early); I_l = sum(real(baseband) .* code_late); Q_l = sum(imag(baseband) .* code_late); % DLL鉴相器(归一化超前减滞后) E = sqrt(I_e^2 + Q_e^2); L = sqrt(I_l^2 + Q_l^2); dll_error = (E - L) / (E + L + eps); % PLL鉴相器(Costas环) pll_error = atan2(Q_p, I_p); % 环路滤波 dll_integ = dll_integ + Ki_dll * dll_error; code_phase = code_phase + Kp_dll * dll_error + dll_integ; pll_integ = pll_integ + Ki_pll * pll_error; doppler = doppler + Kp_pll * pll_error + pll_integ; carrier_phase = carrier_phase + pll_error; % 记录 track_results(ms, :) = [dll_error, pll_error, doppler, sqrt(I_p^2 + Q_p^2)]; % 解调导航电文比特(简化:直接取符号) nav_bits(ms) = sign(I_p); end % 画跟踪结果 figure; subplot(3,1,1); plot(track_results(:,1)); title('DLL鉴相误差'); grid on; subplot(3,1,2); plot(track_results(:,2)); title('PLL鉴相误差'); grid on; subplot(3,1,3); plot(track_results(:,3)); title('多普勒频率 (Hz)'); grid on; end逻辑说明:每个1ms周期内,先用当前载波相位和多普勒生成载波,混频得到基带。然后用三个码副本(超前、即时、滞后)做相关。DLL鉴相器用归一化超前减滞后功率,PLL鉴相器用Costas环的atan2(Q,I)。环路滤波器输出更新码相位和载波频率。
参数说明:B_dll取2Hz是常见值,动态大时可以放宽到5Hz,但噪声会增大。B_pll取25Hz适合静态场景,动态场景可以到50Hz。zeta取0.707是巴特沃斯响应,兼顾响应速度和过冲。samples_per_code必须是整数,否则码相位更新会有累积误差。
跟踪是否锁定的判断标准:PLL鉴相误差应该在零附近波动,标准差小于0.1弧度;DLL鉴相误差同样应该在零附近。如果鉴相误差持续偏向一侧,说明初始多普勒估计偏差太大,环路拉不回来。
2.4 帧同步与定位解算:从比特到经纬度
跟踪环路输出的是50bps的导航电文比特流。要从中提取星历和时间信息,必须先做帧同步,找到子帧的起始位置。GPS L1 C/A的导航电文每子帧300比特,每帧5个子帧,前8比特是固定的同步头(10001011)。
% frame_sync.m % 帧同步与星历解析(简化版) function [eph, t_tx] = frame_sync(nav_bits, prn) % nav_bits: 跟踪输出的比特流(±1) % prn: 卫星PRN % 同步头模式(8比特) preamble = [1 -1 -1 -1 1 -1 1 1]; % 10001011 % 滑动相关找同步头 corr = zeros(1, length(nav_bits) - 8); for i = 1:length(corr) corr(i) = sum(nav_bits(i:i+7) .* preamble); end [max_corr, sync_pos] = max(corr); if max_corr < 6 warning('PRN %d: 帧同步失败,相关峰值 = %d', prn, max_corr); eph = []; t_tx = []; return; end fprintf('PRN %d: 帧同步成功,位置 = %d,相关峰值 = %d\n', prn, sync_pos, max_corr); % 解析星历(这里只提取关键参数,实际需要按ICD逐字段解析) % 子帧1包含周数、时钟校正参数 % 子帧2和3包含轨道参数 % 简化:假设从sync_pos开始有完整的5个子帧 subframe1 = nav_bits(sync_pos:sync_pos+299); subframe2 = nav_bits(sync_pos+300:sync_pos+599); subframe3 = nav_bits(sync_pos+600:sync_pos+899); % 提取星历参数(这里用占位值,实际需要按比特位置解析) eph = struct(); eph.prn = prn; eph.toc = 0; % 时钟参考时间 eph.a = 26560000; % 半长轴(占位) eph.e = 0.01; % 偏心率(占位) eph.i0 = 0.96; % 轨道倾角(占位) eph.OMEGA0 = 1.5; % 升交点赤经(占位) eph.omega = 0.5; % 近地点角距(占位) eph.M0 = 0.3; % 平近点角(占位) t_tx = sync_pos * 0.02; % 发射时间(秒),每比特20ms end逻辑说明:同步头是固定的8比特模式,用滑动相关找最大峰值位置。找到后按子帧长度切分,然后按ICD定义的比特位置提取星历参数。实际工程中这一步需要严格按接口控制文档逐字段解析,包括缩放因子和符号位。
参数说明:preamble是GPS L1 C/A的同步头,BDS的同步头不同(是11100010010)。相关峰值门限一般设6以上(满分8),低于这个值说明比特流质量太差。t_tx是信号发射时间,用于后续伪距计算。
定位解算用最小二乘迭代。需要至少4颗卫星的伪距和星历。伪距 = 光速 × (接收时间 - 发射时间)。接收时间由本地时钟给出,发射时间从帧同步得到。然后迭代求解接收机位置和钟差。
% solve_position.m % 最小二乘定位解算 function [pos, clock_bias] = solve_position(sat_pos, pseudoranges, init_pos) % sat_pos: N×3 卫星位置矩阵(ECEF) % pseudoranges: N×1 伪距观测值 % init_pos: 初始位置估计 [x0, y0, z0, dt0] c = 299792458; % 光速 x = init_pos(:); max_iter = 10; tol = 1e-3; for iter = 1:max_iter N = size(sat_pos, 1); H = zeros(N, 4); y = zeros(N, 1); for i = 1:N dx = sat_pos(i,1) - x(1); dy = sat_pos(i,2) - x(2); dz = sat_pos(i,3) - x(3); r = sqrt(dx^2 + dy^2 + dz^2); % 设计矩阵 H(i,:) = [-dx/r, -dy/r, -dz/r, 1]; % 残差 y(i) = pseudoranges(i) - r - x(4); end % 最小二乘更新 delta = (H' * H) \ (H' * y); x = x + delta; if norm(delta(1:3)) < tol break; end end pos = x(1:3); clock_bias = x(4); % 转经纬高 [lat, lon, alt] = ecef2lla(pos); fprintf('定位结果: 纬度 = %.6f°, 经度 = %.6f°, 高度 = %.1f m\n', lat, lon, alt); fprintf('钟差 = %.2f m\n', clock_bias); end逻辑说明:每次迭代计算设计矩阵H和残差向量y,然后求解最小二乘更新量delta。收敛条件是位置更新量小于1mm。最后把ECEF坐标转成经纬高。
参数说明:init_pos可以设为零向量或者上一次定位结果。迭代次数一般5次以内就收敛。如果发散,检查伪距是否有粗差,或者卫星位置是否算错。
3. 参数整定与性能验证:捕获门限、环路带宽、定位精度的实操标定
3.1 捕获门限怎么定才不虚警
捕获门限设太低,噪声峰值会被误判为卫星信号,导致跟踪环路锁在噪声上;设太高,弱信号卫星被漏掉,可见星数不够,定位解算失败。我一般用峰值比(Peak Ratio)作为判据:主峰幅度除以次峰幅度。对于1ms积分时间,经验门限是2.5到3.0。如果数据质量好,可以降到2.0;如果噪声大,要提到3.5以上。
更严谨的做法是做虚警概率分析。假设相关输出服从瑞利分布,给定虚警概率Pfa,门限Vt = sqrt(-2 * ln(Pfa) * sigma^2),其中sigma是噪声标准差。实际中sigma可以用所有非峰值点的均方根估计。
% 计算自适应捕获门限 noise_floor = median(acq_result(:)); % 用中位数估计噪声底 noise_std = 1.4826 * mad(acq_result(:), 1); % 鲁棒标准差估计 Pfa = 1e-6; % 虚警概率 threshold = noise_floor + sqrt(-2 * log(Pfa)) * noise_std; fprintf('自适应门限 = %.2f\n', threshold);这段代码用中位数和MAD(绝对中位差)估计噪声统计量,避免峰值点拉高均值。Pfa取1e-6是常用值,对应每百万次搜索允许一次虚警。
3.2 环路带宽对跟踪性能的影响
PLL带宽决定了跟踪环路对相位噪声和动态应力的权衡。带宽越窄,噪声抑制越好,但对动态的响应越慢。静态场景下,20到25Hz是甜点;车载场景建议30到35Hz;高动态场景(比如火箭)要到50Hz以上。
DLL带宽一般比PLL窄一个数量级,取1到2Hz。如果码相位误差抖动大,先检查DLL带宽是否太宽。如果码相位跟不上,检查是否太窄。
验证方法:跟踪稳定后,看PLL鉴相误差的标准差。如果大于0.2弧度,说明带宽太宽或者信号太弱。看DLL鉴相误差的标准差,如果大于0.1,同样需要调整。
3.3 定位精度验证:用已知坐标做闭环
如果你有接收机天线的已知坐标(比如用高精度接收机测过),可以直接对比软件接收机的输出。没有的话,可以用最小二乘残差作为间接指标。残差应该接近伪距噪声水平,一般在几米以内。如果残差超过几十米,说明星历解析错了或者伪距计算有粗差。
% 定位残差分析 residuals = y - H * delta; % 最后一次迭代的残差 RMS = sqrt(mean(residuals.^2)); fprintf('定位残差RMS = %.2f m\n', RMS); if RMS > 10 warning('残差过大,检查星历和伪距'); end残差RMS在3到5米是正常水平(单频C/A码)。如果小于1米,可能是伪距做了平滑或者用了载波相位。如果大于10米,先检查卫星位置是否用错了时间标签。
4. 避坑与排查:软件接收机跑不通时先查这五件事
4.1 捕获峰不明显,所有卫星相关值都差不多
现象:跑捕获代码,30颗卫星的相关峰都在同一水平,没有明显突出的峰值。
原因:最常见的是采样率或中频频率填错了。比如数据实际是4.092MHz采样,你填了8.184MHz,重采样后的码速率完全对不上。其次是数据格式读错,比如实际是16位有符号整数,你按8位读,波形完全乱掉。
解决:先用频谱确认中频位置。如果频谱峰值不在你设定的fc附近,说明fc错了。如果频谱是一片噪声没有峰值,说明数据格式或采样率错了。用fread读几个值出来看数量级,8位数据范围是-128到127,16位是-32768到32767。
4.2 跟踪环路不收敛,鉴相误差持续偏向一侧
现象:PLL鉴相误差一直在正或负的某个值附近,不收敛到零。DLL鉴相误差同样。
原因:初始多普勒估计偏差太大,超出了环路的牵引范围。PLL的牵引范围大约是环路带宽的2到3倍。如果捕获给出的多普勒偏差超过100Hz,而PLL带宽只有25Hz,环路拉不回来。
解决:检查捕获的多普勒搜索步长是否太粗。500Hz步长意味着最坏情况下偏差250Hz。可以把步长降到250Hz,或者用捕获的多普勒估计值直接初始化PLL的积分器。另一个办法是先用较宽的带宽(比如50Hz)让环路锁定,再切换到窄带宽。
4.3 帧同步失败,相关峰值低于门限
现象:跟踪输出的比特流做帧同步,最大相关峰值只有3或4,低于6的门限。
原因:比特流质量太差。可能是PLL鉴相误差太大导致解调比特误码率高,也可能是跟踪时间不够长,比特流里混入了未锁定的段。
解决:先看PLL鉴相误差的标准差。如果大于0.3弧度,先调PLL带宽。如果PLL没问题,检查比特同步是否做了。GPS的比特边界和码边界对齐,但有些实现需要先做比特同步再解调。另外,确保用于帧同步的比特流是从跟踪稳定之后开始的,把前几百毫秒的过渡段丢掉。
4.4 定位结果跳变,每次解算差几十米
现象:连续定位,经纬度每次输出都在跳,幅度几十米。
原因:伪距粗差。某颗卫星的伪距因为帧同步错误或者星历解析错误,偏差了几百米甚至几公里。最小二乘对粗差很敏感,一颗坏星就能把整个解拉偏。
解决:加RAIM(接收机自主完好性监测)做粗差检测。最简单的方法是算残差,如果某颗星的残差超过3倍RMS,把它剔除后重新解算。另外检查帧同步的位置是否唯一,有时候噪声会导致同步头在错误位置相关。
4.5 跑几分钟后跟踪失锁
现象:刚开始跟踪正常,几十秒后PLL鉴相误差突然增大,环路失锁。
原因:多普勒频率变化超出了环路的跟踪能力。静态场景下多普勒变化率很小,但如果接收机晶振漂移大,或者数据是从运动平台上采集的,多普勒变化率可能超过PLL的跟踪范围。
解决:检查多普勒频率的时间序列,看是否有趋势性漂移。如果有,说明晶振或者平台运动导致的。可以增大PLL带宽,或者用二阶环路(加频率辅助)。另外,检查积分时间是否太长,1ms积分在动态下可能不够,可以降到0.5ms。
5. 从跑通到跑好:用真实数据做闭环验证的几个技巧
跑通一条软件接收机链路只是起点,真正难的是让它在不同数据上稳定工作。我自己的习惯是,每拿到一段新数据,先跑一个最小闭环:捕获一颗最强卫星,跟踪10秒,看PLL和DLL鉴相误差是否收敛,然后做帧同步,看能否解出星历。这个闭环能在几分钟内告诉你数据是否可用、参数是否合理。
一个容易被忽略的技巧是:用捕获的多普勒估计值去校准跟踪环路的初始频率。很多实现直接把捕获的多普勒赋给PLL,但捕获的多普勒分辨率受搜索步长限制,可能有几百Hz的偏差。更好的做法是用捕获的多普勒作为中心,在±500Hz范围内做一次精细搜索,步长50Hz,找到相关峰最大的频点,再初始化PLL。这样PLL的初始误差小,锁定更快。
另一个技巧是跟踪结果的置信度评估。不要只看鉴相误差,还要看相关幅值(I_p^2 + Q_p^2的平方根)。如果相关幅值突然掉到噪声水平,说明信号被遮挡或者环路失锁。我一般设一个自适应门限:相关幅值低于前1秒均值的50%就标记为失锁,跳过这段数据。
% 跟踪置信度评估 amp = track_results(:,4); % 相关幅值 win = 1000; % 1秒窗口 for i = win+1:length(amp) baseline = mean(amp(i-win:i-1)); if amp(i) < 0.5 * baseline fprintf('第 %d ms 相关幅值下降,可能失锁\n', i); end end最后说一个血泪教训:不要用一段数据调好的参数直接套到另一段数据上。采样率、中频、数据格式、甚至采集设备的晶振稳定性都会影响环路参数。每次换数据,先跑捕获看峰值比,再跑跟踪看鉴相误差,最后跑定位看残差。三步都过了,再批量处理。希望帮到你。
本文还有配套的精品资源,点击获取