概要
ソフトウェア無線によるFM受信の技術メモです。今回は懸案であったコンポジットステレオ信号のソフトウェア復調をおこなってみました。
関連記事)
ADALM PLUTOでFM受信してみる(python版)
ADALM PLUTOでFM受信(Julia版)
解説
コンポジット信号はL+R信号、19kHzのパイロット信号、パイロット信号に位相同期した38kHzでDBS変調されたL-R信号、さらにDARCの信号が多重されています。
図 コンポジット信号のスペクトル例
復号の手順は、受信サービスさんに連載されている、「shu-chanの放送ネットワーク道しるべ」の手順に従って実装しました。
(参考)Shu-chan,<FM放送用受信機(その3)>,放送ネットワーク道しるべ中山道(小田井宿),受信サービス株式会社
具体的手順は次の通り。
- まずパイロット信号を再生
- パイロット信号を低減
- Fc=15kHz程度のLPFでL+R信号をとりだす
- 3KHzの信号をMIXしたのちLPFをかけてL-R信号をとりだす。
- L+R,L-RからL,Rを取り出し、De-empasisフィルタをかける
パラメータとレジスタ格納領域を作成
サンプリングパラメータ、NCO設定データ、PLLパラメータとレジスタ、低域通過フィルタのレジスタ、ディエンファシスパラメータの格納領域を定義する。
mutable struct StereoDecoder
FS_AUD # sampling rate of audio signal
FS_DEM # sampling rate of composit signal(FS_AUD x 4)
rNco::Nco # NCOクラス
k1_pll # PLL parameter
k2_pll
z_pll # PLL register
aStatCicSin # registers for CIC LPF
aStatCicCos
aStatCicP
aStatCicM
a_deemp # ディエンファシス パラメータ
b_deemp
end
パラメータ設定
NCOをパイロット信号の周波数19kHzで初期化し、ディエンファシスを50μ秒とする。
(参考)ディエンファシスフィルタの設計は、ADALM PLUTOでFM受信してみる(python版)
function cStereoDecoder_create(FsAud,FsDem)::StereoDecoder
StereoDecoder(FsAud, FsDem, cNco_create(FsDem,19000.0),
5,2,0.0,
[],[],[],[],
[FsAud/10000+1,-(FsAud/10000-1)],[1,1])
end
デコード処理
処理手順は
-
NCOを19kHzのパイロット信号に同期させる
このため、NCOの正弦波出力とコンポジット信号をMIXしDC成分をとりだし、これが0になるようにNCOを制御する。
-
パイロット信号の振幅を計算する
NCOから余弦波を作成し、コンポジット信号にMIXしDC成分を取り出すと、パイロット信号の振幅の1/2が得られる -
前記信号にLPFをかけて15kHzまでのL+R信号をとりだす
-
L+R,L-RからL,R信号をとりだす
function mStereoDecoder_proc!(rSD::StereoDecoder,x,nDiv)
nSmp=size(dem)[1]
nDur=nSmp ÷ nDiv
nR=128;nCasc=4
adm2=[]; amx2=[] # storages for output
for iPos=1:nDur:nSmp
#NCOで位相を計算してから、19kHzのSin信号を作成する
aPh = mNco_generatePhase!(rSD.rNco,nDur)
aSin= cNco_sinWave(aPh)
### LOCK PILOT ###
#--- mix ---
mx= x[iPos:iPos+nDur-1] .* aSin
#--- LPF ---
(outSin,rSD.aStatCicSin)=cic_ma(mx,nR,nCasc,rSD.aStatCicSin)
s= sum(outSin)/nDur *10 # 1/10% *rSD.FS_DEM /2/75000 *10
#--- PLL ---
fctr = rSD.k1_pll * s + rSD.z_pll
rSD.z_pll += rSD.k2_pll*s
rSD.rNco.dFreqDelta = -fctr
### CANCEL PILOT ###
aCos= cNco_cosWave(aPh)
mx= x[iPos:iPos+nDur-1] .* aCos
#--- LPF ---
(outCos,rSD.aStatCicCos)=cic_ma(mx,nR,nCasc,rSD.aStatCicCos)
#--- amplitude of Pilot
p= sum(outCos)/nDur *2 # *rSD.FS_DEM /2/75000 *10
#--- cancel pilot form dem
dm2= x[iPos:iPos+nDur-1] - aCos * p
### demod L-R ###
aSin2=cNco_sin2Wave(aPh)
mx2 = dm2 .* aSin2
if size(amx2)[1]==0
adm2=copy(dm2)
amx2=copy(mx2)
else
append!(adm2,dm2)
append!(amx2,mx2)
end
end
(outP,rSD.aStatCicP)=cic_ma(adm2,4,4,rSD.aStatCicP)
(outM,rSD.aStatCicM)=cic_ma(amx2,4,4,rSD.aStatCicM)
outPd=outP[1:4:end]
outMd=outM[1:4:end]
Lr=outPd+outMd
Rr=outPd-outMd
L=filt(rSD.b_deemp,rSD.a_deemp,Lr)
R=filt(rSD.b_deemp,rSD.a_deemp,Rr)
return (L,R)
end
性能、残課題など
SNがよいFM局であれば、ほぼ問題なくステレオ分離できているが、信号が弱いとノイジーになります。このあたりは、PLLのパラメータ、分割区間の調整で改善するとよいのですが。
L+R,L-R信号を取り出すためのダウンサンプル用LPFをCIC_Filterで代用しています。このため通過帯域がだれています。本線なので、FIR構成のフィルタなどできちんと処理した方がよいかと。
NCO,CICフィルタの実装解説は、またの機会に。