ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GPS信号处理全链路仿真:捕获、跟踪到帧同步的MATLAB实现

GPS信号处理全链路仿真:捕获、跟踪到帧同步的MATLAB实现 简介本资源是一套面向卫星导航研发人员、高校师生及通信方向科技工作者的GPS信号处理全流程MATLAB仿真方案系统覆盖GPS信号产生、伪码捕获、载波跟踪、比特同步、帧同步及导航电文解码等核心环节解决从理论建模到算法实现的一体化学习与工程验证难题。压缩包共590个文件含480个MATLAB源码.m、26个预存数据集.mat、20个结果可视化图.fig及少量C/C底层函数.c/.cpp、动态链接库.dll/.mex*和说明文档.txt/.pdf总大小7.23MB结构清晰、模块解耦便于分步调试与原理验证。已有239人下载学习资源附带完整可运行代码、详细注释、运行指南及地理坐标计算结果演示读者可直接复现信号生成至定位解算全过程并深入理解环路参数设计、相关峰检测、位同步判决、TOW提取与子帧校验等关键技术实现细节。 GPS信号处理这套链路从信号产生到捕获、跟踪、再到比特同步和帧同步是很多通信和导航方向的学生、工程师都会遇到的硬骨头。我最早接触这个课题的时候最大的感触是资料虽然多但大多是零散的代码片段很少有人把完整的仿真链路讲清楚。很多人拿着一个捕获算法跑通了就以为完事了结果一接跟踪环路就崩跟踪好不容易稳定了比特同步又不知道该从哪下嘴。这篇文章我就按照自己做过的完整仿真流程把GPS信号处理从信号产生到帧同步的每一个关键模块拆开讲包括为什么这么做、参数怎么定、代码怎么组织踩过的坑和调试心得也一并交代清楚。1. 先搞清楚GPS信号处理仿真的整体链路再动手做仿真最忌讳的就是上来就写代码。GPS接收机的基带信号处理说白了就是一条流水线先是天线收到射频信号经过前端下变频变成中频信号然后ADC采样成数字中频后面所有事情都在数字域完成。你的MATLAB仿真就是从数字中频信号开始一步步地把导航电文解调出来。1.1 基带信号处理五大模块的逻辑关系为什么说这几个模块是闭环的呢我画个逻辑链路你就明白了。信号产生模块负责生成一个模拟的GPS中频信号相当于你手里捏了一份标准答案。有了这个标准答案后面的捕获、跟踪、比特同步、帧同步每一步的结果都能和真实值对照调起来特别方便。捕获模块解决的是有没有卫星信号和大概在哪的问题它的输出是一个粗略的载波频率和码相位。跟踪模块接手之后用锁相环和延迟锁定环把频率和相位误差收敛到接近零精细地剥离载波和C/A码输出解扩后的导航数据比特流。比特同步从比特流里找到每个数据比特的边界因为一个导航数据比特是20个C/A码周期你不做比特同步根本不知道哪个毫秒边界是比特起点。帧同步在比特流里搜索前导字找到帧边界然后才能把导航电文按字解码提取星历参数为定位解算铺路。所以这个链路的逻辑很清晰捕获给跟踪提供初始值跟踪给比特同步提供干净的比特流比特同步给帧同步提供比特边界帧同步输出最终的电文。任何一个环节出问题后面全都白搭。1.2 MATLAB仿真平台的参数体系设定我用的参数体系是这样的GPS L1频段载波频率1575.42MHzC/A码速率1.023Mcps码长1023周期1ms导航电文速率50bps。在纯软件仿真里不需要真的跑到1575.42MHz那么高的频率一般把信号生成在一个中频上比如1.25MHz或者4.092MHz采样率选5MHz或者10MHz左右就行。我这次选的是中频1.25MHz、采样率5MHz。为什么选这个组合因为采样率正好是C/A码速率的5倍左右每个码片能采到4到5个点相关峰的形状比较干净。如果采样率太低比如只比奈奎斯特频率高一点点捕获的相关峰会很难看跟踪环路的鉴别器输出噪声也大。实际跑的时候还有一个很关键的参数——信号功率和噪声的比值也就是载噪比C/N0。仿真的时候我一般把C/N0设置在40到45dB-Hz之间这个范围对应室外开阔环境的典型值。太低了后面跟踪环路的表现会很差你会分不清是算法问题还是信噪比问题太高了又不符合实际场景。建议仿真初期用45dB-Hz先把流程跑通再往下压看算法的极限。2. 信号产生模块从C/A码生成到中频采样信号产生是整个仿真链路的起点也是很多人容易轻视的一步。如果信号生成得不对后面所有模块调试的参照系都是错的那种挫败感我太熟悉了。这一节我把从C/A码到最终中频信号生成的完整过程讲清楚。2.1 C/A码的生成原理与MATLAB实现C/A码是Gold码由两个10级的线性反馈移位寄存器G1和G2生成码长1023码率1.023Mcps所以一个完整的C/A码周期正好1ms。G1和G2各自生成一个m序列然后把G2经过特定抽头延迟后的序列与G1序列做模2加得到某个特定卫星的C/A码。G1的反馈多项式是1x^3x^10G2的反馈多项式是1x^2x^3x^6x^8x^9x^10。不同卫星的区别在于G2的输出抽头选择不同。比如PRN1号星的G2输出抽头是2和6PRN2号星是3和732颗卫星的抽头选择表在ICD-GPS-200文档里有完整列表。MATLAB实现起来并不复杂核心代码如下function caCode generateCACode(prn) % 生成指定PRN的C/A码输出长度为1023的±1序列 g1 ones(1, 10); g2 ones(1, 10); % G2抽头选择表前32颗卫星 g2DelayTable [2 6; 3 7; 4 8; 5 9; 1 9; 2 10; 1 8; 2 9; 3 10; 2 3; 3 4; 5 6; 6 7; 7 8; 8 9; 9 10; 1 4; 2 5; 3 6; 4 7; 5 8; 6 9; 1 3; 4 6; 5 7; 6 8; 7 9; 8 10; 1 6; 2 7; 3 8; 4 9]; tap1 g2DelayTable(prn, 1); tap2 g2DelayTable(prn, 2); caCode zeros(1, 1023); for i 1:1023 g1out g1(10); g2out xor(g2(tap1), g2(tap2)); caCode(i) xor(g1out, g2out); % G1反馈 fb1 xor(g1(3), g1(10)); g1 [fb1, g1(1:9)]; % G2反馈 fb2 xor(xor(xor(xor(xor(g2(2), g2(3)), g2(6)), g2(8)), g2(9)), g2(10)); g2 [fb2, g2(1:9)]; end % 转换为±1 caCode 2 * caCode - 1; end这段代码生成的是码片级的±1序列。实际仿真中需要在时间轴上展开每个码片重复fs/fc个采样点。如果你采样率5MHz、C/A码速率1.023MHz那么每个码片大约4.887个采样点。最简单的做法是用repelem按整数倍展开但这样展开之后码率和采样率之间会有微小偏差。更严谨的做法是定义一个码片计数器按码率逐步累加维护一个当前采样点对应哪个码片的索引。这种做法和真实接收机中码NCO的工作方式一致后面跟踪环路也是这样处理的。2.2 中频信号模型与噪声叠加有了C/A码就可以构造完整的中频信号了。GPS L1信号经过前端下变频后的中频模型是s(n) √(2P) * C(n) * D(n) * cos(2π * f_IF * n * Ts φ) noise其中C(n)是C/A码序列D(n)是导航电文比特±1速率50bpsf_IF是中频频率φ是初始载波相位P是信号功率noise是高斯白噪声。我把信号产生的代码写成一个函数这样后面加多颗卫星、加噪声、调整参数都方便function [signal, params] generateGPSSignal(prnList, fs, fIF, cn0dBHz, durationMs) % 生成多颗GPS卫星的中频信号 % prnList: 卫星编号数组例如 [1, 5, 9] % fs: 采样率(Hz) % fIF: 中频频率(Hz) % cn0dBHz: 载噪比(dB-Hz) % durationMs: 信号时长(ms) fc 1.023e6; % C/A码速率 T 1e-3; % 每ms一个C/A码周期 nSamples round(fs * durationMs / 1000); t (0:nSamples-1) / fs; signal zeros(1, nSamples); for prn prnList caCode generateCACode(prn); % 扩展C/A码到采样域码片索引映射 codeIdx mod(floor(t * fc), 1023) 1; caSeq caCode(codeIdx); % 生成导航电文这里用随机比特模拟实际可用真实星历 nBits ceil(durationMs / 20); navBits 2 * (randi([0 1], 1, nBits) * 2 - 1); bitIdx mod(floor(t / 0.02), nBits) 1; dataSeq navBits(bitIdx); % 载波扩频调制 carrier cos(2 * pi * fIF * t rand * 2 * pi); signal signal sqrt(2) * caSeq .* dataSeq .* carrier; end % 叠加噪声根据C/N0计算噪声功率 cn0 10^(cn0dBHz / 10); noisePower 0.5 * length(prnList) / (cn0 * T); % 简化计算 signal signal sqrt(noisePower) * randn(1, nSamples); params.sampleRate fs; params.ifFreq fIF; params.durationMs durationMs; end这里有个细节噪声功率的计算我做了简化。实际上精确的做法是先固定信号功率再按C/N0 信号功率/(噪声功率谱密度)反推噪声方差。我一般把信号幅度设为√2这样信号功率就是1噪声功率 fs / (10^(C/N0/10))这个公式更标准。生成信号之后的第一个自检方法是画频谱图你应该能在1.25MHz附近看到一个凸起的峰多普勒频移为0时峰的位置就在中频上。如果频谱和预期不符先不要往下做捕获回去检查信号生成环节。这个习惯帮我省了不少时间。3. 捕获算法三维搜索的串行思路与并行实现捕获的任务是确定可见卫星、粗略载波频率和码相位。这里有一个三维搜索的问题卫星维度PRN、频率维度多普勒频移、码相位维度0到1022个码片。3.1 为什么频率搜索范围和步进这样设置GPS卫星在轨道上运动用户也在运动两者之间有相对运动就会产生多普勒频移。对于静止用户来说L1载波上的多普勒范围大约是±5kHz。但如果接收机在高速运动场景比如飞机或者汽车多普勒范围会更大。所以捕获阶段的频率搜索范围一般设为±10kHz覆盖大多数场景。频率搜索步进的选择很讲究。步进太大频率误差超过一定范围后相关峰会被展平信号能量损失严重步进太小搜索维度太多计算量翻倍。理论上积分时间为1ms时频率误差为Δf时相关损耗正比于|sinc(Δf * T)|当Δf500Hz时损耗约为4dB这个损耗还在可接受范围内。所以我通常用500Hz作为频率搜索步进在±10kHz范围内搜索41个频率点。频率步进越小损耗越小但计算量线性增长。对于二维搜索频率×码相位500Hz步进和1ms相干积分时间是一个经典的平衡选择。3.2 并行码相位搜索和串行搜索的取舍串行搜索的思路很简单每个频率点每个码相位做一次相关运算。码相位有1023个频率有41个再乘以12颗可见卫星总的相关次数是50万次左右。每次相关做2048个点的乘加运算MATLAB跑起来会很慢尤其是你要跑几百毫秒的数据时。所以我推荐用FFT并行码相位搜索。原理是利用循环相关定理信号与本地码的循环相关等于信号FFT乘以本地码FFT的共轭再做逆FFT。一次FFT操作能同时得到所有码相位的相关结果计算效率提升了几个数量级。核心代码大概是这样的function [peakMetric, freqEst, codePhaseEst] acquisition(signal, fs, fIF, prn, freqRange) fc 1.023e6; T 1e-3; nSamplesInMs round(fs * T); % 取1ms数据 if length(signal) nSamplesInMs error(信号长度不足1ms); end data signal(1:nSamplesInMs); % 生成本地码采样域 localCode generateCACode(prn); t_ms (0:nSamplesInMs-1) / fs; codePhaseVec mod(t_ms * fc, 1023); localCodeSampled localCode(floor(codePhaseVec) 1); % 频率搜索 metric zeros(length(freqRange), nSamplesInMs); for k 1:length(freqRange) f freqRange(k); % 混频到基带 carrier exp(-1j * 2 * pi * (fIF - f) * t_ms); mixed data .* carrier; % FFT相关 S fft(mixed); L conj(fft(localCodeSampled)); corr ifft(S .* L); metric(k, :) abs(corr); end % 寻找峰值 [maxVal, idx] max(metric(:)); [freqIdx, codeIdx] ind2sub(size(metric), idx); freqEst freqRange(freqIdx); codePhaseEst codeIdx - 1; % 码相位采样点单位 peakMetric maxVal; end这段代码有两点要注意。第一混频时我用的是fIF - f意思是如果把本地振荡器频率调到f混频后残余频率是fIF - f正好落在基带。这个符号关系虽然小但搞反了会导致搜索的频率全部偏离真实值。第二FFT相关的输出长度和输入一样但循环相关和线性相关在高码相位偏移时会有差异不过对GPS捕获来说通常使用的1ms数据内C/A码只重复一次循环相关误差很小工程上完全能用。3.3 捕获门限设定与峰值判定捕获的判定通常用峰均比或者峰值与次大值之比。我习惯用峰值/第二峰值比比值超过2.5就认为捕获成功。这个判据在多卫星场景下表现稳定比固定的绝对门限可靠得多。具体操作时在metric矩阵中先找到最大值然后把最大值附近一个小邻域比如±1个频率bin、±10个码相位挖掉再找该区域外的最大值作为第二峰值这样能避免峰值旁瓣被误判为次大值。还有一个常被忽略的点捕获最好取多段数据做非相干累加。单次1ms的相关结果受导航电文比特跳变的影响很大如果比特跳变恰好发生在积分区间内峰值能量会被削减。我一般取5到10段1ms数据每段分别做相关把幅度平方累加再寻找峰值。这个操作叫非相干积分能显著提高捕获灵敏度代价只是计算量稍微增加。4. 载波跟踪与码跟踪环路滤波器的参数设计和实现捕获给出的频率和码相位精度不够高频率误差可能还有几百赫兹码相位误差最多到半个采样点。这些误差直接解扩的话信号会被残余载波调制误码率非常差。所以跟踪环路的作用就是把这些误差收敛到足够小。4.1 载波环选择Costas环的原因GPS导航电文是BPSK调制相位在0度和180度之间跳变。如果用一个普通的锁相环它会把180度相位跳变当成相位误差去纠正结果载波环路失锁。所以必须用Costas环它的相位鉴别器输出对180度相位模糊不敏感在BPSK信号中才能稳定跟踪。Costas环鉴别器有几种选择我常用的是二象限反正切e atan(Q / I)其中I和Q是相干积分后的同相和正交分量。这个鉴别器在低信噪比下性能接近最优而且实现简单。如果信号中有数据比特跳变反正切鉴别器也能自动处理因为它只关心相位差本身。积分时间T一般取1ms正好一个C/A码周期。导航电文比特是20ms一个所以对每20个连贯积分时间来说最多有一个比特跳变而且有时候跳变发生在积分区间中间能量损失可以通过非相干处理来平衡。4.2 环路滤波器系数的完整推导二阶环路滤波器是GPS跟踪中最常用的结构。载波环我一般设计噪声带宽Bn为10到18Hz码环噪声带宽为1到2Hz。噪声带宽宽了动态响应好但输出噪声大窄了噪声小但无法跟上高动态的信号。对于静态或者低动态的仿真场景载波环15Hz、码环1Hz是个比较稳的组合。对于理想二阶环已知噪声带宽Bn自然角频率ωn的计算公式是Bn (ωn / 2) * (ξ 1 / (4ξ))取阻尼系数ξ0.707这个公式就变成Bn ≈ 0.53 * ωn所以ωn Bn / 0.53对于Bn15Hzωn约等于28.3rad/s。环路滤波器输出的两个系数是C1 2ξωnT C2 (ωnT)²其中T是积分时间1ms。代入数字C1 2 * 0.707 * 28.3 * 0.001 0.04 C2 (28.3 * 0.001)² 0.0008码环的噪声带宽通常取1Hz那么ωn 1 / 0.53 ≈ 1.887rad/sC1和C2就小得多。实现环路滤波器的时候我用的是标准的二阶数字环路function [freqNco, phaseNco] updateCarrierLoop(phaseError, loopState, C1, C2) % loopState.dfreq: 频率累加器 % loopState.dphase: 相位累加器 loopState.dfreq loopState.dfreq C2 * phaseError; loopState.dphase loopState.dphase C1 * phaseError loopState.dfreq; freqNco loopState.dfreq; phaseNco loopState.dphase; end这个结构相当于一个比例积分PI控制器。C1控制环路的比例项响应快但容易振荡C2控制积分项负责消除稳态误差。两个参数配好了环路就能在不振荡的前提下快速收敛。4.3 码环的E-L鉴别器与归一化处理码环的作用是保持本地C/A码和接收信号的码相位对齐。经典做法是生成三个本地码超前Early、即时Prompt、滞后Late三者相差半个码片或者更小的间距。即时码用于解扩超前码和滞后码用于产生误差信号。鉴别器我用归一化的超前减滞后包络e (√(I_E² Q_E²) - √(I_L² Q_L²)) / (√(I_E² Q_E²) √(I_L² Q_L²))为什么要做归一化因为信号功率在不同通道间可能有差异如果不归一化鉴别器的增益会随信号功率变化导致环路带宽漂移。归一化之后鉴别器输出在码相位误差为零时是0误差为一个码片时接近±1增益稳定。码环的本地码频率通过码NCO控制码NCO的输出频率在1.023MHz的基础上加上一个来自码环滤波器的修正量。这样码环才能跟踪卫星运动引起的码多普勒大约等于载波多普勒的1/1540倍。跟踪环路跑起来后有一个很好的可视化自检方法画出I支路的输出。如果环路锁定I支路应该能看到清晰的、每20ms跳变一次的导航电文比特Q支路的能量应该远小于I支路。如果Q支路能量和I支路差不多说明载波相位没有完全对齐环路还没锁住。5. 比特同步和帧同步从比特流到导航电文到了这一步解扩后的数据已经可以直观地看到导航比特的轮廓了但还没有对齐到正确的比特边界。比特同步就是解决边界到底在哪的问题。5.1 直方图法做比特同步的完整过程一个导航电文比特持续20ms对应20个C/A码周期。但是接收机的采样时钟和发射时钟不同步所以比特边界不一定落在每个毫秒边界上理论上可能落在20个毫秒中的任意一个位置。我的做法是直方图法。具体来说每1ms相干积分得到一个I值记录相邻两个1ms积分之间I值符号的变化。如果符号变化说明这个1ms边界附近可能是一个比特边界。统计很长时间比如1秒即1000个1ms积分把符号变化的次数按它在20ms周期内的位置0到19画成直方图。符号变化最频繁的那个位置就是最可能的比特边界。这个方法的原理是导航电文比特跳变只发生在真正的比特边界上在非边界位置虽然可能因为噪声偶尔出现符号变化但概率远低于真实边界处的跳变频率。代码如下function bitEdge bitSynchronization(I_values) % I_values: 逐ms的I支路输出长度N % 返回值: 比特边界在20ms内的位置0~19 histCounts zeros(1, 20); for n 2:length(I_values) if sign(I_values(n)) ~ sign(I_values(n-1)) pos mod(n-2, 20) 1; % 第n个积分对应的位置 histCounts(pos) histCounts(pos) 1; end end [~, bitEdge] max(histCounts); bitEdge bitEdge - 1; % 转换为0~19 end找到比特边界后把每20个相邻1ms的I值累加就得到20ms积分的数据比特软值符号即为解调出的导航比特。这里有一个我在调试中遇到的坑如果载波环路整周模糊度没有解决I支路的极性可能反了导致符号全部反转。所以做比特同步之前最好先确认I支路输出的包络是稳定的即环路锁定的否则直方图的统计规律会被破坏。5.2 帧同步的前导字搜索与奇偶校验验证导航电文按子帧组织每个子帧300比特持续6秒。每个子帧的前8比特是固定的前导字10001011十六进制0x8B。帧同步的核心就是在比特流里搜索这个前导字。搜索的时候有个关键技巧不要只找一次前导字就确认帧同步那样误判概率太高。正确做法是找到前导字后隔300比特再找下一个前导字连续两次匹配才算真正的帧同步。这个策略能有效排除噪声引起的假前导字。找到帧边界后还有一个更严格的验证手段奇偶校验。GPS每个字是30比特24个数据位加6个校验位奇偶校验用的是(32,26)汉明码的变种其中最后两个校验位的计算依赖前一个字的最后两个比特形成一种特殊的链式校验。我在MATLAB里按ICD-GPS-200文档里的算法实现了校验函数每帧校验全部通过后才认为帧同步完全正确。奇偶校验的实现举个例子。对于每个字取前24个数据比特加上前一个字传来的D29和D30两个比特组成26个比特的输入然后计算6个校验位D25到D30。校验位的生成矩阵是固定的文档里给出了完整的生成表。我一开始自己推导了半天后来发现直接用文档里的表反而更简单如果你在实现的时候卡住了直接查表就行。完成帧同步之后就可以从HOW字中提取TOW周内秒从各个子帧中解码星历参数计算卫星位置了。不过这是定位解算的范畴了项目做到帧同步已经完成了GPS基带信号处理的主链路。6. 实际调试的实战经验采样率选择、从仿真到实数据的衔接最后这部分是我做完整套仿真之后回头整理的一些最实用的心得。这些经验不是教科书上能直接找到的但我几乎每一个都在实际调试中被坑过写出来帮你避坑。6.1 采样率和中频选择的工程权衡很多人一开始会问采样率越高信号保真度越高那是不是选得越高越好从仿真计算量的角度来说绝对不是。采样率每提高一倍捕获和跟踪的FFT点数就翻倍计算量线性甚至超线性增长。我自己的建议是纯教学仿真用5MHz就够了最多到10MHz。选择中频时注意要让中频频率和采样率之间有合适的余量中频不能太靠近采样率的一半否则抗混叠滤波很难做。比如采样率5MHz时中频选1.25MHz就是一个好组合。需要特别提醒的是采样率不一定是C/A码速率的整数倍。很多教程为了简化选5.115MHz正好是1.023MHz的5倍这样每个码片精确对应5个采样点省去了码相位索引的麻烦。但真实接收机很少这么巧5.714MHz这种非整数倍关系更常见。所以我不建议过度依赖整数倍关系在代码中把码相位索引做成可配置的能适应各种采样率才是更通用的做法。6.2 仿真信号和真实采样数据的四点差异很多人把仿真跑通了自信心满满地换成真实GPS采样数据结果发现各种问题。我总结四点主要差异真实信号的载波频率和中频频率存在不确定性你不知道它到底在哪个频率上捕获时要搜索的频率范围可能需要更宽。真实信号的C/A码相位和采样时钟之间存在长期的相对漂移跟踪环路的码NCO需要持续修正否则时间长了码相位会逐渐漂走。真实信号有多径、天线增益波动、前端非理想特性等相关峰的形态可能不如仿真那么干净这时捕获门限和跟踪滤波器的参数需要适当放宽。真实信号中导航电文的比特边界和子帧结构与仿真中完全一致但需要先经过比特同步和帧同步才能解出没有仿真中提前知道答案的便利。建议你从仿真过渡到真实数据时先找一个公开的GPS中频数据文件比如常见的一些采集数据集按照这套代码流程走一遍遇到的问题往往是宝贵的调试经验。6.3 调试定位问题的方法论我在调试过程中总结了一个重要原则每个模块都要有独立的验证手段别想着等整个链路跑通了再一起排查。信号生成模块画频谱图验证捕获模块在已知卫星号的前提下验证峰值位置跟踪模块用已知的C/A码相位验证I支路的解扩增益比特同步用已知的电文比特序列验证边界检测结果帧同步用已知的前导字位置验证搜索逻辑。每个环节只依赖前一个环节的已知输出出了问题就能快速定位到具体模块。如果你发现跟踪环路输出噪声很大先回过去检查捕获给的初始码相位是否精确而不是急着调环路滤波器参数。还有一个经验是仿真中务必加足够的噪声。我见过很多人在信噪比极高的条件下调好了参数一降低载噪比就全面崩溃。建议你从45dB-Hz开始每个模块调好后逐步降到40、35甚至30dB-Hz观察算法的性能衰退规律。这样你才会知道自己的算法边界在哪里面试或者答辩的时候也更有底气回答你的算法在什么条件下失效这类问题。我自己在做接收机方向的项目时一直保留这套GPS基带仿真代码后来把信号产生部分换成射频前端采集的真实中频数据整个捕获跟踪链路只做了很小的修改就能运行这就是模块化设计带来的收益。如果你也想长期在这个方向深耕建议你从一开始就把代码结构组织好把参数配置和核心算法分开把每个功能封装成独立的函数这些习惯会在后续迭代中给你省下大把时间。本文还有配套的精品资源点击获取
返回列表