1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-12-04 15:30:01 +00:00

Merge branch 'osnma-cesare' of https://github.com/cesaaargm/osnma into cesaaargm-osnma-cesare

This commit is contained in:
Carles Fernandez 2024-09-25 18:21:19 +02:00
commit 67cc301408
No known key found for this signature in database
GPG Key ID: 4C583C52B0C3877D
47 changed files with 8294 additions and 224 deletions

View File

@ -34,7 +34,9 @@ jobs:
- name: check
run: cd build && ninja check && ../install/volk_gnsssdr_profile && ../install/run_tests
- name: default position_test
run: cd build && cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON .. && ninja && ../install/position_test
run: |
cd build && cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON -DENABLE_UNIT_TESTING_EXTRA=OFF .. && \
ninja && ../install/position_test && ../install/run_tests --gtest_filter=Osnma*
build-macos:
runs-on: macos-latest
@ -66,7 +68,9 @@ jobs:
- name: check
run: cd build && ninja check && ../install/volk_gnsssdr_profile && ../install/run_tests
- name: default position_test
run: cd build && cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON .. && ninja && ../install/position_test
run: |
cd build && cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON -DENABLE_UNIT_TESTING_EXTRA=OFF .. && \
ninja && ../install/position_test && ../install/run_tests --gtest_filter=Osnma*
build-macos-xcode:
runs-on: macos-latest
@ -104,9 +108,10 @@ jobs:
- name: default position_test
run: |
cd build
cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON ..
cmake -DENABLE_SYSTEM_TESTING_EXTRA=ON -DENABLE_UNIT_TESTING_EXTRA=OFF ..
xcodebuild -configuration Release -target position_test
../install/position_test
../install/run_tests --gtest_filter=Osnma*
clang-format:
runs-on: ubuntu-latest

73
AUTHORS
View File

@ -33,41 +33,44 @@ Contact Information
List of authors
--------------------------------------------------------------------------------
Carles Fernández-Prades carles.fernandez@cttc.cat Project manager
Javier Arribas javier.arribas@cttc.es Developer
Luis Esteve Elfau luis@epsilon-formacion.com Developer
Marc Majoral marc.majoral@cttc.cat Developer
Xavier Guerrero xavier.guerrero@cttc.es Developer
Jordi Vilà-Valls jordi.vila-valls@isae-supaero.fr Consultant
Pau Closas pau.closas@northeastern.edu Consultant
Álvaro Cebrián Juan acebrianjuan@gmail.com Contributor
Andres Cecilia Luque a.cecilia.luque@gmail.com Contributor
Anthony Arnold anthony.arnold@uqconnect.edu.au Contributor
Antonio Ramos antonio.ramosdet@gmail.com Contributor
Carlos Avilés carlos.avilesr@googlemail.com Contributor
Cillian O'Driscoll cillian.odriscoll@gmail.com Contributor
Damian Miralles dmiralles2009@gmail.com Contributor
Daniel Fehr daniel.co@bluewin.ch Contributor
David Pubill david.pubill@cttc.cat Contributor
En Shin seanstone5923@gmail.com Contributor
Fran Fabra fabra@ice.csic.es Contributor
Gabriel Araujo gabriel.araujo.5000@gmail.com Contributor
Gerald LaMountain gerald@gece.neu.edu Contributor
Into Pääkkönen into.paakkonen@aalto.fi Contributor
Irene Pérez Riega iperrie@inta.es Contributor
Jim Melton jim.melton@sncorp.com Contributor
Josh Schindehette jschindehette@geontech.com Contributor
Leonardo Tonetto tonetto.dev@gmail.com Contributor
Malte Lenhart malte.lenhart@mailbox.org Contributor
Mara Branzanti mara.branzanti@gmail.com Contributor
Marc Molina marc.molina.pena@gmail.com Contributor
Marc Sales marcsales92@gmail.com Contributor
Piyush Gupta piyush04111999@gmail.com Contributor
Rodrigo Muñoz rodrigo.munoz@proteinlab.cl Contributor
Stefan van der Linden spvdlinden@gmail.com Contributor
Víctor Castillo-Agüero victorcastilloaguero@gmail.com Contributor
Will Silberman wsilberm@google.com Contributor
Carlos Paniego carpanie@hotmail.com Artwork
Carles Fernández-Prades carles.fernandez@cttc.cat Project manager
Javier Arribas javier.arribas@cttc.es Developer
Luis Esteve Elfau luis@epsilon-formacion.com Developer
Marc Majoral marc.majoral@cttc.cat Developer
Xavier Guerrero xavier.guerrero@cttc.es Developer
Jordi Vilà-Valls jordi.vila-valls@isae-supaero.fr Consultant
Pau Closas pau.closas@northeastern.edu Consultant
Álvaro Cebrián Juan acebrianjuan@gmail.com Contributor
Andres Cecilia Luque a.cecilia.luque@gmail.com Contributor
Anthony Arnold anthony.arnold@uqconnect.edu.au Contributor
Antonio Ramos antonio.ramosdet@gmail.com Contributor
Carlos Avilés carlos.avilesr@googlemail.com Contributor
Cesare Ghionoiu Martinez c.ghionoiu-martinez@tu-braunschweig.de Contributor
Cillian O'Driscoll cillian.odriscoll@gmail.com Contributor
Damian Miralles dmiralles2009@gmail.com Contributor
Daniel Fehr daniel.co@bluewin.ch Contributor
David Pubill david.pubill@cttc.cat Contributor
En Shin seanstone5923@gmail.com Contributor
Fran Fabra fabra@ice.csic.es Contributor
Gabriel Araujo gabriel.araujo.5000@gmail.com Contributor
Gerald LaMountain gerald@gece.neu.edu Contributor
Into Pääkkönen into.paakkonen@aalto.fi Contributor
Irene Pérez Riega iperrie@inta.es Contributor
Jim Melton jim.melton@sncorp.com Contributor
Josh Schindehette jschindehette@geontech.com Contributor
Leonardo Tonetto tonetto.dev@gmail.com Contributor
Malte Lenhart malte.lenhart@mailbox.org Contributor
Mara Branzanti mara.branzanti@gmail.com Contributor
Marc Molina marc.molina.pena@gmail.com Contributor
Marc Sales marcsales92@gmail.com Contributor
Piyush Gupta piyush04111999@gmail.com Contributor
Rodrigo Muñoz rodrigo.munoz@proteinlab.cl Contributor
Stefan van der Linden spvdlinden@gmail.com Contributor
Víctor Castillo-Agüero victorcastilloaguero@gmail.com Contributor
Will Silberman wsilberm@google.com Contributor
Carlos Paniego carpanie@hotmail.com Artwork
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2011-2024 Carles Fernandez-Prades <carles.fernandez@cttc.es>

View File

@ -61,6 +61,11 @@ authors:
- email: daniel.co@bluewin.ch
family-names: Fehr
given-names: Daniel
- alias: cesaaargm
affiliation: "Technische Universität Braunschweig"
email: c.ghionoiu-martinez@tu-braunschweig.de
family-names: "Ghionoiu Martinez"
given-names: Cesare
- alias: piyush0411
email: piyush04111999@gmail.com
family-names: Gupta

View File

@ -102,6 +102,8 @@ option(ENABLE_STRIP "Create stripped binaries without debugging symbols (in Rele
option(Boost_USE_STATIC_LIBS "Use Boost static libs" OFF)
option(ENABLE_GNUTLS "Forces linking against GnuTLS" OFF)
if(ENABLE_PACKAGING)
set(ENABLE_ARMA_NO_DEBUG ON)
set(CMAKE_VERBOSE_MAKEFILE ON)
@ -2217,97 +2219,9 @@ endif()
################################################################################
# GnuTLS - https://www.gnutls.org/
# OpenSSL https://www.openssl.org/ or GnuTLS - https://www.gnutls.org/
################################################################################
find_package(GnuTLS)
set_package_properties(GnuTLS PROPERTIES
URL "https://www.gnutls.org/"
PURPOSE "Used for the SUPL protocol implementation."
TYPE REQUIRED
)
if(GnuTLS_FOUND AND GNUTLS_VERSION_STRING)
set_package_properties(GnuTLS PROPERTIES
DESCRIPTION "Transport Layer Security Library (found: v${GNUTLS_VERSION_STRING})"
)
else()
set_package_properties(GnuTLS PROPERTIES
DESCRIPTION "Transport Layer Security Library"
)
endif()
find_library(GNUTLS_OPENSSL_LIBRARY
NAMES gnutls-openssl libgnutls-openssl.so.27
PATHS
/usr/lib
/usr/lib64
/usr/lib/x86_64-linux-gnu
/usr/lib/aarch64-linux-gnu
/usr/lib/arm-linux-gnueabihf
/usr/lib/arm-linux-gnueabi
/usr/lib/i386-linux-gnu
/usr/lib/alpha-linux-gnu
/usr/lib/hppa-linux-gnu
/usr/lib/i386-gnu
/usr/lib/i686-gnu
/usr/lib/i686-linux-gnu
/usr/lib/x86_64-kfreebsd-gnu
/usr/lib/i686-kfreebsd-gnu
/usr/lib/m68k-linux-gnu
/usr/lib/mips-linux-gnu
/usr/lib/mips64el-linux-gnuabi64
/usr/lib/mipsel-linux-gnu
/usr/lib/powerpc-linux-gnu
/usr/lib/powerpc-linux-gnuspe
/usr/lib/powerpc64-linux-gnu
/usr/lib/powerpc64le-linux-gnu
/usr/lib/s390x-linux-gnu
/usr/lib/riscv64-linux-gnu
/usr/lib/sparc64-linux-gnu
/usr/lib/x86_64-linux-gnux32
/usr/lib/sh4-linux-gnu
/usr/lib/loongarch64-linux-gnu
/usr/local/lib
/usr/local/lib64
/opt/local/lib
)
if(NOT GNUTLS_OPENSSL_LIBRARY)
if(GnuTLS_FOUND)
message(STATUS " But it was not built with openssl compatibility.")
endif()
message(STATUS " Looking for OpenSSL instead...")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) # Trick for Homebrew
endif()
find_package(OpenSSL)
set_package_properties(OpenSSL PROPERTIES
URL "https://www.openssl.org"
DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})"
PURPOSE "Used for the SUPL protocol implementation."
TYPE REQUIRED
)
if(OPENSSL_FOUND)
set_package_properties(GnuTLS PROPERTIES
PURPOSE "Not found, but OpenSSL can replace it."
)
set(GNUTLS_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
set(GNUTLS_LIBRARIES "")
set(GNUTLS_OPENSSL_LIBRARY ${OPENSSL_SSL_LIBRARY})
else()
message(" The GnuTLS library with openssl compatibility enabled has not been found.")
message(" You can try to install the required libraries by typing:")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|kFreeBSD|GNU")
if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat")
message(" sudo yum install openssl-devel")
else()
message(" sudo apt-get install libgnutls28-dev")
endif()
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(" 'sudo port install gnutls', if you are using Macports, or")
message(" 'brew install openssl', if you are using Homebrew.")
endif()
message(FATAL_ERROR "GnuTLS libraries with openssl compatibility are required to build gnss-sdr")
endif()
endif()
include(GnsssdrCrypto)
@ -3496,6 +3410,112 @@ endif()
#####################################################################
# Check signal sources related to FPGA only.
#####################################################################
if(ENABLE_MAX2771 AND NOT ENABLE_FPGA)
message(STATUS "The SPIdev driver is enabled, but the FPGA is not enabled. The FPGA is required when using the SPIdev driver.")
if(ENABLE_PACKAGING)
set(ENABLE_MAX2771 OFF)
else()
message(FATAL_ERROR "ENABLE_MAX2771 can only be set when ENABLE_FPGA is also set.")
endif()
endif()
if(ENABLE_DMA_PROXY AND NOT ENABLE_FPGA)
message(STATUS "The DMA Proxy driver is enabled, but the FPGA is not enabled. The FPGA is required when using the DMA Proxy driver.")
if(ENABLE_PACKAGING)
set(ENABLE_DMA_PROXY OFF)
else()
message(FATAL_ERROR "ENABLE_DMA_PROXY can only be set when ENABLE_FPGA is also set.")
endif()
endif()
#####################################################################
# spidev driver - OPTIONAL
# Linux kernel driver that provides user-space access to Serial
# Peripheral Interface)
#####################################################################
if(ENABLE_MAX2771)
if(DEFINED ENV{SDKTARGETSYSROOT})
set(TARGET_ROOTFS_PATH $ENV{SDKTARGETSYSROOT})
else()
set(TARGET_ROOTFS_PATH "")
endif()
find_program(STRINGS_EXECUTABLE strings)
if(NOT STRINGS_EXECUTABLE)
message(STATUS "The 'strings' command could not be found. See https://www.gnu.org/software/binutils/")
message(STATUS " You can try to install it by typing:")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|kFreeBSD|GNU")
if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat")
message(STATUS " sudo yum install binutils")
elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE")
message(STATUS " sudo zypper install binutils")
else()
message(STATUS " sudo apt-get install binutils")
endif()
endif()
message(FATAL_ERROR "Binutils are required to build GNSS-SDR for SoC FPGA devices using the MAX2771 option.")
endif()
set(DTB_FILE "${TARGET_ROOTFS_PATH}/boot/devicetree/system-top.dtb")
if(EXISTS "${DTB_FILE}")
message(STATUS "Found DTB file: ${DTB_FILE}")
# Run the strings command and grep for "spidev"
execute_process(
COMMAND ${STRINGS_EXECUTABLE} ${DTB_FILE}
COMMAND grep "spidev"
OUTPUT_VARIABLE GREP_OUTPUT
RESULT_VARIABLE GREP_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(GREP_RESULT EQUAL 0)
message(STATUS "Found spidev-compatible peripheral in ${DTB_FILE}.")
else()
message(STATUS "SPIdev driver not found, its installation is required.")
if(ENABLE_PACKAGING)
set(ENABLE_MAX2771 OFF)
else()
message(FATAL_ERROR "SPIdev driver is required for building gnss-sdr with -DENABLE_MAX2271=ON.")
endif()
endif()
else()
message(FATAL_ERROR "The device tree (DTB) file ${DTB_FILE} cannot be found.")
endif()
endif()
#####################################################################
# DMA Proxy driver - OPTIONAL
# Simplified and efficient interface for user-space applications
# to leverage DMA capabilities for Xilinx FPGA and SoC systems
#####################################################################
if(ENABLE_DMA_PROXY)
if(DEFINED ENV{SDKTARGETSYSROOT})
set(TARGET_ROOTFS_PATH $ENV{SDKTARGETSYSROOT})
else()
string(REGEX MATCH "(.*/tmp-glibc)" MATCHED_PATH "${GNURADIO_RUNTIME_INCLUDE_DIRS}")
if(MATCHED_PATH)
set(TARGET_ROOTFS_PATH "${MATCHED_PATH}/sysroots-components")
else()
set(TARGET_ROOTFS_PATH "")
endif()
endif()
file(GLOB_RECURSE DMA_PROXY_FILE "${TARGET_ROOTFS_PATH}/*/dma-proxy.ko")
if(EXISTS "${DMA_PROXY_FILE}")
message(STATUS "Found dma-proxy.ko file: ${DMA_PROXY_FILE}")
else()
if(ENABLE_PACKAGING)
set(ENABLE_DMA_PROXY OFF)
else()
message(FATAL_ERROR "DMA Proxy driver is required for building gnss-sdr with -DENABLE_DMA_PROXY=ON.")
endif()
endif()
endif()
##############################################
# TELEORBIT FLEXIBAND FRONTEND - OPTIONAL
##############################################
@ -3721,6 +3741,7 @@ add_feature_info(ENABLE_OWN_GLOG ENABLE_OWN_GLOG "Forces the downloading and bui
add_feature_info(ENABLE_GLOG_AND_GFLAGS ENABLE_GLOG_AND_GFLAGS "Forces the usage of Google glog and Gflags instead of Abseil.")
add_feature_info(ENABLE_OWN_ABSEIL ENABLE_OWN_ABSEIL "Forces downloading and building Abseil. Supersedes ENABLE_OWN_GLOG.")
add_feature_info(ENABLE_OWN_ARMADILLO ENABLE_OWN_ARMADILLO "Forces the downloading and building of Armadillo.")
add_feature_info(ENABLE_GNUTLS ENABLE_GNUTLS "Forces linking against GnuTLS instead of OpenSSL.")
add_feature_info(ENABLE_LOG ENABLE_LOG "Enables runtime internal logging.")
add_feature_info(ENABLE_ORC ENABLE_ORC "Use the Optimized Inner Loop Runtime Compiler (ORC) for building volk_gnsssdr.")
add_feature_info(ENABLE_STRIP ENABLE_STRIP "Enables the generation of stripped binaries (without debugging symbols).")

View File

@ -74,7 +74,7 @@ information about this open-source, software-defined GNSS receiver.
- [Install Armadillo, a C++ linear algebra library](#install-armadillo-a-c-linear-algebra-library)
- [Install Gflags, a commandline flags processing module for C++](#install-gflags-a-commandline-flags-processing-module-for-c)
- [Install Glog, a library that implements application-level logging](#install-glog-a-library-that-implements-application-level-logging)
- [Install the GnuTLS or OpenSSL libraries](#install-the-gnutls-or-openssl-libraries)
- [Install the OpenSSL libraries](#install-the-openssl-libraries)
- [Install Matio, MATLAB MAT file I/O library](#install-matio-matlab-mat-file-io-library)
- [Install Protocol Buffers, a portable mechanism for serialization of structured data](#install-protocol-buffers-a-portable-mechanism-for-serialization-of-structured-data)
- [Install Pugixml, a light-weight C++ XML processing library](#install-pugixml-a-light-weight-c-xml-processing-library)
@ -167,16 +167,19 @@ $ sudo apt-get install build-essential cmake git pkg-config libboost-dev libboos
libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-chrono-dev \
libboost-serialization-dev liblog4cpp5-dev libuhd-dev gnuradio-dev gr-osmosdr \
libblas-dev liblapack-dev libarmadillo-dev libgflags-dev libgoogle-glog-dev \
libgnutls-openssl-dev libpcap-dev libmatio-dev libpugixml-dev libgtest-dev \
libprotobuf-dev protobuf-compiler python3-mako
libssl-dev libpcap-dev libmatio-dev libpugixml-dev libgtest-dev \
libprotobuf-dev libcpu-features-dev protobuf-compiler python3-mako
```
Please note that the required files from `libgtest-dev` were named `googletest`
in Debian 9 "stretch" and Ubuntu 18.04 "bionic", and renamed to `libgtest-dev`
in Debian 10 "buster" and above.
Since Ubuntu 21.04 Hirsute / Debian 11, the package `libcpu-features-dev` is
also required.
In distributions older than Ubuntu 21.04 Hirsute / Debian 11, the package
`libcpu-features-dev` is not required.
In distributions older than Ubuntu 22.04 Jammy / Debian 12, the package
`libssl-dev` must be replaced by `libgnutls-openssl-dev`.
**Note for Ubuntu 14.04 LTS "trusty" users:** you will need to build from source
and install GNU Radio manually, as explained below, since GNSS-SDR requires
@ -431,20 +434,15 @@ Please note that Glog is replaced by the
[Abseil Logging Library](https://abseil.io/docs/cpp/guides/logging) if Abseil >=
v20240116 is available in your system.
#### Install the GnuTLS or OpenSSL libraries
#### Install the OpenSSL libraries
```
$ sudo apt-get install libgnutls-openssl-dev # For Debian/Ubuntu/LinuxMint
$ sudo yum install openssl-devel # For Fedora/RHEL
$ sudo zypper install openssl-devel # For OpenSUSE
$ sudo pacman -S openssl # For Arch Linux
$ sudo apt-get install libssl-dev # For Debian/Ubuntu/LinuxMint
$ sudo yum install openssl-devel # For Fedora/CentOS/RHEL
$ sudo zypper install openssl-devel # For OpenSUSE
$ sudo pacman -S openssl # For Arch Linux
```
In case the [GnuTLS](https://www.gnutls.org/ "GnuTLS's Homepage") library with
openssl extensions package is not available in your GNU/Linux distribution,
GNSS-SDR can also work well with
[OpenSSL](https://www.openssl.org/ "OpenSSL's Homepage").
#### Install [Matio](https://github.com/tbeu/matio "Matio's Homepage"), MATLAB MAT file I/O library
```
@ -818,7 +816,7 @@ In a terminal, type:
```
$ sudo port selfupdate
$ sudo port upgrade outdated
$ sudo port install armadillo cmake pkgconfig protobuf3-cpp pugixml gnutls
$ sudo port install armadillo cmake pkgconfig protobuf3-cpp pugixml openssl3
$ sudo port install gnuradio +uhd +grc +zeromq
$ sudo port install boost matio libad9361-iio libiio
$ sudo port install py311-mako

View File

@ -0,0 +1,79 @@
# GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
# This file is part of GNSS-SDR.
#
# SPDX-FileCopyrightText: 2024 C. Fernandez-Prades cfernandez(at)cttc.es
# SPDX-License-Identifier: BSD-3-Clause
if(NOT COMMAND feature_summary)
include(FeatureSummary)
endif()
if(NOT GNSSSDR_LIB_PATHS)
include(GnsssdrLibPaths)
endif()
if(NOT PKG_CONFIG_FOUND)
include(FindPkgConfig)
endif()
pkg_check_modules(PC_GMP "gmp")
set(GMP_DEFINITIONS ${PC_GMP_CFLAGS_OTHER})
find_path(GMP_INCLUDE_DIR
NAMES gmpxx.h
HINTS ${PC_GMP_INCLUDEDIR}
PATHS ${CMAKE_INSTALL_PREFIX}/include
/usr/local/include
/usr/include
/opt/local/include
)
set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR})
set(GMP_PC_ADD_CFLAGS "-I${GMP_INCLUDE_DIR}")
find_library(GMPXX_LIBRARY
NAMES gmpxx
HINTS ${PC_GMP_LIBDIR}
PATHS ${GNSSSDR_LIB_PATHS}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
)
find_library(GMP_LIBRARY
NAMES gmp
HINTS ${PC_GMP_LIBDIR}
PATHS ${GNSSSDR_LIB_PATHS}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
)
set(GMP_LIBRARIES ${GMPXX_LIBRARY} ${GMP_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG GMPXX_LIBRARY GMP_LIBRARY GMP_INCLUDE_DIR)
if(GMP_FOUND AND NOT TARGET Gmp::gmp)
add_library(Gmp::gmp SHARED IMPORTED)
set_target_properties(Gmp::gmp PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${GMPXX_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${GMP_LIBRARIES}"
)
endif()
set_package_properties(GMP PROPERTIES
URL "https://gmplib.org/"
)
if(PC_GMP_VERSION)
set_package_properties(GMP PROPERTIES
DESCRIPTION "The GNU Multiple Precision Arithmetic Library (found: v.${PC_GMP_VERSION})"
)
else()
set_package_properties(GMP PROPERTIES
DESCRIPTION "The GNU Multiple Precision Arithmetic Library"
)
endif()
mark_as_advanced(GMPXX_LIBRARY GMP_LIBRARY GMP_INCLUDE_DIR)

View File

@ -366,8 +366,10 @@ if(GNURADIO_RUNTIME_INCLUDE_DIRS)
)
if(CMAKE_VERSION VERSION_GREATER 3.13)
target_link_libraries(Gnuradio::filter INTERFACE Log4cpp::log4cpp)
target_link_libraries(Gnuradio::runtime INTERFACE Log4cpp::log4cpp)
else()
set_target_properties(Gnuradio::filter PROPERTIES INTERFACE_LINK_LIBRARIES Log4cpp::log4cpp)
set_target_properties(Gnuradio::runtime PROPERTIES INTERFACE_LINK_LIBRARIES Log4cpp::log4cpp)
endif()
endif()
if(${_uses_spdlog})

View File

@ -0,0 +1,191 @@
# GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
# This file is part of GNSS-SDR.
#
# SPDX-FileCopyrightText: 2024 C. Fernandez-Prades cfernandez(at)cttc.es
# SPDX-License-Identifier: BSD-3-Clause
if(NOT COMMAND feature_summary)
include(FeatureSummary)
endif()
if(NOT GNSSSDR_LIB_PATHS)
include(GnsssdrLibPaths)
endif()
################################################################################
# OpenSSL https://www.openssl.org/
################################################################################
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) # Trick for Homebrew
endif()
unset(OPENSSL_FOUND CACHE)
unset(GnuTLS_FOUND CACHE)
unset(GMP_FOUND CACHE)
if(NOT ENABLE_GNUTLS)
find_package(OpenSSL)
endif()
set_package_properties(OpenSSL
PROPERTIES
URL "https://www.openssl.org"
PURPOSE "Used for the OSNMA and SUPL protocol implementations."
TYPE REQUIRED
)
if(OPENSSL_FOUND)
set_package_properties(OpenSSL
PROPERTIES
DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})"
)
else()
set_package_properties(OpenSSL
PROPERTIES
DESCRIPTION "OpenSSL has not been found, but GnuTLS with openssl compatibility can replace it"
)
################################################################################
# GnuTLS - https://www.gnutls.org/
################################################################################
find_package(GnuTLS)
set_package_properties(GnuTLS PROPERTIES
URL "https://www.gnutls.org/"
PURPOSE "Used for the OSNMA and SUPL protocol implementations."
TYPE REQUIRED
)
if(GnuTLS_FOUND AND GNUTLS_VERSION_STRING)
set_package_properties(GnuTLS PROPERTIES
DESCRIPTION "Transport Layer Security Library (found: v${GNUTLS_VERSION_STRING})"
)
else()
set_package_properties(GnuTLS PROPERTIES
DESCRIPTION "Transport Layer Security Library"
)
endif()
find_library(GNUTLS_OPENSSL_LIBRARY
NAMES gnutls-openssl libgnutls-openssl.so.27
PATHS ${GNSSSDR_LIB_PATHS}
)
find_path(GNUTLS_INCLUDE_DIR NAMES gnutls/gnutls.h
PATHS
/usr/include
/usr/local/include
/opt/local/include # default location in Macports
/opt/homebrew/opt/gnutls/include/
${GNUTLS_ROOT_DIR}/include/
)
if(NOT GNUTLS_OPENSSL_LIBRARY)
message(" The GnuTLS library with openssl compatibility enabled has not been found.")
message(" You can try to install the required libraries by typing:")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|kFreeBSD|GNU")
if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat")
message(" sudo yum install openssl-devel")
else()
message(" sudo apt-get install libgnutls28-dev")
endif()
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(" 'sudo port install openssl3', if you are using Macports, or")
message(" 'brew install openssl', if you are using Homebrew.")
endif()
message(FATAL_ERROR "OpenSSL or the GnuTLS libraries with openssl compatibility are required to build gnss-sdr")
endif()
# Test GnuTLS capabilities
file(READ "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h" gnutls_gnutls_file_contents)
if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_SIGN_ECDSA_SHA256")
set(GNUTLS_SIGN_ECDSA_SHA256 TRUE)
endif()
if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_SIGN_ECDSA_SHA512")
set(GNUTLS_SIGN_ECDSA_SHA512 TRUE)
endif()
if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_DIG_SHA3_256")
set(GNUTLS_DIG_SHA3_256 TRUE)
endif()
if("${gnutls_gnutls_file_contents}" MATCHES "#define GNUTLS_VERSION_MAJOR 2")
set(GNUTLS_HMAC_INIT_WITH_DIGEST TRUE)
endif()
if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_MAC_AES_CMAC_128")
set(GNUTLS_MAC_AES_CMAC_128 TRUE)
endif()
file(READ "${GNUTLS_INCLUDE_DIR}/gnutls/abstract.h" gnutls_abstract_file_contents)
if("${gnutls_abstract_file_contents}" MATCHES "gnutls_pubkey_export2")
set(GNUTLS_PUBKEY_EXPORT2 TRUE)
endif()
find_package(GMP)
set_package_properties(GMP PROPERTIES
PURPOSE "Required to decompress cryptographic keys."
TYPE REQUIRED
)
if(NOT GMP_FOUND)
message(FATAL_ERROR "GMP is required by gnss-sdr if linking against GnuTLS")
endif()
endif()
################################################################################
function(link_to_crypto_dependencies target)
if(OPENSSL_FOUND)
if(TARGET OpenSSL::SSL)
target_link_libraries(${target}
PUBLIC
OpenSSL::SSL
)
if(TARGET OpenSSL::Crypto)
target_link_libraries(${target}
PUBLIC
OpenSSL::Crypto
)
endif()
else()
target_link_libraries(${target}
PUBLIC
${OPENSSL_LIBRARIES}
"${OPENSSL_CRYPTO_LIBRARIES}"
)
target_include_directories(${target}
PUBLIC
${OPENSSL_INCLUDE_DIR}
)
endif()
if(OPENSSL_VERSION)
if(OPENSSL_VERSION VERSION_GREATER "3.0.0")
target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_3=1)
else()
if(NOT OPENSSL_VERSION VERSION_LESS "1.1.1")
target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_111=1)
endif()
endif()
endif()
else() # GnuTLS
target_link_libraries(${target}
PUBLIC
${GNUTLS_LIBRARIES}
${GNUTLS_OPENSSL_LIBRARY}
PRIVATE
Gmp::gmp
)
target_include_directories(${target}
PUBLIC
${GNUTLS_INCLUDE_DIR}
)
target_compile_definitions(${target} PUBLIC -DUSE_GNUTLS_FALLBACK=1)
if(GNUTLS_SIGN_ECDSA_SHA256)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_SIGN_ECDSA_SHA256=1)
endif()
if(GNUTLS_SIGN_ECDSA_SHA512)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_SIGN_ECDSA_SHA512=1)
endif()
if(GNUTLS_DIG_SHA3_256)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_DIG_SHA3_256=1)
endif()
if(GNUTLS_PUBKEY_EXPORT2)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_PUBKEY_EXPORT2=1)
endif()
if(GNUTLS_HMAC_INIT_WITH_DIGEST)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_HMAC_INIT_WITH_DIGEST=1)
endif()
if(GNUTLS_MAC_AES_CMAC_128)
target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_MAC_AES_CMAC_128=1)
endif()
endif()
endfunction()

View File

@ -81,6 +81,25 @@ All notable changes to GNSS-SDR will be documented in this file.
This change has a downside in maintainability, since the source code becomes
plagued with preprocessor directives required to maintain compatibility both
with gflags and glog, and with Abseil.
- Historically, GNSS-SDR linked against the GnuTLS library for cryptographic
functions. If GnuTLS was not found, then the building system looked for and
linked against OpenSSL as a fallback. This was due to the OpenSSL 1.x dual
license scheme, which was incompatible with GPL v3.0 license, preventing it
from being a mandatory dependency for GNSS-SDR in most GNU/Linux
distributions. This issue was solved with the release of OpenSSL 3.0.0, which
transitioned to the Apache License 2.0, fully compatible with GPL v3.0.
Accordingly, the GNSS-SDR building system now looks for OpenSSL in the first
place and, if not found, then it looks for GnuTLS as a fallback.
### Reliability
- Implementation of the Galileo Open Service Navigation Message Authentication
(OSNMA), a data authentication function for the Galileo Open Service worldwide
users, freely accessible to all. OSNMA provides receivers with the assurance
that the received Galileo navigation message is coming from the system itself
and has not been modified. OSNMA is enabled by default if the receiver
configuration defines Galileo E1 OS channels. More details can be found in
[Introducing GNSS Navigation Message Authentication](https://gnss-sdr.org/osnma).
### Improvements in Usability:

View File

@ -919,6 +919,17 @@ Rtklib_Pvt::Rtklib_Pvt(const ConfigurationInterface* configuration,
// Use unhealthy satellites
pvt_output_parameters.use_unhealthy_sats = configuration->property(role + ".use_unhealthy_sats", pvt_output_parameters.use_unhealthy_sats);
// OSNMA
if (gal_1B_count > 0)
{
std::string osnma_mode = configuration->property("GNSS-SDR.osnma_mode", std::string(""));
bool enable_osnma = configuration->property("GNSS-SDR.osnma_enable", true);
if (enable_osnma && osnma_mode == "strict")
{
pvt_output_parameters.osnma_strict = true;
}
}
// make PVT object
pvt_ = rtklib_make_pvt_gs(in_streams_, pvt_output_parameters, rtk);
DLOG(INFO) << "pvt(" << pvt_->unique_id() << ")";

View File

@ -52,6 +52,7 @@
#include "monitor_pvt.h"
#include "monitor_pvt_udp_sink.h"
#include "nmea_printer.h"
#include "osnma_data.h"
#include "pvt_conf.h"
#include "rinex_printer.h"
#include "rtcm_printer.h"
@ -184,7 +185,8 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
d_an_printer_enabled(conf_.an_output_enabled),
d_log_timetag(conf_.log_source_timetag),
d_use_has_corrections(conf_.use_has_corrections),
d_use_unhealthy_sats(conf_.use_unhealthy_sats)
d_use_unhealthy_sats(conf_.use_unhealthy_sats),
d_osnma_strict(conf_.osnma_strict)
{
// Send feedback message to observables block with the receiver clock offset
this->message_port_register_out(pmt::mp("pvt_to_observables"));
@ -217,6 +219,19 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
#else
boost::bind(&rtklib_pvt_gs::msg_handler_has_data, this, _1));
#endif
#endif
// Galileo OSNMA messages port in
this->message_port_register_in(pmt::mp("OSNMA_to_PVT"));
this->set_msg_handler(pmt::mp("OSNMA_to_PVT"),
#if HAS_GENERIC_LAMBDA
[this](auto&& PH1) { msg_handler_osnma(PH1); });
#else
#if USE_BOOST_BIND_PLACEHOLDERS
boost::bind(&rtklib_pvt_gs::msg_handler_osnma, this, boost::placeholders::_1));
#else
boost::bind(&rtklib_pvt_gs::msg_handler_osnma, this, _1));
#endif
#endif
d_initial_carrier_phase_offset_estimation_rads = std::vector<double>(nchannels, 0.0);
@ -1639,6 +1654,28 @@ void rtklib_pvt_gs::msg_handler_has_data(const pmt::pmt_t& msg)
}
void rtklib_pvt_gs::msg_handler_osnma(const pmt::pmt_t& msg)
{
try
{
// Still not sure about what we should receive here.
// It should be a structure with the list of PRNs authenticated (NavData and utcData,
// so with ADKD0 and ADKD12 validated), their corresponding TOW at the beginning
// of the authenticated subframe, and maybe the COP.
const size_t msg_type_hash_code = pmt::any_ref(msg).type().hash_code();
if (msg_type_hash_code == typeid(std::shared_ptr<OSNMA_NavData>).hash_code())
{
const auto osnma_data = wht::any_cast<std::shared_ptr<OSNMA_NavData>>(pmt::any_ref(msg));
d_auth_nav_data_map[osnma_data->get_prn_d()].insert(osnma_data->get_IOD_nav());
}
}
catch (const wht::bad_any_cast& e)
{
LOG(WARNING) << "msg_handler_osnma Bad any_cast: " << e.what();
}
}
std::map<int, Gps_Ephemeris> rtklib_pvt_gs::get_gps_ephemeris_map() const
{
return d_internal_pvt_solver->gps_ephemeris_map;
@ -1997,7 +2034,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
bool store_valid_observable = false;
if (tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend())
if (!d_osnma_strict && tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_gps->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && (std::string(in[i][epoch].Signal, 2) == std::string("1C")) && (d_use_unhealthy_sats || (tmp_eph_iter_gps->second.SV_health == 0)))
@ -2013,10 +2050,25 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
((std::string(in[i][epoch].Signal, 2) == std::string("5X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5a_DVS == false) && (tmp_eph_iter_gal->second.E5a_HS == 0)))) ||
((std::string(in[i][epoch].Signal, 2) == std::string("7X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5b_DVS == false) && (tmp_eph_iter_gal->second.E5b_HS == 0))))))
{
store_valid_observable = true;
if (d_osnma_strict && ((std::string(in[i][epoch].Signal, 2) == std::string("1B")) || ((std::string(in[i][epoch].Signal, 2) == std::string("7X")))))
{
// Pick up only authenticated satellites
auto IOD_nav_list = d_auth_nav_data_map.find(tmp_eph_iter_gal->second.PRN);
if (IOD_nav_list != d_auth_nav_data_map.cend())
{
if (IOD_nav_list->second.find(tmp_eph_iter_gal->second.IOD_nav) != IOD_nav_list->second.cend())
{
store_valid_observable = true;
}
}
}
else
{
store_valid_observable = true;
}
}
}
if (tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend())
if (!d_osnma_strict && tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_cnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && (((std::string(in[i][epoch].Signal, 2) == std::string("2S")) || (std::string(in[i][epoch].Signal, 2) == std::string("L5")))))
@ -2024,7 +2076,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
store_valid_observable = true;
}
}
if (tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend())
if (!d_osnma_strict && tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_glo_gnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && ((std::string(in[i][epoch].Signal, 2) == std::string("1G")) || (std::string(in[i][epoch].Signal, 2) == std::string("2G"))))
@ -2032,7 +2084,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
store_valid_observable = true;
}
}
if (tmp_eph_iter_bds_dnav != d_internal_pvt_solver->beidou_dnav_ephemeris_map.cend())
if (!d_osnma_strict && tmp_eph_iter_bds_dnav != d_internal_pvt_solver->beidou_dnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_bds_dnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && (((std::string(in[i][epoch].Signal, 2) == std::string("B1")) || (std::string(in[i][epoch].Signal, 2) == std::string("B3"))) && (d_use_unhealthy_sats || (tmp_eph_iter_bds_dnav->second.SV_health == 0))))
@ -2042,7 +2094,14 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
}
if (std::string(in[i][epoch].Signal, 2) == std::string("E6"))
{
store_valid_observable = true;
if (d_osnma_strict)
{
// TODO
}
else
{
store_valid_observable = true;
}
}
if (store_valid_observable)

View File

@ -20,6 +20,7 @@
#include "gnss_block_interface.h"
#include "gnss_synchro.h"
#include "gnss_time.h"
#include "osnma_data.h"
#include "rtklib.h"
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@ -34,6 +35,7 @@
#include <map> // for map
#include <memory> // for shared_ptr, unique_ptr
#include <queue> // for std::queue
#include <set> // for std::set
#include <string> // for string
#include <sys/types.h> // for key_t
#include <vector> // for vector
@ -144,6 +146,8 @@ private:
void msg_handler_has_data(const pmt::pmt_t& msg);
void msg_handler_osnma(const pmt::pmt_t& msg);
void initialize_and_apply_carrier_phase_offset();
void apply_rx_clock_offset(std::map<int, Gnss_Synchro>& observables_map,
@ -201,6 +205,7 @@ private:
std::map<int, Gnss_Synchro> d_gnss_observables_map;
std::map<int, Gnss_Synchro> d_gnss_observables_map_t0;
std::map<int, Gnss_Synchro> d_gnss_observables_map_t1;
std::map<uint32_t, std::set<uint32_t>> d_auth_nav_data_map;
std::queue<GnssTime> d_TimeChannelTagTimestamps;
@ -278,6 +283,7 @@ private:
bool d_log_timetag;
bool d_use_has_corrections;
bool d_use_unhealthy_sats;
bool d_osnma_strict;
};

View File

@ -95,6 +95,7 @@ public:
bool use_e6_for_pvt = true;
bool use_has_corrections = true;
bool use_unhealthy_sats = false;
bool osnma_strict = false;
// PVT KF parameters
bool enable_pvt_kf = false;

View File

@ -45,6 +45,7 @@
#include <limits> // for std::numeric_limits
#include <map> // for std::map
#include <stdexcept> // for std::out_of_range
#include <tuple> // for std::tuple
#include <typeinfo> // for typeid
#include <utility> // for std::pair
@ -111,6 +112,7 @@ galileo_telemetry_decoder_gs::galileo_telemetry_decoder_gs(
d_enable_reed_solomon_inav(false),
d_valid_timetag(false),
d_E6_TOW_set(false),
d_there_are_e1_channels(conf.there_are_e1_channels),
d_there_are_e6_channels(conf.there_are_e6_channels),
d_use_ced(conf.use_ced)
{
@ -121,12 +123,19 @@ galileo_telemetry_decoder_gs::galileo_telemetry_decoder_gs(
// Control messages to tracking block
this->message_port_register_out(pmt::mp("telemetry_to_trk"));
if (d_there_are_e1_channels)
{
// register OSM out
this->message_port_register_out(pmt::mp("OSNMA_from_TLM"));
}
if (d_there_are_e6_channels)
{
// register Gal E6 messages HAS out
this->message_port_register_out(pmt::mp("E6_HAS_from_TLM"));
// register TOW from map out
this->message_port_register_out(pmt::mp("TOW_from_TLM"));
// register TOW to TLM input
this->message_port_register_in(pmt::mp("TOW_to_TLM"));
// handler for input port
@ -361,7 +370,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
// 1. De-interleave
std::vector<float> page_part_symbols_soft_value(frame_length);
deinterleaver(GALILEO_INAV_INTERLEAVER_ROWS, GALILEO_INAV_INTERLEAVER_COLS, page_part_symbols, page_part_symbols_soft_value.data());
// 2. Viterbi decoder
// 2.1 Take into account the NOT gate in G2 polynomial (Galileo ICD Figure 13, FEC encoder)
for (int32_t i = 0; i < frame_length; i++)
@ -431,7 +439,29 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
}
// 4. Push the new navigation data to the queues
if (d_inav_nav.have_new_ephemeris() == true)
// extract OSNMA bits, reset container.
if (d_inav_nav.get_osnma_adkd_0_12_nav_bits().size() == 549)
{
DLOG(INFO) << "Galileo OSNMA: new ADKD=0/12 navData from " << d_satellite << " at TOW_sf=" << d_inav_nav.get_TOW5() - 25;
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
d_satellite.get_PRN(),
d_inav_nav.get_osnma_adkd_0_12_nav_bits(),
d_inav_nav.get_TOW5() - 25);
this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj_osnma));
d_inav_nav.reset_osnma_nav_bits_adkd0_12();
}
if (d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141)
{
DLOG(INFO) << "Galileo OSNMA: new ADKD=4 navData from " << d_satellite << " at TOW_sf=" << d_inav_nav.get_TOW6() - 5;
const auto tmp_obj = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf> // TODO conversion from W6 to W_Start_of_subframe
d_satellite.get_PRN(),
d_inav_nav.get_osnma_adkd_4_nav_bits(),
d_inav_nav.get_TOW6() - 5);
this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj));
d_inav_nav.reset_osnma_nav_bits_adkd4();
}
if (d_inav_nav.have_new_ephemeris() == true) // C: tells if W1-->W4 available from same blcok (and W5!)
{
// get object for this SV (mandatory)
const std::shared_ptr<Galileo_Ephemeris> tmp_obj = std::make_shared<Galileo_Ephemeris>(d_inav_nav.get_ephemeris());
@ -466,7 +496,7 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
else
{
// If we still do not have ephemeris, check if we have a reduced CED
if ((d_band == '1') && d_use_ced && !d_first_eph_sent && (d_inav_nav.have_new_reduced_ced() == true))
if ((d_band == '1') && d_use_ced && !d_first_eph_sent && (d_inav_nav.have_new_reduced_ced() == true)) // C: W16 has some Eph. params, uneeded for OSNMa I guess
{
const std::shared_ptr<Galileo_Ephemeris> tmp_obj = std::make_shared<Galileo_Ephemeris>(d_inav_nav.get_reduced_ced());
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
@ -482,7 +512,7 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
}
}
if (d_inav_nav.have_new_iono_and_GST() == true)
if (d_inav_nav.have_new_iono_and_GST() == true) // C: W5
{
// get object for this SV (mandatory)
const std::shared_ptr<Galileo_Iono> tmp_obj = std::make_shared<Galileo_Iono>(d_inav_nav.get_iono());
@ -513,7 +543,7 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
}
}
if (d_inav_nav.have_new_utc_model() == true)
if (d_inav_nav.have_new_utc_model() == true) // C: tells if W6 is available
{
// get object for this SV (mandatory)
const std::shared_ptr<Galileo_Utc_Model> tmp_obj = std::make_shared<Galileo_Utc_Model>(d_inav_nav.get_utc_model());
@ -548,7 +578,7 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
DLOG(INFO) << "delta_t=" << d_delta_t << "[s]";
}
if (d_inav_nav.have_new_almanac() == true)
if (d_inav_nav.have_new_almanac() == true) // flag_almanac_4 tells if W10 available.
{
const std::shared_ptr<Galileo_Almanac_Helper> tmp_obj = std::make_shared<Galileo_Almanac_Helper>(d_inav_nav.get_almanac());
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
@ -580,6 +610,12 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
DLOG(INFO) << "d_TOW_at_current_symbol_ms=" << d_TOW_at_current_symbol_ms;
DLOG(INFO) << "d_nav.WN_0=" << d_inav_nav.get_Galileo_week();
}
auto newOSNMA = d_inav_nav.have_new_nma();
if (d_band == '1' && newOSNMA)
{
const std::shared_ptr<OSNMA_msg> tmp_obj = std::make_shared<OSNMA_msg>(d_inav_nav.get_osnma_msg());
this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj));
}
}
@ -742,7 +778,7 @@ void galileo_telemetry_decoder_gs::decode_CNAV_word(uint64_t time_stamp, float *
std::cout << TEXT_MAGENTA << "Receiving Galileo E6 CNAV dummy pages in channel "
<< d_channel << " from satellite "
<< d_satellite << " with CN0="
<< std::setprecision(2) << cn0 << " dB-Hz" << std::setprecision(default_precision)
<< std::setprecision(2) << cn0 << std::setprecision(default_precision) << " dB-Hz"
<< TEXT_RESET << std::endl;
}
}

View File

@ -152,6 +152,7 @@ private:
bool d_enable_reed_solomon_inav;
bool d_valid_timetag;
bool d_E6_TOW_set;
bool d_there_are_e1_channels;
bool d_there_are_e6_channels;
bool d_use_ced;
};

View File

@ -30,6 +30,11 @@ void Tlm_Conf::SetFromConfiguration(const ConfigurationInterface *configuration,
const std::string default_crc_stats_dumpname("telemetry_crc_stats");
dump_crc_stats_filename = configuration->property(role + ".dump_crc_stats_filename", default_crc_stats_dumpname);
enable_navdata_monitor = configuration->property("NavDataMonitor.enable_monitor", false);
if (configuration->property("Channels_1B.count", 0) > 0)
{
there_are_e1_channels = true;
}
if (configuration->property("Channels_E6.count", 0) > 0)
{
there_are_e6_channels = true;

View File

@ -42,6 +42,7 @@ public:
bool enable_reed_solomon{false}; // for INAV message in Galileo E1B
bool dump_crc_stats{false}; // telemetry CRC statistics
bool enable_navdata_monitor{false};
bool there_are_e1_channels{false};
bool there_are_e6_channels{false};
bool use_ced{false};
};

View File

@ -9,34 +9,42 @@ protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${GNSSSDR_SOURCE_DIR}/docs/protobuf/
add_subdirectory(supl)
set(CORE_LIBS_SOURCES
ini.cc
INIReader.cc
string_converter.cc
gnss_sdr_supl_client.cc
gnss_sdr_sample_counter.cc
channel_status_msg_receiver.cc
channel_event.cc
channel_status_msg_receiver.cc
command_event.cc
galileo_e6_has_msg_receiver.cc
galileo_tow_map.cc
gnss_crypto.cc
gnss_sdr_sample_counter.cc
gnss_sdr_supl_client.cc
ini.cc
INIReader.cc
nav_message_monitor.cc
nav_message_udp_sink.cc
galileo_tow_map.cc
osnma_helper.cc
osnma_msg_receiver.cc
osnma_nav_data_manager.cc
string_converter.cc
)
set(CORE_LIBS_HEADERS
channel_event.h
channel_status_msg_receiver.h
command_event.h
galileo_tow_map.h
gnss_crypto.h
gnss_sdr_sample_counter.h
gnss_sdr_supl_client.h
ini.h
INIReader.h
string_converter.h
gnss_sdr_supl_client.h
gnss_sdr_sample_counter.h
channel_status_msg_receiver.h
channel_event.h
command_event.h
nav_message_monitor.h
nav_message_packet.h
nav_message_udp_sink.h
osnma_helper.h
osnma_msg_receiver.h
osnma_nav_data_manager.h
serdes_nav_message.h
nav_message_monitor.h
galileo_tow_map.h
string_converter.h
)
if(ENABLE_FPGA)
@ -91,10 +99,10 @@ target_link_libraries(core_libs
core_libs_supl
core_system_parameters
pvt_libs
algorithms_libs
PRIVATE
algorithms_libs
Boost::serialization
Boost::system
Pugixml::pugixml
)
@ -122,6 +130,10 @@ target_include_directories(core_libs
${GNSSSDR_SOURCE_DIR}/src/core/interfaces
)
# links to the appropriate library and defines
# USE_GNUTLS_FALLBACK, USE_OPENSSL_3, or USE_OPENSSL_111 accordingly.
link_to_crypto_dependencies(core_libs)
if(USE_GENERIC_LAMBDAS)
set(has_generic_lambdas HAS_GENERIC_LAMBDA=1)
set(no_has_generic_lambdas HAS_GENERIC_LAMBDA=0)

1919
src/core/libs/gnss_crypto.cc Normal file

File diff suppressed because it is too large Load Diff

103
src/core/libs/gnss_crypto.h Normal file
View File

@ -0,0 +1,103 @@
/*!
* \file gnss_crypto.h
* \brief Class for computing cryptographic functions
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GNSS_CRYPTO_H
#define GNSS_SDR_GNSS_CRYPTO_H
#include <cstdint>
#include <string>
#include <vector>
#if USE_GNUTLS_FALLBACK
#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#else // OpenSSL
#include <openssl/ec.h>
#endif
/** \addtogroup Core
* \{ */
/** \addtogroup Core_Receiver_Library
* \{ */
/*!
* \brief Class implementing cryptographic functions
* for Navigation Message Authentication
*/
class Gnss_Crypto
{
public:
Gnss_Crypto(); //!< Default constructor
/*!
* Constructor with a .crt or .pem file for the ECDSA Public Key
* and a XML file for the Merkle Tree root.
* Files can be downloaded by registering at https://www.gsc-europa.eu/
*/
Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath);
~Gnss_Crypto(); //!< Default destructor
bool have_public_key() const; //!< Returns true if the ECDSA Public Key is already loaded
/*!
* Stores the ECDSA Public Key in a .pem file, which is read in a following run if the .crt file is not found
*/
bool store_public_key(const std::string& pubKeyFilePath) const;
bool verify_signature_ecdsa_p256(const std::vector<uint8_t>& message, const std::vector<uint8_t>& signature) const; //!< Verify ECDSA-P256 signature (message in plain hex, signature in raw format)
bool verify_signature_ecdsa_p521(const std::vector<uint8_t>& message, const std::vector<uint8_t>& signature) const; //!< Verify ECDSA-P521 signature (message in plain hex, signature in raw format)
std::vector<uint8_t> compute_SHA_256(const std::vector<uint8_t>& input) const; //!< Computes SHA-256 hash
std::vector<uint8_t> compute_SHA3_256(const std::vector<uint8_t>& input) const; //!< Computes SHA3-256 hash
std::vector<uint8_t> compute_HMAC_SHA_256(const std::vector<uint8_t>& key, const std::vector<uint8_t>& input) const; //!< Computes HMAC-SHA-256 message authentication code
std::vector<uint8_t> compute_CMAC_AES(const std::vector<uint8_t>& key, const std::vector<uint8_t>& input) const; //!< Computes CMAC-AES message authentication code
std::vector<uint8_t> get_merkle_root() const; //!< Gets the Merkle Tree root node (\f$ x_{4,0} \f$)
std::string get_public_key_type() const; //!< Gets the ECDSA Public Key type (ECDSA P-256 / ECDSA P-521 / Unknown)
void set_public_key(const std::vector<uint8_t>& publickey); //!< Sets the ECDSA Public Key (publickey compressed format)
void set_public_key_type(const std::string& public_key_type); //!< Sets the ECDSA Public Key type (ECDSA P-256 / ECDSA P-521)
void set_merkle_root(const std::vector<uint8_t>& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$)
void read_merkle_xml(const std::string& merkleFilePath); //!> Reads the XML file provided from the GSC OSNMA server
private:
void readPublicKeyFromPEM(const std::string& pemFilePath);
bool readPublicKeyFromCRT(const std::string& crtFilePath);
bool convert_raw_to_der_ecdsa(const std::vector<uint8_t>& raw_signature, std::vector<uint8_t>& der_signature) const;
std::vector<uint8_t> convert_from_hex_str(const std::string& input) const; // TODO - deprecate if OSNMA helper is to do this operation
#if USE_GNUTLS_FALLBACK
void decompress_public_key_secp256r1(const std::vector<uint8_t>& compressed_key, std::vector<uint8_t>& x, std::vector<uint8_t>& y) const;
void decompress_public_key_secp521r1(const std::vector<uint8_t>& compressed_key, std::vector<uint8_t>& x, std::vector<uint8_t>& y) const;
bool pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest);
gnutls_pubkey_t d_PublicKey{};
#else // OpenSSL
#if USE_OPENSSL_3
bool pubkey_copy(EVP_PKEY* src, EVP_PKEY** dest);
EVP_PKEY* d_PublicKey{};
#else // OpenSSL 1.x
bool pubkey_copy(EC_KEY* src, EC_KEY** dest);
EC_KEY* d_PublicKey = nullptr;
#endif
#endif
std::vector<uint8_t> d_x_4_0;
std::string d_PublicKeyType;
};
/** \} */
/** \} */
#endif // GNSS_SDR_GNSS_CRYPTO_H

View File

@ -0,0 +1,175 @@
/*!
* \file osnma_helper.h
* \brief Class for auxiliary osnma functions
* \author Carles Fernandez-Prades, 2024 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "osnma_helper.h"
#include <bitset>
#include <chrono>
#include <iomanip>
#include <ios>
#include <sstream>
Osnma_Helper::Osnma_Helper()
{
GST_START_EPOCH.tm_mday = 22;
GST_START_EPOCH.tm_mon = 7; // August (0-based)
GST_START_EPOCH.tm_year = 1999 - 1900;
}
uint32_t Osnma_Helper::compute_gst(uint32_t WN, uint32_t TOW) const
{
return (WN & 0x00000FFF) << 20 | (TOW & 0x000FFFFF);
}
uint32_t Osnma_Helper::compute_gst(tm& input)
{
auto epoch_time_point = std::chrono::system_clock::from_time_t(mktime(&GST_START_EPOCH));
auto input_time_point = std::chrono::system_clock::from_time_t(mktime(&input));
// Get the duration from epoch in seconds
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(input_time_point - epoch_time_point);
// Calculate the week number (WN) and time of week (TOW)
const uint32_t sec_in_week = 604800;
const uint32_t week_number = duration_sec.count() / sec_in_week;
const uint32_t time_of_week = duration_sec.count() % sec_in_week;
return compute_gst(week_number, time_of_week);
}
uint32_t Osnma_Helper::compute_gst_now()
{
time_t now = time(nullptr);
struct tm local_tm = *std::localtime(&now);
struct tm utc_tm = *std::gmtime(&now);
auto timezone_offset = std::mktime(&utc_tm) - std::mktime(&local_tm);
auto epoch_time_point = std::chrono::system_clock::from_time_t(std::mktime(&GST_START_EPOCH) - timezone_offset) + std::chrono::seconds(13);
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - epoch_time_point);
const uint32_t sec_in_week = 604800;
const uint32_t week_number = duration_sec.count() / sec_in_week;
const uint32_t time_of_week = duration_sec.count() % sec_in_week;
return compute_gst(week_number, time_of_week);
}
std::vector<uint8_t> Osnma_Helper::gst_to_uint8(uint32_t GST) const
{
std::vector<uint8_t> res;
res.push_back(static_cast<uint8_t>((GST & 0xFF000000) >> 24));
res.push_back(static_cast<uint8_t>((GST & 0x00FF0000) >> 16));
res.push_back(static_cast<uint8_t>((GST & 0x0000FF00) >> 8));
res.push_back(static_cast<uint8_t>(GST & 0x000000FF));
return res;
}
/**
* @brief Convert a binary string to a vector of bytes.
*
* This function takes a binary string and converts it into a vector of uint8_t bytes.
* The binary string is padded with zeros if necessary to ensure that the total number
* of bits is a multiple of a byte.
*
* @param binaryString The binary string to be converted.
* @return The vector of bytes converted from the binary string.
*/
std::vector<uint8_t> Osnma_Helper::bytes(const std::string& binaryString) const
{
std::vector<uint8_t> bytes;
// Determine the size of the padding needed.
size_t padding_size = binaryString.size() % 8;
std::string padded_binary = binaryString;
if (padding_size != 0)
{
padding_size = 8 - padding_size; // Compute padding size
padded_binary.append(padding_size, '0'); // Append zeros to the binary string
}
for (size_t i = 0; i < padded_binary.size(); i += 8)
{
uint8_t byte = std::bitset<8>(padded_binary.substr(i, 8)).to_ulong();
bytes.push_back(byte);
}
return bytes;
}
std::string Osnma_Helper::verification_status_str(int status) const
{
switch (status)
{
case 0:
return "SUCCESS";
case 1:
return "FAIL";
case 2:
return "UNVERIFIED";
default:
return "UNKNOWN";
}
}
std::string Osnma_Helper::convert_to_hex_string(const std::vector<uint8_t>& vector) const
{
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (auto byte : vector)
{
ss << std::setw(2) << static_cast<int>(byte);
}
return ss.str();
}
std::vector<uint8_t> Osnma_Helper::convert_from_hex_string(const std::string& hex_string) const
{
std::vector<uint8_t> result;
std::string adjusted_hex_string = hex_string;
if (hex_string.length() % 2 != 0)
{
adjusted_hex_string = "0" + hex_string;
}
for (std::size_t i = 0; i < adjusted_hex_string.length(); i += 2)
{
std::string byte_string = adjusted_hex_string.substr(i, 2);
auto byte = static_cast<uint8_t>(std::stoul(byte_string, nullptr, 16));
result.push_back(byte);
}
return result;
}
uint32_t Osnma_Helper::get_WN(uint32_t GST) const
{
return (GST & 0xFFF00000) >> 20;
}
uint32_t Osnma_Helper::get_TOW(uint32_t GST) const
{
return GST & 0x000FFFFF;
}

View File

@ -0,0 +1,51 @@
/*!
* \file osnma_helper.h
* \brief Class for auxiliary osnma functions
* \author Carles Fernandez-Prades, 2024 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_OSNMA_HELPER_H
#define GNSS_SDR_OSNMA_HELPER_H
#include <cstdint>
#include <ctime>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup Core_Receiver_Library
* \{ */
class Osnma_Helper
{
public:
Osnma_Helper();
~Osnma_Helper() = default;
uint32_t compute_gst(uint32_t WN, uint32_t TOW) const;
uint32_t compute_gst(std::tm& input);
uint32_t compute_gst_now();
uint32_t get_WN(uint32_t GST) const;
uint32_t get_TOW(uint32_t GST) const;
std::vector<uint8_t> gst_to_uint8(uint32_t GST) const;
std::vector<uint8_t> bytes(const std::string& binaryString) const;
std::string verification_status_str(int status) const;
std::string convert_to_hex_string(const std::vector<uint8_t>& vector) const;
std::vector<uint8_t> convert_from_hex_string(const std::string& hex_string) const; // TODO remove similar function in gnss_crypto
std::tm GST_START_EPOCH{};
};
/** \} */
/** \} */
#endif // GNSS_SDR_OSNMA_HELPER_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
/*!
* \file osnma_msg_receiver.h
* \brief GNU Radio block that processes Galileo OSNMA data received from
* Galileo E1B telemetry blocks. After successful decoding, sends the content to
* the PVT block.
* \author Carles Fernandez-Prades, 2023-2024. cfernandez(at)cttc.es
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_OSNMA_MSG_RECEIVER_H
#define GNSS_SDR_OSNMA_MSG_RECEIVER_H
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
#include "galileo_inav_message.h" // for OSNMA_msg
#include "gnss_block_interface.h" // for gnss_shared_ptr
#include "osnma_data.h" // for OSNMA_data structures
#include "osnma_nav_data_manager.h" // for OSNMA_NavDataManager
#include <gnuradio/block.h> // for gr::block
#include <pmt/pmt.h> // for pmt::pmt_t
#include <array> // for std::array
#include <cstdint> // for uint8_t
#include <ctime> // for std::time_t
#include <map> // for std::map, std::multimap
#include <memory> // for std::shared_ptr
#include <string> // for std::string
#include <utility> // for std::pair
#include <vector> // for std::vector
/** \addtogroup Core
* \{ */
/** \addtogroup Core_Receiver_Library
* \{ */
class OSNMA_DSM_Reader;
class Gnss_Crypto;
class Osnma_Helper;
class osnma_msg_receiver;
using osnma_msg_receiver_sptr = gnss_shared_ptr<osnma_msg_receiver>;
osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, bool strict_mode = false);
/*!
* \brief GNU Radio block that receives asynchronous OSNMA messages
* from the telemetry blocks, stores them in memory, and decodes OSNMA info
* when enough data have been received.
* The decoded OSNMA data is sent to the PVT block.
*/
class osnma_msg_receiver : public gr::block
{
public:
~osnma_msg_receiver() = default; //!< Default destructor
bool verify_dsm_pkr(const DSM_PKR_message& message) const; //!< Public for benchmarking purposes
void msg_handler_osnma(const pmt::pmt_t& msg); //!< For testing purposes
void read_merkle_xml(const std::string& merklepath); //!< Public for testing purposes
void set_merkle_root(const std::vector<uint8_t>& v); //!< Public for benchmarking purposes
private:
friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, bool strict_mode);
osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, bool strict_mode);
void process_osnma_message(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void read_nma_header(uint8_t nma_header);
void read_dsm_header(uint8_t dsm_header);
void read_dsm_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void process_dsm_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void process_dsm_message(const std::vector<uint8_t>& dsm_msg, const uint8_t& nma_header);
void read_and_process_mack_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void read_mack_header();
void read_mack_body();
void process_mack_message();
void remove_verified_tags();
void control_tags_awaiting_verify_size();
void display_data();
void send_data_to_pvt(const std::vector<OSNMA_NavData>& data);
bool verify_tesla_key(std::vector<uint8_t>& key, uint32_t TOW);
bool verify_tag(Tag& tag) const;
bool tag_has_nav_data_available(const Tag& t) const;
bool tag_has_key_available(const Tag& t) const;
bool verify_macseq(const MACK_message& mack);
bool store_dsm_kroot(const std::vector<uint8_t>& dsm, const uint8_t nma_header) const;
std::pair<std::vector<uint8_t>, uint8_t> parse_dsm_kroot() const;
std::vector<uint8_t> get_merkle_tree_leaves(const DSM_PKR_message& dsm_pkr_message) const;
std::vector<uint8_t> compute_merkle_root(const DSM_PKR_message& dsm_pkr_message, const std::vector<uint8_t>& m_i) const;
std::vector<uint8_t> build_message(Tag& tag) const;
std::vector<uint8_t> hash_chain(uint32_t num_of_hashes_needed, const std::vector<uint8_t>& key, uint32_t GST_SFi, const uint8_t lk_bytes) const;
std::vector<MACK_tag_and_info> verify_macseq_new(const MACK_message& mack);
std::map<uint32_t, std::map<uint32_t, OSNMA_NavData>> d_satellite_nav_data; // map holding OSNMA_NavData sorted by SVID (first key) and TOW (second key).
std::map<uint32_t, std::vector<uint8_t>> d_tesla_keys; // tesla keys over time, sorted by TOW
std::multimap<uint32_t, Tag> d_tags_awaiting_verify; // container with tags to verify from arbitrary SVIDs, sorted by TOW
std::vector<uint8_t> d_new_public_key;
std::vector<uint8_t> d_tags_to_verify{0, 4, 12};
std::vector<MACK_message> d_macks_awaiting_MACSEQ_verification;
std::array<std::array<uint8_t, 256>, 16> d_dsm_message{}; // structure for recording DSM blocks, when filled it sends them to parse and resets itself.
std::array<std::array<uint8_t, 16>, 16> d_dsm_id_received{};
std::array<uint16_t, 16> d_number_of_blocks{};
std::array<uint8_t, 60> d_mack_message{}; // C: 480 b
std::unique_ptr<Gnss_Crypto> d_crypto; // class for cryptographic functions
std::unique_ptr<OSNMA_DSM_Reader> d_dsm_reader; // osnma parameters parser
std::unique_ptr<Osnma_Helper> d_helper; // helper class with auxiliary functions
std::unique_ptr<OSNMA_NavDataManager> d_nav_data_manager; // refactor for holding and processing navigation data
OSNMA_data d_osnma_data{};
uint32_t d_last_received_GST{0}; // latest GST received
uint32_t d_GST_Sf{}; // Scaled GST time for cryptographic computations
uint32_t d_GST_Rx{0}; // local GST receiver time
uint32_t d_last_verified_key_GST{0}; // GST for the latest verified TESLA key
uint32_t d_GST_0{}; // Time of applicability GST (KROOT + 30 s)
uint32_t d_GST_SIS{}; // GST coming from W6 and W5 of SIS
uint32_t d_GST_PKR_PKREV_start{};
uint32_t d_GST_PKR_AM_start{};
uint32_t d_GST_chain_renewal_start{};
uint32_t d_GST_chain_revocation_start{};
uint32_t d_count_successful_tags{0};
uint32_t d_count_failed_tags{0};
uint32_t d_count_failed_Kroot{0};
uint32_t d_count_failed_pubKey{0}; // failed public key verifications against Merkle root
uint32_t d_count_failed_macseq{0};
uint8_t const d_T_L{30}; // s RG Section 2.1
uint8_t d_new_public_key_id{};
bool d_new_data{false};
bool d_public_key_verified{false};
bool d_kroot_verified{false};
bool d_tesla_key_verified{false};
bool d_strict_mode{false};
bool d_flag_hot_start{false};
bool d_flag_PK_renewal{false};
bool d_flag_PK_revocation{false};
bool d_flag_NPK_set{false};
bool d_flag_alert_message{false};
bool d_flag_chain_renewal{false};
bool d_flag_chain_revocation{false};
// Provide access to inner functions to Gtest
FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification);
FRIEND_TEST(OsnmaMsgReceiverTest, TagVerification);
FRIEND_TEST(OsnmaMsgReceiverTest, BuildTagMessageM0);
FRIEND_TEST(OsnmaMsgReceiverTest, VerifyPublicKey);
FRIEND_TEST(OsnmaMsgReceiverTest, ComputeBaseLeaf);
FRIEND_TEST(OsnmaMsgReceiverTest, ComputeMerkleRoot);
FRIEND_TEST(OsnmaTestVectors, NominalTestConf1);
FRIEND_TEST(OsnmaTestVectors, NominalTestConf2);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRenewal);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRevocation);
FRIEND_TEST(OsnmaTestVectors, ChainRenewal);
FRIEND_TEST(OsnmaTestVectors, ChainRevocation);
FRIEND_TEST(OsnmaTestVectors, AlertMessage);
};
/** \} */
/** \} */
#endif // GNSS_SDR_OSNMA_MSG_RECEIVER_H

View File

@ -0,0 +1,322 @@
/*!
* \file osnma_nav_data_manager.cc
* \brief Class for Galileo OSNMA navigation data management
* \author Cesare Ghionoiu-Martinez, 2020-2023 cesare.martinez(at)proton.me
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "osnma_nav_data_manager.h"
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h> // for DLOG
#else
#include <absl/log/log.h>
#endif
/**
* @brief Adds the navigation data bits to the container holding OSNMA_NavData objects.
*
* @param nav_bits The navigation bits.
* @param PRNd The satellite ID.
* @param TOW The TOW of the received data.
*/
void OSNMA_NavDataManager::add_navigation_data(const std::string& nav_bits, uint32_t PRNd, uint32_t TOW)
{
if (not have_nav_data(nav_bits, PRNd, TOW))
{
d_satellite_nav_data[PRNd][TOW].add_nav_data(nav_bits);
d_satellite_nav_data[PRNd][TOW].set_prn_d(PRNd);
d_satellite_nav_data[PRNd][TOW].set_tow_sf0(TOW);
d_satellite_nav_data[PRNd][TOW].set_last_received_TOW(TOW);
}
}
/**
* @brief loops over the verified tags and updates the navigation data tag length
*/
void OSNMA_NavDataManager::update_nav_data(const std::multimap<uint32_t, Tag>& tags_verified, uint8_t tag_size)
{
if (d_satellite_nav_data.empty())
{
return;
}
// loop through all tags
for (const auto& tag : tags_verified)
{
// if tag status is verified, look for corresponding OSNMA_NavData and add increase verified tag bits.
if (tag.second.status == Tag::e_verification_status::SUCCESS)
{
auto sat_it = d_satellite_nav_data.find(tag.second.PRN_d);
if (sat_it == d_satellite_nav_data.end())
{
continue;
}
auto& tow_map = sat_it->second;
for (auto& tow_it : tow_map) // note: starts with smallest (i.e. oldest) navigation dataset
{
std::string nav_data;
if (tag.second.ADKD == 0 || tag.second.ADKD == 12)
{
nav_data = tow_it.second.get_ephemeris_data();
}
else if (tag.second.ADKD == 4)
{
nav_data = tow_it.second.get_utc_data();
}
// find associated OSNMA_NavData
if (tag.second.nav_data == nav_data)
{
d_satellite_nav_data[tag.second.PRN_d][tow_it.first].set_update_verified_bits(tag_size);
}
}
}
}
}
std::vector<OSNMA_NavData> OSNMA_NavDataManager::get_verified_data()
{
std::vector<OSNMA_NavData> result;
for (const auto& prna : d_satellite_nav_data)
{
for (const auto& tow_navdata : prna.second)
{
if (tow_navdata.second.get_verified_bits() >= L_t_min)
{
result.push_back(tow_navdata.second);
d_satellite_nav_data[prna.first][tow_navdata.first].set_verified_status(true);
}
}
}
return result;
}
bool OSNMA_NavDataManager::have_nav_data(uint32_t PRNd, uint32_t TOW, uint8_t ADKD) const
{
const auto sat_it = d_satellite_nav_data.find(PRNd);
if (sat_it == d_satellite_nav_data.cend())
{
return false;
}
const auto tow_it = sat_it->second.find(TOW);
if (tow_it == sat_it->second.cend())
{
return false;
}
switch (ADKD)
{
case 0:
case 12:
return !tow_it->second.get_ephemeris_data().empty();
case 4:
return !tow_it->second.get_utc_data().empty();
default:
return false;
}
}
std::string OSNMA_NavDataManager::get_navigation_data(const Tag& tag) const
{
// Check if Dummy Tag, navData is all zeros
if (tag.cop == 0)
{
if (tag.ADKD == 0 || tag.ADKD == 12)
{
return {std::string(549, '0')};
}
else if (tag.ADKD == 4)
{
return {std::string(141, '0')};
}
}
auto prn_it = d_satellite_nav_data.find(tag.PRN_d);
if (prn_it == d_satellite_nav_data.end())
{
return "";
}
// satellite was found, check if TOW exists in inner map
auto nav_data = prn_it->second.find(tag.TOW - 30);
if (nav_data != prn_it->second.end())
{
if (tag.ADKD == 0 || tag.ADKD == 12)
{
if (!nav_data->second.get_ephemeris_data().empty())
{
return nav_data->second.get_ephemeris_data();
}
}
else if (tag.ADKD == 4)
{
if (!nav_data->second.get_utc_data().empty())
{
return nav_data->second.get_utc_data();
}
}
}
else
{
for (auto rev_it = prn_it->second.rbegin(); rev_it != prn_it->second.rend(); ++rev_it) // NOLINT(modernize-loop-convert)
{
// note: starts with largest (i.e. newest) navigation dataset
// Check if current key (TOW) fulfills condition
if ((tag.TOW - 30 * tag.cop <= rev_it->first || tag.TOW - 30 * tag.cop <= rev_it->second.get_last_received_TOW()) && rev_it->first < tag.TOW)
{
if (tag.ADKD == 0 || tag.ADKD == 12)
{
if (!rev_it->second.get_ephemeris_data().empty())
{
return rev_it->second.get_ephemeris_data();
}
}
else if (tag.ADKD == 4)
{
if (!rev_it->second.get_utc_data().empty())
{
return rev_it->second.get_utc_data();
}
}
}
}
}
return "";
}
/**
* @brief Checks if the OSNMA_NavData bits are already present. In case affirmative, it updates the OSNMA_NavData 'last received' timestamp
* @remarks e.g.: a SV may repeat the bits over several subframes. In that case, need to save them only once.
* @param nav_bits
* @param PRNd
* @return
*/
bool OSNMA_NavDataManager::have_nav_data(const std::string& nav_bits, uint32_t PRNd, uint32_t TOW)
{
if (d_satellite_nav_data.find(PRNd) != d_satellite_nav_data.end())
{
for (auto& data_timestamp : d_satellite_nav_data[PRNd])
{
if (nav_bits.size() == EPH_SIZE)
{
if (data_timestamp.second.get_ephemeris_data() == nav_bits)
{
data_timestamp.second.set_last_received_TOW(TOW);
return true;
}
}
else if (nav_bits.size() == UTC_SIZE)
{
if (data_timestamp.second.get_utc_data() == nav_bits)
{
data_timestamp.second.set_last_received_TOW(TOW);
return true;
}
}
}
}
return false;
}
/**
* @brief Checks if there is a OSNMA_NavData element within the COP time interval for a Tag t
* @param t Tag object
* @return True if the needed navigation data for the tag is available (oldest possible OSNMA_NavData available)
*/
bool OSNMA_NavDataManager::have_nav_data(const Tag& t) const
{
if (t.cop == 0)
{
return true;
}
auto prn_it = d_satellite_nav_data.find(t.PRN_d);
if (prn_it == d_satellite_nav_data.end())
{
return false;
}
// satellite was found, check if TOW exists in inner map
// try find target TOW directly first
auto nav_data = prn_it->second.find(t.TOW - 30);
if (nav_data != prn_it->second.end())
{
if (t.ADKD == 0 || t.ADKD == 12)
{
if (!nav_data->second.get_ephemeris_data().empty())
{
return true;
}
}
else if (t.ADKD == 4)
{
if (!nav_data->second.get_utc_data().empty())
{
return true;
}
}
}
else
{
// iterate in reverse order to find matching TOW with Tag's COP value
std::map<uint32_t, OSNMA_NavData> tow_map = prn_it->second;
for (auto rev_it = tow_map.rbegin(); rev_it != tow_map.rend(); ++rev_it) // NOLINT(modernize-loop-convert)
{
// note: starts with largest (i.e. newest) navigation dataset
// Check if current key (TOW) fulfills cut-off point and is not received after the tag
if ((t.TOW - 30 * t.cop <= rev_it->first || t.TOW - 30 * t.cop <= rev_it->second.get_last_received_TOW()) && rev_it->first < t.TOW)
{
if (t.ADKD == 0 || t.ADKD == 12)
{
if (!rev_it->second.get_ephemeris_data().empty())
{
return true;
}
}
else if (t.ADKD == 4)
{
if (!rev_it->second.get_utc_data().empty())
{
return true;
}
}
}
}
}
return false;
}
void OSNMA_NavDataManager::log_status() const
{
for (const auto& satellite : d_satellite_nav_data)
{
LOG(INFO) << "Galileo OSNMA: NavData status :: SVID=" << satellite.first;
const auto& tow_data = satellite.second;
for (const auto& nav_data : tow_data)
{
LOG(INFO) << "Galileo OSNMA: IOD_nav=0b" << std::uppercase
<< std::bitset<10>(nav_data.second.get_IOD_nav())
<< ", TOW_start="
<< nav_data.second.get_tow_sf0()
<< ", TOW_last="
<< nav_data.second.get_last_received_TOW()
<< ", l_t="
<< nav_data.second.get_verified_bits()
<< ", PRNd="
<< nav_data.second.get_prn_d()
<< ", verified="
<< nav_data.second.get_verified_status();
}
}
}

View File

@ -0,0 +1,59 @@
/*!
* \file osnma_nav_data_manager.h
* \brief Class for Galileo OSNMA navigation data management
* \author Cesare Ghionoiu-Martinez, 2020-2023 cesare.martinez(at)proton.me
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_OSNMA_NAV_DATA_MANAGER_H
#define GNSS_SDR_OSNMA_NAV_DATA_MANAGER_H
#include "osnma_data.h" // for OSNMA_NavData, Tag
#include <cstdint> // for uint32_t
#include <map>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup Core_Receiver_Library
* \{ */
/**
* @class OSNMA_NavDataManager
* @brief Class for managing OSNMA navigation data
*/
class OSNMA_NavDataManager
{
public:
OSNMA_NavDataManager() = default;
void log_status() const;
bool have_nav_data(const Tag& t) const;
bool have_nav_data(uint32_t PRNd, uint32_t TOW, uint8_t ADKD) const;
std::string get_navigation_data(const Tag& t) const;
void add_navigation_data(const std::string& nav_bits, uint32_t PRNd, uint32_t TOW);
void update_nav_data(const std::multimap<uint32_t, Tag>& tags_verified, uint8_t tag_size);
bool have_nav_data(const std::string& nav_bits, uint32_t PRNd, uint32_t TOW);
std::vector<OSNMA_NavData> get_verified_data();
private:
std::map<uint32_t, std::map<uint32_t, OSNMA_NavData>> d_satellite_nav_data{}; // NavData sorted by [PRNd][TOW_start]
const uint32_t L_t_min{40};
const uint16_t EPH_SIZE{549};
const uint16_t UTC_SIZE{141};
};
/** \} */
/** \} */
#endif // GNSS_SDR_OSNMA_NAV_DATA_MANAGER_H

View File

@ -46,15 +46,9 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU" AND (CMAKE_VERSION VERSION_GREATER "3
target_compile_options(core_libs_supl PUBLIC $<$<COMPILE_LANGUAGE:C>:${MY_C_FLAGS}>)
endif()
if(OPENSSL_FOUND)
target_compile_definitions(core_libs_supl PUBLIC -DUSE_OPENSSL_FALLBACK=1)
endif()
target_link_libraries(core_libs_supl
PUBLIC
${GNUTLS_LIBRARIES}
${GNUTLS_OPENSSL_LIBRARY}
)
# links to the appropriate library and defines
# USE_GNUTLS_FALLBACK, USE_OPENSSL_3, or USE_OPENSSL_111 accordingly.
link_to_crypto_dependencies(core_libs_supl)
target_include_directories(core_libs_supl
PUBLIC

View File

@ -24,18 +24,18 @@
#define EXPORT
#endif
// clang-format off
#if USE_OPENSSL_FALLBACK
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#else
#if USE_GNUTLS_FALLBACK
#include <gnutls/gnutls.h>
#include <gnutls/compat.h>
#include <gnutls/crypto.h>
#include <gnutls/openssl.h>
#include <gnutls/x509.h>
#else
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
// clang-format on
#include <PDU.h>

View File

@ -27,6 +27,7 @@
#include "Galileo_E5a.h"
#include "Galileo_E5b.h"
#include "Galileo_E6.h"
#include "Galileo_OSNMA.h"
#include "channel.h"
#include "channel_fsm.h"
#include "channel_interface.h"
@ -82,6 +83,7 @@ GNSSFlowgraph::GNSSFlowgraph(std::shared_ptr<ConfigurationInterface> configurati
connected_(false),
running_(false),
multiband_(GNSSFlowgraph::is_multiband()),
enable_osnma_rx_(false),
enable_e6_has_rx_(false)
{
enable_fpga_offloading_ = configuration_->property("GNSS-SDR.enable_FPGA", false);
@ -120,6 +122,24 @@ void GNSSFlowgraph::init()
galileo_tow_map_ = nullptr;
}
if (configuration_->property("Channels_1B.count", 0) > 0 && configuration_->property("GNSS-SDR.osnma_enable", true))
{
enable_osnma_rx_ = true;
const auto certFilePath = configuration_->property("GNSS-SDR.osnma_public_key", CRTFILE_DEFAULT);
const auto merKleTreePath = configuration_->property("GNSS-SDR.osnma_merkletree", MERKLEFILE_DEFAULT);
std::string osnma_mode = configuration_->property("GNSS-SDR.osnma_mode", std::string(""));
bool strict_mode = false;
if (osnma_mode == "strict")
{
strict_mode = true;
}
osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath, strict_mode);
}
else
{
osnma_rx_ = nullptr;
}
// 1. read the number of RF front-ends available (one file_source per RF front-end)
int sources_count_deprecated = configuration_->property("Receiver.sources_count", 1);
sources_count_ = configuration_->property("GNSS-SDR.num_sources", sources_count_deprecated);
@ -520,6 +540,14 @@ int GNSSFlowgraph::connect_desktop_flowgraph()
}
}
if (enable_osnma_rx_)
{
if (connect_osnma() != 0)
{
return 1;
}
}
// Activate acquisition in enabled channels
std::lock_guard<std::mutex> lock(signal_list_mutex_);
for (int i = 0; i < channels_count_; i++)
@ -640,6 +668,14 @@ int GNSSFlowgraph::connect_fpga_flowgraph()
}
}
if (enable_osnma_rx_)
{
if (connect_osnma() != 0)
{
return 1;
}
}
check_desktop_conf_in_fpga_env();
LOG(INFO) << "The GNU Radio flowgraph for the current GNSS-SDR configuration with FPGA off-loading has been successfully connected";
@ -1365,6 +1401,42 @@ int GNSSFlowgraph::connect_monitors()
}
int GNSSFlowgraph::connect_osnma()
{
try
{
bool gal_e1_channels = false;
for (int i = 0; i < channels_count_; i++)
{
const std::string gnss_signal = channels_.at(i)->get_signal().get_signal_str();
switch (mapStringValues_[gnss_signal])
{
case evGAL_1B:
top_block_->msg_connect(channels_.at(i)->get_right_block(), pmt::mp("OSNMA_from_TLM"), osnma_rx_, pmt::mp("OSNMA_from_TLM"));
gal_e1_channels = true;
break;
default:
break;
}
}
if (gal_e1_channels == true)
{
top_block_->msg_connect(osnma_rx_, pmt::mp("OSNMA_to_PVT"), pvt_->get_left_block(), pmt::mp("OSNMA_to_PVT"));
}
}
catch (const std::exception& e)
{
LOG(ERROR) << "Can't connect Galileo OSNMA msg ports: " << e.what();
top_block_->disconnect_all();
return 1;
}
DLOG(INFO) << "Galileo OSNMA message ports connected";
return 0;
}
int GNSSFlowgraph::connect_gal_e6_has()
{
try

View File

@ -30,6 +30,7 @@
#include "galileo_tow_map.h"
#include "gnss_sdr_sample_counter.h"
#include "gnss_signal.h"
#include "osnma_msg_receiver.h"
#include "pvt_interface.h"
#include <gnuradio/blocks/null_sink.h> // for null_sink
#include <gnuradio/runtime_types.h> // for basic_block_sptr, top_block_sptr
@ -179,6 +180,7 @@ private:
int connect_channels_to_observables();
int connect_observables_to_pvt();
int connect_monitors();
int connect_osnma();
int connect_gal_e6_has();
int connect_gnss_synchro_monitor();
int connect_acquisition_monitor();
@ -234,6 +236,7 @@ private:
channel_status_msg_receiver_sptr channels_status_; // class that receives and stores the current status of the receiver channels
galileo_e6_has_msg_receiver_sptr gal_e6_has_rx_;
galileo_tow_map_sptr galileo_tow_map_;
osnma_msg_receiver_sptr osnma_rx_;
gnss_sdr_sample_counter_sptr ch_out_sample_counter_;
#if ENABLE_FPGA
@ -290,6 +293,7 @@ private:
bool enable_tracking_monitor_;
bool enable_navdata_monitor_;
bool enable_fpga_offloading_;
bool enable_osnma_rx_;
bool enable_e6_has_rx_;
};

View File

@ -28,6 +28,8 @@ set(SYSTEM_PARAMETERS_SOURCES
glonass_gnav_utc_model.cc
glonass_gnav_navigation_message.cc
reed_solomon.cc
osnma_data.cc
osnma_dsm_reader.cc
)
set(SYSTEM_PARAMETERS_HEADERS
@ -89,6 +91,9 @@ set(SYSTEM_PARAMETERS_HEADERS
MATH_CONSTANTS.h
reed_solomon.h
galileo_has_page.h
Galileo_OSNMA.h
osnma_data.h
osnma_dsm_reader.h
)
list(SORT SYSTEM_PARAMETERS_HEADERS)
@ -114,6 +119,8 @@ target_link_libraries(core_system_parameters
PUBLIC
Boost::date_time
Boost::serialization
PRIVATE
Pugixml::pugixml
)
if(ENABLE_GLOG_AND_GFLAGS)

View File

@ -0,0 +1,199 @@
/*!
* \file Galileo_OSNMA.h
* \brief Galileo OSNMA mesage constants
* \author Carles Fernandez, 2023. cfernandez(at)cttc.es
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GALILEO_OSNMA_H
#define GNSS_SDR_GALILEO_OSNMA_H
#include <cstddef>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup System_Parameters
* \{ */
constexpr size_t SIZE_DSM_BLOCKS_BYTES = 13;
// OSNMA User ICD, Issue 1.1, Table 1
const std::unordered_map<uint8_t, std::string> OSNMA_TABLE_1 = {
{0, std::string("Reserved")},
{1, std::string("Test")},
{2, std::string("Operational")},
{3, std::string("Don't use")}}; // key: nmas, value: nmas status
// OSNMA User ICD, Issue 1.1, Table 2
const std::unordered_map<uint8_t, std::string> OSNMA_TABLE_2 = {
{0, std::string("Reserved")},
{1, std::string("Nominal")},
{2, std::string("End of Chain (EOC)")},
{3, std::string("Chain Revoked (CREV)")},
{4, std::string("New Publick Key (NPK)")},
{5, std::string("Public Key Revoked (PKREV)")},
{6, std::string("New Merkle Tree (NMT)")},
{7, std::string("Alert Message (AM)")}}; // key: cpks, value: cpks status
// OSNMA User ICD for the Test Phase, Issue 1.0, Table 3
const std::unordered_map<uint8_t, std::pair<uint16_t, uint16_t>> OSNMA_TABLE_3 = {
{0, {0, 0}},
{1, {0, 0}},
{2, {0, 0}},
{3, {0, 0}},
{4, {0, 0}},
{5, {0, 0}},
{6, {0, 0}},
{7, {13, 1352}},
{8, {14, 1456}},
{9, {15, 1560}},
{10, {16, 1664}},
{11, {0, 0}},
{12, {0, 0}},
{13, {0, 0}},
{14, {0, 0}},
{15, {0, 0}}}; // key: nb_dp, value: {num_blocks, l_dp_bits}
const std::unordered_map<uint8_t, std::string> OSNMA_TABLE_5 = {
{0, std::string("Reserved")},
{1, std::string("ECDSA P-256")},
{2, std::string("Reserved")},
{3, std::string("ECDSA P-521")},
{4, std::string("OAM")},
{5, std::string("Reserved")},
{6, std::string("Reserved")},
{7, std::string("Reserved")},
{8, std::string("Reserved")},
{9, std::string("Reserved")},
{10, std::string("Reserved")},
{11, std::string("Reserved")},
{12, std::string("Reserved")},
{13, std::string("Reserved")},
{14, std::string("Reserved")},
{15, std::string("Reserved")}}; // key: nptk, value: message
const std::unordered_map<std::string, uint16_t> OSNMA_TABLE_6 = {
{std::string("ECDSA P-256"), 264},
{std::string("ECDSA P-521"), 536}};
// OSNMA User ICD, Issue 1.1, Table 7
const std::unordered_map<uint8_t, std::pair<uint16_t, uint16_t>> OSNMA_TABLE_7 = {
{0, {0, 0}},
{1, {7, 728}},
{2, {8, 832}},
{3, {9, 936}},
{4, {10, 1040}},
{5, {11, 1144}},
{6, {12, 1248}},
{7, {13, 1352}},
{8, {14, 1456}},
{9, {0, 0}},
{10, {0, 0}},
{11, {0, 0}},
{12, {0, 0}},
{13, {0, 0}},
{14, {0, 0}},
{15, {0, 0}}}; // key: nb_dk, value: {num_blocks, l_dk_bits}
const std::unordered_map<uint8_t, std::string> OSNMA_TABLE_8 = {
{0, std::string("SHA-256")},
{1, std::string("Reserved")},
{2, std::string("SHA3-256")},
{3, std::string("Reserved")}}; // key: hs, value: hash_function
const std::unordered_map<uint8_t, uint16_t> OSNMA_TABLE_10 = {
{0, 96},
{1, 104},
{2, 112},
{3, 120},
{4, 128},
{5, 160},
{6, 192},
{7, 224},
{8, 256},
{9, 0},
{10, 0},
{11, 0},
{12, 0},
{13, 0},
{15, 0},
{15, 0}}; // key: ks, value: lk_bits
const std::unordered_map<uint8_t, uint8_t> OSNMA_TABLE_11 = {
{0, 0},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 20},
{6, 24},
{7, 28},
{8, 32},
{9, 40},
{10, 0},
{11, 0},
{12, 0},
{13, 0},
{14, 0},
{15, 0},
};
const std::unordered_map<std::string, uint16_t> OSNMA_TABLE_15 = {
{std::string("ECDSA P-256"), 512},
{std::string("ECDSA P-521"), 1056}}; // key: ECDSA Curve and hash function, value: {l_ds_bits}
const std::string PEMFILE_DEFAULT("./OSNMA_PublicKey.pem");
const std::string CRTFILE_DEFAULT("./OSNMA_PublicKey_20240115100000_newPKID_1.crt");
const std::string MERKLEFILE_DEFAULT("./OSNMA_MerkleTree_20240115100000_newPKID_1.xml");
const std::string KROOTFILE_DEFAULT("./OSNMA_DSM_KROOT_NMAHeader.bin");
class Mack_lookup
{
public:
Mack_lookup() = default;
Mack_lookup(uint8_t msg_,
uint8_t nt_,
const std::vector<std::string>& s1_,
const std::vector<std::string>& s2_) : msg(msg_),
nt(nt_),
sequence1(s1_),
sequence2(s2_){};
uint8_t msg{};
uint8_t nt{};
std::vector<std::string> sequence1;
std::vector<std::string> sequence2;
};
const std::unordered_map<uint8_t, Mack_lookup> OSNMA_TABLE_16 = {
{27, {2, 6, {"00S", "00E", "00E", "00E", "12S", "00E"}, {"00S ", "00E", "00E", "04S", "12S", "00E"}}},
{28, {2, 10, {"00S", "00E", "00E", "00E", "00S", "00E", "00E", "12S", "00E", "00E"}, {"00S", "00E", "00E", "00S", "00E", "00E", "04S", "12S", "00E", "00E"}}},
{31, {2, 5, {"00S", "00E", "00E", "12S", "00E"}, {"00S", "00E", "00E", "12S", "04S"}}},
{33, {2, 6, {"00S", "00E", "04S", "00E", "12S", "00E"}, {"00S", "00E", "00E", "12S", "00E", "12E"}}},
{34, {2, 6, {"00S", "FLX", "04S", "FLX", "12S", "00E"}, {"00S", "FLX", "00E", "12S", "00E", "12E"}}},
{35, {2, 6, {"00S", "FLX", "04S", "FLX", "12S", "FLX"}, {"00S", "FLX", "FLX", "12S", "FLX", "FLX"}}},
{36, {2, 5, {"00S", "FLX", "04S", "FLX", "12S"}, {"00S", "FLX", "00E", "12S", "12E"}}},
{37, {2, 5, {"00S", "00E", "04S", "00E", "12S"}, {"00S", "00E", "00E", "12S", "12E"}}},
{38, {2, 5, {"00S", "FLX", "04S", "FLX", "12S"}, {"00S", "FLX", "FLX", "12S", "FLX"}}},
{39, {2, 4, {"00S", "FLX", "04S", "FLX"}, {"00S", "FLX", "00E", "12S"}}},
{40, {2, 4, {"00S", "00E", "04S", "12S"}, {"00S", "00E", "00E", "12E"}}},
{41, {2, 4, {"00S", "FLX", "04S", "FLX"}, {"00S", "FLX", "FLX", "12S"}}}};
/** \} */
/** \} */
#endif // GNSS_SDR_GALILEO_OSNMA_H

View File

@ -143,8 +143,6 @@ bool Galileo_Inav_Message::read_navigation_bool(const std::bitset<GALILEO_DATA_J
void Galileo_Inav_Message::split_page(std::string page_string, int32_t flag_even_word)
{
int32_t Page_type = 0;
if (page_string.at(0) == '1') // if page is odd
{
const std::string& page_Odd = page_string;
@ -153,21 +151,21 @@ void Galileo_Inav_Message::split_page(std::string page_string, int32_t flag_even
{
const std::string page_INAV_even = page_Even;
const std::string page_INAV = page_INAV_even + page_Odd; // Join pages: Even + Odd = INAV page
const std::string Even_bit = page_INAV.substr(0, 1);
const std::string Page_type_even = page_INAV.substr(1, 1);
const std::string nominal = "0";
const std::string Data_k = page_INAV.substr(2, 112);
const std::string Odd_bit = page_INAV.substr(114, 1);
const std::string Page_type_Odd = page_INAV.substr(115, 1);
const std::string Data_j = page_INAV.substr(116, 16);
const std::string Reserved_1 = page_INAV.substr(132, 40);
const std::string SAR = page_INAV.substr(172, 22);
const std::string Spare = page_INAV.substr(194, 2);
const std::string osnma_sis = page_INAV.substr(132, 40);
// const std::string SAR = page_INAV.substr(172, 22);
// const std::string Spare = page_INAV.substr(194, 2);
const std::string CRC_data = page_INAV.substr(196, 24);
const std::string Reserved_2 = page_INAV.substr(220, 8);
const std::string Tail_odd = page_INAV.substr(228, 6);
// const std::string Reserved_2 = page_INAV.substr(220, 8);
// const std::string Tail_odd = page_INAV.substr(228, 6);
if (page_position_in_inav_subframe != 255)
{
page_position_in_inav_subframe++;
}
// ************ CRC checksum control *******/
std::stringstream TLM_word_for_CRC_stream;
@ -180,12 +178,29 @@ void Galileo_Inav_Message::split_page(std::string page_string, int32_t flag_even
{
flag_CRC_test = true;
// CRC correct: Decode word
const std::string page_number_bits = Data_k.substr(0, 6);
const std::bitset<GALILEO_PAGE_TYPE_BITS> page_type_bits(page_number_bits); // from string to bitset
Page_type = static_cast<int32_t>(read_page_type_unsigned(page_type_bits, TYPE));
Page_type_time_stamp = Page_type;
const std::string Data_jk_ephemeris = Data_k + Data_j;
page_jk_decoder(Data_jk_ephemeris.c_str());
// Fill OSNMA data
if (page_position_in_inav_subframe != 255)
{
if (page_position_in_inav_subframe == 0)
{ // TODO - is it redundant? receiving Word 2 already resets this
nma_position_filled = std::array<int8_t, 15>{};
nma_msg.mack = std::array<uint32_t, 15>{};
nma_msg.hkroot = std::array<uint8_t, 15>{};
}
std::bitset<8> hkroot_bs(osnma_sis.substr(0, 8));
std::bitset<32> mack_bs(osnma_sis.substr(8, 32));
if (hkroot_bs.count() != 0 && mack_bs.count() != 0)
{
hkroot_sis = static_cast<uint8_t>(hkroot_bs.to_ulong());
mack_sis = static_cast<uint32_t>(mack_bs.to_ulong());
nma_msg.mack[page_position_in_inav_subframe] = mack_sis;
nma_msg.hkroot[page_position_in_inav_subframe] = hkroot_sis;
nma_position_filled[page_position_in_inav_subframe] = 1;
}
}
}
else
{
@ -201,6 +216,7 @@ void Galileo_Inav_Message::split_page(std::string page_string, int32_t flag_even
}
// C: tells if W1-->W4 available from same blcok
bool Galileo_Inav_Message::have_new_ephemeris() // Check if we have a new ephemeris stored in the galileo navigation class
{
if ((flag_ephemeris_1 == true) and (flag_ephemeris_2 == true) and (flag_ephemeris_3 == true) and (flag_ephemeris_4 == true) and (flag_iono_and_GST == true))
@ -335,6 +351,7 @@ bool Galileo_Inav_Message::have_new_ephemeris() // Check if we have a new ephem
}
// C: tells if W5 is available
bool Galileo_Inav_Message::have_new_iono_and_GST() // Check if we have a new iono data set stored in the galileo navigation class
{
if ((flag_iono_and_GST == true) and (flag_utc_model == true)) // the condition on flag_utc_model is added to have a time stamp for iono
@ -347,6 +364,7 @@ bool Galileo_Inav_Message::have_new_iono_and_GST() // Check if we have a new io
}
// C: tells if W6 is available
bool Galileo_Inav_Message::have_new_utc_model() // Check if we have a new utc data set stored in the galileo navigation class
{
if (flag_utc_model == true)
@ -359,6 +377,7 @@ bool Galileo_Inav_Message::have_new_utc_model() // Check if we have a new utc d
}
// flag_almanac_4 tells if W10 available.
bool Galileo_Inav_Message::have_new_almanac() // Check if we have a new almanac data set stored in the galileo navigation class
{
if ((flag_almanac_1 == true) and (flag_almanac_2 == true) and (flag_almanac_3 == true) and (flag_almanac_4 == true))
@ -599,6 +618,7 @@ void Galileo_Inav_Message::read_page_1(const std::bitset<GALILEO_DATA_JK_BITS>&
DLOG(INFO) << "A_1= " << A_1;
flag_ephemeris_1 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_1 = data_bits.to_string().substr(6, 120);
}
@ -620,6 +640,7 @@ void Galileo_Inav_Message::read_page_2(const std::bitset<GALILEO_DATA_JK_BITS>&
DLOG(INFO) << "iDot_2= " << iDot_2;
flag_ephemeris_2 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_2 = data_bits.to_string().substr(6, 120);
}
@ -649,6 +670,7 @@ void Galileo_Inav_Message::read_page_3(const std::bitset<GALILEO_DATA_JK_BITS>&
DLOG(INFO) << "SISA_3= " << SISA_3;
flag_ephemeris_3 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_3 = data_bits.to_string().substr(6, 122);
}
@ -657,6 +679,7 @@ void Galileo_Inav_Message::read_page_4(const std::bitset<GALILEO_DATA_JK_BITS>&
IOD_nav_4 = static_cast<int32_t>(read_navigation_unsigned(data_bits, IOD_NAV_4_BIT));
DLOG(INFO) << "IOD_nav_4= " << IOD_nav_4;
SV_ID_PRN_4 = static_cast<int32_t>(read_navigation_unsigned(data_bits, SV_ID_PRN_4_BIT));
nma_msg.PRN = static_cast<uint32_t>(SV_ID_PRN_4);
DLOG(INFO) << "SV_ID_PRN_4= " << SV_ID_PRN_4;
C_ic_4 = static_cast<double>(read_navigation_signed(data_bits, C_IC_4_BIT));
C_ic_4 = C_ic_4 * C_IC_4_LSB;
@ -681,6 +704,7 @@ void Galileo_Inav_Message::read_page_4(const std::bitset<GALILEO_DATA_JK_BITS>&
DLOG(INFO) << "spare_4 = " << spare_4;
flag_ephemeris_4 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_4 = data_bits.to_string().substr(6, 120);
}
@ -815,6 +839,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
{
case 1: // Word type 1: Ephemeris (1/4)
{
page_position_in_inav_subframe = 10;
read_page_1(data_jk_bits);
if (enable_rs)
{
@ -851,6 +876,14 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 2: // Word type 2: Ephemeris (2/4)
{
// start of subframe, reset osnma parameters TODO - refactor
page_position_in_inav_subframe = 0;
nma_msg.mack = std::array<uint32_t, 15>{};
nma_msg.hkroot = std::array<uint8_t, 15>{};
nma_position_filled = std::array<int8_t, 15>{};
reset_osnma_nav_bits_adkd4();
reset_osnma_nav_bits_adkd0_12();
read_page_2(data_jk_bits);
if (enable_rs)
{
@ -882,6 +915,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
}
case 3: // Word type 3: Ephemeris (3/4) and SISA
{
page_position_in_inav_subframe = 11;
read_page_3(data_jk_bits);
if (enable_rs)
{
@ -914,6 +948,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 4: // Word type 4: Ephemeris (4/4) and Clock correction parameters
{
page_position_in_inav_subframe = 1;
read_page_4(data_jk_bits);
if (enable_rs)
{
@ -945,6 +980,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
}
case 5: // Word type 5: Ionospheric correction, BGD, signal health and data validity status and GST
page_position_in_inav_subframe = 12;
// Ionospheric correction
ai0_5 = static_cast<double>(read_navigation_unsigned(data_jk_bits, AI0_5_BIT));
ai0_5 = ai0_5 * AI0_5_LSB;
@ -992,9 +1028,11 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
flag_iono_and_GST = true; // set to false externally
flag_TOW_set = true; // set to false externally
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_5 = data_jk_bits.to_string().substr(6, 67);
break;
case 6: // Word type 6: GST-UTC conversion parameters
page_position_in_inav_subframe = 2;
A0_6 = static_cast<double>(read_navigation_signed(data_jk_bits, A0_6_BIT));
A0_6 = A0_6 * A0_6_LSB;
DLOG(INFO) << "A0_6= " << A0_6;
@ -1020,9 +1058,11 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
flag_utc_model = true; // set to false externally
flag_TOW_set = true; // set to false externally
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_6 = data_jk_bits.to_string().substr(6, 99);
break;
case 7: // Word type 7: Almanac for SVID1 (1/2), almanac reference time and almanac reference week number
page_position_in_inav_subframe = 3;
IOD_a_7 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, IOD_A_7_BIT));
DLOG(INFO) << "IOD_a_7= " << IOD_a_7;
WN_a_7 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, WN_A_7_BIT));
@ -1057,7 +1097,8 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
break;
case 8: // Word type 8: Almanac for SVID1 (2/2) and SVID2 (1/2)*/
case 8: // Word type 8: Almanac for SVID1 (2/2) and SVID2 (1/2)
page_position_in_inav_subframe = 4;
IOD_a_8 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, IOD_A_8_BIT));
DLOG(INFO) << "IOD_a_8= " << IOD_a_8;
af0_8 = static_cast<double>(read_navigation_signed(data_jk_bits, AF0_8_BIT));
@ -1095,6 +1136,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
break;
case 9: // Word type 9: Almanac for SVID2 (2/2) and SVID3 (1/2)
page_position_in_inav_subframe = 3;
IOD_a_9 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, IOD_A_9_BIT));
DLOG(INFO) << "IOD_a_9= " << IOD_a_9;
WN_a_9 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, WN_A_9_BIT));
@ -1134,6 +1176,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
break;
case 10: // Word type 10: Almanac for SVID3 (2/2) and GST-GPS conversion parameters
page_position_in_inav_subframe = 4;
IOD_a_10 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, IOD_A_10_BIT));
DLOG(INFO) << "IOD_a_10= " << IOD_a_10;
Omega0_10 = static_cast<double>(read_navigation_signed(data_jk_bits, OMEGA0_10_BIT));
@ -1172,6 +1215,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
DLOG(INFO) << "WN_0G_10= " << WN_0G_10;
flag_almanac_4 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_10 = data_jk_bits.to_string().substr(86, 42);
break;
case 16: // Word type 16: Reduced Clock and Ephemeris Data (CED) parameters
@ -1205,6 +1249,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 17: // Word type 17: FEC2 Reed-Solomon for CED
{
page_position_in_inav_subframe = 5;
if (enable_rs)
{
IODnav_LSB17 = read_octet_unsigned(data_jk_bits, RS_IODNAV_LSBS);
@ -1234,6 +1279,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 18: // Word type 18: FEC2 Reed-Solomon for CED
{
page_position_in_inav_subframe = 5;
if (enable_rs)
{
IODnav_LSB18 = read_octet_unsigned(data_jk_bits, RS_IODNAV_LSBS);
@ -1263,6 +1309,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 19: // Word type 19: FEC2 Reed-Solomon for CED
{
page_position_in_inav_subframe = 6;
if (enable_rs)
{
IODnav_LSB19 = read_octet_unsigned(data_jk_bits, RS_IODNAV_LSBS);
@ -1292,6 +1339,7 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
case 20: // Word type 20: FEC2 Reed-Solomon for CED
{
page_position_in_inav_subframe = 6;
if (enable_rs)
{
IODnav_LSB20 = read_octet_unsigned(data_jk_bits, RS_IODNAV_LSBS);
@ -1338,5 +1386,85 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
default:
break;
}
if (page_position_in_inav_subframe > 14 &&
page_position_in_inav_subframe != 255)
{
// something weird happened, reset
page_position_in_inav_subframe = 255;
nma_position_filled = std::array<int8_t, 15>{};
nma_msg.mack = std::array<uint32_t, 15>{};
nma_msg.hkroot = std::array<uint8_t, 15>{};
reset_osnma_nav_bits_adkd4();
reset_osnma_nav_bits_adkd0_12();
}
return page_number;
}
/**
* @brief Get data relevant for Galileo OSNMA
*
* \details This function retrieves various parameters and data to compose the OSNMA_msg.
* It fills the TOW and WN fields of the message and retrieves ephemeris, iono, and
*
* @return The OSNMA message
*/
OSNMA_msg Galileo_Inav_Message::get_osnma_msg()
{
nma_position_filled = std::array<int8_t, 15>{};
// Fill TOW and WN
nma_msg.WN_sf0 = WN_0;
int32_t TOW_sf0 = TOW_5 - 25;
if (TOW_sf0 < 0)
{
TOW_sf0 += 604800;
}
nma_msg.TOW_sf0 = static_cast<uint32_t>(TOW_sf0);
return nma_msg;
}
bool Galileo_Inav_Message::have_new_nma()
{
if (std::all_of(nma_position_filled.begin(), nma_position_filled.end(), [](int8_t element) { return element == 1; }))
{
return true;
}
else
{
return false;
}
}
std::string Galileo_Inav_Message::get_osnma_adkd_4_nav_bits()
{
nav_bits_adkd_4 = nav_bits_word_6 + nav_bits_word_10;
return nav_bits_adkd_4;
}
std::string Galileo_Inav_Message::get_osnma_adkd_0_12_nav_bits()
{
nav_bits_adkd_0_12 = nav_bits_word_1 + nav_bits_word_2 + nav_bits_word_3 + nav_bits_word_4 + nav_bits_word_5;
return nav_bits_adkd_0_12;
}
void Galileo_Inav_Message::reset_osnma_nav_bits_adkd0_12()
{
nav_bits_word_1 = "";
nav_bits_word_2 = "";
nav_bits_word_3 = "";
nav_bits_word_4 = "";
nav_bits_word_5 = "";
}
void Galileo_Inav_Message::reset_osnma_nav_bits_adkd4()
{
nav_bits_word_6 = "";
nav_bits_word_10 = "";
}

View File

@ -25,6 +25,7 @@
#include "galileo_iono.h"
#include "galileo_utc_model.h"
#include "gnss_sdr_make_unique.h" // for std::unique_ptr in C++11
#include <array>
#include <bitset>
#include <cstdint>
#include <memory>
@ -38,7 +39,19 @@ class ReedSolomon; // Forward declaration of the ReedSolomon class
* \{ */
/** \addtogroup System_Parameters
* \{ */
/*!
* \brief This class fills the OSNMA_msg structure with the data received from the telemetry blocks.
*/
class OSNMA_msg
{
public:
OSNMA_msg() = default;
std::array<uint32_t, 15> mack{};
std::array<uint8_t, 15> hkroot{};
uint32_t PRN{}; // PRN_a authentication data PRN
uint32_t WN_sf0{}; // Week number at the start of OSNMA subframe
uint32_t TOW_sf0{}; // TOW at the start of OSNMA subframe
};
/*!
* \brief This class handles the Galileo I/NAV Data message, as described in the
@ -57,13 +70,6 @@ public:
*/
void split_page(std::string page_string, int32_t flag_even_word);
/*
* \brief Takes in input Data_jk (128 bit) and split it in ephemeris parameters according ICD 4.3.5
*
* Takes in input Data_jk (128 bit) and split it in ephemeris parameters according ICD 4.3.5
*/
int32_t page_jk_decoder(const char* data_jk);
/*
* \brief Returns true if new Ephemeris has arrived. The flag is set to false when the function is executed
*/
@ -89,6 +95,11 @@ public:
*/
bool have_new_reduced_ced();
/*
* \brief Returns true if new NMA data have arrived. The flag is set to false when the function is executed
*/
bool have_new_nma();
/*
* \brief Returns a Galileo_Ephemeris object filled with the latest navigation data received
*/
@ -114,6 +125,31 @@ public:
*/
Galileo_Ephemeris get_reduced_ced() const;
/*
* \brief Returns an OSNMA_msg object filled with the latest NMA message received. Resets msg buffer.
*/
OSNMA_msg get_osnma_msg();
/*
* @brief Retrieves the OSNMA ADKD 4 NAV bits. Resets the string.
*/
std::string get_osnma_adkd_4_nav_bits();
/*
* @brief Resets the OSNMA ADKD 4 NAV bits.
*/
void reset_osnma_nav_bits_adkd4();
/*
* @brief Retrieves the OSNMA ADKD 0/12 NAV bits. Resets the string.
*/
std::string get_osnma_adkd_0_12_nav_bits();
/*
* @brief Resets the OSNMA ADKD 0/12 NAV bits.
*/
void reset_osnma_nav_bits_adkd0_12();
inline bool get_flag_CRC_test() const
{
return flag_CRC_test;
@ -210,6 +246,11 @@ public:
inline void init_PRN(uint32_t prn)
{
SV_ID_PRN_4 = prn;
nma_msg.PRN = prn;
nma_msg.mack = std::array<uint32_t, 15>{};
nma_msg.hkroot = std::array<uint8_t, 15>{};
page_position_in_inav_subframe = 255;
nma_position_filled = std::array<int8_t, 15>{};
}
/*
@ -242,7 +283,7 @@ private:
std::unique_ptr<ReedSolomon> rs; // The Reed-Solomon decoder
std::vector<int> inav_rs_pages; // Pages 1,2,3,4,17,18,19,20. Holds 1 if the page has arrived, 0 otherwise.
int32_t Page_type_time_stamp{};
int32_t page_jk_decoder(const char* data_jk);
int32_t IOD_ephemeris{};
// Word type 1: Ephemeris (1/4)
@ -394,6 +435,22 @@ private:
int32_t current_IODnav{};
// OSNMA
uint32_t mack_sis{};
uint8_t hkroot_sis{};
uint8_t page_position_in_inav_subframe{255};
std::array<int8_t, 15> nma_position_filled{};
OSNMA_msg nma_msg{};
std::string nav_bits_adkd_4{};
std::string nav_bits_word_6{};
std::string nav_bits_word_10{};
std::string nav_bits_adkd_0_12{};
std::string nav_bits_word_1{};
std::string nav_bits_word_2{};
std::string nav_bits_word_3{};
std::string nav_bits_word_4{};
std::string nav_bits_word_5{};
uint8_t IODnav_LSB17{};
uint8_t IODnav_LSB18{};
uint8_t IODnav_LSB19{};

View File

@ -0,0 +1,45 @@
/*!
* \file osnma_data.cc
* \brief Class for Galileo OSNMA data storage
* \author Carles Fernandez-Prades, 2020-2023 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "osnma_data.h"
uint32_t Tag::id_counter = 0;
uint32_t OSNMA_NavData::id_counter = 0;
bool OSNMA_NavData::add_nav_data(const std::string& nav_data)
{
if (nav_data.size() == 549)
{
d_ephemeris_iono = nav_data;
std::bitset<10> bits(nav_data.substr(0, 10));
IOD_nav = static_cast<uint8_t>(bits.to_ulong());
return true;
}
else if (nav_data.size() == 141)
{
d_utc = nav_data;
return true;
}
return false;
}
std::string OSNMA_NavData::get_utc_data() const
{
return d_utc;
}
std::string OSNMA_NavData::get_ephemeris_data() const
{
return d_ephemeris_iono;
}

View File

@ -0,0 +1,250 @@
/*!
* \file osnma_data.h
* \brief Class for Galileo OSNMA data storage
* \author Carles Fernandez-Prades, 2020-2023 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_OSNMA_DATA_H
#define GNSS_SDR_OSNMA_DATA_H
#include "galileo_inav_message.h" // for OSNMA_msg
#include <array>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup System_Parameters
* \{ */
class DSM_nma_header
{
public:
DSM_nma_header() = default;
uint8_t nmas{};
uint8_t cid{};
uint8_t cpks{};
bool reserved{};
};
class DSM_dsm_header
{
public:
DSM_dsm_header() = default;
uint8_t dsm_id{};
uint8_t dsm_block_id{};
};
class MACK_header
{
public:
MACK_header() = default;
uint64_t tag0{};
uint16_t macseq{};
uint8_t cop{};
};
class MACK_tag_info
{
public:
MACK_tag_info() = default;
uint8_t PRN_d{};
uint8_t ADKD{};
uint8_t cop{};
};
class MACK_tag_and_info
{
public:
MACK_tag_and_info() = default;
uint64_t tag; // C: 20-40 bits
MACK_tag_info tag_info;
uint32_t counter; // CTR
};
class DSM_PKR_message
{
public:
DSM_PKR_message() = default;
std::array<uint8_t, 128> itn{}; // bitset<1024>
std::vector<uint8_t> npk;
std::vector<uint8_t> p_dp;
uint8_t nb_dp{};
uint8_t mid{};
uint8_t npkt{};
uint8_t npktid{};
};
class DSM_KROOT_message
{
public:
DSM_KROOT_message() = default;
std::vector<uint8_t> kroot;
std::vector<uint8_t> ds;
std::vector<uint8_t> p_dk;
uint64_t alpha{};
uint16_t wn_k{};
uint8_t nb_dk{};
uint8_t pkid{};
uint8_t cidkr{};
uint8_t reserved1{};
uint8_t hf{};
uint8_t mf{};
uint8_t ks{}; // key size, in bits
uint8_t ts{};
uint8_t maclt{};
uint8_t reserved{};
uint8_t towh_k{};
bool verified{false};
};
class MACK_message
{
public:
MACK_message() = default;
MACK_header header;
std::vector<MACK_tag_and_info> tag_and_info;
std::vector<uint8_t> key;
uint32_t TOW; // TODO duplicated variable, also in OSNMA_NavData
uint32_t WN;
uint32_t PRNa;
};
class OSNMA_NavData
{
public:
OSNMA_NavData() : nav_data_id(id_counter++) {}
const uint32_t nav_data_id;
std::string get_utc_data() const;
std::string get_ephemeris_data() const;
uint32_t get_verified_bits() const { return verified_bits; }
uint32_t get_prn_d() const { return PRNd; }
uint32_t get_IOD_nav() const { return IOD_nav; }
uint32_t get_last_received_TOW() const { return d_last_received_TOW; }
uint32_t get_tow_sf0() const { return d_TOW_sf0; }
bool have_this_bits(std::string nav_data);
bool get_verified_status() const { return verified; }
bool add_nav_data(const std::string& nav_data);
void set_tow_sf0(int value) { d_TOW_sf0 = value; }
void set_ephemeris_data(std::string value) { d_ephemeris_iono = value; }
void set_utc_data(std::string value) { d_utc = value; }
void update_last_received_timestamp(uint32_t TOW);
void set_prn_d(uint32_t value) { PRNd = value; }
void set_last_received_TOW(uint32_t TOW) { d_last_received_TOW = TOW; };
void set_update_verified_bits(uint32_t morebits) { verified_bits += morebits; }
void set_verified_status(bool value) { verified = value; }
void set_IOD_nav(uint32_t value) { IOD_nav = value; }
private:
static uint32_t id_counter;
std::string d_ephemeris_iono{""};
std::string d_utc{""};
uint32_t d_TOW_sf0{0};
uint32_t d_last_received_TOW{0};
uint32_t PRNd{0};
uint32_t verified_bits{0};
uint32_t IOD_nav{0};
bool verified{false};
};
/*!
* \brief This class handles ONSMA data
* See https://www.gsc-europa.eu/sites/default/files/sites/all/files/Galileo_OSNMA_User_ICD_for_Test_Phase_v1.0.pdf
*/
class OSNMA_data
{
public:
OSNMA_data() = default;
DSM_nma_header d_nma_header;
DSM_dsm_header d_dsm_header;
DSM_PKR_message d_dsm_pkr_message;
DSM_KROOT_message d_dsm_kroot_message;
DSM_KROOT_message d_dsm_kroot_new_message;
MACK_message d_mack_message;
OSNMA_NavData d_nav_data;
};
class Tag
{
public:
enum e_verification_status
{
SUCCESS,
FAIL,
UNVERIFIED
};
Tag(const MACK_tag_and_info& MTI, uint32_t TOW, uint32_t WN, uint32_t PRNa, uint8_t CTR) // standard tag constructor, for tags within Tag&Info field
: tag_id(id_counter++),
TOW(TOW), // TODO missing for build_message WN for GST computation, CTR, NMAS, OSNMA_NavData missing
WN(WN),
PRNa(PRNa),
CTR(CTR),
status(UNVERIFIED),
received_tag(MTI.tag),
computed_tag(0),
PRN_d(MTI.tag_info.PRN_d),
ADKD(MTI.tag_info.ADKD),
cop(MTI.tag_info.cop),
skipped(0)
{
}
explicit Tag(const MACK_message& mack) // constructor for Tag0
: tag_id(id_counter++),
TOW(mack.TOW), // TODO missing for build_message WN for GST computation, CTR, NMAS, OSNMA_NavData missing
WN(mack.WN),
PRNa(mack.PRNa),
CTR(1),
status(UNVERIFIED),
received_tag(mack.header.tag0),
computed_tag(0),
PRN_d(mack.PRNa), // Tag0 are self-authenticating
ADKD(0),
cop(mack.header.cop),
skipped(0)
{
}
const uint32_t tag_id;
uint32_t static id_counter;
uint32_t TOW;
uint32_t WN;
uint32_t PRNa;
uint8_t CTR;
e_verification_status status;
uint64_t received_tag;
uint64_t computed_tag;
uint8_t PRN_d;
uint8_t ADKD;
uint8_t cop;
uint32_t skipped;
std::string nav_data;
};
/** \} */
/** \} */
#endif // GNSS_SDR_OSNMA_DATA_H

View File

@ -0,0 +1,228 @@
/*!
* \file osnma_dsm_reader.cc
* \brief Class for reading OSNMA DSM messages
* \author Carles Fernandez-Prades, 2023 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "osnma_dsm_reader.h"
#include "Galileo_OSNMA.h"
uint8_t OSNMA_DSM_Reader::get_nmas(uint8_t nma_header) const
{
return (nma_header & mask_nmas) >> 6;
}
uint8_t OSNMA_DSM_Reader::get_cid(uint8_t nma_header) const
{
return (nma_header & mask_cid) >> 4;
}
uint8_t OSNMA_DSM_Reader::get_cpks(uint8_t nma_header) const
{
return (nma_header & mask_cpks) >> 1;
}
bool OSNMA_DSM_Reader::get_nma_header_reserved(uint8_t nma_header) const
{
return (nma_header & mask_nma_header_reserved) != 0;
}
uint8_t OSNMA_DSM_Reader::get_dsm_id(uint8_t dsm_header) const
{
return (dsm_header & mask_dsm_id) >> 4;
}
uint8_t OSNMA_DSM_Reader::get_dsm_block_id(uint8_t dsm_header) const
{
return dsm_header & mask_dsm_block_id;
}
uint8_t OSNMA_DSM_Reader::get_number_blocks_index(uint8_t dsm_msg_0) const
{
return (dsm_msg_0 & mask_dsm_number_blocks) >> 4;
}
uint8_t OSNMA_DSM_Reader::get_pkid(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[0] & mask_dsm_pkid);
}
uint8_t OSNMA_DSM_Reader::get_cidkr(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[1] & mask_dsm_cidkr) >> 6;
}
uint8_t OSNMA_DSM_Reader::get_dsm_reserved1(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[1] & mask_dsm_reserved1) >> 4;
}
uint8_t OSNMA_DSM_Reader::get_hf(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[1] & mask_dsm_hf) >> 2;
}
uint8_t OSNMA_DSM_Reader::get_mf(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[1] & mask_dsm_mf);
}
uint8_t OSNMA_DSM_Reader::get_ks(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[2] & mask_dsm_ks) >> 4;
}
uint8_t OSNMA_DSM_Reader::get_ts(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[2] & mask_dsm_ts);
}
uint8_t OSNMA_DSM_Reader::get_maclt(const std::vector<uint8_t>& dsm_msg) const
{
return dsm_msg[3];
}
uint8_t OSNMA_DSM_Reader::get_dsm_reserved(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[4] & mask_dsm_reserved) >> 4;
}
uint16_t OSNMA_DSM_Reader::get_wn_k(const std::vector<uint8_t>& dsm_msg) const
{
return (static_cast<uint16_t>((dsm_msg[4] & mask_dsm_wk_k_msbyte) << 8) + static_cast<uint16_t>(dsm_msg[5]));
}
uint8_t OSNMA_DSM_Reader::get_towh_k(const std::vector<uint8_t>& dsm_msg) const
{
return dsm_msg[6];
}
uint64_t OSNMA_DSM_Reader::get_alpha(const std::vector<uint8_t>& dsm_msg) const
{
uint64_t alpha = (static_cast<uint64_t>(dsm_msg[7]) << 40) +
(static_cast<uint64_t>(dsm_msg[8]) << 32) +
(static_cast<uint64_t>(dsm_msg[9]) << 24) +
(static_cast<uint64_t>(dsm_msg[10]) << 16) +
(static_cast<uint64_t>(dsm_msg[11]) << 8) +
(static_cast<uint64_t>(dsm_msg[12]));
return alpha;
}
uint16_t OSNMA_DSM_Reader::get_l_dk_bits(uint8_t nb_dk) const
{
const auto it = OSNMA_TABLE_7.find(nb_dk);
if (it != OSNMA_TABLE_7.cend())
{
return it->second.second;
}
return 0;
}
uint16_t OSNMA_DSM_Reader::get_lk_bits(uint8_t ks) const
{
const auto it = OSNMA_TABLE_10.find(ks);
if (it != OSNMA_TABLE_10.cend())
{
return it->second;
}
return 0;
}
std::vector<uint8_t> OSNMA_DSM_Reader::get_kroot(const std::vector<uint8_t>& dsm_msg, uint16_t bytes_lk) const
{
std::vector<uint8_t> kroot = std::vector<uint8_t>(bytes_lk, 0);
if (dsm_msg.size() > static_cast<uint64_t>(13 + bytes_lk))
{
for (uint16_t k = 0; k < bytes_lk; k++)
{
kroot[k] = dsm_msg[13 + k];
}
}
return kroot;
}
std::string OSNMA_DSM_Reader::get_hash_function(uint8_t hf) const
{
std::string hash_;
const auto it = OSNMA_TABLE_8.find(hf);
if (it != OSNMA_TABLE_8.cend())
{
hash_ = it->second;
}
return hash_;
}
uint8_t OSNMA_DSM_Reader::get_mid(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[0] & mask_dsm_mid);
}
uint8_t OSNMA_DSM_Reader::get_npkt(const std::vector<uint8_t>& dsm_msg) const
{
return ((dsm_msg[129] & mask_dsm_npkt) >> 4);
}
uint8_t OSNMA_DSM_Reader::get_npktid(const std::vector<uint8_t>& dsm_msg) const
{
return (dsm_msg[129] & mask_dsm_npktid);
}
std::string OSNMA_DSM_Reader::get_nmas_status(uint8_t nmas) const
{
std::string status_;
const auto it = OSNMA_TABLE_1.find(nmas);
if (it != OSNMA_TABLE_1.cend())
{
status_ = it->second;
}
return status_;
}
std::string OSNMA_DSM_Reader::get_cpks_status(uint8_t cpks) const
{
std::string status_;
const auto it = OSNMA_TABLE_2.find(cpks);
if (it != OSNMA_TABLE_2.cend())
{
status_ = it->second;
}
return status_;
}

View File

@ -0,0 +1,89 @@
/*!
* \file osnma_dsm_reader.h
* \brief Class for reading OSNMA DSM messages
* \author Carles Fernandez-Prades, 2023 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2023 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_OSNMA_DSM_READER_H
#define GNSS_SDR_OSNMA_DSM_READER_H
#include <cstdint>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup System_Parameters
* \{ */
class OSNMA_DSM_Reader
{
public:
OSNMA_DSM_Reader() = default;
uint8_t get_nmas(uint8_t nma_header) const;
uint8_t get_cid(uint8_t nma_header) const;
uint8_t get_cpks(uint8_t nma_header) const;
bool get_nma_header_reserved(uint8_t nma_header) const;
uint8_t get_dsm_id(uint8_t dsm_header) const;
uint8_t get_dsm_block_id(uint8_t dsm_header) const;
uint8_t get_number_blocks_index(uint8_t dsm_msg_0) const;
uint8_t get_pkid(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_cidkr(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_dsm_reserved1(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_hf(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_mf(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_ks(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_ts(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_maclt(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_dsm_reserved(const std::vector<uint8_t>& dsm_msg) const;
uint16_t get_wn_k(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_towh_k(const std::vector<uint8_t>& dsm_msg) const;
uint64_t get_alpha(const std::vector<uint8_t>& dsm_msg) const;
uint16_t get_l_dk_bits(uint8_t nb_dk) const;
uint16_t get_lk_bits(uint8_t ks) const;
std::vector<uint8_t> get_kroot(const std::vector<uint8_t>& dsm_msg, uint16_t bytes_lk) const;
std::string get_hash_function(uint8_t hf) const;
std::string get_nmas_status(uint8_t nmas) const;
std::string get_cpks_status(uint8_t cpks) const;
uint8_t get_mid(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_npkt(const std::vector<uint8_t>& dsm_msg) const;
uint8_t get_npktid(const std::vector<uint8_t>& dsm_msg) const;
private:
static constexpr std::uint8_t mask_nmas{0xC0};
static constexpr std::uint8_t mask_cid{0x30};
static constexpr std::uint8_t mask_cpks{0x0E};
static constexpr std::uint8_t mask_nma_header_reserved{0x01};
static constexpr std::uint8_t mask_dsm_id{0xF0};
static constexpr std::uint8_t mask_dsm_block_id{0x0F};
static constexpr std::uint8_t mask_dsm_number_blocks{0xF0};
static constexpr std::uint8_t mask_dsm_pkid{0x0F};
static constexpr std::uint8_t mask_dsm_cidkr{0xC0};
static constexpr std::uint8_t mask_dsm_reserved1{0x30};
static constexpr std::uint8_t mask_dsm_hf{0x0C};
static constexpr std::uint8_t mask_dsm_mf{0x03};
static constexpr std::uint8_t mask_dsm_ks{0xF0};
static constexpr std::uint8_t mask_dsm_ts{0x0F};
static constexpr std::uint8_t mask_dsm_reserved{0xF0};
static constexpr std::uint8_t mask_dsm_wk_k_msbyte{0x0F};
static constexpr std::uint8_t mask_dsm_mid{0x0F};
static constexpr std::uint8_t mask_dsm_npkt{0xF0};
static constexpr std::uint8_t mask_dsm_npktid{0x0F};
};
/** \} */
/** \} */
#endif // GNSS_SDR_OSNMA_DSM_READER_H

View File

@ -7,6 +7,7 @@
add_subdirectory(unit-tests/signal-processing-blocks/libs)
add_subdirectory(system-tests/libs)
include_directories("${GNSSSDR_SOURCE_DIR}/src/core/receiver")
include(XcodeRemoveWarningDuplicates)
@ -549,6 +550,18 @@ if(ENABLE_UNIT_TESTING_EXTRA)
EXPECTED_HASH MD5=066d0d8434a8bc81e161778b7c34cc07
)
endif()
if(NOT EXISTS ${GNSSSDR_BINARY_DIR}/thirdparty/osnma_tests/Test_vectors.zip)
message(STATUS "Downloading file: Test_vectors.zip")
file(DOWNLOAD https://www.gsc-europa.eu/sites/default/files/sites/all/files/Test_vectors.zip
${GNSSSDR_BINARY_DIR}/thirdparty/osnma_tests/Test_vectors.zip
SHOW_PROGRESS
EXPECTED_HASH MD5=8158aebee735652c9398e5bb6d944364
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${GNSSSDR_BINARY_DIR}/thirdparty/osnma_tests/Test_vectors.zip
WORKING_DIRECTORY ${GNSSSDR_BINARY_DIR}/thirdparty/osnma_tests/
)
endif()
message(STATUS "Done.")
if(ENABLE_INSTALL_TESTS)
install(FILES ${GNSSSDR_BINARY_DIR}/thirdparty/signal_samples/gps_l2c_m_prn7_5msps.dat DESTINATION share/gnss-sdr/signal_samples)
@ -651,6 +664,7 @@ if(ENABLE_UNIT_TESTING)
if(GNSSTK_OLDER_THAN_9)
target_compile_definitions(run_tests PRIVATE -DGNSSTK_OLDER_THAN_9=1)
endif()
target_compile_definitions(run_tests PRIVATE -DBASE_OSNMA_TEST_VECTORS="${GNSSSDR_BINARY_DIR}/thirdparty/osnma_tests/Test_vectors/")
endif()
xcode_remove_warning_duplicates(run_tests)
if(ENABLE_STRIP)

View File

@ -104,11 +104,18 @@ macro(add_benchmark)
)
endmacro()
add_benchmark(benchmark_copy)
add_benchmark(benchmark_preamble core_system_parameters)
add_benchmark(benchmark_detector core_system_parameters)
add_benchmark(benchmark_reed_solomon core_system_parameters)
set(EXTRA_BENCHMARK_DEPENDENCIES "")
if(ENABLE_GLOG_AND_GFLAGS)
set(EXTRA_BENCHMARK_DEPENDENCIES "Gflags::gflags;Glog::glog")
endif()
add_benchmark(benchmark_atan2 Gnuradio::runtime)
add_benchmark(benchmark_copy)
add_benchmark(benchmark_crypto core_libs Boost::headers ${EXTRA_BENCHMARK_DEPENDENCIES})
add_benchmark(benchmark_osnma core_libs Boost::headers ${EXTRA_BENCHMARK_DEPENDENCIES})
add_benchmark(benchmark_detector core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
add_benchmark(benchmark_preamble core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
add_benchmark(benchmark_reed_solomon core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
if(has_std_plus_void)
target_compile_definitions(benchmark_detector PRIVATE -DCOMPILER_HAS_STD_PLUS_VOID=1)

View File

@ -0,0 +1,184 @@
/*!
* \file benchmark_crypto.cc
* \brief Benchmarks for cryptographic functions
* \author Carles Fernandez-Prades, 2024. cfernandez(at)cttc.es
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "gnss_crypto.h"
#include <benchmark/benchmark.h>
#include <memory>
void bm_SHA_256(benchmark::State& state)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A};
while (state.KeepRunning())
{
std::vector<uint8_t> output = d_crypto->compute_SHA_256(message);
}
}
void bm_SHA3_256(benchmark::State& state)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A};
while (state.KeepRunning())
{
std::vector<uint8_t> output = d_crypto->compute_SHA3_256(message);
}
}
void bm_HMAC_SHA_256(benchmark::State& state)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> key = {
0x24, 0x24, 0x3B, 0x76, 0xF9, 0x14, 0xB1, 0xA7,
0x7D, 0x48, 0xE7, 0xF1, 0x48, 0x0C, 0xC2, 0x98,
0xEB, 0x62, 0x3E, 0x95, 0x6B, 0x2B, 0xCE, 0xA3,
0xB4, 0xD4, 0xDB, 0x31, 0xEE, 0x96, 0xAB, 0xFA};
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A};
while (state.KeepRunning())
{
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
}
}
void bm_CMAC_AES(benchmark::State& state)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> key = {
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C};
std::vector<uint8_t> message{
0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96,
0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A};
while (state.KeepRunning())
{
std::vector<uint8_t> output = d_crypto->compute_CMAC_AES(key, message);
}
}
void bm_verify_ecdsa_p256(benchmark::State& state)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
// RG example - import crt certificate
std::vector<uint8_t> message = {
0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF,
0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04,
0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF};
// ECDSA P-256 signature, raw format
std::vector<uint8_t> signature = {
0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20,
0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F,
0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B,
0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D,
0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22,
0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87,
0x28, 0xC1, 0x77, 0xFB};
// compressed ECDSA P-256 format
std::vector<uint8_t> publicKey = {
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
0x79, 0x80, 0xEA};
d_crypto->set_public_key(publicKey);
while (state.KeepRunning())
{
bool output = d_crypto->verify_signature_ecdsa_p256(message, signature);
if (output)
{
// Avoid unused-but-set-variable warning
}
}
}
void bm_verify_ecdsa_p521(benchmark::State& state)
{
std::unique_ptr<Gnss_Crypto> d_crypto = std::make_unique<Gnss_Crypto>();
// Message to be verified
std::vector<uint8_t> message = {
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // "Hello world\n"
// Public key in compressed X format
std::vector<uint8_t> publicKey = {
0x03, 0x00, 0x28, 0x35, 0xBB, 0xE9, 0x24, 0x59, 0x4E, 0xF0,
0xE3, 0xA2, 0xDB, 0xC0, 0x49, 0x30, 0x60, 0x7C, 0x61, 0x90,
0xE4, 0x03, 0xE0, 0xC7, 0xB8, 0xC2, 0x62, 0x37, 0xF7, 0x58,
0x56, 0xBE, 0x63, 0x5C, 0x97, 0xF7, 0x53, 0x64, 0x7E, 0xE1,
0x0C, 0x07, 0xD3, 0x97, 0x8D, 0x58, 0x46, 0xFD, 0x6E, 0x06,
0x44, 0x01, 0xA7, 0xAA, 0xC4, 0x95, 0x13, 0x5D, 0xC9, 0x77,
0x26, 0xE9, 0xF8, 0x72, 0x0C, 0xD3, 0x88};
// ECDSA P-521 signature, raw format
std::vector<uint8_t> signature = {
0x01, 0x5C, 0x23, 0xC0, 0xBE, 0xAD, 0x1E, 0x44, 0x60, 0xD4,
0xE0, 0x81, 0x38, 0xF2, 0xBA, 0xF5, 0xB5, 0x37, 0x5A, 0x34,
0xB5, 0xCA, 0x6B, 0xC8, 0x0F, 0xCD, 0x75, 0x1D, 0x5E, 0xC0,
0x8A, 0xD3, 0xD7, 0x79, 0xA7, 0xC1, 0xB8, 0xA2, 0xC6, 0xEA,
0x5A, 0x7D, 0x60, 0x66, 0x50, 0x97, 0x37, 0x6C, 0xF9, 0x0A,
0xF6, 0x3D, 0x77, 0x9A, 0xE2, 0x19, 0xF7, 0xF9, 0xDD, 0x52,
0xC4, 0x0F, 0x98, 0xAA, 0xA2, 0xA4, 0x01, 0xC9, 0x41, 0x0B,
0xD0, 0x25, 0xDD, 0xC9, 0x7C, 0x3F, 0x70, 0x32, 0x23, 0xCF,
0xFE, 0x37, 0x67, 0x3A, 0xBC, 0x0B, 0x76, 0x16, 0x82, 0x83,
0x27, 0x3D, 0x1D, 0x19, 0x15, 0x78, 0x08, 0x2B, 0xD4, 0xA7,
0xC2, 0x0F, 0x11, 0xF4, 0xDD, 0xE5, 0x5A, 0x5D, 0x04, 0x8D,
0x6D, 0x5E, 0xC4, 0x1F, 0x54, 0x44, 0xA9, 0x13, 0x34, 0x71,
0x0F, 0xF7, 0x57, 0x9A, 0x9F, 0x2E, 0xF4, 0x97, 0x7D, 0xAE,
0x28, 0xEF};
d_crypto->set_public_key(publicKey);
while (state.KeepRunning())
{
bool output = d_crypto->verify_signature_ecdsa_p521(message, signature);
if (output)
{
// Avoid unused-but-set-variable warning
}
}
}
BENCHMARK(bm_SHA_256);
BENCHMARK(bm_SHA3_256);
BENCHMARK(bm_HMAC_SHA_256);
BENCHMARK(bm_CMAC_AES);
BENCHMARK(bm_verify_ecdsa_p256);
BENCHMARK(bm_verify_ecdsa_p521);
BENCHMARK_MAIN();

View File

@ -0,0 +1,127 @@
/*!
* \file benchmark_osnma.cc
* \brief Benchmarks for osnma functions
* \author Carles Fernandez-Prades, 2024. cfernandez(at)cttc.es
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "Galileo_OSNMA.h"
#include "gnss_crypto.h"
#include "osnma_helper.h"
#include "osnma_msg_receiver.h"
#include <benchmark/benchmark.h>
#include <memory>
void bm_verify_public_key(benchmark::State& state)
{
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
Osnma_Helper helper;
osnma->set_merkle_root(helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D"));
DSM_PKR_message dsm_pkr_message;
dsm_pkr_message.npkt = 0x01;
dsm_pkr_message.npktid = 0x2;
dsm_pkr_message.mid = 0x01;
std::vector<uint8_t> vec = helper.convert_from_hex_string(
"7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06"
"956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7"
"407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1"
"24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87");
std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin());
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
while (state.KeepRunning())
{
osnma->verify_dsm_pkr(dsm_pkr_message);
}
}
void bm_verify_tesla_key(benchmark::State& state)
{
// osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
// Osnma_Helper helper;
// osnma->d_tesla_key_verified = false;
// osnma->d_osnma_data.d_dsm_kroot_message.kroot = {0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; // Kroot, TOW 345570 GST_0 - 30
// osnma->d_osnma_data.d_dsm_kroot_message.ks = 4; // TABLE 10 --> 128 bits
// osnma->d_osnma_data.d_dsm_kroot_message.alpha = 0x610BDF26D77B;
// osnma->d_GST_SIS = (1248 & 0x00000FFF) << 20 | (345630 & 0x000FFFFF);
// osnma->d_GST_0 = ((1248 & 0x00000FFF) << 20 | (345600 & 0x000FFFFF)); // applicable time (GST_Kroot + 30)
// osnma->d_GST_Sf = osnma->d_GST_0 + 30 * std::floor((osnma->d_GST_SIS - osnma->d_GST_0) / 30); // Eq. 3 R.G.
//
// osnma->d_tesla_keys.insert((std::pair<uint32_t, std::vector<uint8_t>>(345600, {0xEF, 0xF9, 0x99, 0x04, 0x0E, 0x19, 0xB5, 0x70, 0x83, 0x50, 0x60, 0xBE, 0xBD, 0x23, 0xED, 0x92}))); // K1, not needed, just for reference.
// std::vector<uint8_t> key = {0x2D, 0xC3, 0xA3, 0xCD, 0xB1, 0x17, 0xFA, 0xAD, 0xB8, 0x3B, 0x5F, 0x0B, 0x6F, 0xEA, 0x88, 0xEB}; // K2
// uint32_t TOW = 345630;
//
// while (state.KeepRunning())
// {
// osnma->verify_tesla_key(key, TOW);
// }
}
void bm_verify_tesla_key_24h(benchmark::State& state)
{
// TODO - copy of normal tesla verification but with 2800 steps instead of only two (max Kroot time is 1 day as per spec.)
}
void bm_tag_verification(benchmark::State& state)
{
// osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
// Osnma_Helper helper;
// uint32_t TOW_Tag0 = 345660;
// uint32_t TOW_NavData = TOW_Tag0 - 30;
// uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
// uint32_t WN = 1248;
// uint32_t PRNa = 2;
// uint8_t CTR = 1;
//
// osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
// osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
// osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
// osnma->d_nav_data_manager->add_navigation_data(
// "000011101001011001000100000101000111010110100100100101100000000000"
// "011101101011001111101110101010000001010000011011111100000011101011"
// "011100101101011010101011011011001001110111101011110110111111001111"
// "001000011111101110011000111111110111111010000011101011111111110000"
// "110111000000100000001110110000110110001110000100001110101100010100"
// "110100010001000110001110011010110000111010000010000000000001101000"
// "000000000011100101100100010000000000000110110100110001111100000000"
// "000000100110100000000101010010100000001011000010001001100000011111"
// "110111111111000000000",
// PRNa, TOW_NavData);
// osnma->d_osnma_data.d_nma_header.nmas = 0b10;
//
// MACK_tag_and_info MTI;
// MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
// MTI.tag_info.PRN_d = 0x02;
// MTI.tag_info.ADKD = 0x00;
// MTI.tag_info.cop = 0x0F;
// Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
//
// while (state.KeepRunning())
// {
// osnma->verify_tag(t0);
// }
}
void bm_kroot_verification(benchmark::State& state)
{
// TODO - this is essentially the signature verification, maybe could implement it for comparison purposes
}
BENCHMARK(bm_verify_public_key);
BENCHMARK(bm_verify_tesla_key);
BENCHMARK(bm_verify_tesla_key_24h);
BENCHMARK(bm_tag_verification);
BENCHMARK(bm_kroot_verification);
BENCHMARK_MAIN();

View File

@ -18,6 +18,7 @@
#include "concurrent_map.h"
#include "concurrent_queue.h"
#include "gnss_sdr_flags.h"
#include "gps_acq_assist.h"
#include <gtest/gtest.h>
#include <fstream>
@ -36,7 +37,6 @@ using namespace google;
DECLARE_string(log_dir);
#endif
#else
#include "gnss_sdr_flags.h"
#include <absl/flags/flag.h>
#include <absl/flags/parse.h>
#include <absl/log/flags.h>
@ -44,7 +44,6 @@ DECLARE_string(log_dir);
#include <absl/log/log.h>
#include <absl/log/log_sink.h>
#include <absl/log/log_sink_registry.h>
class TestLogSink : public absl::LogSink
{
public:
@ -78,15 +77,14 @@ Concurrent_Map<Gps_Acq_Assist> global_gps_acq_assist_map;
int main(int argc, char **argv)
{
#if USE_GLOG_AND_GFLAGS
gflags::ParseCommandLineFlags(&argc, &argv, true);
try
{
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
}
catch (...)
{
} // catch the "testing::internal::<unnamed>::ClassUniqueToAlwaysTrue" from gtest
google::InitGoogleLogging(argv[0]);
#else
absl::ParseCommandLine(argc, argv);
try

View File

@ -112,6 +112,8 @@ private:
#include "unit-tests/signal-processing-blocks/adapter/adapter_test.cc"
#include "unit-tests/signal-processing-blocks/adapter/pass_through_test.cc"
#include "unit-tests/signal-processing-blocks/libs/item_type_helpers_test.cc"
#include "unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc"
#include "unit-tests/signal-processing-blocks/osnma/osnma_msg_receiver_test.cc"
#include "unit-tests/signal-processing-blocks/pvt/geohash_test.cc"
#include "unit-tests/signal-processing-blocks/pvt/nmea_printer_test.cc"
#include "unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc"
@ -181,6 +183,7 @@ private:
#ifndef EXCLUDE_TESTS_REQUIRING_BINARIES
#include "unit-tests/signal-processing-blocks/acquisition/glonass_l1_ca_pcps_acquisition_test.cc"
#include "unit-tests/signal-processing-blocks/acquisition/gps_l2_m_pcps_acquisition_test.cc"
#include "unit-tests/signal-processing-blocks/osnma/osnma_test_vectors.cc"
#include "unit-tests/signal-processing-blocks/tracking/gps_l2_m_dll_pll_tracking_test.cc"
#endif
// #include "unit-tests/signal-processing-blocks/pvt/rtklib_solver_test.cc"

View File

@ -0,0 +1,347 @@
/*!
* \file gnss_crypto_test.cc
* \brief Tests for the Gnss_Crypto class.
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "gnss_crypto.h"
#include "gnss_sdr_filesystem.h"
#include "gnss_sdr_make_unique.h"
#include <gtest/gtest.h>
#include <fstream>
#include <iterator>
#include <utility>
class GnssCryptoTest : public ::testing::Test
{
};
TEST(GnssCryptoTest, VerifyPubKeyImport)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
// Input taken from RG 1.3 A7.1
// compressed ECDSA P-256 format
std::vector<uint8_t> publicKey = {
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
0x79, 0x80, 0xEA};
ASSERT_FALSE(d_crypto->have_public_key());
d_crypto->set_public_key(publicKey);
ASSERT_TRUE(d_crypto->have_public_key());
}
TEST(GnssCryptoTest, VerifyPublicKeyStorage)
{
const std::string f1("./osnma_test_file1.pem");
const std::string f2("./osnma_test_file2.pem");
const std::string f3("./osnma_test_file3.pem");
// Input taken from RG 1.3 A7.1
// compressed ECDSA P-256 format
std::vector<uint8_t> publicKey = {
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
0x79, 0x80, 0xEA};
auto d_crypto = std::make_unique<Gnss_Crypto>();
ASSERT_FALSE(d_crypto->have_public_key());
ASSERT_TRUE(d_crypto->get_public_key_type() == "Unknown");
d_crypto->set_public_key(publicKey);
ASSERT_TRUE(d_crypto->have_public_key());
ASSERT_TRUE(d_crypto->store_public_key(f1));
auto d_crypto2 = std::make_unique<Gnss_Crypto>(f1, "");
ASSERT_TRUE(d_crypto2->have_public_key());
ASSERT_TRUE(d_crypto2->get_public_key_type() == "ECDSA P-256");
ASSERT_TRUE(d_crypto2->store_public_key(f2));
std::ifstream t(f1);
std::string content_file((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
std::ifstream t2(f2);
std::string content_file2((std::istreambuf_iterator<char>(t2)), std::istreambuf_iterator<char>());
ASSERT_EQ(content_file, content_file2);
// P-521 Public key in compressed X format
std::vector<uint8_t> publicKey_P521 = {
0x03, 0x00, 0x28, 0x35, 0xBB, 0xE9, 0x24, 0x59, 0x4E, 0xF0,
0xE3, 0xA2, 0xDB, 0xC0, 0x49, 0x30, 0x60, 0x7C, 0x61, 0x90,
0xE4, 0x03, 0xE0, 0xC7, 0xB8, 0xC2, 0x62, 0x37, 0xF7, 0x58,
0x56, 0xBE, 0x63, 0x5C, 0x97, 0xF7, 0x53, 0x64, 0x7E, 0xE1,
0x0C, 0x07, 0xD3, 0x97, 0x8D, 0x58, 0x46, 0xFD, 0x6E, 0x06,
0x44, 0x01, 0xA7, 0xAA, 0xC4, 0x95, 0x13, 0x5D, 0xC9, 0x77,
0x26, 0xE9, 0xF8, 0x72, 0x0C, 0xD3, 0x88};
d_crypto->set_public_key(publicKey_P521);
ASSERT_TRUE(d_crypto->have_public_key());
ASSERT_TRUE(d_crypto->get_public_key_type() == "ECDSA P-521");
ASSERT_TRUE(d_crypto->store_public_key(f3));
auto d_crypto3 = std::make_unique<Gnss_Crypto>(f3, "");
ASSERT_TRUE(d_crypto3->have_public_key());
ASSERT_TRUE(d_crypto3->get_public_key_type() == "ECDSA P-521");
std::vector<uint8_t> wrong_publicKey = {
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
0x63, 0xFF, 0xA0};
auto d_crypto4 = std::make_unique<Gnss_Crypto>();
d_crypto4->set_public_key(wrong_publicKey);
ASSERT_FALSE(d_crypto4->have_public_key());
ASSERT_TRUE(d_crypto4->get_public_key_type() == "Unknown");
errorlib::error_code ec;
ASSERT_TRUE(fs::remove(fs::path(f1), ec));
ASSERT_TRUE(fs::remove(fs::path(f2), ec));
ASSERT_TRUE(fs::remove(fs::path(f3), ec));
}
TEST(GnssCryptoTest, TestComputeSHA_256)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
std::vector<uint8_t> expected_output = {
0x18, 0x94, 0xA1, 0x9C, 0x85, 0xBA, 0x15, 0x3A, 0xCB, 0xF7,
0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04,
0xB2, 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7,
0xC4, 0x8F};
std::vector<uint8_t> output = d_crypto->compute_SHA_256(message);
ASSERT_EQ(expected_output, output);
}
TEST(GnssCryptoTest, TestComputeSHA3_256)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
std::vector<uint8_t> expected_output = {
0xCC, 0xB8, 0xF9, 0x23, 0x5F, 0x4A, 0x93, 0x2C, 0xA0, 0xAB,
0xBB, 0x2C, 0x24, 0x36, 0x72, 0x5E, 0x2E, 0x8D, 0xC7, 0x5B,
0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6,
0x3B, 0x3F};
std::vector<uint8_t> output = d_crypto->compute_SHA3_256(message);
ASSERT_EQ(expected_output, output);
}
// Unit test for computeHMAC_SHA_256 function.
TEST(GnssCryptoTest, TestComputeHMACSHA256)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> key = {
0x24, 0x24, 0x3B, 0x76, 0xF9, 0x14, 0xB1, 0xA7,
0x7D, 0x48, 0xE7, 0xF1, 0x48, 0x0C, 0xC2, 0x98,
0xEB, 0x62, 0x3E, 0x95, 0x6B, 0x2B, 0xCE, 0xA3,
0xB4, 0xD4, 0xDB, 0x31, 0xEE, 0x96, 0xAB, 0xFA};
std::vector<uint8_t> message{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
std::vector<uint8_t> expected_output = {
0xC3, 0x51, 0xF6, 0xFD, 0xDD, 0xC9, 0x8B, 0x41,
0xD6, 0xF4, 0x77, 0x6D, 0xAC, 0xE8, 0xE0, 0x14,
0xB2, 0x7A, 0xCC, 0x22, 0x00, 0xAA, 0xD2, 0x37,
0xD0, 0x79, 0x06, 0x12, 0x83, 0x40, 0xB7, 0xA6};
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
ASSERT_EQ(expected_output, output);
}
TEST(GnssCryptoTest, TestComputeHMACSHA256_m0)
{
// key and message generated from RG A.6.5.1
auto d_crypto = std::make_unique<Gnss_Crypto>();
// RG K4 @ 345690
std::vector<uint8_t> key = {
0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6,
0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};
// m0
std::vector<uint8_t> message = {
0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5,
0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB,
0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37,
0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0,
0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07,
0xF7, 0xFC, 0x00};
std::vector<uint8_t> expected_output = {
0xE3, 0x7B, 0xC4, 0xF8, 0x58, 0xAE, 0x1E, 0x5C,
0xFD, 0xC4, 0x6F, 0x05, 0x4B, 0x1F, 0x47, 0xB9,
0xD2, 0xEA, 0x61, 0xE1, 0xEF, 0x09, 0x11, 0x5C,
0xFE, 0x70, 0x68, 0x52, 0xBF, 0xF2, 0x3A, 0x83};
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
ASSERT_EQ(expected_output, output);
}
TEST(GnssCryptoTest, TestComputeHMACSHA256_adkd4)
{
// key and message generated from RG A.6.5.2
auto d_crypto = std::make_unique<Gnss_Crypto>();
// RG K4 @ 345690
std::vector<uint8_t> key = {
0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6,
0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};
std::vector<uint8_t> message = {
0x02, 0x02, 0x4E, 0x05, 0x46, 0x3C, 0x03, 0xBF,
0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x44, 0x92,
0x38, 0x22, 0x78, 0x97, 0xFD, 0xEF, 0xF9, 0x30,
0x40};
std::vector<uint8_t> expected_output = {
0x7B, 0xB2, 0x38, 0xC8, 0x83, 0xC0, 0x6A, 0x2B,
0x50, 0x8F, 0xE6, 0x3F, 0xB7, 0xF4, 0xF5, 0x4D,
0x44, 0xAB, 0xEE, 0x4D, 0xCE, 0xB9, 0x3D, 0xCF,
0x65, 0xCB, 0x3A, 0x5B, 0x81, 0x4A, 0x34, 0xE9};
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
ASSERT_EQ(expected_output, output);
}
TEST(GnssCryptoTest, TestComputeCMAC_AES)
{
// Tests vectors from https://datatracker.ietf.org/doc/html/rfc4493#appendix-A
auto d_crypto = std::make_unique<Gnss_Crypto>();
std::vector<uint8_t> key = {
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C};
std::vector<uint8_t> message{
0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96,
0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A};
std::vector<uint8_t> expected_output = {
0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44,
0xF7, 0x9B, 0xDD, 0x9D, 0xD0, 0x4A, 0x28, 0x7C};
std::vector<uint8_t> output = d_crypto->compute_CMAC_AES(key, message);
ASSERT_EQ(expected_output, output);
}
TEST(GnssCryptoTest, VerifySignatureP256)
{
auto d_crypto = std::make_unique<Gnss_Crypto>();
// RG example - import crt certificate
std::vector<uint8_t> message = {
0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF,
0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04,
0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF};
// ECDSA P-256 signature, raw format
std::vector<uint8_t> signature = {
0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20,
0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F,
0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B,
0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D,
0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22,
0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87,
0x28, 0xC1, 0x77, 0xFB};
// Input taken from RG 1.3 A7.1
// compressed ECDSA P-256 format
std::vector<uint8_t> publicKey = {
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
0x79, 0x80, 0xEA};
d_crypto->set_public_key(publicKey);
ASSERT_TRUE(d_crypto->verify_signature_ecdsa_p256(message, signature));
std::vector<uint8_t> wrong_signature = std::move(signature);
wrong_signature[1] = 1;
ASSERT_FALSE(d_crypto->verify_signature_ecdsa_p256(message, wrong_signature));
}
TEST(GnssCryptoTest, VerifySignatureP521)
{
std::unique_ptr<Gnss_Crypto> d_crypto = std::make_unique<Gnss_Crypto>();
// Message to be verified
std::vector<uint8_t> message = {
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // "Hello world\n"
// Public key in compressed X format
std::vector<uint8_t> publicKey = {
0x03, 0x00, 0x28, 0x35, 0xBB, 0xE9, 0x24, 0x59, 0x4E, 0xF0,
0xE3, 0xA2, 0xDB, 0xC0, 0x49, 0x30, 0x60, 0x7C, 0x61, 0x90,
0xE4, 0x03, 0xE0, 0xC7, 0xB8, 0xC2, 0x62, 0x37, 0xF7, 0x58,
0x56, 0xBE, 0x63, 0x5C, 0x97, 0xF7, 0x53, 0x64, 0x7E, 0xE1,
0x0C, 0x07, 0xD3, 0x97, 0x8D, 0x58, 0x46, 0xFD, 0x6E, 0x06,
0x44, 0x01, 0xA7, 0xAA, 0xC4, 0x95, 0x13, 0x5D, 0xC9, 0x77,
0x26, 0xE9, 0xF8, 0x72, 0x0C, 0xD3, 0x88};
// ECDSA P-521 signature, raw format
std::vector<uint8_t> signature = {
0x01, 0x5C, 0x23, 0xC0, 0xBE, 0xAD, 0x1E, 0x44, 0x60, 0xD4,
0xE0, 0x81, 0x38, 0xF2, 0xBA, 0xF5, 0xB5, 0x37, 0x5A, 0x34,
0xB5, 0xCA, 0x6B, 0xC8, 0x0F, 0xCD, 0x75, 0x1D, 0x5E, 0xC0,
0x8A, 0xD3, 0xD7, 0x79, 0xA7, 0xC1, 0xB8, 0xA2, 0xC6, 0xEA,
0x5A, 0x7D, 0x60, 0x66, 0x50, 0x97, 0x37, 0x6C, 0xF9, 0x0A,
0xF6, 0x3D, 0x77, 0x9A, 0xE2, 0x19, 0xF7, 0xF9, 0xDD, 0x52,
0xC4, 0x0F, 0x98, 0xAA, 0xA2, 0xA4, 0x01, 0xC9, 0x41, 0x0B,
0xD0, 0x25, 0xDD, 0xC9, 0x7C, 0x3F, 0x70, 0x32, 0x23, 0xCF,
0xFE, 0x37, 0x67, 0x3A, 0xBC, 0x0B, 0x76, 0x16, 0x82, 0x83,
0x27, 0x3D, 0x1D, 0x19, 0x15, 0x78, 0x08, 0x2B, 0xD4, 0xA7,
0xC2, 0x0F, 0x11, 0xF4, 0xDD, 0xE5, 0x5A, 0x5D, 0x04, 0x8D,
0x6D, 0x5E, 0xC4, 0x1F, 0x54, 0x44, 0xA9, 0x13, 0x34, 0x71,
0x0F, 0xF7, 0x57, 0x9A, 0x9F, 0x2E, 0xF4, 0x97, 0x7D, 0xAE,
0x28, 0xEF};
d_crypto->set_public_key(publicKey);
ASSERT_TRUE(d_crypto->verify_signature_ecdsa_p521(message, signature));
std::vector<uint8_t> wrong_signature = std::move(signature);
wrong_signature[1] = 1;
ASSERT_FALSE(d_crypto->verify_signature_ecdsa_p521(message, wrong_signature));
}

View File

@ -0,0 +1,301 @@
/*!
* \file osmna_msg_receiver_test.cc
* \brief Tests for the osnma_msg_receiver class.
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "Galileo_OSNMA.h"
#include "gnss_crypto.h"
#include "osnma_helper.h"
#include "osnma_msg_receiver.h"
#include <gtest/gtest.h>
#include <bitset>
#include <chrono>
#include <fstream>
#include <vector>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h> // for LOG
#else
#include <absl/log/log.h>
#endif
class OsnmaMsgReceiverTest : public ::testing::Test
{
protected:
Osnma_Helper helper;
osnma_msg_receiver_sptr osnma;
OSNMA_msg osnma_msg{};
std::array<int8_t, 15> nma_position_filled;
uint32_t d_GST_SIS{};
uint32_t TOW{};
uint32_t WN{};
std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0, 0, 0, 0, 0}; // months start with 0 and years since 1900 in std::tm
const uint32_t LEAP_SECONDS = 0; // tried with 13 + 5, which is the official count, but won't parse correctly
void set_time(std::tm& input);
void SetUp() override
{
// std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 1
std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 2
set_time(input_time);
osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
}
};
TEST_F(OsnmaMsgReceiverTest, ComputeMerkleRoot)
{
// input data taken from Receiver Guidelines v1.3, A.7
// Arrange
std::vector<uint8_t> computed_merkle_root;
std::vector<uint8_t> expected_merkle_root = helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D");
DSM_PKR_message dsm_pkr_message;
dsm_pkr_message.npkt = 0x01;
dsm_pkr_message.npktid = 0x2;
dsm_pkr_message.mid = 0x01;
std::vector<uint8_t> base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
// ITN
std::vector<uint8_t> vec = helper.convert_from_hex_string(
"7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06"
"956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7"
"407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1"
"24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87");
std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin());
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
// Act
computed_merkle_root = osnma->compute_merkle_root(dsm_pkr_message, base_leaf);
// Assert
ASSERT_EQ(computed_merkle_root, expected_merkle_root);
}
TEST_F(OsnmaMsgReceiverTest, ComputeBaseLeaf)
{
// input data taken from Receiver Guidelines v1.3, A.7
// Arrange
std::vector<uint8_t> expected_base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
DSM_PKR_message dsm_pkr_message;
dsm_pkr_message.npkt = 0x01;
dsm_pkr_message.npktid = 0x2;
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
// Act
std::vector<uint8_t> computed_base_leaf = osnma->get_merkle_tree_leaves(dsm_pkr_message);
// Assert
ASSERT_EQ(computed_base_leaf, expected_base_leaf);
}
TEST_F(OsnmaMsgReceiverTest, VerifyPublicKey)
{
// Input data taken from Receiver Guidelines v1.3, A.7
// Arrange
osnma->d_crypto->set_merkle_root(helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D"));
DSM_PKR_message dsm_pkr_message;
dsm_pkr_message.npkt = 0x01;
dsm_pkr_message.npktid = 0x2;
dsm_pkr_message.mid = 0x01;
std::vector<uint8_t> vec = helper.convert_from_hex_string(
"7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06"
"956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7"
"407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1"
"24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87");
std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin());
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
// Act
bool result = osnma->verify_dsm_pkr(dsm_pkr_message); // TODO - refactor method so that output is more than a boolean.
// Assert
ASSERT_TRUE(result);
}
TEST_F(OsnmaMsgReceiverTest, BuildTagMessageM0)
{
// input data taken from Receiver Guidelines v1.3, A.6.5.1
// Arrange
std::vector<uint8_t> expected_message = {
0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, 0xF7, 0xFC, 0x00};
uint32_t TOW_Tag0 = 345660;
uint32_t TOW_NavData = TOW_Tag0 - 30;
uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
uint32_t WN = 1248;
uint32_t PRNa = 2;
uint8_t CTR = 1;
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDB, 0xBC, 0x73}; // K4
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
osnma->d_nav_data_manager->add_navigation_data(
"000011101001011001000100000101000111010110100100100101100000000000"
"011101101011001111101110101010000001010000011011111100000011101011"
"011100101101011010101011011011001001110111101011110110111111001111"
"001000011111101110011000111111110111111010000011101011111111110000"
"110111000000100000001110110000110110001110000100001110101100010100"
"110100010001000110001110011010110000111010000010000000000001101000"
"000000000011100101100100010000000000000110110100110001111100000000"
"000000100110100000000101010010100000001011000010001001100000011111"
"110111111111000000000",
PRNa, TOW_NavData);
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
MACK_tag_and_info MTI;
MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
MTI.tag_info.PRN_d = 0x02;
MTI.tag_info.ADKD = 0x00;
MTI.tag_info.cop = 0x0F;
Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
// Act
auto computed_message = osnma->build_message(t0);
// Assert
ASSERT_TRUE(computed_message == expected_message);
}
TEST_F(OsnmaMsgReceiverTest, TagVerification)
{
// input data taken from Receiver Guidelines v1.3, A.6.5.1
// Arrange
// Tag0
uint32_t TOW_Tag0 = 345660;
uint32_t TOW_NavData = TOW_Tag0 - 30;
uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
uint32_t WN = 1248;
uint32_t PRNa = 2;
uint8_t CTR = 1;
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
osnma->d_nav_data_manager->add_navigation_data(
"000011101001011001000100000101000111010110100100100101100000000000"
"011101101011001111101110101010000001010000011011111100000011101011"
"011100101101011010101011011011001001110111101011110110111111001111"
"001000011111101110011000111111110111111010000011101011111111110000"
"110111000000100000001110110000110110001110000100001110101100010100"
"110100010001000110001110011010110000111010000010000000000001101000"
"000000000011100101100100010000000000000110110100110001111100000000"
"000000100110100000000101010010100000001011000010001001100000011111"
"110111111111000000000",
PRNa, TOW_NavData);
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
MACK_tag_and_info MTI;
MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
MTI.tag_info.PRN_d = 0x02;
MTI.tag_info.ADKD = 0x00;
MTI.tag_info.cop = 0x0F;
Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
// Act
bool result_tag0 = osnma->verify_tag(t0);
// Assert
// Tag3
uint32_t TOW_Key_Tag3 = TOW_Tag0 + 30;
WN = 1248;
PRNa = 2;
CTR = 3;
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
osnma->d_tesla_keys[TOW_Key_Tag3] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
osnma->d_nav_data_manager->add_navigation_data(
"111111111111111111111111111111110000000000000000000000010001001001001000"
"111000001000100111100010010111111111011110111111111001001100000100000",
PRNa, TOW_NavData);
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
MTI.tag = static_cast<uint64_t>(0x7BB238C883);
MTI.tag_info.PRN_d = 0x02;
MTI.tag_info.ADKD = 0x04;
MTI.tag_info.cop = 0x0F;
Tag t3(MTI, TOW_Tag0, WN, PRNa, CTR);
bool result_tag3 = osnma->verify_tag(t3);
ASSERT_TRUE(result_tag0 && result_tag3);
}
TEST_F(OsnmaMsgReceiverTest, TeslaKeyVerification)
{
// input data taken from Receiver Guidelines v1.3, A.5.2
// Arrange
osnma->d_tesla_key_verified = false;
osnma->d_osnma_data.d_dsm_kroot_message.kroot = {0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; // Kroot, TOW 345570 GST_0 - 30
osnma->d_osnma_data.d_dsm_kroot_message.ks = 4; // TABLE 10 --> 128 bits
osnma->d_osnma_data.d_dsm_kroot_message.alpha = 0x610BDF26D77B;
osnma->d_GST_SIS = (1248 & 0x00000FFF) << 20 | (345630 & 0x000FFFFF);
osnma->d_GST_0 = ((1248 & 0x00000FFF) << 20 | (345600 & 0x000FFFFF)); // applicable time (GST_Kroot + 30)
osnma->d_GST_Sf = osnma->d_GST_0 + 30 * std::floor((osnma->d_GST_SIS - osnma->d_GST_0) / 30); // Eq. 3 R.G.
osnma->d_tesla_keys.insert((std::pair<uint32_t, std::vector<uint8_t>>(345600, {0xEF, 0xF9, 0x99, 0x04, 0x0E, 0x19, 0xB5, 0x70, 0x83, 0x50, 0x60, 0xBE, 0xBD, 0x23, 0xED, 0x92}))); // K1, not needed, just for reference.
std::vector<uint8_t> key = {0x2D, 0xC3, 0xA3, 0xCD, 0xB1, 0x17, 0xFA, 0xAD, 0xB8, 0x3B, 0x5F, 0x0B, 0x6F, 0xEA, 0x88, 0xEB}; // K2
uint32_t TOW = 345630;
// Act
bool result = osnma->verify_tesla_key(key, TOW); // TODO - refactor so that output is not a boolean. Or use last_verified_tesla_key?
// Assert
ASSERT_TRUE(result);
}
/**
* @brief Sets the time based on the given input.
*
* This function calculates the week number (WN) and time of week (TOW)
* based on the input time and the GST_START_EPOCH. It then stores the
* calculated values in the WN and TOW member variables. Finally, it
* combines the WN and TOW into a single 32-bit value and stores it in
* the d_GST_SIS member variable.
*
* @param input The input time as a tm struct.
*/
void OsnmaMsgReceiverTest::set_time(std::tm& input)
{
auto epoch_time_point = std::chrono::system_clock::from_time_t(mktime(&GST_START_EPOCH));
auto input_time_point = std::chrono::system_clock::from_time_t(mktime(&input));
// Get the duration from epoch in seconds
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(input_time_point - epoch_time_point);
// Calculate the week number (WN) and time of week (TOW)
uint32_t sec_in_week = 7 * 24 * 60 * 60;
uint32_t week_number = duration_sec.count() / sec_in_week;
uint32_t time_of_week = duration_sec.count() % sec_in_week;
this->WN = week_number;
this->TOW = time_of_week + LEAP_SECONDS;
// Return the week number and time of week as a pair
// TODO: d_GST_SIS or d_receiver_time? doubt
// I am assuming that local realisation of receiver is identical to SIS GST time coming from W5 or W0
this->d_GST_SIS = (this->WN & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
}

View File

@ -0,0 +1,671 @@
/*!
* \file osmna_test_vectors.cc
* \brief Tests for the osnma_msg_receiver class.
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "gnss_crypto.h"
#include "osnma_msg_receiver.h"
#include <gtest/gtest.h>
#include <bitset>
#include <chrono>
#include <fstream>
#include <vector>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h> // for LOG
#else
#include <absl/log/log.h>
#endif
struct TestVector
{
int svId;
int numNavBits;
std::vector<uint8_t> navBits;
};
class OsnmaTestVectors : public ::testing::Test
{
protected:
std::vector<uint8_t> parseNavBits(const std::string& hex);
std::vector<TestVector> readTestVectorsFromFile(const std::string& filename);
std::string bytes_to_str(const std::vector<uint8_t>& bytes);
std::vector<uint8_t> extract_page_bytes(const TestVector& tv, int byte_index, int num_bytes);
bool feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles);
void set_time(std::tm& input);
void SetUp() override
{
}
uint32_t d_GST_SIS{};
uint32_t TOW{};
uint32_t WN{};
std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0, 0, 0, 0, 0}; // months start with 0 and years since 1900 in std::tm
const uint32_t LEAP_SECONDS = 0;
const int SIZE_PAGE_BYTES{240 / 8}; // total bytes of a page
const int SIZE_SUBFRAME_PAGES{15}; // number of pages of a subframe
const int DURATION_SUBFRAME{30}; // duration of a subframe, in seconds// 13 + 5;
bool d_flag_NPK{false}; // flag for NPK, new MT will be set when the new Kroot is received.
};
TEST_F(OsnmaTestVectors, NominalTestConf1)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_1/PublicKey/OSNMA_PublicKey_20230803105952_newPKID_1.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_1/MerkleTree/OSNMA_MerkleTree_20230803105953_newPKID_1.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time};
std::vector<TestVector> testVector = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/configuration_1/16_AUG_2023_GST_05_00_01.csv");
if (testVector.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVector};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, NominalTestConf2)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20230720113300_newPKID_2.crt"; // conf. 2
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20230720113300_newPKID_2.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 2
std::vector<std::tm> input_times = {input_time};
std::vector<TestVector> testVector = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/configuration_2/27_JUL_2023_GST_00_00_01.csv");
if (testVector.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVector};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, PublicKeyRenewal)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 2, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 45, 3, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step3 = {0, 45, 4, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step1/07_OCT_2023_GST_02_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step2/07_OCT_2023_GST_03_45_01.csv");
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step3/07_OCT_2023_GST_04_45_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
// Act
d_flag_NPK = true;
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, PublicKeyRevocation)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007081500_PKID_8.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007081500_PKID_8.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 7, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 30, 9, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step3 = {0, 30, 10, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step1/07_OCT_2023_GST_07_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step2/07_OCT_2023_GST_09_30_01.csv");
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step3/07_OCT_2023_GST_10_30_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, ChainRenewal)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 16, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 30, 18, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step1/06_OCT_2023_GST_16_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step2/06_OCT_2023_GST_18_30_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, ChainRevocation)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 21, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 30, 23, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step3 = {0, 30, 00, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step1/06_OCT_2023_GST_21_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step2/06_OCT_2023_GST_23_30_01.csv");
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step3/07_OCT_2023_GST_00_30_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, AlertMessage)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/PublicKey/OSNMA_PublicKey_20231007201500_PKID_1.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/MerkleTree/OSNMA_MerkleTree_20231007201500_PKID_1.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 18, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 45, 19, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step1/07_OCT_2023_GST_18_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step2/07_OCT_2023_GST_19_45_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
// Auxiliary functions for the OsnmaTestVectorsSimulation test fixture.
// Essentially, they perform same work as the telemetry decoder block, but adapted to the osnma-test-vector files.
bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles)
{
bool end_of_hex_stream;
int offset_byte{0};
int byte_index{0}; // index containing the last byte position of the hex stream that was retrieved. Takes advantage that all TVs have same size
const int DUMMY_PAGE{63};
bool flag_dummy_page{false};
// Act
// loop over all bytes of data. Note: all TestVectors have same amount of data.
// if needed, add global flags so that particular logic may be done at certain points in between files
for (size_t test_step = 0; test_step < testVectors.size(); test_step++)
{
// set variables for each file
end_of_hex_stream = false;
offset_byte = 0;
byte_index = 0;
set_time(startTimesFiles[test_step]);
std::cout << "OsnmaTestVectorsSimulation:"
<< " d_GST_SIS= " << d_GST_SIS
<< ", TOW=" << TOW
<< ", WN=" << WN << std::endl;
if (test_step == 1 && d_flag_NPK == true)
{
// step 2: this simulates the osnma connecting to the GSC server and downloading the Merkle tree of the next public key
osnma_object->read_merkle_xml(
std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007081500_PKID_8.xml");
}
while (!end_of_hex_stream)
{
// loop over all SVs, extract a subframe
for (const TestVector& tv : testVectors[test_step])
{ // loop over all SVs, extract a subframe
std::cout << "OsnmaTestVectorsSimulation: SVID (PRN_a) " << tv.svId << std::endl;
auto osnmaMsg_sptr = std::make_shared<OSNMA_msg>();
std::array<uint8_t, 15> hkroot{};
std::array<uint32_t, 15> mack{};
byte_index = offset_byte; // reset byte_index to the offset position for the next test vector. Offset is updated at the end of each Subframe (every 30 s or 450 Bytes)
std::map<uint8_t, std::bitset<128>> words_for_OSNMA; // structure containing <WORD_NUMBER> and <EXTRACTED_BITS>
for (int idx = 0; idx < SIZE_SUBFRAME_PAGES; ++idx) // extract all pages of a subframe
{
// extract bytes of complete page (odd+even) -- extract SIZE_PAGE from tv.navBits, starting from byte_index
std::vector<uint8_t> page_bytes = extract_page_bytes(tv, byte_index, SIZE_PAGE_BYTES);
if (page_bytes.empty())
{
std::cout << "OsnmaTestVectorsSimulation: end of TestVectors \n"
<< "byte_index=" << byte_index << " expected= " << 432000 / 8 << std::endl;
end_of_hex_stream = true;
break;
}
// convert them to bitset representation using bytes_to_string
std::string page_bits = bytes_to_str(page_bytes);
// Extract the 40 OSNMA bits starting from the 18th bit
std::string even_page = page_bits.substr(0, page_bits.size() / 2);
std::string odd_page = page_bits.substr(page_bits.size() / 2);
if (even_page.size() < 120 || odd_page.size() < 120)
{
std::cout << "OsnmaTestVectorsSimulation: error parsing pages" << std::endl;
}
bool even_odd_OK = even_page[0] == '0' && odd_page[0] == '1';
bool page_type_OK = even_page[1] == '0' && odd_page[1] == '0';
bool tail_bits_OK = even_page.substr(even_page.size() - 6) == "000000" && odd_page.substr(odd_page.size() - 6) == "000000";
if (!even_odd_OK || !page_type_OK || !tail_bits_OK)
std::cerr << "OsnmaTestVectorsSimulation: error parsing pages." << std::endl;
std::bitset<112> data_k(even_page.substr(2, 112));
std::bitset<16> data_j(odd_page.substr(2, 16));
std::bitset<112> shifted_data_k = data_k;
uint8_t word_type = static_cast<uint8_t>((shifted_data_k >>= 106).to_ulong()); // word type is the first 6 bits of the word
// std::cout << "OsnmaTestVectorsSimulation: received Word " << static_cast<int>(word_type) << std::endl;
if ((word_type >= 1 && word_type <= 5) || word_type == 6 || word_type == 10)
{
// store raw word
std::bitset<128> data_combined(data_k.to_string() + data_j.to_string());
words_for_OSNMA[word_type] = data_combined;
}
if (word_type == DUMMY_PAGE)
flag_dummy_page = true;
// place it into osnma object.
std::bitset<40> osnmaBits(odd_page.substr(18, 40));
// Extract bits for hkroot and mack
std::bitset<8> hkrootBits(osnmaBits.to_string().substr(0, 8));
std::bitset<32> mackBits(osnmaBits.to_string().substr(8, 32));
hkroot[idx] = static_cast<uint8_t>(hkrootBits.to_ulong());
mack[idx] = static_cast<uint32_t>(mackBits.to_ulong());
byte_index += SIZE_PAGE_BYTES;
}
// std::cout << "----------" << std::endl;
if (end_of_hex_stream)
break;
if (flag_dummy_page)
{
flag_dummy_page = false;
continue; // skip this SV
}
// Fill osnma object
osnmaMsg_sptr->hkroot = hkroot;
osnmaMsg_sptr->mack = mack;
osnmaMsg_sptr->TOW_sf0 = d_GST_SIS & 0x000FFFFF;
osnmaMsg_sptr->WN_sf0 = (d_GST_SIS & 0xFFF00000) >> 20;
osnmaMsg_sptr->PRN = tv.svId; // PRNa
// TODO - refactor this logic, currently it is split
// check if words_for_OSNMA 1--> 5 words_for_OSNMA are received => fill EphClockStatus data vector
bool ephClockStatusWordsReceived = true;
for (int i = 1; i <= 5; ++i)
{
if (words_for_OSNMA.find(i) == words_for_OSNMA.end())
{
ephClockStatusWordsReceived = false;
std::cerr << "OsnmaTestVectorsSimulation: error parsing words_for_OSNMA 1->5. "
"Word "
<< i << " should be received for each subframe but was not." << std::endl;
}
}
// extract bits as needed by osnma block
if (ephClockStatusWordsReceived)
{
// Define the starting position and length of bits to extract for each word
std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
{1, {6, 120}},
{2, {6, 120}},
{3, {6, 122}},
{4, {6, 120}},
{5, {6, 67}},
};
// Fill NavData bits -- Iterate over the extraction parameters
std::string nav_data_ADKD_0_12 = "";
for (const auto& param : extractionParams)
{
uint8_t wordKey = param.first;
uint8_t start = param.second.first;
uint8_t length = param.second.second;
// Extract the required bits and fill osnma block
nav_data_ADKD_0_12 += words_for_OSNMA[wordKey].to_string().substr(start, length);
}
// send to osnma block
bool check_size_is_ok = nav_data_ADKD_0_12.size() == 549;
if (check_size_is_ok)
{
std::cout << "Galileo OSNMA: sending ADKD=0/12 navData, PRN_d (" << tv.svId << ") "
<< "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
tv.svId,
nav_data_ADKD_0_12,
osnmaMsg_sptr->TOW_sf0);
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_0_12;
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
}
}
// check w6 && w10 is received => fill TimingData data vector
bool timingWordsReceived = words_for_OSNMA.find(6) != words_for_OSNMA.end() &&
words_for_OSNMA.find(10) != words_for_OSNMA.end();
// extract bits as needed by osnma block
if (timingWordsReceived)
{
// Define the starting position and length of bits to extract for each word
std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
{6, {6, 99}},
{10, {86, 42}}};
std::string nav_data_ADKD_4 = "";
// Fill NavData bits -- Iterate over the extraction parameters
for (const auto& param : extractionParams)
{
uint8_t wordKey = param.first;
uint8_t start = param.second.first;
uint8_t length = param.second.second;
// Extract the required bits and fill osnma block
nav_data_ADKD_4 += words_for_OSNMA[wordKey].to_string().substr(start, length);
}
// send to osnma block
bool check_size_is_ok = nav_data_ADKD_4.size() == 141;
if (check_size_is_ok)
{
std::cout << "Galileo OSNMA: sending ADKD=04 navData, PRN_d (" << tv.svId << ") "
<< "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
tv.svId,
nav_data_ADKD_4,
osnmaMsg_sptr->TOW_sf0);
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_4;
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
}
}
// Call the handler, as if it came from telemetry decoder block
auto temp_obj = pmt::make_any(osnmaMsg_sptr);
osnma_object->msg_handler_osnma(temp_obj); // osnma entry point
}
if (!end_of_hex_stream)
{
offset_byte = byte_index; // update offset for the next subframe
d_GST_SIS += DURATION_SUBFRAME;
TOW = d_GST_SIS & 0x000FFFFF;
WN = (d_GST_SIS & 0xFFF00000) >> 20;
std::cout << "OsnmaTestVectorsSimulation:"
<< " d_GST_SIS= " << d_GST_SIS
<< ", TOW=" << TOW
<< ", WN=" << WN << std::endl;
}
}
if (end_of_hex_stream)
continue;
}
return true;
}
std::vector<TestVector> OsnmaTestVectors::readTestVectorsFromFile(const std::string& filename)
{
std::ifstream file(filename);
std::vector<TestVector> testVectors;
if (!file.is_open())
{
std::cerr << "Error reading the file \"" << filename << "\" \n";
return testVectors;
}
std::string line;
std::getline(file, line);
if (line != "SVID,NumNavBits,NavBitsHEX\r")
{
std::cerr << "Error parsing first line"
<< "\n";
}
while (std::getline(file, line))
{
std::stringstream ss(line);
TestVector tv;
std::string val;
std::getline(ss, val, ',');
tv.svId = std::stoi(val);
std::getline(ss, val, ',');
tv.numNavBits = std::stoi(val);
std::getline(ss, val, ',');
tv.navBits = OsnmaTestVectors::parseNavBits(val);
testVectors.push_back(tv);
}
return testVectors;
}
std::vector<uint8_t> OsnmaTestVectors::parseNavBits(const std::string& hexadecimal)
{
std::vector<uint8_t> bytes;
for (unsigned int i = 0; i < hexadecimal.length() - 1; i += 2)
{
std::string byteString = hexadecimal.substr(i, 2);
uint8_t byte = static_cast<uint8_t>(strtol(byteString.c_str(), nullptr, 16));
bytes.push_back(byte);
}
return bytes;
}
std::string OsnmaTestVectors::bytes_to_str(const std::vector<uint8_t>& bytes)
{
std::string bit_string;
bit_string.reserve(bytes.size() * 8);
for (const auto& byte : bytes)
{
std::bitset<8> bits(byte);
bit_string += bits.to_string();
}
return bit_string;
}
/**
* @brief Extracts a range of bytes from a TestVector's navBits vector.
*
* This function extracts a extracts the bytes of complete page (odd+even)
* from the navBits vector of a TestVector object.
*
*
* @param tv The TestVector object from which to extract bytes.
* @param byte_index The index of the first byte to extract.
* @param num_bytes The number of bytes to extract.
* @return A vector containing the extracted bytes, or an empty vector if extraction is not possible.
*/
std::vector<uint8_t> OsnmaTestVectors::extract_page_bytes(const TestVector& tv, int byte_index, int num_bytes)
{
// Ensure we don't go beyond the end of tv.navBits
int num_bytes_to_extract = std::min(num_bytes, static_cast<int>(tv.navBits.size() - byte_index));
// If byte_index is beyond the end of tv.navBits, return an empty vector
if (num_bytes_to_extract <= 0)
{
return std::vector<uint8_t>();
}
// Use std::next to get an iterator to the range to extract
std::vector<uint8_t> extracted_bytes(tv.navBits.begin() + byte_index, tv.navBits.begin() + byte_index + num_bytes_to_extract);
return extracted_bytes;
}
/**
* @brief Sets the time based on the given input.
*
* This function calculates the week number (WN) and time of week (TOW)
* based on the input time and the GST_START_EPOCH. It then stores the
* calculated values in the WN and TOW member variables. Finally, it
* combines the WN and TOW into a single 32-bit value and stores it in
* the d_GST_SIS member variable.
* \post WN, TOW and GST_SIS are set up based on the input time.
*
* @param input The input time as a tm struct.
*/
void OsnmaTestVectors::set_time(std::tm& input)
{
auto epoch_time_point = std::chrono::system_clock::from_time_t(mktime(&GST_START_EPOCH));
auto input_time_point = std::chrono::system_clock::from_time_t(mktime(&input));
// Get the duration from epoch in seconds
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(input_time_point - epoch_time_point);
// Calculate the week number (WN) and time of week (TOW)
uint32_t sec_in_week = 7 * 24 * 60 * 60;
uint32_t week_number = duration_sec.count() / sec_in_week;
uint32_t time_of_week = duration_sec.count() % sec_in_week;
this->WN = week_number;
this->TOW = time_of_week + LEAP_SECONDS;
// Return the week number and time of week as a pair
// TODO: d_GST_SIS or d_receiver_time? doubt
// I am assuming that local realisation of receiver is identical to SIS GST time coming from W5 or W0
this->d_GST_SIS = (this->WN & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
}