Awesome PocketSDR (L6 band signal decode)
Introduction
PocketSDR has been updated to v.0.6. Since the decoding function of the quasi-zenith satellite L6 band signal was added, I tried to decode the L6E (MADOCA) message on my personal computer. PocketSDR is amazing.
Create a shared library
From this version, a shared library of low density parity check (LDPC) is required. PocketSDR ships with shared libraries for Windows and Linux, so this step is not necessary in Windows and Linux environments.
On macOS, you have to create the shared library yourself, but you can easily create this library because there is a Makefile in the lib/ldpc
directory of PocketSDR. Simply change the output file extension from .so
to .dylib
and change the shared library creation option from -shared
to -dynamiclib
. The shared library for macOS that I created is as follows. Feel free to use it.
pocketsdr_maclib.zip
(updated on 2022-02-19 for PocketSDR version 0.7)
Unzip this ZIP file and you will see the folders darwin_arm
and darwin_x86
. The former is for M1 Mac and the latter is for Intel Mac. Place these folders in the PocketSDR lib
directory.
Due to the security features of your macOS, you may not be able to run the downloaded file as is. In the Finder, “click with two fingers” and execute “Open” to give permission to execute these shared library files.
Next, we make PocketSDR’s Python code work with these shared libraries. The method is the same as Awesome PocketSDR (pocket_trk), but the platform
module is used to automatically determine the OS. Please rewrite the beginning of the following 3 files as follows.
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')
Decoding sample data
Open the sample
folder of PocketSDR and get the ZIP format sample data from the URL written in the .link
extension file. Unzip these files by using unzip
command and put them in this folder. The sample data used this time is L6_20211226_082212_12MHz_IQ.bin
.
First, I checked the L6D signal (CLAS: centimeter level augmentation service) that can be observed in this sample data. I executed pocket_rtk.py
in the python
folder. The meaning of the options is similar to that of 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
The L6D signals of QZS-2 (PRN 194), QZS-3 (PRN 199), and QZS-4 (PRN 195) could be observed. The accumulated delta range (ADR) is also shown, and we can expected to use this L6D signal for positioning. Here, signal tracking is performed for the QZS-2.
python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 194 -f 12 -sig L6D -p
Next, I decode the L6E signal (MADOCA: multi-GNSS advanced demonstration tool for orbit and clock analysis). The PRN 204, which is the PRN number of QZS-2 L6D signal plus 10, corresponds to this L6E signal.
python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 204 -f 12 -sig L6E -p
Save this decoded data in the log file pocket.log
.
python3 pocket_trk.py ../sample/L6_20211226_082212_12MHz_IQ.bin -prn 204 -f 12 -sig L6E -log pocket.log
L6E signal decoding
L6D signal and L6E signal messages are transmitted in a message called data part, and it is sent a single message per second.
The target here is the L6E signal, because the message is complete within one data part. For L6D signals, if subtype 1 that is broadcast only once every 30 seconds cannot be decoded, other messages cannot be decoded, and the message may span multiple data parts, so it is hard for me to decode the L6D signals.
This log file stores the signal tracking status and decryption data separated by commas. This L6E signal message is written in hexadecimal text format in column 6 of the line beginning with $L6FRM
. I created a Python program to convert this log message into the L6E binary format.
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()
Then, we read this L6E message with the code qzsl62rtcm.py
that extracts the payload from the L6 message and converts it into an RTCM 3 (Radio Technical Commission for Maritime Services version 3) message and the code showrtcm.py
that displays the contents of the RTCM 3 message.
First, we decode this L6E message.
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
It shows the contents of one data part per line. Each line contains the PRN number, control station (Hitachi-ota or Kobe) and system number (0 or 1), alert flag status (asterisk shows alert on status), UTC date and time, RTCM message number and number of satellites to be augmented. One data part sometimes contains two RTCM messages. Then we decode this RTCM 3 message.
cat pocket.log | ./pksdr2l6.py | qzsl62rtcm.py 2> /dev/null | showrtcm.py
Some of the execution results are as follows. I placed all RTCM 3 messages that you can observe in the last 30 seconds in 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)
I have successfully decoded the RTCM format augmentation information!
MADOCA will augment three satellite systems: G
(GPS), R
(Glonass), and J
(QZS-1). SSR (space state representation) is a method of transmitting correction information by dividing the error into factors. MADOCA augments the satellite internal clock and satellite orbit. We can see the high rate clock correction message frequently. MADOCA in L6E signals places a message in a data part, so it may transmit a continue to different data parts with the same message number. For example, the continuation of the contents of the first line is the contents of the fourth line. nsat
represents the number of satellites to be augmented, and iod
(issue of data) represents the SSR message issue number.
If we add “clock correction information” to this RTCM message, we can use the MADOCA augmentation.
There is an L6 signal header 1a
cf
fc
1d
in hexadecimal at the beginning of the line of the message decoding screen after error correction of PocketSDR, it is obvious that the messages were correctly decoded.
Not long ago, the only receivers that could extract L6 signal messages were, for example, Javad DELTA-G3T with the quasi-zenith satellite option and the L6 signal reception option, which was very expensive. This is amazing.
Conclusion
The PocketSDR code has been upgraded to be able to decode L6 signals. From the sample data, I confirmed the RTCM messages.
Related article(s):
- Awesome Pocket SDR (realtime positioning function) 13th October 2024
- Galileo E6B signal reception with Pocket SDR, a open source software-defined radio 27th January 2023
- Failure in reflow soldering 19th January 2023
- Pocket SDR hardware production (part 3) 30th September 2022
- Pocket SDR hardware production (part 2) 14th September 2022
- Pocket SDR hardware production (part 1) 4th September 2022
- Awesome PocketSDR (order of hardware parts) 9th April 2022
- I want to use bladeRF with PocketSDR AP, part 2 16th March 2022
- I want to use bladeRF with PocketSDR AP 5th March 2022
- Awesome PocketSDR (snapshot positioning) 23rd February 2022
- Awesome PocketSDR (reducing processing time with FFTW) 19th February 2022
- Awesome PocketSDR (pocket_trk) 28th December 2021
- Awesome PocketSDR(pocket_acq) 4th December 2021