概要
ソフトウエア無線によるFM受信の技術メモです。RFアンプとAD変換部分は、Analog DevicesのPLUTOを使い、Windows環境のJuliaにてFM受信を行ってみました。
※更新
- 実行環境の準備に、PLUTOの環境変数の設定を追加(2024/6/28)
関連記事) python版->ADALM PLUTOでFM受信してみる
想定するアプリケーションと実行環境
想定するアプリケーション:SDR、FM受信、音声処理
実行環境:julia, windows10, ADALM PLUTO
実行環境の準備
PLUTOを使うために次のリンクに従ってWindows版のlibiioをインストールする。
初期設定では、ローカル周波数の設定範囲が(325 - 3800MHz)であるため、(teratermなど使って)シリアル通信でログインし、環境変数を設定しrebootすることで、周波数設定範囲を(70 - 6000 MHz)に拡大する。
(参考)Customizing the Pluto configuration Updating to the AD9364
`:シリアルコンソールから
fw_setenv attr_name compatible
fw_setenv attr_val ad9364
reboot
`
解説
FM受信処理を3種類に分解し、それぞれJuliaの非同期タスクとして起動し、Channelで接続しています。
- PLUTOからIQデータを読み出す処理
- FM復調処理
- 音声出力処理
1の処理では、Plutoがサンプリング周波数の下限が大きいため、オーディオのサンプリング周波数(44.1kHz,48kHz)の16倍でIQデータをとりこむことにしました。
2の復調処理ではこれを4分の1にダウンサンプルしたのちに、遅延検波を行いFM復調します。そのあとで50usのディエンファシス処理を行います。ステレオ復調は今回はおこなっていません。
サンプルプログラム(復調処理のみ)
音声出力タスクは、PortAudioをオープンしたのち、
`Julia:音声出力
T=Int16
function playSound(chMod,chSnd,FS_AUD)
stream=PortAudioStream("デバイス名",0,1, eltype=T,samplerate=FS_AUD)
try
take!(chSnd)
sleep(1)
while true
dat = take!(chSnd)
write(stream,dat)
end
catch e
println("sound 例外を受信:",e)
finally
close(stream)
end
end
`
遅延検波の原理はpython版記事に記載しています。
ディエンファシスフィルタは、$Hd(z)=\frac{1+z^{-1}}{\frac{Fs}{10000} + 1 -({\frac{Fs}{10000} - 1})z^{-1}}$
function demodFM(chDem,chSnd,FS_AUD)
try
aStI=[]
aStQ=[]
aStBB=[]
cIQdemLast=0 #遅延検波の直前の最終サンプルを保存
while true
asmp =take!(chDem)# get raw data
dIn=convert.(Float32,asmp)
dI=dIn[1:2:end];dQ=dIn[2:2:end] # demax IQ and downsample by 4
dId,aStI =downSample4(dI,aStI)
dQd,aStQ =downSample4(dQ,aStQ)
cIQdem= dId + im*dQd # complex
# ===================================================
# 遅延検波で1サンプル前のデータを必要とするため、
# 最後のサンプルを保存しておき、処理の直前に先頭に追加する。
#bbIQ = np.insert(decim,0,demLastIQ)
pushfirst!(cIQdem,cIQdemLast)
#bbIQ = decim
cIQdemLast = cIQdem[end]
#------------------------
# 遅延検波
# -+-[Delay]-- [x]---[ angle ]-->
# | ^
# +-[Conj ]----+
#------------------------
dem = angle.(cIQdem[2:end] .* conj.(cIQdem[1:end-1])) / π
# ===========================================
# decimate by 4 : convert <FS_AUD> 176ksps = 44.1
# ===========================================
dBB,aStBB =downSample4(dem,aStBB)
#de emphasis
b=[1,1]
a=[FS_AUD/10000+1, -(FS_AUD/10000-1)]
dEmp=filt(b,a,dBB)
x=convert.(T,floor.(dEmp*16000))
put!(chSnd,x)
end
catch e
println("Dem 例外を受信:",e)
finally
close(chSnd)
end
end
性能、残課題など
pythonで組んだ場合は、ブチブチと細かいノイズが入っていた気がするのですが、Julia版ではクリアになっています。動作も安定で音もよいのですが、遅延が大きい。チューニングの余地がありそう。
また、ステレオ復調までもっていきたいですが、簡単なパイロット抽出の方法がないか検討中。
-> 記事かきました。FM ステレオ復調(Julia)
余談ですが、Juliaでのiioライブラリへのアクセスは難儀しました。ニーズがあれば記事書こうかと思うのですが、どうでしょうね。ダウンサンプルフィルタは、juliaのfirライブラリにちょっとしたバグがあったので、手直しして実装しています。こちらもニーズがあれば記事書こうかと。
参考)
KATU(0x20FE),Julia ライブラリ呼び出し