PocketSDRすごい(L6信号デコード編)

category: gnss

はじめに

PocketSDRがアップデートされ、v.0.6になりました。みちびきL6帯信号の復号機能が追加されたので、今回は私のパソコンにてL6E(MADOCA)メッセージを読み出してみました。PocketSDRすごいです。

共有ライブラリ作成

このバージョンから低密度パリティ検査(LDPC: low density parity check)の共有ライブラリが必要になります。PocketSDRには、WindowsとLinuxに対する共有ライブラリが同梱されていますので、これらの環境ではこのステップは不要です。

macOSでは、自ら共有ライブラリを作成しなければなりませんが、PocketSDRのlib/ldpcフォルダにMakefileがありますので、容易にこのライブラリを作成できます。出力ファイルの拡張子を.soから.dylibにすることと、共有ライブラリ作成オプションを-sharedから-dynamiclibに変更するだけです。私の作成したmacOS向け共有ライブラリは次の通りです。ご自由にお使いください。

pocketsdr_maclib.zip
(2022-02-19更新: PocketSDR version 0.7に合わせて更新しました。)

このZIPファイルを解凍すると、フォルダdarwin_armdarwin_x86が現れます。前者がM1 Mac用で、後者がIntel Mac用です。これらのフォルダをPocketSDRのlibディレクトリにおいてください。

macOSのセキュリティ機能により、ダウンロードしたファイルをそのままでは実行できないかもしれません。Finderにてこれらの共有ライブラリファイルを「2本指クリック」して「開く」を実行して、実行許可を与えてください。

macOS共有ライブラリをダウンロードした後に一度は実行しなければならないかもしれません

次に、PocketSDRのPythonコードから、これらの共有ライブラリを扱えるようにします。その方法はPocketSDRすごい(pocket_trk編)と同様ですが、今回はplatformモジュールにより自動機種判定を行うようにしました。PocketSDRのpythonフォルダにある次の3ファイルの先頭部分を次のように書き換えてください。

sdr_fec.py

# load LIBFEC ([1]) ------------------------------------------------------------
dir = os.path.dirname(__file__)
import platform
environment = platform.platform()
if 'Windows' in environment:
    libfec = cdll.LoadLibrary(dir + '/../lib/win32/libfec.so')
elif 'Linux' in environment:
    libfec = cdll.LoadLibrary(dir + '/../lib/linux/libfec.so')
elif 'macOS' in environment and 'x86_64' in environment:
    libfec = cdll.LoadLibrary(dir + '/../lib/darwin_x86/libfec.dylib')
elif 'macOS' in environment and 'arm' in environment:
    libfec = cdll.LoadLibrary(dir + '/../lib/darwin_arm/libfec.dylib')

sdr_ldpc.py

# load library of LDPC-codes ([1],[2]) -----------------------------------------
dir = os.path.dirname(__file__)
import platform
environment = platform.platform()
if 'Windows' in environment:
    libldpc = cdll.LoadLibrary(dir + '/../lib/win32/libldpc.so')
elif 'Linux' in environment:
    libldpc = cdll.LoadLibrary(dir + '/../lib/linux/libldpc.so')
elif 'macOS' in environment and 'x86_64' in environment:
    libldpc = cdll.LoadLibrary(dir + '/../lib/darwin_x86/libldpc.dylib')
elif 'macOS' in environment and 'arm' in environment:
    libldpc = cdll.LoadLibrary(dir + '/../lib/darwin_arm/libldpc.dylib')

sdr_rtk.py

# load RTKLIB ([1]) ------------------------------------------------------------
dir = os.path.dirname(__file__)
import platform
environment = platform.platform()
if 'Windows' in environment:
    librtk = cdll.LoadLibrary(dir + '/../lib/win32/librtk.so')
elif 'Linux' in environment:
    librtk = cdll.LoadLibrary(dir + '/../lib/linux/librtk.so')
elif 'macOS' in environment and 'x86_64' in environment:
    librtk = cdll.LoadLibrary(dir + '/../lib/darwin_x86/librtk.dylib')
elif 'macOS' in environment and 'arm' in environment:
    librtk = cdll.LoadLibrary(dir + '/../lib/darwin_arm/librtk.dylib')

サンプルデータの復号

PocketSDRのsampleフォルダを開き、.link拡張子ファイルに書かれたURLから、ZIP形式サンプルデータを取得します。これらをunzipなどで解凍して、このフォルダに置きます。今回、使用するサンプルデータはL6_20211226_082212_12MHz_IQ.binです。

はじめに、このサンプルデータ中にて観測できるL6D信号(CLAS: centimeter level augmentation service)を確認します。pythonフォルダにあるpocket_rtk.pyを実行します。オプションの意味はpocket_acq.pyのものと同様です。

python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 193-199 -f 12 -sig L6D
  TIME(s)   SIG  PRN STATE   LOCK(s)  C/N0 (dB-Hz)         COFF(ms)  DOP(Hz)    ADR(cyc) SYNC #NAV #ERR #LOL NER
     0.02   L6D  193  IDLE      0.00   0.0                0.0000000      0.0         0.0  ---    0    0    0   0
     0.05   L6D  194  LOCK      0.03  43.2 ||||||||       3.8254181   -104.9        -3.7  ---    0    0    0   0
     0.05   L6D  195  LOCK      0.03  40.9 |||||||        0.7774230   -250.6        -8.0  ---    0    0    0   0
     0.02   L6D  196  IDLE      0.00   0.0                0.0000000      0.0         0.0  ---    0    0    0   0
     0.02   L6D  197  IDLE      0.00   0.0                0.0000000      0.0         0.0  ---    0    0    0   0
     0.02   L6D  198  IDLE      0.00   0.0                0.0000000      0.0         0.0  ---    0    0    0   0
     0.05   L6D  199  LOCK      0.03  40.9 |||||||        2.0844222   -153.7        -4.4  ---    0    0    0   0

みちびき2号機(PRN 194)、3号機(PRN 199)、4号機(PRN 195)のL6D信号が観測できました。積算デルタレンジ(ADR)も表示されていて、このL6D信号を測位に利用できることも期待できます。ここでは、みちびき2号機を対象に信号追尾します。

python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 194 -f 12 -sig L6D -p

Awesome PocketSDR - L6D signal decode

次に、L6E信号(MADOCA: multi-GNSS advanced demonstration tool for orbit and clock analysis)を復号します。みちびき2号機L6D信号のPRN番号に10を加えたPRN 204が、このL6E信号に相当します。

python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 204 -f 12 -sig L6E -p

L6D信号のものと同様に表示できることを確認しましたので、この復号データをログファイルpocket.logに保存します。

python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 204 -f 12 -sig L6E -log pocket.log

L6E信号解読

L6D信号やL6E信号のメッセージは、1秒間に1データパートと呼ばれるメッセージ単位が伝送されます。

ここで、L6E信号を対象にするのは、メッセージが1つのデータパート内に完結しているからです。L6D信号については、30秒に1度だけ放送されるサブタイプ1を解読できないと他のメッセージを読み出すことができず、またメッセージが複数データパートにまたがることもあるので、その解読のハードルが高いです。

このログファイルには、信号追尾状況と復号データがカンマ区切りで保存されています。このL6E信号メッセージは、先頭が$L6FRMではじまる行の6列目に16進テキスト形式で書かれています。これをバイナリ形式化するプログラムを作成しました。

pksdr2l6.py

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import sys

line = sys.stdin.readline().strip()
while (line):
    if line[0:6] != "$L6FRM":
        line = sys.stdin.readline().strip()
        continue
    t_data = line.split(',')[5]
    sys.stdout.buffer.write(bytes.fromhex(t_data))
    sys.stdout.flush()
    line = sys.stdin.readline().strip()

そして、L6メッセージからペイロードを抽出してRTCM 3(Radio Technical Commission for Maritime Services version 3)メッセージに変換するコードshowqzsl6.pyqzsl62rtcm.pyと、RTCM 3メッセージの内容を表示するコードshowrtcm.pyを用いて、このL6Eメッセージを読み出します。このshowqzsl6.pyshowrtcm.pyは、まだ未完成ではありますが、近日中に公開します公開しました。(2022-02-03公開し、コード名をshowqzsl6.pyからqzsl62rtcm.pyに変更しました)

はじめに、このL6Eメッセージを解読します。

cat pocket.log | ./pksdr2l6.py | qzsl62rtcm.py > /dev/null
204 Hitachi-Ota:0* 2021-12-26 08:21:58 1059(24)
204 Hitachi-Ota:0* 2021-12-26 08:22:00 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:00 1059(2)
204 Hitachi-Ota:0* 2021-12-26 08:22:02 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:02 1065(19)
204 Hitachi-Ota:0* 2021-12-26 08:22:04 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:04
204 Hitachi-Ota:0* 2021-12-26 08:22:06 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:06
204 Hitachi-Ota:0* 2021-12-26 08:22:08 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:08
204 Hitachi-Ota:0* 2021-12-26 08:22:10 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:10
204 Hitachi-Ota:0* 2021-12-26 08:22:12 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:12 1057(8) 1061(8)
204 Hitachi-Ota:0* 2021-12-26 08:22:14 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:14 1057(8) 1061(8)
204 Hitachi-Ota:0* 2021-12-26 08:22:16 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:16 1057(8) 1061(8)
204 Hitachi-Ota:0* 2021-12-26 08:22:18 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:18
204 Hitachi-Ota:0* 2021-12-26 08:22:20 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:20 1063(8) 1067(8)
204 Hitachi-Ota:0* 2021-12-26 08:22:22 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:22 1063(8) 1067(8)
204 Hitachi-Ota:0* 2021-12-26 08:22:24 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:24 1063(1) 1067(1)
204 Hitachi-Ota:0* 2021-12-26 08:22:26 1062(24) 1068(17)
204 Hitachi-Ota:0* 2021-12-26 08:22:26

これは、1行に1データパートの内容を示しています。それぞれの行には、PRN番号、管制局(常陸太田または神戸)と系統番号(0または1)、アラートフラグ状態(アスタリスクはアラートオン状態です)、UTC日時、RTCMメッセージ番号と補強対象衛星数が含まれます。ひとつのデータパートに、2つのRTCMメッセージを含むこともあれば、メッセージを含まないこともありました。次に、このRTCM 3メッセージを解読します。

cat pocket.log | ./pksdr2l6.py | qzsl62rtcm.py 2> /dev/null | showrtcm.py

実行結果の一部は次の通りです。この30秒間に観測できたすべてのRTCM 3メッセージをpocket.rtcm3.txtに置きます。

RTCM 1059 G SSR code bias  G01 G02 G03 G05 G06 G07 G08 G09 G10 G12 G13 G15 G16 G17 G19 G20 G21 G22 G24 G25 G26 G27 G29 G30 (nsat=24 iod=4 cont.)
RTCM 1062 G SSR hr clock   G01 G02 G03 G05 G06 G07 G08 G09 G10 G12 G13 G15 G16 G17 G19 G20 G21 G24 G25 G26 G29 G30 G31 G32 (nsat=24 iod=4)
RTCM 1068 R SSR hr clock   R01 R02 R03 R04 R05 R07 R08 R12 R13 R14 R15 R17 R18 R19 R20 R21 R22 (nsat=17 iod=5)
RTCM 1059 G SSR code bias  G31 G32 (nsat=2 iod=4)
RTCM 1062 G SSR hr clock   G01 G02 G03 G05 G06 G07 G08 G09 G10 G12 G13 G15 G16 G17 G19 G20 G21 G24 G25 G26 G29 G30 G31 G32 (nsat=24 iod=4)
RTCM 1068 R SSR hr clock   R01 R02 R03 R04 R05 R07 R08 R12 R13 R14 R15 R17 R18 R19 R20 R21 R22 (nsat=17 iod=5)
RTCM 1065 R SSR code bias  R01 R02 R03 R04 R05 R07 R08 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 (nsat=19 iod=5)

無事、RTCM形式の補強情報が復号できました!

MADOCAでは、G(GPS)、R(Glonass)、J(みちびき、ただし初号機のみ)の3衛星システムが補強対象です。SSR(space state representation)空間情報表現とは、誤差を要因別にわけて、それぞれの補正情報を伝送する方法です。MADOCAでは、衛星内部時計と衛星軌道を補強します。そのメッセージのひとつである、High rate clock(高速時刻補正)が頻繁に伝送されていることがわかります。L6E信号のMADOCAでは、メッセージをデータパート内に納めるため、異なるデータパートに同じメッセージ番号で続き(continue)を伝送することがあります。例えば、この1行目内容の続きが4行目内容です。nsatは補強対象衛星数を、iod(issue of data)とはSSRメッセージ発行番号を、それぞれ表します。

このRTCMメッセージに、「時刻補正情報」を付加すれば、MADOCAによる補強ができる見込みです。

昨年11月頃から、L6E信号でのみちびきの補強情報が来なくなりました。みちびき初号機の状態がよくないので、補強情報も来なくなったのだと推測しています。これはAllystarの受信モジュールを用いた受信でも確認しています。はやくみちびき初号機が元気になることを望んでいます。

PocketSDRの誤り訂正後メッセージ解読画面の行頭に、16進数でのL6信号ヘッダ1a cf fc 1dがありますので、正しく復号されていることは明らかですが、ここではそのメッセージを読み出してみました。

少し前まで、L6信号メッセージを抽出できる受信機は、例えばJavad DELTA-G3TにみちびきオプションとL6オプションをつけた高性能受信機しかなく、大変高価でした。これはとてもすごいことです。

おわりに

PocketSDRのコードがアップグレードされ、L6信号をも復号できるようになりました。サンプルデータから、RTCMメッセージと補強対象衛星まで確認できました。とても楽しかったです。

PocketSDRのコードを読みましたが、準同期検波から同期を引き込む方法として、一般的に用いられるコスタスループを使用していないようです。PocketSDRは、動作とコードの両方が楽しめ、1粒で2度美味しいグリコのようです。


関連記事