制解調(diào)仿真:BFSK與慢跳頻完整實(shí)現(xiàn)解析)
簡介利用MATLAB實(shí)現(xiàn)跳頻信號調(diào)制與解調(diào)的仿真源碼面向通信工程專業(yè)學(xué)生、科研人員及無線通信入門開發(fā)者。資源聚焦跳頻通信中隨機(jī)頻率選擇、基帶調(diào)制如FSK/PSK、信道噪聲疊加與同步解調(diào)等關(guān)鍵環(huán)節(jié)通過可運(yùn)行腳本直觀展示發(fā)射端到接收端的完整鏈路跳頻技術(shù)廣泛用于藍(lán)牙、無線個(gè)域網(wǎng)WPAN及軍事通信學(xué)習(xí)該腳本有助于理解抗干擾通信原理。包體為單個(gè)m文件約2KB壓縮包內(nèi)共1個(gè)文件結(jié)構(gòu)精簡適合直接閱讀、修改參數(shù)和移植至自身項(xiàng)目中。已有1079人學(xué)習(xí)下載。借助該腳本可快速理解跳頻信號產(chǎn)生、頻率跳變規(guī)則、調(diào)制解調(diào)流程以及信噪比對誤碼性能的影響可作為課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)或技術(shù)預(yù)研的參考起點(diǎn)。 跳頻信號Frequency Hopping Signal一直是通信仿真里的??鸵彩俏医o學(xué)生上課和做項(xiàng)目驗(yàn)證時(shí)最喜歡拿來講的一類信號。用MATLAB把跳頻信號的調(diào)制和解調(diào)完整跑通這件事看起來簡單但真正動(dòng)手之后會(huì)發(fā)現(xiàn)參數(shù)怎么匹配、頻點(diǎn)怎么選、接收端怎么解跳每一步都有講究。這篇文章把我這次用MATLAB實(shí)現(xiàn)跳頻調(diào)制解調(diào)的全過程寫下來包括完整代碼、設(shè)計(jì)思路、以及我反復(fù)調(diào)試后總結(jié)的幾個(gè)坑。如果你是通信專業(yè)的學(xué)生或者剛接觸跳頻技術(shù)的工程師這篇文章應(yīng)該能幫你少走不少彎路。1. 跳頻系統(tǒng)設(shè)計(jì)的幾個(gè)關(guān)鍵選擇1.1 數(shù)據(jù)調(diào)制為什么選BFSK跳頻通信本身只是把載波頻率按偽隨機(jī)序列跳變并不規(guī)定上面調(diào)制什么所以理論上FSK、PSK、QAM都能和數(shù)據(jù)跳頻配合。我這次選的是BFSK也就是二進(jìn)制頻移鍵控原因很直接在跳頻系統(tǒng)里收發(fā)雙方的頻率同步本來就是個(gè)難題如果數(shù)據(jù)調(diào)制再選對相位敏感的PSK那解調(diào)時(shí)還需要額外做載波相位恢復(fù)在演示和教學(xué)場景里很容易把核心問題帶偏。BFSK是非相干檢測友好的接收端只需要比較兩個(gè)頻率支路的能量大小就能判數(shù)據(jù)容錯(cuò)空間大很多。實(shí)際工程中跳頻電臺也確實(shí)喜歡用FSK或者M(jìn)SK這類恒包絡(luò)調(diào)制一方面是因?yàn)楹惆j(luò)信號對功率放大器的線性度要求低另一方面是這一類調(diào)制方式的抗頻偏能力比較強(qiáng)和跳頻的頻率切換場景天然匹配。這算是我這次選型時(shí)的一個(gè)底層邏輯讓數(shù)據(jù)調(diào)制盡量簡單把注意力放在跳頻本身。1.2 慢跳頻還是快跳頻參數(shù)怎么定跳頻按照“跳頻速率和碼速率的關(guān)系”分為慢跳頻和快跳頻。如果一跳時(shí)間內(nèi)包含多個(gè)信息碼元叫慢跳頻如果一個(gè)信息碼元跨越多個(gè)跳頻周期叫快跳頻。我這次用的是慢跳頻每跳駐留時(shí)間10毫秒每個(gè)碼元寬度1毫秒也就是說每一跳里承載10個(gè)碼元。選慢跳頻的好處是接收端的跳頻同步壓力小很多只要在一個(gè)跳駐留時(shí)間內(nèi)對準(zhǔn)跳頻圖案就行不需要在碼元級別上做切跳處理。慢跳頻也更接近很多實(shí)際戰(zhàn)術(shù)電臺的工作方式比如藍(lán)牙的跳頻雖然快一些但本質(zhì)上也是按時(shí)間片跳變。如果做成快跳頻每一跳只有零點(diǎn)幾個(gè)碼元接收端需要在碼元內(nèi)拼接多個(gè)跳頻片段對同步精度的要求會(huì)高出好幾個(gè)量級不適合作為入門仿真項(xiàng)目。2. MATLAB仿真中的參數(shù)計(jì)算2.1 采樣率和觀測帶寬怎么匹配參數(shù)設(shè)計(jì)是這次仿真的關(guān)鍵。我先定數(shù)據(jù)速率再根據(jù)數(shù)據(jù)速率反推采樣率。碼速率設(shè)為1000bps也就是每個(gè)碼元1毫秒。為了把正弦波形看得比較平滑采樣率設(shè)為100kHz這樣每個(gè)碼元內(nèi)有100個(gè)采樣點(diǎn)畫波形、做頻譜都夠用。采樣率不是隨便取的它決定了整個(gè)仿真能承載的最大頻率。根據(jù)奈奎斯特定理100kHz采樣率最多能表示50kHz以內(nèi)的信號。我后面設(shè)置的最大跳頻頻點(diǎn)是18kHz加上數(shù)據(jù)調(diào)制頻率3kHz最高頻率21kHz遠(yuǎn)低于50kHz這個(gè)余量是需要的否則在實(shí)信號混頻時(shí)會(huì)產(chǎn)生混疊。做這類仿真時(shí)我習(xí)慣讓最高信號頻率控制在采樣率的0.5倍以內(nèi)最好是0.3倍以下留給頻譜展寬和濾波一個(gè)安全空間。2.2 跳頻間隔、碼速率和頻點(diǎn)數(shù)的約束跳頻間隔我定為2kHz頻點(diǎn)數(shù)8個(gè)所以跳頻頻率范圍從4kHz到18kHz每隔2kHz一個(gè)頻點(diǎn)。為什么要讓跳頻間隔遠(yuǎn)大于碼速率因?yàn)榻邮斩藦奶l信號里解出數(shù)據(jù)之后還要用相關(guān)器區(qū)分0和1兩個(gè)數(shù)據(jù)頻率如果跳頻間隔不夠大混頻后的鏡像分量和數(shù)據(jù)分量會(huì)擠在一起區(qū)分度會(huì)很差。BFSK的兩個(gè)數(shù)據(jù)頻率我分別取1kHz和3kHz頻差2kHz正好是碼速率的兩倍。在BFSK里頻差越大兩個(gè)頻率的正交性越好解調(diào)誤碼率越低但占用帶寬也越大。2kHz這個(gè)值算是一個(gè)平衡點(diǎn)在1ms的碼元寬度內(nèi)1kHz信號正好一個(gè)周期3kHz信號正好三個(gè)周期積分判決時(shí)天然正交這個(gè)數(shù)學(xué)關(guān)系在解調(diào)端會(huì)派上大用場。3. 調(diào)制端實(shí)現(xiàn)從數(shù)據(jù)比特到跳頻波形3.1 生成數(shù)據(jù)流和跳頻圖案調(diào)制端的第一步是生成要發(fā)送的二進(jìn)制數(shù)據(jù)流和跳頻圖案。數(shù)據(jù)流直接用隨機(jī)數(shù)生成模擬真實(shí)的信息比特。跳頻圖案我用了偽隨機(jī)序列來指定每一跳的頻點(diǎn)序號實(shí)際工程中會(huì)用m序列或Gold序列來生成這種偽隨機(jī)圖案這里為了可復(fù)現(xiàn)直接用帶固定種子的隨機(jī)數(shù)發(fā)生器。%% 參數(shù)設(shè)置 fs 100e3; % 采樣率 100kHz Tb 1e-3; % 碼元寬度 1ms Nsamp fs * Tb; % 每碼元采樣點(diǎn)數(shù) 100 Rb 1 / Tb; % 碼速率 1000bps FH 8; % 跳頻頻點(diǎn)數(shù) Th 10e-3; % 每跳駐留時(shí)間 10ms慢跳頻 Nh round(Th / Tb); % 每跳包含碼元數(shù) 10 hop_sep 2e3; % 跳頻間隔 2kHz hop_freqs (2:FH1) * hop_sep; % 頻點(diǎn)從 4kHz 到 18kHz f0_data 1e3; % BFSK 碼元0對應(yīng)頻率 f1_data 3e3; % BFSK 碼元1對應(yīng)頻率 Nhop 20; % 總跳數(shù) rng(2024); hop_pattern randi([0 FH-1], 1, Nhop); Nbits Nhop * Nh; data randi([0 1], 1, Nbits);你可能注意到我把跳頻最低頻點(diǎn)設(shè)成了4kHz而不是從0開始。這個(gè)細(xì)節(jié)后面會(huì)細(xì)講簡單說就是實(shí)信號混頻時(shí)會(huì)產(chǎn)生鏡像分量如果跳頻載頻太低鏡像成分會(huì)直接落到數(shù)據(jù)頻帶附近導(dǎo)致解調(diào)性能急劇惡化。4kHz起點(diǎn)是為了給鏡像分量留出足夠距離。3.2 基帶BFSK調(diào)制和跳頻上變頻數(shù)據(jù)流生成后先把每個(gè)比特映射成對應(yīng)頻率的余弦波形。這里我把“基帶”這個(gè)說法稍微放寬了實(shí)際上生成的是1kHz和3kHz的數(shù)據(jù)載波還不能直接發(fā)射需要再和一個(gè)更高頻率的跳頻載波相乘完成上變頻。%% 基帶BFSK信號生成 t_code (0:Nsamp-1) / fs; sig_base zeros(1, Nhop * Nsamp * Nh); idx 0; for k 1:Nbits if data(k) 1 sig_base(idx1:idxNsamp) cos(2*pi*f1_data*t_code); else sig_base(idx1:idxNsamp) cos(2*pi*f0_data*t_code); end idx idx Nsamp; end %% 跳頻上變頻 sig_tx zeros(1, length(sig_base)); idx 0; for h 1:Nhop fhop hop_freqs(hop_pattern(h) 1); t_hop (0:Nh*Nsamp-1) / fs; seg_base sig_base(idx1:idxNh*Nsamp); sig_tx(idx1:idxNh*Nsamp) seg_base .* cos(2*pi*fhop*t_hop); idx idx Nh*Nsamp; end上變頻的核心操作是逐跳進(jìn)行第h跳里整段的基帶信號乘上這一跳對應(yīng)的載波cos(2πf_hop t)。乘完之后信號頻譜里同時(shí)出現(xiàn)了“和頻”和“差頻”兩個(gè)邊帶這是實(shí)信號混頻的固有現(xiàn)象。接收端在解跳時(shí)會(huì)把有用的邊帶恢復(fù)出來而多余的鏡像邊帶只要頻率位置選得合適就不會(huì)造成干擾。仿真過程中我建議隨時(shí)用plot(t, sig_tx)看一眼發(fā)射波形你會(huì)看到信號幅度包絡(luò)沒變但內(nèi)部正弦波的疏密程度隨著跳頻圖案在變化。這就是跳頻最直觀的時(shí)域表現(xiàn)。4. 解調(diào)端實(shí)現(xiàn)同步解跳與BFSK判決4.1 接收端解跳處理解調(diào)的第一步是模擬信道加噪然后用和發(fā)射端完全相同的跳頻圖案去“解跳”。解跳的原理是接收信號里包含第h跳的載波成分接收機(jī)用同一個(gè)頻率的載波再去乘一次就能把數(shù)據(jù)頻率搬回低頻。%% 加噪信道 SNR_dB 10; sig_rx awgn(sig_tx, SNR_dB, measured); %% 解跳乘以本地同步載波 sig_dehop zeros(1, length(sig_rx)); idx 0; for h 1:Nhop fhop hop_freqs(hop_pattern(h) 1); t_hop (0:Nh*Nsamp-1) / fs; seg_rx sig_rx(idx1:idxNh*Nsamp); sig_dehop(idx1:idxNh*Nsamp) seg_rx .* (2*cos(2*pi*fhop*t_hop)); idx idx Nh*Nsamp; end注意這里乘的是2cos而不是cos這是為了補(bǔ)償混頻帶來的幅度減半讓后面相關(guān)器的輸出幅度保持穩(wěn)定。實(shí)際系統(tǒng)中這個(gè)“2”可以不用刻意做因?yàn)榻邮諜C(jī)自動(dòng)增益控制會(huì)統(tǒng)一調(diào)整信號幅度但在仿真里乘2能讓相關(guān)器輸出更清晰省去幅度歸一化的麻煩。解跳之后高頻部分還殘留著2倍跳頻載波附近的分量我沒有額外加低通濾波器。原因是后面的相關(guān)器本身就是一個(gè)窄帶濾波器它對每個(gè)碼元做積分只保留與本地參考頻率一致的分量高頻成分會(huì)在積分過程中被自然平均掉。這里跳頻最低頻點(diǎn)設(shè)置得夠高就保證了這些高頻殘留頻率在1ms積分時(shí)間內(nèi)都是整數(shù)周期積分結(jié)果趨近于零。4.2 基于相關(guān)器的BFSK解調(diào)BFSK解調(diào)最經(jīng)典、最穩(wěn)的方法是用兩個(gè)相關(guān)器分別和1kHz、3kHz的本地參考信號做相關(guān)哪個(gè)輸出大就判哪個(gè)。這個(gè)思路本質(zhì)上是在問接收到的這段信號里1kHz的成分多還是3kHz的成分多。%% BFSK相關(guān)解調(diào) demod_data zeros(1, Nbits); idx 0; for k 1:Nbits seg sig_dehop(idx1:idxNsamp); r0 sum(seg .* cos(2*pi*f0_data*t_code)); r1 sum(seg .* cos(2*pi*f1_data*t_code)); if r1 r0 demod_data(k) 1; else demod_data(k) 0; end idx idx Nsamp; end %% 誤碼率統(tǒng)計(jì) ber sum(data ~ demod_data) / Nbits; fprintf(SNR %d dB, BER %.4f\n, SNR_dB, ber);這段代碼直接對解跳后的每個(gè)碼元做相關(guān)積分。積分長度正好是1ms和碼元寬度一致1kHz信號在1ms內(nèi)積出一個(gè)正周期3kHz信號積出三個(gè)正周期取值相差很大判決裕量充足。如果積分長度和碼元寬度不匹配相關(guān)性能會(huì)明顯下降這個(gè)我在后文的坑里詳細(xì)說。5. 仿真結(jié)果和誤碼率驗(yàn)證5.1 時(shí)域波形和頻譜觀察跑通一次完整收發(fā)之后我建議你把發(fā)射信號和接收解調(diào)信號放在一起看。發(fā)射信號的頻譜會(huì)呈現(xiàn)一種“離散頻率跳變”的特征8個(gè)頻點(diǎn)輪流出現(xiàn)每個(gè)頻點(diǎn)附近都有一段能量。這和我們平時(shí)看到的固定載波通信系統(tǒng)完全不同——固定載波只有一個(gè)譜峰而跳頻信號是多個(gè)譜峰按時(shí)間切換。用pwelch或fft觀察接收信號也能看到噪聲背景下8個(gè)頻點(diǎn)的譜線依然清晰。這正好說明跳頻信號的“抗截獲”特性來源頻譜被攤開到更寬的頻帶里單點(diǎn)能量密度降低窄帶接收機(jī)很難完整捕獲。figure; subplot(2,1,1); plot((0:length(sig_tx)-1)/fs, sig_tx); title(發(fā)射跳頻信號時(shí)域波形); xlabel(時(shí)間/s); ylabel(幅度); subplot(2,1,2); Nfft 4096; f_axis (0:Nfft-1)/Nfft*fs; sig_fft fft(sig_tx, Nfft); plot(f_axis, 20*log10(abs(sig_fft)1e-10)); title(發(fā)射信號頻譜); xlabel(頻率/Hz); ylabel(dB);5.2 誤碼率隨信噪比變化為了驗(yàn)證系統(tǒng)的解調(diào)性能我在不同的信噪比下重復(fù)跑了仿真畫出誤碼率曲線。這是評估一個(gè)通信鏈路最直觀的指標(biāo)。%% 誤碼率曲線 SNR_list -5:2:15; BER_list zeros(size(SNR_list)); for m 1:length(SNR_list) sig_rx awgn(sig_tx, SNR_list(m), measured); % 解跳 相關(guān)解調(diào)統(tǒng)計(jì)誤碼率 % ...與上面相同流程 BER_list(m) ber; end BER_list(BER_list 0) 1e-6; figure; semilogy(SNR_list, BER_list, o-); grid on; xlabel(SNR/dB); ylabel(BER);實(shí)測下來在信噪比高于8dB之后基本能達(dá)到零誤碼5dB左右還有少量誤碼整體趨勢和理論BFSK誤碼率曲線能對得上。這個(gè)結(jié)果說明跳頻本身并沒有給數(shù)據(jù)解調(diào)增加額外的性能損失只要跳頻圖案同步準(zhǔn)確解調(diào)性能和普通BFSK是接近的。6. 調(diào)試中踩過的坑和解決方法6.1 實(shí)信號混頻后的鏡像分量“污染”數(shù)據(jù)頻帶這是我調(diào)試時(shí)栽得最深的一個(gè)坑。最開始我把跳頻最低頻點(diǎn)設(shè)成2kHz結(jié)果誤碼率怎么調(diào)都降不下來甚至在無噪聲情況下都有很高的誤碼。后來逐段分析才明白實(shí)信號上變頻會(huì)產(chǎn)生兩個(gè)邊帶接收端解跳后除了有用的數(shù)據(jù)頻率還有2倍跳頻載波的鏡像分量。當(dāng)跳頻載波比較低時(shí)鏡像分量會(huì)直接落到另一個(gè)數(shù)據(jù)頻率上相當(dāng)于自己干擾自己。解決辦法就是把跳頻最低頻點(diǎn)抬高到4kHz以上讓鏡像分量遠(yuǎn)離1kHz和3kHz的數(shù)據(jù)頻率。這也是很多實(shí)信號仿真里“載波頻率不能太低”的根本原因。如果你改用復(fù)信號仿真也就是用exp(j*2*pi*f*t)代替cos(2*pi*f*t)做上變頻這個(gè)鏡像問題會(huì)天然消失但需要引入復(fù)數(shù)運(yùn)算代碼會(huì)稍微復(fù)雜一些。6.2 相關(guān)器積分長度與碼元寬度必須嚴(yán)格對齊另一個(gè)容易忽略的點(diǎn)是相關(guān)器的積分長度。我一開始圖省事直接用整個(gè)解跳數(shù)據(jù)流去和本地參考頻率相乘再求和結(jié)果誤碼率極差。后來把相關(guān)運(yùn)算改成了逐碼元循環(huán)保證每個(gè)相關(guān)積分的窗口正好對應(yīng)一個(gè)碼元寬度性能立刻恢復(fù)正常。原因是BFSK相關(guān)解調(diào)在數(shù)學(xué)上等價(jià)于匹配濾波匹配濾波器的積分時(shí)間必須和信號持續(xù)時(shí)間一致。如果積分時(shí)間跨了兩個(gè)碼元不同數(shù)據(jù)的符號會(huì)互相抵消這就像拿一把長尺去量一個(gè)短物體永遠(yuǎn)量不準(zhǔn)。在做仿真時(shí)建議用一個(gè)idx指針精確控制每個(gè)碼元的起止位置不要用全序列一次性運(yùn)算。6.3 解跳誤碼率高企時(shí)先查頻點(diǎn)索引還有一個(gè)很隱蔽的bugMATLAB數(shù)組索引從1開始而我的跳頻圖案是用randi([0 FH-1])生成的頻點(diǎn)序號從0到7。如果取頻點(diǎn)時(shí)忘記加1hop_freqs(0)會(huì)直接報(bào)錯(cuò)而如果用了hop_freqs(hop_pattern(h))但hop_pattern正好出現(xiàn)0就會(huì)靜默地取出錯(cuò)誤頻點(diǎn)導(dǎo)致接收端解跳頻率和發(fā)射端不匹配誤碼率直接變成0.5。這類問題在仿真里特別難查因?yàn)樗粓?bào)錯(cuò)只是結(jié)果不對。排查方法是在調(diào)制端和解調(diào)端分別打印每一跳的頻點(diǎn)逐跳對照是否一致。我在代碼里統(tǒng)一用hop_pattern(h) 1來索引從根上避免了這個(gè)坑。7. 我的幾點(diǎn)實(shí)踐體會(huì)這個(gè)項(xiàng)目整體做下來我對跳頻通信最核心的認(rèn)知變化是跳頻并不改變信息本身的調(diào)制解調(diào)方式它只是在射頻層把載波按規(guī)則搬來搬去。最難的不是BFSK怎么解調(diào)而是如何保證收發(fā)雙方的頻率圖案在時(shí)間上嚴(yán)格對齊。一旦跳頻速率提高或者信道存在頻率偏移同步問題會(huì)立刻成為系統(tǒng)瓶頸。后續(xù)如果你想繼續(xù)擴(kuò)展我建議按這個(gè)順序做先給解跳模塊加上頻偏估計(jì)模擬真實(shí)收發(fā)信機(jī)晶振不一致的情況再把AWGN信道換成帶窄帶干擾的信道直觀感受跳頻的抗干擾效果最后可以嘗試把BFSK換成MSK看看在頻帶利用率和誤碼率上有什么變化。每一步改動(dòng)的驗(yàn)證手段都是這套時(shí)域波形、頻譜和誤碼率曲線它們會(huì)把系統(tǒng)的真實(shí)狀態(tài)如實(shí)地?cái)[在你面前。本文還有配套的精品資源點(diǎn)擊獲取