LoginSignup
0
0

ADALM PLUTOでFM受信(Julia版)

Last updated at Posted at 2023-08-15

概要

ソフトウエア無線による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で接続しています。

  1. PLUTOからIQデータを読み出す処理
  2. FM復調処理
  3. 音声出力処理

1の処理では、Plutoがサンプリング周波数の下限が大きいため、オーディオのサンプリング周波数(44.1kHz,48kHz)の16倍でIQデータをとりこむことにしました。
2の復調処理ではこれを4分の1にダウンサンプルしたのちに、遅延検波を行いFM復調します。そのあとで50usのディエンファシス処理を行います。ステレオ復調は今回はおこなっていません。
diaRxFM2.jpg

サンプルプログラム(復調処理のみ)

音声出力タスクは、PortAudioをオープンしたのち、

音声出力
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 ライブラリ呼び出し

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0