PocketSDR APにてbladeRFを使いたい
PocketSDR AP
GitHubにて公開されているPocketSDRは、ソフトウェア無線(SDR: software defined radio)にて測位衛星信号の受信と処理を行うパッケージであり、Maxim MAX2771を用いたハードウェアと、Pythonにて記述されたソフトウェア群(AP: application)からなります。PocketSDRには、このハードウェアにて観測したサンプルデータが添付されていて、実際の信号処理を容易に体験できます。
- PocketSDRすごい(スナップショット測位編)
- PocketSDRすごい(FFTWによる高速化)
- PocketSDRすごい(L6信号デコード編)
- PocketSDRすごい(pocket_trk編)
- PocketSDRすごい(pocket_acq編)
PocketSDRハードウェア
PocketSDRには、CAD(computer aided design)ソフトウェアAutodesk EAGLE用の回路図エディタファイル(拡張子sch)と、配線パターンファイル(拡張子brd)が添付されています。このEAGLEは一定条件にて無償で利用できます。EAGLEにて、schファイルとbrdファイルを読み込めば、基板発注に必要なガーバーデータやメタルマスクデータを出力できます。
私も、リフロー炉(基板ヒーター)を準備して、PocketSDRハードウェア作成を目指していますが、現在、そのMAX2771が入手できないのです。末尾に何もつかないMAX2769にも興味がありますが、こちらも入手できない状況です。
しかしながら、PocketSDR APは、特定ハードウェアに依存しないように作成されています。そこで、手持ちSDRハードウェアのbladeRF x40に測位衛星アンテナを接続して信号を観測し、そのPocketSDR APでの処理を試みます。
結果は微妙ですが、最低限の確認はできました。いずれはPocketSDRハードウェアを利用したいです。
機器構成
ここで使用したハードウェア機材は次の通りです。信号スプリッタの1ポートを利用して、今回の実験を行います。残りポートには、bynav C1-FS受信機と、u-blox F9P受信機・Drogger VRSC受信機を接続しています。
- Nuand bladeRF x40 (300 MHz-3.8 GHz, 12-bit 40MSPS, 40KLE Altera Cyclone 4E FPGA)
- DFRobot LattePanda Alpha 800s (Intel Core m3-8100y, 8GB LPDDR3) + NVMe SSD 1TB
- Beitian BT-200(L1帯・L2帯・L5帯・L6帯対応アンテナ)
- 同軸ケーブル20 m
- InStock Wireless GPS410(4分岐スプリッタ)
LattePandaには、Ubuntu 20.04.4 LTSをインストールしています。ここで使用したbladeRFは古い製品ですが、現行製品のbladeRF 2.0 microでも同様に利用できると思います。また、bladeRF-cli -i
にて確認したbladeRFのバージョンは次の通りです。オプション-i
は対話モード利用を表します。
bladeRF> version
bladeRF-cli version: 1.8.0-git-c8320f71-ppafocal
libbladeRF version: 2.2.1-git-c8320f71-ppafocal
Firmware version: 2.3.2
FPGA version: 0.11.0 (configured by USB host)
GPS L1 C/A信号の観測
GPS L1 C/A信号の中心周波数は、1.57542 GHzです。はじめに、bladeRFに設定する標本化周波数と帯域幅を決めなければなりません。PocketSDRのサンプルには、ベースバンド標本化を行ったものと、Low-IF(intermediate frequency)標本化を行ったものとがあります。ここでは、ベースバンド標本化を行ったサンプルL1_20211202_084700_4MHz_IQ.bin
の諸元(標本化周波数4 MHz、帯域幅2.5 MHz)を参考にして、標本化周波数を4 MHzに、また、帯域幅を3 MHzに設定します。bladeRFにて設定できる帯域幅は、1.5 MHz、2.5 MHz、2.75 MHz、3 MHz、3.84 MHz、5 MHz、5.5 MHz、6 MHz、7 MHz、8.75 MHz、10 MHz、12 MHz、14 MHz、20 MHz、または、28 MHzです。
実際には、AGC(automatic gain control)をオフにした上で、利得を最大の60 dBに設定して、直流オフセット補正を行い、0.1秒間の記録を行います。ここではbladeRF-cli -i
により対話的にbladeRFを設定しますが、これをファイルに保存して-s
オプションでバッチ実行することも可能です。
set frequency rx 1.57542g
set bandwidth rx 3m
set samplerate rx 4m
set agc rx off
set gain rx 60
cal lms
cal dc rx
rx config format=bin n=400k file=ch.bin
rx start
rx wait
すぐにbladeRF>
のプロンプトが出て、観測ファイルch.binが保存されます。
bladeRF観測データの読み込み
この観測ファイルをPocketSDR APに読み込むために、python
ディレクトリにあるコードsdr_func.py
とpocket_trk.py
を修正します。修正する関数はread_data()
です。
sdr_func.py
def read_data(file, fs, IQ, T, toff=0.0, sdrname='pocketsdr'):
off = int(fs * toff * IQ)
cnt = int(fs * T * IQ) if T > 0.0 else -1 # all if T=0.0
if sdrname == 'pocketsdr':
dt = 'int8' # data type
div = 1 # divisor of data
qsgn = -1 # Q sign inverted in MAX2771
elif sdrname == 'bladerf':
dt = 'int16' # data type
div = 256 # divisor of data
qsgn = 1 # Q sign
else:
raise ValueError('Please specify appropriate SDR.')
raw = np.fromfile(file, dtype=dt, offset=off, count=cnt)
if len(raw) < cnt:
return np.array([], dtype='complex64')
elif IQ == 1: # I-sampling
return np.array(raw/div, dtype='complex64')
else: # IQ-sampling
return np.array(raw[0::2]/div + raw[1::2] * 1j*qsgn/div, dtype='complex64')
pocket_trk.py
def read_data(fp, N, IQ, buff, ix, sdrname = 'pocketsdr'):
if sdrname == 'pocketsdr':
dt ='int8' # data type
ds = 1 # data size
div = 1 # divisor of data
qsgn = -1 # Q sign inverted in MAX2771
elif sdrname == 'bladerf':
dt = 'int16' # data type
ds = 2 # data size
div = 256 # divisor of data
qsgn = 1 # Q sign
else:
raise ValueError('Please specify appropriate SDR.')
if fp == None:
raw = np.frombuffer(sys.stdin.buffer.read(N * IQ * ds), dtype=dt)
else:
raw = np.frombuffer(fp.read(N * IQ * ds), dtype=dt)
if len(raw) < N * IQ:
return False
elif IQ == 1: # I
buff[ix:ix+N] = np.array(raw/div, dtype='complex64')
else: # IQ
buff[ix:ix+N] = np.array(raw[0::2]/div + raw[1::2]*1j*qsgn/div, dtype='complex64')
return True
PocketSDRはIQ(in-phase, quadrature)成分をそれぞれ2ビットで標本化する一方、bladeRFは12ビットで標本化します。しかし、bladeRFでの観測では、必ずしも全ビットが活かされてはいませんでした。
PocketSDR APは観測データをcomplex64
にて読み込みますので、ここで読み込んだ整数値をそのままbuff
に設定しても構いません。しかし、PocketSDR APユーザインターフェースにてきれいに表示するために、bladeRFの観測データを256で割ってbuff
に格納します。
そして、-sdr
オプションにてbladeRFをSDRとして選択できるよう、pocket_*.py
を修正します。
GPS L1 C/A信号の処理
このようにして得た観測ファイルに対して、電力スペクトル密度確認、信号捕捉、信号追尾の処理を行います。
はじめに、この電力スペクトル密度とヒストグラムを確認します。PocketSDRのsample
ディレクトリにて./pocket_psd.py -f 4 -IQ -h -sdr bladerf ch.bin
を実行します。
下記のPocketSDRでのスペクトルと比較すると、このスペクトルには明確な信号部分がありませんでした。
bladeRFにて観測した信号のIQ(in-phase, quadrature)成分ともにガウス形をしていて、良好に動作しているように見えます。しかし、このヒストグラムにおいては、信号があればゼロ成分が現れない方が望ましくて、PocketSDRのヒストグラムにもゼロ成分はありませんでした。Nuandのフォーラムでは、bladeRFのRX VGA2(reception variable gain amplifier 2)の利得を10 dBにまで低下させて、LNA(low noise amplifier)を付加することが議論されています。私の環境では、VGA2の利得を10 dBまで下げるとヒストグラムが直流のみになったので、このVGA2利得を最大値の28 dBに設定しました。衛星信号観測にbladeRFを用いるには、LNAの付加が必要なようです。
bladeRFでは、特に、直流オフセット補正が重要です。前述のcal lms
とcal dc rx
を実行しないと、下図のように、ヒストグラム上に顕著な直流オフセットが現れます。MacにbladeRFを接続して、データ取得を試みましたが、この直流オフセット補正コマンド実行時にSegmentation Faultが発生して、補正できませんでした。
次に、L1 C/A信号の信号捕捉を行います。GPS PRN(pseudo random noise)番号1から32を対象にして、pocket_acq.py
を実行します。
./pocket_acq.py -f 4 -prn 1-32 -sdr bladerf ch.bin
SIG= L1CA, PRN= 1, COFF= 0.92000 ms, DOP= 3115 Hz, C/N0= 33.8 dB-Hz
SIG= L1CA, PRN= 2, COFF= 0.43575 ms, DOP= 4107 Hz, C/N0= 46.2 dB-Hz
SIG= L1CA, PRN= 3, COFF= 0.87875 ms, DOP= -424 Hz, C/N0= 34.0 dB-Hz
SIG= L1CA, PRN= 4, COFF= 0.32325 ms, DOP= 2525 Hz, C/N0= 33.5 dB-Hz
SIG= L1CA, PRN= 5, COFF= 0.00100 ms, DOP= 5000 Hz, C/N0= 39.7 dB-Hz
SIG= L1CA, PRN= 6, COFF= 0.01025 ms, DOP= 1701 Hz, C/N0= 47.6 dB-Hz
SIG= L1CA, PRN= 7, COFF= 0.98725 ms, DOP= 4268 Hz, C/N0= 38.1 dB-Hz
SIG= L1CA, PRN= 8, COFF= 0.97525 ms, DOP= 4034 Hz, C/N0= 34.0 dB-Hz
SIG= L1CA, PRN= 9, COFF= 0.33500 ms, DOP= 330 Hz, C/N0= 41.3 dB-Hz
SIG= L1CA, PRN= 10, COFF= 0.61250 ms, DOP= 4454 Hz, C/N0= 33.4 dB-Hz
SIG= L1CA, PRN= 11, COFF= 0.42475 ms, DOP= 3940 Hz, C/N0= 46.4 dB-Hz
SIG= L1CA, PRN= 12, COFF= 0.68625 ms, DOP= 527 Hz, C/N0= 38.8 dB-Hz
SIG= L1CA, PRN= 13, COFF= 0.94275 ms, DOP= -5000 Hz, C/N0= 34.2 dB-Hz
SIG= L1CA, PRN= 14, COFF= 0.00600 ms, DOP= 3484 Hz, C/N0= 34.5 dB-Hz
SIG= L1CA, PRN= 15, COFF= 0.22575 ms, DOP= -4452 Hz, C/N0= 34.2 dB-Hz
SIG= L1CA, PRN= 16, COFF= 0.75900 ms, DOP= 1574 Hz, C/N0= 35.4 dB-Hz
SIG= L1CA, PRN= 17, COFF= 0.63100 ms, DOP= -1015 Hz, C/N0= 38.5 dB-Hz
SIG= L1CA, PRN= 18, COFF= 0.92475 ms, DOP= 1880 Hz, C/N0= 34.4 dB-Hz
SIG= L1CA, PRN= 19, COFF= 0.01675 ms, DOP= -933 Hz, C/N0= 42.7 dB-Hz
SIG= L1CA, PRN= 20, COFF= 0.55075 ms, DOP= 3894 Hz, C/N0= 44.1 dB-Hz
SIG= L1CA, PRN= 21, COFF= 0.98650 ms, DOP= 5000 Hz, C/N0= 34.2 dB-Hz
SIG= L1CA, PRN= 22, COFF= 0.31575 ms, DOP= 2504 Hz, C/N0= 34.6 dB-Hz
SIG= L1CA, PRN= 23, COFF= 0.18825 ms, DOP= 3006 Hz, C/N0= 33.8 dB-Hz
SIG= L1CA, PRN= 24, COFF= 0.77775 ms, DOP= 5000 Hz, C/N0= 34.2 dB-Hz
SIG= L1CA, PRN= 25, COFF= 0.47925 ms, DOP= 3984 Hz, C/N0= 33.8 dB-Hz
SIG= L1CA, PRN= 26, COFF= 0.04125 ms, DOP= -508 Hz, C/N0= 34.4 dB-Hz
SIG= L1CA, PRN= 27, COFF= 0.17050 ms, DOP= 2453 Hz, C/N0= 33.8 dB-Hz
SIG= L1CA, PRN= 28, COFF= 0.52325 ms, DOP= 496 Hz, C/N0= 34.1 dB-Hz
SIG= L1CA, PRN= 29, COFF= 0.13950 ms, DOP= 507 Hz, C/N0= 34.3 dB-Hz
SIG= L1CA, PRN= 30, COFF= 0.28775 ms, DOP= 3535 Hz, C/N0= 34.3 dB-Hz
SIG= L1CA, PRN= 31, COFF= 0.32300 ms, DOP= -1984 Hz, C/N0= 34.1 dB-Hz
SIG= L1CA, PRN= 32, COFF= 0.06225 ms, DOP= -1980 Hz, C/N0= 34.4 dB-Hz
TIME = 2.778 s
複数GPS衛星からの信号捕捉ができました。
次に、信号追尾を行うために、観測時間を30秒程度にまで延ばした別データch1.bin
を用意しました。しかし、ここで数秒から数十秒に1回の割合で、大きくヒストグラムが崩れる課題に気づきました。高須先生は、Ubuntu 20.04 LTSでは信号取りこぼしがあるので、Ubuntu 18.04 LTSを使用されています。
OS入れ替えは次の機会にして、ここではUbuntu 20.04 LTSを用います。./pocket_trk.py -f 4 -prn 32 -sdr bladerf ch1.bin -p
にて、L1 C/A信号を追尾します。
なんとか航法データを復号できました。
次に、この観測データch.bin
を用いてスナップショット測位を行います。この観測は、2022-03-04 14:31 UTC(世界協定時: coordinated universal time、日本時間では23:31)頃に行いました。ここで必要になる航法データ20220304o.navを以前と同様の手順で得ました。
$ ./pocket_snap.py -f 4 -nav 20220304o.nav ch.bin -ts 2022-03-04-14:31:00 -sdr bladerf
2022/03/04 14:31:00.000 -90.000000000 0.000000000 -6378137.000 5 0
TIME (s) = 4.938
残念ながら、失敗です。時刻を微調整しても、また、広島の大まかな座標を与えても、座標を得られませんでした。
みちびきL6D信号の抽出
みちびきL6帯信号の中心周波数は、1.17645 GHzです。ここでは、標本化周波数を12 MHzに、帯域幅を10 MHzにそれぞれ設定して、L6帯信号を標本化し、その結果を観測ファイルl6.bin
に保存します。bladeRF-cli -i
にて入力するコマンドは次の通りです。
set frequency rx 1.17645g
set bandwidth rx 10m
set samplerate rx 12m
set agc rx off
set gain rx 60
cal lms
cal dc rx
rx config format=bin n=100m file=l6.bin
rx start
rx wait
この電力スペクトル密度とヒストグラムを求めます。
./pocket_psd.py -f 12 -IQ -h -sdr bladerf l6.bin
次に、このL6D信号の捕捉を行います。
この結果からは、L6D信号を見つけられませんでした。
まとめ
GPSやみちびきの信号観測に汎用ソフトウェア無線機bladeRFを使用しました。その過程で、利得不足、信号取り込みビットが有効に活用されていない、直流オフセット補正が求められる、などの課題がありました。データ取りこぼしについては、データ収集パソコンのOSによるものかもしれません。
GPS L1 C/A信号の捕捉と追尾はでき、また航法データの一部を取得できましたが、測位には至りませんでした。
今後の追加実験として、データ収集パソコンのOSダウングレード、LNA追加とbladeRFのRX VGA2の利得調整、GPSDO(disciplined oscillator)による安定クロック供給、他SDRの利用、などを考えています。しかしながら、PocketSDRなどの測位専用SDRを使ってみたいです。
関連記事
- Pocket SDRすごい(リアルタイム測位機能) 13th October 2024
- Pocket SDRを用いたGalileo E6B信号の受信 27th January 2023
- リフローでやらかしました 19th January 2023
- Pocket SDRハードウェア製作(第3報) 30th September 2022
- Pocket SDRハードウェア製作(第2報) 14th September 2022
- Pocket SDRハードウェア製作(第1報) 4th September 2022
- PocketSDRすごい(パーツ購入編) 9th April 2022
- PocketSDR APにてbladeRFを使いたい(その2) 16th March 2022
- PocketSDRすごい(スナップショット測位編) 23rd February 2022
- PocketSDRすごい(FFTWによる高速化) 19th February 2022
- PocketSDRすごい(L6信号デコード編) 19th January 2022
- PocketSDRすごい(pocket_trk編) 28th December 2021
- PocketSDRすごい(pocket_acq編) 4th December 2021