diff --git a/.bazelrc b/.bazelrc index fdc199a..f397f49 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,6 +1,101 @@ +# Causes the build to default to the custom toolchain +build --incompatible_enable_cc_toolchain_resolution + +# Overrides our custom toolchain and uses the system +build:system --noincompatible_enable_cc_toolchain_resolution +build:system --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN="0" +build:system --platform_suffix=system + build:clang-format-check --aspects @rules_swiftnav//clang_format:clang_format_check.bzl%clang_format_check_aspect build:clang-format-check --@rules_swiftnav//clang_format:clang_format_config=//:clang_format_config build:clang-format-check --output_groups=report build:clang-tidy --aspects @rules_swiftnav//clang_tidy:clang_tidy.bzl%clang_tidy_aspect build:clang-tidy --output_groups=report + +# Shared sanitizer configuration +common:sanitize -c dbg +common:sanitize --cxxopt=-g3 +common:sanitize --copt=-g3 +common:sanitize --linkopt=-g3 +common:sanitize --cxxopt=-fno-omit-frame-pointer +common:sanitize --copt=-fno-omit-frame-pointer +common:sanitize --linkopt=-fno-omit-frame-pointer +common:sanitize --@rules_swiftnav//cc:enable_symbolizer=true + +# Sanitizer overhead can cause some functions to become so large that +# the compiler falls back to a linear register allocator. This +# shouldn't cause a sanitizer build to fail. +common:sanitize --cxxopt=-Wno-error=disabled-optimization +common:sanitize --copt=-Wno-error=disabled-optimization +common:sanitize --linkopt=-Wno-error=disabled-optimization +# https://github.com/bazelbuild/bazel/issues/12797#issuecomment-980641064 +common:sanitize --linkopt='-fsanitize-link-c++-runtime' +common:sanitize --cxxopt=-fPIC +common:sanitize --copt=-fPIC +common:sanitize --linkopt=-fPIC + +# Address sanitizer +common:asan --config=sanitize +common:asan --cxxopt=-fsanitize=address +common:asan --copt=-fsanitize=address +common:asan --linkopt=-fsanitize=address +common:asan --cxxopt=-fno-optimize-sibling-calls +common:asan --copt=-fno-optimize-sibling-calls +common:asan --linkopt=-fno-optimize-sibling-calls +common:asan --platform_suffix=asan + +# Undefined behavior sanitizer +common:ubsan --config=sanitize +common:ubsan --cxxopt=-fsanitize=undefined +common:ubsan --copt=-fsanitize=undefined +common:ubsan --linkopt=-fsanitize=undefined +common:ubsan --cxxopt=-fno-sanitize-recover=all +common:ubsan --copt=-fno-sanitize-recover=all +common:ubsan --linkopt=-fno-sanitize-recover=all +common:ubsan --cxxopt=-fsanitize=local-bounds +common:ubsan --copt=-fsanitize=local-bounds +common:ubsan --linkopt=-fsanitize=local-bounds +common:ubsan --@rules_swiftnav//cc:enable_rtti=true +# Unfortunately the current build setup doesn't seem to provide stack +# frame names using clang++ and llvm-symbolizer. (This was also the +# case in cmake, but the default of g++ there _did_ provide frame +# names in backtraces.) +test:ubsan --action_env="UBSAN_OPTIONS=print_stacktrace=1" +run:ubsan --action_env="UBSAN_OPTIONS=print_stacktrace=1" +# vptr sanitizer is horribly broken to the point of throwing false positives at a +# rate that suppressions or an ignore list are impractical. +common:ubsan --cxxopt=-fno-sanitize=vptr +common:ubsan --linkopt=-fno-sanitize=vptr +common:ubsan --platform_suffix=ubsan + +# Dynamic memory sanitizer +# +# Warning: takes an incredible amount of space! Try testing only one +# target at a time. +common:msan --config=sanitize +common:msan --cxxopt=-fsanitize=memory +common:msan --copt=-fsanitize=memory +common:msan --linkopt=-fsanitize=memory +common:msan --cxxopt=-fsanitize-memory-track-origins=2 +common:msan --copt=-fsanitize-memory-track-origins=2 +common:msan --linkopt=-fsanitize-memory-track-origins=2 +common:msan --cxxopt=-fsanitize-memory-use-after-dtor +common:msan --copt=-fsanitize-memory-use-after-dtor +common:msan --linkopt=-fsanitize-memory-use-after-dtor +common:msan --platform_suffix=msan + +# > Currently, ThreadSanitizer symbolizes its output using an external +# > addr2line process (this will be fixed in future). +# +# https://clang.llvm.org/docs/ThreadSanitizer.html#usage +common:tsan --config=sanitize +common:tsan --cxxopt=-fsanitize=thread +common:tsan --copt=-fsanitize=thread +common:tsan --linkopt=-fsanitize=thread +common:tsan --platform_suffix=tsan + +# Helpful aliases +common:asan_ubsan --config=asan +common:asan_ubsan --config=ubsan +common:ubsan_asan --config=asan_ubsan diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..c75e875 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/auto-cancellation.yaml b/.github/workflows/auto-cancellation.yaml deleted file mode 100644 index 1bc1675..0000000 --- a/.github/workflows/auto-cancellation.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Cancelling previous actions runs. -"on": pull_request -jobs: - cancel: - name: auto-cancellation-running-action - runs-on: ubuntu-latest - steps: - - uses: fauguste/auto-cancellation-running-action@0.1.4 - with: - githubToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 29dca2a..e0ece6b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,20 +4,31 @@ on: pull_request: push: branches: - - 'master' - - 'starling-v*-release' - - 'v*-release' + - "master" + - "starling-v*-release" + - "v*-release" tags: - - 'v*' - - 'starling-v*' + - "v*" + - "starling-v*" -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write +jobs: ubuntu-codecov: runs-on: ubuntu-latest - container: ubuntu:18.04 + container: "ubuntu:24.04" steps: + - name: Setup container + run: | + apt-get update + apt-get install -y gpg wget curl software-properties-common unzip git cmake build-essential clang llvm - name: Setup container run: | @@ -32,7 +43,7 @@ jobs: apt-get install -y git cmake build-essential clang llvm - name: Checkout source - uses: actions/checkout@v2 + uses: actions/checkout@v4.1.7 with: fetch-depth: 0 submodules: recursive @@ -46,10 +57,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_ORGANIZATION: swift-nav - SONAR_PROJECT_KEY: swift-nav_libswiftnav-private - SONAR_PROJECT_NAME: libswiftnav-private + SONAR_PROJECT_KEY: swift-nav_libswiftnav + SONAR_PROJECT_NAME: libswiftnav SONAR_HOST_URL: https://sonarcloud.io - SONAR_SCANNER_VERSION: 4.2.0.1873 + SONAR_SCANNER_VERSION: 5.0.1.3006 run: | bash ./ci-build.sh @@ -58,16 +69,18 @@ jobs: strategy: matrix: include: - - {env: "MSVC", arch: "Win32"} - - {env: "MSVC", arch: "x64"} - - {env: "MinGW"} + - { env: "MSVC", arch: "Win32" } + - { env: "MSVC", arch: "x64" } + - { env: "MinGW" } - runs-on: windows-2019 + runs-on: windows-latest steps: + - name: Configure git + run: git config --global http.postBuffer 1048576000 - name: Checkout source - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive ssh-key: ${{ secrets.SSH_KEY }} @@ -92,7 +105,7 @@ jobs: - name: Run build (MSVC) if: matrix.env == 'MSVC' env: - CMAKE_GENERATOR: "Visual Studio 16 2019" + CMAKE_GENERATOR: "Visual Studio 17 2022" run: | cmake -G "$env:CMAKE_GENERATOR" -A ${{ matrix.arch }} -S . -B build; cmake --build build --config Release; @@ -112,20 +125,46 @@ jobs: strategy: matrix: include: - - {cc: "gcc-6", cxx: "g++-6", test_suite: "unit", - package: "gcc-6 g++-6", runs_on: "ubuntu-latest", container: "ubuntu:18.04"} - - {cc: "clang-6.0", cxx: "clang++-6.0", test_suite: "lint", - package: "clang-6.0 libc++-dev libc++abi-dev clang-format-6.0 clang-tidy-6.0", - runs_on: "ubuntu-latest", container: "ubuntu:18.04"} - - {cc: "gcc-11", cxx: "g++-11", test_suite: "unit", - package: "gcc-11 g++-11", runs_on: "ubuntu-latest", container: "ubuntu:18.04"} - - {cc: "clang", cxx: "clang++", test_suite: "unit", - runs_on: "macos-11", container: ~} - + - { + cc: "gcc-10", + cxx: "g++-10", + test_suite: "unit", + package: "gcc-10 g++-10", + runs_on: "ubuntu-latest", + container: "ubuntu:24.04", + } + - { + cc: "clang-14", + cxx: "clang++-14", + test_suite: "lint", + package: "clang-14 libc++-dev libc++abi-dev clang-format-14 clang-tidy-14", + runs_on: "ubuntu-latest", + container: "ubuntu:24.04", + } + - { + cc: "gcc-14", + cxx: "g++-14", + test_suite: "unit", + package: "gcc-14 g++-14", + runs_on: "ubuntu-latest", + container: "ubuntu:24.04", + } + - { + cc: "clang", + cxx: "clang++", + test_suite: "unit", + runs_on: "macos-latest", + container: ~, + } runs-on: ${{ matrix.runs_on }} container: ${{ matrix.container }} steps: + - name: Setup container + if: matrix.container == 'ubuntu:24.04' + run: | + apt-get update + apt-get install -y gpg wget curl software-properties-common zip libeigen3-dev libserialport-dev git cmake build-essential ${{ matrix.package }} - name: Setup container if: matrix.container == 'ubuntu:18.04' @@ -142,7 +181,7 @@ jobs: apt-get install -y libeigen3-dev libserialport-dev git cmake build-essential ${{ matrix.package }} - name: Checkout source - uses: actions/checkout@v2 + uses: actions/checkout@v4.1.7 with: submodules: recursive ssh-key: ${{ secrets.SSH_KEY }} @@ -155,3 +194,17 @@ jobs: TESTENV: c run: | bash ./ci-build.sh + + dependabot: + name: "Dependabot" + needs: [unix] + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} # Detect that the PR author is dependabot + steps: + - name: Enable auto-merge for Dependabot PRs + run: | + gh pr review --approve "$PR_URL" + gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.gitmodules b/.gitmodules index bf98b2c..93d41bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "cmake/common"] path = cmake/common url = https://github.com/swift-nav/cmake.git -[submodule "third_party/check"] - path = third_party/check - url = https://github.com/swift-nav/check.git +[submodule "third_party/googletest"] + path = third_party/googletest + url = git@github.com:google/googletest.git diff --git a/BUILD.bazel b/BUILD.bazel index 60361a3..0862798 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -37,12 +37,16 @@ swift_c_library( "src/almanac.c", "src/bits.c", "src/coord_system.c", + "src/correct_iono_tropo.c", "src/decode_glo.c", "src/edc.c", "src/ephemeris.c", "src/fifo_byte.c", "src/geoid_model.c", + "src/geoid_model.h", + "src/geoid_model_15_minute.c", "src/geoid_model_15_minute.inc", + "src/geoid_model_1_degree.c", "src/geoid_model_1_degree.inc", "src/glo_map.c", "src/glonass_phase_biases.c", @@ -72,12 +76,14 @@ swift_c_library( "include/swiftnav/common.h", "include/swiftnav/constants.h", "include/swiftnav/coord_system.h", + "include/swiftnav/correct_iono_tropo.h", "include/swiftnav/decode_glo.h", "include/swiftnav/edc.h", "include/swiftnav/ephemeris.h", "include/swiftnav/fifo_byte.h", "include/swiftnav/float_equality.h", "include/swiftnav/geoid_model.h", + "include/swiftnav/get_unaligned.h", "include/swiftnav/glo_map.h", "include/swiftnav/glonass_phase_biases.h", "include/swiftnav/gnss_capabilities.h", @@ -118,45 +124,39 @@ swift_c_library( visibility = ["//visibility:public"], ) -swift_cc_test_library( - name = "check-utils", - srcs = ["tests/common/check_utils.c"], - hdrs = ["tests/common/check_utils.h"], - deps = ["//:swiftnav"], -) - swift_cc_test( name = "swiftnav-test", srcs = [ - "tests/check_almanac.c", - "tests/check_bits.c", - "tests/check_coord_system.c", - "tests/check_decode_glo.c", - "tests/check_edc.c", - "tests/check_ephemeris.c", - "tests/check_geoid_model.cc", - "tests/check_glo_map.c", - "tests/check_gnss_time.c", - "tests/check_gnss_time_cpp.cc", - "tests/check_ionosphere.c", - "tests/check_linear_algebra.c", - "tests/check_log.c", - "tests/check_main.c", - "tests/check_nav_meas.c", - "tests/check_pvt.c", - "tests/check_set.c", - "tests/check_shm.c", - "tests/check_sid_set.c", - "tests/check_signal.c", - "tests/check_subsystem_status_report.c", - "tests/check_suites.h", - "tests/check_troposphere.c", + "tests/check_utils.h", + "tests/test_almanac.cc", + "tests/test_bits.cc", + "tests/test_coord_system.cc", + "tests/test_correct_iono_tropo.cc", + "tests/test_data.h", + "tests/test_decode_glo.cc", + "tests/test_edc.cc", + "tests/test_ephemeris.cc", + "tests/test_geoid_model.cc", + "tests/test_glo_map.cc", + "tests/test_gnss_time.cc", + "tests/test_gnss_time_cpp.cc", + "tests/test_ionosphere.cc", + "tests/test_linear_algebra.cc", + "tests/test_log.cc", + "tests/test_nav_meas.cc", + "tests/test_pvt.cc", + "tests/test_set.cc", + "tests/test_shm.cc", + "tests/test_sid_set.cc", + "tests/test_signal.cc", + "tests/test_subsystem_status_report.cc", + "tests/test_troposphere.cc", ], type = UNIT, + timeout="short", deps = [ - "//:check-utils", "//:swiftnav", - "@check", + "@gtest//:gtest_main", ], ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55d6b6d..3beb641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.13) project(libswiftnav) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/" "${CMAKE_CURRENT_LIST_DIR}/cmake/common") @@ -25,7 +25,7 @@ swift_create_project_options( HAS_TESTS HAS_TEST_LIBS DISABLE_TEST_COMPONENTS ${disable_tests} - TEST_PACKAGES "Check" + TEST_PACKAGES "Googletest" ) include(ClangFormat) @@ -49,6 +49,7 @@ set(HDRS include/swiftnav/common.h include/swiftnav/constants.h include/swiftnav/coord_system.h + include/swiftnav/correct_iono_tropo.h include/swiftnav/decode_glo.h include/swiftnav/edc.h include/swiftnav/ephemeris.h @@ -81,11 +82,14 @@ set(SRCS src/almanac.c src/bits.c src/coord_system.c + src/correct_iono_tropo.c src/decode_glo.c src/edc.c src/ephemeris.c src/fifo_byte.c src/geoid_model.c + src/geoid_model_1_degree.c + src/geoid_model_15_minute.c src/glo_map.c src/glonass_phase_biases.c src/gnss_time.c diff --git a/Dockerfile.modern b/Dockerfile.modern deleted file mode 100644 index 99d1e3f..0000000 --- a/Dockerfile.modern +++ /dev/null @@ -1,4 +0,0 @@ -# Base image is created by https://github.com/swift-nav/docker-recipes -FROM 571934480752.dkr.ecr.us-west-2.amazonaws.com/swift-build-modern:2022-07-29 - -WORKDIR /mnt/workspace diff --git a/Jenkinsfile b/Jenkinsfile index 3fc5c5b..6f8b918 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,6 +13,9 @@ def context = new Context(context: this) context.setRepo("libswiftnav") def builder = context.getBuilder() +bazelDockerImage = '571934480752.dkr.ecr.us-west-2.amazonaws.com/swift-build-bazel:2024-05-29' +modernDockerImage = '571934480752.dkr.ecr.us-west-2.amazonaws.com/swift-build-modern:2024-05-29' + /** * - Mount the refrepo to keep git operations functional on a repo that uses ref-repo during clone **/ @@ -34,37 +37,41 @@ pipeline { parallel { stage('Unit Test') { agent { - dockerfile { + docker { + image modernDockerImage args dockerMountArgs } } + environment { + CC = "gcc-11" + CXX = "g++-11" + } steps { gitPrep() script { builder.cmake() - builder.make(workDir: "build") + builder.make(workDir: "build", target: "build-all-tests") } + sh("./build/tests/test-swiftnav-common --gtest_output=xml") // Convert the test results into a Jenkins-parsable junit-XML format - sh("./tools/check2junit.py build/tests/test_results.xml > build/tests/test_results_junit.xml") + //sh("./tools/check2junit.py test_detail.xml > build/tests/test_results_junit.xml") stash( name: 'libswiftnavUnit', - includes: 'build/tests/test_results_junit.xml') + includes: 'test_detail.xml') } } stage('Bazel Build') { agent { docker { - image '571934480752.dkr.ecr.us-west-2.amazonaws.com/swift-build-bazel:2023-06-21' + image bazelDockerImage } } steps { gitPrep() script { sh('''#!/bin/bash -ex - | CC=gcc-8 CXX=g++-8 bazel build --subcommands //... - | bazel run //:gen_compile_commands - | bazel run //:swiftnav-test - | bazel coverage --collect_code_coverage --combined_report=lcov //... + | bazel build --subcommands --config=system //... + | bazel coverage --collect_code_coverage --combined_report=lcov --config=system //... | genhtml bazel-out/_coverage/_coverage_report.dat -o coverage | tar -zcvf coverage.tar.gz coverage/ |'''.stripMargin()) @@ -76,10 +83,30 @@ pipeline { } } } + stage('ASAN') { + agent { + docker { + image bazelDockerImage + } + } + steps { + gitPrep() + script { + sh('''#!/bin/bash -ex + | bazel test --config asan //... + |'''.stripMargin()) + } + } + post { + always { + archiveArtifacts(artifacts: 'coverage.tar.gz', allowEmptyArchive: true) + } + } + } stage('Format & Lint') { agent { - dockerfile { - filename "Dockerfile.modern" + docker { + image modernDockerImage args dockerMountArgs } } @@ -99,10 +126,15 @@ pipeline { } stage('Code Coverage') { agent { - dockerfile { + docker { + image modernDockerImage args dockerMountArgs } } + environment { + CC = "gcc-11" + CXX = "g++-11" + } steps { gitPrep() script { @@ -125,7 +157,7 @@ pipeline { always { node('linux') { unstash(name: "libswiftnavUnit") - junit('build/tests/*.xml') + junit('*.xml') } script { context.slackNotify() diff --git a/Makefile b/Makefile index d00facc..e2d0605 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ docker-image: - docker-compose build libswiftnav + docker compose build libswiftnav docker: docker-image - docker-compose run libswiftnav + docker compose run libswiftnav docker-build: docker-image mkdir -p build - docker-compose run -T libswiftnav /bin/bash -c "cd build && cmake .. && make -j4" + docker compose run -T libswiftnav /bin/bash -c "cd build && cmake .. && make -j4" docker-lint: docker-image mkdir -p build - docker-compose run -T libswiftnav /bin/bash -c "cd build && cmake .. && make -j4 clang-format-all" + docker compose run -T libswiftnav /bin/bash -c "cd build && cmake .. && make -j4 clang-format-all" do-all-unit-tests: bazel test --test_tag_filters=unit --test_output=all //... diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 34f9bd5..9f573be 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -4,8 +4,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_swiftnav", - strip_prefix = "rules_swiftnav-26426be6b89a5b9f0489158098599b9fcd973ed4", - url = "https://github.com/swift-nav/rules_swiftnav/archive/26426be6b89a5b9f0489158098599b9fcd973ed4.tar.gz", + strip_prefix = "rules_swiftnav-cb82c790d50dd87f301df67b710161906dafb0c2", + url = "https://github.com/swift-nav/rules_swiftnav/archive/cb82c790d50dd87f301df67b710161906dafb0c2.tar.gz", ) http_archive( @@ -16,6 +16,12 @@ http_archive( load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") +load("@rules_swiftnav//cc:repositories.bzl", "register_swift_cc_toolchains", "swift_cc_toolchain") + +swift_cc_toolchain() + +register_swift_cc_toolchains() + rules_foreign_cc_dependencies() # Hedron's Compile Commands Extractor for Bazel @@ -31,8 +37,7 @@ load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_ hedron_compile_commands_setup() -new_local_repository( - name = "check", - build_file = "@rules_swiftnav//third_party:check.BUILD", - path = "third_party/check", +local_repository( + name = "gtest", + path = "third_party/googletest", ) diff --git a/cmake/common b/cmake/common index 48d4af0..61cdba1 160000 --- a/cmake/common +++ b/cmake/common @@ -1 +1 @@ -Subproject commit 48d4af0f349b94b6e76bccb1a7cee11969898236 +Subproject commit 61cdba176136b9ef499d518ad41f2bf1f4335fc7 diff --git a/include/swiftnav/bits.h b/include/swiftnav/bits.h index b432a79..b04531a 100644 --- a/include/swiftnav/bits.h +++ b/include/swiftnav/bits.h @@ -35,8 +35,9 @@ extern "C" { * * \return 32-bit signed integer with 2-complement of \a n_bits in \a arg */ -#define BITS_SIGN_EXTEND_32(n_bits, arg) \ - ((struct { s32 bits : (n_bits); }){.bits = (arg)}.bits) +#define BITS_SIGN_EXTEND_32(n_bits, arg) sign_extend_32(n_bits, arg) +int32_t sign_extend_32(uint8_t n_bits, uint32_t arg); + /** * Sign extension macro for 64-bit integers. * @@ -45,8 +46,8 @@ extern "C" { * * \return 64-bit signed integer with 2-complement of \a n_bits in \a arg */ -#define BITS_SIGN_EXTEND_64(n_bits, arg) \ - ((struct { s64 bits : (n_bits); }){.bits = (arg)}.bits) +#define BITS_SIGN_EXTEND_64(n_bits, arg) sign_extend_64(n_bits, arg) +int64_t sign_extend_64(uint8_t n_bits, uint64_t arg); u8 parity(u32 x); u16 bytes_interleave(const u8 x, const u8 y); diff --git a/include/swiftnav/correct_iono_tropo.h b/include/swiftnav/correct_iono_tropo.h new file mode 100644 index 0000000..53d6615 --- /dev/null +++ b/include/swiftnav/correct_iono_tropo.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Swift Navigation Inc. + * Contact: Swift Navigation + * + * This source is subject to the license found in the file 'LICENSE' which must + * be distributed together with this source. All other rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef LIBSWIFTNAV_CORRECT_IONO_TROPO_H +#define LIBSWIFTNAV_CORRECT_IONO_TROPO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void correct_iono(const double *pos_ecef, + const ionosphere_t *iono_params, + u8 n_meas, + navigation_measurement_t *nav_meas); + +void correct_tropo(const double *pos_ecef, + u8 n_meas, + navigation_measurement_t *nav_meas); + +#ifdef __cplusplus +} // end extern "C" +#endif + +#endif // LIBSWIFTNAV_CORRECT_IONO_TROPO_H diff --git a/include/swiftnav/ephemeris.h b/include/swiftnav/ephemeris.h index 73dc7b8..b900a51 100644 --- a/include/swiftnav/ephemeris.h +++ b/include/swiftnav/ephemeris.h @@ -84,6 +84,13 @@ extern "C" { #define GAL_INAV_CONTENT_BYTE ((GAL_INAV_CONTENT_BIT + CHAR_BIT - 1) / CHAR_BIT) #endif +/** + * SBAS ephemeris - En route seconds for GEO Navigation Data + * according to SBAS MOPS (aviation GPS receiver specification) + * https://gssc.esa.int/navipedia/index.php/The_EGNOS_SBAS_Message_Format_Explained#Message_time-outs + */ +extern const double SBAS_FIT_INTERVAL_SECONDS; + /** \addtogroup ephemeris * \{ */ @@ -279,6 +286,13 @@ typedef struct { } data; } ephemeris_t; +/** Structure containing the beginning and end of an ephemeris validity window. + */ +typedef struct { + gps_time_t bgn; /**< Begin time of validity window. */ + gps_time_t end; /**< End time of validity window. */ +} ephemeris_validity_window_t; + #define GLO_NAV_STR_BITS 85 /**< Length of GLO navigation string */ #define GLO_NAV_STR_WORDS 3 /**< Number of u32 words for nav string buffer */ @@ -292,10 +306,6 @@ typedef gps_time_t (*glo2gps_with_utc_params_t)(const glo_time_t *glo_t); /** \} */ -/* BDS satellites can be either geostationary (GEO), geosynchronous (IGSO) or - medium earth orbit (MEO) */ -typedef enum { GEO, IGSO, MEO } satellite_orbit_type_t; - s8 calc_sat_state(const ephemeris_t *e, const gps_time_t *t, double pos[3], @@ -336,6 +346,8 @@ ephemeris_status_t get_ephemeris_status_t(const ephemeris_t *e); ephemeris_status_t ephemeris_valid_detailed(const ephemeris_t *e, const gps_time_t *t); u8 ephemeris_valid(const ephemeris_t *e, const gps_time_t *t); +ephemeris_validity_window_t ephemeris_validity_window(const ephemeris_t *e, + gps_time_t *t); u8 ephemeris_params_valid(const gps_time_t *bgn, const gps_time_t *end, const gps_time_t *toc, diff --git a/include/swiftnav/geoid_model.h b/include/swiftnav/geoid_model.h index 4ad797e..2aeea79 100644 --- a/include/swiftnav/geoid_model.h +++ b/include/swiftnav/geoid_model.h @@ -22,6 +22,8 @@ extern "C" { // range for lat_rad is (-M_PI_2, M_PI_2) // range for lon_rad is (-2 * M_PI, 2 * M_PI) float get_geoid_offset(double lat_rad, double lon_rad); +float get_geoid_offset_1_degree(double lat_rad, double lon_rad); +float get_geoid_offset_15_minute(double lat_rad, double lon_rad); typedef enum { GEOID_MODEL_NONE = 0, diff --git a/include/swiftnav/get_unaligned.h b/include/swiftnav/get_unaligned.h new file mode 100644 index 0000000..a6578b9 --- /dev/null +++ b/include/swiftnav/get_unaligned.h @@ -0,0 +1,13 @@ +#ifndef SWIFTNAV_GET_UNALIGNED_H +#define SWIFTNAV_GET_UNALIGNED_H + +#include + +template +T get_unaligned(const U *buf, size_t offset) { + T value; + memcpy(&value, reinterpret_cast(buf) + offset, sizeof(T)); + return value; +} + +#endif diff --git a/include/swiftnav/gnss_time.h b/include/swiftnav/gnss_time.h index dbf871c..5b50ec8 100644 --- a/include/swiftnav/gnss_time.h +++ b/include/swiftnav/gnss_time.h @@ -272,6 +272,10 @@ void gps_time_match_weeks(gps_time_t *t, const gps_time_t *ref); u16 gps_adjust_week_cycle(u16 wn_raw, u16 wn_ref); u16 gps_adjust_week_cycle256(u16 wn_raw, u16 wn_ref); +double decimal_year_to_mjd(const double epoch_years); +double gps_time_to_decimal_years(const gps_time_t *time); +gps_time_t decimal_years_to_gps_time(const double years); + static inline bool is_leap_year(s32 year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } @@ -358,6 +362,10 @@ static inline bool operator==(const gps_time_t &a, const gps_time_t &b) { return fabs(gpsdifftime(&a, &b)) < FLOAT_EQUALITY_EPS; } +static inline bool operator!=(const gps_time_t &a, const gps_time_t &b) { + return !(a == b); +} + static inline bool operator<(const gps_time_t &a, const gps_time_t &b) { return gpsdifftime(&a, &b) < 0; } diff --git a/include/swiftnav/leap_seconds.h b/include/swiftnav/leap_seconds.h index e4d10ed..9dd4236 100644 --- a/include/swiftnav/leap_seconds.h +++ b/include/swiftnav/leap_seconds.h @@ -14,7 +14,7 @@ * Automatically generated from scripts/leap_seconds_generator.py. Please do * * not hand edit! * * * - * Updated: 30-03-2023 * + * Updated: 14-07-2025 * ******************************************************************************/ #ifndef LIBSWIFTNAV_LEAP_SECONDS_H @@ -52,11 +52,11 @@ static const s32 utc_leaps[][3] = { {1930, 17, 18}, /* 01-01-2017 */ }; -/** GPS time when the utc_leaps table expires 28-12-2023 */ -static const s32 gps_time_utc_leaps_expiry[2] = {2294, 345618}; +/** GPS time when the utc_leaps table expires 28-06-2026 */ +static const s32 gps_time_utc_leaps_expiry[2] = {2425, 18}; -/** UNIX time when the utc_leaps table expires 28-12-2023 */ -static const s64 unix_time_utc_leaps_expiry = 1703721600; +/** UNIX time when the utc_leaps table expires 28-06-2026 */ +static const s64 unix_time_utc_leaps_expiry = 1782604800; #ifdef __cplusplus } diff --git a/include/swiftnav/macros.h b/include/swiftnav/macros.h index b1691b9..b33cf4f 100644 --- a/include/swiftnav/macros.h +++ b/include/swiftnav/macros.h @@ -112,6 +112,13 @@ #define SWIFT_ATTR_NORETURN __attribute__((noreturn)) #define SWIFT_DECLSPEC __attribute__((visibility("default"))) +// This attribute has sporadic compatibility with GCC, but since +// we always use clang to run UBSAN we can safely restrict it +// to just clang here +#if defined(__clang__) +#define SWIFT_ATTR_NO_SANITIZE_ENUM __attribute__((no_sanitize("enum"))) +#endif + #elif defined(_MSC_VER) /* * MSVC @@ -193,5 +200,8 @@ #if !defined(SWIFT_DECLSPEC) #define SWIFT_DECLSPEC #endif +#if !defined(SWIFT_ATTR_NO_SANITIZE_ENUM) +#define SWIFT_ATTR_NO_SANITIZE_ENUM +#endif #endif diff --git a/include/swiftnav/signal.h b/include/swiftnav/signal.h index c4bbc06..5f13480 100644 --- a/include/swiftnav/signal.h +++ b/include/swiftnav/signal.h @@ -334,6 +334,7 @@ typedef enum sbas_system_e { SBAS_WAAS = 0, SBAS_EGNOS, SBAS_GAGAN, + SBAS_KASS, SBAS_MSAS, SBAS_COUNT } sbas_system_t; @@ -463,6 +464,44 @@ static inline bool sid_is_equal(const gnss_signal_t a, const gnss_signal_t b) { return sid_hash(a) == sid_hash(b); } +/* BDS satellites can be either geostationary (GEO), geosynchronous (IGSO) or + medium earth orbit (MEO) */ +typedef enum satellite_orbit_type_e { GEO, IGSO, MEO } satellite_orbit_type_t; + +/** Determine the orbit type of a given BDS satellite + * + * \param PRN number of a BDS satellite + * \return orbit type of BDS satellite + */ +static inline satellite_orbit_type_t get_bds_orbit_type(u8 prn) { + satellite_orbit_type_t orbit = MEO; + + if (((prn >= 1) && (prn <= 5)) || ((prn >= 59) && (prn <= 60))) { + orbit = GEO; + } else if (((prn >= 6) && (prn <= 10)) || ((prn >= 38) && (prn <= 40))) { + orbit = IGSO; + } + + return orbit; +} + +/** Determine the generation of a given BDS satellite + * + * \param PRN number of a BDS satellite + * \return BDS satellite generation (2 or 3) or -1 if unknown + */ +static inline int get_bds_generation(u8 prn) { + int generation = -1; + + if ((prn >= 19) && (prn <= 60)) { + generation = 3; + } else if ((prn >= 1) && (prn <= 18)) { + generation = 2; + } + + return generation; +} + #define _LOG_SIDn(func, sid, format, ...) \ do { \ char sid_str[SID_STR_LEN_MAX]; \ diff --git a/scripts/gtx_convert.pl b/scripts/gtx_convert.pl index aa2c723..e0b5d15 100755 --- a/scripts/gtx_convert.pl +++ b/scripts/gtx_convert.pl @@ -112,8 +112,8 @@ ($$) print FILE < + * + * This source is subject to the license found in the file 'LICENSE' which must + * be distributed together with this source. All other rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include +#include + +/* calculate azimuth and elevation at 't' seconds ago */ +static inline void calculate_prev_sat_pos( + const navigation_measurement_t* nav_meas, + const double t, + const double* pos_ecef, + double* az, + double* el) { + double sat_pos0[3]; + + for (u8 j = 0; j < 3; j++) { + sat_pos0[j] = nav_meas->sat_pos[j] - (t * nav_meas->sat_vel[j]); + } + wgsecef2azel(sat_pos0, pos_ecef, az, el); +} + +void correct_iono(const double* pos_ecef, + const ionosphere_t* iono_params, + u8 n_meas, + navigation_measurement_t* nav_meas) { + double az; + double el; + double az0; + double el0; + static const double h = 1.0; /* length of the finite difference, seconds */ + + if (!iono_params) { + return; + } + + double pos_llh[3]; + wgsecef2llh(pos_ecef, pos_llh); + + for (u8 i = 0; i < n_meas; i++) { + /* this signal's frequency */ + double carrier_freq = sid_to_carr_freq(nav_meas[i].sid); + + /* calculate current azimuth and elevation of SV */ + wgsecef2azel(nav_meas[i].sat_pos, pos_ecef, &az, &el); + /* calculate past azimuth and elevation of SV */ + calculate_prev_sat_pos(&nav_meas[i], h, pos_ecef, &az0, &el0); + + /* calculate iono correction */ + double iono_correction = calc_ionosphere( + &nav_meas[i].tot, pos_llh[0], pos_llh[1], az, el, iono_params); + + /* finite differences estimate of iono correction time derivative */ + double iono_correction_delta = + (iono_correction - + calc_ionosphere( + &nav_meas[i].tot, pos_llh[0], pos_llh[1], az0, el0, iono_params)) / + h; + + /* convert from L1CA Klobuchar correction */ + iono_correction *= (GPS_L1_HZ / carrier_freq) * (GPS_L1_HZ / carrier_freq); + iono_correction_delta *= + (GPS_L1_HZ / carrier_freq) * (GPS_L1_HZ / carrier_freq); + + /* correct pseudorange */ + nav_meas[i].raw_pseudorange -= iono_correction; + /* correct carrier phase based on iono correction */ + double cp_correction = iono_correction * (carrier_freq / GPS_C); + nav_meas[i].raw_carrier_phase -= cp_correction; + /* correct Doppler (sign opposite to pseudorange correction) */ + nav_meas[i].raw_measured_doppler += + iono_correction_delta * (carrier_freq / GPS_C); + nav_meas[i].raw_computed_doppler += + iono_correction_delta * (carrier_freq / GPS_C); + log_debug("%u: I %10.5f", i, iono_correction); + } +} + +void correct_tropo(const double* pos_ecef, + u8 n_meas, + navigation_measurement_t* nav_meas) { + double az; + double el; + double az0; + double el0; + static const double h = 1.0; /* length of the finite difference, seconds */ + + double pos_llh[3]; + wgsecef2llh(pos_ecef, pos_llh); + + for (u8 i = 0; i < n_meas; i++) { + /* this signal's frequency */ + double carrier_freq = sid_to_carr_freq(nav_meas[i].sid); + + /* calculate current azimuth and elevation of SV */ + wgsecef2azel(nav_meas[i].sat_pos, pos_ecef, &az, &el); + /* calculate past azimuth and elevation of SV */ + calculate_prev_sat_pos(&nav_meas[i], h, pos_ecef, &az0, &el0); + + /* compute day of year from gps time */ + double doy = (double)gps2doy(&nav_meas[i].tot); + + /* calculate tropo correction. + * ellipsoidal height is used due to lack of a geoid model */ + double tropo_correction = calc_troposphere(doy, pos_llh[0], pos_llh[2], el); + /* finite differences estimate of tropo correction time derivative */ + double tropo_correction_delta = + (tropo_correction - + calc_troposphere(doy, pos_llh[0], pos_llh[2], el0)) / + h; + /* correct pseudorange */ + nav_meas[i].raw_pseudorange -= tropo_correction; + /* correct carrier phase based on tropo correction */ + double cp_correction = tropo_correction * (carrier_freq / GPS_C); + /* sign here is opposite to normal due to + * Piksi's unusual sign convention on carrier phase */ + nav_meas[i].raw_carrier_phase += cp_correction; + /* correct Doppler (sign opposite to pseudorange correction) */ + nav_meas[i].raw_measured_doppler += + tropo_correction_delta * (carrier_freq / GPS_C); + nav_meas[i].raw_computed_doppler += + tropo_correction_delta * (carrier_freq / GPS_C); + log_debug("%u: T %10.5f", i, tropo_correction); + } +} diff --git a/src/ephemeris.c b/src/ephemeris.c index b872a7e..9d95b92 100644 --- a/src/ephemeris.c +++ b/src/ephemeris.c @@ -48,6 +48,8 @@ #define EPHEMERIS_INVALID_IOD_LOG_MESSAGE \ "invalid IOD ephemeris (v:%d, fi:%d, [%d, %f], iodc:%d, iode:%d), [%d, %f]" +const double SBAS_FIT_INTERVAL_SECONDS = 360.0; + /* Galileo OS SIS ICD, Table 71 */ enum gal_data_validity_status_t { GAL_DVS_NAVIGATION_DATA_VALID, @@ -936,6 +938,39 @@ ephemeris_status_t get_ephemeris_status_t(const ephemeris_t *e) { return EPH_VALID; } +/** Calculates the beginning and end of the ephemeris validity window. Given a + * reference gps time to ensure the week numbers align. + * + * \param e Ephemeris struct + * \param t The current GPS time. This is used to align gps week number. + */ +ephemeris_validity_window_t ephemeris_validity_window(const ephemeris_t *e, + gps_time_t *t) { + gps_time_t toe = e->toe; + fake_gps_wns(&toe, t); + + gps_time_t bgn = toe; + gps_time_t end = toe; + + if (IS_GPS(e->sid) || IS_QZSS(e->sid) || IS_GLO(e->sid)) { + /* TOE is a middle of ephemeris validity interval */ + bgn.tow -= e->fit_interval / 2; + end.tow += e->fit_interval / 2; + } else if (IS_BDS2(e->sid) || IS_GAL(e->sid)) { + /* TOE is the beginning of ephemeris validity interval */ + end.tow += e->fit_interval; + } else if (IS_SBAS(e->sid)) { + end.tow += SBAS_FIT_INTERVAL_SECONDS; + } else { + assert(0); + } + normalize_gps_time(t); + normalize_gps_time(&bgn); + normalize_gps_time(&end); + + return (ephemeris_validity_window_t){bgn, end}; +} + /** Used internally by other ephemeris valid functions. Given a valid ephemeris, * is this ephemeris valid at gps time t? * diff --git a/src/geoid_model.c b/src/geoid_model.c index dca6549..42612c4 100644 --- a/src/geoid_model.c +++ b/src/geoid_model.c @@ -10,6 +10,8 @@ * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. */ +#include "geoid_model.h" + #include #include #include @@ -23,25 +25,19 @@ #define MIN_LAT (-90) #define MAX_LAT 90 -#ifdef GEOID_MODEL_15_MINUTE_RESOLUTION -/* Geoid model with geoid heights derived from EGM2008 (0.25 x 0.25 deg grid) */ -#include "geoid_model_15_minute.inc" -#else -/* Geoid model with geoid heights derived from EGM2008 (1 x 1 deg grid) */ -#include "geoid_model_1_degree.inc" -#endif /* GEOID_MODEL_15_MINUTE_RESOLUTION */ - /* * Get GEOID[x][y], accounting for wrap-around of longitude values */ -static inline float get_geoid_val(int x, int y) { - if (x >= (MAX_LON - MIN_LON) / LON_GRID_SPACING_DEG) { - x -= (int)((MAX_LON - MIN_LON) / LON_GRID_SPACING_DEG); +static inline float get_geoid_val(const struct GeoidModel *model, + int x, + int y) { + if (x >= (MAX_LON - MIN_LON) / model->lon_spacing) { + x -= (int)((MAX_LON - MIN_LON) / model->lon_spacing); } else if (x < 0) { - x += (int)((MAX_LON - MIN_LON) / LON_GRID_SPACING_DEG); + x += (int)((MAX_LON - MIN_LON) / model->lon_spacing); } - return GEOID[x][y]; + return model->data[x * model->n_lat + y]; } /* @@ -55,12 +51,13 @@ static inline float get_geoid_val(int x, int y) { * 'fx' and 'fy' specify the fractional offset with respect to the (x,y) * corner. */ -static float bilinear_interpolation(int x, int y, float fx, float fy) { +static float bilinear_interpolation( + const struct GeoidModel *model, int x, int y, float fx, float fy) { /* Get the values at the four corners */ - float southwest = get_geoid_val(x, y); - float southeast = get_geoid_val(x + 1, y); - float northwest = get_geoid_val(x, y + 1); - float northeast = get_geoid_val(x + 1, y + 1); + float southwest = get_geoid_val(model, x, y); + float southeast = get_geoid_val(model, x + 1, y); + float northwest = get_geoid_val(model, x, y + 1); + float northeast = get_geoid_val(model, x + 1, y + 1); /* Bilinear interpolation */ return (1 - fy) * ((1 - fx) * southwest + fx * southeast) + @@ -84,7 +81,9 @@ static double cubic_interpolation(double p[4], double x) { geoid_model_t get_geoid_model(void) { return GEOID_MODEL_EGM2008; } /* Return the geoid offset */ -float get_geoid_offset(double lat_rad, double lon_rad) { +static float get_geoid_offset_internal(const struct GeoidModel *model, + double lat_rad, + double lon_rad) { /* Convert to degrees, returning 0.0 if out of bounds */ float lat_deg = (float)(R2D * lat_rad); if (lat_deg > MAX_LAT || lat_deg < MIN_LAT) { @@ -104,8 +103,8 @@ float get_geoid_offset(double lat_rad, double lon_rad) { float fx, fy; // fractional offset from cell corners float ixf, iyf; // integer offset of cell corners - fy = modff((lat_deg - MIN_LAT) / LAT_GRID_SPACING_DEG, &iyf); - fx = modff((lon_deg - MIN_LON) / LON_GRID_SPACING_DEG, &ixf); + fy = modff((lat_deg - MIN_LAT) / model->lat_spacing, &iyf); + fx = modff((lon_deg - MIN_LON) / model->lon_spacing, &ixf); int ix = (int)ixf; int iy = (int)iyf; @@ -114,8 +113,8 @@ float get_geoid_offset(double lat_rad, double lon_rad) { * Special Case 1: if lat is +90 then use the geoid value directly (note: * at this latitude, height is same regardless of value of x) */ - if (iy == (int)((MAX_LAT - MIN_LAT) / LAT_GRID_SPACING_DEG)) { - return GEOID[ix][iy]; + if (iy == (int)((MAX_LAT - MIN_LAT) / model->lat_spacing)) { + return model->data[ix * model->n_lat + iy]; } /* @@ -142,10 +141,10 @@ float get_geoid_offset(double lat_rad, double lon_rad) { * (ix-1,iy ) (ix ,iy ) (ix+1,iy ) (ix+2,iy ) * (ix-1,iy-1) (ix ,iy-1) (ix+1,iy-1) (ix+2,iy-1) */ - if (iy > 0 && iy < (MAX_LAT - MIN_LAT) / LAT_GRID_SPACING_DEG - 1) { + if (iy > 0 && iy < (MAX_LAT - MIN_LAT) / model->lat_spacing - 1) { int y0, y1, y2, y3; - if (iy == (int)((MAX_LAT - MIN_LAT) / LAT_GRID_SPACING_DEG - 2)) { + if (iy == (int)((MAX_LAT - MIN_LAT) / model->lat_spacing - 2)) { /* * Special Case 2: for latitudes in range * [90 - 2 * LAT_GRID_SPACING, 90 - LAT_GRID_SPACING) @@ -170,22 +169,22 @@ float get_geoid_offset(double lat_rad, double lon_rad) { y3 = iy + 2; } - double heights[4][4] = {{get_geoid_val(ix - 1, y0), - get_geoid_val(ix - 1, y1), - get_geoid_val(ix - 1, y2), - get_geoid_val(ix - 1, y3)}, - {get_geoid_val(ix, y0), - get_geoid_val(ix, y1), - get_geoid_val(ix, y2), - get_geoid_val(ix, y3)}, - {get_geoid_val(ix + 1, y0), - get_geoid_val(ix + 1, y1), - get_geoid_val(ix + 1, y2), - get_geoid_val(ix + 1, y3)}, - {get_geoid_val(ix + 2, y0), - get_geoid_val(ix + 2, y1), - get_geoid_val(ix + 2, y2), - get_geoid_val(ix + 2, y3)}}; + double heights[4][4] = {{get_geoid_val(model, ix - 1, y0), + get_geoid_val(model, ix - 1, y1), + get_geoid_val(model, ix - 1, y2), + get_geoid_val(model, ix - 1, y3)}, + {get_geoid_val(model, ix, y0), + get_geoid_val(model, ix, y1), + get_geoid_val(model, ix, y2), + get_geoid_val(model, ix, y3)}, + {get_geoid_val(model, ix + 1, y0), + get_geoid_val(model, ix + 1, y1), + get_geoid_val(model, ix + 1, y2), + get_geoid_val(model, ix + 1, y3)}, + {get_geoid_val(model, ix + 2, y0), + get_geoid_val(model, ix + 2, y1), + get_geoid_val(model, ix + 2, y2), + get_geoid_val(model, ix + 2, y3)}}; double arr[4] = {cubic_interpolation(heights[0], fy), cubic_interpolation(heights[1], fy), @@ -197,5 +196,19 @@ float get_geoid_offset(double lat_rad, double lon_rad) { * is in range [-90,-90 + LAT_GRID_SPACING_DEG) or * [90 - LAT_GRID_SPACING, 90) */ - return bilinear_interpolation(ix, iy, fx, fy); + return bilinear_interpolation(model, ix, iy, fx, fy); +} + +float get_geoid_offset(double lat_rad, double lon_rad) { + return get_geoid_offset_1_degree(lat_rad, lon_rad); +} + +float get_geoid_offset_1_degree(double lat_rad, double lon_rad) { + return get_geoid_offset_internal( + get_geoid_model_1_degree(), lat_rad, lon_rad); +} + +float get_geoid_offset_15_minute(double lat_rad, double lon_rad) { + return get_geoid_offset_internal( + get_geoid_model_15_minute(), lat_rad, lon_rad); } diff --git a/src/geoid_model.h b/src/geoid_model.h new file mode 100644 index 0000000..83c9a5d --- /dev/null +++ b/src/geoid_model.h @@ -0,0 +1,25 @@ +#ifndef LIBSWIFTNAV_SRC_GEOID_MODEL_H +#define LIBSWIFTNAV_SRC_GEOID_MODEL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct GeoidModel { + const float *data; + float lat_spacing; + float lon_spacing; + size_t n_lat; + size_t n_lon; +}; + +const struct GeoidModel *get_geoid_model_15_minute(void); +const struct GeoidModel *get_geoid_model_1_degree(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/geoid_model_15_minute.c b/src/geoid_model_15_minute.c new file mode 100644 index 0000000..4e6a88f --- /dev/null +++ b/src/geoid_model_15_minute.c @@ -0,0 +1,13 @@ +#include "geoid_model_15_minute.inc" + +#include "geoid_model.h" + +static const struct GeoidModel model = { + .data = &GEOID[0][0], + .lat_spacing = LAT_GRID_SPACING_DEG, + .lon_spacing = LON_GRID_SPACING_DEG, + .n_lat = sizeof(GEOID[0]) / sizeof(GEOID[0][0]), + .n_lon = sizeof(GEOID) / sizeof(GEOID[0]), +}; + +const struct GeoidModel *get_geoid_model_15_minute(void) { return &model; } diff --git a/src/geoid_model_15_minute.inc b/src/geoid_model_15_minute.inc index 3066989..a76b5cb 100644 --- a/src/geoid_model_15_minute.inc +++ b/src/geoid_model_15_minute.inc @@ -1,7 +1,7 @@ /* File automatically generated by ../scripts/gtx_convert.pl, do not edit */ -static const float LAT_GRID_SPACING_DEG = 0.25; -static const float LON_GRID_SPACING_DEG = 0.25; +#define LAT_GRID_SPACING_DEG (0.25) +#define LON_GRID_SPACING_DEG (0.25) static const float GEOID[1441][721] = { diff --git a/src/geoid_model_1_degree.c b/src/geoid_model_1_degree.c new file mode 100644 index 0000000..d189846 --- /dev/null +++ b/src/geoid_model_1_degree.c @@ -0,0 +1,13 @@ +#include "geoid_model_1_degree.inc" + +#include "geoid_model.h" + +static const struct GeoidModel model = { + .data = &GEOID[0][0], + .lat_spacing = LAT_GRID_SPACING_DEG, + .lon_spacing = LON_GRID_SPACING_DEG, + .n_lat = sizeof(GEOID[0]) / sizeof(GEOID[0][0]), + .n_lon = sizeof(GEOID) / sizeof(GEOID[0]), +}; + +const struct GeoidModel *get_geoid_model_1_degree(void) { return &model; } diff --git a/src/geoid_model_1_degree.inc b/src/geoid_model_1_degree.inc index 7f99ae3..aa8a316 100644 --- a/src/geoid_model_1_degree.inc +++ b/src/geoid_model_1_degree.inc @@ -1,7 +1,7 @@ /* File automatically generated by ../scripts/gtx_convert.pl, do not edit */ -static const float LAT_GRID_SPACING_DEG = 1; -static const float LON_GRID_SPACING_DEG = 1; +#define LAT_GRID_SPACING_DEG (1) +#define LON_GRID_SPACING_DEG (1) static const float GEOID[361][181] = { diff --git a/src/gnss_time.c b/src/gnss_time.c index 7d59b34..3b411d5 100644 --- a/src/gnss_time.c +++ b/src/gnss_time.c @@ -507,6 +507,53 @@ u16 gps_adjust_week_cycle256(u16 wn_raw, u16 wn_ref) { return wn_raw + 256 * ((wn_ref + 255 - wn_raw) / 256); } +/** + * Converts a decimal year to a MJD (modified Julian date). + * + * \param epoch_years The epoch in decimal years representation. + * \return The epoch in MJD representation. + */ +double decimal_year_to_mjd(const double epoch_years) { + const double integer_year = floor(epoch_years); + const double fractional_year = epoch_years - integer_year; + const double mjd_start_of_year = + date2mjd((int32_t)integer_year, 1, 1, 0, 0, 0); + const double mjd_start_of_following_year = + date2mjd((int32_t)integer_year + 1, 1, 1, 0, 0, 0); + const double mjd_per_year = mjd_start_of_following_year - mjd_start_of_year; + const double epoch_mjd = mjd_start_of_year + (fractional_year * mjd_per_year); + return epoch_mjd; +} + +/** + * Converts a GPS time to a decimal year. + * + * \param gps_time The GPS epoch to convert. + * \return The epoch in decimal years representation. + */ +double gps_time_to_decimal_years(const gps_time_t *time) { + utc_tm utc; + make_utc_tm(time, &utc); + double days_in_year = YEAR_DAYS; + + if (is_leap_year(utc.year)) { + days_in_year = LEAP_YEAR_DAYS; + } + + return (double)utc.year + (double)utc.year_day / days_in_year; +} + +/** + * Converts a epoch represented as a decimal year to a GPS time. + * + * \param years The epoch in decimal years representation. + * \return The epoch in GPS time representation. + */ +gps_time_t decimal_years_to_gps_time(const double years) { + const double mjd = decimal_year_to_mjd(years); + return mjd2gps(mjd); +} + /** Transformation of GLONASS-M current data information into gps_time_t. * * Reference: GLONASS ICD Edition 5.1 2008 diff --git a/src/signal.c b/src/signal.c index 38478b0..95b9b24 100644 --- a/src/signal.c +++ b/src/signal.c @@ -22,11 +22,12 @@ /** Element in the code data table. */ typedef struct { /* NOLINT(clang-analyzer-optin.performance.Padding) */ + enum code_e code; constellation_t constellation; u16 sat_count; u16 sig_count; u16 sat_start; - const char str[12]; + const char str[13]; double carr_freq; u32 chip_count; double chip_rate; @@ -42,862 +43,1045 @@ typedef struct { /* NOLINT(clang-analyzer-optin.performance.Padding) */ * "Reference code and phase alignment by constellation and frequency band" */ static const code_table_element_t code_table[CODE_COUNT + 1] = { - /** GPS */ - [CODE_GPS_L1CA] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1CA, - GPS_FIRST_PRN, - "GPS L1CA", - GPS_L1_HZ, - GPS_L1CA_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - true, - GPS_L1CA_PRN_PERIOD_MS, - GPS_L1_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L1C in [1]) */ - true}, - [CODE_AUX_GPS] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1CA, - GPS_FIRST_PRN, - "GPS AUX", - GPS_L1_HZ, - GPS_L1CA_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - false, - GPS_L1CA_PRN_PERIOD_MS, - GPS_L1_DOPPLER_MAX_HZ, - 0.f, /* assuming CODE_GPS_L1CA */ - false}, - [CODE_GPS_L1CI] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1C, - GPS_FIRST_PRN, - "GPS L1CI", - GPS_L1_HZ, - GPS_L1C_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - false, - GPS_L1C_PRN_PERIOD_MS, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1S in [1] */ - false}, - [CODE_GPS_L1CQ] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1C, - GPS_FIRST_PRN, - "GPS L1CQ", - GPS_L1_HZ, - 0, - 0, - false, - 0, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1L in [1] */ - false}, - [CODE_GPS_L1CX] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1C, - GPS_FIRST_PRN, - "GPS L1C", - GPS_L1_HZ, - 0, - 0, - false, - 0, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1X in [1] */ - false}, - [CODE_GPS_L2CM] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L2C, - GPS_FIRST_PRN, - "GPS L2CM", - GPS_L2_HZ, - GPS_L2CM_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - false, - GPS_L2CM_PRN_PERIOD_MS, - (float)GPS_L2_DOPPLER_MAX_HZ, - -0.25f, /* see L2S in [1] */ - true}, - [CODE_GPS_L2CL] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L2C, - GPS_FIRST_PRN, - "GPS L2CL", - GPS_L2_HZ, - GPS_L2CL_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - false, - GPS_L2CL_PRN_PERIOD_MS, - (float)GPS_L2_DOPPLER_MAX_HZ, - -0.25f, /* see L2L in [1] */ - false}, - [CODE_GPS_L2CX] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L2C, - GPS_FIRST_PRN, - "GPS L2C", - GPS_L2_HZ, - 0, - 0, - false, - 0, - (float)GPS_L2_DOPPLER_MAX_HZ, - -0.25f, /* see L2X [1] */ - false}, - [CODE_GPS_L5I] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L5, - GPS_FIRST_PRN, - "GPS L5I", - GPS_L5_HZ, - GPS_L5_CHIPS_NUM, - GPS_L5_CHIPPING_RATE, - false, - GPS_L5_PRN_PERIOD_MS, - (float)GPS_L5_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L5I in [1]) */ - false}, - [CODE_GPS_L5Q] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L5, - GPS_FIRST_PRN, - "GPS L5Q", - GPS_L5_HZ, - 0, - 0, - false, - 0, - (float)GPS_L5_DOPPLER_MAX_HZ, - -0.25f, /* see L5Q in [1] */ - false}, - [CODE_GPS_L5X] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L5, - GPS_FIRST_PRN, - "GPS L5", - GPS_L5_HZ, - 0, - 0, - false, - 0, - (float)GPS_L5_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GPS_L5I (see L5X in [1])*/ - false}, - [CODE_GPS_L1P] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L1P, - GPS_FIRST_PRN, - "GPS L1P", - GPS_L1_HZ, - 0, - 0, - false, - 0, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1P in [1] */ - false}, - [CODE_GPS_L2P] = {CONSTELLATION_GPS, - NUM_SATS_GPS, - NUM_SIGNALS_GPS_L2P, - GPS_FIRST_PRN, - "GPS L2P", - GPS_L2_HZ, - 0, - 0, - false, - 0, - (float)GPS_L2_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L2P in [1]) */ - false}, - - /** SBAS */ - [CODE_SBAS_L1CA] = {CONSTELLATION_SBAS, - NUM_SATS_SBAS, - NUM_SIGNALS_SBAS_L1CA, - SBAS_FIRST_PRN, - "SBAS L1", - SBAS_L1_HZ, - SBAS_L1CA_CHIPS_NUM, - SBAS_L1CA_CHIPPING_RATE, - true, - SBAS_L1CA_PRN_PERIOD_MS, - SBAS_L1_DOPPLER_MAX_HZ, - 0.f, /* reference signal */ - true}, - [CODE_AUX_SBAS] = {CONSTELLATION_SBAS, - NUM_SATS_SBAS, - NUM_SIGNALS_SBAS_L1CA, - SBAS_FIRST_PRN, - "SBAS AUX", - SBAS_L1_HZ, - SBAS_L1CA_CHIPS_NUM, - SBAS_L1CA_CHIPPING_RATE, - false, - SBAS_L1CA_PRN_PERIOD_MS, - SBAS_L1_DOPPLER_MAX_HZ, - 0.f, /* not used */ - false}, - [CODE_SBAS_L5I] = {CONSTELLATION_SBAS, - NUM_SATS_SBAS, - NUM_SIGNALS_SBAS_L5, - SBAS_FIRST_PRN, - "SBAS L5I", - SBAS_L5_HZ, - SBAS_L5_CHIPS_NUM, - SBAS_L5_CHIPPING_RATE, - false, - SBAS_L5_PRN_PERIOD_MS, - (float)SBAS_L5_DOPPLER_MAX_HZ, - 0.f, /* reference signal */ - true}, - [CODE_SBAS_L5Q] = {CONSTELLATION_SBAS, - NUM_SATS_SBAS, - NUM_SIGNALS_SBAS_L5, - SBAS_FIRST_PRN, - "SBAS L5Q", - SBAS_L5_HZ, - SBAS_L5_CHIPS_NUM, - SBAS_L5_CHIPPING_RATE, - false, - SBAS_L5_PRN_PERIOD_MS, - (float)SBAS_L5_DOPPLER_MAX_HZ, - -0.25f, /* not used */ - false}, - [CODE_SBAS_L5X] = {CONSTELLATION_SBAS, - NUM_SATS_SBAS, - NUM_SIGNALS_SBAS_L5, - SBAS_FIRST_PRN, - "SBAS L5", - SBAS_L5_HZ, - SBAS_L5_CHIPS_NUM, - SBAS_L5_CHIPPING_RATE, - false, - SBAS_L5_PRN_PERIOD_MS, - (float)SBAS_L5_DOPPLER_MAX_HZ, - 0.f, /* must be aligned with CODE_SBAS_L5I */ - false}, - - /** Glonass */ - [CODE_GLO_L1OF] = {CONSTELLATION_GLO, - NUM_SATS_GLO, - NUM_FREQ_GLO_L1OF, - GLO_FIRST_PRN, - "GLO L1OF", - GLO_L1_HZ, - GLO_CA_CHIPS_NUM, - GLO_CA_CHIPPING_RATE, - true, - GLO_PRN_PERIOD_MS, - GLO_L1_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L1C in [1]) */ - true}, - [CODE_GLO_L2OF] = {CONSTELLATION_GLO, - NUM_SATS_GLO, - NUM_FREQ_GLO_L2OF, - GLO_FIRST_PRN, - "GLO L2OF", - GLO_L2_HZ, - GLO_CA_CHIPS_NUM, - GLO_CA_CHIPPING_RATE, - false, - GLO_PRN_PERIOD_MS, - (float)GLO_L2_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L2C in [1]) */ - true}, - [CODE_GLO_L1P] = {CONSTELLATION_GLO, - NUM_SATS_GLO, - NUM_FREQ_GLO_L1OF, - GLO_FIRST_PRN, - "GLO L1P", - GLO_L1_HZ, - 0, - 0, - false, - 0, - GLO_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1P in [1] */ - false}, - [CODE_GLO_L2P] = {CONSTELLATION_GLO, - NUM_SATS_GLO, - NUM_FREQ_GLO_L2OF, - GLO_FIRST_PRN, - "GLO L2P", - GLO_L2_HZ, - 0, - 0, - false, - 0, - (float)GLO_L2_DOPPLER_MAX_HZ, - 0.25f, /* see L2P in [1] */ - false}, - - /** Galileo */ - [CODE_GAL_E1B] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E1, - GAL_FIRST_PRN, - "GAL E1B", - GAL_E1_HZ, - GAL_E1B_CHIPS_NUM, - GAL_E1_CHIPPING_RATE, - true, - GAL_E1B_PRN_PERIOD_MS, - GAL_E1_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L1B in [1]) */ - true}, - [CODE_GAL_E1C] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E1, - GAL_FIRST_PRN, - "GAL E1C", - GAL_E1_HZ, - 0, - 0, - false, - 0, - GAL_E1_DOPPLER_MAX_HZ, - 0.5f, /* see L1C in [1] */ - false}, - [CODE_GAL_E1X] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E1, - GAL_FIRST_PRN, - "GAL E1", - GAL_E1_HZ, - 0, - 0, - false, - 0, - GAL_E1_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GAL_E1B (see L1X in [1])*/ - false}, - [CODE_AUX_GAL] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E1, - GAL_FIRST_PRN, - "GAL AUX", - GAL_E1_HZ, - GAL_E1B_CHIPS_NUM, - GAL_E1_CHIPPING_RATE, - false, - GAL_E1B_PRN_PERIOD_MS, - GAL_E1_DOPPLER_MAX_HZ, - 0.f, /* assuming CODE_GAL_E1B */ - false}, - [CODE_GAL_E6B] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E6, - GAL_FIRST_PRN, - "GAL E6B", - GAL_E6_HZ, - GAL_E6_CHIPS_NUM, - GAL_E6_CHIPPING_RATE, - false, - GAL_E6B_PRN_PERIOD_MS, - (float)GAL_E6_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L6B in [1]) */ - true}, - [CODE_GAL_E6C] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E6, - GAL_FIRST_PRN, - "GAL E6C", - GAL_E6_HZ, - 0, - 0, - false, - 0, - (float)GAL_E6_DOPPLER_MAX_HZ, - -0.5f, /* see L6C in [1] */ - false}, - [CODE_GAL_E6X] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E6, - GAL_FIRST_PRN, - "GAL E6", - GAL_E6_HZ, - 0, - 0, - false, - 0, - (float)GAL_E6_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GAL_E6B (see L6X in [1])*/ - false}, - [CODE_GAL_E7I] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E7, - GAL_FIRST_PRN, - "GAL E5bI", - GAL_E7_HZ, - GAL_E7_CHIPS_NUM, - GAL_E7_CHIPPING_RATE, - false, - GAL_E7I_PRN_PERIOD_MS, - (float)GAL_E7_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L7I in [1]) */ - true}, - [CODE_GAL_E7Q] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E7, - GAL_FIRST_PRN, - "GAL E5bQ", - GAL_E7_HZ, - 0, - 0, - false, - 0, - (float)GAL_E7_DOPPLER_MAX_HZ, - -0.25f, /* see L7Q in [1] */ - false}, - [CODE_GAL_E7X] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E7, - GAL_FIRST_PRN, - "GAL E5b", - GAL_E7_HZ, - 0, - 0, - false, - 0, - (float)GAL_E7_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GAL_E7I (see L7X in [1])*/ - false}, - [CODE_GAL_E8I] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E8, - GAL_FIRST_PRN, - "GAL E8I", - GAL_E8_HZ, - 0, - 0, - false, - 0, - (float)GAL_E8_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L8I in [1]) */ - false}, - [CODE_GAL_E8Q] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E8, - GAL_FIRST_PRN, - "GAL E8Q", - GAL_E8_HZ, - 0, - 0, - false, - 0, - (float)GAL_E8_DOPPLER_MAX_HZ, - -0.25f, /* see L8Q in [1] */ - false}, - [CODE_GAL_E8X] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E8, - GAL_FIRST_PRN, - "GAL E8", - GAL_E8_HZ, - 0, - 0, - false, - 0, - (float)GAL_E8_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GAL_E8Q (see L8X in [1])*/ - false}, - [CODE_GAL_E5I] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E5, - GAL_FIRST_PRN, - "GAL E5aI", - GAL_E5_HZ, - GAL_E5_CHIPS_NUM, - GAL_E5_CHIPPING_RATE, - false, - GAL_E5I_PRN_PERIOD_MS, - (float)GAL_E5_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L5I in [1]) */ - true}, - [CODE_GAL_E5Q] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E5, - GAL_FIRST_PRN, - "GAL E5aQ", - GAL_E5_HZ, - 0, - 0, - false, - 0, - (float)GAL_E5_DOPPLER_MAX_HZ, - -0.25f, /* see L5Q in [1] */ - false}, - [CODE_GAL_E5X] = {CONSTELLATION_GAL, - NUM_SATS_GAL, - NUM_SIGNALS_GAL_E5, - GAL_FIRST_PRN, - "GAL E5a", - GAL_E5_HZ, - 0, - 0, - false, - 0, - (float)GAL_E5_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_GAL_E5I (see L5X in [1])*/ - false}, - - /** Beidou */ - [CODE_BDS2_B1] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS2_B1, - BDS_FIRST_PRN, - "BDS B1", - BDS2_B1I_HZ, - BDS2_B1I_CHIPS_NUM, - BDS2_B1I_CHIPPING_RATE, - true, - BDS2_B1I_PRN_PERIOD_MS, - BDS2_B1I_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L2I in [1]) */ - true}, - [CODE_AUX_BDS] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS2_B1, - BDS_FIRST_PRN, - "BDS AUX", - BDS2_B1I_HZ, - BDS2_B1I_CHIPS_NUM, - BDS2_B1I_CHIPPING_RATE, - false, - BDS2_B1I_PRN_PERIOD_MS, - BDS2_B1I_DOPPLER_MAX_HZ, - 0.f, /* assuming CODE_BDS2_B1 */ - false}, - [CODE_BDS2_B2] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS2_B2, - BDS_FIRST_PRN, - "BDS B2", - BDS2_B2_HZ, - BDS2_B2_CHIPS_NUM, - BDS2_B2_CHIPPING_RATE, - false, - BDS2_B2_PRN_PERIOD_MS, - (float)BDS2_B2_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L7I in [1]) */ - true}, - [CODE_BDS3_B1CI] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B1C, - BDS_FIRST_PRN, - "BDS3 B1CI", - BDS3_B1C_HZ, - BDS3_B1C_CHIPS_NUM, - BDS3_B1C_CHIPPING_RATE, - false, - BDS3_B1C_PRN_PERIOD_MS, - BDS3_B1C_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - [CODE_BDS3_B1CQ] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B1C, - BDS_FIRST_PRN, - "BDS3 B1CQ", - BDS3_B1C_HZ, - 0, - 0, - false, - 0, - BDS3_B1C_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - [CODE_BDS3_B1CX] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B1C, - BDS_FIRST_PRN, - "BDS3 B1C", - BDS3_B1C_HZ, - 0, - 0, - false, - 0, - BDS3_B1C_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - [CODE_BDS3_B3I] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B3, - BDS_FIRST_PRN, - "BDS3 B3I", - BDS3_B3_HZ, - BDS3_B3_CHIPS_NUM, - BDS3_B3_CHIPPING_RATE, - false, - BDS3_B3_PRN_PERIOD_MS, - (float)BDS3_B3_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L6I in [1]) */ - false}, - [CODE_BDS3_B3Q] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B3, - BDS_FIRST_PRN, - "BDS3 B3Q", - BDS3_B3_HZ, - 0, - 0, - false, - 0, - (float)BDS3_B3_DOPPLER_MAX_HZ, - -0.25f, /* see L6Q in [1] */ - false}, - [CODE_BDS3_B3X] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B3, - BDS_FIRST_PRN, - "BDS3 B3", - BDS3_B3_HZ, - 0, - 0, - false, - 0, - (float)BDS3_B3_DOPPLER_MAX_HZ, - 0.f, /*must be aligned to CODE_BDS3_B3I (L6X in [1]) */ - false}, - [CODE_BDS3_B7I] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B7, - BDS_FIRST_PRN, - "BDS3 B7I", - BDS3_B7_HZ, - BDS3_B7_CHIPS_NUM, - BDS3_B7_CHIPPING_RATE, - false, - BDS3_B7_PRN_PERIOD_MS, - (float)BDS3_B7_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L7I in [1]) */ - false}, - [CODE_BDS3_B7Q] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B7, - BDS_FIRST_PRN, - "BDS3 B7Q", - BDS3_B7_HZ, - 0, - 0, - false, - 0, - (float)BDS3_B7_DOPPLER_MAX_HZ, - -0.25f, /* see L7Q in [1] */ - false}, - [CODE_BDS3_B7X] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B7, - BDS_FIRST_PRN, - "BDS3 B7", - BDS3_B7_HZ, - BDS3_B7_CHIPS_NUM, - BDS3_B7_CHIPPING_RATE, - false, - BDS3_B7_PRN_PERIOD_MS, - (float)BDS3_B7_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_BDS3_B7I (L7X in [1]) */ - false}, - [CODE_BDS3_B5I] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B5, - BDS_FIRST_PRN, - "BDS3 B5I", - BDS3_B5_HZ, - BDS3_B5_CHIPS_NUM, - BDS3_B5_CHIPPING_RATE, - false, - BDS3_B5_PRN_PERIOD_MS, - (float)BDS3_B5_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - [CODE_BDS3_B5Q] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B5, - BDS_FIRST_PRN, - "BDS3 B5Q", - BDS3_B5_HZ, - 0, - 0, - false, - 0, - (float)BDS3_B5_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - [CODE_BDS3_B5X] = {CONSTELLATION_BDS, - NUM_SATS_BDS, - NUM_SIGNALS_BDS3_B5, - BDS_FIRST_PRN, - "BDS3 B5", - BDS3_B5_HZ, - 0, - 0, - false, - 0, - (float)BDS3_B5_DOPPLER_MAX_HZ, - 0.f, /* not used (interoperable with SBAS) */ - false}, - - /** QZS L1C/A has all the same characteristics as GPS L1 C/A */ - [CODE_QZS_L1CA] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L1, - QZS_FIRST_PRN, - "QZS L1CA", - QZS_L1_HZ, - QZS_L1CA_CHIPS_NUM, - QZS_L1CA_CHIPPING_RATE, - true, - QZS_L1CA_PRN_PERIOD_MS, - QZS_L1_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L1C in [1]) */ - true}, - [CODE_AUX_QZS] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L1, - QZS_FIRST_PRN, - "QZS AUX", - QZS_L1_HZ, - QZS_L1CA_CHIPS_NUM, - QZS_L1CA_CHIPPING_RATE, - false, - QZS_L1CA_PRN_PERIOD_MS, - QZS_L1_DOPPLER_MAX_HZ, - 0.f, /* assuming CODE_QZS_L1CA */ - false}, - [CODE_QZS_L1CI] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L1C, - QZS_FIRST_PRN, - "QZS L1CI", - QZS_L1_HZ, - GPS_L1C_CHIPS_NUM, - GPS_CA_CHIPPING_RATE, - false, - GPS_L1C_PRN_PERIOD_MS, - GPS_L1_DOPPLER_MAX_HZ, - 0.f, /* see L1S in [1] */ - false}, - [CODE_QZS_L1CQ] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L1C, - QZS_FIRST_PRN, - "QZS L1CQ", - QZS_L1_HZ, - 0, - 0, - false, - 0, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1L in [1] */ - false}, - [CODE_QZS_L1CX] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L1C, - QZS_FIRST_PRN, - "QZS L1CX", - QZS_L1_HZ, - 0, - 0, - false, - 0, - GPS_L1_DOPPLER_MAX_HZ, - 0.25f, /* see L1X in [1] */ - false}, - [CODE_QZS_L2CM] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L2C, - QZS_FIRST_PRN, - "QZS L2CM", - GPS_L2_HZ, - GPS_L2CM_CHIPS_NUM, - QZS_L1CA_CHIPPING_RATE, - false, - GPS_L2CM_PRN_PERIOD_MS, - (float)QZS_L2_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L2S in [1]) */ - false}, - [CODE_QZS_L2CL] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L2C, - QZS_FIRST_PRN, - "QZS L2CL", - GPS_L2_HZ, - GPS_L2CL_CHIPS_NUM, - QZS_L1CA_CHIPPING_RATE, - false, - GPS_L2CL_PRN_PERIOD_MS, - (float)QZS_L2_DOPPLER_MAX_HZ, - 0.f, /* see L2L in [1] */ - false}, - [CODE_QZS_L2CX] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L2C, - QZS_FIRST_PRN, - "QZS L2C", - GPS_L2_HZ, - 0, - 0, - false, - 0, - (float)QZS_L2_DOPPLER_MAX_HZ, - 0.f, /* see L2X in [1] */ - false}, - [CODE_QZS_L5I] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L5, - QZS_FIRST_PRN, - "QZS L5I", - QZS_L5_HZ, - GPS_L5_CHIPS_NUM, - GPS_L5_CHIPPING_RATE, - false, - GPS_L5_PRN_PERIOD_MS, - (float)QZS_L5_DOPPLER_MAX_HZ, - 0.f, /* reference signal (see L5I in [1]) */ - false}, - [CODE_QZS_L5Q] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L5, - QZS_FIRST_PRN, - "QZS L5Q", - QZS_L5_HZ, - 0, - 0, - false, - 0, - (float)QZS_L5_DOPPLER_MAX_HZ, - -0.25f, /* see L5Q in [1] */ - false}, - [CODE_QZS_L5X] = {CONSTELLATION_QZS, - NUM_SATS_QZS, - NUM_SIGNALS_QZS_L5, - QZS_FIRST_PRN, - "QZS L5", - QZS_L5_HZ, - 0, - 0, - false, - 0, - (float)QZS_L5_DOPPLER_MAX_HZ, - 0.f, /* must be aligned to CODE_QZS_L5I (see L5X in [1])*/ - false}, - [CODE_COUNT] = {CONSTELLATION_INVALID, - 0, - 0, - 0, - "Invalid code", - 0, - 0, - 0, - false, - 0, - 0.0, - 0.0, - false}, + /*[CODE_GPS_L1CA] =*/ + {CODE_GPS_L1CA, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1CA, + GPS_FIRST_PRN, + "GPS L1CA", + GPS_L1_HZ, + GPS_L1CA_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + true, + GPS_L1CA_PRN_PERIOD_MS, + GPS_L1_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L1C in [1]) */ true}, + + /*[CODE_GPS_L2CM] =*/ + {CODE_GPS_L2CM, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L2C, + GPS_FIRST_PRN, + "GPS L2CM", + GPS_L2_HZ, + GPS_L2CM_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + false, + GPS_L2CM_PRN_PERIOD_MS, + (float)GPS_L2_DOPPLER_MAX_HZ, + -0.25f, + /* see L2S in [1] */ true}, + + /*[CODE_SBAS_L1CA] =*/ + {CODE_SBAS_L1CA, + CONSTELLATION_SBAS, + NUM_SATS_SBAS, + NUM_SIGNALS_SBAS_L1CA, + SBAS_FIRST_PRN, + "SBAS L1", + SBAS_L1_HZ, + SBAS_L1CA_CHIPS_NUM, + SBAS_L1CA_CHIPPING_RATE, + true, + SBAS_L1CA_PRN_PERIOD_MS, + SBAS_L1_DOPPLER_MAX_HZ, + 0.f, + /* reference signal */ true}, + + /*[CODE_GLO_L1OF] =*/ + {CODE_GLO_L1OF, + CONSTELLATION_GLO, + NUM_SATS_GLO, + NUM_FREQ_GLO_L1OF, + GLO_FIRST_PRN, + "GLO L1OF", + GLO_L1_HZ, + GLO_CA_CHIPS_NUM, + GLO_CA_CHIPPING_RATE, + true, + GLO_PRN_PERIOD_MS, + GLO_L1_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L1C in [1]) */ true}, + + /*[CODE_GLO_L2OF] =*/ + {CODE_GLO_L2OF, + CONSTELLATION_GLO, + NUM_SATS_GLO, + NUM_FREQ_GLO_L2OF, + GLO_FIRST_PRN, + "GLO L2OF", + GLO_L2_HZ, + GLO_CA_CHIPS_NUM, + GLO_CA_CHIPPING_RATE, + false, + GLO_PRN_PERIOD_MS, + (float)GLO_L2_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L2C in [1]) */ true}, + + /*[CODE_GPS_L1P] =*/ + {CODE_GPS_L1P, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1P, + GPS_FIRST_PRN, + "GPS L1P", + GPS_L1_HZ, + 0, + 0, + false, + 0, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1P in [1] */ false}, + + /*[CODE_GPS_L2P] =*/ + {CODE_GPS_L2P, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L2P, + GPS_FIRST_PRN, + "GPS L2P", + GPS_L2_HZ, + 0, + 0, + false, + 0, + (float)GPS_L2_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L2P in [1]) */ false}, + + /*[CODE_GPS_L2CL] =*/ + {CODE_GPS_L2CL, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L2C, + GPS_FIRST_PRN, + "GPS L2CL", + GPS_L2_HZ, + GPS_L2CL_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + false, + GPS_L2CL_PRN_PERIOD_MS, + (float)GPS_L2_DOPPLER_MAX_HZ, + -0.25f, + /* see L2L in [1] */ false}, + + /*[CODE_GPS_L2CX] =*/ + {CODE_GPS_L2CX, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L2C, + GPS_FIRST_PRN, + "GPS L2C", + GPS_L2_HZ, + 0, + 0, + false, + 0, + (float)GPS_L2_DOPPLER_MAX_HZ, + -0.25f, + /* see L2X [1] */ false}, + + /*[CODE_GPS_L5I] =*/ + {CODE_GPS_L5I, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L5, + GPS_FIRST_PRN, + "GPS L5I", + GPS_L5_HZ, + GPS_L5_CHIPS_NUM, + GPS_L5_CHIPPING_RATE, + false, + GPS_L5_PRN_PERIOD_MS, + (float)GPS_L5_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L5I in [1]) */ false}, + + /*[CODE_GPS_L5Q] =*/ + {CODE_GPS_L5Q, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L5, + GPS_FIRST_PRN, + "GPS L5Q", + GPS_L5_HZ, + 0, + 0, + false, + 0, + (float)GPS_L5_DOPPLER_MAX_HZ, + -0.25f, + /* see L5Q in [1] */ false}, + + /*[CODE_GPS_L5X] =*/ + {CODE_GPS_L5X, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L5, + GPS_FIRST_PRN, + "GPS L5", + GPS_L5_HZ, + 0, + 0, + false, + 0, + (float)GPS_L5_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GPS_L5I (see L5X in [1])*/ false}, + + /*[CODE_BDS2_B1] =*/ + {CODE_BDS2_B1, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS2_B1, + BDS_FIRST_PRN, + "BDS B1", + BDS2_B1I_HZ, + BDS2_B1I_CHIPS_NUM, + BDS2_B1I_CHIPPING_RATE, + true, + BDS2_B1I_PRN_PERIOD_MS, + BDS2_B1I_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L2I in [1]) */ true}, + + /*[CODE_BDS2_B2] =*/ + {CODE_BDS2_B2, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS2_B2, + BDS_FIRST_PRN, + "BDS B2", + BDS2_B2_HZ, + BDS2_B2_CHIPS_NUM, + BDS2_B2_CHIPPING_RATE, + false, + BDS2_B2_PRN_PERIOD_MS, + (float)BDS2_B2_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L7I in [1]) */ true}, + + /*[CODE_GAL_E1B] =*/ + {CODE_GAL_E1B, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E1, + GAL_FIRST_PRN, + "GAL E1B", + GAL_E1_HZ, + GAL_E1B_CHIPS_NUM, + GAL_E1_CHIPPING_RATE, + true, + GAL_E1B_PRN_PERIOD_MS, + GAL_E1_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L1B in [1]) */ true}, + + /*[CODE_GAL_E1C] =*/ + {CODE_GAL_E1C, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E1, + GAL_FIRST_PRN, + "GAL E1C", + GAL_E1_HZ, + 0, + 0, + false, + 0, + GAL_E1_DOPPLER_MAX_HZ, + 0.5f, + /* see L1C in [1] */ false}, + + /*[CODE_GAL_E1X] =*/ + {CODE_GAL_E1X, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E1, + GAL_FIRST_PRN, + "GAL E1", + GAL_E1_HZ, + 0, + 0, + false, + 0, + GAL_E1_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GAL_E1B (see L1X in [1])*/ false}, + + /*[CODE_GAL_E6B] =*/ + {CODE_GAL_E6B, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E6, + GAL_FIRST_PRN, + "GAL E6B", + GAL_E6_HZ, + GAL_E6_CHIPS_NUM, + GAL_E6_CHIPPING_RATE, + false, + GAL_E6B_PRN_PERIOD_MS, + (float)GAL_E6_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L6B in [1]) */ true}, + + /*[CODE_GAL_E6C] =*/ + {CODE_GAL_E6C, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E6, + GAL_FIRST_PRN, + "GAL E6C", + GAL_E6_HZ, + 0, + 0, + false, + 0, + (float)GAL_E6_DOPPLER_MAX_HZ, + -0.5f, + /* see L6C in [1] */ false}, + + /*[CODE_GAL_E6X] =*/ + {CODE_GAL_E6X, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E6, + GAL_FIRST_PRN, + "GAL E6", + GAL_E6_HZ, + 0, + 0, + false, + 0, + (float)GAL_E6_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GAL_E6B (see L6X in [1])*/ false}, + + /*[CODE_GAL_E7I] =*/ + {CODE_GAL_E7I, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E7, + GAL_FIRST_PRN, + "GAL E5bI", + GAL_E7_HZ, + GAL_E7_CHIPS_NUM, + GAL_E7_CHIPPING_RATE, + false, + GAL_E7I_PRN_PERIOD_MS, + (float)GAL_E7_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L7I in [1]) */ true}, + + /*[CODE_GAL_E7Q] =*/ + {CODE_GAL_E7Q, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E7, + GAL_FIRST_PRN, + "GAL E5bQ", + GAL_E7_HZ, + 0, + 0, + false, + 0, + (float)GAL_E7_DOPPLER_MAX_HZ, + -0.25f, + /* see L7Q in [1] */ false}, + + /*[CODE_GAL_E7X] =*/ + {CODE_GAL_E7X, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E7, + GAL_FIRST_PRN, + "GAL E5b", + GAL_E7_HZ, + 0, + 0, + false, + 0, + (float)GAL_E7_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GAL_E7I (see L7X in [1])*/ false}, + + /*[CODE_GAL_E8I] =*/ + {CODE_GAL_E8I, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E8, + GAL_FIRST_PRN, + "GAL E8I", + GAL_E8_HZ, + 0, + 0, + false, + 0, + (float)GAL_E8_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L8I in [1]) */ false}, + + /*[CODE_GAL_E8Q] =*/ + {CODE_GAL_E8Q, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E8, + GAL_FIRST_PRN, + "GAL E8Q", + GAL_E8_HZ, + 0, + 0, + false, + 0, + (float)GAL_E8_DOPPLER_MAX_HZ, + -0.25f, + /* see L8Q in [1] */ false}, + + /*[CODE_GAL_E8X] =*/ + {CODE_GAL_E8X, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E8, + GAL_FIRST_PRN, + "GAL E8", + GAL_E8_HZ, + 0, + 0, + false, + 0, + (float)GAL_E8_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GAL_E8Q (see L8X in [1])*/ false}, + + /*[CODE_GAL_E5I] =*/ + {CODE_GAL_E5I, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E5, + GAL_FIRST_PRN, + "GAL E5aI", + GAL_E5_HZ, + GAL_E5_CHIPS_NUM, + GAL_E5_CHIPPING_RATE, + false, + GAL_E5I_PRN_PERIOD_MS, + (float)GAL_E5_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L5I in [1]) */ true}, + + /*[CODE_GAL_E5Q] =*/ + {CODE_GAL_E5Q, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E5, + GAL_FIRST_PRN, + "GAL E5aQ", + GAL_E5_HZ, + 0, + 0, + false, + 0, + (float)GAL_E5_DOPPLER_MAX_HZ, + -0.25f, + /* see L5Q in [1] */ false}, + + /*[CODE_GAL_E5X] =*/ + {CODE_GAL_E5X, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E5, + GAL_FIRST_PRN, + "GAL E5a", + GAL_E5_HZ, + 0, + 0, + false, + 0, + (float)GAL_E5_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_GAL_E5I (see L5X in [1])*/ false}, + + /*[CODE_GLO_L1P] =*/ + {CODE_GLO_L1P, + CONSTELLATION_GLO, + NUM_SATS_GLO, + NUM_FREQ_GLO_L1OF, + GLO_FIRST_PRN, + "GLO L1P", + GLO_L1_HZ, + 0, + 0, + false, + 0, + GLO_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1P in [1] */ false}, + + /*[CODE_GLO_L2P] =*/ + {CODE_GLO_L2P, + CONSTELLATION_GLO, + NUM_SATS_GLO, + NUM_FREQ_GLO_L2OF, + GLO_FIRST_PRN, + "GLO L2P", + GLO_L2_HZ, + 0, + 0, + false, + 0, + (float)GLO_L2_DOPPLER_MAX_HZ, + 0.25f, + /* see L2P in [1] */ false}, + + /*[CODE_QZS_L1CA] =*/ + {CODE_QZS_L1CA, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L1, + QZS_FIRST_PRN, + "QZS L1CA", + QZS_L1_HZ, + QZS_L1CA_CHIPS_NUM, + QZS_L1CA_CHIPPING_RATE, + true, + QZS_L1CA_PRN_PERIOD_MS, + QZS_L1_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L1C in [1]) */ true}, + + /*[CODE_QZS_L1CI] =*/ + {CODE_QZS_L1CI, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L1C, + QZS_FIRST_PRN, + "QZS L1CI", + QZS_L1_HZ, + GPS_L1C_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + false, + GPS_L1C_PRN_PERIOD_MS, + GPS_L1_DOPPLER_MAX_HZ, + 0.f, + /* see L1S in [1] */ false}, + + /*[CODE_QZS_L1CQ] =*/ + {CODE_QZS_L1CQ, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L1C, + QZS_FIRST_PRN, + "QZS L1CQ", + QZS_L1_HZ, + 0, + 0, + false, + 0, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1L in [1] */ false}, + + /*[CODE_QZS_L1CX] =*/ + {CODE_QZS_L1CX, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L1C, + QZS_FIRST_PRN, + "QZS L1CX", + QZS_L1_HZ, + 0, + 0, + false, + 0, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1X in [1] */ false}, + + /*[CODE_QZS_L2CM] =*/ + {CODE_QZS_L2CM, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L2C, + QZS_FIRST_PRN, + "QZS L2CM", + GPS_L2_HZ, + GPS_L2CM_CHIPS_NUM, + QZS_L1CA_CHIPPING_RATE, + false, + GPS_L2CM_PRN_PERIOD_MS, + (float)QZS_L2_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L2S in [1]) */ false}, + + /*[CODE_QZS_L2CL] =*/ + {CODE_QZS_L2CL, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L2C, + QZS_FIRST_PRN, + "QZS L2CL", + GPS_L2_HZ, + GPS_L2CL_CHIPS_NUM, + QZS_L1CA_CHIPPING_RATE, + false, + GPS_L2CL_PRN_PERIOD_MS, + (float)QZS_L2_DOPPLER_MAX_HZ, + 0.f, + /* see L2L in [1] */ false}, + + /*[CODE_QZS_L2CX] =*/ + {CODE_QZS_L2CX, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L2C, + QZS_FIRST_PRN, + "QZS L2C", + GPS_L2_HZ, + 0, + 0, + false, + 0, + (float)QZS_L2_DOPPLER_MAX_HZ, + 0.f, + /* see L2X in [1] */ false}, + + /*[CODE_QZS_L5I] =*/ + {CODE_QZS_L5I, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L5, + QZS_FIRST_PRN, + "QZS L5I", + QZS_L5_HZ, + GPS_L5_CHIPS_NUM, + GPS_L5_CHIPPING_RATE, + false, + GPS_L5_PRN_PERIOD_MS, + (float)QZS_L5_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L5I in [1]) */ false}, + + /*[CODE_QZS_L5Q] =*/ + {CODE_QZS_L5Q, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L5, + QZS_FIRST_PRN, + "QZS L5Q", + QZS_L5_HZ, + 0, + 0, + false, + 0, + (float)QZS_L5_DOPPLER_MAX_HZ, + -0.25f, + /* see L5Q in [1] */ false}, + + /*[CODE_QZS_L5X] =*/ + {CODE_QZS_L5X, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L5, + QZS_FIRST_PRN, + "QZS L5", + QZS_L5_HZ, + 0, + 0, + false, + 0, + (float)QZS_L5_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_QZS_L5I (see L5X in [1])*/ false}, + + /*[CODE_SBAS_L5I] =*/ + {CODE_SBAS_L5I, + CONSTELLATION_SBAS, + NUM_SATS_SBAS, + NUM_SIGNALS_SBAS_L5, + SBAS_FIRST_PRN, + "SBAS L5I", + SBAS_L5_HZ, + SBAS_L5_CHIPS_NUM, + SBAS_L5_CHIPPING_RATE, + false, + SBAS_L5_PRN_PERIOD_MS, + (float)SBAS_L5_DOPPLER_MAX_HZ, + 0.f, + /* reference signal */ true}, + + /*[CODE_SBAS_L5Q] =*/ + {CODE_SBAS_L5Q, + CONSTELLATION_SBAS, + NUM_SATS_SBAS, + NUM_SIGNALS_SBAS_L5, + SBAS_FIRST_PRN, + "SBAS L5Q", + SBAS_L5_HZ, + SBAS_L5_CHIPS_NUM, + SBAS_L5_CHIPPING_RATE, + false, + SBAS_L5_PRN_PERIOD_MS, + (float)SBAS_L5_DOPPLER_MAX_HZ, + -0.25f, + /* not used */ false}, + + /*[CODE_SBAS_L5X] =*/ + {CODE_SBAS_L5X, + CONSTELLATION_SBAS, + NUM_SATS_SBAS, + NUM_SIGNALS_SBAS_L5, + SBAS_FIRST_PRN, + "SBAS L5", + SBAS_L5_HZ, + SBAS_L5_CHIPS_NUM, + SBAS_L5_CHIPPING_RATE, + false, + SBAS_L5_PRN_PERIOD_MS, + (float)SBAS_L5_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned with CODE_SBAS_L5I */ false}, + + /*[CODE_BDS3_B1CI] =*/ + {CODE_BDS3_B1CI, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B1C, + BDS_FIRST_PRN, + "BDS3 B1CI", + BDS3_B1C_HZ, + BDS3_B1C_CHIPS_NUM, + BDS3_B1C_CHIPPING_RATE, + false, + BDS3_B1C_PRN_PERIOD_MS, + BDS3_B1C_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B1CQ] =*/ + {CODE_BDS3_B1CQ, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B1C, + BDS_FIRST_PRN, + "BDS3 B1CQ", + BDS3_B1C_HZ, + 0, + 0, + false, + 0, + BDS3_B1C_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B1CX] =*/ + {CODE_BDS3_B1CX, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B1C, + BDS_FIRST_PRN, + "BDS3 B1C", + BDS3_B1C_HZ, + 0, + 0, + false, + 0, + BDS3_B1C_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B5I] =*/ + {CODE_BDS3_B5I, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B5, + BDS_FIRST_PRN, + "BDS3 B5I", + BDS3_B5_HZ, + BDS3_B5_CHIPS_NUM, + BDS3_B5_CHIPPING_RATE, + false, + BDS3_B5_PRN_PERIOD_MS, + (float)BDS3_B5_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B5Q] =*/ + {CODE_BDS3_B5Q, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B5, + BDS_FIRST_PRN, + "BDS3 B5Q", + BDS3_B5_HZ, + 0, + 0, + false, + 0, + (float)BDS3_B5_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B5X] =*/ + {CODE_BDS3_B5X, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B5, + BDS_FIRST_PRN, + "BDS3 B5", + BDS3_B5_HZ, + 0, + 0, + false, + 0, + (float)BDS3_B5_DOPPLER_MAX_HZ, + 0.f, + /* not used (interoperable with SBAS) */ false}, + + /*[CODE_BDS3_B7I] =*/ + {CODE_BDS3_B7I, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B7, + BDS_FIRST_PRN, + "BDS3 B7I", + BDS3_B7_HZ, + BDS3_B7_CHIPS_NUM, + BDS3_B7_CHIPPING_RATE, + false, + BDS3_B7_PRN_PERIOD_MS, + (float)BDS3_B7_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L7I in [1]) */ false}, + + /*[CODE_BDS3_B7Q] =*/ + {CODE_BDS3_B7Q, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B7, + BDS_FIRST_PRN, + "BDS3 B7Q", + BDS3_B7_HZ, + 0, + 0, + false, + 0, + (float)BDS3_B7_DOPPLER_MAX_HZ, + -0.25f, + /* see L7Q in [1] */ false}, + + /*[CODE_BDS3_B7X] =*/ + {CODE_BDS3_B7X, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B7, + BDS_FIRST_PRN, + "BDS3 B7", + BDS3_B7_HZ, + BDS3_B7_CHIPS_NUM, + BDS3_B7_CHIPPING_RATE, + false, + BDS3_B7_PRN_PERIOD_MS, + (float)BDS3_B7_DOPPLER_MAX_HZ, + 0.f, + /* must be aligned to CODE_BDS3_B7I (L7X in [1]) */ false}, + + /*[CODE_BDS3_B3I] =*/ + {CODE_BDS3_B3I, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B3, + BDS_FIRST_PRN, + "BDS3 B3I", + BDS3_B3_HZ, + BDS3_B3_CHIPS_NUM, + BDS3_B3_CHIPPING_RATE, + false, + BDS3_B3_PRN_PERIOD_MS, + (float)BDS3_B3_DOPPLER_MAX_HZ, + 0.f, + /* reference signal (see L6I in [1]) */ false}, + + /*[CODE_BDS3_B3Q] =*/ + {CODE_BDS3_B3Q, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B3, + BDS_FIRST_PRN, + "BDS3 B3Q", + BDS3_B3_HZ, + 0, + 0, + false, + 0, + (float)BDS3_B3_DOPPLER_MAX_HZ, + -0.25f, + /* see L6Q in [1] */ false}, + + /*[CODE_BDS3_B3X] =*/ + {CODE_BDS3_B3X, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS3_B3, + BDS_FIRST_PRN, + "BDS3 B3", + BDS3_B3_HZ, + 0, + 0, + false, + 0, + (float)BDS3_B3_DOPPLER_MAX_HZ, + 0.f, + /*must be aligned to CODE_BDS3_B3I (L6X in [1]) */ false}, + + /*[CODE_GPS_L1CI] =*/ + {CODE_GPS_L1CI, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1C, + GPS_FIRST_PRN, + "GPS L1CI", + GPS_L1_HZ, + GPS_L1C_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + false, + GPS_L1C_PRN_PERIOD_MS, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1S in [1] */ false}, + + /*[CODE_GPS_L1CQ] =*/ + {CODE_GPS_L1CQ, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1C, + GPS_FIRST_PRN, + "GPS L1CQ", + GPS_L1_HZ, + 0, + 0, + false, + 0, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1L in [1] */ false}, + + /*[CODE_GPS_L1CX] =*/ + {CODE_GPS_L1CX, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1C, + GPS_FIRST_PRN, + "GPS L1C", + GPS_L1_HZ, + 0, + 0, + false, + 0, + GPS_L1_DOPPLER_MAX_HZ, + 0.25f, + /* see L1X in [1] */ false}, + + /*[CODE_AUX_GPS] =*/ + {CODE_AUX_GPS, + CONSTELLATION_GPS, + NUM_SATS_GPS, + NUM_SIGNALS_GPS_L1CA, + GPS_FIRST_PRN, + "GPS AUX", + GPS_L1_HZ, + GPS_L1CA_CHIPS_NUM, + GPS_CA_CHIPPING_RATE, + false, + GPS_L1CA_PRN_PERIOD_MS, + GPS_L1_DOPPLER_MAX_HZ, + 0.f, + /* assuming CODE_GPS_L1CA */ false}, + + /*[CODE_AUX_SBAS] =*/ + {CODE_AUX_SBAS, + CONSTELLATION_SBAS, + NUM_SATS_SBAS, + NUM_SIGNALS_SBAS_L1CA, + SBAS_FIRST_PRN, + "SBAS AUX", + SBAS_L1_HZ, + SBAS_L1CA_CHIPS_NUM, + SBAS_L1CA_CHIPPING_RATE, + false, + SBAS_L1CA_PRN_PERIOD_MS, + SBAS_L1_DOPPLER_MAX_HZ, + 0.f, + /* not used */ false}, + + /*[CODE_AUX_GAL] =*/ + {CODE_AUX_GAL, + CONSTELLATION_GAL, + NUM_SATS_GAL, + NUM_SIGNALS_GAL_E1, + GAL_FIRST_PRN, + "GAL AUX", + GAL_E1_HZ, + GAL_E1B_CHIPS_NUM, + GAL_E1_CHIPPING_RATE, + false, + GAL_E1B_PRN_PERIOD_MS, + GAL_E1_DOPPLER_MAX_HZ, + 0.f, + /* assuming CODE_GAL_E1B */ false}, + + /*[CODE_AUX_QZS] =*/ + {CODE_AUX_QZS, + CONSTELLATION_QZS, + NUM_SATS_QZS, + NUM_SIGNALS_QZS_L1, + QZS_FIRST_PRN, + "QZS AUX", + QZS_L1_HZ, + QZS_L1CA_CHIPS_NUM, + QZS_L1CA_CHIPPING_RATE, + false, + QZS_L1CA_PRN_PERIOD_MS, + QZS_L1_DOPPLER_MAX_HZ, + 0.f, + /* assuming CODE_QZS_L1CA */ false}, + + /*[CODE_AUX_BDS] =*/ + {CODE_AUX_BDS, + CONSTELLATION_BDS, + NUM_SATS_BDS, + NUM_SIGNALS_BDS2_B1, + BDS_FIRST_PRN, + "BDS AUX", + BDS2_B1I_HZ, + BDS2_B1I_CHIPS_NUM, + BDS2_B1I_CHIPPING_RATE, + false, + BDS2_B1I_PRN_PERIOD_MS, + BDS2_B1I_DOPPLER_MAX_HZ, + 0.f, + /* assuming CODE_BDS2_B1 */ false}, + + /*[CODE_COUNT] =*/ + {CODE_COUNT, + CONSTELLATION_INVALID, + 0, + 0, + 0, + "Invalid code", + 0, + 0, + 0, + false, + 0, + 0.0, + 0.0, + false}, }; static const char *constellation_table[CONSTELLATION_COUNT] = { @@ -918,7 +1102,24 @@ static const char *sub_constellation_table[SUB_CONSTELLATION_COUNT] = { [SUB_CONSTELLATION_GAL] = "GAL"}; const char *constellation_to_string(const constellation_t cons) { - return constellation_table[cons]; + switch (cons) { + case CONSTELLATION_GPS: + return "GPS"; + case CONSTELLATION_SBAS: + return "SBAS"; + case CONSTELLATION_GLO: + return "GLO"; + case CONSTELLATION_BDS: + return "BDS"; + case CONSTELLATION_QZS: + return "QZS"; + case CONSTELLATION_GAL: + return "GAL"; + case CONSTELLATION_INVALID: + case CONSTELLATION_COUNT: + default: + return "INVALID"; + } } constellation_t constellation_string_to_enum(const char *constellation_string) { @@ -931,7 +1132,26 @@ constellation_t constellation_string_to_enum(const char *constellation_string) { } const char *sub_constellation_to_string(const sub_constellation_t sub_cons) { - return sub_constellation_table[sub_cons]; + switch (sub_cons) { + case SUB_CONSTELLATION_GPS: + return "GPS"; + case SUB_CONSTELLATION_SBAS: + return "SBAS"; + case SUB_CONSTELLATION_GLO: + return "GLO"; + case SUB_CONSTELLATION_BDS2: + return "BDS2"; + case SUB_CONSTELLATION_BDS3: + return "BDS3"; + case SUB_CONSTELLATION_QZS: + return "QZS"; + case SUB_CONSTELLATION_GAL: + return "GAL"; + case SUB_CONSTELLATION_COUNT: + case SUB_CONSTELLATION_INVALID: + default: + return "INVALID"; + } } sub_constellation_t sub_constellation_string_to_enum( @@ -998,6 +1218,9 @@ static const sbas_prn_table_t sbas_prn_table[SBAS_COUNT] = { * https://www.pulsesat.com/satellites/s/a4ec8035-624e-4344-a22c-90d64b047b62/ */ [SBAS_GAGAN] = {{127, 128}}, + /* https://aim.koca.go.kr/eaipPub/Package/2025-03-19-AIRAC/html/eAIP/KR-ENR-4.3-en-GB.html + */ + [SBAS_KASS] = {{134}}, /* https://en.wikipedia.org/wiki/MTSAT_Satellite_Augmentation_System */ [SBAS_MSAS] = {{129, 137}}, }; @@ -1015,7 +1238,7 @@ static const char *unknown_str = "?"; * \return gnss_signal_t corresponding to the specified arguments. */ gnss_signal_t construct_sid(code_t code, u16 sat) { - gnss_signal_t sid = {.code = code, .sat = sat}; + gnss_signal_t sid = {.sat = sat, .code = code}; return sid; } @@ -1149,6 +1372,9 @@ u16 sid_to_code_index(gnss_signal_t sid) { * \return Constellation to which sid belongs. */ constellation_t sid_to_constellation(gnss_signal_t sid) { + if (!code_valid(sid.code)) { + return CONSTELLATION_INVALID; + } return code_table[sid.code].constellation; } @@ -1311,7 +1537,7 @@ const u8 *get_sbas_prn_list(sbas_system_t sbas_system) { sbas_system_t get_sbas_system(const gnss_signal_t sid) { assert(IS_SBAS(sid)); for (sbas_system_t sbas_system = (sbas_system_t)0; sbas_system < SBAS_COUNT; - sbas_system = sbas_system + 1) { + sbas_system = (sbas_system_t)(sbas_system + 1)) { if (is_value_in_array(sbas_prn_table[sbas_system].prn_list, MAX_SBAS_SATS_PER_SYSTEM, sid.sat)) { diff --git a/src/single_epoch_solver.c b/src/single_epoch_solver.c index 53c6229..b4f9093 100644 --- a/src/single_epoch_solver.c +++ b/src/single_epoch_solver.c @@ -1490,7 +1490,9 @@ static s8 calc_PVT_pred_internal(const u8 n_meas, soln->clock_offset = lsq_data.rx_state[3] / GPS_C; soln->clock_drift = lsq_data.rx_state[3 + n_states] / GPS_C; soln->clock_offset_var = lsq_data.V[3 * n_states + 3] / GPS_C / GPS_C; - soln->clock_drift_var = lsq_data.V_vel[3 * n_states + 3] / GPS_C / GPS_C; + if (!disable_velocity) { + soln->clock_drift_var = lsq_data.V_vel[3 * n_states + 3] / GPS_C / GPS_C; + } /* Correct the time of reception with the solved bias to get solution time */ soln->time = *tor; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dee996e..6e3c376 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,53 +1,4 @@ -if(libswiftnav_BUILD_TEST_LIBS) - add_subdirectory(common) -endif() - if(libswiftnav_BUILD_TESTS) - find_package(Threads) - - set(SRCS - check_almanac.c - check_bits.c - check_coord_system.c - check_decode_glo.c - check_edc.c - check_ephemeris.c - check_geoid_model.cc - check_glo_map.c - check_gnss_time.c - check_gnss_time_cpp.cc - check_ionosphere.c - check_linear_algebra.c - check_log.c - check_main.c - check_nav_meas.c - check_set.c - check_shm.c - check_sid_set.c - check_signal.c - check_subsystem_status_report.c - check_pvt.c - check_troposphere.c) - - swift_add_test(test-swiftnav-common - UNIT_TEST - POST_BUILD - SRCS ${SRCS} - LINK swiftnav::check-utils check Threads::Threads - ) - target_include_directories(test-swiftnav-common PRIVATE ${PROJECT_SOURCE_DIR}) - - swift_set_compile_options( - test-swiftnav-common - REMOVE - -Wconversion - -Wstack-protector - -Wfloat-equal - -Wsign-compare - ADD - -Wno-format-extra-args - ) - swift_add_test(test-swiftnav-pedantic UNIT_TEST SRCS check_pedantic.cc @@ -55,4 +6,36 @@ if(libswiftnav_BUILD_TESTS) ) swift_set_compile_options(test-swiftnav-pedantic ADD -pedantic) + +swift_add_test(test-swiftnav-common + UNIT_TEST + SRCS + test_almanac.cc + test_bits.cc + test_coord_system.cc + test_correct_iono_tropo.cc + test_decode_glo.cc + test_edc.cc + test_ephemeris.cc + test_geoid_model.cc + test_glo_map.cc + test_gnss_time.cc + test_gnss_time_cpp.cc + test_ionosphere.cc + test_linear_algebra.cc + test_log.cc + test_nav_meas.cc + test_pvt.cc + test_set.cc + test_shm.cc + test_sid_set.cc + test_signal.cc + test_subsystem_status_report.cc + test_troposphere.cc + LINK + swiftnav::swiftnav + gtest + gtest_main +) + endif(libswiftnav_BUILD_TESTS) diff --git a/tests/check_almanac.c b/tests/check_almanac.c deleted file mode 100644 index 7a973ab..0000000 --- a/tests/check_almanac.c +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include - -#include "check_suites.h" - -START_TEST(test_almanac_equal) { - almanac_t a; - almanac_t b; - - memset(&a, 0, sizeof(a)); - memset(&b, 0, sizeof(b)); - - fail_unless(almanac_equal(&a, &b), "Almanacs should be equal"); - - a.valid = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (valid)"); - memset(&a, 0, sizeof(a)); - - a.health_bits = 0x3f; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (health_bits)"); - memset(&a, 0, sizeof(a)); - - a.sid.sat = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (sid.sat)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (sid.band)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (sid.constellation)"); - memset(&a, 0, sizeof(a)); - - a.toa.wn = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (toa.wn)"); - memset(&a, 0, sizeof(a)); - - a.toa.tow = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (toa.tow)"); - memset(&a, 0, sizeof(a)); - - a.ura = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (ura)"); - memset(&a, 0, sizeof(a)); - - a.fit_interval = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (fit_interval)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.m0 = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.m0)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.ecc = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.ecc)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.sqrta = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.sqrta)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.omega0 = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.omega0)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.omegadot = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.omegadot)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.w = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.w)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.inc = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.inc)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.af0 = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.af0)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GPS_L1CA; - a.data.kepler.af1 = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (kepler.af1)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.pos[0] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.pos[0])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.pos[1] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.pos[1])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.pos[2] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.pos[2])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.vel[0] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.vel[0])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.vel[1] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.vel[1])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.vel[2] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.vel[2])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.acc[0] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.acc[0])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.acc[1] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.acc[1])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_SBAS_L1CA; - a.data.xyz.acc[2] = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (xyz.acc[2])"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.lambda = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (glo.lambda)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.t_lambda = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (glo.t_lambda)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.i = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (glo.i)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.t = 1; - fail_unless(!almanac_equal(&a, &b), "Almanacs should not be equal (glo.t)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.t_dot = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (glo.t_dot)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.epsilon = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (glo.epsilon)"); - memset(&a, 0, sizeof(a)); - - a.sid.code = CODE_GLO_L1OF; - a.data.glo.omega = 1; - fail_unless(!almanac_equal(&a, &b), - "Almanacs should not be equal (glo.omega)"); - memset(&a, 0, sizeof(a)); -} -END_TEST - -Suite *almanac_suite(void) { - Suite *s = suite_create("Almanac"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_almanac_equal); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_bits.c b/tests/check_bits.c deleted file mode 100644 index 2efd495..0000000 --- a/tests/check_bits.c +++ /dev/null @@ -1,403 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -START_TEST(test_parity) { - fail_unless(parity(0x00000000) == 0); - fail_unless(parity(0xFFFFFFFF) == 0); - fail_unless(parity(0x01010101) == 0); - fail_unless(parity(0x10101010) == 0); - fail_unless(parity(0x10A010A0) == 0); - - fail_unless(parity(0x10000000) == 1); - fail_unless(parity(0x00000001) == 1); - fail_unless(parity(0x70707000) == 1); - fail_unless(parity(0x0B0B0B00) == 1); - fail_unless(parity(0x00E00000) == 1); -} -END_TEST - -START_TEST(test_getbitu) { - u8 test_data[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; - - u32 ret; - - ret = getbitu(test_data, 0, 8); - fail_unless(ret == 0x01, "Test case 1 expected 0x01, got 0x%02X", ret); - - ret = getbitu(test_data, 4, 8); - fail_unless(ret == 0x12, "test case 2 expected 0x12, got 0x%02X", ret); - - ret = getbitu(test_data, 28, 16); - fail_unless(ret == 0x789A, "test case 3 expected 0x789A, got 0x%04X", ret); - - ret = getbitu(test_data, 12, 32); - fail_unless( - ret == 0x3456789A, "test case 4 expected 0x3456789A, got 0x%08X", ret); - - ret = getbitu(test_data, 10, 3); - fail_unless(ret == 0x4, "test case 5 expected 0x4, got 0x%01X", ret); - - ret = getbitu(test_data, 10, 13); - fail_unless(ret == 0x11A2, "test case 6 expected 0x11A2, got 0x%04X", ret); -} -END_TEST - -START_TEST(test_getbits) { - u8 test_data[] = {0x00, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF}; - - s32 ret; - - ret = getbits(test_data, 0, 8); - fail_unless(ret == 0, "Test case 1 expected 0, got %d", ret); - - ret = getbits(test_data, 13, 3); - fail_unless(ret == 3, "Test case 2 expected 3, got %d", ret); - - ret = getbits(test_data, 14, 3); - fail_unless(ret == -1, "Test case 3 expected -1, got %d", ret); - - ret = getbits(test_data, 14, 4); - fail_unless(ret == -2, "Test case 4 expected -2, got %d", ret); - - ret = getbits(test_data, 24, 32); - fail_unless(ret == -1, "Test case 5 expected -1, got %d", ret); -} -END_TEST - -START_TEST(test_setbitu) { - u8 test_data[10]; - u8 zeroes[10]; - - u32 ret; - unsigned seed = time(NULL); - - srand(seed); - - memset(zeroes, 0, sizeof(zeroes)); - memset(test_data, 0, sizeof(test_data)); - - for (unsigned len = 0; len <= 32; len++) { - for (unsigned pos = 0; pos < 48; pos++) { - u32 data = rand(); - - /* Set 'data' and check that we get the same value when we read it - * back */ - setbitu(test_data, pos, len, data); - /* Mask off bits higher than 'len' since they shouldn't be set when we - * read back */ - data &= (len == 32 ? ~0 : ((1 << len) - 1)); - ret = getbitu(test_data, pos, len); - fail_unless( - ret == data, - "test case 1 expected %04X, got 0x%04X (len %u, pos %u, seed %u)", - data, - ret, - len, - pos, - seed); - - /* Clear data and make sure that no additional bits have changed */ - setbitu(test_data, pos, len, 0); - fail_unless(!memcmp(test_data, zeroes, sizeof(test_data)), - "test case 2 not completely zeroed"); - } - } -} -END_TEST - -START_TEST(test_setbits) { - u8 test_data[10]; - - s32 ret; - - setbits(test_data, 14, 3, -1); - ret = getbits(test_data, 14, 3); - fail_unless(ret == -1, "Test case 1 expected -1, got %d", ret); - - setbits(test_data, 14, 8, 22); - ret = getbits(test_data, 14, 8); - fail_unless(ret == 22, "Test case 2 expected 22, got %d", ret); - - setbits(test_data, 24, 32, -1); - ret = getbits(test_data, 24, 32); - fail_unless(ret == -1, "Test case 3 expected -1, got %d", ret); -} -END_TEST - -START_TEST(test_setbitul) { - u8 test_data[64] = {0}; - u8 zeroes[64] = {0}; - - u64 ret = 0; - unsigned seed = time(NULL); - - srand(seed); - - for (unsigned len = 0; len <= sizeof(u64); len++) { - for (unsigned pos = 0; pos <= sizeof(u64); pos++) { - u64 data = (((u64)rand() << 32) | ((u64)rand())); - - /* Set 'data' and check that we get the same value when we read it - * back */ - setbitul(test_data, pos, len, data); - /* Mask off bits higher than 'len' since they shouldn't be set when we - * read back */ - data &= (len == 64 ? ~(u64)0 : (((u64)1 << len) - 1)); - ret = getbitul(test_data, pos, len); - fail_unless( - ret == data, - "test case 1 expected 0x%04X, got 0x%04X (len %u, pos %u, seed %u)", - (u32)data, - (u32)ret, - len, - pos, - seed); - - /* Clear data and make sure that no additional bits have changed */ - setbitul(test_data, pos, len, 0); - fail_unless(!memcmp(test_data, zeroes, sizeof(test_data)), - "test case 2 not completely zeroed"); - } - } -} -END_TEST - -START_TEST(test_setbitsl) { - u8 test_data[64] = {0}; - s64 ret = 0; - - s64 input = INT64_MIN; - setbitsl(test_data, 0, 64, input); - ret = getbitsl(test_data, 0, 64); - fail_unless(ret == input, - "Test case 1 expected %04X, got %04X", - (s32)input, - (s32)ret); - - ret = 0; - memset(test_data, 0, sizeof(test_data)); - input = 0xABCD; - setbitsl(test_data, 32, 8, input); - ret = getbitsl(test_data, 32, 8); - fail_unless(ret == (s8)input, - "Test case 2 expected 0x%04X, got 0x%04X", - (s32)input, - (s32)ret); - - // This test case should fail due to buffer overflow. setbitsl need fixing. - ret = 0; - memset(test_data, 0, sizeof(test_data)); - input = 0xABCD; - setbitsl(test_data, 56, 32, input); - ret = getbitsl(test_data, 56, 32); - fail_unless(ret == input, - "Test case 3 expected 0x%04X, got 0x%04X", - (s32)input, - (s32)ret); -} -END_TEST - -START_TEST(test_bitshl) { - u8 src0[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res0[] = {0xBE, 0xEF, 0x00, 0x00}; - - u8 src1[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res1[] = {0xEA, 0xDB, 0xEE, 0xF0}; - - u8 src2[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res2[] = {0xDB, 0xEE, 0xF0, 0x00}; - - u8 src3[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res3[] = {0xB6, 0xFB, 0xBC, 0x00}; - - bitshl(src0, sizeof(src0), 16); - fail_unless(0 == memcmp(src0, res0, 4), "Byte shift test"); - - bitshl(src1, sizeof(src1), 4); - fail_unless(0 == memcmp(src1, res1, 4), "4-bit shift"); - - bitshl(src2, sizeof(src2), 12); - fail_unless(0 == memcmp(src2, res2, 4), "12-bit shift"); - - bitshl(src3, sizeof(src3), 10); - fail_unless(0 == memcmp(src3, res3, 4), "10-bit shift"); -} -END_TEST - -START_TEST(test_bitcopy) { - u8 src0[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res0[] = {0xBE, 0xEF, 0xBE, 0xEF}; - - u8 src1[] = {0xDE, 0xAD, 0xBE, 0xEF}; - u8 res1[] = {0xEA, 0xDB, 0xEE, 0xFF}; - - u8 src2[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; - // u8 dst2[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; - u8 res2[] = {0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xAD}; - - bitcopy(src0, 0, src0, 16, 16); - fail_unless(0 == memcmp(src0, res0, 4), "16-bit copy"); - - bitcopy(src1, 0, src1, 4, 28); - fail_unless(0 == memcmp(src1, res1, 4), "28-bit copy"); - - bitcopy(src2, 0, src2, 8, 72); - fail_unless(0 == memcmp(src2, res2, 4), "72-bit copy"); -} -END_TEST - -START_TEST(test_count_bits_x) { - u8 src8[] = {0xDE, 0xAD, 0x12, 0xEF}; - u8 res8[] = {6, 5, 2, 7}; - - u16 src16[] = {0xDE05, 0xADF6, 0xBE32, 0xEF45}; - u8 res16[] = {8, 11, 9, 10}; - - u32 src32[] = {0xDE051234, 0x00000000, 0x00329300, 0x1F45A6C8}; - u8 res32[] = {13, 0, 7, 15}; - - u64 src64[] = {0xDE051234432150ED, - 0x0000000080000000, - 0x0032930000392300, - 0x10F14350A060C080}; - u8 res64[] = {26, 1, 14, 18}; - - for (unsigned i = 0; i < sizeof(src8) / sizeof(src8[0]); i++) { - u8 r = count_bits_u8(src8[i], 1); - fail_unless(res8[i] == r, "count_bits_u8(0x%x, 1) = %d", src8[i], r); - r = count_bits_u8(src8[i], 0); - fail_unless(8 - res8[i] == r, "count_bits_u8(0x%x, 0) = %d", src8[i], r); - } - - for (unsigned i = 0; i < sizeof(src16) / sizeof(src16[0]); i++) { - u8 r = count_bits_u16(src16[i], 1); - fail_unless(res16[i] == r, "count_bits_u16(0x%x, 1) = %d", src16[i], r); - r = count_bits_u16(src16[i], 0); - fail_unless( - 16 - res16[i] == r, "count_bits_u16(0x%x, 0) = %d", src16[i], r); - } - - for (unsigned i = 0; i < sizeof(src32) / sizeof(src32[0]); i++) { - u8 r = count_bits_u32(src32[i], 1); - fail_unless(res32[i] == r, "count_bits_u32(0x%x, 1) = %d", src32[i], r); - r = count_bits_u32(src32[i], 0); - fail_unless( - 32 - res32[i] == r, "count_bits_u32(0x%x, 0) = %d", src32[i], r); - } - - for (unsigned i = 0; i < sizeof(src64) / sizeof(src64[0]); i++) { - u8 r = count_bits_u64(src64[i], 1); - fail_unless( - res64[i] == r, "count_bits_u64(0x%" PRIx64 ", 1) = %d", src64[i], r); - r = count_bits_u64(src64[i], 0); - fail_unless(64 - res64[i] == r, - "count_bits_u64(0x%" PRIx64 ", 0) = %d", - src64[i], - r); - } -} -END_TEST - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wbitfield-constant-conversion" -#endif - -START_TEST(test_sign_extend32) { - fail_unless(BITS_SIGN_EXTEND_32(5, 0) == 0); - fail_unless(BITS_SIGN_EXTEND_32(5, 1) == 1); - fail_unless(BITS_SIGN_EXTEND_32(5, 15) == 15); - fail_unless(BITS_SIGN_EXTEND_32(5, 0x1F) == -1); - fail_unless(BITS_SIGN_EXTEND_32(5, 0x10) == -16); -} -END_TEST - -START_TEST(test_sign_extend64) { - fail_unless(BITS_SIGN_EXTEND_64(33, 0) == 0); - fail_unless(BITS_SIGN_EXTEND_64(33, 1) == 1); - fail_unless(BITS_SIGN_EXTEND_64(33, INT64_C(0xFFFFFFFF)) == - INT64_C(0xFFFFFFFF)); - fail_unless(BITS_SIGN_EXTEND_64(33, INT64_C(0x1FFFFFFFF)) == -INT64_C(0x1)); - fail_unless(BITS_SIGN_EXTEND_64(33, INT64_C(0x100000000)) == - -INT64_C(0x100000000)); -} -END_TEST - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -START_TEST(test_endianess) { -#ifdef __BYTE_ORDER__ // Available on gcc and clang, proabbly not other - // compilers -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - enum endianess expected_endianess = SWIFT_LITTLE_ENDIAN; - u16 u16_values[] = {0x1234, 0x3412, 0x3412}; - u32 u32_values[] = {0x12345678, 0x78563412, 0x78563412}; - u64 u64_values[] = { - 0x123456789abcdef0, 0xf0debc9a78563412, 0xf0debc9a78563412}; -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - enum endianess expected_endianess = SWIFT_BIG_ENDIAN; - u16 u16_values[] = {0x1234, 0x3412, 0x1234}; - u32 u32_values[] = {0x12345678, 0x78563412, 0x12345678}; - u64 u64_values[] = { - 0x123456789abcdef0, 0xf0debc9a78563412, 0x123456789abcdef0}; -#else -#error "__BYTE_ORDER__ not set properly by compiler" -#endif - fail_unless(get_endianess() == expected_endianess); - // Order of elements in value array is big, little, host - fail_unless(byte_swap_16(u16_values[0]) == u16_values[1]); - fail_unless(byte_swap_16(u16_values[1]) == u16_values[0]); - fail_unless(betoh_16(u16_values[0]) == u16_values[2]); - fail_unless(letoh_16(u16_values[1]) == u16_values[2]); - fail_unless(htobe_16(u16_values[2]) == u16_values[0]); - fail_unless(htole_16(u16_values[2]) == u16_values[1]); - - fail_unless(byte_swap_32(u32_values[0]) == u32_values[1]); - fail_unless(byte_swap_32(u32_values[1]) == u32_values[0]); - fail_unless(betoh_32(u32_values[0]) == u32_values[2]); - fail_unless(letoh_32(u32_values[1]) == u32_values[2]); - fail_unless(htobe_32(u32_values[2]) == u32_values[0]); - fail_unless(htole_32(u32_values[2]) == u32_values[1]); - - fail_unless(byte_swap_64(u64_values[0]) == u64_values[1]); - fail_unless(byte_swap_64(u64_values[1]) == u64_values[0]); - fail_unless(betoh_64(u64_values[0]) == u64_values[2]); - fail_unless(letoh_64(u64_values[1]) == u64_values[2]); - fail_unless(htobe_64(u64_values[2]) == u64_values[0]); - fail_unless(htole_64(u64_values[2]) == u64_values[1]); -#else -// No compiler indication of endianess, tests disabled -#endif -} -END_TEST - -Suite *bits_suite(void) { - Suite *s = suite_create("Bit Utils"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_parity); - tcase_add_test(tc_core, test_getbitu); - tcase_add_test(tc_core, test_getbits); - tcase_add_test(tc_core, test_setbitu); - tcase_add_test(tc_core, test_setbitul); - tcase_add_test(tc_core, test_setbits); - tcase_add_test(tc_core, test_setbitsl); - tcase_add_test(tc_core, test_bitshl); - tcase_add_test(tc_core, test_bitcopy); - tcase_add_test(tc_core, test_count_bits_x); - tcase_add_test(tc_core, test_sign_extend32); - tcase_add_test(tc_core, test_sign_extend64); - tcase_add_test(tc_core, test_endianess); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_coord_system.c b/tests/check_coord_system.c deleted file mode 100644 index acc8c88..0000000 --- a/tests/check_coord_system.c +++ /dev/null @@ -1,395 +0,0 @@ -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -/* Maximum allowable error in quantities with units of length (in meters). */ -#define MAX_DIST_ERROR_M 1e-6 -/* Maximum allowable error in quantities with units of angle (in sec of arc). - * 1 second of arc on the equator is ~31 meters. */ -#define MAX_ANGLE_ERROR_SEC 1e-7 -#define MAX_ANGLE_ERROR_RAD (MAX_ANGLE_ERROR_SEC * (D2R / 3600.0)) - -/* Semi-major axis. */ -#define EARTH_A 6378137.0 -/* Semi-minor axis. */ -#define EARTH_B 6356752.31424517929553985595703125 - -#define NUM_COORDS 10 -const double llhs[NUM_COORDS][3] = { - {0, 0, 0}, /* On the Equator and Prime Meridian. */ - {0, 180 * D2R, 0}, /* On the Equator. */ - {0, 90 * D2R, 0}, /* On the Equator. */ - {0, -90 * D2R, 0}, /* On the Equator. */ - {90 * D2R, 0, 0}, /* North pole. */ - {-90 * D2R, 0, 0}, /* South pole. */ - {90 * D2R, 0, 22}, /* 22m above the north pole. */ - {-90 * D2R, 0, 22}, /* 22m above the south pole. */ - {0, 0, 22}, /* 22m above the Equator and Prime Meridian. */ - {0, 180 * D2R, 22}, /* 22m above the Equator. */ -}; -const double ecefs[NUM_COORDS][3] = { - {EARTH_A, 0, 0}, - {-EARTH_A, 0, 0}, - {0, EARTH_A, 0}, - {0, -EARTH_A, 0}, - {0, 0, EARTH_B}, - {0, 0, -EARTH_B}, - {0, 0, (EARTH_B + 22)}, - {0, 0, -(EARTH_B + 22)}, - {(22 + EARTH_A), 0, 0}, - {-(22 + EARTH_A), 0, 0}, -}; - -START_TEST(test_llhdeg2rad) { - double rads[3]; - - llhdeg2rad(llhs[0], rads); - - // We expect the zero-point to be the same in degrees and radians - for (int n = 0; n < 3; n++) { - fail_unless(rads[n] == 0); - } - - // We expect an arbitrary point to convert correctly - double swiftHomeLLH[3] = {37.779804, -122.391751, 60.0}; - llhdeg2rad(swiftHomeLLH, rads); - - fail_unless(fabs(rads[0] - 0.659381970558) < MAX_ANGLE_ERROR_RAD); - fail_unless(fabs(rads[1] + 2.136139032231) < MAX_ANGLE_ERROR_RAD); - fail_unless(rads[2] == swiftHomeLLH[2]); -} -END_TEST - -START_TEST(test_wgsllh2ecef) { - double ecef[3]; - - wgsllh2ecef(llhs[_i], ecef); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(ecef[n]), "NaN in output from wgsllh2ecef."); - double err = fabs(ecef[n] - ecefs[_i][n]); - fail_unless(err < MAX_DIST_ERROR_M, - "Conversion from WGS84 LLH to ECEF has >1e-6m error:\n" - "LLH: %f, %f, %f\n" - "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g", - llhs[_i][0] * R2D, - llhs[_i][1] * R2D, - llhs[_i][2], - (ecef[0] - ecefs[_i][0]) * 1e3, - (ecef[1] - ecefs[_i][1]) * 1e3, - (ecef[2] - ecefs[_i][2]) * 1e3); - } -} -END_TEST - -START_TEST(test_wgsecef2llh) { - double llh[3]; - - wgsecef2llh(ecefs[_i], llh); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(llh[n]), "NaN in output from wgsecef2llh."); - } - - double lat_err = fabs(llh[0] - llhs[_i][0]); - double lon_err = fabs(llh[1] - llhs[_i][1]); - double hgt_err = fabs(llh[2] - llhs[_i][2]); - fail_unless( - (lat_err < MAX_ANGLE_ERROR_RAD) && (lon_err < MAX_ANGLE_ERROR_RAD) && - (hgt_err < MAX_DIST_ERROR_M), - "Conversion from WGS84 ECEF to LLH has >1e-6 {rad, m} error:\n" - "ECEF: %f, %f, %f\n" - "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g", - ecefs[_i][0], - ecefs[_i][1], - ecefs[_i][2], - (llh[0] - llhs[_i][0]) * (R2D * 3600), - (llh[1] - llhs[_i][1]) * (R2D * 3600), - (llh[2] - llhs[_i][2]) * 1e3); -} -END_TEST - -START_TEST(test_wgsllh2ecef2llh) { - double ecef[3]; - double llh[3]; - - wgsllh2ecef(llhs[_i], ecef); - wgsecef2llh(ecef, llh); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion."); - fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion."); - } - - double lat_err = fabs(llh[0] - llhs[_i][0]); - double lon_err = fabs(llh[1] - llhs[_i][1]); - double hgt_err = fabs(llh[2] - llhs[_i][2]); - fail_unless( - (lat_err < MAX_ANGLE_ERROR_RAD) && (lon_err < MAX_ANGLE_ERROR_RAD) && - (hgt_err < MAX_DIST_ERROR_M), - "Converting WGS84 LLH to ECEF and back again does not return the " - "original values.\n" - "Initial LLH: %f, %f, %f\n" - "ECEF: %f, %f, %f\n" - "Final LLH: %f, %f, %f\n" - "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g", - R2D * llhs[_i][0], - R2D * llhs[_i][1], - llhs[_i][2], - ecef[0], - ecef[1], - ecef[2], - R2D * llh[0], - R2D * llh[1], - llh[2], - (llh[0] - llhs[_i][0]) * (R2D * 3600), - (llh[1] - llhs[_i][1]) * (R2D * 3600), - (llh[2] - llhs[_i][2]) * 1e3); -} -END_TEST - -START_TEST(test_wgsecef2llh2ecef) { - double llh[3]; - double ecef[3]; - - wgsecef2llh(ecefs[_i], llh); - wgsllh2ecef(llh, ecef); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion."); - fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion."); - } - - for (int n = 0; n < 3; n++) { - double err = fabs(ecef[n] - ecefs[_i][n]); - fail_unless( - err < MAX_DIST_ERROR_M, - "Converting WGS84 ECEF to LLH and back again does not return the " - "original values.\n" - "Initial ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n" - "Final ECEF: %f, %f, %f\n" - "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g", - ecefs[_i][0], - ecefs[_i][1], - ecefs[_i][2], - R2D * llh[0], - R2D * llh[1], - llh[2], - ecef[0], - ecef[1], - ecef[2], - (ecef[0] - ecefs[_i][0]) * 1e3, - (ecef[1] - ecefs[_i][1]) * 1e3, - (ecef[2] - ecefs[_i][2]) * 1e3); - } -} -END_TEST - -START_TEST(test_random_wgsllh2ecef2llh) { - double ecef[3]; - double llh_init[3]; - double llh[3]; - - srand(0); - - llh_init[0] = D2R * frand(-90, 90); - llh_init[1] = D2R * frand(-180, 180); - llh_init[2] = frand(-0.5 * EARTH_A, 4 * EARTH_A); - - wgsllh2ecef(llh_init, ecef); - wgsecef2llh(ecef, llh); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion."); - fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion."); - } - - double lat_err = fabs(llh[0] - llh_init[0]); - double lon_err = fabs(llh[1] - llh_init[1]); - double hgt_err = fabs(llh[2] - llh_init[2]); - fail_unless( - (lat_err < MAX_ANGLE_ERROR_RAD) && (lon_err < MAX_ANGLE_ERROR_RAD) && - (hgt_err < MAX_DIST_ERROR_M), - "Converting random WGS84 LLH to ECEF and back again does not return the " - "original values.\n" - "Initial LLH: %f, %f, %f\n" - "ECEF: %f, %f, %f\n" - "Final LLH: %f, %f, %f\n" - "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g", - R2D * llh_init[0], - R2D * llh_init[1], - llh_init[2], - ecef[0], - ecef[1], - ecef[2], - R2D * llh[0], - R2D * llh[1], - llh[2], - (llh[0] - llh_init[0]) * (R2D * 3600), - (llh[1] - llh_init[1]) * (R2D * 3600), - (llh[2] - llh_init[2]) * 1e3); - - fail_unless((R2D * llh[0] >= -90) && (R2D * llh[0] <= 90), - "Converting random WGS84 ECEF gives latitude out of bounds.\n" - "ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef[0], - ecef[1], - ecef[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); - - fail_unless((R2D * llh[1] >= -180) && (R2D * llh[1] <= 180), - "Converting random WGS84 ECEF gives longitude out of bounds.\n" - "ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef[0], - ecef[1], - ecef[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); - - fail_unless(llh[2] > -EARTH_A, - "Converting random WGS84 ECEF gives height out of bounds.\n" - "ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef[0], - ecef[1], - ecef[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); -} -END_TEST - -START_TEST(test_random_wgsecef2llh2ecef) { - double ecef_init[3]; - double llh[3]; - double ecef[3]; - - srand(0); - - ecef_init[0] = frand(-4 * EARTH_A, 4 * EARTH_A); - ecef_init[1] = frand(-4 * EARTH_A, 4 * EARTH_A); - ecef_init[2] = frand(-4 * EARTH_A, 4 * EARTH_A); - - wgsecef2llh(ecef_init, llh); - wgsllh2ecef(llh, ecef); - - for (int n = 0; n < 3; n++) { - fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion."); - fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion."); - } - - for (int n = 0; n < 3; n++) { - double err = fabs(ecef[n] - ecef_init[n]); - fail_unless(err < MAX_DIST_ERROR_M, - "Converting random WGS84 ECEF to LLH and back again does not " - "return the " - "original values.\n" - "Initial ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n" - "Final ECEF: %f, %f, %f\n" - "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g", - ecef_init[0], - ecef_init[1], - ecef_init[2], - R2D * llh[0], - R2D * llh[1], - llh[2], - ecef[0], - ecef[1], - ecef[2], - (ecef[0] - ecef_init[0]) * 1e3, - (ecef[1] - ecef_init[1]) * 1e3, - (ecef[2] - ecef_init[2]) * 1e3); - } - - fail_unless((R2D * llh[0] >= -90) && (R2D * llh[0] <= 90), - "Converting random WGS84 ECEF gives latitude out of bounds.\n" - "Initial ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef_init[0], - ecef_init[1], - ecef_init[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); - - fail_unless((R2D * llh[1] >= -180) && (R2D * llh[1] <= 180), - "Converting random WGS84 ECEF gives longitude out of bounds.\n" - "Initial ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef_init[0], - ecef_init[1], - ecef_init[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); - - fail_unless(llh[2] > -EARTH_A, - "Converting random WGS84 ECEF gives height out of bounds.\n" - "Initial ECEF: %f, %f, %f\n" - "LLH: %f, %f, %f\n", - ecef_init[0], - ecef_init[1], - ecef_init[2], - R2D * llh[0], - R2D * llh[1], - llh[2]); -} -END_TEST - -/* Check simply that passing the ECEF position the same as the - * reference position returns (0, 0, 0) in NED frame */ -START_TEST(test_random_wgsecef2ned_d_0) { - s32 i, j; - double ned[3]; - - srand(0); - for (i = 0; i < 222; i++) { - const double ecef[3] = { - frand(-1e8, 1e8), frand(-1e8, 1e8), frand(-1e8, 1e8)}; - wgsecef2ned_d(ecef, ecef, ned); - for (j = 0; j < 3; j++) - fail_unless(fabs(ned[j]) < 1e-8, - "NED vector to reference ECEF point " - "has nonzero element %d: %lf\n" - "(point was <%lf %lf %lf>)\n", - j, - ned[j], - ecef[0], - ecef[1], - ecef[2]); - } -} -END_TEST - -Suite *coord_system_suite(void) { - Suite *s = suite_create("Coordinate systems"); - - /* Core test case */ - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_llhdeg2rad); - tcase_add_loop_test(tc_core, test_wgsllh2ecef, 0, NUM_COORDS); - tcase_add_loop_test(tc_core, test_wgsecef2llh, 0, NUM_COORDS); - tcase_add_loop_test(tc_core, test_wgsllh2ecef2llh, 0, NUM_COORDS); - tcase_add_loop_test(tc_core, test_wgsecef2llh2ecef, 0, NUM_COORDS); - suite_add_tcase(s, tc_core); - - TCase *tc_random = tcase_create("Random"); - tcase_add_loop_test(tc_random, test_random_wgsllh2ecef2llh, 0, 22); - tcase_add_loop_test(tc_random, test_random_wgsecef2llh2ecef, 0, 22); - tcase_add_loop_test(tc_random, test_random_wgsecef2ned_d_0, 0, 22); - suite_add_tcase(s, tc_random); - - return s; -} diff --git a/tests/check_edc.c b/tests/check_edc.c deleted file mode 100644 index 84355ed..0000000 --- a/tests/check_edc.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -#include "check_suites.h" - -const u8 *test_data = (const u8 *)"123456789"; - -START_TEST(test_crc24q) { - u32 crc; - - crc = crc24q(test_data, 0, 0); - fail_unless(crc == 0, - "CRC of empty buffer with starting value 0 should be 0, not %d", - crc); - - crc = crc24q(test_data, 0, 22); - fail_unless(crc == 22, - "CRC of empty buffer with starting value 22 should be 22, not %d", - crc); - - /* Test value taken from python crcmod package tests, see: - * http://crcmod.sourceforge.net/crcmod.predefined.html */ - crc = crc24q(test_data, 9, 0xB704CE); - fail_unless( - crc == 0x21CF02, - "CRC of \"123456789\" with init value 0xB704CE should be 0x21CF02, " - "not 0x%06X", - crc); -} -END_TEST - -Suite *edc_suite(void) { - Suite *s = suite_create("Error Detection and Correction"); - - TCase *tc_crc = tcase_create("CRC"); - tcase_add_test(tc_crc, test_crc24q); - suite_add_tcase(s, tc_crc); - - return s; -} diff --git a/tests/check_geoid_model.cc b/tests/check_geoid_model.cc deleted file mode 100644 index f6751f2..0000000 --- a/tests/check_geoid_model.cc +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Geoid model tests - * - * Contains code from - * https://github.com/swift-nav/RTKLIB/blob/master/src/geoid.c in accordance - * with the terms of the RTKLib licence - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* embedded geoid area range {W,E,S,N} (deg) */ -static const double range[4] = {0.0, 360.0, -90.0, 90.0}; - -/* bilinear interpolation ----------------------------------------------------*/ -static double interpb(const double* y, double a, double b) { - return y[0] * (1.0 - a) * (1.0 - b) + y[1] * a * (1.0 - b) + - y[2] * (1.0 - a) * b + y[3] * a * b; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -/* lookup function for embedded geoid model */ -template -float geoidh_emb(const double& lat, - const double& lon, - const T& geoid, - const double& dlat, - const double& dlon) { - double a, b, y[4]; - int i1, i2, j1, j2; - - if (lon < range[0] || range[1] < lon || lat < range[2] || range[3] < lat) { - return 0.0; - } - a = (lon - range[0]) / dlon; - b = (lat - range[2]) / dlat; - i1 = (int)a; - a -= i1; - i2 = i1 < 360 / dlon ? i1 + 1 : i1; - j1 = (int)b; - b -= j1; - j2 = j1 < 180 / dlat ? j1 + 1 : j1; - y[0] = geoid[i1][j1]; - y[1] = geoid[i2][j1]; - y[2] = geoid[i1][j2]; - y[3] = geoid[i2][j2]; - return interpb(y, a, b); -} - -// directly include the 1 degree geoid for testing purposes -namespace geoid_1_degree { -#include "src/geoid_model_1_degree.inc" -inline float geoidh_emb(const double& lat, const double& lon) { - return ::geoidh_emb( - lat, lon, GEOID, LAT_GRID_SPACING_DEG, LON_GRID_SPACING_DEG); -} -} // namespace geoid_1_degree - -// directly include the 0.25 degree geoid for testing purposes -namespace geoid_15_minute { -#include "src/geoid_model_15_minute.inc" -inline float geoidh_emb(const double& lat, const double& lon) { - return ::geoidh_emb( - lat, lon, GEOID, LAT_GRID_SPACING_DEG, LON_GRID_SPACING_DEG); -} -} // namespace geoid_15_minute - -#include -#undef log_error -#define log_error(...) - -// directly include our implementation with 1 degree geoid for testing purposes -namespace src_geoid_model_1_degree { -#undef GEOID_MODEL_15_MINUTE_RESOLUTION -#include "src/geoid_model.c" -} // namespace src_geoid_model_1_degree - -// directly include our implementation with 0.25 degree geoid for testing -// purposes -namespace src_geoid_model_15_minute { -#define GEOID_MODEL_15_MINUTE_RESOLUTION -#include "src/geoid_model.c" -} // namespace src_geoid_model_15_minute - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Perform direct lookups on GEOID to compare height values from the 1 degree - * resolution geoid vs heights from the 15 minute resolution geoid for an - * exact match (for all points which exist in the 1 degree grid) - */ -START_TEST(compare_geoid_models_matching_points) { - // 360 degrees of longitude (1 repeated) - for (int lon = 0; lon < 361; lon++) { - // 180 degrees of latitude (1 repeated) - for (int lat = 0; lat < 181; lat++) { - float val_1_degree = geoid_1_degree::GEOID[lon][lat]; - float val_15_minute = geoid_15_minute::GEOID[lon * 4][lat * 4]; - - fail_unless(fabs(val_1_degree - val_15_minute) < 1e-4, - "Mismatch between 1 degree resolution and 0.25 degree " - "resolution geoids: " - "%f vs %f\n", - val_1_degree, - val_15_minute); - } - } -} -END_TEST - -/* - * Use geoidh_emb() to compare heights from the 1 degree resolution geoid vs - * heights from the 15 minute resolution geoid for every 10th of a degree - * (with bilinear interpolation). Maximum expected difference is 15.2m (in - * Hawaii). - */ -START_TEST(compare_geoid_models_tenth_degree) { - for (int lon = 0; lon <= 3600; lon++) { - for (int lat = -900; lat <= 900; lat++) { - double lat_deg = lat / 10.; - double lon_deg = lon / 10.; - float val_1_degree = geoid_1_degree::geoidh_emb(lat_deg, lon_deg); - float val_15_minute = geoid_15_minute::geoidh_emb(lat_deg, lon_deg); - fail_unless(fabs(val_1_degree - val_15_minute) < 15.5, - "Mismatch between 1 degree resolution and 0.25 degree " - "resolution geoids " - "at lat %g, lon %g: %f vs %f, delta %f\n", - lat_deg, - lon_deg, - val_1_degree, - val_15_minute, - fabs(val_1_degree - val_15_minute)); - } - } -} -END_TEST - -/* - * Compare heights from the 1 degree geoid (with bilinear interpolation) vs - * heights from the 1 degree geoid (with bicubic interpolation) for every 10th - * of a degree. Maximum offset should be 3.69m (in Indonesia). - */ -START_TEST(compare_1_degree_bilinear_vs_bicubic) { - for (int lon = 0; lon <= 3600; lon++) { - for (int lat = -900; lat <= 900; lat++) { - double lat_deg = lat / 10.; - double lon_deg = lon / 10.; - - float bilinear = geoid_1_degree::geoidh_emb(lat_deg, lon_deg); - float bicubic = src_geoid_model_1_degree::get_geoid_offset(lat_deg * D2R, - lon_deg * D2R); - fail_unless(fabs(bilinear - bicubic) < 3.69, - "Mismatch between bilinear and bicubic interpolation for 1 " - "degree geoid " - "at lat %g, lon %g: %f vs %f, delta %f\n", - lat_deg, - lon_deg, - bilinear, - bicubic, - fabs(bilinear - bicubic)); - } - } -} -END_TEST - -/* - * Compare heights from the 15 minute geoid (with bilinear interpolation) vs - * heights from the 15 degree geoid (with bicubic interpolation) for every 10th - * of a degree. Maximum offset should be 1.33m (in Columbia) - */ -START_TEST(compare_15_minute_bilinear_vs_bicubic) { - for (int lon = 0; lon <= 3600; lon++) { - for (int lat = -900; lat <= 900; lat++) { - double lat_deg = lat / 10.; - double lon_deg = lon / 10.; - float bilinear = geoid_15_minute::geoidh_emb(lat_deg, lon_deg); - float bicubic = src_geoid_model_15_minute::get_geoid_offset( - lat_deg * D2R, lon_deg * D2R); - fail_unless(fabs(bilinear - bicubic) < 1.33, - "Mismatch between bilinear and bicubic interpolation for 15 " - "minute grid " - "at lat %g, lon %g: %f vs %f, delta %f\n", - lat_deg, - lon_deg, - bilinear, - bicubic, - fabs(bilinear - bicubic)); - } - } -} -END_TEST - -Suite* geoid_model_test_suite(void) { - Suite* s = suite_create("Geoid model"); - - TCase* tc_core = tcase_create("Core"); - tcase_add_test(tc_core, compare_geoid_models_matching_points); - tcase_add_test(tc_core, compare_geoid_models_tenth_degree); - tcase_add_test(tc_core, compare_1_degree_bilinear_vs_bicubic); - tcase_add_test(tc_core, compare_15_minute_bilinear_vs_bicubic); - suite_add_tcase(s, tc_core); - - return s; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/tests/check_glo_map.c b/tests/check_glo_map.c deleted file mode 100644 index aafd688..0000000 --- a/tests/check_glo_map.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2017 Swift Navigation Inc. - * Contact: Dmitry Tatarinov - * - * This source is subject to the license found in the file 'LICENSE' which must - * be distributed together with this source. All other rights reserved. - * - * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, - * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. - */ - -#include -#include -#include - -#include "check_suites.h" - -#define FCN_TEST_VAL 14 -#define SLOT_ID_TEST_VAL_1 5 -#define SLOT_ID_TEST_VAL_2 10 - -static void glo_map_lock(void) {} -static void glo_map_unlock(void) {} - -START_TEST(test_glo_map) { - /* We do not test thread safety here. - Therefore lock & unlock functions are just stubs. */ - glo_map_init(glo_map_lock, glo_map_unlock); - - for (u16 i = 1; i <= NUM_SATS_GLO; i++) { - gnss_signal_t glo_sid = construct_sid(CODE_GLO_L1OF, i); - glo_map_set_slot_id(FCN_TEST_VAL, /*glo_slot_id=*/i); - - fail_if(glo_map_valid(glo_sid) == false, "GLO mapping should be valid"); - - u16 fcn = glo_map_get_fcn(glo_sid); - fail_if(fcn != FCN_TEST_VAL, - "Incorrect GLO FCN mapping (have, expected): %d, %d", - fcn, - FCN_TEST_VAL); - - glo_map_clear_slot_id(i); - - fail_if(glo_map_valid(glo_sid) == true, "GLO mapping should be invalid"); - } - - u16 slot_id1, slot_id2; - u8 si_num; - si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); - fail_if(si_num != 0 || slot_id1 != 0 || slot_id2 != 0, - "Incorrect number of slot ID or slot ID\n" - "Number of slot ID (have, expected) %d, 0\n" - "slot_id1 (have, expected): %d, 0\n" - "slot_id2 (have, expected): %d, 0", - si_num, - slot_id1, - slot_id2); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); - si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); - fail_if(si_num != 1 || slot_id1 != SLOT_ID_TEST_VAL_1 || slot_id2 != 0, - "Incorrect number of slot ID or slot ID\n" - "Number of slot ID (have, expected) %d, 1\n" - "slot_id1 (have, expected): %d, %d\n" - "slot_id2 (have, expected): %d, 0", - si_num, - slot_id1, - SLOT_ID_TEST_VAL_1, - slot_id2); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2); - si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); - fail_if(si_num != 2 || slot_id1 != SLOT_ID_TEST_VAL_1 || - slot_id2 != SLOT_ID_TEST_VAL_2, - "Incorrect number of slot ID or slot ID\n" - "Number of slot ID (have, expected) %d, 2\n" - "slot_id1 (have, expected): %d, %d\n," - "slot_id2 (have, expected): %d, %d", - si_num, - slot_id1, - SLOT_ID_TEST_VAL_1, - slot_id2, - SLOT_ID_TEST_VAL_2); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2); - glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2 + 1); - si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); - fail_if(si_num != 2 || slot_id1 != SLOT_ID_TEST_VAL_1 || - slot_id2 != SLOT_ID_TEST_VAL_2, - "Incorrect number of slot ID or slot ID\n" - "Number of slot ID (have, expected) %d, 2\n" - "slot_id1 (have, expected): %d, %d\n," - "slot_id2 (have, expected): %d, %d", - si_num, - slot_id1, - SLOT_ID_TEST_VAL_1, - slot_id2, - SLOT_ID_TEST_VAL_2); -} -END_TEST - -Suite *glo_map_test_suite(void) { - Suite *s = suite_create("GLO FCN map"); - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_glo_map); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_gnss_time_cpp.cc b/tests/check_gnss_time_cpp.cc deleted file mode 100644 index ab2c5b8..0000000 --- a/tests/check_gnss_time_cpp.cc +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define GPS_TIME_TOL (1.0e-9) - -START_TEST(test_gpstime_comparison_ops) { - gps_time_t a = {567890.0, 1234}; - gps_time_t b = {567890.5, 1234}; - gps_time_t c = {567890.0, 1234}; - gps_time_t time_before_rollover = {WEEK_SECS - 1.0, 1234}; - gps_time_t time_after_rollover{1.0, 1234}; - gps_time_t time_unknown_wn_only{1.0, WN_UNKNOWN}; - gps_time_t time_unknown_tow_only{TOW_UNKNOWN, 1234}; - gps_time_t time_unknown_wn_a{0.0, WN_UNKNOWN}; - gps_time_t time_unknown_wn_b{WEEK_SECS - 1.0, WN_UNKNOWN}; - gps_time_t time_unknown_tow_a{TOW_UNKNOWN, 1234}; - gps_time_t time_unknown_tow_b{TOW_UNKNOWN, 1235}; - fail_unless(a < b, "operator< failed"); - fail_unless(b > a, "operator> failed"); - fail_unless(!(a == b), "operator== failed"); - fail_unless(!(b == a), "operator== failed"); - fail_unless(a == c, "operator== failed"); - fail_unless(a <= b, "operator<= failed"); - fail_unless(b >= a, "operator>= failed"); - fail_unless(a >= c, "operator>= failed"); - fail_unless(a <= c, "operator<= failed"); - fail_unless(GPS_TIME_UNKNOWN == GPS_TIME_UNKNOWN, "operator== failed"); - fail_unless(!(time_before_rollover == GPS_TIME_UNKNOWN), "operator== failed"); - fail_unless(!(GPS_TIME_UNKNOWN == time_before_rollover), "operator== failed"); - fail_unless(!(time_after_rollover == GPS_TIME_UNKNOWN), "operator== failed"); - fail_unless(!(GPS_TIME_UNKNOWN == time_after_rollover), "operator== failed"); - fail_unless(!(time_unknown_wn_only == GPS_TIME_UNKNOWN), "operator== failed"); - fail_unless(!(GPS_TIME_UNKNOWN == time_unknown_wn_only), "operator== failed"); - fail_unless(!(time_unknown_tow_only == GPS_TIME_UNKNOWN), - "operator== failed"); - fail_unless(!(GPS_TIME_UNKNOWN == time_unknown_tow_only), - "operator== failed"); - fail_unless(!(time_unknown_wn_a == time_unknown_wn_b), "operator== failed"); - fail_unless(!(time_unknown_wn_b == time_unknown_wn_a), "operator== failed"); - fail_unless(!(time_unknown_tow_a == time_unknown_tow_b), "operator== failed"); - fail_unless(!(time_unknown_tow_b == time_unknown_tow_a), "operator== failed"); -} -END_TEST - -START_TEST(test_gpstime_operator_minus) { - gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; - fail_unless(fabs((b - a) - 0.5) < GPS_TIME_TOL, "operator- failed"); - fail_unless(fabs((b - 0.5) - a) < GPS_TIME_TOL, "operator- failed"); -} -END_TEST - -START_TEST(test_gpstime_operator_plus) { - gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; - fail_unless(fabs((a + 0.5) - b) < GPS_TIME_TOL, "operator+ failed"); - fail_unless(fabs((0.5 + a) - b) < GPS_TIME_TOL, "operator+ failed"); -} -END_TEST - -START_TEST(test_gpstime_operator_plus_assignment) { - gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; - a += 0.5; - fail_unless(fabs(a - b) < GPS_TIME_TOL, "operator+= failed"); -} -END_TEST - -START_TEST(test_gpstime_operator_minus_assignment) { - gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; - b -= 0.5; - fail_unless(fabs(a - b) < GPS_TIME_TOL, "operator-= failed"); -} -END_TEST - -Suite *gnss_time_cpp_test_suite(void) { - Suite *s = suite_create("GPS Time C++ operators"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_gpstime_comparison_ops); - tcase_add_test(tc_core, test_gpstime_operator_minus); - tcase_add_test(tc_core, test_gpstime_operator_plus); - tcase_add_test(tc_core, test_gpstime_operator_plus_assignment); - tcase_add_test(tc_core, test_gpstime_operator_minus_assignment); - suite_add_tcase(s, tc_core); - - return s; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif \ No newline at end of file diff --git a/tests/check_linear_algebra.c b/tests/check_linear_algebra.c deleted file mode 100644 index cfa9f41..0000000 --- a/tests/check_linear_algebra.c +++ /dev/null @@ -1,756 +0,0 @@ -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -#define LINALG_TOL 1e-9 -#define LINALG_NUM 22 -#define MATRIX_MIN -1e3 -#define MATRIX_MAX 1e3 -#define mrand frand(MATRIX_MIN, MATRIX_MAX) -#define MSIZE_MAX 64 - -/* TODO: matrix_multiply, matrix_add_sc, matrix_copy, all vector functions */ - -START_TEST(test_matrix_inverse_2x2) { - u32 i, j, t; - double A[4]; - double B[4]; - double I[4]; - - srand(0); - /* 2x2 inverses */ - for (t = 0; t < LINALG_NUM; t++) { - do { - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) A[2 * i + j] = mrand; - } while (matrix_inverse(2, A, B) < 0); - matrix_multiply(2, 2, 2, A, B, I); - fail_unless( - fabs(I[0] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[0]); - fail_unless( - fabs(I[3] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[3]); - } - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) - if (j == 0) - A[2 * i + j] = 22; - else - A[2 * i + j] = 1; - s32 mi = matrix_inverse(2, A, B); - fail_unless(mi < 0, "Singular matrix not detected."); -} -END_TEST - -START_TEST(test_matrix_inverse_3x3) { - u32 i, j, t; - double A[9]; - double B[9]; - double I[9]; - - srand(0); - /* 3x3 inverses */ - for (t = 0; t < LINALG_NUM; t++) { - do { - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) A[3 * i + j] = mrand; - } while (matrix_inverse(3, A, B) < 0); - matrix_multiply(3, 3, 3, A, B, I); - fail_unless( - fabs(I[0] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[0]); - fail_unless( - fabs(I[4] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[4]); - fail_unless( - fabs(I[8] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[8]); - } - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) - if (j == 0) - A[3 * i + j] = 33; - else - A[3 * i + j] = 1; - s32 mi = matrix_inverse(3, A, B); - fail_unless(mi < 0, "Singular matrix not detected."); -} -END_TEST - -START_TEST(test_matrix_inverse_4x4) { - u32 i, j, t; - double A[16]; - double B[16]; - double I[16]; - - srand(0); - /* 4x4 inverses */ - for (t = 0; t < LINALG_NUM; t++) { - do { - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++) A[4 * i + j] = mrand; - } while (matrix_inverse(4, A, B) < 0); - matrix_multiply(4, 4, 4, A, B, I); - fail_unless( - fabs(I[0] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[0]); - fail_unless( - fabs(I[5] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[5]); - fail_unless(fabs(I[10] - 1) < LINALG_TOL, - "Matrix differs from identity: %lf", - I[10]); - fail_unless(fabs(I[15] - 1) < LINALG_TOL, - "Matrix differs from identity: %lf", - I[15]); - } - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++) - if (j == 0) - A[4 * i + j] = 44; - else - A[4 * i + j] = 1; - s32 mi = matrix_inverse(4, A, B); - fail_unless(mi < 0, "Singular matrix not detected."); -} -END_TEST - -START_TEST(test_matrix_inverse_5x5) { - u32 i, j, t; - double A[25]; - double B[25]; - double I[25]; - - srand(0); - /* 5x5 inverses */ - for (t = 0; t < LINALG_NUM; t++) { - do { - for (i = 0; i < 5; i++) - for (j = 0; j < 5; j++) A[5 * i + j] = mrand; - } while (matrix_inverse(5, A, B) < 0); - matrix_multiply(5, 5, 5, A, B, I); - fail_unless( - fabs(I[0] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[0]); - fail_unless( - fabs(I[6] - 1) < LINALG_TOL, "Matrix differs from identity: %lf", I[6]); - fail_unless(fabs(I[12] - 1) < LINALG_TOL, - "Matrix differs from identity: %lf", - I[12]); - fail_unless(fabs(I[18] - 1) < LINALG_TOL, - "Matrix differs from identity: %lf", - I[18]); - fail_unless(fabs(I[24] - 1) < LINALG_TOL, - "Matrix differs from identity: %lf", - I[24]); - } - for (i = 0; i < 5; i++) - for (j = 0; j < 5; j++) - if (j == 0) - A[5 * i + j] = 55; - else - A[5 * i + j] = 1; - s32 mi = matrix_inverse(5, A, B); - fail_unless(mi < 0, "Singular matrix not detected."); -} -END_TEST - -START_TEST(test_matrix_eye) { - double M[10][10]; - - matrix_eye(10, (double *)M); - - for (u32 i = 0; i < 10; i++) { - for (u32 j = 0; j < 10; j++) { - if (i == j) { - fail_unless(M[i][j] == 1, "Identity diagonal element != 1"); - } else { - fail_unless(M[i][j] == 0, "Identity off-diagonal element != 0"); - } - } - } -} -END_TEST - -START_TEST(test_matrix_triu) { - double M[4][4] = { - {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - - double M_[4][4] = {{1, 2, 3, 4}, {0, 6, 7, 8}, {0, 0, 11, 12}, {0, 0, 0, 16}}; - - matrix_triu(4, (double *)M); - - for (u32 i = 0; i < 4; i++) { - for (u32 j = 0; j < 4; j++) { - fail_unless(M[i][j] == M_[i][j], "triu result != test matrix"); - } - } -} -END_TEST - -START_TEST(test_matrix_udu_1) { - double M[4][4] = {{100, 145, 121, 16}, - {145, 221, 183, 24}, - {121, 183, 199, 28}, - {16, 24, 28, 4}}; - - double U[4][4] = {{0}}; - double D[4] = {0}; - - matrix_udu(4, (double *)M, (double *)U, D); - - double U_[4][4] = {{1, 2, 3, 4}, {0, 1, 5, 6}, {0, 0, 1, 7}, {0, 0, 0, 1}}; - - double D_[4] = {1, 2, 3, 4}; - - for (u32 i = 0; i < 4; i++) { - for (u32 j = 0; j < 4; j++) { - fail_unless(U[i][j] == U_[i][j], "U result != test matrix"); - } - } - for (u32 i = 0; i < 4; i++) { - fail_unless(D[i] == D_[i], "D result != test D"); - } -} -END_TEST - -START_TEST(test_matrix_udu_2) { - u32 n = sizerand(MSIZE_MAX); - double M[n][n]; - double M_orig[n][n]; - - for (u32 i = 0; i < n; i++) { - for (u32 j = 0; j <= i; j++) { - M[i][j] = M[j][i] = mrand; - } - } - - /* Square the random matrix to ensure it is positive semi-definite. */ - matrix_multiply(n, n, n, (double *)M, (double *)M, (double *)M_orig); - memcpy(M, M_orig, n * n * sizeof(double)); - - double U[n][n]; - memset(U, 0, n * n * sizeof(double)); - double D[n]; - memset(D, 0, n * sizeof(double)); - - matrix_udu(n, (double *)M, (double *)U, D); - - /* Check U is unit upper triangular. */ - for (u32 i = 0; i < n; i++) { - for (u32 j = 0; j < n; j++) { - if (i == j) { - fail_unless(fabs(U[i][j] - 1) < LINALG_TOL, - "U diagonal element != 1 (was %f)", - M[i][j]); - } - if (i > j) { - fail_unless(fabs(U[i][j]) < LINALG_TOL, - "U lower triangle element != 0"); - } - } - } - - /* Check reconstructed matrix is correct. */ - double M_[n][n]; - memset(M_, 0, n * n * sizeof(double)); - matrix_reconstruct_udu(n, (double *)U, D, (double *)M_); - - for (u32 i = 0; i < n; i++) { - for (u32 j = 0; j < n; j++) { - fail_unless(fabs(M_orig[i][j] - M_[i][j]) < LINALG_TOL * MATRIX_MAX, - "reconstructed result != original matrix, delta[%d][%d] = %f", - i, - j, - fabs(M_orig[i][j] - M_[i][j])); - } - } -} -END_TEST - -START_TEST(test_matrix_udu_3) { - double M[3][3] = { - {36, 49, 9}, - {49, 77, 15}, - {9, 15, 3}, - }; - - double U[3][3] = {{0}}; - double D[3] = {0}; - - matrix_udu(3, (double *)M, (double *)U, D); - - /* Check using formula for 3x3 matrix on Gibbs p. 393 */ - fail_unless(D[2] == M[2][2], "D[2] incorrect"); - fail_unless(U[2][1] == M[2][1] / D[2], "U[2][1] incorrect"); - fail_unless(U[2][0] == M[2][0] / D[2], "U[2][0] incorrect"); - fail_unless(D[1] == (M[1][1] - U[2][1] * U[2][1] * D[2]), "D[1] incorrect"); - fail_unless(U[1][0] == ((M[1][0] - U[2][0] * U[2][1] * D[2]) / D[1]), - "U[1][0] incorrect"); - fail_unless( - D[0] == (M[0][0] - U[1][0] * U[1][0] * D[1] - U[2][0] * U[2][0] * D[2]), - "D[0] incorrect"); -} -END_TEST - -START_TEST(test_matrix_reconstruct_udu) { - double U[4][4] = {{1, 2, 3, 4}, {0, 1, 5, 6}, {0, 0, 1, 7}, {0, 0, 0, 1}}; - - double D[4] = {1, 2, 3, 4}; - - double M[4][4] = {{0}}; - - double M_[4][4] = {{100, 145, 121, 16}, - {145, 221, 183, 24}, - {121, 183, 199, 28}, - {16, 24, 28, 4}}; - - matrix_reconstruct_udu(4, (double *)U, D, (double *)M); - - for (u32 i = 0; i < 4; i++) { - for (u32 j = 0; j < 4; j++) { - fail_unless(M[i][j] == M_[i][j], "reconstructed result != test matrix"); - } - } -} -END_TEST - -START_TEST(test_matrix_add_sc) { - u32 i, j, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - u32 m = sizerand(MSIZE_MAX); - double A[n * m]; - double B[m * n]; - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) { - A[m * i + j] = mrand; - } - matrix_add_sc(n, m, A, A, -1, B); - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) - fail_unless(fabs(B[m * i + j]) < LINALG_TOL, - "Matrix differs from zero: %lf", - B[m * i + j]); - } -} -END_TEST - -START_TEST(test_matrix_copy) { - u32 i, j, t; - double tmp; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - u32 m = sizerand(MSIZE_MAX); - double A[n * m]; - double B[m * n]; - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) A[m * i + j] = mrand; - matrix_copy(n, m, A, B); - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) { - tmp = fabs(B[m * i + j] - A[m * i + j]); - fail_unless(tmp < LINALG_TOL, "Matrix differs from zero: %lf", tmp); - } - } -} -END_TEST - -START_TEST(test_matrix_transpose) { - u32 i, j, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - u32 m = sizerand(MSIZE_MAX); - double A[n * m]; - double B[m * n]; - double C[n * m]; - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) A[m * i + j] = mrand; - matrix_transpose(n, m, A, B); - matrix_transpose(m, n, B, C); - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) - fail_unless(fabs(A[m * i + j] - C[m * i + j]) < LINALG_TOL, - "Matrix element differs from original: %lf, %lf", - A[m * i + j], - C[m * i + j]); - } -} -END_TEST - -START_TEST(test_vector_dot) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - u32 mid; - if (n % 2 == 0) - mid = n / 2; - else - mid = (n - 1) / 2; - - double A[n], B[n]; - for (i = 0; i < n; i++) { - A[i] = mrand / 1e20; - if (i < mid) - B[n - i - 1] = -A[i]; - else - B[n - i - 1] = A[i]; - } - double dot = vector_dot(n, A, B); - if (n % 2 == 0) - fail_unless(fabs(dot) < LINALG_TOL, - "Dot product differs from zero: %lf", - vector_dot(n, A, B)); - else - fail_unless(fabs(dot - A[mid] * B[mid]) < LINALG_TOL, - "Dot product differs from square of middle element " - "%lf: %lf (%lf)", - A[mid] * B[mid], - dot, - dot - A[mid] * B[mid]); - } -} -END_TEST - -START_TEST(test_vector_mean) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double A[n]; - double test = mrand / 1e22; - for (i = 0; i < n; i++) A[i] = test + i; - double mean = vector_mean(n, A); - double expect = test + (n - 1.0) / 2.0; - fail_unless(fabs(mean - expect) < LINALG_TOL, - "Mean differs from expected %lf: %lf (%lf)", - expect, - mean, - fabs(mean - expect)); - } -} -END_TEST - -START_TEST(test_vector_norm) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double test = mrand / 1e22; - double A[n]; - for (i = 0; i < n; i++) A[i] = test; - fail_unless(fabs(vector_norm(n, A) * vector_norm(n, A) - n * test * test) < - LINALG_TOL * vector_norm(n, A), - "Norm differs from expected %lf: %lf (%lf)", - n * test * test, - vector_norm(n, A) * vector_norm(n, A), - fabs(vector_norm(n, A) * vector_norm(n, A) - n * test * test)); - } -} -END_TEST - -START_TEST(test_vector_normalize) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double A[n]; - for (i = 0; i < n; i++) A[i] = mrand; - vector_normalize(n, A); - double vnorm = vector_norm(n, A); - fail_unless(fabs(vnorm - 1) < LINALG_TOL, - "Norm differs from 1: %lf", - vector_norm(n, A)); - } -} -END_TEST - -START_TEST(test_vector_add_sc) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double A[n], B[n]; - for (i = 0; i < n; i++) A[i] = mrand; - vector_add_sc(n, A, A, -1, B); - for (i = 0; i < n; i++) - fail_unless( - fabs(B[i]) < LINALG_TOL, "Vector element differs from 0: %lf", B[i]); - } -} -END_TEST - -START_TEST(test_vector_add) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double A[n], B[n], C[n]; - for (i = 0; i < n; i++) { - A[i] = mrand; - B[i] = -A[i]; - } - vector_add(n, A, B, C); - for (i = 0; i < n; i++) - fail_unless( - fabs(C[i]) < LINALG_TOL, "Vector element differs from 0: %lf", C[i]); - } -} -END_TEST - -START_TEST(test_vector_subtract) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double A[n], B[n], C[n]; - for (i = 0; i < n; i++) { - A[i] = mrand; - B[i] = A[i]; - } - vector_subtract(n, A, B, C); - for (i = 0; i < n; i++) - fail_unless( - fabs(C[i]) < LINALG_TOL, "Vector element differs from 0: %lf", C[i]); - } -} -END_TEST - -START_TEST(test_vector_cross) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - double A[3], B[3], C[3], D[3]; - for (i = 0; i < 3; i++) { - A[i] = mrand; - B[i] = A[i]; - } - vector_cross(A, B, C); - for (i = 0; i < 3; i++) - fail_unless( - fabs(C[i]) < LINALG_TOL, "Vector element differs from 0: %lf", C[i]); - for (i = 0; i < 3; i++) { - A[i] = mrand; - B[i] = mrand; - } - vector_cross(A, B, C); - for (i = 0; i < 3; i++) B[i] *= -1; - vector_cross(B, A, D); - for (i = 0; i < 3; i++) - fail_unless(fabs(C[i] - D[i]) < LINALG_TOL, - "Vector equality fails: %lf != %lf", - C[i], - D[i]); - } -} -END_TEST - -START_TEST(test_vector_three) { - u32 i, t; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - double A[3], B[3], C[3], tmp[3]; - double D, E, F, norm; - for (i = 0; i < 3; i++) { - A[i] = mrand / 1e20; - B[i] = mrand / 1e20; - C[i] = mrand / 1e20; - } - /* Check triple product identity */ - vector_cross(A, B, tmp); - D = vector_dot(3, C, tmp); - vector_cross(B, C, tmp); - E = vector_dot(3, A, tmp); - vector_cross(C, A, tmp); - F = vector_dot(3, B, tmp); - - norm = (vector_norm(3, A) + vector_norm(3, B) + vector_norm(3, C)) / 3; - fail_unless(fabs(E - D) < LINALG_TOL * norm, - "Triple product failure between %lf and %lf", - D, - E); - fail_unless(fabs(E - F) < LINALG_TOL * norm, - "Triple product failure between %lf and %lf", - E, - F); - fail_unless(fabs(F - D) < LINALG_TOL * norm, - "Triple product failure between %lf and %lf", - F, - D); - } -} -END_TEST - -/* -START_TEST(test_qrsolve_consistency) { - u32 i, j, t; - double norm; - - srand(0); - for (t = 0; t < LINALG_NUM; t++) { - u32 n = sizerand(MSIZE_MAX); - double x_gauss[n], x_qr[n]; - double A[n*n], Ainv[n*n], b[n]; - do { - for (i = 0; i < n; i++) { - b[i] = mrand; - for (j = 0; j < n; j++) - A[n*i + j] = mrand; - } - } while (matrix_inverse(n, A, Ainv) < 0); - matrix_multiply(n, n, 1, Ainv, b, x_gauss); - qrsolve(A, n, n, b, x_qr); - - norm = (vector_norm(n, x_qr) + vector_norm(n, x_gauss)) / 2; - for (i = 0; i < n; i++) - fail_unless(fabs(x_qr[i] - x_gauss[i]) < LINALG_TOL * norm, - "QR solve failure; difference was %lf for element %u", - x_qr[i] - x_gauss[i], i); - } -} -END_TEST - -START_TEST(test_qrsolve_rect) { - s32 i; - const double A[8] = {-0.0178505395610981, 1.4638781031761146, - -0.8242742209580581, -0.6843477128009663, - 0.9155272861151404, -0.1651159277864960, - -0.9929037180867774, -0.1491537478964264}; - double Q[16], R[8]; - - double buf[10] SWIFT_ATTR_UNUSED = {22, 22, 22, 22, 22, - 22, 22, 22, 22, 22}; - - i = qrdecomp(A, 4, 2, Q, R); - - printf("i returned %d\n", i); - - MAT_PRINTF(A, 4, 2); - MAT_PRINTF(Q, 4, 4); - MAT_PRINTF(R, 4, 2); -} -END_TEST -*/ - -START_TEST(test_submatrix) { - const double A[3 * 3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - - double A2[2 * 2]; - - u32 row_map[2] = {1, 2}; - u32 col_map[2] = {0, 1}; - - const double answer[2 * 2] = {3, 4, 6, 7}; - - submatrix(2, 2, 3, A, row_map, col_map, A2); - - for (u8 i = 0; i < 2 * 2; i++) { - fail_unless(answer[i] == A2[i]); - } -} -END_TEST - -START_TEST(test_vector_distance) { - const double A1[1 * 4] = {0, 1, 2, 3}; - - const double B1[1 * 4] = {0, 2, 1, -3}; - - const double C1[1 * 4] = {0, 1, 1, 6}; - - for (u8 i = 0; i < 4; i++) { - double dist; - dist = vector_distance(1, &A1[i], &B1[i]); - fail_unless(fabs(dist - C1[i]) < LINALG_TOL, - "Distance differs from expected %lf: %lf", - C1[i], - dist); - } - - const double A2[2 * 5] = {0, 0, 1, 1, 2, 1, 0, 0, 0, 0}; - - const double B2[2 * 5] = {0, 0, 1, 1, 1, 1, 1, 1, -1, -1}; - - const double C2[1 * 5] = {0, 0, 1, M_SQRT2, M_SQRT2}; - - for (u8 i = 0; i < 5; i++) { - double dist; - dist = vector_distance(2, &A2[i * 2], &B2[i * 2]); - fail_unless(fabs(dist - C2[i]) < LINALG_TOL, - "Distance differs from expected %lf: %lf", - C2[i], - dist); - } - - const double A3[3 * 5] = {0, 0, 0, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0}; - - const double B3[3 * 5] = {0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, -1, -1, 1}; - - const double C3[3 * 5] = { - 0, - 0, - 1, - 1.73205080756887729352744634150587236694280525381038062805580, - 1.73205080756887729352744634150587236694280525381038062805580}; - - for (u8 i = 0; i < 5; i++) { - double dist; - dist = vector_distance(3, &A3[i * 3], &B3[i * 3]); - fail_unless(fabs(dist - C3[i]) < LINALG_TOL, - "Distance differs from expected %lf: %lf", - C3[i], - dist); - } -} -END_TEST - -Suite *linear_algebra_suite(void) { - Suite *s = suite_create("Linear algebra"); - - /* Core test case */ - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_matrix_eye); - tcase_add_test(tc_core, test_matrix_triu); - tcase_add_test(tc_core, test_matrix_reconstruct_udu); - tcase_add_test(tc_core, test_matrix_udu_1); - tcase_add_test(tc_core, test_matrix_udu_2); - tcase_add_test(tc_core, test_matrix_udu_3); - tcase_add_test(tc_core, test_matrix_add_sc); - tcase_add_test(tc_core, test_matrix_copy); - tcase_add_test(tc_core, test_matrix_transpose); - tcase_add_test(tc_core, test_matrix_inverse_2x2); - tcase_add_test(tc_core, test_matrix_inverse_3x3); - tcase_add_test(tc_core, test_matrix_inverse_4x4); - tcase_add_test(tc_core, test_matrix_inverse_5x5); - - tcase_add_test(tc_core, test_vector_dot); - tcase_add_test(tc_core, test_vector_mean); - tcase_add_test(tc_core, test_vector_norm); - tcase_add_test(tc_core, test_vector_normalize); - tcase_add_test(tc_core, test_vector_add_sc); - tcase_add_test(tc_core, test_vector_add); - tcase_add_test(tc_core, test_vector_subtract); - tcase_add_test(tc_core, test_vector_cross); - tcase_add_test(tc_core, test_vector_three); - /*tcase_add_test(tc_core, test_qrsolve_consistency);*/ - /*tcase_add_test(tc_core, test_qrsolve_rect);*/ - tcase_add_test(tc_core, test_vector_distance); - - tcase_add_test(tc_core, test_submatrix); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_log.c b/tests/check_log.c deleted file mode 100644 index ee5b3f2..0000000 --- a/tests/check_log.c +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include -#include - -#include "check_suites.h" -#define MAX_STR 1024 - -/*globals for test */ -static char out_str[MAX_STR]; -char *ptr = out_str; -int last_level = 0; - -static void reset_log(void) { - ptr = out_str; - memset(out_str, 0, MAX_STR); -} - -static void test_log(int level, const char *msg, ...) { - va_list ap; - va_start(ap, msg); - ptr += vsprintf(ptr, msg, ap); - va_end(ap); - ptr += sprintf(ptr, "\n"); - last_level = level; -} - -static void test_detailed_log(int level, - const char *file_path, - const int line_number, - const char *msg, - ...) { - (void)level; - (void)file_path; - (void)line_number; - (void)msg; -} - -START_TEST(test_logging) { - /* check ptr arithmetic in print and null terminatino */ - int expected_len = 11; - logging_set_implementation(test_log, test_detailed_log); - log_info("log_info_1"); - fail_unless((ptr - out_str) == expected_len, - "log_info macro failed length check. got %zu, should be %i. %s", - (size_t)(ptr - out_str), - expected_len, - out_str); - fail_unless( - strnlen(out_str, MAX_STR) == (size_t)expected_len, - "log_info macro failed length check. strlen was %zu, should be %i. %s", - strnlen(out_str, MAX_STR), - expected_len, - out_str); - reset_log(); - - /* test log with rate limit based upon arbitrary tics. no need to check - * pointer as it was done above*/ - expected_len = (14 * 4); /*should print 4 times. line length is 14.*/ - - for (int i = 0; i < 4000; i++) { - LOG_RATE_LIMIT(i, log_info("log_rate_%04d", i)); - } - fail_unless( - strnlen(out_str, MAX_STR) == (size_t)expected_len, - "log_rate_limit macro failed length check. got %zu, should be %i. %s", - strnlen(out_str, MAX_STR), - expected_len, - out_str); - reset_log(); - /* if we somehow go back in time, should print again*/ - expected_len = 2 * 14; /* should print 2 times. String is length 14. */ - for (int j = 2000; j > 0; j -= 1500) { - LOG_RATE_LIMIT(j, log_info("log_rate_%04d", j)); - } - fail_unless( - strnlen(out_str, MAX_STR) == (size_t)expected_len, - "log_rate_limit macro back in time failed length check. got %zu, " - "should be %i. %s", - strnlen(out_str, MAX_STR), - expected_len, - out_str); - reset_log(); - /* if we wrap, arithmetic rules should just work */ - expected_len = (20 * 4); /* should print 4 times.*/ - for (int i = 0xffffffff - 1998; i < 1999; i++) { - LOG_RATE_LIMIT(i, log_info("log_rate_%010u", i)); - } - fail_unless(strnlen(out_str, MAX_STR) == (size_t)expected_len, - "log_rate_limit macro wrap failed length check. got %zu, should " - "be %i. %s", - strnlen(out_str, MAX_STR), - expected_len, - out_str); -} -END_TEST - -Suite *log_suite(void) { - Suite *s = suite_create("Logging"); - - TCase *tc_log = tcase_create("logging"); - tcase_add_test(tc_log, test_logging); - suite_add_tcase(s, tc_log); - - return s; -} diff --git a/tests/check_main.c b/tests/check_main.c deleted file mode 100644 index 21c9f41..0000000 --- a/tests/check_main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -#include "check_suites.h" - -int main(void) { - int number_failed; - - Suite *s = edc_suite(); - - SRunner *sr = srunner_create(s); - srunner_set_xml(sr, "test_results.xml"); - - srunner_add_suite(sr, almanac_suite()); - srunner_add_suite(sr, bits_suite()); - srunner_add_suite(sr, edc_suite()); - srunner_add_suite(sr, ionosphere_suite()); - srunner_add_suite(sr, coord_system_suite()); - srunner_add_suite(sr, linear_algebra_suite()); - srunner_add_suite(sr, troposphere_suite()); - srunner_add_suite(sr, ephemeris_suite()); - srunner_add_suite(sr, decode_glo_suite()); - srunner_add_suite(sr, set_suite()); - srunner_add_suite(sr, gnss_time_test_suite()); - srunner_add_suite(sr, signal_test_suite()); - srunner_add_suite(sr, geoid_model_test_suite()); - srunner_add_suite(sr, glo_map_test_suite()); - srunner_add_suite(sr, shm_suite()); - srunner_add_suite(sr, pvt_test_suite()); - srunner_add_suite(sr, nav_meas_test_suite()); - srunner_add_suite(sr, sid_set_test_suite()); - srunner_add_suite(sr, status_report_suite()); - srunner_add_suite(sr, log_suite()); - srunner_add_suite(sr, gnss_time_cpp_test_suite()); - - srunner_set_fork_status(sr, CK_NOFORK); - srunner_run_all(sr, CK_NORMAL); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/tests/check_nav_meas.c b/tests/check_nav_meas.c deleted file mode 100644 index 8b19c99..0000000 --- a/tests/check_nav_meas.c +++ /dev/null @@ -1,171 +0,0 @@ -#include -#include -#include -#include -#include - -#include "check_suites.h" - -START_TEST(test_encode_lock_time) { - u8 ret; - - ret = encode_lock_time(0.0); - fail_unless( - ret == 0, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)0); - - ret = encode_lock_time(0.05); - fail_unless( - ret == 1, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)1); - - ret = encode_lock_time(0.1); - fail_unless( - ret == 2, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)2); - - ret = encode_lock_time(0.2); - fail_unless( - ret == 3, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)3); - - ret = encode_lock_time(0.5); - fail_unless( - ret == 4, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)4); - - ret = encode_lock_time(1.0); - fail_unless( - ret == 5, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)5); - - ret = encode_lock_time(2.0); - fail_unless( - ret == 6, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)6); - - ret = encode_lock_time(4.0); - fail_unless( - ret == 7, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)7); - - ret = encode_lock_time(5.0); - fail_unless( - ret == 8, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)8); - - ret = encode_lock_time(10.0); - fail_unless( - ret == 9, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)9); - - ret = encode_lock_time(20.0); - fail_unless( - ret == 10, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)10); - - ret = encode_lock_time(50.0); - fail_unless( - ret == 11, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)11); - - ret = encode_lock_time(100.0); - fail_unless( - ret == 12, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)12); - - ret = encode_lock_time(200.0); - fail_unless( - ret == 13, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)13); - - ret = encode_lock_time(500.0); - fail_unless( - ret == 14, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)14); - - ret = encode_lock_time(1000.0); - fail_unless( - ret == 15, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)15); - - ret = encode_lock_time(DBL_MAX); - fail_unless( - ret == 15, "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", ret, (u8)15); -} -END_TEST - -START_TEST(test_decode_lock_time) { - double ret; - - ret = decode_lock_time(0); - fail_unless(ret == 0.0, "Incorrect return (%f vs %f)", ret, 0.0); - - ret = decode_lock_time(0xF0); - fail_unless(ret == 0.0, "Incorrect return (%f vs %f)", ret, 0.0); - - ret = decode_lock_time(1); - fail_unless(ret == 0.032, "Incorrect return (%f vs %f)", ret, 0.032); - - ret = decode_lock_time(2); - fail_unless(ret == 0.064, "Incorrect return (%f vs %f)", ret, 0.064); - - ret = decode_lock_time(3); - fail_unless(ret == 0.128, "Incorrect return (%f vs %f)", ret, 0.128); - - ret = decode_lock_time(4); - fail_unless(ret == 0.256, "Incorrect return (%f vs %f)", ret, 0.256); - - ret = decode_lock_time(5); - fail_unless(ret == 0.512, "Incorrect return (%f vs %f)", ret, 0.512); - - ret = decode_lock_time(6); - fail_unless(ret == 1.024, "Incorrect return (%f vs %f)", ret, 1.024); - - ret = decode_lock_time(7); - fail_unless(ret == 2.048, "Incorrect return (%f vs %f)", ret, 2.048); - - ret = decode_lock_time(8); - fail_unless(ret == 4.096, "Incorrect return (%f vs %f)", ret, 4.096); - - ret = decode_lock_time(9); - fail_unless(ret == 8.192, "Incorrect return (%f vs %f)", ret, 8.192); - - ret = decode_lock_time(10); - fail_unless(ret == 16.384, "Incorrect return (%f vs %f)", ret, 16.384); - - ret = decode_lock_time(11); - fail_unless(ret == 32.768, "Incorrect return (%f vs %f)", ret, 32.768); - - ret = decode_lock_time(12); - fail_unless(ret == 65.536, "Incorrect return (%f vs %f)", ret, 65.536); - - ret = decode_lock_time(13); - fail_unless(ret == 131.072, "Incorrect return (%f vs %f)", ret, 131.072); - - ret = decode_lock_time(14); - fail_unless(ret == 262.144, "Incorrect return (%f vs %f)", ret, 262.144); - - ret = decode_lock_time(15); - fail_unless(ret == 524.288, "Incorrect return (%f vs %f)", ret, 524.288); -} -END_TEST - -START_TEST(test_roundtrip_lock_time) { - const double value_to_encode = 260.0; - u8 encoded_value; - double decoded_value; - - encoded_value = encode_lock_time(value_to_encode); - decoded_value = decode_lock_time(encoded_value); - - fail_unless(encoded_value == 13, - "Incorrect return (%" PRIu8 " vs %" PRIu8 ")", - encoded_value, - (u8)13); - - fail_unless(decoded_value == 131.072, - "Incorrect return (%f vs %f)", - decoded_value, - 131.072); - - fail_unless(decoded_value < value_to_encode, - "Minimum lock time not less than original lock time (%f < %f)", - decoded_value, - value_to_encode); -} -END_TEST - -Suite *nav_meas_test_suite(void) { - Suite *s = suite_create("Navigation Measurement"); - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_encode_lock_time); - tcase_add_test(tc_core, test_decode_lock_time); - tcase_add_test(tc_core, test_roundtrip_lock_time); - suite_add_tcase(s, tc_core); - return s; -} diff --git a/tests/check_pvt.c b/tests/check_pvt.c deleted file mode 100644 index c88e29a..0000000 --- a/tests/check_pvt.c +++ /dev/null @@ -1,792 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -#define TOR_WN 1939 -#define TOR_TOW 42.0 - -/* time of reception for the tests */ -static const gps_time_t tor = {.wn = TOR_WN, .tow = TOR_TOW}; - -static navigation_measurement_t nm1 = { - .sid = {.sat = 9, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 23946993.888943646, - .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, - .lock_time = 5, - .cn0 = 41.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm1_no_doppler = { - .sid = {.sat = 9, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 23946993.888943646, - .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, - .lock_time = 5, - .cn0 = 39.0, - .flags = NAV_MEAS_FLAG_CODE_VALID}; - -static navigation_measurement_t nm2 = { - .sid = {.sat = 1, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 22932174.156858064, - .sat_pos = {-9680013.5408340245, -15286326.354385279, 19429449.383770257}, - .lock_time = 5, - .cn0 = 43.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm3 = { - .sid = {.sat = 2, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 24373231.648055989, - .sat_pos = {-19858593.085281931, -3109845.8288993631, 17180320.439503901}, - .lock_time = 5, - .cn0 = 35.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm4 = { - .sid = {.sat = 3, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 24779663.252316438, - .sat_pos = {6682497.8716542246, -14006962.389166718, 21410456.275678463}, - .lock_time = 5, - .cn0 = 27.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm5 = { - .sid = {.sat = 4, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 26948717.022331879, - .sat_pos = {7415370.9916331079, -24974079.044485383, -3836019.0262199985}, - .lock_time = 5, - .cn0 = 39.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm6 = { - .sid = {.sat = 5, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 23327405.435463827, - .sat_pos = {-2833466.1648670658, -22755197.793894723, 13160322.082875408}, - .lock_time = 5, - .cn0 = 38.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -/* doppler measurement broken */ -static navigation_measurement_t nm6b = { - .sid = {.sat = 5, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 23327405.435463827, - .sat_pos = {-2833466.1648670658, -22755197.793894723, 13160322.082875408}, - .sat_vel = {0, 0, 0}, - .cn0 = 40, - .lock_time = 5, - .raw_measured_doppler = 10000, /* Doppler outlier */ - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID | NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm7 = { - .sid = {.sat = 6, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 27371419.016328193, - .sat_pos = {14881660.383624561, -5825253.4316490609, 21204679.68313824}, - .lock_time = 5, - .cn0 = 42.3, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm8 = { - .sid = {.sat = 7, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 26294221.697782904, - .sat_pos = {12246530.477279386, -22184711.955107089, 7739084.2855069181}, - .lock_time = 5, - .cn0 = 45.1, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm9 = { - .sid = {.sat = 8, .code = CODE_GPS_L1CA}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 25781999.479948733, - .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, - .lock_time = 5, - .cn0 = 37.3, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm10 = { - .sid = {.sat = 8, .code = CODE_GPS_L2CM}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 25781999.479948733, - .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, - .lock_time = 5, - .cn0 = 41.0, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -/* broken measurement */ -static navigation_measurement_t nm10b = { - .sid = {.sat = 8, .code = CODE_GPS_L2CM}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 25781999.479948733 + 30000, - .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, - .lock_time = 5, - .cn0 = 39.2, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -static navigation_measurement_t nm11 = { - .sid = {.sat = 11, .code = CODE_GPS_L2CM}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 25781999.479948733, - .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, - .lock_time = 5, - .cn0 = 45.4, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -// Note this is a copy of GPS nm1 but set to code GAL_E1B, do not combine -// them in the same test case -static navigation_measurement_t gal_nm1 = { - .sid = {.sat = 9, .code = CODE_GAL_E1B}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 23946993.888943646, - .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, - .lock_time = 5, - .cn0 = 41.1, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -// Note this is a copy of GPS nm2 but set to code GAL_E1B, do not combine -// them in the same test case -static navigation_measurement_t gal_nm2 = { - .sid = {.sat = 1, .code = CODE_GAL_E1B}, - .tot = {.wn = TOR_WN, .tow = TOR_TOW - 0.077}, - .raw_pseudorange = 22932174.156858064, - .sat_pos = {-9680013.5408340245, -15286326.354385279, 19429449.383770257}, - .lock_time = 5, - .cn0 = 39.7, - .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | - NAV_MEAS_FLAG_PHASE_VALID}; - -START_TEST(test_pvt_failed_repair) { - u8 n_used = 5; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - navigation_measurement_t nms[9] = {nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8}; - - calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - /* PVT repair requires at least 6 measurements. */ - fail_unless(soln.valid == 0, "Solution should be invalid!"); -} -END_TEST - -START_TEST(test_pvt_repair) { - u8 n_used = 6; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - gnss_signal_t expected_removed_sid = {.code = CODE_GPS_L1CA, .sat = 9}; - - navigation_measurement_t nms[9] = { - nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == 1, "Return code should be 1 (pvt repair). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_used - 1, - "n_sigs_used should be %u. Saw: %u\n", - n_used - 1, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_used - 1, - "n_sats_used should be %u. Saw: %u\n", - n_used - 1, - soln.n_sats_used); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid), - "Unexpected RAIM removed SID!\n"); -} -END_TEST - -START_TEST(test_pvt_raim_singular) { - /* test the case of bug 946 where extreme pseudorange errors lead to singular - * geometry */ - u8 n_used = 9; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - navigation_measurement_t nm1_broken = nm1; - navigation_measurement_t nm2_broken = nm2; - nm1_broken.raw_pseudorange += 5e8; - nm2_broken.raw_pseudorange -= 2e7; - - navigation_measurement_t nms[9] = { - nm1_broken, nm2_broken, nm3, nm4, nm5, nm6, nm7, nm9, nm10}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == -4, "Return code should be -4 (RAIM failed). Saw: %d\n", code); -} -END_TEST - -START_TEST(test_pvt_vel_repair) { - u8 n_used = 6; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - gnss_signal_t expected_removed_sid = {.code = CODE_GPS_L1CA, .sat = 5}; - - navigation_measurement_t nms[6] = {nm2, nm3, nm4, nm5, nm6b, nm7}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == 1, "Return code should be 1 (pvt repair). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_used - 1, - "n_sigs_used should be %u. Saw: %u\n", - n_used - 1, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_used - 1, - "n_sats_used should be %u. Saw: %u\n", - n_used - 1, - soln.n_sats_used); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid), - "Unexpected RAIM removed SID!\n"); -} -END_TEST - -START_TEST(test_pvt_repair_multifailure) { - u8 n_used = 7; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - gnss_signal_t expected_removed_sid = {.code = CODE_GPS_L1CA, .sat = 9}; - - navigation_measurement_t nms[8] = {nm1, nm2, nm3, nm7, nm10b, nm5, nm6, nm7}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == 1, "Return code should be 1 (pvt repair). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_used - 2, - "n_sigs_used should be %u. Saw: %u\n", - n_used - 2, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_used - 2, - "n_sats_used should be %u. Saw: %u\n", - n_used - 2, - soln.n_sats_used); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid), - "Unexpected RAIM removed SID!\n"); -} -END_TEST - -START_TEST(test_pvt_raim_gps_l1ca_only) { - /* 9 L1CA signals (one broken) and 1 L2CM signal */ - u8 n_used = 10; - u8 n_gps_l1ca = 9; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{false, 25}}; - gnss_signal_t expected_removed_sid = {.code = CODE_GPS_L1CA, .sat = 9}; - - navigation_measurement_t nms[10] = { - nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - GPS_L1CA_WHEN_POSSIBLE, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == 1, "Return code should be 1 (pvt repair). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_gps_l1ca - 1, - "n_sigs_used should be %u. Saw: %u\n", - n_gps_l1ca - 1, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_gps_l1ca - 1, - "n_sats_used should be %u. Saw: %u\n", - n_gps_l1ca - 1, - soln.n_sats_used); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid), - "Unexpected RAIM removed SID!\n"); -} -END_TEST - -START_TEST(test_pvt_outlier_gps_l1ca_only) { - /* 9 L1CA signals and 1 (broken) L2CM signal */ - u8 n_used = 9; - u8 n_gps_l1ca = 8; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - navigation_measurement_t nms[9] = { - nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10b}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - GPS_L1CA_WHEN_POSSIBLE, - &soln, - &dops, - &raim_removed_sids); - - fail_unless(code == 0, - "Return code should be 0 (Solution converged and verified by " - "RAIM). Saw: %d\n", - code); - fail_unless(soln.n_sigs_used == n_gps_l1ca, - "n_sigs_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_gps_l1ca, - "n_sats_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sats_used); -} -END_TEST - -// Regression test for PIKSI-191 -START_TEST(test_calc_pvt_exclude_gal) { - u8 n_used = 8; - u8 n_gps_l1ca = 6; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - // Mixing GPS and GAL satellites - navigation_measurement_t nms[9] = { - nm3, gal_nm1, gal_nm2, nm5, nm6, nm7, nm8, nm9}; - - // Now using predicate GPS_ONLY would trigger an assert in outlier detection - // which is called from within calc_PVT() - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - GPS_ONLY, - &soln, - &dops, - &raim_removed_sids); - - fail_unless(code == 0, - "Return code should be 0 (Solution converged and verified by " - "RAIM). Saw: %d\n", - code); - fail_unless(soln.n_sigs_used == n_gps_l1ca, - "n_sigs_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_gps_l1ca, - "n_sats_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sats_used); -} - -START_TEST(test_pvt_flag_outlier_bias) { - /* 8 L1CA signals and 2 L2CM signals */ - u8 n_used = 9; - u8 n_gps_l1ca = 7; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - navigation_measurement_t nm10_bias = nm10; - navigation_measurement_t nm11_bias = nm11; - - /* add a common bias of 120 m to the L2CM measurements */ - nm10_bias.raw_pseudorange += 120; - nm11_bias.raw_pseudorange += 120; - - /* healthy measurements, with bias on L2 */ - navigation_measurement_t nms[9] = { - nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm10_bias, nm11_bias}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - GPS_L1CA_WHEN_POSSIBLE, - &soln, - &dops, - &raim_removed_sids); - fail_unless(code == 0, "Return code should be 0 (success). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_gps_l1ca, - "n_sigs_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_gps_l1ca, - "n_sats_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sats_used); - - /* add outlier to one of the L2 measurements */ - nm11_bias.raw_pseudorange += 1000; - nms[8] = nm11_bias; - - code = calc_PVT(n_used, - nms, - &tor, - false, - false, - &obs_mask_config, - GPS_L1CA_WHEN_POSSIBLE, - &soln, - &dops, - &raim_removed_sids); - - fail_unless(code == 0, - "Return code should be 0 (Solution converged and verified by " - "RAIM). Saw: %d\n", - code); - fail_unless(soln.n_sigs_used == n_gps_l1ca, - "n_sigs_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sigs_used); - fail_unless(soln.n_sats_used == n_gps_l1ca, - "n_sats_used should be %u. Saw: %u\n", - n_gps_l1ca, - soln.n_sats_used); -} -END_TEST - -START_TEST(test_disable_pvt_raim) { - u8 n_used = 6; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{false, 25}}; - - navigation_measurement_t nms[9] = { - nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; - - /* disable raim check */ - s8 code = calc_PVT(n_used, - nms, - &tor, - true, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code == 2, "Return code should be 2 (raim not used). Saw: %d\n", code); - fail_unless(soln.valid == 1, "Solution should be valid!"); -} -END_TEST - -START_TEST(test_disable_pvt_velocity) { - u8 n_used = 6; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - navigation_measurement_t nms[9] = { - nm1_no_doppler, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; - - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code >= 0, "Return code should be >=0 (success). Saw: %d\n", code); - fail_unless(soln.valid == 1, "Solution should be valid!"); - fail_unless((soln.vel_ned[0] == 0.0) && (soln.vel_ned[1] == 0.0) && - (soln.vel_ned[2] == 0.0), - "Velocity NED was not zero. " - "Saw: %.5f, %.5f, %.5f\n", - soln.vel_ned[0], - soln.vel_ned[1], - soln.vel_ned[2]); - fail_unless((soln.vel_ecef[0] == 0.0) && (soln.vel_ecef[1] == 0.0) && - (soln.vel_ecef[2] == 0.0), - "Velocity ECEF was not zero. " - "Saw: %.5f, %.5f, %.5f\n", - soln.vel_ecef[0], - soln.vel_ecef[1], - soln.vel_ecef[2]); -} -END_TEST - -START_TEST(test_count_sats) { - u8 n_used = 10; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{false, 25}}; - - navigation_measurement_t nms[10] = { - nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; - - /* disable raim check */ - s8 code = calc_PVT(n_used, - nms, - &tor, - true, - false, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code >= 0, "Return code should be >=0 (success). Saw: %d\n", code); - fail_unless(soln.valid == 1, "Solution should be valid!"); - fail_unless(soln.n_sigs_used == 10, - "n_sigs_used should be 10. Saw: %u\n", - soln.n_sigs_used); - fail_unless(soln.n_sats_used == 9, - "n_sats_used should be 9. Saw: %u\n", - soln.n_sats_used); -} -END_TEST - -START_TEST(test_count_sats_l1ca_only) { - u8 n_used = 10; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - /* 10 signals of which one is GPS L2 and others GPS L1 */ - navigation_measurement_t nms[10] = { - nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; - - /* disable raim check */ - s8 code = calc_PVT(n_used, - nms, - &tor, - true, - false, - &obs_mask_config, - GPS_L1CA_WHEN_POSSIBLE, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code >= 0, "Return code should be >=0 (success). Saw: %d\n", code); - fail_unless(soln.valid == 1, "Solution should be valid!"); - fail_unless(soln.n_sigs_used == 9, - "n_sigs_used should be 9. Saw: %u\n", - soln.n_sigs_used); - fail_unless(soln.n_sats_used == 9, - "n_sats_used should be 9. Saw: %u\n", - soln.n_sats_used); -} -END_TEST - -START_TEST(test_dops) { - u8 n_used = 6; - gnss_solution soln; - dops_t dops = {.pdop = 22, .gdop = 22, .tdop = 22, .hdop = 22, .vdop = 22}; - dops_t truedops = {.pdop = 2.69955, - .gdop = 3.07696, - .tdop = 1.47652, - .hdop = 1.76157, - .vdop = 2.04559}; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{false, 25}}; - - const double dop_tol = 1e-3; - - navigation_measurement_t nms[6] = {nm1, nm2, nm3, nm4, nm5, nm6}; - - /* disable raim check */ - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - fail_unless( - code >= 0, "Return code should be >=0 (success). Saw: %d\n", code); - fail_unless(soln.valid == 1, "Solution should be valid!"); - fail_unless(fabs(dops.pdop * dops.pdop - - (dops.vdop * dops.vdop + dops.hdop * dops.hdop)) < dop_tol, - "HDOP^2 + VDOP^2 != PDOP^2. Saw: %.5f, %.5f, %.5f, %.5f, %.5f\n", - dops.pdop, - dops.gdop, - dops.tdop, - dops.hdop, - dops.vdop); - double dop_err = - fabs(dops.pdop - truedops.pdop) + fabs(dops.gdop - truedops.gdop) + - fabs(dops.tdop - truedops.tdop) + fabs(dops.hdop - truedops.hdop) + - fabs(dops.vdop - truedops.vdop); - fail_unless(dop_err < dop_tol, - "DOPs don't match hardcoded correct values. " - "Saw: %.5f, %.5f, %.5f, %.5f, %.5f\n", - dops.pdop, - dops.gdop, - dops.tdop, - dops.hdop, - dops.vdop); -} -END_TEST - -START_TEST(test_pvt_successful_repair_with_cn0_mask) { - /* Emulate TES-238 scenario */ - - u8 n_used = 9; - gnss_solution soln; - dops_t dops; - gnss_sid_set_t raim_removed_sids; - obs_mask_config_t obs_mask_config = {{true, 25}}; - - // Given erroneous pseudorange measurement with C/N0 above mask threshold (sat - // 2) - navigation_measurement_t nm3_bias = nm3; - nm3_bias.raw_pseudorange += 4350; - - // Given erroneous measurement with C/N0 below mask threshold and pseudorange - // that if processed, does not allow for successful RAIM repair - navigation_measurement_t nm4_bias_low_cn0 = nm4; - nm4_bias_low_cn0.raw_pseudorange += 4134; - nm4_bias_low_cn0.cn0 = 21; - - // When calc_PVT is attempted with measurements defined above and C/N0 - // observation masking enabled - navigation_measurement_t nms[9] = { - nm1, nm2, nm3_bias, nm4_bias_low_cn0, nm5, nm6, nm7, nm8, nm9}; - s8 code = calc_PVT(n_used, - nms, - &tor, - false, - true, - &obs_mask_config, - ALL_CONSTELLATIONS, - &soln, - &dops, - &raim_removed_sids); - - // Expect successful RAIM repair - gnss_signal_t expected_removed_sid_1 = {.code = CODE_GPS_L1CA, .sat = 9}; - gnss_signal_t expected_removed_sid_2 = {.code = CODE_GPS_L1CA, .sat = 2}; - fail_unless( - code == 1, "Return code should be 1 (pvt repair). Saw: %d\n", code); - fail_unless(soln.n_sigs_used == n_used - 3, - "n_sigs_used should be %u. Saw: %u\n", - n_used - 3, - soln.n_sigs_used); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid_1), - "Unexpected RAIM removed SID!\n"); - fail_unless(sid_set_contains(&raim_removed_sids, expected_removed_sid_2), - "Unexpected RAIM removed SID!\n"); -} -END_TEST - -Suite *pvt_test_suite(void) { - Suite *s = suite_create("PVT Solver"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_pvt_repair); - tcase_add_test(tc_core, test_pvt_vel_repair); - tcase_add_test(tc_core, test_pvt_repair_multifailure); - tcase_add_test(tc_core, test_pvt_raim_gps_l1ca_only); - tcase_add_test(tc_core, test_pvt_outlier_gps_l1ca_only); - tcase_add_test(tc_core, test_calc_pvt_exclude_gal); - tcase_add_test(tc_core, test_pvt_flag_outlier_bias); - tcase_add_test(tc_core, test_pvt_failed_repair); - tcase_add_test(tc_core, test_pvt_raim_singular); - tcase_add_test(tc_core, test_disable_pvt_raim); - tcase_add_test(tc_core, test_disable_pvt_velocity); - tcase_add_test(tc_core, test_count_sats); - tcase_add_test(tc_core, test_count_sats_l1ca_only); - tcase_add_test(tc_core, test_dops); - tcase_add_test(tc_core, test_pvt_successful_repair_with_cn0_mask); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_sid_set.c b/tests/check_sid_set.c deleted file mode 100644 index d3e10ad..0000000 --- a/tests/check_sid_set.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -START_TEST(test_sid_set_empty) { - gnss_sid_set_t sid_set; - sid_set_init(&sid_set); - u32 count = sid_set_get_sat_count(&sid_set); - - fail_unless(count == 0, "count was not 0. Saw %u", count); -} -END_TEST - -START_TEST(test_sid_set) { - gnss_signal_t sids[] = { - {.code = CODE_GPS_L1CA, .sat = 1}, - {.code = CODE_GPS_L2CM, .sat = 1}, - {.code = CODE_GPS_L1CA, .sat = 2}, - {.code = CODE_GPS_L2CM, .sat = 3}, - {.code = CODE_GLO_L1OF, .sat = 1}, - }; - - gnss_sid_set_t sid_set; - sid_set_init(&sid_set); - - for (u32 i = 0; i < sizeof(sids) / sizeof(sids[0]); i++) { - const gnss_signal_t sid = sids[i]; - sid_set_add(&sid_set, sid); - fail_unless(sid_set_get_sig_count(&sid_set) == i + 1); - } - - u32 count = sid_set_get_sat_count(&sid_set); - - fail_unless(count == 4, "count was not 4. Saw %u", count); -} -END_TEST - -START_TEST(test_sid_set_contains) { - gnss_signal_t sids[] = { - {.code = CODE_GPS_L1CA, .sat = 1}, - {.code = CODE_GPS_L2CM, .sat = 1}, - {.code = CODE_GPS_L1CA, .sat = 2}, - {.code = CODE_GPS_L2CM, .sat = 3}, - {.code = CODE_GLO_L1OF, .sat = 1}, - }; - - gnss_sid_set_t sid_set; - sid_set_init(&sid_set); - - /* check that add works by adding sids one by one */ - for (u32 i = 0; i < sizeof(sids) / sizeof(sids[0]); i++) { - const gnss_signal_t sid = sids[i]; - fail_unless(sid_set_contains(&sid_set, sid) == false, - "%d: sid_set should not contain (code %d, sat %d)", - i, - sid.code, - sid.sat); - sid_set_add(&sid_set, sid); - fail_unless(sid_set_contains(&sid_set, sid) == true, - "%d: sid_set should contain (code %d, sat %d)", - i, - sid.code, - sid.sat); - } - - /* check that remove works by removing sids one by one */ - for (u32 i = 0; i < sizeof(sids) / sizeof(sids[0]); i++) { - const gnss_signal_t sid = sids[i]; - fail_unless(sid_set_contains(&sid_set, sid) == true, - "%d: sid_set should contain (code %d, sat %d)", - i, - sid.code, - sid.sat); - sid_set_remove(&sid_set, sid); - fail_unless(sid_set_contains(&sid_set, sid) == false, - "%d: sid_set should not contain (code %d, sat %d)", - i, - sid.code, - sid.sat); - } -} -END_TEST - -Suite *sid_set_test_suite(void) { - Suite *s = suite_create("SID Set"); - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_sid_set_empty); - tcase_add_test(tc_core, test_sid_set); - tcase_add_test(tc_core, test_sid_set_contains); - suite_add_tcase(s, tc_core); - return s; -} diff --git a/tests/check_signal.c b/tests/check_signal.c deleted file mode 100644 index f089342..0000000 --- a/tests/check_signal.c +++ /dev/null @@ -1,618 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "check_suites.h" -#include "common/check_utils.h" - -/* Include singal.c here to have a chance to turn off asserts. - Otherwise the code lines with asserts cannot be covered by - tests and it will lower test coverage statistics. */ -#ifndef NDEBUG -#define NDEBUG -#endif -#include "src/signal.c" - -#define ARRAY_COUNT(arr) ((sizeof(arr) / sizeof(arr[0]))) - -static const struct code_data_element { - code_t code; - u16 sat_count; -} code_data[] = { - {CODE_GPS_L1CA, NUM_SIGNALS_GPS_L1CA}, - {CODE_AUX_GPS, NUM_SIGNALS_GPS_L1CA}, - {CODE_GPS_L2CM, NUM_SIGNALS_GPS_L2C}, - {CODE_GPS_L2CL, NUM_SIGNALS_GPS_L2C}, - {CODE_GPS_L2CX, NUM_SIGNALS_GPS_L2C}, - {CODE_GPS_L5I, NUM_SIGNALS_GPS_L5}, - {CODE_GPS_L5Q, NUM_SIGNALS_GPS_L5}, - {CODE_GPS_L5X, NUM_SIGNALS_GPS_L5}, - {CODE_GPS_L1CI, NUM_SIGNALS_GPS_L1C}, - {CODE_GPS_L1CQ, NUM_SIGNALS_GPS_L1C}, - {CODE_GPS_L1CX, NUM_SIGNALS_GPS_L1C}, - {CODE_GPS_L1P, NUM_SIGNALS_GPS_L1P}, - {CODE_GPS_L2P, NUM_SIGNALS_GPS_L2P}, - - {CODE_SBAS_L1CA, NUM_SIGNALS_SBAS_L1CA}, - {CODE_AUX_SBAS, NUM_SIGNALS_SBAS_L1CA}, - {CODE_SBAS_L5I, NUM_SIGNALS_SBAS_L5}, - {CODE_SBAS_L5Q, NUM_SIGNALS_SBAS_L5}, - {CODE_SBAS_L5X, NUM_SIGNALS_SBAS_L5}, - - {CODE_GLO_L1OF, NUM_SIGNALS_GLO_L1OF}, - {CODE_GLO_L2OF, NUM_SIGNALS_GLO_L2OF}, - {CODE_GLO_L1P, NUM_SIGNALS_GLO_L1P}, - {CODE_GLO_L2P, NUM_SIGNALS_GLO_L2P}, - - {CODE_BDS2_B1, NUM_SIGNALS_BDS2_B1}, - {CODE_AUX_BDS, NUM_SIGNALS_BDS2_B1}, - {CODE_BDS2_B2, NUM_SIGNALS_BDS2_B2}, - {CODE_BDS3_B1CI, NUM_SIGNALS_BDS3_B1C}, - {CODE_BDS3_B1CQ, NUM_SIGNALS_BDS3_B1C}, - {CODE_BDS3_B1CX, NUM_SIGNALS_BDS3_B1C}, - {CODE_BDS3_B5I, NUM_SIGNALS_BDS3_B5}, - {CODE_BDS3_B5Q, NUM_SIGNALS_BDS3_B5}, - {CODE_BDS3_B5X, NUM_SIGNALS_BDS3_B5}, - {CODE_BDS3_B7I, NUM_SIGNALS_BDS3_B7}, - {CODE_BDS3_B7Q, NUM_SIGNALS_BDS3_B7}, - {CODE_BDS3_B7X, NUM_SIGNALS_BDS3_B7}, - {CODE_BDS3_B3I, NUM_SIGNALS_BDS3_B3}, - {CODE_BDS3_B3Q, NUM_SIGNALS_BDS3_B3}, - {CODE_BDS3_B3X, NUM_SIGNALS_BDS3_B3}, - - {CODE_GAL_E1B, NUM_SIGNALS_GAL_E1}, - {CODE_GAL_E1C, NUM_SIGNALS_GAL_E1}, - {CODE_GAL_E1X, NUM_SIGNALS_GAL_E1}, - {CODE_AUX_GAL, NUM_SIGNALS_GAL_E1}, - {CODE_GAL_E6B, NUM_SIGNALS_GAL_E6}, - {CODE_GAL_E6C, NUM_SIGNALS_GAL_E6}, - {CODE_GAL_E6X, NUM_SIGNALS_GAL_E6}, - {CODE_GAL_E7I, NUM_SIGNALS_GAL_E7}, - {CODE_GAL_E7Q, NUM_SIGNALS_GAL_E7}, - {CODE_GAL_E7X, NUM_SIGNALS_GAL_E7}, - {CODE_GAL_E8I, NUM_SIGNALS_GAL_E8}, - {CODE_GAL_E8Q, NUM_SIGNALS_GAL_E8}, - {CODE_GAL_E8X, NUM_SIGNALS_GAL_E8}, - {CODE_GAL_E5I, NUM_SIGNALS_GAL_E5}, - {CODE_GAL_E5Q, NUM_SIGNALS_GAL_E5}, - {CODE_GAL_E5X, NUM_SIGNALS_GAL_E5}, - - {CODE_QZS_L1CA, NUM_SIGNALS_QZS_L1}, - {CODE_AUX_QZS, NUM_SIGNALS_QZS_L1}, - {CODE_QZS_L1CI, NUM_SIGNALS_QZS_L1C}, - {CODE_QZS_L1CQ, NUM_SIGNALS_QZS_L1C}, - {CODE_QZS_L1CX, NUM_SIGNALS_QZS_L1C}, - {CODE_QZS_L2CM, NUM_SIGNALS_QZS_L2C}, - {CODE_QZS_L2CL, NUM_SIGNALS_QZS_L2C}, - {CODE_QZS_L2CX, NUM_SIGNALS_QZS_L2C}, - {CODE_QZS_L5I, NUM_SIGNALS_QZS_L5}, - {CODE_QZS_L5Q, NUM_SIGNALS_QZS_L5}, - {CODE_QZS_L5X, NUM_SIGNALS_QZS_L5}, -}; - -static const struct constellation_data_element { - constellation_t constellation; - u16 sat_count; - u16 code_count; -} constellation_data[] = { - {CONSTELLATION_GPS, NUM_SIGNALS_GPS, NUM_CODES_GPS}, - {CONSTELLATION_SBAS, NUM_SIGNALS_SBAS, NUM_CODES_SBAS}, - {CONSTELLATION_GLO, NUM_SIGNALS_GLO, NUM_CODES_GLO}, - {CONSTELLATION_BDS, NUM_SIGNALS_BDS, NUM_CODES_BDS}, - {CONSTELLATION_QZS, NUM_SIGNALS_QZS, NUM_CODES_QZS}, - {CONSTELLATION_GAL, NUM_SIGNALS_GAL, NUM_CODES_GAL}, -}; - -START_TEST(test_signal_aggregates) { - fail_unless(ARRAY_COUNT(code_data) == CODE_COUNT, - "missing code entry in code_data[]"); - - fail_unless(ARRAY_COUNT(constellation_data) == CONSTELLATION_COUNT, - "missing constellation entry in constellation_data[]"); - - u16 constellation_code_counts[CONSTELLATION_COUNT]; - memset(constellation_code_counts, 0, sizeof(constellation_code_counts)); - for (u32 i = 0; i < ARRAY_COUNT(code_data); i++) { - const struct code_data_element *e = &code_data[i]; - constellation_t constellation = code_to_constellation(e->code); - constellation_code_counts[constellation]++; - } - for (u32 i = 0; i < ARRAY_COUNT(constellation_data); i++) { - const struct constellation_data_element *e = &constellation_data[i]; - fail_unless(e->code_count == constellation_code_counts[e->constellation], - "invalid code count definition for code %d", - e->constellation); - } -} -END_TEST - -START_TEST(test_signal_from_index) { - for (u32 i = 0; i < ARRAY_COUNT(code_data); i++) { - const struct code_data_element *e = &code_data[i]; - code_t code = e->code; - u16 sat_count = e->sat_count; - for (u16 code_index = 0; code_index < sat_count; code_index++) { - gnss_signal_t sid = sid_from_code_index(code, code_index); - - fail_unless(sid_valid(sid), - "signal from code index not valid: " - "code %d code index %d", - code, - code_index); - fail_unless(sid_to_code_index(sid) == code_index, - "signal from code index to code index failed: " - "code %d code index %d", - code, - code_index); - } - } -} -END_TEST - -START_TEST(test_signal_properties) { - const struct test_case { - gnss_signal_t sid; - bool valid; - char str[SID_STR_LEN_MAX]; - } test_cases[] = { - {.sid = {.code = CODE_INVALID, .sat = 0}, .valid = false}, - {.sid = {.code = CODE_COUNT, .sat = 0}, .valid = false}, - {.sid = {.code = CODE_INVALID, .sat = 1}, .valid = false}, - { - .sid = {.code = CODE_GPS_L1CA, .sat = 0}, - .valid = false, - }, - {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .valid = true, - .str = "GPS L1CA 1"}, - {.sid = {.code = CODE_GPS_L2CM, .sat = 1}, - .valid = true, - .str = "GPS L2CM 1"}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 1}, .valid = false}, - {.sid = {.code = CODE_GPS_L1CA, .sat = 32}, - .valid = true, - .str = "GPS L1CA 32"}, - {.sid = {.code = CODE_GPS_L1CA, .sat = 33}, .valid = false}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 0}, .valid = false}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 119}, .valid = false}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 120}, - .valid = true, - .str = "SBAS L1 120"}, - {.sid = {.code = CODE_GPS_L1CA, .sat = 120}, .valid = false}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 138}, - .valid = true, - .str = "SBAS L1 138"}, - {.sid = {.code = CODE_SBAS_L1CA, .sat = 139}, .valid = false}, - { - .sid = {.code = CODE_GLO_L1OF, .sat = 0}, - .valid = false, - }, - {.sid = {.code = CODE_GLO_L1OF, .sat = 1}, - .valid = true, - .str = "GLO L1OF 1"}, - {.sid = {.code = CODE_GLO_L1OF, .sat = 28}, - .valid = true, - .str = "GLO L1OF 28"}, - {.sid = {.code = CODE_GLO_L1OF, .sat = 29}, .valid = false}, - { - .sid = {.code = CODE_GLO_L2OF, .sat = 0}, - .valid = false, - }, - {.sid = {.code = CODE_GLO_L2OF, .sat = 1}, - .valid = true, - .str = "GLO L2OF 1"}, - {.sid = {.code = CODE_GLO_L2OF, .sat = 28}, - .valid = true, - .str = "GLO L2OF 28"}, - {.sid = {.code = CODE_GLO_L2OF, .sat = 29}, .valid = false}, - {.sid = {.code = CODE_GPS_L1P, .sat = 0}, - .valid = false, - .str = "GPS L1P 0"}, - {.sid = {.code = CODE_GPS_L1P, .sat = 1}, - .valid = true, - .str = "GPS L1P 1"}, - {.sid = {.code = CODE_GPS_L1P, .sat = 24}, - .valid = true, - .str = "GPS L1P 24"}, - {.sid = {.code = CODE_GPS_L2P, .sat = 0}, - .valid = false, - .str = "GPS L2P 0"}, - {.sid = {.code = CODE_GPS_L2P, .sat = 1}, - .valid = true, - .str = "GPS L2P 1"}, - {.sid = {.code = CODE_GPS_L2P, .sat = 24}, - .valid = true, - .str = "GPS L2P 24"}, - {.sid = {.code = CODE_BDS2_B1, .sat = 0}, .valid = false}, - {.sid = {.code = CODE_BDS2_B1, .sat = 1}, - .valid = true, - .str = "BDS B1 1"}, - {.sid = {.code = CODE_BDS2_B1, .sat = 41}, - .valid = true, - .str = "BDS B1 41"}, - {.sid = {.code = CODE_BDS2_B1, .sat = 65}, .valid = false}, - }; - - for (u32 i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { - const struct test_case *t = &test_cases[i]; - bool valid = sid_valid(t->sid); - fail_unless(t->valid == valid, "test signal %d validity incorrect", i); - if (valid) { - gnss_signal_t sid = - sid_from_code_index(t->sid.code, sid_to_code_index(t->sid)); - fail_unless(sid_is_equal(t->sid, sid), - "test signal %d code index conversion failed", - sid.code); - char str[SID_STR_LEN_MAX] = {0}; - u32 ret = sid_to_string(str, sizeof(str), sid); - fail_unless((strcmp(str, t->str) == 0) && (ret == strlen(t->str)), - "signal to string \"%s\" failed", - t->str); - fail_unless(constellation_valid(code_to_constellation(sid.code)), - "constellation_valid failed for code %d", - sid.code); - } - fail_unless(!constellation_valid(CONSTELLATION_COUNT), - "constellation_valid failed for constellation %d", - CONSTELLATION_COUNT); - } -} -END_TEST - -START_TEST(test_signal_compare) { - gnss_signal_t sids[NUM_SIGNALS]; - u32 signal_index = 0; - - for (u32 i = 0; i < ARRAY_COUNT(code_data); i++) { - const struct code_data_element *e = &code_data[i]; - code_t code = e->code; - u16 sat_count = e->sat_count; - for (u16 sat_index = 0; sat_index < sat_count; sat_index++) { - gnss_signal_t sid = sid_from_code_index(code, sat_index); - sids[signal_index++] = sid; - } - } - - qsort(sids, NUM_SIGNALS, sizeof(gnss_signal_t), cmp_sid_sid); - - for (u32 i = 1; i < NUM_SIGNALS; i++) { - fail_unless( - !(sid_is_equal(sids[i], sids[i - 1])), "signal index %d not unique", i); - fail_unless(sid_compare(sids[i], sids[i - 1]) >= 0, - "signal index %d not in order", - i); - } -} -END_TEST - -START_TEST(test_signal_construction) { - for (u32 i = 0; i < ARRAY_COUNT(code_data); i++) { - const struct code_data_element *e = &code_data[i]; - code_t code = e->code; - u16 sat_count = e->sat_count; - for (u16 code_index = 0; code_index < sat_count; code_index++) { - gnss_signal_t sid = sid_from_code_index(code, code_index); - gnss_signal_t csid = construct_sid(sid.code, sid.sat); - fail_unless(sid_valid(csid), - "constructed signal not valid: " - "code %d code index %d", - code, - code_index); - fail_unless(sid_is_equal(sid, csid), - "constructed signal mismatch: " - "code %d code index %d", - code, - code_index); - } - } -} -END_TEST - -static void glo_map_lock(void) {} -static void glo_map_unlock(void) {} - -START_TEST(test_signal_sid_to_carr_freq) { - /* We do not test thread safety here. - Therefore lock & unlock functions are just stubs. */ - glo_map_init(glo_map_lock, glo_map_unlock); - - double carr_freq; - gnss_signal_t sid = construct_sid(CODE_GPS_L2CM, GPS_FIRST_PRN); - carr_freq = sid_to_carr_freq(sid); - fail_unless(GPS_L2_HZ == carr_freq, "L2 carr freq error"); - - sid = construct_sid(CODE_GPS_L1CA, GPS_FIRST_PRN); - carr_freq = sid_to_carr_freq(sid); - fail_unless(GPS_L1_HZ == carr_freq, "L1 carr freq error"); - - /* check all GLO frequency and orbital slots */ - for (u16 sat = GLO_FIRST_PRN; sat <= NUM_SATS_GLO; sat++) { - for (u16 fcn = GLO_MIN_FCN; fcn <= GLO_MAX_FCN; fcn++) { - /* L2 first */ - /* map orb and fcn slots */ - glo_map_set_slot_id(fcn, sat); - sid = construct_sid(CODE_GLO_L2OF, sat); - carr_freq = sid_to_carr_freq(sid); - fail_unless((GLO_L2_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * - GLO_L2_DELTA_HZ) == carr_freq, - "Glonass L2 carrier error"); - /* now L1 */ - sid = construct_sid(CODE_GLO_L1OF, sat); - carr_freq = sid_to_carr_freq(sid); - fail_unless((GLO_L1_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * - GLO_L1_DELTA_HZ) == carr_freq, - "Glonass L1 carrier error"); - } - } -} -END_TEST - -START_TEST(test_signal_sid_to_lambda) { - /* We do not test thread safety here. - Therefore lock & unlock functions are just stubs. */ - glo_map_init(glo_map_lock, glo_map_unlock); - - double lambda; - - gnss_signal_t sid = construct_sid(CODE_GPS_L2CM, GPS_FIRST_PRN); - lambda = sid_to_lambda(sid); - fail_unless((GPS_C / GPS_L2_HZ) == lambda, "GPS L2 wavelength error"); - - sid = construct_sid(CODE_GPS_L1CA, GPS_FIRST_PRN); - lambda = sid_to_lambda(sid); - fail_unless((GPS_C / GPS_L1_HZ) == lambda, "GPS L1 wavelength error"); - - /* check all GLO frequency and orbital slots */ - for (u16 orb_slot = GLO_FIRST_PRN; orb_slot <= NUM_SATS_GLO; orb_slot++) { - for (u16 fcn = GLO_MIN_FCN; fcn <= GLO_MAX_FCN; fcn++) { - /* L2 first */ - /* map orb and fcn slots */ - glo_map_set_slot_id(fcn, orb_slot); - sid = construct_sid(CODE_GLO_L2OF, orb_slot); - lambda = sid_to_lambda(sid); - fail_unless( - (GPS_C / (GLO_L2_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * - GLO_L2_DELTA_HZ)) == lambda, - "Glonass L2 wavelength error"); - /* now L1 */ - sid = construct_sid(CODE_GLO_L1OF, orb_slot); - lambda = sid_to_lambda(sid); - fail_unless( - (GPS_C / (GLO_L1_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * - GLO_L1_DELTA_HZ)) == lambda, - "Glonass L1 wavelength error"); - } - } -} -END_TEST - -START_TEST(test_signal_code_to_chip_count) { - u32 chip_count; - - chip_count = code_to_chip_count(CODE_GPS_L1CA); - fail_unless(GPS_L1CA_CHIPS_NUM == chip_count, "GPS_L1CA_CHIPS_NUM error"); - - chip_count = code_to_chip_count(CODE_SBAS_L1CA); - fail_unless(GPS_L1CA_CHIPS_NUM == chip_count, "CODE_SBAS_L1CA error"); - - chip_count = code_to_chip_count(CODE_GPS_L2CM); - fail_unless(GPS_L2CM_CHIPS_NUM == chip_count, "CODE_GPS_L2CM error"); - - chip_count = code_to_chip_count(CODE_GPS_L2CL); - fail_unless(GPS_L2CL_CHIPS_NUM == chip_count, "CODE_GPS_L2CL error"); - - /* check unsupported branch for code coverage stats */ - chip_count = code_to_chip_count(CODE_GLO_L2OF); -} -END_TEST - -START_TEST(test_signal_code_to_chip_rate) { - double chip_rate; - - chip_rate = code_to_chip_rate(CODE_GPS_L1CA); - fail_unless(GPS_CA_CHIPPING_RATE == chip_rate, "GPS_CA_CHIPPING_RATE error"); - - chip_rate = code_to_chip_rate(CODE_SBAS_L1CA); - fail_unless(GPS_CA_CHIPPING_RATE == chip_rate, - "CODE_SBAS_L1CA chip rate error"); - - chip_rate = code_to_chip_rate(CODE_GPS_L2CM); - fail_unless(GPS_CA_CHIPPING_RATE == chip_rate, - "CODE_GPS_L2CM chip rate error"); - - /* check unsupported branch for code coverage stats */ - chip_rate = code_to_chip_rate(CODE_GLO_L2OF); -} -END_TEST - -START_TEST(test_signal_code_requires_direct_acq) { - bool requires; - - requires = code_requires_direct_acq(CODE_GPS_L1CA); - fail_unless(true == requires, "CODE_GPS_L1CA requires code_to_chip_rate"); - - requires = code_requires_direct_acq(CODE_GPS_L2CM); - fail_unless(false == requires, - "CODE_GPS_L2CM requires code_requires_direct_acq"); -} -END_TEST - -START_TEST(test_signal_code_to_prn_period) { - u16 period; - - period = code_to_prn_period_ms(CODE_GPS_L1CA); - fail_unless(1 == period, "period not 1"); - - period = code_to_prn_period_ms(CODE_GPS_L2CM); - fail_unless(20 == period, "period not 1"); - - period = code_to_prn_period_ms(CODE_GLO_L1OF); - fail_unless(1 == period, "period not 1"); -} -END_TEST - -START_TEST(test_sid_system_check) { - for (u8 i = 0; i < CODE_COUNT; i++) { - bool gps = - (i == CODE_GPS_L1CA) || (i == CODE_AUX_GPS) || (i == CODE_GPS_L2CM) || - (i == CODE_GPS_L2CL) || (i == CODE_GPS_L2CX) || (i == CODE_GPS_L1P) || - (i == CODE_GPS_L2P) || (i == CODE_GPS_L5I) || (i == CODE_GPS_L5Q) || - (i == CODE_GPS_L5X) || (i == CODE_GPS_L1CI) || (i == CODE_GPS_L1CQ) || - (i == CODE_GPS_L1CX); - - fail_unless(gps == IS_GPS(construct_sid(i, GPS_FIRST_PRN)), - "is_gps_sid fail"); - - bool glo = (i == CODE_GLO_L1OF) || (i == CODE_GLO_L2OF) || - (i == CODE_GLO_L1P) || (i == CODE_GLO_L2P); - - fail_unless(glo == IS_GLO(construct_sid(i, GLO_FIRST_PRN)), - "is_glo_sid fail"); - - bool sbas = (i == CODE_SBAS_L1CA) || (i == CODE_AUX_SBAS) || - (i == CODE_SBAS_L5I) || (i == CODE_SBAS_L5Q) || - (i == CODE_SBAS_L5X); - - fail_unless(sbas == IS_SBAS(construct_sid(i, SBAS_FIRST_PRN)), - "is_sbas_sid fail"); - } -} -END_TEST - -START_TEST(test_sbas_prn_list) { - for (u8 prn = SBAS_FIRST_PRN; prn < SBAS_FIRST_PRN + NUM_SATS_SBAS; prn++) { - gnss_signal_t sid = {prn, CODE_SBAS_L1CA}; - sbas_system_t sbas_system = get_sbas_system(sid); - fail_unless(sbas_system < SBAS_COUNT); - } - - for (sbas_system_t sbas_system = (sbas_system_t)0; sbas_system < SBAS_COUNT; - sbas_system = (sbas_system_t)(sbas_system + 1)) { - for (u8 i = 0; i < MAX_SBAS_SATS_PER_SYSTEM; i++) { - u8 prn = sbas_prn_table[sbas_system].prn_list[i]; - fail_unless(prn == 0 || prn >= SBAS_FIRST_PRN, - "invalid PRN in sbas_prn_table"); - fail_unless(prn < SBAS_FIRST_PRN + NUM_SATS_SBAS, - "invalid PRN in sbas_prn_table"); - - if (prn >= SBAS_FIRST_PRN) { - gnss_signal_t sid = {prn, CODE_SBAS_L1CA}; - fail_unless(get_sbas_system(sid) == sbas_system); - } - } - } -} -END_TEST - -START_TEST(test_signal_hashes) { - for (s32 test_round = 0; test_round < 1000000; ++test_round) { - gnss_signal_t sid1, sid2; - sid1.code = (code_t)(rand() % CODE_COUNT); - sid1.sat = (u16)(rand() % MAX_NUM_SATS); - sid2.code = (code_t)(rand() % CODE_COUNT); - sid2.sat = (u16)(rand() % MAX_NUM_SATS); - - constellation_t sid1_constellation = sid_to_constellation(sid1); - constellation_t sid2_constellation = sid_to_constellation(sid2); - - int comparison = sid_compare(sid1, sid2); - - if (sid1_constellation < sid2_constellation) { - ck_assert_int_lt(comparison, 0); - } else if (sid1_constellation > sid2_constellation) { - ck_assert_int_gt(comparison, 0); - } else { - if (sid1.code < sid2.code) { - ck_assert_int_lt(comparison, 0); - } else if (sid1.code > sid2.code) { - ck_assert_int_gt(comparison, 0); - } else { - if (sid1.sat < sid2.sat) { - ck_assert_int_lt(comparison, 0); - } else if (sid1.sat > sid2.sat) { - ck_assert_int_gt(comparison, 0); - } else { - ck_assert_int_eq(comparison, 0); - } - } - } - } -} -END_TEST - -START_TEST(test_constellation_to_string) { - ck_assert_str_eq("GPS", constellation_to_string(CONSTELLATION_GPS)); - ck_assert_str_eq("GPS", constellation_to_string(CONSTELLATION_GPS)); - ck_assert_str_eq("SBAS", constellation_to_string(CONSTELLATION_SBAS)); - ck_assert_str_eq("GLO", constellation_to_string(CONSTELLATION_GLO)); - ck_assert_str_eq("BDS", constellation_to_string(CONSTELLATION_BDS)); - ck_assert_str_eq("QZS", constellation_to_string(CONSTELLATION_QZS)); - ck_assert_str_eq("GAL", constellation_to_string(CONSTELLATION_GAL)); -} -END_TEST - -START_TEST(test_sub_constellation_to_string) { - ck_assert_str_eq("GPS", sub_constellation_to_string(SUB_CONSTELLATION_GPS)); - ck_assert_str_eq("SBAS", sub_constellation_to_string(SUB_CONSTELLATION_SBAS)); - ck_assert_str_eq("GLO", sub_constellation_to_string(SUB_CONSTELLATION_GLO)); - ck_assert_str_eq("BDS2", sub_constellation_to_string(SUB_CONSTELLATION_BDS2)); - ck_assert_str_eq("BDS3", sub_constellation_to_string(SUB_CONSTELLATION_BDS3)); - ck_assert_str_eq("QZS", sub_constellation_to_string(SUB_CONSTELLATION_QZS)); - ck_assert_str_eq("GAL", sub_constellation_to_string(SUB_CONSTELLATION_GAL)); -} -END_TEST - -START_TEST(test_sub_constellation_to_constellation) { - fail_unless(CONSTELLATION_GPS == - sub_constellation_to_constellation(SUB_CONSTELLATION_GPS), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_GPS == - sub_constellation_to_constellation(SUB_CONSTELLATION_GPS), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_SBAS == - sub_constellation_to_constellation(SUB_CONSTELLATION_SBAS), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_GLO == - sub_constellation_to_constellation(SUB_CONSTELLATION_GLO), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_BDS == - sub_constellation_to_constellation(SUB_CONSTELLATION_BDS2), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_BDS == - sub_constellation_to_constellation(SUB_CONSTELLATION_BDS3), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_QZS == - sub_constellation_to_constellation(SUB_CONSTELLATION_QZS), - "bad sub_constellation conversion"); - fail_unless(CONSTELLATION_GAL == - sub_constellation_to_constellation(SUB_CONSTELLATION_GAL), - "bad sub_constellation conversion"); -} -END_TEST - -Suite *signal_test_suite(void) { - Suite *s = suite_create("Signal"); - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_signal_aggregates); - tcase_add_test(tc_core, test_signal_from_index); - tcase_add_test(tc_core, test_signal_properties); - tcase_add_test(tc_core, test_signal_compare); - tcase_add_test(tc_core, test_signal_construction); - tcase_add_test(tc_core, test_signal_sid_to_carr_freq); - tcase_add_test(tc_core, test_signal_code_to_chip_count); - tcase_add_test(tc_core, test_signal_code_to_chip_rate); - tcase_add_test(tc_core, test_signal_code_requires_direct_acq); - tcase_add_test(tc_core, test_signal_sid_to_lambda); - tcase_add_test(tc_core, test_signal_code_to_prn_period); - tcase_add_test(tc_core, test_sid_system_check); - tcase_add_test(tc_core, test_sbas_prn_list); - tcase_add_test(tc_core, test_signal_hashes); - tcase_add_test(tc_core, test_constellation_to_string); - tcase_add_test(tc_core, test_sub_constellation_to_string); - tcase_add_test(tc_core, test_sub_constellation_to_constellation); - suite_add_tcase(s, tc_core); - return s; -} diff --git a/tests/check_subsystem_status_report.c b/tests/check_subsystem_status_report.c deleted file mode 100644 index 0c6727b..0000000 --- a/tests/check_subsystem_status_report.c +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright (C) 2022 Swift Navigation Inc. - * Contact: Swift Navigation - * - * This source is subject to the license found in the file 'LICENSE' which must - * distributed together with this source. All other rights reserved. - * - * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, - * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. - */ - -#include -#include -#include - -#include "check_suites.h" - -typedef struct { - size_t invocations; - uint16_t component; - uint8_t generic; - uint8_t specific; - void *context; - size_t counter; -} callback_tracker_t; - -static size_t counter_tracker; -static callback_tracker_t callback_tracker[3]; - -static void callback1(uint16_t component, - uint8_t generic, - uint8_t specific, - void *context) { - const size_t index = 0; - callback_tracker[index].invocations++; - callback_tracker[index].component = component; - callback_tracker[index].generic = generic; - callback_tracker[index].specific = specific; - callback_tracker[index].context = context; - callback_tracker[index].counter = counter_tracker++; -} - -static void callback2(uint16_t component, - uint8_t generic, - uint8_t specific, - void *context) { - const size_t index = 1; - callback_tracker[index].invocations++; - callback_tracker[index].component = component; - callback_tracker[index].generic = generic; - callback_tracker[index].specific = specific; - callback_tracker[index].context = context; - callback_tracker[index].counter = counter_tracker++; -} - -static void callback3(uint16_t component, - uint8_t generic, - uint8_t specific, - void *context) { - const size_t index = 2; - callback_tracker[index].invocations++; - callback_tracker[index].component = component; - callback_tracker[index].generic = generic; - callback_tracker[index].specific = specific; - callback_tracker[index].context = context; - callback_tracker[index].counter = counter_tracker++; -} - -static void fixture_teardown() { - counter_tracker = 0; - memset(&callback_tracker, 0, sizeof(callback_tracker)); - swiftnav_subsystem_status_report_callback_reset(); -} - -START_TEST(test_no_registered_callbacks) { - swiftnav_send_subsystem_status_report(0, 1, 2); - - ck_assert_uint_eq(callback_tracker[0].invocations, 0); - ck_assert_uint_eq(callback_tracker[1].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 0); -} -END_TEST - -START_TEST(test_one_registered_callbacks) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_send_subsystem_status_report(0, 1, 2); - - ck_assert_uint_eq(callback_tracker[0].invocations, 1); - ck_assert_uint_eq(callback_tracker[0].component, 0); - ck_assert_uint_eq(callback_tracker[0].generic, 1); - ck_assert_uint_eq(callback_tracker[0].specific, 2); - ck_assert_ptr_eq(callback_tracker[0].context, (void *)0xff); - ck_assert_uint_eq(callback_tracker[0].counter, 0); - - ck_assert_uint_eq(callback_tracker[1].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 0); -} -END_TEST - -START_TEST(test_two_registered_callbacks) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_subsystem_status_report_callback_node_t node2; - swiftnav_subsystem_status_report_callback_register( - &node2, callback2, (void *)0xee); - - swiftnav_send_subsystem_status_report(0, 1, 2); - - ck_assert_uint_eq(callback_tracker[0].invocations, 1); - ck_assert_uint_eq(callback_tracker[0].component, 0); - ck_assert_uint_eq(callback_tracker[0].generic, 1); - ck_assert_uint_eq(callback_tracker[0].specific, 2); - ck_assert_ptr_eq(callback_tracker[0].context, (void *)0xff); - ck_assert_uint_eq(callback_tracker[0].counter, 0); - - ck_assert_uint_eq(callback_tracker[1].invocations, 1); - ck_assert_uint_eq(callback_tracker[1].component, 0); - ck_assert_uint_eq(callback_tracker[1].generic, 1); - ck_assert_uint_eq(callback_tracker[1].specific, 2); - ck_assert_ptr_eq(callback_tracker[1].context, (void *)0xee); - ck_assert_uint_eq(callback_tracker[1].counter, 1); - - ck_assert_uint_eq(callback_tracker[2].invocations, 0); -} -END_TEST - -START_TEST(test_no_registered_callbacks_deregister) { - swiftnav_subsystem_status_report_callback_deregister(NULL); - - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_deregister(&node1); -} -END_TEST - -START_TEST(test_one_registered_callbacks_deregister) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_subsystem_status_report_callback_deregister(NULL); - swiftnav_subsystem_status_report_callback_deregister(&node1); - - swiftnav_send_subsystem_status_report(0, 1, 2); - ck_assert_uint_eq(callback_tracker[0].invocations, 0); - ck_assert_uint_eq(callback_tracker[1].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 0); -} -END_TEST - -START_TEST(test_two_registered_callbacks_deregister_first) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_subsystem_status_report_callback_node_t node2; - swiftnav_subsystem_status_report_callback_register( - &node2, callback2, (void *)0xee); - - swiftnav_subsystem_status_report_callback_deregister(NULL); - swiftnav_subsystem_status_report_callback_deregister(&node1); - - swiftnav_send_subsystem_status_report(0, 1, 2); - ck_assert_uint_eq(callback_tracker[0].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 0); - - ck_assert_uint_eq(callback_tracker[1].invocations, 1); - ck_assert_uint_eq(callback_tracker[1].component, 0); - ck_assert_uint_eq(callback_tracker[1].generic, 1); - ck_assert_uint_eq(callback_tracker[1].specific, 2); - ck_assert_ptr_eq(callback_tracker[1].context, (void *)0xee); - ck_assert_uint_eq(callback_tracker[1].counter, 0); -} -END_TEST - -START_TEST(test_two_registered_callbacks_deregister_second) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_subsystem_status_report_callback_node_t node2; - swiftnav_subsystem_status_report_callback_register( - &node2, callback2, (void *)0xee); - - swiftnav_subsystem_status_report_callback_deregister(NULL); - swiftnav_subsystem_status_report_callback_deregister(&node2); - - swiftnav_send_subsystem_status_report(0, 1, 2); - ck_assert_uint_eq(callback_tracker[0].invocations, 1); - ck_assert_uint_eq(callback_tracker[1].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 0); - - ck_assert_uint_eq(callback_tracker[0].component, 0); - ck_assert_uint_eq(callback_tracker[0].generic, 1); - ck_assert_uint_eq(callback_tracker[0].specific, 2); - ck_assert_ptr_eq(callback_tracker[0].context, (void *)0xff); - ck_assert_uint_eq(callback_tracker[0].counter, 0); -} -END_TEST - -START_TEST(test_three_registered_callbacks_deregister_second) { - swiftnav_subsystem_status_report_callback_node_t node1; - swiftnav_subsystem_status_report_callback_register( - &node1, callback1, (void *)0xff); - - swiftnav_subsystem_status_report_callback_node_t node2; - swiftnav_subsystem_status_report_callback_register( - &node2, callback2, (void *)0xee); - - swiftnav_subsystem_status_report_callback_node_t node3; - swiftnav_subsystem_status_report_callback_register( - &node3, callback3, (void *)0xdd); - - swiftnav_subsystem_status_report_callback_deregister(NULL); - swiftnav_subsystem_status_report_callback_deregister(&node2); - - swiftnav_send_subsystem_status_report(0, 1, 2); - ck_assert_uint_eq(callback_tracker[0].invocations, 1); - ck_assert_uint_eq(callback_tracker[1].invocations, 0); - ck_assert_uint_eq(callback_tracker[2].invocations, 1); - - ck_assert_uint_eq(callback_tracker[0].component, 0); - ck_assert_uint_eq(callback_tracker[0].generic, 1); - ck_assert_uint_eq(callback_tracker[0].specific, 2); - ck_assert_ptr_eq(callback_tracker[0].context, (void *)0xff); - ck_assert_uint_eq(callback_tracker[0].counter, 0); - - ck_assert_uint_eq(callback_tracker[2].component, 0); - ck_assert_uint_eq(callback_tracker[2].generic, 1); - ck_assert_uint_eq(callback_tracker[2].specific, 2); - ck_assert_ptr_eq(callback_tracker[2].context, (void *)0xdd); - ck_assert_uint_eq(callback_tracker[2].counter, 1); -} -END_TEST - -Suite *status_report_suite(void) { - Suite *suite = suite_create("Status Report"); - - TCase *test_cases = tcase_create("Core"); - tcase_add_checked_fixture(test_cases, NULL, fixture_teardown); - tcase_add_test(test_cases, test_no_registered_callbacks); - tcase_add_test(test_cases, test_one_registered_callbacks); - tcase_add_test(test_cases, test_two_registered_callbacks); - tcase_add_test(test_cases, test_no_registered_callbacks_deregister); - tcase_add_test(test_cases, test_one_registered_callbacks_deregister); - tcase_add_test(test_cases, test_two_registered_callbacks_deregister_first); - tcase_add_test(test_cases, test_two_registered_callbacks_deregister_second); - tcase_add_test(test_cases, test_three_registered_callbacks_deregister_second); - suite_add_tcase(suite, test_cases); - - return suite; -} diff --git a/tests/check_suites.h b/tests/check_suites.h deleted file mode 100644 index 5502785..0000000 --- a/tests/check_suites.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CHECK_SUITES_H -#define CHECK_SUITES_H - -#ifdef __cplusplus -extern "C" { -#endif - -Suite* almanac_suite(void); -Suite* coord_system_suite(void); -Suite* bits_suite(void); -Suite* edc_suite(void); -Suite* linear_algebra_suite(void); -Suite* ephemeris_suite(void); -Suite* decode_glo_suite(void); -Suite* ionosphere_suite(void); -Suite* set_suite(void); -Suite* gnss_time_test_suite(void); -Suite* gnss_time_cpp_test_suite(void); -Suite* signal_test_suite(void); -Suite* geoid_model_test_suite(void); -Suite* glo_map_test_suite(void); -Suite* shm_suite(void); -Suite* troposphere_suite(void); -Suite* pvt_test_suite(void); -Suite* nav_meas_test_suite(void); -Suite* nav_meas_calc_test_suite(void); -Suite* sid_set_test_suite(void); -Suite* status_report_suite(void); -Suite* log_suite(void); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* CHECK_SUITES_H */ diff --git a/tests/check_troposphere.c b/tests/check_troposphere.c deleted file mode 100644 index 71911ed..0000000 --- a/tests/check_troposphere.c +++ /dev/null @@ -1,100 +0,0 @@ - -#include -#include -#include -#include - -#include "check_suites.h" - -START_TEST(test_calc_troposphere) { - const double d_tol = 1e-4; - - /* some tests against "true" values computed with UNB3M.f */ - /* http://www2.unb.ca/gge/Personnel/Santos/UNB_pack.pdf */ - - double lat = 40 * D2R; - double h = 1300.0; - double doy = 32.5; - double el = 45 * D2R; - double d_true = 2.8567; - - double d_tropo = calc_troposphere(doy, lat, h, el); - - fail_unless( - fabs(d_tropo - d_true) < d_tol, - "Distance didn't match hardcoded correct values %0.5f. Saw: %.5f\n", - d_true, - d_tropo); - - lat = -10 * D2R; - h = 0.0; - doy = 180.5; - el = 20 * D2R; - d_true = 7.4942; - - d_tropo = calc_troposphere(doy, lat, h, el); - - fail_unless( - fabs(d_tropo - d_true) < d_tol, - "Distance didn't match hardcoded correct values %0.5f. Saw: %.5f\n", - d_true, - d_tropo); - - lat = 75 * D2R; - h = 0.0; - doy = 50.5; - el = 10 * D2R; - d_true = 12.90073; - - d_tropo = calc_troposphere(doy, lat, h, el); - - fail_unless( - fabs(d_tropo - d_true) < d_tol, - "Distance didn't match hardcoded correct values %0.5f. Saw: %.5f\n", - d_true, - d_tropo); - - /* altitude sanity tests */ - double max_tropo_correction = 30.0; - h = -5000; - d_tropo = calc_troposphere(doy, lat, h, el); - - fail_unless(fabs(d_tropo) < max_tropo_correction, - "Sanity test fail at altitude %0.5f. : Correction was %.5f\n", - h, - d_tropo); - - h = 12000; - d_tropo = calc_troposphere(doy, lat, h, el); - - fail_unless(fabs(d_tropo) < max_tropo_correction, - "Sanity test fail at altitude %0.5f. : Correction was %.5f\n", - h, - d_tropo); - - /* satellite elevation sanity tests */ - h = 100; - double elevation_testcases[] = {1e-3, 1e-4, 1e-5, 0, -1e3, -0.1}; - max_tropo_correction = 100.0; - - for (u8 i = 0; i < sizeof(elevation_testcases) / sizeof(double); i++) { - el = elevation_testcases[i]; - d_tropo = calc_troposphere(doy, lat, h, el); - fail_unless(fabs(d_tropo) < max_tropo_correction, - "Sanity test fail at satellite elevation %0.5f. : Correction " - "was %.5f\n", - el, - d_tropo); - } -} -END_TEST - -Suite *troposphere_suite(void) { - Suite *s = suite_create("Troposphere"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_calc_troposphere); - suite_add_tcase(s, tc_core); - - return s; -} diff --git a/tests/check_utils.h b/tests/check_utils.h new file mode 100644 index 0000000..697ce92 --- /dev/null +++ b/tests/check_utils.h @@ -0,0 +1,32 @@ +#include + +#include +#include + +class Random final { + public: + Random() : engine_{}, dist_{0, std::numeric_limits::max()} {} + + double frand(double fmin, double fmax) { + size_t v = dist_(engine_); + double d = static_cast(v) / + static_cast(std::numeric_limits::max()); + return fmin + d * (fmax - fmin); + } + + template + void frand(double fmin, double fmax, double (&arr)[N]) { + for (auto &i : arr) { + i = frand(fmin, fmax); + } + } + + uint32_t sizerand(uint32_t sizemax) { + double f = frand(0.0, 1.0); + return static_cast(ceil(f * sizemax)); + } + + private: + std::random_device engine_; + std::uniform_int_distribution dist_; +}; diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt deleted file mode 100644 index d1dc844..0000000 --- a/tests/common/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -swift_add_test_library(check-utils SOURCES check_utils.c) -target_link_libraries(check-utils PUBLIC swiftnav::swiftnav) \ No newline at end of file diff --git a/tests/common/check_utils.c b/tests/common/check_utils.c deleted file mode 100644 index 11d74e6..0000000 --- a/tests/common/check_utils.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "check_utils.h" - -#include -#include -#include -#include -#include - -/*#define epsilon 0.0001*/ -#define EPSILON 1e-5 - -u8 within_epsilon(double a, double b) { - if (fabs(a - b) < EPSILON) { - return 1; - } - return 0; -} - -u8 arr_within_epsilon(u32 n, const double *a, const double *b) { - for (u32 i = 0; i < n; i++) { - if (!within_epsilon(a[i], b[i])) { - return false; - } - } - return true; -} - -void seed_rng(void) { srand((unsigned int)time(NULL)); } - -double frand(double fmin, double fmax) { - double f = (double)rand() / RAND_MAX; - return fmin + f * (fmax - fmin); -} - -void arr_frand(u32 n, double fmin, double fmax, double *v) { - for (u32 i = 0; i < n; i++) { - v[i] = frand(fmin, fmax); - } -} - -u32 sizerand(u32 sizemax) { - double f = (double)rand() / RAND_MAX; - return (u32)ceil(f * sizemax); -} diff --git a/tests/common/check_utils.h b/tests/common/check_utils.h deleted file mode 100644 index e9b72c0..0000000 --- a/tests/common/check_utils.h +++ /dev/null @@ -1,8 +0,0 @@ -#include - -u8 within_epsilon(double a, double b); -u8 arr_within_epsilon(u32 n, const double *a, const double *b); -void seed_rng(void); -double frand(double fmin, double fmax); -void arr_frand(u32 n, double fmin, double fmax, double *v); -u32 sizerand(u32 sizemax); diff --git a/tests/test_almanac.cc b/tests/test_almanac.cc new file mode 100644 index 0000000..7579425 --- /dev/null +++ b/tests/test_almanac.cc @@ -0,0 +1,203 @@ +#include +#include + +TEST(TestAlmanac, AlmanacEqual) { + almanac_t a; + almanac_t b; + + memset(&a, 0, sizeof(a)); + memset(&b, 0, sizeof(b)); + + EXPECT_TRUE(almanac_equal(&a, &b)) << "Almanacs should be equal"; + + a.valid = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) << "Almanacs should not be equal (valid)"; + memset(&a, 0, sizeof(a)); + + a.health_bits = 0x3f; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (health_bits)"; + memset(&a, 0, sizeof(a)); + + a.sid.sat = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (sid.sat)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = static_cast(1); + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (sid.band)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = static_cast(1); + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (sid.constellation)"; + memset(&a, 0, sizeof(a)); + + a.toa.wn = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (toa.wn)"; + memset(&a, 0, sizeof(a)); + + a.toa.tow = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (toa.tow)"; + memset(&a, 0, sizeof(a)); + + a.ura = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) << "Almanacs should not be equal (ura)"; + memset(&a, 0, sizeof(a)); + + a.fit_interval = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (fit_interval)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.m0 = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.m0)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.ecc = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.ecc)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.sqrta = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.sqrta)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.omega0 = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.omega0)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.omegadot = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.omegadot)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.w = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.w)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.inc = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.inc)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.af0 = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.af0)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GPS_L1CA; + a.data.kepler.af1 = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (kepler.af1)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.pos[0] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.pos[0])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.pos[1] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.pos[1])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.pos[2] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.pos[2])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.vel[0] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.vel[0])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.vel[1] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.vel[1])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.vel[2] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.vel[2])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.acc[0] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.acc[0])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.acc[1] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.acc[1])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_SBAS_L1CA; + a.data.xyz.acc[2] = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (xyz.acc[2])"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.lambda = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (glo.lambda)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.t_lambda = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (glo.t_lambda)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.i = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) << "Almanacs should not be equal (glo.i)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.t = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) << "Almanacs should not be equal (glo.t)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.t_dot = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (glo.t_dot)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.epsilon = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (glo.epsilon)"; + memset(&a, 0, sizeof(a)); + + a.sid.code = CODE_GLO_L1OF; + a.data.glo.omega = 1; + EXPECT_FALSE(almanac_equal(&a, &b)) + << "Almanacs should not be equal (glo.omega)"; + memset(&a, 0, sizeof(a)); +} diff --git a/tests/test_bits.cc b/tests/test_bits.cc new file mode 100644 index 0000000..7befe6b --- /dev/null +++ b/tests/test_bits.cc @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include + +#include "check_utils.h" + +namespace { + +TEST(TestBitUtils, Parity) { + EXPECT_EQ(parity(0x00000000), 0); + EXPECT_EQ(parity(0xFFFFFFFF), 0); + EXPECT_EQ(parity(0x01010101), 0); + EXPECT_EQ(parity(0x10101010), 0); + EXPECT_EQ(parity(0x10A010A0), 0); + + EXPECT_EQ(parity(0x10000000), 1); + EXPECT_EQ(parity(0x00000001), 1); + EXPECT_EQ(parity(0x70707000), 1); + EXPECT_EQ(parity(0x0B0B0B00), 1); + EXPECT_EQ(parity(0x00E00000), 1); +} + +TEST(TestBitUtils, Getbitu) { + u8 test_data[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + + u32 ret; + + ret = getbitu(test_data, 0, 8); + EXPECT_EQ(ret, 0x01); + + ret = getbitu(test_data, 4, 8); + EXPECT_EQ(ret, 0x12); + + ret = getbitu(test_data, 28, 16); + EXPECT_EQ(ret, 0x789A); + + ret = getbitu(test_data, 12, 32); + EXPECT_EQ(ret, 0x3456789A); + + ret = getbitu(test_data, 10, 3); + EXPECT_EQ(ret, 0x4); + + ret = getbitu(test_data, 10, 13); + EXPECT_EQ(ret, 0x11A2); +} + +TEST(TestBitUtils, Getbits) { + u8 test_data[] = {0x00, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF}; + + s32 ret; + + ret = getbits(test_data, 0, 8); + EXPECT_EQ(ret, 0); + + ret = getbits(test_data, 13, 3); + EXPECT_EQ(ret, 3); + + ret = getbits(test_data, 14, 3); + EXPECT_EQ(ret, -1); + + ret = getbits(test_data, 14, 4); + EXPECT_EQ(ret, -2); + + ret = getbits(test_data, 24, 32); + EXPECT_EQ(ret, -1); +} + +TEST(TestBitUtils, Setbitu) { + u8 test_data[10]; + u8 zeroes[10]; + + u32 ret; + unsigned seed = time(NULL); + + srand(seed); + + memset(zeroes, 0, sizeof(zeroes)); + memset(test_data, 0, sizeof(test_data)); + + for (unsigned len = 0; len <= 32; len++) { + for (unsigned pos = 0; pos < 48; pos++) { + u32 data = rand(); + + /* Set 'data' and check that we get the same value when we read it + * back */ + setbitu(test_data, pos, len, data); + /* Mask off bits higher than 'len' since they shouldn't be set when we + * read back */ + data &= (len == 32u ? ~0u : ((1u << len) - 1u)); + ret = getbitu(test_data, pos, len); + EXPECT_EQ(ret, data); + + /* Clear data and make sure that no additional bits have changed */ + setbitu(test_data, pos, len, 0); + EXPECT_EQ(memcmp(test_data, zeroes, sizeof(test_data)), 0) + << "test case 2 not completely zeroed"; + } + } +} + +TEST(TestBitUtils, Setbits) { + u8 test_data[10]; + + s32 ret; + + setbits(test_data, 14, 3, -1); + ret = getbits(test_data, 14, 3); + EXPECT_EQ(ret, -1); + + setbits(test_data, 14, 8, 22); + ret = getbits(test_data, 14, 8); + EXPECT_EQ(ret, 22); + + setbits(test_data, 24, 32, -1); + ret = getbits(test_data, 24, 32); + EXPECT_EQ(ret, -1); +} + +TEST(TestBitUtils, Setbitul) { + u8 test_data[64] = {0}; + u8 zeroes[64] = {0}; + + u64 ret = 0; + unsigned seed = time(NULL); + + srand(seed); + + for (unsigned len = 0; len <= sizeof(u64); len++) { + for (unsigned pos = 0; pos <= sizeof(u64); pos++) { + u64 data = (((u64)rand() << 32) | ((u64)rand())); + + /* Set 'data' and check that we get the same value when we read it + * back */ + setbitul(test_data, pos, len, data); + /* Mask off bits higher than 'len' since they shouldn't be set when we + * read back */ + data &= (len == 64 ? ~(u64)0 : (((u64)1 << len) - 1)); + ret = getbitul(test_data, pos, len); + EXPECT_EQ(ret, data); + + /* Clear data and make sure that no additional bits have changed */ + setbitul(test_data, pos, len, 0); + EXPECT_EQ(memcmp(test_data, zeroes, sizeof(test_data)), 0) + << "test case 2 not completely zeroed"; + } + } +} + +TEST(TestBitUtils, Setbitsl) { + u8 test_data[64] = {0}; + s64 ret = 0; + + s64 input = INT64_MIN; + setbitsl(test_data, 0, 64, input); + ret = getbitsl(test_data, 0, 64); + EXPECT_EQ(ret, input); + + ret = 0; + memset(test_data, 0, sizeof(test_data)); + input = 0xABCD; + setbitsl(test_data, 32, 8, input); + ret = getbitsl(test_data, 32, 8); + EXPECT_EQ(ret, (s8)input); + + // This test case should fail due to buffer overflow. setbitsl need fixing. + ret = 0; + memset(test_data, 0, sizeof(test_data)); + input = 0xABCD; + setbitsl(test_data, 56, 32, input); + ret = getbitsl(test_data, 56, 32); + EXPECT_EQ(ret, input); +} + +TEST(TestBitUtils, Bitshl) { + u8 src0[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res0[] = {0xBE, 0xEF, 0x00, 0x00}; + + u8 src1[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res1[] = {0xEA, 0xDB, 0xEE, 0xF0}; + + u8 src2[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res2[] = {0xDB, 0xEE, 0xF0, 0x00}; + + u8 src3[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res3[] = {0xB6, 0xFB, 0xBC, 0x00}; + + bitshl(src0, sizeof(src0), 16); + EXPECT_EQ(0, memcmp(src0, res0, 4)); + + bitshl(src1, sizeof(src1), 4); + EXPECT_EQ(0, memcmp(src1, res1, 4)); + + bitshl(src2, sizeof(src2), 12); + EXPECT_EQ(0, memcmp(src2, res2, 4)); + + bitshl(src3, sizeof(src3), 10); + EXPECT_EQ(0, memcmp(src3, res3, 4)); +} + +TEST(TestBitUtils, Bitcopy) { + u8 src0[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res0[] = {0xBE, 0xEF, 0xBE, 0xEF}; + + u8 src1[] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8 res1[] = {0xEA, 0xDB, 0xEE, 0xFF}; + + u8 src2[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; + // u8 dst2[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; + u8 res2[] = {0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xAD}; + + bitcopy(src0, 0, src0, 16, 16); + EXPECT_EQ(0, memcmp(src0, res0, 4)); + + bitcopy(src1, 0, src1, 4, 28); + EXPECT_EQ(0, memcmp(src1, res1, 4)); + + bitcopy(src2, 0, src2, 8, 72); + EXPECT_EQ(0, memcmp(src2, res2, 4)); +} + +TEST(TestBitUtils, CountBitsX) { + u8 src8[] = {0xDE, 0xAD, 0x12, 0xEF}; + u8 res8[] = {6, 5, 2, 7}; + + u16 src16[] = {0xDE05, 0xADF6, 0xBE32, 0xEF45}; + u8 res16[] = {8, 11, 9, 10}; + + u32 src32[] = {0xDE051234, 0x00000000, 0x00329300, 0x1F45A6C8}; + u8 res32[] = {13, 0, 7, 15}; + + u64 src64[] = {0xDE051234432150ED, + 0x0000000080000000, + 0x0032930000392300, + 0x10F14350A060C080}; + u8 res64[] = {26, 1, 14, 18}; + + for (unsigned i = 0; i < sizeof(src8) / sizeof(src8[0]); i++) { + u8 r = count_bits_u8(src8[i], 1); + EXPECT_EQ(res8[i], r); + r = count_bits_u8(src8[i], 0); + EXPECT_EQ(8 - res8[i], r); + } + + for (unsigned i = 0; i < sizeof(src16) / sizeof(src16[0]); i++) { + u8 r = count_bits_u16(src16[i], 1); + EXPECT_EQ(res16[i], r); + r = count_bits_u16(src16[i], 0); + EXPECT_EQ(16 - res16[i], r); + } + + for (unsigned i = 0; i < sizeof(src32) / sizeof(src32[0]); i++) { + u8 r = count_bits_u32(src32[i], 1); + EXPECT_EQ(res32[i], r); + r = count_bits_u32(src32[i], 0); + EXPECT_EQ(32 - res32[i], r); + } + + for (unsigned i = 0; i < sizeof(src64) / sizeof(src64[0]); i++) { + u8 r = count_bits_u64(src64[i], 1); + EXPECT_EQ(res64[i], r); + r = count_bits_u64(src64[i], 0); + EXPECT_EQ(64 - res64[i], r); + } +} + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbitfield-constant-conversion" +#endif + +TEST(TestBitUtils, SignExtend32) { + EXPECT_EQ(BITS_SIGN_EXTEND_32(5, 0), 0); + EXPECT_EQ(BITS_SIGN_EXTEND_32(5, 1), 1); + EXPECT_EQ(BITS_SIGN_EXTEND_32(5, 15), 15); + EXPECT_EQ(BITS_SIGN_EXTEND_32(5, 0x1F), -1); + EXPECT_EQ(BITS_SIGN_EXTEND_32(5, 0x10), -16); +} + +TEST(TestBitUtils, SignExtend64) { + EXPECT_EQ(BITS_SIGN_EXTEND_64(33, 0), 0); + EXPECT_EQ(BITS_SIGN_EXTEND_64(33, 1), 1); + EXPECT_EQ(BITS_SIGN_EXTEND_64(33, INT64_C(0xFFFFFFFF)), INT64_C(0xFFFFFFFF)); + EXPECT_EQ(BITS_SIGN_EXTEND_64(33, INT64_C(0x1FFFFFFFF)), -INT64_C(0x1)); + EXPECT_EQ(BITS_SIGN_EXTEND_64(33, INT64_C(0x100000000)), + -INT64_C(0x100000000)); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +TEST(TestBitUtils, Endianess) { +#ifdef __BYTE_ORDER__ // Available on gcc and clang, proabbly not other + // compilers +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + enum endianess expected_endianess = SWIFT_LITTLE_ENDIAN; + u16 u16_values[] = {0x1234, 0x3412, 0x3412}; + u32 u32_values[] = {0x12345678, 0x78563412, 0x78563412}; + u64 u64_values[] = { + 0x123456789abcdef0, 0xf0debc9a78563412, 0xf0debc9a78563412}; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + enum endianess expected_endianess = SWIFT_BIG_ENDIAN; + u16 u16_values[] = {0x1234, 0x3412, 0x1234}; + u32 u32_values[] = {0x12345678, 0x78563412, 0x12345678}; + u64 u64_values[] = { + 0x123456789abcdef0, 0xf0debc9a78563412, 0x123456789abcdef0}; +#else +#error "__BYTE_ORDER__ not set properly by compiler" +#endif + EXPECT_EQ(get_endianess(), expected_endianess); + // Order of elements in value array is big, little, host + EXPECT_EQ(byte_swap_16(u16_values[0]), u16_values[1]); + EXPECT_EQ(byte_swap_16(u16_values[1]), u16_values[0]); + EXPECT_EQ(betoh_16(u16_values[0]), u16_values[2]); + EXPECT_EQ(letoh_16(u16_values[1]), u16_values[2]); + EXPECT_EQ(htobe_16(u16_values[2]), u16_values[0]); + EXPECT_EQ(htole_16(u16_values[2]), u16_values[1]); + + EXPECT_EQ(byte_swap_32(u32_values[0]), u32_values[1]); + EXPECT_EQ(byte_swap_32(u32_values[1]), u32_values[0]); + EXPECT_EQ(betoh_32(u32_values[0]), u32_values[2]); + EXPECT_EQ(letoh_32(u32_values[1]), u32_values[2]); + EXPECT_EQ(htobe_32(u32_values[2]), u32_values[0]); + EXPECT_EQ(htole_32(u32_values[2]), u32_values[1]); + + EXPECT_EQ(byte_swap_64(u64_values[0]), u64_values[1]); + EXPECT_EQ(byte_swap_64(u64_values[1]), u64_values[0]); + EXPECT_EQ(betoh_64(u64_values[0]), u64_values[2]); + EXPECT_EQ(letoh_64(u64_values[1]), u64_values[2]); + EXPECT_EQ(htobe_64(u64_values[2]), u64_values[0]); + EXPECT_EQ(htole_64(u64_values[2]), u64_values[1]); +#else +// No compiler indication of endianess, tests disabled +#endif +} + +} // namespace diff --git a/tests/test_coord_system.cc b/tests/test_coord_system.cc new file mode 100644 index 0000000..6167847 --- /dev/null +++ b/tests/test_coord_system.cc @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include + +#include "check_utils.h" + +namespace { + +/* Maximum allowable error in quantities with units of length (in meters). */ +#define MAX_DIST_ERROR_M 1e-6 +/* Maximum allowable error in quantities with units of angle (in sec of arc). + * 1 second of arc on the equator is ~31 meters. */ +#define MAX_ANGLE_ERROR_SEC 1e-7 +#define MAX_ANGLE_ERROR_RAD (MAX_ANGLE_ERROR_SEC * (D2R / 3600.0)) + +/* Semi-major axis. */ +#define EARTH_A 6378137.0 +/* Semi-minor axis. */ +#define EARTH_B 6356752.31424517929553985595703125 + +#define NUM_COORDS 10 +const double llhs[NUM_COORDS][3] = { + {0, 0, 0}, /* On the Equator and Prime Meridian. */ + {0, 180 * D2R, 0}, /* On the Equator. */ + {0, 90 * D2R, 0}, /* On the Equator. */ + {0, -90 * D2R, 0}, /* On the Equator. */ + {90 * D2R, 0, 0}, /* North pole. */ + {-90 * D2R, 0, 0}, /* South pole. */ + {90 * D2R, 0, 22}, /* 22m above the north pole. */ + {-90 * D2R, 0, 22}, /* 22m above the south pole. */ + {0, 0, 22}, /* 22m above the Equator and Prime Meridian. */ + {0, 180 * D2R, 22}, /* 22m above the Equator. */ +}; +const double ecefs[NUM_COORDS][3] = { + {EARTH_A, 0, 0}, + {-EARTH_A, 0, 0}, + {0, EARTH_A, 0}, + {0, -EARTH_A, 0}, + {0, 0, EARTH_B}, + {0, 0, -EARTH_B}, + {0, 0, (EARTH_B + 22)}, + {0, 0, -(EARTH_B + 22)}, + {(22 + EARTH_A), 0, 0}, + {-(22 + EARTH_A), 0, 0}, +}; + +TEST(TestCoordSystem, llhdeg2rad) { + double rads[3]; + + llhdeg2rad(llhs[0], rads); + + // We expect the zero-point to be the same in degrees and radians + for (int n = 0; n < 3; n++) { + EXPECT_EQ(rads[n], 0); + } + + // We expect an arbitrary point to convert correctly + double swiftHomeLLH[3] = {37.779804, -122.391751, 60.0}; + llhdeg2rad(swiftHomeLLH, rads); + + EXPECT_NEAR(rads[0], 0.659381970558, MAX_ANGLE_ERROR_RAD); + EXPECT_LT(fabs(rads[1] + 2.136139032231), MAX_ANGLE_ERROR_RAD); + EXPECT_EQ(rads[2], swiftHomeLLH[2]); +} + +class TestCoordSystemCore : public ::testing::TestWithParam {}; + +TEST_P(TestCoordSystemCore, wgsllh2ecef) { + double ecef[3]; + + wgsllh2ecef(llhs[GetParam()], ecef); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(ecef[n])) << "NaN in output from wgsllh2ecef."; + double err = fabs(ecef[n] - ecefs[GetParam()][n]); + EXPECT_LT(err, MAX_DIST_ERROR_M) + << "Conversion from WGS84 LLH to ECEF has >1e-6m error:\n" + "LLH: %f, %f, %f\n" + "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g" + << llhs[GetParam()][0] * R2D << llhs[GetParam()][1] * R2D + << llhs[GetParam()][2] << (ecef[0] - ecefs[GetParam()][0]) * 1e3 + << (ecef[1] - ecefs[GetParam()][1]) * 1e3 + << (ecef[2] - ecefs[GetParam()][2]) * 1e3; + } +} + +TEST_P(TestCoordSystemCore, wgsecef2llh) { + double llh[3]; + + wgsecef2llh(ecefs[GetParam()], llh); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(llh[n])) << "NaN in output from wgsecef2llh."; + } + + double lat_err = fabs(llh[0] - llhs[GetParam()][0]); + double lon_err = fabs(llh[1] - llhs[GetParam()][1]); + double hgt_err = fabs(llh[2] - llhs[GetParam()][2]); + EXPECT_TRUE((lat_err < MAX_ANGLE_ERROR_RAD) && + (lon_err < MAX_ANGLE_ERROR_RAD) && (hgt_err < MAX_DIST_ERROR_M)) + << "Conversion from WGS84 ECEF to LLH has >1e-6 {rad, m} error:\n" + "ECEF: %f, %f, %f\n" + "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g" + << ecefs[GetParam()][0] << ecefs[GetParam()][1] << ecefs[GetParam()][2] + << (llh[0] - llhs[GetParam()][0]) * (R2D * 3600) + << (llh[1] - llhs[GetParam()][1]) * (R2D * 3600) + << (llh[2] - llhs[GetParam()][2]) * 1e3; +} + +TEST_P(TestCoordSystemCore, wgsllh2ecef2llh) { + double ecef[3]; + double llh[3]; + + wgsllh2ecef(llhs[GetParam()], ecef); + wgsecef2llh(ecef, llh); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(llh[n])) << "NaN in LLH output from conversion."; + EXPECT_FALSE(isnan(ecef[n])) << "NaN in ECEF output from conversion."; + } + + double lat_err = fabs(llh[0] - llhs[GetParam()][0]); + double lon_err = fabs(llh[1] - llhs[GetParam()][1]); + double hgt_err = fabs(llh[2] - llhs[GetParam()][2]); + EXPECT_TRUE((lat_err < MAX_ANGLE_ERROR_RAD) && + (lon_err < MAX_ANGLE_ERROR_RAD) && (hgt_err < MAX_DIST_ERROR_M)) + << "Converting WGS84 LLH to ECEF and back again does not return the " + "original values.\n" + "Initial LLH: " + << R2D * llhs[GetParam()][0] << " " << R2D * llhs[GetParam()][1] << " " + << llhs[GetParam()][2] << " \n" + << "ECEF: " << ecef[0] << " " << ecef[1] << " " << ecef[2] << " \n" + << "Final LLH: " << R2D * llh[0] << " " << R2D * llh[1] << " " << llh[2] + << " \n" + << "Lat error (arc sec): " + << (llh[0] - llhs[GetParam()][0]) * (R2D * 3600) << "\n" + << "Lon error (arc sec): " + << (llh[1] - llhs[GetParam()][1]) * (R2D * 3600) << "\n" + << "H error (mm): " << (llh[2] - llhs[GetParam()][2]) * 1e3; +} + +TEST_P(TestCoordSystemCore, wgsecef2llh2ecef) { + double llh[3]; + double ecef[3]; + + wgsecef2llh(ecefs[GetParam()], llh); + wgsllh2ecef(llh, ecef); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(llh[n])) << "NaN in LLH output from conversion."; + EXPECT_FALSE(isnan(ecef[n])) << "NaN in ECEF output from conversion."; + } + + for (int n = 0; n < 3; n++) { + double err = fabs(ecef[n] - ecefs[GetParam()][n]); + EXPECT_LE(err, MAX_DIST_ERROR_M) + << "Converting WGS84 ECEF to LLH and back again does not return the " + "original values.\n" + "Initial ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + "Final ECEF: %f, %f, %f\n" + "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g" + << ecefs[GetParam()][0] << ecefs[GetParam()][1] << ecefs[GetParam()][2] + << R2D * llh[0] << R2D * llh[1] << llh[2] << ecef[0] << ecef[1] + << ecef[2] << (ecef[0] - ecefs[GetParam()][0]) * 1e3 + << (ecef[1] - ecefs[GetParam()][1]) * 1e3 + << (ecef[2] - ecefs[GetParam()][2]) * 1e3; + } +} + +INSTANTIATE_TEST_SUITE_P(TestCoordSystemCore, + TestCoordSystemCore, + ::testing::Range(size_t(0), size_t(NUM_COORDS))); +class TestCoordSystemRandom : public ::testing::TestWithParam {}; + +TEST_P(TestCoordSystemRandom, RandomWgsllh2ecef2llh) { + double ecef[3]; + double llh_init[3]; + double llh[3]; + + Random rand{}; + + llh_init[0] = D2R * rand.frand(-90, 90); + llh_init[1] = D2R * rand.frand(-180, 180); + llh_init[2] = rand.frand(-0.5 * EARTH_A, 4 * EARTH_A); + + wgsllh2ecef(llh_init, ecef); + wgsecef2llh(ecef, llh); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(llh[n])) << "NaN in LLH output from conversion."; + EXPECT_FALSE(isnan(ecef[n])) << "NaN in ECEF output from conversion."; + } + + double lat_err = fabs(llh[0] - llh_init[0]); + double lon_err = fabs(llh[1] - llh_init[1]); + double hgt_err = fabs(llh[2] - llh_init[2]); + EXPECT_TRUE((lat_err < MAX_ANGLE_ERROR_RAD) && + (lon_err < MAX_ANGLE_ERROR_RAD) && (hgt_err < MAX_DIST_ERROR_M)) + << "Converting random WGS84 LLH to ECEF and back again does not return " + "the " + "original values.\n" + "Initial LLH: %f, %f, %f\n" + "ECEF: %f, %f, %f\n" + "Final LLH: %f, %f, %f\n" + "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g" + << R2D * llh_init[0] << R2D * llh_init[1] << llh_init[2] << ecef[0] + << ecef[1] << ecef[2] << R2D * llh[0] << R2D * llh[1] << llh[2] + << (llh[0] - llh_init[0]) * (R2D * 3600) + << (llh[1] - llh_init[1]) * (R2D * 3600) << (llh[2] - llh_init[2]) * 1e3; + + EXPECT_TRUE((R2D * llh[0] >= -90) && (R2D * llh[0] <= 90)) + << "Converting random WGS84 ECEF gives latitude out of bounds.\n" + "ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef[0] << ecef[1] << ecef[2] << R2D * llh[0] << R2D * llh[1] + << llh[2]; + + EXPECT_TRUE((R2D * llh[1] >= -180) && (R2D * llh[1] <= 180)) + << "Converting random WGS84 ECEF gives longitude out of bounds.\n" + "ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef[0] << ecef[1] << ecef[2] << R2D * llh[0] << R2D * llh[1] + << llh[2]; + + EXPECT_GT(llh[2], -EARTH_A) + << "Converting random WGS84 ECEF gives height out of bounds.\n" + "ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef[0] << ecef[1] << ecef[2] << R2D * llh[0] << R2D * llh[1] + << llh[2]; +} + +TEST_P(TestCoordSystemRandom, RandomWgsecef2llh2ecef) { + double ecef_init[3]; + double llh[3]; + double ecef[3]; + + Random rand{}; + + ecef_init[0] = rand.frand(-4 * EARTH_A, 4 * EARTH_A); + ecef_init[1] = rand.frand(-4 * EARTH_A, 4 * EARTH_A); + ecef_init[2] = rand.frand(-4 * EARTH_A, 4 * EARTH_A); + + wgsecef2llh(ecef_init, llh); + wgsllh2ecef(llh, ecef); + + for (int n = 0; n < 3; n++) { + EXPECT_FALSE(isnan(llh[n])) << "NaN in LLH output from conversion."; + EXPECT_FALSE(isnan(ecef[n])) << "NaN in ECEF output from conversion."; + } + + for (int n = 0; n < 3; n++) { + double err = fabs(ecef[n] - ecef_init[n]); + EXPECT_LT(err, MAX_DIST_ERROR_M) + << "Converting random WGS84 ECEF to LLH and back again does not " + "return the " + "original values.\n" + "Initial ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + "Final ECEF: %f, %f, %f\n" + "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g" + << ecef_init[0] << ecef_init[1] << ecef_init[2] << R2D * llh[0] + << R2D * llh[1] << llh[2] << ecef[0] << ecef[1] << ecef[2] + << (ecef[0] - ecef_init[0]) * 1e3 << (ecef[1] - ecef_init[1]) * 1e3 + << (ecef[2] - ecef_init[2]) * 1e3; + } + + EXPECT_TRUE((R2D * llh[0] >= -90) && (R2D * llh[0] <= 90)) + << "Converting random WGS84 ECEF gives latitude out of bounds.\n" + "Initial ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef_init[0] << ecef_init[1] << ecef_init[2] << R2D * llh[0] + << R2D * llh[1] << llh[2]; + + EXPECT_TRUE((R2D * llh[1] >= -180) && (R2D * llh[1] <= 180)) + << "Converting random WGS84 ECEF gives longitude out of bounds.\n" + "Initial ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef_init[0] << ecef_init[1] << ecef_init[2] << R2D * llh[0] + << R2D * llh[1] << llh[2]; + + EXPECT_GT(llh[2], -EARTH_A) + << "Converting random WGS84 ECEF gives height out of bounds.\n" + "Initial ECEF: %f, %f, %f\n" + "LLH: %f, %f, %f\n" + << ecef_init[0] << ecef_init[1] << ecef_init[2] << R2D * llh[0] + << R2D * llh[1] << llh[2]; +} + +/* Check simply that passing the ECEF position the same as the + * reference position returns (0, 0, 0) in NED frame */ +TEST_P(TestCoordSystemRandom, RandomWgsecef2nedD0) { + s32 i, j; + double ned[3]; + + Random rand{}; + for (i = 0; i < 222; i++) { + const double ecef[3] = { + rand.frand(-1e8, 1e8), rand.frand(-1e8, 1e8), rand.frand(-1e8, 1e8)}; + wgsecef2ned_d(ecef, ecef, ned); + for (j = 0; j < 3; j++) + EXPECT_LT(fabs(ned[j]), 1e-8) + << "NED vector to reference ECEF point " + "has nonzero element %d: %lf\n" + "(point was <%lf %lf %lf>)\n" + << j << ned[j] << ecef[0] << ecef[1] << ecef[2]; + } +} + +INSTANTIATE_TEST_SUITE_P(TestCoordSystemRandom, + TestCoordSystemRandom, + ::testing::Range(size_t(0), size_t(22))); + +} // namespace diff --git a/tests/test_correct_iono_tropo.cc b/tests/test_correct_iono_tropo.cc new file mode 100644 index 0000000..8948c7c --- /dev/null +++ b/tests/test_correct_iono_tropo.cc @@ -0,0 +1,153 @@ +#include +#include +#include + +#include "test_data.h" + +namespace { + +using namespace test_data; + +static void run_calc_pvt(const u8 n_used, + const navigation_measurement_t nms[9], + gnss_solution *soln) { + dops_t dops; + obs_mask_config_t obs_mask_config = {{false, 25}}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + true, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + soln, + &dops, + nullptr); + EXPECT_EQ(code, 2) << "Return code should be 2 (raim not used). Saw: " + << code; + EXPECT_EQ(soln->valid, 1) << "Solution should be valid!"; +} + +TEST(TestCorrectIonoTropo, TestCorrectIono) { + u8 n_used = 9; + gnss_solution soln1; + gnss_solution soln2; + + /* compute PVT with no iono correction */ + + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; + + run_calc_pvt(n_used, nms, &soln1); + + EXPECT_NEAR(soln1.pos_ecef[0], -2715898.028, 0.001); + EXPECT_NEAR(soln1.pos_ecef[1], -4266139.598, 0.001); + EXPECT_NEAR(soln1.pos_ecef[2], 3891352.859, 0.001); + + /* compute PVT with iono correction */ + + navigation_measurement_t nms_iono[9]; + memcpy(nms_iono, nms, sizeof(nms)); + correct_iono(soln1.pos_ecef, &DEFAULT_IONO_PARAMS, n_used, nms_iono); + run_calc_pvt(n_used, nms_iono, &soln2); + + EXPECT_NEAR(soln2.pos_ecef[0], -2715896.609, 0.001); + EXPECT_NEAR(soln2.pos_ecef[1], -4266137.564, 0.001); + EXPECT_NEAR(soln2.pos_ecef[2], 3891350.932, 0.001); + + /* check position delta */ + + double delta_x = soln1.pos_ecef[0] - soln2.pos_ecef[0]; + double delta_y = soln1.pos_ecef[1] - soln2.pos_ecef[1]; + double delta_z = soln1.pos_ecef[2] - soln2.pos_ecef[2]; + + double pos_shift = + sqrt(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z); + EXPECT_NEAR(pos_shift, 3.141, 0.001); + + /* check PR/CP deltas */ + + static const double expected_pr_deltas[9] = {-1.797691397369, + -1.521543189883, + -2.111414518207, + -2.330238319933, + -3.942552670836, + -1.669149085879, + -4.086640629917, + -3.286862179637, + -2.850360091776}; + + for (int i = 0; i < n_used; i++) { + double pr_delta = nms_iono[i].raw_pseudorange - nms[i].raw_pseudorange; + EXPECT_NEAR(pr_delta, expected_pr_deltas[i], 0.001); + + double cp_delta = nms_iono[i].raw_carrier_phase - nms[i].raw_carrier_phase; + EXPECT_NEAR(cp_delta, + expected_pr_deltas[i] * (sid_to_carr_freq(nms[i].sid) / GPS_C), + 0.001); + } +} + +TEST(TestCorrectIonoTropo, TestCorrectTropo) { + u8 n_used = 9; + gnss_solution soln1; + gnss_solution soln2; + + /* compute PVT with no tropo correction */ + + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; + + run_calc_pvt(n_used, nms, &soln1); + + EXPECT_NEAR(soln1.pos_ecef[0], -2715898.028, 0.001); + EXPECT_NEAR(soln1.pos_ecef[1], -4266139.598, 0.001); + EXPECT_NEAR(soln1.pos_ecef[2], 3891352.859, 0.001); + + /* compute PVT with tropo correction */ + + navigation_measurement_t nms_tropo[9]; + memcpy(nms_tropo, nms, sizeof(nms)); + + correct_tropo(soln1.pos_ecef, n_used, nms_tropo); + run_calc_pvt(n_used, nms_tropo, &soln2); + + EXPECT_NEAR(soln2.pos_ecef[0], -2715896.697, 0.001); + EXPECT_NEAR(soln2.pos_ecef[1], -4266137.874, 0.001); + EXPECT_NEAR(soln2.pos_ecef[2], 3891351.398, 0.001); + + /* check position delta */ + + double delta_x = soln1.pos_ecef[0] - soln2.pos_ecef[0]; + double delta_y = soln1.pos_ecef[1] - soln2.pos_ecef[1]; + double delta_z = soln1.pos_ecef[2] - soln2.pos_ecef[2]; + + double pos_shift = + sqrt(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z); + EXPECT_NEAR(pos_shift, 2.623, 0.001); + + /* check PR/CP deltas */ + + static const double expected_pr_deltas[9] = {-0.643832463771, + -0.531037796289, + -0.768872588873, + -0.866686545312, + -2.582971405238, + -0.594299942255, + -2.985528819263, + -1.532460253686, + -1.163043931127}; + + for (int i = 0; i < n_used; i++) { + double pr_delta = nms_tropo[i].raw_pseudorange - nms[i].raw_pseudorange; + EXPECT_NEAR(pr_delta, expected_pr_deltas[i], 0.001); + + double cp_delta = nms_tropo[i].raw_carrier_phase - nms[i].raw_carrier_phase; + EXPECT_NEAR(cp_delta, + -expected_pr_deltas[i] * (sid_to_carr_freq(nms[i].sid) / GPS_C), + 0.001); + } +} + +} // namespace diff --git a/tests/test_data.h b/tests/test_data.h new file mode 100644 index 0000000..47f80d0 --- /dev/null +++ b/tests/test_data.h @@ -0,0 +1,184 @@ +#ifndef TEST_DATA_H +#define TEST_DATA_H + +#include +#include + +namespace test_data { + +constexpr uint16_t cTorWn = 1939; +constexpr double cTorTow = 42.0; + +/* time of reception for the tests */ +constexpr gps_time_t tor = {.tow = cTorTow, .wn = cTorWn}; + +constexpr navigation_measurement_t nm1 = { + .raw_pseudorange = 23946993.888943646, + .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, + .cn0 = 41.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 9, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm1_no_doppler = { + .raw_pseudorange = 23946993.888943646, + .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, + .cn0 = 39.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 9, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID}; + +constexpr navigation_measurement_t nm2 = { + .raw_pseudorange = 22932174.156858064, + .sat_pos = {-9680013.5408340245, -15286326.354385279, 19429449.383770257}, + .cn0 = 43.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm3 = { + .raw_pseudorange = 24373231.648055989, + .sat_pos = {-19858593.085281931, -3109845.8288993631, 17180320.439503901}, + .cn0 = 35.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 2, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm4 = { + .raw_pseudorange = 24779663.252316438, + .sat_pos = {6682497.8716542246, -14006962.389166718, 21410456.275678463}, + .cn0 = 27.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 3, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm5 = { + .raw_pseudorange = 26948717.022331879, + .sat_pos = {7415370.9916331079, -24974079.044485383, -3836019.0262199985}, + .cn0 = 39.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 4, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm6 = { + .raw_pseudorange = 23327405.435463827, + .sat_pos = {-2833466.1648670658, -22755197.793894723, 13160322.082875408}, + .cn0 = 38.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 5, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +/* doppler measurement broken */ +constexpr navigation_measurement_t nm6b = { + .raw_pseudorange = 23327405.435463827, + .raw_measured_doppler = 10000, /* Doppler outlier */ + .sat_pos = {-2833466.1648670658, -22755197.793894723, 13160322.082875408}, + .sat_vel = {0, 0, 0}, + .cn0 = 40, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 5, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID | NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm7 = { + .raw_pseudorange = 27371419.016328193, + .sat_pos = {14881660.383624561, -5825253.4316490609, 21204679.68313824}, + .cn0 = 42.3, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 6, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm8 = { + .raw_pseudorange = 26294221.697782904, + .sat_pos = {12246530.477279386, -22184711.955107089, 7739084.2855069181}, + .cn0 = 45.1, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 7, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm9 = { + .raw_pseudorange = 25781999.479948733, + .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, + .cn0 = 37.3, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 8, .code = CODE_GPS_L1CA}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm10 = { + .raw_pseudorange = 25781999.479948733, + .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, + .cn0 = 41.0, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 8, .code = CODE_GPS_L2CM}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +/* broken measurement */ +constexpr navigation_measurement_t nm10b = { + .raw_pseudorange = 25781999.479948733 + 30000, + .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, + .cn0 = 39.2, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 8, .code = CODE_GPS_L2CM}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +constexpr navigation_measurement_t nm11 = { + .raw_pseudorange = 25781999.479948733, + .sat_pos = {-25360766.249484103, -1659033.490658124, 7821492.0398916304}, + .cn0 = 45.4, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 11, .code = CODE_GPS_L2CM}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +// Note this is a copy of GPS nm1 but set to code GAL_E1B, do not combine +// them in the same test case +constexpr navigation_measurement_t gal_nm1 = { + .raw_pseudorange = 23946993.888943646, + .sat_pos = {-19477278.087422125, -7649508.9457812719, 16674633.163554827}, + .cn0 = 41.1, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 9, .code = CODE_GAL_E1B}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +// Note this is a copy of GPS nm2 but set to code GAL_E1B, do not combine +// them in the same test case +constexpr navigation_measurement_t gal_nm2 = { + .raw_pseudorange = 22932174.156858064, + .sat_pos = {-9680013.5408340245, -15286326.354385279, 19429449.383770257}, + .cn0 = 39.7, + .lock_time = 5, + .tot = {.tow = cTorTow - 0.077, .wn = cTorWn}, + .sid = {.sat = 1, .code = CODE_GAL_E1B}, + .flags = NAV_MEAS_FLAG_CODE_VALID | NAV_MEAS_FLAG_MEAS_DOPPLER_VALID | + NAV_MEAS_FLAG_PHASE_VALID}; + +} // namespace test_data + +#endif diff --git a/tests/check_decode_glo.c b/tests/test_decode_glo.cc similarity index 68% rename from tests/check_decode_glo.c rename to tests/test_decode_glo.cc index 312a8a9..323da81 100644 --- a/tests/check_decode_glo.c +++ b/tests/test_decode_glo.cc @@ -1,10 +1,10 @@ -#include +#include #include #include #include #include -#include "check_suites.h" +namespace { #define LOW_TOL 1e-6 #define HIGH_TOL 1e-1 @@ -78,53 +78,20 @@ void e_out(void) { eph.data.glo.acc[0], eph.data.glo.acc[1], eph.data.glo.acc[2]); - fail_unless(fabs(eph.data.glo.pos[0] - X) < LOW_TOL, - "glo.pos[0] %.1f vs %.1f", - eph.data.glo.pos[0], - X); - fail_unless(fabs(eph.data.glo.pos[1] - Y) < LOW_TOL, - "glo.pos[1] %.1f vs %.1f", - eph.data.glo.pos[1], - Y); - fail_unless(fabs(eph.data.glo.pos[2] - Z) < LOW_TOL, - "glo.pos[1] %.1f vs %.1f", - eph.data.glo.pos[2], - Z); - fail_unless(fabs(eph.data.glo.vel[0] - VX) < LOW_TOL, - "glo.vel[0] %.1f vs %.1f", - eph.data.glo.vel[0], - VX); - fail_unless(fabs(eph.data.glo.vel[1] - VY) < LOW_TOL, - "glo.vel[1] %.1f vs %.1f", - eph.data.glo.vel[1], - VY); - fail_unless(fabs(eph.data.glo.vel[2] - VZ) < LOW_TOL, - "glo.vel[1] %.1f vs %.1f", - eph.data.glo.vel[2], - VZ); - fail_unless(fabs(eph.data.glo.acc[0] - AX) < LOW_TOL, - "glo.acc[0] %.1f vs %.1f", - eph.data.glo.acc[0], - AX); - fail_unless(fabs(eph.data.glo.acc[1] - AY) < LOW_TOL, - "glo.acc[1] %.1f vs %.1f", - eph.data.glo.acc[1], - AY); - fail_unless(fabs(eph.data.glo.acc[2] - AZ) < LOW_TOL, - "glo.acc[1] %.1f vs %.1f", - eph.data.glo.acc[02], - AZ); - fail_unless(fabs(eph.data.glo.tau - TAU) < LOW_TOL, - "glo.tau %.1f vs %.1f", - eph.data.glo.tau, - TAU); - fail_unless(fabs(eph.data.glo.gamma - GAMMA) < LOW_TOL, - "glo.gamma %.1f vs %.1f", - eph.data.glo.gamma, - GAMMA); + EXPECT_NEAR(eph.data.glo.pos[0], X, LOW_TOL); + EXPECT_NEAR(eph.data.glo.pos[1], Y, LOW_TOL); + EXPECT_NEAR(eph.data.glo.pos[2], Z, LOW_TOL); + EXPECT_NEAR(eph.data.glo.vel[0], VX, LOW_TOL); + EXPECT_NEAR(eph.data.glo.vel[1], VY, LOW_TOL); + EXPECT_NEAR(eph.data.glo.vel[2], VZ, LOW_TOL); + EXPECT_NEAR(eph.data.glo.acc[0], AX, LOW_TOL); + EXPECT_NEAR(eph.data.glo.acc[1], AY, LOW_TOL); + EXPECT_NEAR(eph.data.glo.acc[2], AZ, LOW_TOL); + EXPECT_NEAR(eph.data.glo.tau, TAU, LOW_TOL); + EXPECT_NEAR(eph.data.glo.gamma, GAMMA, LOW_TOL); } -START_TEST(test_extract_glo_word) { +TEST(TestDecodeGlonass, ExtractGloWord) { u32 ret = 0; glo_string_t string; @@ -133,38 +100,36 @@ START_TEST(test_extract_glo_word) { string.word[1] = 5; string.word[2] = 5; ret = extract_word_glo(&string, 1, 32); - fail_unless(ret == 5); + EXPECT_EQ(ret, 5); ret = extract_word_glo(&string, 33, 3); - fail_unless(ret == 5); + EXPECT_EQ(ret, 5); ret = extract_word_glo(&string, 65, 3); - fail_unless(ret == 5); + EXPECT_EQ(ret, 5); string.word[0] = 0x12345678; string.word[1] = 0xdeadbeef; string.word[2] = 0x87654321; ret = extract_word_glo(&string, 1, 32); - fail_unless(ret == 0x12345678); + EXPECT_EQ(ret, 0x12345678); ret = extract_word_glo(&string, 33, 32); - fail_unless(ret == 0xdeadbeef); + EXPECT_EQ(ret, 0xdeadbeef); ret = extract_word_glo(&string, 65, 32); - fail_unless(ret == 0x87654321); + EXPECT_EQ(ret, 0x87654321); ret = extract_word_glo(&string, 49, 4); - fail_unless(ret == 0xd); + EXPECT_EQ(ret, 0xd); string.word[0] = 0xbeef0000; string.word[1] = 0x4321dead; string.word[2] = 0x00008765; ret = extract_word_glo(&string, 17, 32); - fail_unless(ret == 0xdeadbeef); + EXPECT_EQ(ret, 0xdeadbeef); ret = extract_word_glo(&string, 49, 32); - fail_unless(ret == 0x87654321); + EXPECT_EQ(ret, 0x87654321); ret = extract_word_glo(&string, 49, 16); - fail_unless(ret == 0x4321); - - END_TEST + EXPECT_EQ(ret, 0x4321); } -START_TEST(error_correction_glo) { +TEST(TestDecodeGlonass, ErrorCorrectionGlo) { const struct { glo_string_t str_in; /**< input string for test */ s8 ret; /** result of the test */ @@ -203,30 +168,18 @@ START_TEST(error_correction_glo) { {{{0xc90cfb81, 0x9743a301, 0x010748}}, 0}, /* no errors here */ }; - for (u8 i = 0; i < sizeof(test_case) / sizeof(test_case[0]); i++) { - s8 ret = error_detection_glo(&test_case[i].str_in); - fail_unless(test_case[i].ret == ret); + for (auto i : test_case) { + s8 ret = error_detection_glo(&i.str_in); + EXPECT_EQ(i.ret, ret); } - END_TEST } -START_TEST(test_decode_ephemeris_glo) { +TEST(TestDecodeGlonass, DecodeEphemerisGlo) { gnss_signal_t sid = SID_UNKNOWN; - decode_glo_ephemeris(strings_in, sid, /* utc_params = */ NULL, &eph); + decode_glo_ephemeris(strings_in, sid, /* utc_params = */ nullptr, &eph); e_out(); - END_TEST } -Suite *decode_glo_suite(void) { - Suite *s = suite_create("Decode Glonass"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_extract_glo_word); - tcase_add_test(tc_core, error_correction_glo); - tcase_add_test(tc_core, test_decode_ephemeris_glo); - suite_add_tcase(s, tc_core); - - return s; -} +} // namespace diff --git a/tests/test_edc.cc b/tests/test_edc.cc new file mode 100644 index 0000000..f4700e1 --- /dev/null +++ b/tests/test_edc.cc @@ -0,0 +1,28 @@ +#include +#include + +namespace { + +const u8 *test_data = (const u8 *)"123456789"; + +TEST(TestEdc, Crc24q) { + u32 crc; + + crc = crc24q(test_data, 0, 0); + EXPECT_EQ(crc, 0) + << "CRC of empty buffer with starting value 0 should be 0, not " << crc; + + crc = crc24q(test_data, 0, 22); + EXPECT_EQ(crc, 22) + << "CRC of empty buffer with starting value 22 should be 22, not " << crc; + + /* Test value taken from python crcmod package tests, see: + * http://crcmod.sourceforge.net/crcmod.predefined.html */ + crc = crc24q(test_data, 9, 0xB704CE); + EXPECT_EQ(crc, 0x21CF02) + << "CRC of \"123456789\" with init value 0xB704CE should be 0x21CF02, " + "not " + << crc; +} + +} // namespace diff --git a/tests/check_ephemeris.c b/tests/test_ephemeris.cc similarity index 51% rename from tests/check_ephemeris.c rename to tests/test_ephemeris.cc index 08296b5..4b2b431 100644 --- a/tests/check_ephemeris.c +++ b/tests/test_ephemeris.cc @@ -1,11 +1,19 @@ -#include +#include #include #include #include #include #include -#include "check_suites.h" +namespace { + +/* Helper enum for triggering test behavior based on where toe lies within fit + * interval for a sid. */ +enum class ephemeris_toe_section_of_fit_interval_t { + SBAS = 0, + BEGINNING = 1, + MIDDLE = 2, +}; /* Set thresholds so high that the unit tests * for ephemeris - almanac cross checks actually pass. @@ -13,17 +21,26 @@ #define VALID_ALM_ACCURACY (500000) #define VALID_EPH_ACCURACY (500000) +const double EPHEMERIS_EPSILON_FOR_TOW_VALIDITY_CHECKS = 1.0e-6; + /* GPS ephemeris for tests. * See scenario ME-45. */ static const ephemeris_t gps_eph = { - .sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .toe = {.wn = 1916, .tow = 14400}, + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .toe = {.tow = 14400, .wn = 1916}, .ura = 2.0, .fit_interval = 14400, .valid = 1, .health_bits = 0, .source = EPH_SOURCE_GPS_LNAV, - .data.kepler = {.tgd.gps_s = {5.122274160385132E-9, 0.0}, + .data = + { + .kepler = + { + .tgd = + { + .gps_s = {5.122274160385132E-9, 0.0}, + }, .crc = 198.9375, .crs = 10.28125, .cuc = 5.327165126800537E-7, @@ -42,20 +59,27 @@ static const ephemeris_t gps_eph = { .af0 = 2.5494489818811417E-5, .af1 = 1.2505552149377763E-12, .af2 = 0.0, - .toc = {.wn = 1916, .tow = 14400}, + .toc = {.tow = 14400, .wn = 1916}, .iodc = 2, - .iode = 2}}; + .iode = 2, + }, + }, +}; /* GPS almanac for tests. * See scenario ME-45. */ static const almanac_t gps_alm = { - .sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .toa = {.wn = 1916, .tow = 53248}, + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .toa = {.tow = 53248, .wn = 1916}, .ura = 900, .fit_interval = 504000, .valid = 1, .health_bits = 0, - .data.kepler = {.m0 = 1.5509826579560628, + .data = + { + .kepler = + { + .m0 = 1.5509826579560628, .ecc = 0.005649566650390625, .sqrta = 5153.64453125, .omega0 = 1.8715344586823712, @@ -63,25 +87,26 @@ static const almanac_t gps_alm = { .w = 0.4837084091510879, .inc = 0.964996154674105, .af0 = 2.574920654296875E-5, - .af1 = 0.0}}; + .af1 = 0.0, + }, + }, +}; -START_TEST(test_ephemeris_almanac_divergence) { +TEST(TestEphemeris, EphemerisAlmanacDivergence) { /* See scenario ME-45 description. */ /* Initially just copy the original ephemeris. */ ephemeris_t gps_eph_diverged; memcpy(&gps_eph_diverged, &gps_eph, sizeof(gps_eph_diverged)); - fail_unless(ephemeris_equal(&gps_eph, &gps_eph_diverged), - "Ephemerides should be equal"); + EXPECT_TRUE(ephemeris_equal(&gps_eph, &gps_eph_diverged)); /* Next, let's modify and diverge the ephemeris. * Further modify these if the test case changes. */ gps_eph_diverged.data.kepler.dn = 10.4154338446262e-009; gps_eph_diverged.data.kepler.m0 = 2.16970122385066e+000; - fail_unless(!ephemeris_equal(&gps_eph, &gps_eph_diverged), - "Ephemerides should not be equal"); + EXPECT_FALSE(ephemeris_equal(&gps_eph, &gps_eph_diverged)); /* First check point: start of the position test interval */ gps_time_t t_start = gps_eph.toe; @@ -109,563 +134,509 @@ START_TEST(test_ephemeris_almanac_divergence) { &gps_eph_diverged, &t, orbit_type, div_sat_pos, _, _, _, _)); /* Check successful sat state calculation. */ - fail_unless(calc_alm_ok && calc_eph_ok && calc_eph_div_ok, - "Failure computing sat state! \n" - "Alm success: %" PRIu8 - " \n" - "Eph success: %" PRIu8 - " \n" - "Eph diverged success: %" PRIu8 " \n", - (uint8_t)calc_alm_ok, - (uint8_t)calc_eph_ok, - (uint8_t)calc_eph_div_ok); + EXPECT_TRUE(calc_alm_ok && calc_eph_ok && calc_eph_div_ok) + << "Failure computing sat state! \n" + "Alm success: %" PRIu8 + " \n" + "Eph success: %" PRIu8 + " \n" + "Eph diverged success: %" PRIu8 " \n" + << (uint8_t)calc_alm_ok << (uint8_t)calc_eph_ok + << (uint8_t)calc_eph_div_ok; /* Almanac vs. diverged ephemeris comparison. */ /* Compute distance [m] */ d = vector_distance(3, alm_sat_pos, div_sat_pos); - fail_unless(d <= VALID_ALM_ACCURACY, - "Almanac vs. diverging check failed: \n" - "Iteration: %" PRIu8 - " \n" - "Distance: %lf \n" - "Alm_sat_pos_x: %lf, Diverged_sat_pos_x: %lf \n" - "Alm_sat_pos_y: %lf, Diverged_sat_pos_y: %lf \n" - "Alm_sat_pos_z: %lf, Diverged_sat_pos_z: %lf", - i, - d, - alm_sat_pos[0], - div_sat_pos[0], - alm_sat_pos[1], - div_sat_pos[1], - alm_sat_pos[2], - div_sat_pos[2]); + EXPECT_TRUE(d <= VALID_ALM_ACCURACY) + << "Almanac vs. diverging check failed: \n" + "Iteration: %" PRIu8 + " \n" + "Distance: %lf \n" + "Alm_sat_pos_x: %lf, Diverged_sat_pos_x: %lf \n" + "Alm_sat_pos_y: %lf, Diverged_sat_pos_y: %lf \n" + "Alm_sat_pos_z: %lf, Diverged_sat_pos_z: %lf" + << i << d << alm_sat_pos[0] << div_sat_pos[0] << alm_sat_pos[1] + << div_sat_pos[1] << alm_sat_pos[2] << div_sat_pos[2]; /* Ephemeris vs. diverged ephemeris comparison. */ /* Compute distance [m] */ d = vector_distance(3, eph_sat_pos, div_sat_pos); - fail_unless(d <= VALID_EPH_ACCURACY, - "Ephemeris vs. diverging ephemeris check failed: \n" - "Iteration: %" PRIu8 - " \n" - "Distance: %lf \n" - "Eph_sat_pos_x: %lf, Diverged_sat_pos_x: %lf \n" - "Eph_sat_pos_y: %lf, Diverged_sat_pos_y: %lf \n" - "Eph_sat_pos_z: %lf, Diverged_sat_pos_z: %lf", - i, - d, - eph_sat_pos[0], - div_sat_pos[0], - eph_sat_pos[1], - div_sat_pos[1], - eph_sat_pos[2], - div_sat_pos[2]); + EXPECT_TRUE(d <= VALID_EPH_ACCURACY) + << "Ephemeris vs. diverging ephemeris check failed: \n" + "Iteration: %" PRIu8 + " \n" + "Distance: %lf \n" + "Eph_sat_pos_x: %lf, Diverged_sat_pos_x: %lf \n" + "Eph_sat_pos_y: %lf, Diverged_sat_pos_y: %lf \n" + "Eph_sat_pos_z: %lf, Diverged_sat_pos_z: %lf" + << i << d << eph_sat_pos[0] << div_sat_pos[0] << eph_sat_pos[1] + << div_sat_pos[1] << eph_sat_pos[2] << div_sat_pos[2]; } } -END_TEST -START_TEST(test_ephemeris_equal) { +TEST(TestEphemeris, EphemerisEqual) { ephemeris_t a; ephemeris_t b; memset(&a, 0, sizeof(a)); memset(&b, 0, sizeof(b)); - fail_unless(ephemeris_equal(&a, &b), "Ephemerides should be equal"); + EXPECT_TRUE(ephemeris_equal(&a, &b)); a.valid = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (valid)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.health_bits = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (healthy)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.sat = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (sid.sat)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); - a.sid.code = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (sid.band)"); + a.sid.code = static_cast(1); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); - a.sid.code = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (sid.constellation)"); + a.sid.code = static_cast(1); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.toe.wn = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (toe.wn)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.toe.tow = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (toe.tow)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.ura = 1; - fail_unless(ephemeris_equal(&a, &b), "Ephemerides should be equal (ura)"); + EXPECT_TRUE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.fit_interval = 1; - fail_unless(ephemeris_equal(&a, &b), - "Ephemerides should be equal (fit_interval)"); + EXPECT_TRUE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.tgd.gps_s[0] = 1.0; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.tgd_gps_s)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.crs = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.crs)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.crc = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.crc)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.cuc = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.cuc)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.cus = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.cus)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.cic = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.cic)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.cis = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.cis)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.dn = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.dn)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.m0 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.m0)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.ecc = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.ecc)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.sqrta = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.sqrta)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.omega0 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.omega0)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.omegadot = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.omegadot)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.w = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.w)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.inc = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.inc)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.inc_dot = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.inc_dot)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.af0 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.af0)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.af1 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.af1)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.af2 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.af2)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.iode = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.iode)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.iodc = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.iodc)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.toc.wn = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.toc.wn)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GPS_L1CA; a.data.kepler.toc.tow = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (kepler.toc.tow)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.pos[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.pos[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.pos[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.pos[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.pos[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.pos[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.vel[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.vel[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.vel[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.vel[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.vel[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.vel[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.acc[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.acc[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.acc[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.acc[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.acc[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.acc[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.a_gf0 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.a_gf0)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_SBAS_L1CA; a.data.xyz.a_gf1 = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (xyz.a_gf1)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.gamma = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.gamma)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.tau = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.tau)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.d_tau = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.d_tau)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.iod = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.iod)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.fcn = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.fcn)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.pos[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.pos[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.pos[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.vel[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.vel[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.vel[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.acc[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.acc[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L1OF; a.data.glo.acc[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.gamma = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.gamma)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.tau = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.tau)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.d_tau = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.d_tau)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.iod = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.iod)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.fcn = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.fcn)"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.pos[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.pos[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.pos[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.pos[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.vel[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.vel[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.vel[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.vel[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.acc[0] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[0])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.acc[1] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[1])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); a.sid.code = CODE_GLO_L2OF; a.data.glo.acc[2] = 1; - fail_unless(!ephemeris_equal(&a, &b), - "Ephemerides should not be equal (glo.acc[2])"); + EXPECT_FALSE(ephemeris_equal(&a, &b)); memset(&a, 0, sizeof(a)); } -END_TEST -START_TEST(test_ephemeris_health) { +TEST(TestEphemeris, EphemerisHealth) { const struct test_case { ephemeris_t e; bool healthy; } test_cases[] = { - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .health_bits = 0, - .ura = 2.0, - .valid = true}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .ura = 2.0, + .valid = 1u, + .health_bits = 0, + }, .healthy = true}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .health_bits = 0, - .ura = 200.0, - .valid = false}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .ura = 200.0, + .valid = 0u, + .health_bits = 0, + }, .healthy = true}, - {.e = {.sid = {.code = CODE_GPS_L2CM, .sat = 32}, - .health_bits = 0, - .ura = 2000.0, - .valid = true}, + {.e = + { + .sid = {.sat = 32, .code = CODE_GPS_L2CM}, + .ura = 2000.0, + .valid = 1u, + .health_bits = 0, + }, .healthy = true}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .health_bits = 0, - .ura = 33333.0, - .valid = true}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .ura = 33333.0, + .valid = 1u, + .health_bits = 0, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .health_bits = 0, - .ura = INVALID_URA_VALUE, - .valid = true}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .ura = INVALID_URA_VALUE, + .valid = 1u, + .health_bits = 0, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 1}, - .health_bits = 0, - .ura = -100.0, - .valid = true}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .ura = -100.0, + .valid = 1u, + .health_bits = 0, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 11}, - .health_bits = 0x3F, - .ura = 1.0, - .valid = true}, + {.e = + { + .sid = {.sat = 11, .code = CODE_GPS_L1CA}, + .ura = 1.0, + .valid = 1u, + .health_bits = 0x3F, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 12}, - .health_bits = 0x2A, - .ura = 1.0, - .valid = true}, + {.e = + { + .sid = {.sat = 12, .code = CODE_GPS_L1CA}, + .ura = 1.0, + .valid = 1u, + .health_bits = 0x2A, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L2CM, .sat = 13}, - .health_bits = 0x2E, - .ura = 4000.0, - .valid = true}, + {.e = + { + .sid = {.sat = 13, .code = CODE_GPS_L2CM}, + .ura = 4000.0, + .valid = 1u, + .health_bits = 0x2E, + }, .healthy = false}, - {.e = {.sid = {.code = CODE_GPS_L2CM, .sat = 1}, - .health_bits = 0x2A, - .ura = 4000.0, - .valid = true}, + {.e = + { + .sid = {.sat = 1, .code = CODE_GPS_L2CM}, + .ura = 4000.0, + .valid = 1u, + .health_bits = 0x2A, + }, .healthy = true}, - {.e = {.sid = {.code = CODE_GPS_L1CA, .sat = 22}, - .health_bits = 0x2E, - .ura = 10.0, - .valid = true}, + {.e = + { + .sid = {.sat = 22, .code = CODE_GPS_L1CA}, + .ura = 10.0, + .valid = 1u, + .health_bits = 0x2E, + }, .healthy = false}, }; for (u8 i = 0; i < (sizeof(test_cases) / sizeof(test_cases[0])); i++) { const struct test_case *t = &test_cases[i]; - fail_unless(t->healthy == ephemeris_healthy(&t->e, t->e.sid.code), - "test signal %d healthy incorrect", - i); + EXPECT_EQ(t->healthy, ephemeris_healthy(&t->e, t->e.sid.code)) + << "test signal " << i << " healthy incorrect"; } } -END_TEST -START_TEST(test_6bit_health_word) { +TEST(TestEphemeris, Test6bitHealthWord) { /* * u8 gps_healthy(u8 health_bits, code_t code) */ @@ -694,23 +665,20 @@ START_TEST(test_6bit_health_word) { {.health_bits = 0x01, .code = CODE_GPS_L2P, .result = 0}, {.health_bits = 0, .code = CODE_GPS_L5I, .result = 1}, }; - for (u32 i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { - const struct test_case *t = &test_cases[i]; - fail_unless(t->result == check_6bit_health_word(t->health_bits, t->code), - "check_6bit_health_word(%d, %d) test failed (%d)", - t->health_bits, - t->code, - t->result); + for (const auto &i : test_cases) { + const struct test_case *t = &i; + EXPECT_EQ(t->result, check_6bit_health_word(t->health_bits, t->code)) + << "check_6bit_health_word(%d, %d) test failed (%d)" << t->health_bits + << t->code << t->result; } } -END_TEST -START_TEST(test_ephemeris_bds) { +TEST(TestEphemeris, EphemerisBds) { /* clang-format off */ static const ephemeris_t ephe_exp = { .sid = { .sat = 25, - .code = 12 + .code = static_cast(12) }, .toe = { .tow = 460800.000000, @@ -721,7 +689,7 @@ START_TEST(test_ephemeris_bds) { .valid = 0, .health_bits = 0, .source = EPH_SOURCE_BDS_D1_D2_NAV, - .data.kepler = { + .data = {.kepler = { .tgd = { .bds_s = {-2.99999997e-10, -2.99999997e-10} }, @@ -749,7 +717,7 @@ START_TEST(test_ephemeris_bds) { }, .iodc = 160, .iode = 160 - } + }} }; static const u32 words[3][10] = { @@ -764,23 +732,21 @@ START_TEST(test_ephemeris_bds) { }; /* clang-format on */ - gnss_signal_t sid = {.sat = 25, .code = 12}; + gnss_signal_t sid = {.sat = 25, .code = static_cast(12)}; ephemeris_t ephe; memset(&ephe, 0, sizeof(ephe)); decode_bds_d1_ephemeris(words, sid, &ephe); - fail_unless(ephemeris_equal(&ephe_exp, &ephe), - "BDS ephemerides should be equal"); - END_TEST + EXPECT_TRUE(ephemeris_equal(&ephe_exp, &ephe)); } -START_TEST(test_ephemeris_gal) { +TEST(TestEphemeris, EphemerisGal) { /* clang-format off */ static const ephemeris_t ephe_exp = { .sid = { .sat = 8, - .code = 14 + .code = static_cast(14) }, .toe = { .tow = 135000., @@ -791,12 +757,12 @@ START_TEST(test_ephemeris_gal) { .valid = 1, .health_bits = 0, .source = EPH_SOURCE_GAL_INAV, - .data.kepler = { + .data = {.kepler = { .tgd = { .gal_s = {-5.5879354476928711e-09, -6.5192580223083496e-09} }, - .crs = -54.0625, .crc = 62.375, + .crs = -54.0625, .cuc = -2.3748725652694702e-06, .cus = 1.2902542948722839e-05, .cic = 7.4505805969238281e-09, @@ -818,10 +784,10 @@ START_TEST(test_ephemeris_gal) { .tow = 135000, .wn = 2090 }, + .iodc = 97, .iode = 97, - .iodc = 97 } - }; + }}; static const u8 words[5][GAL_INAV_CONTENT_BYTE] = { { 0x4, 0x61, 0x23, 0x28, 0xBF, 0x30, 0x9B, 0xA0, 0x0, 0x71, 0xC8, 0x6A, 0xA8, 0x14, 0x16, 0x7}, @@ -838,283 +804,346 @@ START_TEST(test_ephemeris_gal) { ephe.sid.code = CODE_GAL_E1B; ephe.valid = 1; - fail_unless(ephemeris_equal(&ephe_exp, &ephe), - "GAL ephemerides should be equal"); - END_TEST + EXPECT_TRUE(ephemeris_equal(&ephe_exp, &ephe)); +} + +void test_ephemeris_validity_window( + const ephemeris_t &eph, + gps_time_t &t_valid, + ephemeris_toe_section_of_fit_interval_t toe_section) { + ephemeris_validity_window_t window = + ephemeris_validity_window(&eph, &t_valid); + + gps_time_t lower_bound = eph.toe; + gps_time_t upper_bound = eph.toe; + if (toe_section == ephemeris_toe_section_of_fit_interval_t::MIDDLE) { + lower_bound.tow -= eph.fit_interval / 2.0; + upper_bound.tow += eph.fit_interval / 2.0; + } else if (toe_section == + ephemeris_toe_section_of_fit_interval_t::BEGINNING) { + upper_bound.tow += eph.fit_interval; + } else if (toe_section == ephemeris_toe_section_of_fit_interval_t::SBAS) { + upper_bound.tow += SBAS_FIT_INTERVAL_SECONDS; + } + EXPECT_EQ(window.bgn, lower_bound); + EXPECT_EQ(window.end, upper_bound); + + gps_time_t below_lower_bound = lower_bound; + below_lower_bound.tow -= EPHEMERIS_EPSILON_FOR_TOW_VALIDITY_CHECKS; + gps_time_t above_upper_bound = upper_bound; + above_upper_bound.tow += EPHEMERIS_EPSILON_FOR_TOW_VALIDITY_CHECKS; + EXPECT_EQ(ephemeris_valid(&eph, &lower_bound), 1); + EXPECT_EQ(ephemeris_valid(&eph, &upper_bound), 1); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + if (IS_SBAS(eph.sid)) { + EXPECT_EQ(ephemeris_valid(&eph, &below_lower_bound), 1); + EXPECT_EQ(ephemeris_valid(&eph, &above_upper_bound), 1); + } else { + EXPECT_EQ(ephemeris_valid(&eph, &below_lower_bound), 0); + EXPECT_EQ(ephemeris_valid(&eph, &above_upper_bound), 0); + } +} + +TEST(TestEphemeris, EphemerisValidityWindow) { + ephemeris_t eph; + gps_time_t t_valid = gps_eph.toe; + + eph = gps_eph; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::MIDDLE); + + eph = gps_eph; + t_valid = gps_eph.toe; + eph.sid.code = CODE_QZS_L1CA; + eph.source = EPH_SOURCE_QZS_LNAV; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::MIDDLE); + + eph = gps_eph; + t_valid = gps_eph.toe; + eph.sid.code = CODE_GAL_E1B; + eph.source = EPH_SOURCE_GAL_INAV; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::BEGINNING); + + eph = gps_eph; + t_valid = gps_eph.toe; + eph.sid.code = CODE_BDS2_B1; + eph.source = EPH_SOURCE_BDS_D1_D2_NAV; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::BEGINNING); + + eph = gps_eph; + t_valid = gps_eph.toe; + eph.sid.code = CODE_GLO_L1OF; + eph.source = EPH_SOURCE_GLO_FDMA; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::MIDDLE); + + eph = gps_eph; + t_valid = gps_eph.toe; + eph.sid.code = CODE_SBAS_L1CA; + test_ephemeris_validity_window( + eph, t_valid, ephemeris_toe_section_of_fit_interval_t::SBAS); } -START_TEST(test_ephemeris_valid) { +TEST(TestEphemeris, EphemerisValid) { const gps_time_t t_valid = gps_eph.toe; const gps_time_t t_late = { + .tow = + t_valid.tow + static_cast(gps_eph.fit_interval) / 2.0 + 1.0, .wn = t_valid.wn, - .tow = t_valid.tow + (double)gps_eph.fit_interval / 2.0 + 1.0}; + }; const gps_time_t t_late_gal = { + .tow = t_valid.tow + static_cast(gps_eph.fit_interval) + 1.0, .wn = t_valid.wn, - .tow = t_valid.tow + (double)gps_eph.fit_interval + 1.0}; + }; ephemeris_t eph; - fail_unless(ephemeris_valid(NULL, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(NULL, &t_valid) == EPH_NULL); + EXPECT_EQ(ephemeris_valid(nullptr, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(nullptr, &t_valid), EPH_NULL); eph = gps_eph; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_GLO_L1OF; eph.source = EPH_SOURCE_GLO_FDMA; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_SBAS_L1CA; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; - eph.valid = false; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID); + eph.valid = 0u; + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID); eph = gps_eph; eph.toe.wn = 0; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_WN_EQ_0); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_WN_EQ_0); eph = gps_eph; eph.fit_interval = 0; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == - EPH_FIT_INTERVAL_EQ_0); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_FIT_INTERVAL_EQ_0); eph = gps_eph; eph.health_bits = 0x3F; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); eph = gps_eph; eph.data.kepler.iodc = 1; eph.data.kepler.iode = 3; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.data.kepler.iodc = GPS_IODC_MAX; eph.data.kepler.iode = GPS_IODE_MAX; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.data.kepler.iodc = GPS_IODC_MAX + 1; eph.data.kepler.iode = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.data.kepler.iodc = GPS_IODC_MAX + 1; eph.data.kepler.iode = GPS_IODE_MAX + 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; - fail_unless(ephemeris_valid(&eph, &t_late) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_late) == EPH_TOO_OLD); + EXPECT_EQ(ephemeris_valid(&eph, &t_late), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_late), EPH_TOO_OLD); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; eph.health_bits = 0x3F; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; eph.data.kepler.iodc = 1; eph.data.kepler.iode = 3; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; eph.data.kepler.iodc = GPS_IODC_MAX; eph.data.kepler.iode = GPS_IODE_MAX; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; eph.data.kepler.iodc = GPS_IODC_MAX + 1; eph.data.kepler.iode = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; eph.data.kepler.iodc = GPS_IODC_MAX + 1; eph.data.kepler.iode = GPS_IODE_MAX + 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_QZS_L1CA; eph.source = EPH_SOURCE_QZS_LNAV; - fail_unless(ephemeris_valid(&eph, &t_late) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_late) == EPH_TOO_OLD); + EXPECT_EQ(ephemeris_valid(&eph, &t_late), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_late), EPH_TOO_OLD); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; eph.health_bits = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; eph.data.kepler.iodc = 1; eph.data.kepler.iode = 3; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; eph.data.kepler.iodc = GAL_IOD_NAV_MAX; eph.data.kepler.iode = GAL_IOD_NAV_MAX; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; eph.data.kepler.iodc = GAL_IOD_NAV_MAX + 1; eph.data.kepler.iode = GAL_IOD_NAV_MAX + 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_GAL_E1B; eph.source = EPH_SOURCE_GAL_INAV; - fail_unless(ephemeris_valid(&eph, &t_late_gal) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_late_gal) == EPH_TOO_OLD); + EXPECT_EQ(ephemeris_valid(&eph, &t_late_gal), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_late_gal), EPH_TOO_OLD); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; eph.health_bits = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; eph.data.kepler.iodc = 1; eph.data.kepler.iode = 3; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; eph.data.kepler.iodc = BDS2_IODC_MAX; eph.data.kepler.iode = BDS2_IODE_MAX; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; eph.data.kepler.iodc = BDS2_IODC_MAX + 1; eph.data.kepler.iode = BDS2_IODE_MAX + 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_BDS2_B1; eph.source = EPH_SOURCE_BDS_D1_D2_NAV; - fail_unless(ephemeris_valid(&eph, &t_late_gal) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_late_gal) == EPH_TOO_OLD); + EXPECT_EQ(ephemeris_valid(&eph, &t_late_gal), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_late_gal), EPH_TOO_OLD); eph = gps_eph; eph.sid.code = CODE_BDS3_B1CI; eph.source = EPH_SOURCE_BDS_BCNAV1; eph.data.kepler.iodc = 1; eph.data.kepler.iode = 3; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_BDS3_B1CI; eph.source = EPH_SOURCE_BDS_BCNAV1; eph.data.kepler.iodc = BDS3_IODC_MAX; eph.data.kepler.iode = BDS3_IODE_MAX; - fail_unless(ephemeris_valid(&eph, &t_valid) == 1); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_VALID); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 1); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_VALID); eph = gps_eph; eph.sid.code = CODE_BDS3_B1CI; eph.source = EPH_SOURCE_BDS_BCNAV1; eph.data.kepler.iodc = BDS3_IODC_MAX + 1; eph.data.kepler.iode = BDS3_IODE_MAX + 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_INVALID_IOD); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_INVALID_IOD); eph = gps_eph; eph.sid.code = CODE_GLO_L1OF; eph.source = EPH_SOURCE_GLO_FDMA; eph.health_bits = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); eph = gps_eph; eph.sid.code = CODE_GLO_L1OF; eph.source = EPH_SOURCE_GLO_FDMA; - fail_unless(ephemeris_valid(&eph, &t_late) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_late) == EPH_TOO_OLD); + EXPECT_EQ(ephemeris_valid(&eph, &t_late), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_late), EPH_TOO_OLD); eph = gps_eph; eph.sid.code = CODE_SBAS_L1CA; eph.health_bits = 1; - fail_unless(ephemeris_valid(&eph, &t_valid) == 0); - fail_unless(ephemeris_valid_detailed(&eph, &t_valid) == EPH_UNHEALTHY); - - END_TEST -} - -Suite *ephemeris_suite(void) { - Suite *s = suite_create("Ephemeris"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_ephemeris_almanac_divergence); - tcase_add_test(tc_core, test_ephemeris_equal); - tcase_add_test(tc_core, test_ephemeris_health); - tcase_add_test(tc_core, test_6bit_health_word); - tcase_add_test(tc_core, test_ephemeris_bds); - tcase_add_test(tc_core, test_ephemeris_gal); - tcase_add_test(tc_core, test_ephemeris_valid); - suite_add_tcase(s, tc_core); - - return s; + EXPECT_EQ(ephemeris_valid(&eph, &t_valid), 0); + EXPECT_EQ(ephemeris_valid_detailed(&eph, &t_valid), EPH_UNHEALTHY); } +} // namespace diff --git a/tests/test_geoid_model.cc b/tests/test_geoid_model.cc new file mode 100644 index 0000000..ea37b6d --- /dev/null +++ b/tests/test_geoid_model.cc @@ -0,0 +1,148 @@ +/* + * Geoid model tests + * + * Contains code from + * https://github.com/swift-nav/RTKLIB/blob/master/src/geoid.c in accordance + * with the terms of the RTKLib licence + */ + +#include +#include +#include + +#include + +#include "../src/geoid_model.h" + +namespace { + +/* embedded geoid area range {W,E,S,N} (deg) */ +const double range[4] = {0.0, 360.0, -90.0, 90.0}; + +/* bilinear interpolation ----------------------------------------------------*/ +double interpb(const double* y, double a, double b) { + return y[0] * (1.0 - a) * (1.0 - b) + y[1] * a * (1.0 - b) + + y[2] * (1.0 - a) * b + y[3] * a * b; +} + +const GeoidModel& model_1_degree = *get_geoid_model_1_degree(); +const GeoidModel& model_15_minute = *get_geoid_model_15_minute(); + +/* lookup function for embedded geoid model */ +float geoidh_emb(const double& lat, + const double& lon, + const GeoidModel& model) { + double a, b, y[4]; + int i1, i2, j1, j2; + + if (lon < range[0] || range[1] < lon || lat < range[2] || range[3] < lat) { + return 0.0; + } + a = (lon - range[0]) / model.lon_spacing; + b = (lat - range[2]) / model.lat_spacing; + i1 = static_cast(a); + a -= i1; + i2 = i1 < 360 / model.lon_spacing ? i1 + 1 : i1; + j1 = static_cast(b); + b -= j1; + j2 = j1 < 180 / model.lat_spacing ? j1 + 1 : j1; + y[0] = model.data[i1 * model.n_lat + j1]; + y[1] = model.data[i2 * model.n_lat + j1]; + y[2] = model.data[i1 * model.n_lat + j2]; + y[3] = model.data[i2 * model.n_lat + j2]; + return interpb(y, a, b); +} + +/* + * Perform direct lookups on GEOID to compare height values from the 1 degree + * resolution geoid vs heights from the 15 minute resolution geoid for an + * exact match (for all points which exist in the 1 degree grid) + */ +TEST(TestGeoidModel, CompareGeoidModelsMatchingPoints) { + // 360 degrees of longitude (1 repeated) + for (int lon = 0; lon < 361; lon++) { + // 180 degrees of latitude (1 repeated) + for (int lat = 0; lat < 181; lat++) { + float val_1_degree = + model_1_degree.data[lon * model_1_degree.n_lat + lat]; + float val_15_minute = + model_15_minute.data[4 * lon * model_15_minute.n_lat + 4 * lat]; + + EXPECT_TRUE(std::fabs(val_1_degree - val_15_minute) < 1e-4) + << "Mismatch between 1 degree resolution and 0.25 degree " + "resolution geoids: " + << val_1_degree << " vs " << val_15_minute; + } + } +} + +/* + * Use geoidh_emb() to compare heights from the 1 degree resolution geoid vs + * heights from the 15 minute resolution geoid for every 10th of a degree + * (with bilinear interpolation). Maximum expected difference is 15.2m (in + * Hawaii). + */ +TEST(TestGeoidModel, CompareGeoidModelsTenthDegree) { + for (int lon = 0; lon <= 3600; lon++) { + for (int lat = -900; lat <= 900; lat++) { + double lat_deg = lat / 10.; + double lon_deg = lon / 10.; + float val_1_degree = geoidh_emb(lat_deg, lon_deg, model_1_degree); + float val_15_minute = geoidh_emb(lat_deg, lon_deg, model_15_minute); + EXPECT_TRUE(std::fabs(val_1_degree - val_15_minute) < 15.5) + << "Mismatch between 1 degree resolution and 0.25 degree " + "resolution geoids " + "at lat " + << lat_deg << ", lon " << lon_deg << ": " << val_1_degree << " vs " + << val_15_minute << ", delta " + << std::fabs(val_1_degree - val_15_minute) << "\n"; + } + } +} + +/* + * Compare heights from the 1 degree geoid (with bilinear interpolation) vs + * heights from the 1 degree geoid (with bicubic interpolation) for every 10th + * of a degree. Maximum offset should be 3.69m (in Indonesia). + */ +TEST(TestGeoidModel, Compare1DegreeBilinearVsBicubic) { + for (int lon = 0; lon <= 3600; lon++) { + for (int lat = -900; lat <= 900; lat++) { + double lat_deg = lat / 10.; + double lon_deg = lon / 10.; + + float bilinear = geoidh_emb(lat_deg, lon_deg, model_1_degree); + float bicubic = get_geoid_offset_1_degree(lat_deg * D2R, lon_deg * D2R); + EXPECT_TRUE(std::fabs(bilinear - bicubic) < 3.69) + << "Mismatch between bilinear and bicubic interpolation for 1 " + "degree geoid " + "at lat " + << lat_deg << ", lon " << lon_deg << ": " << bilinear << "vs " + << bicubic << ", delta " << std::fabs(bilinear - bicubic) << "\n"; + } + } +} + +/* + * Compare heights from the 15 minute geoid (with bilinear interpolation) vs + * heights from the 15 degree geoid (with bicubic interpolation) for every 10th + * of a degree. Maximum offset should be 1.33m (in Columbia) + */ +TEST(TestGeoidModel, Compare15MinuteBilinearVsBicubic) { + for (int lon = 0; lon <= 3600; lon++) { + for (int lat = -900; lat <= 900; lat++) { + double lat_deg = lat / 10.; + double lon_deg = lon / 10.; + float bilinear = geoidh_emb(lat_deg, lon_deg, model_15_minute); + float bicubic = get_geoid_offset_15_minute(lat_deg * D2R, lon_deg * D2R); + EXPECT_TRUE(std::fabs(bilinear - bicubic) < 1.33) + << "Mismatch between bilinear and bicubic interpolation for 15 " + "minute grid " + "at lat " + << lat_deg << ", lon " << lon_deg << ": " << bilinear << "vs " + << bicubic << ", delta " << std::fabs(bilinear - bicubic) << "\n"; + } + } +} + +} // namespace diff --git a/tests/test_glo_map.cc b/tests/test_glo_map.cc new file mode 100644 index 0000000..f309c60 --- /dev/null +++ b/tests/test_glo_map.cc @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 Swift Navigation Inc. + * Contact: Dmitry Tatarinov + * + * This source is subject to the license found in the file 'LICENSE' which must + * be distributed together with this source. All other rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include + +#define FCN_TEST_VAL 14 +#define SLOT_ID_TEST_VAL_1 5 +#define SLOT_ID_TEST_VAL_2 10 + +namespace { + +void glo_map_lock(void) {} +void glo_map_unlock(void) {} + +TEST(TestGloMap, GloMap) { + /* We do not test thread safety here. + Therefore lock & unlock functions are just stubs. */ + glo_map_init(glo_map_lock, glo_map_unlock); + + for (u16 i = 1; i <= NUM_SATS_GLO; i++) { + gnss_signal_t glo_sid = construct_sid(CODE_GLO_L1OF, i); + glo_map_set_slot_id(FCN_TEST_VAL, /*glo_slot_id=*/i); + + EXPECT_TRUE(glo_map_valid(glo_sid)); + + u16 fcn = glo_map_get_fcn(glo_sid); + EXPECT_EQ(fcn, FCN_TEST_VAL); + + glo_map_clear_slot_id(i); + + EXPECT_FALSE(glo_map_valid(glo_sid)); + } + + u16 slot_id1, slot_id2; + u8 si_num; + si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); + EXPECT_EQ(si_num, 0); + EXPECT_EQ(slot_id1, 0); + EXPECT_EQ(slot_id2, 0); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); + si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); + EXPECT_EQ(si_num, 1); + EXPECT_EQ(slot_id1, SLOT_ID_TEST_VAL_1); + EXPECT_EQ(slot_id2, 0); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2); + si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); + EXPECT_EQ(si_num, 2); + EXPECT_EQ(slot_id1, SLOT_ID_TEST_VAL_1); + EXPECT_EQ(slot_id2, SLOT_ID_TEST_VAL_2); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_1); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2); + glo_map_set_slot_id(FCN_TEST_VAL, SLOT_ID_TEST_VAL_2 + 1); + si_num = glo_map_get_slot_id(FCN_TEST_VAL, &slot_id1, &slot_id2); + EXPECT_EQ(si_num, 2); + EXPECT_EQ(slot_id1, SLOT_ID_TEST_VAL_1); + EXPECT_EQ(slot_id2, SLOT_ID_TEST_VAL_2); +} + +} // namespace diff --git a/tests/check_gnss_time.c b/tests/test_gnss_time.cc similarity index 54% rename from tests/check_gnss_time.c rename to tests/test_gnss_time.cc index 6d39597..a09c1c0 100644 --- a/tests/check_gnss_time.c +++ b/tests/test_gnss_time.cc @@ -1,13 +1,14 @@ -#include +#include #include #include #include #include -#include "check_suites.h" -#include "common/check_utils.h" +#include "check_utils.h" -START_TEST(test_gpsdifftime) { +namespace { + +TEST(TestGnssTime, Gpsdifftime) { struct gpsdifftime_testcase { gps_time_t a, b; double dt; @@ -22,59 +23,53 @@ START_TEST(test_gpsdifftime) { {.a = {0, 5120}, .b = {0, 1024}, .dt = 2477260800}, }; const double tow_tol = 1e-10; - for (size_t i = 0; - i < sizeof(testcases) / sizeof(struct gpsdifftime_testcase); - i++) { - double dt = gpsdifftime(&testcases[i].a, &testcases[i].b); - fail_unless(fabs(dt - testcases[i].dt) < tow_tol, - "gpsdifftime test case %zu failed, dt = %.12f", - i, - dt); + for (auto &testcase : testcases) { + double dt = gpsdifftime(&testcase.a, &testcase.b); + EXPECT_NEAR(dt, testcase.dt, tow_tol); } } -END_TEST -START_TEST(test_gpsdifftime_week_second) { +TEST(TestGnssTime, GpsdifftimeWeekSecond) { struct gpsdifftime_week_second_testcase { gps_time_t beginning; gps_time_t end; gps_time_duration_t dt; bool success; } testcases[] = { - {.end = {567890.0, 1234}, - .beginning = {567890.0, 1234}, + {.beginning = {567890.0, 1234}, + .end = {567890.0, 1234}, .dt = {.seconds = 0, .weeks = 0}, .success = true}, - {.end = {567890.0, 1234}, - .beginning = {0.0, 1234}, + {.beginning = {0.0, 1234}, + .end = {567890.0, 1234}, .dt = {.seconds = 567890, .weeks = 0}, .success = true}, - {.end = {567890.0, WN_UNKNOWN}, - .beginning = {0.0, 1234}, + {.beginning = {0.0, 1234}, + .end = {567890.0, WN_UNKNOWN}, .dt = {0, 0}, .success = false}, - {.end = {222222.0, 2222}, - .beginning = {2222.0, WN_UNKNOWN}, + {.beginning = {2222.0, WN_UNKNOWN}, + .end = {222222.0, 2222}, .dt = {0, 0}, .success = false}, - {.end = {444444.0, WN_UNKNOWN}, - .beginning = {2222.0, WN_UNKNOWN}, + {.beginning = {2222.0, WN_UNKNOWN}, + .end = {444444.0, WN_UNKNOWN}, .dt = {0, 0}, .success = false}, - {.end = {604578.0, 1000}, - .beginning = {222.222, 1001}, + {.beginning = {222.222, 1001}, + .end = {604578.0, 1000}, .dt = {.seconds = -444.222, .weeks = 0}, .success = true}, - {.end = {222.222, 1001}, - .beginning = {604578.0, 1000}, + {.beginning = {604578.0, 1000}, + .end = {222.222, 1001}, .dt = {.seconds = 444.222, .weeks = 0}, .success = true}, - {.end = {604578.0, 1001}, - .beginning = {222.222, 1000}, + {.beginning = {222.222, 1000}, + .end = {604578.0, 1001}, .dt = {.seconds = 604355.778, .weeks = 1}, .success = true}, - {.end = {0, 5120}, - .beginning = {0, 1024}, + {.beginning = {0, 1024}, + .end = {0, 5120}, .dt = {.seconds = 0, .weeks = 4096}, .success = true}, }; @@ -85,15 +80,17 @@ START_TEST(test_gpsdifftime_week_second) { gps_time_duration_t dt = {0, 0}; bool success = gpsdifftime_week_second( &testcases[i].end, &testcases[i].beginning, &dt); - double absdiff_s = - fabs(dt.seconds - testcases[i].dt.seconds) + - fabs((double)dt.weeks - (double)testcases[i].dt.weeks) * WEEK_SECS; + double absdiff_s = fabs(dt.seconds - testcases[i].dt.seconds) + + fabs(static_cast(dt.weeks) - + static_cast(testcases[i].dt.weeks)) * + WEEK_SECS; bool test_passed = absdiff_s < tow_tol && success == testcases[i].success; - fail_unless(test_passed, "gpsdifftime_week_second test case %zu failed", i); + EXPECT_TRUE(test_passed) + << "gpsdifftime_week_second test case " << i << " failed"; } } -START_TEST(test_long_gps_time_diff) { +TEST(TestGnssTime, LongGpsTimeDiff) { gps_time_t t2 = {.tow = 0, .wn = 2345}; gps_time_t t1 = {.tow = 1.2345678, .wn = 0}; gps_time_duration_t dt = {0, 0}; @@ -103,15 +100,14 @@ START_TEST(test_long_gps_time_diff) { dt.seconds = gpsdifftime(&t2, &t1); dt.weeks = 0; normalize_gps_time_duration(&dt); - fail_unless(fabs(dt.seconds - correct_dt.seconds) < 1e-2); - fail_unless(fabs(dt.seconds - correct_dt.seconds) > 1e-9); + EXPECT_NEAR(dt.seconds, correct_dt.seconds, 1e-2); + EXPECT_TRUE(fabs(dt.seconds - correct_dt.seconds) > 1e-9); gpsdifftime_week_second(&t2, &t1, &dt); - fail_unless(fabs(dt.seconds - correct_dt.seconds) < 1e-12, - "Long GPST difference test failed."); + EXPECT_NEAR(dt.seconds, correct_dt.seconds, 1e-12); } -START_TEST(test_normalize_gps_time) { +TEST(TestGnssTime, NormalizeGpsTime) { gps_time_t testcases[] = {{0, 1234}, {3 * DAY_SECS, 1234}, {WEEK_SECS + DAY_SECS, 1234}, @@ -119,27 +115,20 @@ START_TEST(test_normalize_gps_time) { {DAY_SECS, WN_UNKNOWN}, {WEEK_SECS + 1, WN_UNKNOWN}}; const double tow_tol = 1e-10; - for (size_t i = 0; i < sizeof(testcases) / sizeof(gps_time_t); i++) { - double t_original = testcases[i].wn * WEEK_SECS + testcases[i].tow; - s16 wn = testcases[i].wn; - normalize_gps_time(&testcases[i]); - double t_normalized = testcases[i].wn * WEEK_SECS + testcases[i].tow; - if (testcases[i].wn != WN_UNKNOWN) { - fail_unless( - fabs(t_original - t_normalized) < tow_tol, - "normalize_gps_time test case %zu failed, t_original = %.12f, " - "t_normalized = %.12f", - i, - t_original, - t_normalized); + for (auto &testcase : testcases) { + double t_original = testcase.wn * WEEK_SECS + testcase.tow; + s16 wn = testcase.wn; + normalize_gps_time(&testcase); + double t_normalized = testcase.wn * WEEK_SECS + testcase.tow; + if (testcase.wn != WN_UNKNOWN) { + EXPECT_NEAR(t_original, t_normalized, tow_tol); } /* normalization must not touch unknown week number */ - fail_unless(wn != WN_UNKNOWN || testcases[i].wn == WN_UNKNOWN); + EXPECT_TRUE(wn != WN_UNKNOWN || testcase.wn == WN_UNKNOWN); } } -END_TEST -START_TEST(test_normalize_gps_time_duration) { +TEST(TestGnssTime, NormalizeGpsTimeDuration) { gps_time_duration_t testcases[] = {{0, 1234}, {3 * DAY_SECS, 1234}, {WEEK_SECS + DAY_SECS, 1234}, @@ -165,11 +154,10 @@ START_TEST(test_normalize_gps_time_duration) { bool test_passed = testcases[i].weeks == expected_results[i].weeks && fabs(testcases[i].seconds - expected_results[i].seconds) < tow_tol; - fail_unless( - test_passed, "normalize_gps_time_duration test case %zu failed", i); + EXPECT_TRUE(test_passed) + << "normalize_gps_time_duration test case " << i << " failed"; } } -END_TEST /* test that the make_utc_tm conversion matches gmtime */ void gmtime_test(const char *name, time_t start, time_t end, time_t step) { @@ -178,85 +166,39 @@ void gmtime_test(const char *name, time_t start, time_t end, time_t step) { time_t t_unix = t_gps + GPS_EPOCH; struct tm *date = gmtime(&t_unix); - gps_time_t t = {.wn = t_gps / WEEK_SECS, .tow = t_gps % WEEK_SECS}; + gps_time_t t = {.tow = static_cast(t_gps % WEEK_SECS), + .wn = static_cast(t_gps / WEEK_SECS)}; utc_tm u; make_utc_tm(&t, &u); - fail_unless((date->tm_year + 1900) == u.year, - "%s, expected year %d, got %d, time = %s", - name, - date->tm_year + 1900, - u.year, - asctime(date)); - fail_unless((date->tm_mon + 1) == u.month, - "%s, expected month %d, got %d, time = %s", - name, - date->tm_mon + 1, - u.month, - asctime(date)); - fail_unless((date->tm_yday + 1) == u.year_day, - "%s, expected year_day %d, got %d, time = %s", - name, - date->tm_yday + 1, - u.year_day, - asctime(date)); - fail_unless(date->tm_mday == u.month_day, - "%s, expected month_day %d, got %d, time = %s", - name, - date->tm_mday, - u.month_day, - asctime(date)); - fail_unless(date->tm_wday == (u.week_day % 7), - "%s, expected week_day %d, got %d, time = %s", - name, - date->tm_wday, - u.week_day % 7, - asctime(date)); - fail_unless(date->tm_hour == u.hour, - "%s, expected hour %d, got %d, time = %s", - name, - date->tm_hour, - u.hour, - asctime(date)); - fail_unless(date->tm_min == u.minute, - "%s, expected minute %d, got %d, time = %s", - name, - date->tm_min, - u.minute, - asctime(date)); - fail_unless(date->tm_sec == u.second_int, - "%s, expected second_int %d, got %d, time = %s", - name, - date->tm_sec, - u.second_int, - asctime(date)); - fail_unless(0.0 == u.second_frac, - "%s, expected second_frac %f, got %f, time = %s", - name, - 0.0, - u.second_frac, - asctime(date)); + EXPECT_EQ((date->tm_year + 1900), u.year); + EXPECT_EQ((date->tm_mon + 1), u.month); + EXPECT_EQ((date->tm_yday + 1), u.year_day); + EXPECT_EQ(date->tm_mday, u.month_day); + EXPECT_EQ(date->tm_wday, (u.week_day % 7)); + EXPECT_EQ(date->tm_hour, u.hour); + EXPECT_EQ(date->tm_min, u.minute); + EXPECT_EQ(date->tm_sec, u.second_int); + EXPECT_EQ(0.0, u.second_frac); t_gps += step; } } -START_TEST(test_gps2utc_time) { +TEST(TestGnssTime, Gps2utcTime) { /* test make_utc_tm for Jan 6 1980 in 1 s increments */ gmtime_test("make_utc_tm_day", 0, 1 * DAY_SECS + 1, 1); } -END_TEST -START_TEST(test_gps2utc_date) { +TEST(TestGnssTime, Gps2utcDate) { /* test make_utc_tm from 1980 to 2048 with (1 day + 1 s) increments * (68 years is 2144448000, which is just below the maximum value of a * signed 32-bit number, i.e. 2147483648) */ gmtime_test("make_utc_tm_rollover", 0, 68L * 365 * DAY_SECS, DAY_SECS + 1); } -END_TEST -START_TEST(test_gps_time_match_weeks) { +TEST(TestGnssTime, GpsTimeMatchWeeks) { struct gps_time_match_weeks_testcase { gps_time_t t, ref, ret; } testcases[] = { @@ -295,27 +237,14 @@ START_TEST(test_gps_time_match_weeks) { .ref = {6 * DAY_SECS, WN_UNKNOWN}, .ret = {DAY_SECS, WN_UNKNOWN}}, }; - for (size_t i = 0; - i < sizeof(testcases) / sizeof(struct gps_time_match_weeks_testcase); - i++) { - gps_time_match_weeks(&testcases[i].t, &testcases[i].ref); - fail_unless( - testcases[i].t.wn == testcases[i].ret.wn, - "gps_time_match_weeks test case %zu failed, t.wn = %d, ret.wn = %d", - i, - testcases[i].t.wn, - testcases[i].ret.wn); - fail_unless(testcases[i].t.tow == testcases[i].ret.tow, - "gps_time_match_weeks test case %zu failed, t.tow = %.12f, " - "ret.tow = %.12f", - i, - testcases[i].t.tow, - testcases[i].ret.tow); + for (auto &testcase : testcases) { + gps_time_match_weeks(&testcase.t, &testcase.ref); + EXPECT_EQ(testcase.t.wn, testcase.ret.wn); + EXPECT_EQ(testcase.t.tow, testcase.ret.tow); } } -END_TEST -START_TEST(test_gps_adjust_week_cycle) { +TEST(TestGnssTime, GpsAdjustWeekCycle) { struct gps_adjust_week_cycle_testcase { u16 wn_raw, ret; } testcases[] = { @@ -329,20 +258,13 @@ START_TEST(test_gps_adjust_week_cycle) { {.wn_raw = GPS_WEEK_REFERENCE + 1, .ret = GPS_WEEK_REFERENCE + 1}, }; const u16 wn_ref = GPS_WEEK_REFERENCE; - for (size_t i = 0; - i < sizeof(testcases) / sizeof(struct gps_adjust_week_cycle_testcase); - i++) { - u16 wn = gps_adjust_week_cycle(testcases[i].wn_raw, wn_ref); - fail_unless(wn == testcases[i].ret, - "gps_adjust_week_cycle test case %zu failed, wn = %d, ret = %d", - i, - wn, - testcases[i].ret); + for (auto &testcase : testcases) { + u16 wn = gps_adjust_week_cycle(testcase.wn_raw, wn_ref); + EXPECT_EQ(wn, testcase.ret); } } -END_TEST -START_TEST(test_is_leap_year) { +TEST(TestGnssTime, IsLeapYear) { struct is_leap_year_testcase { u16 year; bool ret; @@ -371,18 +293,12 @@ START_TEST(test_is_leap_year) { {.year = 2019, .ret = false}, {.year = 2020, .ret = true}, }; - for (size_t i = 0; - i < sizeof(testcases) / sizeof(struct is_leap_year_testcase); - i++) { - fail_unless(is_leap_year(testcases[i].year) == testcases[i].ret, - "is_leap_year test case %zu failed, year = %d", - i, - testcases[i].year); + for (auto &testcase : testcases) { + EXPECT_EQ(is_leap_year(testcase.year), testcase.ret); } } -END_TEST -START_TEST(test_glo2gps) { +TEST(TestGnssTime, Glo2gps) { struct glo2gps_testcase { glo_time_t glot; gps_time_t ret; @@ -397,178 +313,138 @@ START_TEST(test_glo2gps) { .ret = GPS_TIME_UNKNOWN}, /* GLO time 29th Dec 2000 01:00:00 */ {.glot = {.nt = 364, .n4 = 2, .h = 1, .m = 0, .s = 0}, - .ret = {.wn = 1094, .tow = 424813}}, + .ret = {.tow = 424813, .wn = 1094}}, /* GLO time 30th Dec 2000 01:00:00 */ {.glot = {.nt = 365, .n4 = 2, .h = 1, .m = 0, .s = 0}, - .ret = {.wn = 1094, .tow = 511213}}, + .ret = {.tow = 511213, .wn = 1094}}, /* GLO time 31st Dec 2000 02:00:00 */ {.glot = {.nt = 366, .n4 = 2, .h = 2, .m = 0, .s = 0}, - .ret = {.wn = 1094, .tow = 601213}}, + .ret = {.tow = 601213, .wn = 1094}}, /* GLO time 1st Jan 2001 02:00:00 */ {.glot = {.nt = 367, .n4 = 2, .h = 2, .m = 0, .s = 0}, - .ret = {.wn = 1095, .tow = 82813}}, + .ret = {.tow = 82813, .wn = 1095}}, /* GLO time 2nd Jan 2001 02:00:00 */ {.glot = {.nt = 368, .n4 = 2, .h = 2, .m = 0, .s = 0}, - .ret = {.wn = 1095, .tow = 169213}}, + .ret = {.tow = 169213, .wn = 1095}}, /* GLO time 31st Dec 2009 12:12:12 */ {.glot = {.nt = 731, .n4 = 4, .h = 12, .m = 12, .s = 12}, - .ret = {.wn = 1564, .tow = 378747}}, + .ret = {.tow = 378747, .wn = 1564}}, /* GLO time 31st Dec 2010 12:12:12 */ {.glot = {.nt = 1096, .n4 = 4, .h = 12, .m = 12, .s = 12}, - .ret = {.wn = 1616, .tow = 465147}}, + .ret = {.tow = 465147, .wn = 1616}}, /* GLO time 31st Dec 2011 12:12:12 */ {.glot = {.nt = 1461, .n4 = 4, .h = 12, .m = 12, .s = 12}, - .ret = {.wn = 1668, .tow = 551547}}, + .ret = {.tow = 551547, .wn = 1668}}, /* GLO time 1st Jan 2017 02:59:59 */ {.glot = {.nt = 367, .n4 = 6, .h = 2, .m = 59, .s = 59}, - .ret = {.wn = 1930, .tow = 16}}, + .ret = {.tow = 16, .wn = 1930}}, /* GLO time 1st Jan 2017 02:59:59.5 */ {.glot = {.nt = 367, .n4 = 6, .h = 2, .m = 59, .s = 59.5}, - .ret = {.wn = 1930, .tow = 16.5}}, + .ret = {.tow = 16.5, .wn = 1930}}, /* GLO time 1st Jan 2017 02:59:60 (leap second)*/ {.glot = {.nt = 367, .n4 = 6, .h = 2, .m = 59, .s = 60}, - .ret = {.wn = 1930, .tow = 17}}, + .ret = {.tow = 17, .wn = 1930}}, /* GLO time 1st Jan 2017 02:59:60.5 (leap second)*/ {.glot = {.nt = 367, .n4 = 6, .h = 2, .m = 59, .s = 60.5}, - .ret = {.wn = 1930, .tow = 17.5}}, + .ret = {.tow = 17.5, .wn = 1930}}, /* GLO time 1st Jan 2017 03:00:00 */ {.glot = {.nt = 367, .n4 = 6, .h = 3, .m = 0, .s = 0}, - .ret = {.wn = 1930, .tow = 18}}, + .ret = {.tow = 18, .wn = 1930}}, /* GLO time 1st Jan 2017 03:00:01 */ {.glot = {.nt = 367, .n4 = 6, .h = 3, .m = 0, .s = 1}, - .ret = {.wn = 1930, .tow = 19}}, + .ret = {.tow = 19, .wn = 1930}}, /* GLO time 1st Jan 2017 03:00:02 */ {.glot = {.nt = 367, .n4 = 6, .h = 3, .m = 0, .s = 2}, - .ret = {.wn = 1930, .tow = 20}}, + .ret = {.tow = 20, .wn = 1930}}, /* GLO time 1st Jan 2017 03:01:00 */ {.glot = {.nt = 367, .n4 = 6, .h = 3, .m = 1, .s = 0}, - .ret = {.wn = 1930, .tow = 78}}, + .ret = {.tow = 78, .wn = 1930}}, }; - for (size_t i = 0; i < sizeof(testcases) / sizeof(struct glo2gps_testcase); - i++) { - gps_time_t ret = glo2gps(&testcases[i].glot, /* utc_params = */ NULL); - fail_unless( - ret.wn == testcases[i].ret.wn && ret.tow == testcases[i].ret.tow, - "glo2gps test case %zu failed, got (%d, %f), expected (%d, %f)", - i, - ret.wn, - ret.tow, - testcases[i].ret.wn, - testcases[i].ret.tow); + for (auto &testcase : testcases) { + gps_time_t ret = glo2gps(&testcase.glot, /* utc_params = */ nullptr); + EXPECT_EQ(ret.wn, testcase.ret.wn); + EXPECT_EQ(ret.tow, testcase.ret.tow); if (gps_time_valid(&ret)) { /* convert back to GLO time */ - glo_time_t glo = gps2glo(&ret, NULL); - fail_unless( - glo.n4 == testcases[i].glot.n4 && glo.nt == testcases[i].glot.nt && - glo.h == testcases[i].glot.h && glo.m == testcases[i].glot.m && - within_epsilon(glo.s, testcases[i].glot.s), - "gps2glo test case %zu failed, got (%d,%d,%d,%d,%f), expected " - "(%d,%d,%d,%d,%f)", - i, - glo.n4, - glo.nt, - glo.h, - glo.m, - glo.s, - testcases[i].glot.n4, - testcases[i].glot.nt, - testcases[i].glot.h, - testcases[i].glot.m, - testcases[i].glot.s); + glo_time_t glo = gps2glo(&ret, nullptr); + EXPECT_EQ(glo.n4, testcase.glot.n4); + EXPECT_EQ(glo.nt, testcase.glot.nt); + EXPECT_EQ(glo.h, testcase.glot.h); + EXPECT_EQ(glo.m, testcase.glot.m); + EXPECT_NEAR(glo.s, testcase.glot.s, 1e-5); } } } -END_TEST -START_TEST(test_utc_offset) { +TEST(TestGnssTime, UtcOffset) { struct utc_offset_testcase { gps_time_t t; double dUTC; bool is_lse; } testcases[] = { /* July 1 1981 */ - {.t = {.wn = 77, .tow = 259199.0}, .dUTC = 0.0, .is_lse = false}, - {.t = {.wn = 77, .tow = 259199.5}, .dUTC = 0.0, .is_lse = false}, - {.t = {.wn = 77, .tow = 259200.0}, .dUTC = 0.0, .is_lse = true}, - {.t = {.wn = 77, .tow = 259200.5}, .dUTC = 0.0, .is_lse = true}, - {.t = {.wn = 77, .tow = 259201.0}, .dUTC = 1.0, .is_lse = false}, - {.t = {.wn = 77, .tow = 259202.0}, .dUTC = 1.0, .is_lse = false}, + {.t = {.tow = 259199.0, .wn = 77}, .dUTC = 0.0, .is_lse = false}, + {.t = {.tow = 259199.5, .wn = 77}, .dUTC = 0.0, .is_lse = false}, + {.t = {.tow = 259200.0, .wn = 77}, .dUTC = 0.0, .is_lse = true}, + {.t = {.tow = 259200.5, .wn = 77}, .dUTC = 0.0, .is_lse = true}, + {.t = {.tow = 259201.0, .wn = 77}, .dUTC = 1.0, .is_lse = false}, + {.t = {.tow = 259202.0, .wn = 77}, .dUTC = 1.0, .is_lse = false}, /* Jan 1 2017 */ - {.t = {.wn = 1930, .tow = 16.0}, .dUTC = 17.0, .is_lse = false}, - {.t = {.wn = 1930, .tow = 16.5}, .dUTC = 17.0, .is_lse = false}, - {.t = {.wn = 1930, .tow = 17.0}, .dUTC = 17.0, .is_lse = true}, - {.t = {.wn = 1930, .tow = 17.5}, .dUTC = 17.0, .is_lse = true}, - {.t = {.wn = 1930, .tow = 18.0}, .dUTC = 18.0, .is_lse = false}, - {.t = {.wn = 1930, .tow = 18.5}, .dUTC = 18.0, .is_lse = false}, - {.t = {.wn = 1930, .tow = 19.0}, .dUTC = 18.0, .is_lse = false}, + {.t = {.tow = 16.0, .wn = 1930}, .dUTC = 17.0, .is_lse = false}, + {.t = {.tow = 16.5, .wn = 1930}, .dUTC = 17.0, .is_lse = false}, + {.t = {.tow = 17.0, .wn = 1930}, .dUTC = 17.0, .is_lse = true}, + {.t = {.tow = 17.5, .wn = 1930}, .dUTC = 17.0, .is_lse = true}, + {.t = {.tow = 18.0, .wn = 1930}, .dUTC = 18.0, .is_lse = false}, + {.t = {.tow = 18.5, .wn = 1930}, .dUTC = 18.0, .is_lse = false}, + {.t = {.tow = 19.0, .wn = 1930}, .dUTC = 18.0, .is_lse = false}, }; - for (size_t i = 0; i < sizeof(testcases) / sizeof(struct utc_offset_testcase); - i++) { - double dUTC = get_gps_utc_offset(&testcases[i].t, NULL); - bool is_lse = is_leap_second_event(&testcases[i].t, NULL); + for (auto &testcase : testcases) { + double dUTC = get_gps_utc_offset(&testcase.t, nullptr); + bool is_lse = is_leap_second_event(&testcase.t, nullptr); - fail_unless(dUTC == testcases[i].dUTC && is_lse == testcases[i].is_lse, - "utc_leap_testcase %zu failed, expected (%f,%d) got (%f,%d)", - i, - testcases[i].dUTC, - testcases[i].is_lse, - dUTC, - is_lse); + EXPECT_EQ(dUTC, testcase.dUTC); + EXPECT_EQ(is_lse, testcase.is_lse); /* check that offset from the resulting UTC time back to GPS time matches, * except during the leap second event when the UTC time is ambiguous */ if (!is_lse) { - gps_time_t utc_time = {.wn = testcases[i].t.wn, - .tow = testcases[i].t.tow - dUTC}; - double dGPS = get_utc_gps_offset(&utc_time, NULL); - fail_unless(dGPS == -testcases[i].dUTC, - "utc_leap_testcase inverse %zu failed, expected %f got %f", - i, - -testcases[i].dUTC, - dGPS); + gps_time_t utc_time = {.tow = testcase.t.tow - dUTC, .wn = testcase.t.wn}; + double dGPS = get_utc_gps_offset(&utc_time, nullptr); + EXPECT_EQ(dGPS, -testcase.dUTC); } } } -END_TEST /* test a fictional leap second on 1st Jan 2020 */ /* note also the polynomial correction which shifts the time of effectivity */ -static utc_params_t p_neg_offset = {.a0 = -0.125, - .a1 = 0.0, - .tot.wn = 2080, - .tot.tow = 0, - .t_lse.wn = 2086, - .t_lse.tow = 259218.0 - 0.125, - .dt_ls = 18, - .dt_lsf = 19}; -static utc_params_t p_pos_offset = {.a0 = +0.125, - .a1 = 0.0, - .tot.wn = 2080, - .tot.tow = 0, - .t_lse.wn = 2086, - .t_lse.tow = 259218.0 + 0.125, - .dt_ls = 18, - .dt_lsf = 19}; -static utc_params_t p_pos_trend = { +utc_params_t p_neg_offset = {.a0 = -0.125, + .a1 = 0.0, + .tot = {.tow = 0, .wn = 2080}, + .t_lse = {.tow = 259218.0 - 0.125, .wn = 2086}, + .dt_ls = 18, + .dt_lsf = 19}; +utc_params_t p_pos_offset = {.a0 = +0.125, + .a1 = 0.0, + .tot = {.tow = 0, .wn = 2080}, + .t_lse = {.tow = 259218.0 + 0.125, .wn = 2086}, + .dt_ls = 18, + .dt_lsf = 19}; +utc_params_t p_pos_trend = { .a0 = 0, .a1 = 1e-12, - .tot.wn = 2080, - .tot.tow = 0, - .t_lse.wn = 2086, - .t_lse.tow = 259218.0 + 1e-12 * (6 * WEEK_SECS + 259218.0), + .tot = {.tow = 0, .wn = 2080}, + .t_lse = {.tow = 259218.0 + 1e-12 * (6 * WEEK_SECS + 259218.0), .wn = 2086}, .dt_ls = 18, .dt_lsf = 19}; -static utc_params_t p_neg_trend = { +utc_params_t p_neg_trend = { .a0 = 0, .a1 = -1e-12, - .tot.wn = 2080, - .tot.tow = 0, - .t_lse.wn = 2086, - .t_lse.tow = 259218.0 - 1e-12 * (6 * WEEK_SECS + 259218.0), + .tot = {.tow = 0, .wn = 2080}, + .t_lse = {.tow = 259218.0 - 1e-12 * (6 * WEEK_SECS + 259218.0), .wn = 2086}, .dt_ls = 18, .dt_lsf = 19}; -START_TEST(test_utc_params) { +TEST(TestGnssTime, UtcParams) { struct utc_params_testcase { gps_time_t t; double dUTC; @@ -576,159 +452,130 @@ START_TEST(test_utc_params) { utc_params_t *p; } testcases[] = { /* Jan 1 2020 (constant negative UTC offset) */ - {.t = {.wn = 2086, .tow = 259217.0 - 0.125}, + {.t = {.tow = 259217.0 - 0.125, .wn = 2086}, .dUTC = 18.0 - 0.125, .is_lse = false, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259217.5 - 0.125}, + {.t = {.tow = 259217.5 - 0.125, .wn = 2086}, .dUTC = 18.0 - 0.125, .is_lse = false, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259218.0 - 0.125}, + {.t = {.tow = 259218.0 - 0.125, .wn = 2086}, .dUTC = 18.0 - 0.125, .is_lse = true, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259218.5 - 0.125}, + {.t = {.tow = 259218.5 - 0.125, .wn = 2086}, .dUTC = 18.0 - 0.125, .is_lse = true, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259219.0 - 0.125}, + {.t = {.tow = 259219.0 - 0.125, .wn = 2086}, .dUTC = 19.0 - 0.125, .is_lse = false, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259219.5 - 0.125}, + {.t = {.tow = 259219.5 - 0.125, .wn = 2086}, .dUTC = 19.0 - 0.125, .is_lse = false, .p = &p_neg_offset}, /* Jan 1 2020 (constant positive UTC offset) */ - {.t = {.wn = 2086, .tow = 259217.0 + 0.125}, + {.t = {.tow = 259217.0 + 0.125, .wn = 2086}, .dUTC = 18.0 + 0.125, .is_lse = false, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259217.5 + 0.125}, + {.t = {.tow = 259217.5 + 0.125, .wn = 2086}, .dUTC = 18.0 + 0.125, .is_lse = false, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259218.0 + 0.125}, + {.t = {.tow = 259218.0 + 0.125, .wn = 2086}, .dUTC = 18.0 + 0.125, .is_lse = true, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259218.5 + 0.125}, + {.t = {.tow = 259218.5 + 0.125, .wn = 2086}, .dUTC = 18.0 + 0.125, .is_lse = true, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259219.0 + 0.125}, + {.t = {.tow = 259219.0 + 0.125, .wn = 2086}, .dUTC = 19.0 + 0.125, .is_lse = false, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259219.5 + 0.125}, + {.t = {.tow = 259219.5 + 0.125, .wn = 2086}, .dUTC = 19.0 + 0.125, .is_lse = false, .p = &p_pos_offset}, /* Jan 1 2020 (positive UTC linear correction) */ - {.t = {.wn = 2086, .tow = 259217.0}, + {.t = {.tow = 259217.0, .wn = 2086}, .dUTC = 18.0, .is_lse = false, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259217.5}, + {.t = {.tow = 259217.5, .wn = 2086}, .dUTC = 18.0, .is_lse = false, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259218.0001}, + {.t = {.tow = 259218.0001, .wn = 2086}, .dUTC = 18.0, .is_lse = true, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259218.5}, + {.t = {.tow = 259218.5, .wn = 2086}, .dUTC = 18.0, .is_lse = true, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259219.0001}, + {.t = {.tow = 259219.0001, .wn = 2086}, .dUTC = 19.0, .is_lse = false, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259219.5}, + {.t = {.tow = 259219.5, .wn = 2086}, .dUTC = 19.0, .is_lse = false, .p = &p_pos_trend}, /* Jan 1 2020 (negative UTC linear correction) */ - {.t = {.wn = 2086, .tow = 259217.0}, + {.t = {.tow = 259217.0, .wn = 2086}, .dUTC = 18.0, .is_lse = false, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259217.5}, + {.t = {.tow = 259217.5, .wn = 2086}, .dUTC = 18.0, .is_lse = false, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259218.0}, + {.t = {.tow = 259218.0, .wn = 2086}, .dUTC = 18.0, .is_lse = true, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259218.5}, + {.t = {.tow = 259218.5, .wn = 2086}, .dUTC = 18.0, .is_lse = true, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259219.0}, + {.t = {.tow = 259219.0, .wn = 2086}, .dUTC = 19.0, .is_lse = false, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259219.5}, + {.t = {.tow = 259219.5, .wn = 2086}, .dUTC = 19.0, .is_lse = false, .p = &p_neg_trend}, }; - for (size_t i = 0; i < sizeof(testcases) / sizeof(struct utc_params_testcase); - i++) { - bool is_lse = is_leap_second_event(&testcases[i].t, testcases[i].p); - fail_unless(is_lse == testcases[i].is_lse, - "utc_params_testcase %zu failed, expected LSE=%d got %d", - i, - testcases[i].is_lse, - is_lse); + for (auto &testcase : testcases) { + bool is_lse = is_leap_second_event(&testcase.t, testcase.p); + EXPECT_EQ(is_lse, testcase.is_lse); - double dUTC = get_gps_utc_offset(&testcases[i].t, testcases[i].p); + double dUTC = get_gps_utc_offset(&testcase.t, testcase.p); - fail_unless(within_epsilon(dUTC, testcases[i].dUTC), - "utc_params_testcase %zu failed, expected dUTC=%.16f got %.16f", - i, - testcases[i].dUTC, - dUTC); + EXPECT_NEAR(dUTC, testcase.dUTC, 1e-5); /* check that offset from the resulting UTC time back to GPS time matches, * except during the leap second event when the UTC time is ambiguous */ if (!is_lse) { - gps_time_t utc_time = {.wn = testcases[i].t.wn, - .tow = testcases[i].t.tow - dUTC}; - double dGPS = get_utc_gps_offset(&utc_time, testcases[i].p); - fail_unless( - within_epsilon(dGPS, -testcases[i].dUTC), - "utc_params_testcase inverse %zu failed, expected %.16f got %.16f", - i, - -testcases[i].dUTC, - dGPS); + gps_time_t utc_time = {.tow = testcase.t.tow - dUTC, .wn = testcase.t.wn}; + double dGPS = get_utc_gps_offset(&utc_time, testcase.p); + EXPECT_NEAR(dGPS, -testcase.dUTC, 1e-5); } /* check conversion to GLO and back with the UTC parameters */ - glo_time_t glo_time = gps2glo(&testcases[i].t, testcases[i].p); - gps_time_t converted = glo2gps(&glo_time, testcases[i].p); - fail_unless( - fabs(gpsdifftime(&testcases[i].t, &converted)) < 0.2, - "utc_params_testcase %zu gps2glo2gps failed for (%u/%u %u:%u:%.16f), " - "expected (%d, %f), got (%d, %f)", - i, - glo_time.n4, - glo_time.nt, - glo_time.h, - glo_time.m, - glo_time.s, - testcases[i].t.wn, - testcases[i].t.tow, - converted.wn, - converted.tow); + glo_time_t glo_time = gps2glo(&testcase.t, testcase.p); + gps_time_t converted = glo2gps(&glo_time, testcase.p); + EXPECT_TRUE(fabs(gpsdifftime(&testcase.t, &converted)) < 0.2); } } -END_TEST -START_TEST(test_gps2utc) { +TEST(TestGnssTime, Gps2utc) { /* test leap second on 1st Jan 2020 */ /* note also the polynomial correction which shifts the time of effectivity */ @@ -738,7 +585,7 @@ START_TEST(test_gps2utc) { utc_params_t *p; } testcases[] = { /* July 1 1981 */ - {.t = {.wn = 77, .tow = 259199.0}, + {.t = {.tow = 259199.0, .wn = 77}, .u = {.year = 1981, .month = 6, .month_day = 30, @@ -746,8 +593,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 77, .tow = 259199.5}, + .p = nullptr}, + {.t = {.tow = 259199.5, .wn = 77}, .u = {.year = 1981, .month = 6, .month_day = 30, @@ -755,8 +602,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.5}, - .p = NULL}, - {.t = {.wn = 77, .tow = 259200.0}, + .p = nullptr}, + {.t = {.tow = 259200.0, .wn = 77}, .u = {.year = 1981, .month = 6, .month_day = 30, @@ -764,8 +611,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 60, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 77, .tow = 259200.5}, + .p = nullptr}, + {.t = {.tow = 259200.5, .wn = 77}, .u = {.year = 1981, .month = 6, .month_day = 30, @@ -773,8 +620,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 60, .second_frac = 0.5}, - .p = NULL}, - {.t = {.wn = 77, .tow = 259201.0}, + .p = nullptr}, + {.t = {.tow = 259201.0, .wn = 77}, .u = {.year = 1981, .month = 7, .month_day = 01, @@ -782,9 +629,9 @@ START_TEST(test_gps2utc) { .minute = 00, .second_int = 00, .second_frac = 0.0}, - .p = NULL}, + .p = nullptr}, /* Jan 1 2017 */ - {.t = {.wn = 1930, .tow = 16.0}, + {.t = {.tow = 16.0, .wn = 1930}, .u = {.year = 2016, .month = 12, .month_day = 31, @@ -792,8 +639,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 1930, .tow = 16.5}, + .p = nullptr}, + {.t = {.tow = 16.5, .wn = 1930}, .u = {.year = 2016, .month = 12, .month_day = 31, @@ -801,8 +648,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.5}, - .p = NULL}, - {.t = {.wn = 1930, .tow = 17.0}, + .p = nullptr}, + {.t = {.tow = 17.0, .wn = 1930}, .u = {.year = 2016, .month = 12, .month_day = 31, @@ -810,8 +657,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 60, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 1930, .tow = 17.5}, + .p = nullptr}, + {.t = {.tow = 17.5, .wn = 1930}, .u = {.year = 2016, .month = 12, .month_day = 31, @@ -819,8 +666,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 60, .second_frac = 0.5}, - .p = NULL}, - {.t = {.wn = 1930, .tow = 18.0}, + .p = nullptr}, + {.t = {.tow = 18.0, .wn = 1930}, .u = {.year = 2017, .month = 01, .month_day = 01, @@ -828,9 +675,9 @@ START_TEST(test_gps2utc) { .minute = 00, .second_int = 00, .second_frac = 0.0}, - .p = NULL}, + .p = nullptr}, /* Jan 8 2017 */ - {.t = {.wn = 1931, .tow = 17.0}, + {.t = {.tow = 17.0, .wn = 1931}, .u = {.year = 2017, .month = 01, .month_day = 7, @@ -838,8 +685,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 1931, .tow = 17.5}, + .p = nullptr}, + {.t = {.tow = 17.5, .wn = 1931}, .u = {.year = 2017, .month = 01, .month_day = 7, @@ -847,8 +694,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 0.5}, - .p = NULL}, - {.t = {.wn = 1931, .tow = 18 - 6e-11}, + .p = nullptr}, + {.t = {.tow = 18 - 6e-11, .wn = 1931}, .u = {.year = 2017, .month = 01, .month_day = 7, @@ -856,8 +703,8 @@ START_TEST(test_gps2utc) { .minute = 59, .second_int = 59, .second_frac = 1 - 6e-11}, - .p = NULL}, - {.t = {.wn = 1931, .tow = 18 - 5e-11}, + .p = nullptr}, + {.t = {.tow = 18 - 5e-11, .wn = 1931}, .u = {.year = 2017, .month = 01, .month_day = 8, @@ -865,8 +712,8 @@ START_TEST(test_gps2utc) { .minute = 00, .second_int = 00, .second_frac = 0.0}, - .p = NULL}, - {.t = {.wn = 1931, .tow = 18.0}, + .p = nullptr}, + {.t = {.tow = 18.0, .wn = 1931}, .u = {.year = 2017, .month = 01, .month_day = 8, @@ -874,10 +721,10 @@ START_TEST(test_gps2utc) { .minute = 00, .second_int = 00, .second_frac = 0.0}, - .p = NULL}, + .p = nullptr}, /* Jan 1 2020 (leap second announced in utc_params_t above, constant negative offset) */ - {.t = {.wn = 2086, .tow = 259217.0 - 0.125}, + {.t = {.tow = 259217.0 - 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -886,7 +733,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.0}, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259217.5 - 0.125}, + {.t = {.tow = 259217.5 - 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -895,7 +742,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.5}, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259218.0 - 0.125}, + {.t = {.tow = 259218.0 - 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -904,7 +751,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.0}, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259218.5 - 0.125}, + {.t = {.tow = 259218.5 - 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -913,7 +760,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.5}, .p = &p_neg_offset}, - {.t = {.wn = 2086, .tow = 259219.0 - 0.125}, + {.t = {.tow = 259219.0 - 0.125, .wn = 2086}, .u = {.year = 2020, .month = 01, .month_day = 01, @@ -924,7 +771,7 @@ START_TEST(test_gps2utc) { .p = &p_neg_offset}, /* Jan 1 2020 (leap second announced in utc_params_t above, constant positive offset) */ - {.t = {.wn = 2086, .tow = 259217.0 + 0.125}, + {.t = {.tow = 259217.0 + 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -933,7 +780,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.0}, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259217.5 + 0.125}, + {.t = {.tow = 259217.5 + 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -942,7 +789,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.5}, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259218.0 + 0.125}, + {.t = {.tow = 259218.0 + 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -951,7 +798,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.0}, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259218.5 + 0.125}, + {.t = {.tow = 259218.5 + 0.125, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -960,7 +807,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.5}, .p = &p_pos_offset}, - {.t = {.wn = 2086, .tow = 259219.0 + 0.125}, + {.t = {.tow = 259219.0 + 0.125, .wn = 2086}, .u = {.year = 2020, .month = 01, .month_day = 01, @@ -971,7 +818,7 @@ START_TEST(test_gps2utc) { .p = &p_pos_offset}, /* Jan 1 2020 (leap second announced in utc_params_t above, positive UTC linear correction) */ - {.t = {.wn = 2086, .tow = 259217.0}, + {.t = {.tow = 259217.0, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -980,7 +827,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.0}, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259217.5}, + {.t = {.tow = 259217.5, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -989,7 +836,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.5}, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259218.0}, + {.t = {.tow = 259218.0, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -998,7 +845,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.0}, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259218.5}, + {.t = {.tow = 259218.5, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -1007,7 +854,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.5}, .p = &p_pos_trend}, - {.t = {.wn = 2086, .tow = 259219.00001}, + {.t = {.tow = 259219.00001, .wn = 2086}, .u = {.year = 2020, .month = 01, .month_day = 01, @@ -1018,7 +865,7 @@ START_TEST(test_gps2utc) { .p = &p_pos_trend}, /* Jan 1 2020 (leap second announced in utc_params_t above, negative UTC linear correction) */ - {.t = {.wn = 2086, .tow = 259217.0}, + {.t = {.tow = 259217.0, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -1027,7 +874,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.0}, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259217.5}, + {.t = {.tow = 259217.5, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -1036,7 +883,7 @@ START_TEST(test_gps2utc) { .second_int = 59, .second_frac = 0.5}, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259218.0}, + {.t = {.tow = 259218.0, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -1045,7 +892,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.0}, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259218.5}, + {.t = {.tow = 259218.5, .wn = 2086}, .u = {.year = 2019, .month = 12, .month_day = 31, @@ -1054,7 +901,7 @@ START_TEST(test_gps2utc) { .second_int = 60, .second_frac = 0.5}, .p = &p_neg_trend}, - {.t = {.wn = 2086, .tow = 259219.0}, + {.t = {.tow = 259219.0, .wn = 2086}, .u = {.year = 2020, .month = 01, .month_day = 01, @@ -1066,65 +913,29 @@ START_TEST(test_gps2utc) { }; utc_tm u; - for (size_t i = 0; i < sizeof(testcases) / sizeof(struct gps2utc_testcase); - i++) { - utc_tm expected = testcases[i].u; - gps2utc(&testcases[i].t, &u, testcases[i].p); - - fail_unless(u.year == expected.year, - "gps2utc_testcase %zu failed, got year %d expected %d", - i, - u.year, - expected.year); - fail_unless(u.month == expected.month, - "gps2utc_testcase %zu failed, got month %d expected %d", - i, - u.month, - expected.month); - fail_unless(u.month_day == expected.month_day, - "gps2utc_testcase %zu failed, got day %d expected %d", - i, - u.month_day, - expected.month_day); - fail_unless(u.hour == expected.hour, - "gps2utc_testcase %zu failed, got hour %d expected %d", - i, - u.hour, - expected.hour); - fail_unless(u.minute == expected.minute, - "gps2utc_testcase %zu failed, got minute %d expected %d", - i, - u.minute, - expected.minute); - fail_unless(within_epsilon(u.second_int + u.second_frac, - expected.second_int + expected.second_frac), - "gps2utc_testcase %zu failed, got second %.16f expected %.16f", - i, - u.second_int + u.second_frac, - expected.second_int + expected.second_frac); - fail_unless(u.second_frac < 1, - "gps2utc_testcase %zu failed, got second_frac %g expected <1", - i, - u.second_frac); - - const gps_time_t *g = &testcases[i].t; + for (auto &testcase : testcases) { + utc_tm expected = testcase.u; + gps2utc(&testcase.t, &u, testcase.p); + + EXPECT_EQ(u.year, expected.year); + EXPECT_EQ(u.month, expected.month); + EXPECT_EQ(u.month_day, expected.month_day); + EXPECT_EQ(u.hour, expected.hour); + EXPECT_EQ(u.minute, expected.minute); + EXPECT_NEAR(u.second_int + u.second_frac, + expected.second_int + expected.second_frac, + 1e-5); + EXPECT_LT(u.second_frac, 1); + + const gps_time_t *g = &testcase.t; gps_time_t converted_gps; - utc2gps(&u, &converted_gps, testcases[i].p); - fail_unless(converted_gps.wn == g->wn, - "gps2utc_testcase %zu failed, got wn %u expected %u", - i, - converted_gps.wn, - g->wn); - fail_unless(within_epsilon(converted_gps.tow, g->tow), - "gps2utc_testcase %zu failed, got tow %.16f expected %.16f", - i, - converted_gps.tow, - g->tow); + utc2gps(&u, &converted_gps, testcase.p); + EXPECT_EQ(converted_gps.wn, g->wn); + EXPECT_NEAR(converted_gps.tow, g->tow, 1e-5); } } -END_TEST -START_TEST(test_time_conversions) { +TEST(TestGnssTime, TimeConversions) { gps_time_t testcases[] = { {567890.0, 1234}, {567890.5, 1234}, @@ -1138,46 +949,35 @@ START_TEST(test_time_conversions) { {18, 1930} /* around Jan 2017 leap second */ }; const double tow_tol = 1e-6; - for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { + for (auto &testcase : testcases) { /* test gps -> mjd -> gps */ - double mjd = gps2mjd(&testcases[i]); + double mjd = gps2mjd(&testcase); gps_time_t ret = mjd2gps(mjd); - fail_unless(fabs(gpsdifftime(&testcases[i], &ret)) < tow_tol, - "gps2mjd2gps test case %zu failed", - i); + EXPECT_LT(fabs(gpsdifftime(&testcase, &ret)), tow_tol); /* test mjd -> date -> mjd */ s32 year, month, day, hour, min; double sec; mjd2date(mjd, &year, &month, &day, &hour, &min, &sec); - fail_unless( - fabs(date2mjd(year, month, day, hour, min, sec) - mjd) < tow_tol, - "mjd2date2mjd test case %zu failed", - i); + EXPECT_LT(fabs(date2mjd(year, month, day, hour, min, sec) - mjd), tow_tol); /* test mjd -> utc -> mjd */ utc_tm utc = mjd2utc(mjd); - fail_unless( - fabs(utc2mjd(&utc) - mjd) < tow_tol, "utc2mjd test case %zu failed", i); + EXPECT_LT(fabs(utc2mjd(&utc) - mjd), tow_tol); /* test gps -> date -> gps */ - gps2date(&testcases[i], &year, &month, &day, &hour, &min, &sec); + gps2date(&testcase, &year, &month, &day, &hour, &min, &sec); ret = date2gps(year, month, day, hour, min, sec); - fail_unless(fabs(gpsdifftime(&testcases[i], &ret)) < tow_tol, - "gps2date2gps test case %zu failed", - i); + EXPECT_LT(fabs(gpsdifftime(&testcase, &ret)), tow_tol); /* test utc -> date -> utc */ utc2date(&utc, &year, &month, &day, &hour, &min, &sec); utc = date2utc(year, month, day, hour, min, sec); - fail_unless(fabs(utc2mjd(&utc) - mjd) < tow_tol, - "utc2date2utc test case %zu failed", - i); + EXPECT_LT(fabs(utc2mjd(&utc) - mjd), tow_tol); } } -END_TEST -START_TEST(test_round_to_epoch) { +TEST(TestGnssTime, RoundToEpoch) { const double soln_freq = 10.0; gps_time_t testcases[] = { @@ -1194,14 +994,11 @@ START_TEST(test_round_to_epoch) { for (size_t i = 0; i < ARRAY_SIZE(testcases); ++i) { gps_time_t rounded = round_to_epoch(&testcases[i], soln_freq); - fail_unless(within_epsilon(gpsdifftime(&rounded, &expectations[i]), 0.0), - "round_to_epoch failed %zu", - i); + EXPECT_NEAR(gpsdifftime(&rounded, &expectations[i]), 0.0, 1e-5); } } -END_TEST -START_TEST(test_floor_to_epoch) { +TEST(TestGnssTime, FloorToEpoch) { const double soln_freq = 10.0; gps_time_t testcases[] = { @@ -1218,35 +1015,8 @@ START_TEST(test_floor_to_epoch) { for (size_t i = 0; i < ARRAY_SIZE(testcases); ++i) { gps_time_t rounded = floor_to_epoch(&testcases[i], soln_freq); - fail_unless(within_epsilon(gpsdifftime(&rounded, &expectations[i]), 0.0), - "floor_to_epoch failed %zu", - i); + EXPECT_NEAR(gpsdifftime(&rounded, &expectations[i]), 0.0, 1e-5); } } -END_TEST - -Suite *gnss_time_test_suite(void) { - Suite *s = suite_create("Time handling"); - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_gpsdifftime); - tcase_add_test(tc_core, test_gpsdifftime_week_second); - tcase_add_test(tc_core, test_long_gps_time_diff); - tcase_add_test(tc_core, test_normalize_gps_time); - tcase_add_test(tc_core, test_normalize_gps_time_duration); - tcase_add_test(tc_core, test_gps_time_match_weeks); - tcase_add_test(tc_core, test_gps_adjust_week_cycle); - tcase_add_test(tc_core, test_is_leap_year); - tcase_add_test(tc_core, test_utc_offset); - tcase_add_test(tc_core, test_utc_params); - tcase_add_test(tc_core, test_gps2utc); - tcase_add_test(tc_core, test_glo2gps); - tcase_add_test(tc_core, test_gps2utc_time); - tcase_add_test(tc_core, test_gps2utc_date); - tcase_add_test(tc_core, test_time_conversions); - tcase_add_test(tc_core, test_round_to_epoch); - tcase_add_test(tc_core, test_floor_to_epoch); - suite_add_tcase(s, tc_core); - - return s; -} +} // namespace diff --git a/tests/test_gnss_time_cpp.cc b/tests/test_gnss_time_cpp.cc new file mode 100644 index 0000000..56c97ea --- /dev/null +++ b/tests/test_gnss_time_cpp.cc @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +#include "check_utils.h" + +#define GPS_TIME_TOL (1.0e-9) + +namespace { + +TEST(GnssTimeCppOperators, GpstimeComparisonOps) { + gps_time_t a = {567890.0, 1234}; + gps_time_t b = {567890.5, 1234}; + gps_time_t c = {567890.0, 1234}; + gps_time_t time_before_rollover = {WEEK_SECS - 1.0, 1234}; + gps_time_t time_after_rollover{1.0, 1234}; + gps_time_t time_unknown_wn_only{1.0, WN_UNKNOWN}; + gps_time_t time_unknown_tow_only{TOW_UNKNOWN, 1234}; + gps_time_t time_unknown_wn_a{0.0, WN_UNKNOWN}; + gps_time_t time_unknown_wn_b{WEEK_SECS - 1.0, WN_UNKNOWN}; + gps_time_t time_unknown_tow_a{TOW_UNKNOWN, 1234}; + gps_time_t time_unknown_tow_b{TOW_UNKNOWN, 1235}; + EXPECT_LE(a, b); + EXPECT_GT(b, a); + EXPECT_NE(a, b); + EXPECT_NE(b, a); + EXPECT_EQ(a, c); + EXPECT_LE(a, b); + EXPECT_GE(b, a); + EXPECT_GE(a, c); + EXPECT_LE(a, c); + EXPECT_EQ(GPS_TIME_UNKNOWN, GPS_TIME_UNKNOWN); + EXPECT_NE(time_before_rollover, GPS_TIME_UNKNOWN); + EXPECT_NE(GPS_TIME_UNKNOWN, time_before_rollover); + EXPECT_NE(time_after_rollover, GPS_TIME_UNKNOWN); + EXPECT_NE(GPS_TIME_UNKNOWN, time_after_rollover); + EXPECT_NE(time_unknown_wn_only, GPS_TIME_UNKNOWN); + EXPECT_NE(GPS_TIME_UNKNOWN, time_unknown_wn_only); + EXPECT_NE(time_unknown_tow_only, GPS_TIME_UNKNOWN); + EXPECT_NE(GPS_TIME_UNKNOWN, time_unknown_tow_only); + EXPECT_NE(time_unknown_wn_a, time_unknown_wn_b); + EXPECT_NE(time_unknown_wn_b, time_unknown_wn_a); + EXPECT_NE(time_unknown_tow_a, time_unknown_tow_b); + EXPECT_NE(time_unknown_tow_b, time_unknown_tow_a); +} + +TEST(GnssTimeCppOperators, GpstimeOperatorMinus) { + gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; + EXPECT_TRUE(fabs((b - a) - 0.5) < GPS_TIME_TOL); + EXPECT_TRUE(fabs((b - 0.5) - a) < GPS_TIME_TOL); +} + +TEST(GnssTimeCppOperators, GpstimeOperatorPlus) { + gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; + EXPECT_TRUE(fabs((a + 0.5) - b) < GPS_TIME_TOL); + EXPECT_TRUE(fabs((0.5 + a) - b) < GPS_TIME_TOL); +} + +TEST(GnssTimeCppOperators, GpstimeOperatorPlusAssignment) { + gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; + a += 0.5; + EXPECT_TRUE(fabs(a - b) < GPS_TIME_TOL); +} + +TEST(GnssTimeCppOperators, GpstimeOperatorMinusAssignment) { + gps_time_t a = {567890.0, 1234}, b = {567890.5, 1234}; + b -= 0.5; + EXPECT_TRUE(fabs(a - b) < GPS_TIME_TOL); +} + +} // namespace diff --git a/tests/check_ionosphere.c b/tests/test_ionosphere.cc similarity index 52% rename from tests/check_ionosphere.c rename to tests/test_ionosphere.cc index 51432ff..8f3758a 100644 --- a/tests/check_ionosphere.c +++ b/tests/test_ionosphere.cc @@ -10,15 +10,15 @@ * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. */ -#include +#include #include #include #include -#include "check_suites.h" +namespace { -START_TEST(test_calc_ionosphere) { - gps_time_t t = {.wn = 1875, .tow = 479820}; +TEST(TestIonosphere, CalcIonosphere) { + gps_time_t t = {.tow = 479820, .wn = 1875}; ionosphere_t i = {.a0 = 0.1583e-7, .a1 = -0.7451e-8, .a2 = -0.5960e-7, @@ -36,11 +36,7 @@ START_TEST(test_calc_ionosphere) { double d_l1 = calc_ionosphere(&t, lat_u, lon_u, a, e, &i); double d_err = fabs(d_l1 - d_true); - fail_unless( - d_err < d_tol, - "Distance didn't match hardcoded correct value %0.5f. Saw: %.5f\n", - d_true, - d_l1); + EXPECT_LT(d_err, d_tol); t.wn = 1042; t.tow = 593100; @@ -61,11 +57,7 @@ START_TEST(test_calc_ionosphere) { d_l1 = calc_ionosphere(&t, lat_u, lon_u, a, e, &i); d_err = fabs(d_l1 - d_true); - fail_unless( - d_err < d_tol, - "Distance didn't match hardcoded correct values %0.5f. Saw: %.5f\n", - d_true, - d_l1); + EXPECT_LT(d_err, d_tol); t.wn = 1042; t.tow = 345600; @@ -86,15 +78,10 @@ START_TEST(test_calc_ionosphere) { d_l1 = calc_ionosphere(&t, lat_u, lon_u, a, e, &i); d_err = fabs(d_l1 - d_true); - fail_unless( - d_err < d_tol, - "Distance didn't match hardcoded correct values %0.5f. Saw: %.5f\n", - d_true, - d_l1); + EXPECT_LT(d_err, d_tol); } -END_TEST -START_TEST(test_decode_iono_parameters) { +TEST(TestIonosphere, DecodeIonoParameters) { #define tol 1e-12 struct { u32 frame_words[8]; @@ -122,56 +109,14 @@ START_TEST(test_decode_iono_parameters) { }}; ionosphere_t i; decode_iono_parameters(t_case.frame_words, &i); - fail_unless(fabs(i.a0 - t_case.result.a0) < tol, - "alfa 0 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.a0, - t_case.result.a0, - tol); - fail_unless(fabs(i.a1 - t_case.result.a1) < tol, - "alfa 1 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.a1, - t_case.result.a1, - tol); - fail_unless(fabs(i.a2 - t_case.result.a2) < tol, - "alfa 2 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.a2, - t_case.result.a2, - tol); - fail_unless(fabs(i.a3 - t_case.result.a3) < tol, - "alfa 3 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.a3, - t_case.result.a3, - tol); - fail_unless(fabs(i.b0 - t_case.result.b0) < tol, - "beta 0 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.b0, - t_case.result.b0, - tol); - fail_unless(fabs(i.b1 - t_case.result.b1) < tol, - "beta 1 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.b1, - t_case.result.b1, - tol); - fail_unless(fabs(i.b2 - t_case.result.b2) < tol, - "beta 2 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.b2, - t_case.result.b2, - tol); - fail_unless(fabs(i.b3 - t_case.result.b3) < tol, - "beta 3 == %30.20f, expected %30.20f, tolerance = %30.20f", - i.b3, - t_case.result.b3, - tol); + EXPECT_NEAR(i.a0, t_case.result.a0, tol); + EXPECT_NEAR(i.a1, t_case.result.a1, tol); + EXPECT_NEAR(i.a2, t_case.result.a2, tol); + EXPECT_NEAR(i.a3, t_case.result.a3, tol); + EXPECT_NEAR(i.b0, t_case.result.b0, tol); + EXPECT_NEAR(i.b1, t_case.result.b1, tol); + EXPECT_NEAR(i.b2, t_case.result.b2, tol); + EXPECT_NEAR(i.b3, t_case.result.b3, tol); } -END_TEST -Suite *ionosphere_suite(void) { - Suite *s = suite_create("Ionosphere"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_calc_ionosphere); - tcase_add_test(tc_core, test_decode_iono_parameters); - suite_add_tcase(s, tc_core); - - return s; -} +} // namespace diff --git a/tests/test_linear_algebra.cc b/tests/test_linear_algebra.cc new file mode 100644 index 0000000..9137d7b --- /dev/null +++ b/tests/test_linear_algebra.cc @@ -0,0 +1,713 @@ +#include +#include +#include +#include +#include + +#include "check_utils.h" + +#define LINALG_TOL 1e-9 +#define LINALG_NUM 22 +#define MATRIX_MIN -1e3 +#define MATRIX_MAX 1e3 +#define MSIZE_MAX 64 + +/* TODO: matrix_multiply, matrix_add_sc, matrix_copy, all vector functions */ + +namespace { + +TEST(TestLinearAlgebra, MatrixInverse2x2) { + u32 i, j, t; + double A[4]; + double B[4]; + double I[4]; + + Random rand{}; + /* 2x2 inverses */ + for (t = 0; t < LINALG_NUM; t++) { + do { + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + A[2 * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + } while (matrix_inverse(2, A, B) < 0); + matrix_multiply(2, 2, 2, A, B, I); + EXPECT_LT(fabs(I[0] - 1), LINALG_TOL) + << "Matrix differs from identity: " << I[0]; + EXPECT_LT(fabs(I[3] - 1), LINALG_TOL) + << "Matrix differs from identity: " << I[3]; + } + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + if (j == 0) { + A[2 * i + j] = 22; + } else { + A[2 * i + j] = 1; + } + } + } + s32 mi = matrix_inverse(2, A, B); + EXPECT_LT(mi, 0) << "Singular matrix not detected."; +} + +TEST(TestLinearAlgebra, MatrixInverse3x3) { + u32 i, j, t; + double A[9]; + double B[9]; + double I[9]; + + Random rand{}; + /* 3x3 inverses */ + for (t = 0; t < LINALG_NUM; t++) { + do { + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + A[3 * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + } while (matrix_inverse(3, A, B) < 0); + matrix_multiply(3, 3, 3, A, B, I); + EXPECT_NEAR(I[0], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[0]; + EXPECT_NEAR(I[4], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[4]; + EXPECT_NEAR(I[8], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[8]; + } + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + if (j == 0) { + A[3 * i + j] = 33; + } else { + A[3 * i + j] = 1; + } + } + } + s32 mi = matrix_inverse(3, A, B); + EXPECT_LT(mi, 0) << "Singular matrix not detected."; +} + +TEST(TestLinearAlgebra, MatrixInverse4x4) { + u32 i, j, t; + double A[16]; + double B[16]; + double I[16]; + + Random rand{}; + /* 4x4 inverses */ + for (t = 0; t < LINALG_NUM; t++) { + do { + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + A[4 * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + } while (matrix_inverse(4, A, B) < 0); + matrix_multiply(4, 4, 4, A, B, I); + EXPECT_NEAR(I[0], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[0]; + EXPECT_NEAR(I[5], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[5]; + EXPECT_NEAR(I[10], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[10]; + EXPECT_NEAR(I[15], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[15]; + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + if (j == 0) { + A[4 * i + j] = 44; + } else { + A[4 * i + j] = 1; + } + } + } + s32 mi = matrix_inverse(4, A, B); + EXPECT_LT(mi, 0) << "Singular matrix not detected."; +} + +TEST(TestLinearAlgebra, MatrixInverse5x5) { + u32 i, j, t; + double A[25]; + double B[25]; + double I[25]; + + Random rand{}; + /* 5x5 inverses */ + for (t = 0; t < LINALG_NUM; t++) { + do { + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + A[5 * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + } while (matrix_inverse(5, A, B) < 0); + matrix_multiply(5, 5, 5, A, B, I); + EXPECT_NEAR(I[0], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[0]; + EXPECT_NEAR(I[6], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[6]; + EXPECT_NEAR(I[12], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[12]; + EXPECT_NEAR(I[18], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[18]; + EXPECT_NEAR(I[24], 1, LINALG_TOL) + << "Matrix differs from identity: " << I[24]; + } + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + if (j == 0) { + A[5 * i + j] = 55; + } else { + A[5 * i + j] = 1; + } + } + } + s32 mi = matrix_inverse(5, A, B); + EXPECT_LT(mi, 0) << "Singular matrix not detected."; +} + +TEST(TestLinearAlgebra, MatrixEye) { + double M[10][10]; + + matrix_eye(10, &M[0][0]); + + for (u32 i = 0; i < 10; i++) { + for (u32 j = 0; j < 10; j++) { + if (i == j) { + EXPECT_EQ(M[i][j], 1) << "Identity diagonal element != 1"; + } else { + EXPECT_EQ(M[i][j], 0) << "Identity off-diagonal element != 0"; + } + } + } +} + +TEST(TestLinearAlgebra, MatrixTriu) { + double M[4][4] = { + {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + double M_[4][4] = {{1, 2, 3, 4}, {0, 6, 7, 8}, {0, 0, 11, 12}, {0, 0, 0, 16}}; + + matrix_triu(4, &M[0][0]); + + for (u32 i = 0; i < 4; i++) { + for (u32 j = 0; j < 4; j++) { + EXPECT_EQ(M[i][j], M_[i][j]) << "triu result != test matrix"; + } + } +} + +TEST(TestLinearAlgebra, MatrixUdu1) { + double M[4][4] = {{100, 145, 121, 16}, + {145, 221, 183, 24}, + {121, 183, 199, 28}, + {16, 24, 28, 4}}; + + double U[4][4] = {{0}}; + double D[4] = {0}; + + matrix_udu(4, &M[0][0], &U[0][0], D); + + double U_[4][4] = {{1, 2, 3, 4}, {0, 1, 5, 6}, {0, 0, 1, 7}, {0, 0, 0, 1}}; + + double D_[4] = {1, 2, 3, 4}; + + for (u32 i = 0; i < 4; i++) { + for (u32 j = 0; j < 4; j++) { + EXPECT_EQ(U[i][j], U_[i][j]) << "U result != test matrix"; + } + } + for (u32 i = 0; i < 4; i++) { + EXPECT_EQ(D[i], D_[i]) << "D result != test D"; + } +} + +TEST(TestLinearAlgebra, MatrixUdu2) { + Random rand{}; + u32 n = rand.sizerand(MSIZE_MAX); + double M[n][n]; + double M_orig[n][n]; + + for (u32 i = 0; i < n; i++) { + for (u32 j = 0; j <= i; j++) { + M[i][j] = M[j][i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + + /* Square the random matrix to ensure it is positive semi-definite. */ + matrix_multiply(n, n, n, &M[0][0], &M[0][0], &M_orig[0][0]); + memcpy(M, M_orig, n * n * sizeof(double)); + + double U[n][n]; + memset(U, 0, n * n * sizeof(double)); + double D[n]; + memset(D, 0, n * sizeof(double)); + + matrix_udu(n, &M[0][0], &U[0][0], D); + + /* Check U is unit upper triangular. */ + for (u32 i = 0; i < n; i++) { + for (u32 j = 0; j < n; j++) { + if (i == j) { + EXPECT_NEAR(U[i][j], 1, LINALG_TOL) + << "U diagonal element != 1 (was " << M[i][j] << ")"; + } + if (i > j) { + EXPECT_LT(fabs(U[i][j]), LINALG_TOL) << "U lower triangle element != 0"; + } + } + } + + /* Check reconstructed matrix is correct. */ + double M_[n][n]; + memset(M_, 0, n * n * sizeof(double)); + matrix_reconstruct_udu(n, &U[0][0], D, &M_[0][0]); + + for (u32 i = 0; i < n; i++) { + for (u32 j = 0; j < n; j++) { + EXPECT_NEAR(M_orig[i][j], M_[i][j], LINALG_TOL * MATRIX_MAX) + << "reconstructed result != original matrix, delta[" << i << "][" << j + << "] = " << fabs(M_orig[i][j] - M_[i][j]); + } + } +} + +TEST(TestLinearAlgebra, MatrixUdu3) { + double M[3][3] = { + {36, 49, 9}, + {49, 77, 15}, + {9, 15, 3}, + }; + + double U[3][3] = {{0}}; + double D[3] = {0}; + + matrix_udu(3, &M[0][0], &U[0][0], D); + + /* Check using formula for 3x3 matrix on Gibbs p. 393 */ + EXPECT_EQ(D[2], M[2][2]) << "D[2] incorrect"; + EXPECT_EQ(U[2][1], M[2][1] / D[2]) << "U[2][1] incorrect"; + EXPECT_EQ(U[2][0], M[2][0] / D[2]) << "U[2][0] incorrect"; + EXPECT_EQ(D[1], (M[1][1] - U[2][1] * U[2][1] * D[2])) << "D[1] incorrect"; + EXPECT_EQ(U[1][0], ((M[1][0] - U[2][0] * U[2][1] * D[2]) / D[1])) + << "U[1][0] incorrect"; + EXPECT_EQ(D[0], + (M[0][0] - U[1][0] * U[1][0] * D[1] - U[2][0] * U[2][0] * D[2])) + << "D[0] incorrect"; +} + +TEST(TestLinearAlgebra, MatrixReconstructUdu) { + double U[4][4] = {{1, 2, 3, 4}, {0, 1, 5, 6}, {0, 0, 1, 7}, {0, 0, 0, 1}}; + + double D[4] = {1, 2, 3, 4}; + + double M[4][4] = {{0}}; + + double M_[4][4] = {{100, 145, 121, 16}, + {145, 221, 183, 24}, + {121, 183, 199, 28}, + {16, 24, 28, 4}}; + + matrix_reconstruct_udu(4, &U[0][0], D, &M[0][0]); + + for (u32 i = 0; i < 4; i++) { + for (u32 j = 0; j < 4; j++) { + EXPECT_EQ(M[i][j], M_[i][j]) << "reconstructed result != test matrix"; + } + } +} + +TEST(TestLinearAlgebra, MatrixAddSc) { + u32 i, j, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + u32 m = rand.sizerand(MSIZE_MAX); + double A[n * m]; + double B[m * n]; + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + A[m * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + matrix_add_sc(n, m, A, A, -1, B); + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + EXPECT_LT(fabs(B[m * i + j]), LINALG_TOL) + << "Matrix differs from zero: " << B[m * i + j]; + } + } + } +} + +TEST(TestLinearAlgebra, MatrixCopy) { + u32 i, j, t; + double tmp; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + u32 m = rand.sizerand(MSIZE_MAX); + double A[n * m]; + double B[m * n]; + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + A[m * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + matrix_copy(n, m, A, B); + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + tmp = fabs(B[m * i + j] - A[m * i + j]); + EXPECT_LT(tmp, LINALG_TOL) << "Matrix differs from zero: %lf" << tmp; + } + } + } +} + +TEST(TestLinearAlgebra, MatrixTranspose) { + u32 i, j, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + u32 m = rand.sizerand(MSIZE_MAX); + double A[n * m]; + double B[m * n]; + double C[n * m]; + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + A[m * i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + } + matrix_transpose(n, m, A, B); + matrix_transpose(m, n, B, C); + for (i = 0; i < n; i++) { + for (j = 0; j < m; j++) { + EXPECT_NEAR(A[m * i + j], C[m * i + j], LINALG_TOL) + << "Matrix element differs from original: " << A[m * i + j] << ", " + << C[m * i + j]; + } + } + } +} + +TEST(TestLinearAlgebra, VectorDot) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + u32 mid; + if (n % 2 == 0) { + mid = n / 2; + } else { + mid = (n - 1) / 2; + } + + double A[n], B[n]; + for (i = 0; i < n; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e20; + if (i < mid) { + B[n - i - 1] = -A[i]; + } else { + B[n - i - 1] = A[i]; + } + } + double dot = vector_dot(n, A, B); + if (n % 2 == 0) { + EXPECT_LT(fabs(dot), LINALG_TOL) + << "Dot product differs from zero: " << vector_dot(n, A, B); + } else { + EXPECT_NEAR(dot, A[mid] * B[mid], LINALG_TOL) + << "Dot product differs from square of middle element " + "%lf: %lf (%lf)" + << A[mid] * B[mid] << dot << dot - A[mid] * B[mid]; + } + } +} + +TEST(TestLinearAlgebra, VectorMean) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double A[n]; + double test = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e22; + for (i = 0; i < n; i++) { + A[i] = test + i; + } + double mean = vector_mean(n, A); + double expect = test + (n - 1.0) / 2.0; + EXPECT_NEAR(mean, expect, LINALG_TOL) + << "Mean differs from expected %lf: %lf (%lf)" << expect << mean + << fabs(mean - expect); + } +} + +TEST(TestLinearAlgebra, VectorNorm) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double test = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e22; + double A[n]; + for (i = 0; i < n; i++) { + A[i] = test; + } + EXPECT_NEAR(vector_norm(n, A) * vector_norm(n, A), + n * test * test, + LINALG_TOL * vector_norm(n, A)) + << "Norm differs from expected %lf: %lf (%lf)" << n * test * test + << vector_norm(n, A) * vector_norm(n, A) + << fabs(vector_norm(n, A) * vector_norm(n, A) - n * test * test); + } +} + +TEST(TestLinearAlgebra, VectorNormalize) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double A[n]; + for (i = 0; i < n; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + vector_normalize(n, A); + double vnorm = vector_norm(n, A); + EXPECT_NEAR(vnorm, 1, LINALG_TOL) + << "Norm differs from 1: %lf" << vector_norm(n, A); + } +} + +TEST(TestLinearAlgebra, VectorAddSc) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double A[n], B[n]; + for (i = 0; i < n; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + vector_add_sc(n, A, A, -1, B); + for (i = 0; i < n; i++) { + EXPECT_LT(fabs(B[i]), LINALG_TOL) + << "Vector element differs from 0: " << B[i]; + } + } +} + +TEST(TestLinearAlgebra, VectorAdd) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double A[n], B[n], C[n]; + for (i = 0; i < n; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + B[i] = -A[i]; + } + vector_add(n, A, B, C); + for (i = 0; i < n; i++) { + EXPECT_LT(fabs(C[i]), LINALG_TOL) + << "Vector element differs from 0: " << C[i]; + } + } +} + +TEST(TestLinearAlgebra, VectorSubtract) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double A[n], B[n], C[n]; + for (i = 0; i < n; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + B[i] = A[i]; + } + vector_subtract(n, A, B, C); + for (i = 0; i < n; i++) { + EXPECT_LT(fabs(C[i]), LINALG_TOL) + << "Vector element differs from 0: " << C[i]; + } + } +} + +TEST(TestLinearAlgebra, VectorCross) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + double A[3], B[3], C[3], D[3]; + for (i = 0; i < 3; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + B[i] = A[i]; + } + vector_cross(A, B, C); + for (i = 0; i < 3; i++) { + EXPECT_LT(fabs(C[i]), LINALG_TOL) + << "Vector element differs from 0: " << C[i]; + } + for (i = 0; i < 3; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + B[i] = rand.frand(MATRIX_MIN, MATRIX_MAX); + } + vector_cross(A, B, C); + for (i = 0; i < 3; i++) { + B[i] *= -1; + } + vector_cross(B, A, D); + for (i = 0; i < 3; i++) { + EXPECT_NEAR(C[i], D[i], LINALG_TOL); + } + } +} + +TEST(TestLinearAlgebra, VectorThree) { + u32 i, t; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + double A[3], B[3], C[3], tmp[3]; + double D, E, F, norm; + for (i = 0; i < 3; i++) { + A[i] = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e20; + B[i] = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e20; + C[i] = rand.frand(MATRIX_MIN, MATRIX_MAX) / 1e20; + } + /* Check triple product identity */ + vector_cross(A, B, tmp); + D = vector_dot(3, C, tmp); + vector_cross(B, C, tmp); + E = vector_dot(3, A, tmp); + vector_cross(C, A, tmp); + F = vector_dot(3, B, tmp); + + norm = (vector_norm(3, A) + vector_norm(3, B) + vector_norm(3, C)) / 3; + EXPECT_NEAR(E, D, LINALG_TOL * norm); + EXPECT_NEAR(E, F, LINALG_TOL * norm); + EXPECT_NEAR(F, D, LINALG_TOL * norm); + } +} + +/* +TEST(TestLinearAlgebra, QrsolveConsistency) { + u32 i, j, t; + double norm; + + Random rand{}; + for (t = 0; t < LINALG_NUM; t++) { + u32 n = rand.sizerand(MSIZE_MAX); + double x_gauss[n], x_qr[n]; + double A[n*n], Ainv[n*n], b[n]; + do { + for (i = 0; i < n; i++) { + b[i] = rand.frand(MATRIX_MIN, MATRIX_MAX);; + for (j = 0; j < n; j++) + A[n*i + j] = rand.frand(MATRIX_MIN, MATRIX_MAX);; + } + } while (matrix_inverse(n, A, Ainv) < 0); + matrix_multiply(n, n, 1, Ainv, b, x_gauss); + qrsolve(A, n, n, b, x_qr); + + norm = (vector_norm(n, x_qr) + vector_norm(n, x_gauss)) / 2; + for (i = 0; i < n; i++) + EXPECT_NEAR(x_qr[i] , x_gauss[i], LINALG_TOL * norm, + "QR solve failure; difference was %lf for element %u", + x_qr[i] - x_gauss[i], i); + } +} + + +TEST(TestLinearAlgebra, QrsolveRect) { + s32 i; + const double A[8] = {-0.0178505395610981, 1.4638781031761146, + -0.8242742209580581, -0.6843477128009663, + 0.9155272861151404, -0.1651159277864960, + -0.9929037180867774, -0.1491537478964264}; + double Q[16], R[8]; + + double buf[10] SWIFT_ATTR_UNUSED = {22, 22, 22, 22, 22, + 22, 22, 22, 22, 22}; + + i = qrdecomp(A, 4, 2, Q, R); + + printf("i returned %d\n", i); + + MAT_PRINTF(A, 4, 2); + MAT_PRINTF(Q, 4, 4); + MAT_PRINTF(R, 4, 2); +} + +*/ + +TEST(TestLinearAlgebra, Submatrix) { + const double A[3 * 3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + + double A2[2 * 2]; + + u32 row_map[2] = {1, 2}; + u32 col_map[2] = {0, 1}; + + const double answer[2 * 2] = {3, 4, 6, 7}; + + submatrix(2, 2, 3, A, row_map, col_map, A2); + + for (u8 i = 0; i < 2 * 2; i++) { + EXPECT_EQ(answer[i], A2[i]); + } +} + +TEST(TestLinearAlgebra, VectorDistance) { + const double A1[1 * 4] = {0, 1, 2, 3}; + + const double B1[1 * 4] = {0, 2, 1, -3}; + + const double C1[1 * 4] = {0, 1, 1, 6}; + + for (u8 i = 0; i < 4; i++) { + double dist; + dist = vector_distance(1, &A1[i], &B1[i]); + EXPECT_NEAR(dist, C1[i], LINALG_TOL); + } + + const double A2[2 * 5] = {0, 0, 1, 1, 2, 1, 0, 0, 0, 0}; + + const double B2[2 * 5] = {0, 0, 1, 1, 1, 1, 1, 1, -1, -1}; + + const double C2[1 * 5] = {0, 0, 1, M_SQRT2, M_SQRT2}; + + for (u8 i = 0; i < 5; i++) { + double dist; + dist = vector_distance(2, &A2[i * 2], &B2[i * 2]); + EXPECT_NEAR(dist, C2[i], LINALG_TOL); + } + + const double A3[3 * 5] = {0, 0, 0, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0}; + + const double B3[3 * 5] = {0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, -1, -1, 1}; + + const double C3[3 * 5] = { + 0, + 0, + 1, + 1.73205080756887729352744634150587236694280525381038062805580, + 1.73205080756887729352744634150587236694280525381038062805580}; + + for (u8 i = 0; i < 5; i++) { + double dist; + dist = vector_distance(3, &A3[i * 3], &B3[i * 3]); + EXPECT_NEAR(dist, C3[i], LINALG_TOL); + } +} + +} // namespace diff --git a/tests/test_log.cc b/tests/test_log.cc new file mode 100644 index 0000000..a502e47 --- /dev/null +++ b/tests/test_log.cc @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#define MAX_STR 1024 + +namespace { + +/*globals for test */ +char out_str[MAX_STR]; +char *ptr = out_str; +int last_level = 0; + +void reset_log(void) { + ptr = out_str; + memset(out_str, 0, MAX_STR); +} + +void test_log(int level, const char *msg, ...) { + va_list ap; + va_start(ap, msg); + ptr += vsprintf(ptr, msg, ap); + va_end(ap); + ptr += sprintf(ptr, "\n"); + last_level = level; +} + +void test_detailed_log(int level, + const char *file_path, + const int line_number, + const char *msg, + ...) { + (void)level; + (void)file_path; + (void)line_number; + (void)msg; +} + +TEST(TestLogging, Logging) { + /* check ptr arithmetic in print and null terminatino */ + int expected_len = 11; + logging_set_implementation(test_log, test_detailed_log); + log_info("log_info_1"); + EXPECT_EQ((ptr - out_str), expected_len); + EXPECT_TRUE(strnlen(out_str, MAX_STR) == (size_t)expected_len); + reset_log(); + + /* test log with rate limit based upon arbitrary tics. no need to check + * pointer as it was done above*/ + expected_len = (14 * 4); /*should print 4 times. line length is 14.*/ + + for (int i = 0; i < 4000; i++) { + LOG_RATE_LIMIT(i, log_info("log_rate_%04d", i)); + } + EXPECT_EQ(strnlen(out_str, MAX_STR), (size_t)expected_len); + reset_log(); + /* if we somehow go back in time, should print again*/ + expected_len = 2 * 14; /* should print 2 times. String is length 14. */ + for (int j = 2000; j > 0; j -= 1500) { + LOG_RATE_LIMIT(j, log_info("log_rate_%04d", j)); + } + EXPECT_EQ(strnlen(out_str, MAX_STR), (size_t)expected_len); + reset_log(); + /* if we wrap, arithmetic rules should just work */ + expected_len = (20 * 4); /* should print 4 times.*/ + for (int i = 0xffffffff - 1998; i < 1999; i++) { + LOG_RATE_LIMIT(i, log_info("log_rate_%010u", i)); + } + EXPECT_EQ(strnlen(out_str, MAX_STR), (size_t)expected_len); + + logging_set_implementation(nullptr, nullptr); +} + +} // namespace diff --git a/tests/test_nav_meas.cc b/tests/test_nav_meas.cc new file mode 100644 index 0000000..92d621b --- /dev/null +++ b/tests/test_nav_meas.cc @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include + +namespace { + +TEST(TestNavMeas, EncodeLockTime) { + u8 ret; + + ret = encode_lock_time(0.0); + EXPECT_EQ(ret, 0); + + ret = encode_lock_time(0.05); + EXPECT_EQ(ret, 1); + + ret = encode_lock_time(0.1); + EXPECT_EQ(ret, 2); + + ret = encode_lock_time(0.2); + EXPECT_EQ(ret, 3); + + ret = encode_lock_time(0.5); + EXPECT_EQ(ret, 4); + + ret = encode_lock_time(1.0); + EXPECT_EQ(ret, 5); + + ret = encode_lock_time(2.0); + EXPECT_EQ(ret, 6); + + ret = encode_lock_time(4.0); + EXPECT_EQ(ret, 7); + + ret = encode_lock_time(5.0); + EXPECT_EQ(ret, 8); + + ret = encode_lock_time(10.0); + EXPECT_EQ(ret, 9); + + ret = encode_lock_time(20.0); + EXPECT_EQ(ret, 10); + + ret = encode_lock_time(50.0); + EXPECT_EQ(ret, 11); + + ret = encode_lock_time(100.0); + EXPECT_EQ(ret, 12); + + ret = encode_lock_time(200.0); + EXPECT_EQ(ret, 13); + + ret = encode_lock_time(500.0); + EXPECT_EQ(ret, 14); + + ret = encode_lock_time(1000.0); + EXPECT_EQ(ret, 15); + + ret = encode_lock_time(DBL_MAX); + EXPECT_EQ(ret, 15); +} + +TEST(TestNavMeas, DecodeLockTime) { + double ret; + + ret = decode_lock_time(0); + EXPECT_EQ(ret, 0.0); + + ret = decode_lock_time(0xF0); + EXPECT_EQ(ret, 0.0); + + ret = decode_lock_time(1); + EXPECT_EQ(ret, 0.032); + + ret = decode_lock_time(2); + EXPECT_EQ(ret, 0.064); + + ret = decode_lock_time(3); + EXPECT_EQ(ret, 0.128); + + ret = decode_lock_time(4); + EXPECT_EQ(ret, 0.256); + + ret = decode_lock_time(5); + EXPECT_EQ(ret, 0.512); + + ret = decode_lock_time(6); + EXPECT_EQ(ret, 1.024); + + ret = decode_lock_time(7); + EXPECT_EQ(ret, 2.048); + + ret = decode_lock_time(8); + EXPECT_EQ(ret, 4.096); + + ret = decode_lock_time(9); + EXPECT_EQ(ret, 8.192); + + ret = decode_lock_time(10); + EXPECT_EQ(ret, 16.384); + + ret = decode_lock_time(11); + EXPECT_EQ(ret, 32.768); + + ret = decode_lock_time(12); + EXPECT_EQ(ret, 65.536); + + ret = decode_lock_time(13); + EXPECT_EQ(ret, 131.072); + + ret = decode_lock_time(14); + EXPECT_EQ(ret, 262.144); + + ret = decode_lock_time(15); + EXPECT_EQ(ret, 524.288); +} + +TEST(TestNavMeas, RoundtripLockTime) { + const double value_to_encode = 260.0; + u8 encoded_value; + double decoded_value; + + encoded_value = encode_lock_time(value_to_encode); + decoded_value = decode_lock_time(encoded_value); + + EXPECT_EQ(encoded_value, 13); + + EXPECT_EQ(decoded_value, 131.072); + + EXPECT_LT(decoded_value, value_to_encode); +} + +} // namespace diff --git a/tests/test_pvt.cc b/tests/test_pvt.cc new file mode 100644 index 0000000..0c16f9f --- /dev/null +++ b/tests/test_pvt.cc @@ -0,0 +1,489 @@ +#include +#include +#include +#include +#include +#include + +#include "check_utils.h" +#include "test_data.h" + +namespace { + +using namespace test_data; + +TEST(TestPvt, PvtFailedRepair) { + u8 n_used = 5; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8}; + + calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + /* PVT repair requires at least 6 measurements. */ + EXPECT_EQ(soln.valid, 0); +} + +TEST(TestPvt, PvtRepair) { + u8 n_used = 6; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + gnss_signal_t expected_removed_sid = {.sat = 9, .code = CODE_GPS_L1CA}; + + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 1); + EXPECT_EQ(soln.n_sigs_used, n_used - 1); + EXPECT_EQ(soln.n_sats_used, n_used - 1); + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid)) + << "Unexpected RAIM removed SID!\n"; +} + +TEST(TestPvt, PvtRaimSingular) { + /* test the case of bug 946 where extreme pseudorange errors lead to singular + * geometry */ + u8 n_used = 9; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + navigation_measurement_t nm1_broken = nm1; + navigation_measurement_t nm2_broken = nm2; + nm1_broken.raw_pseudorange += 5e8; + nm2_broken.raw_pseudorange -= 2e7; + + const navigation_measurement_t nms[9] = { + nm1_broken, nm2_broken, nm3, nm4, nm5, nm6, nm7, nm9, nm10}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, -4); +} + +TEST(TestPvt, PvtVelRepair) { + u8 n_used = 6; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + gnss_signal_t expected_removed_sid = {.sat = 5, .code = CODE_GPS_L1CA}; + + const navigation_measurement_t nms[6] = {nm2, nm3, nm4, nm5, nm6b, nm7}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 1); + EXPECT_EQ(soln.n_sigs_used, n_used - 1); + EXPECT_EQ(soln.n_sats_used, n_used - 1); + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid)) + << "Unexpected RAIM removed SID!\n"; +} + +TEST(TestPvt, PvtRepairMultifailure) { + u8 n_used = 7; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + gnss_signal_t expected_removed_sid = {.sat = 9, .code = CODE_GPS_L1CA}; + + const navigation_measurement_t nms[8] = { + nm1, nm2, nm3, nm7, nm10b, nm5, nm6, nm7}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 1); + EXPECT_EQ(soln.n_sigs_used, n_used - 2); + EXPECT_EQ(soln.n_sats_used, n_used - 2); + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid)) + << "Unexpected RAIM removed SID!\n"; +} + +TEST(TestPvt, PvtRaimGpsL1caOnly) { + /* 9 L1CA signals (one broken) and 1 L2CM signal */ + u8 n_used = 10; + u8 n_gps_l1ca = 9; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{false, 25}}; + gnss_signal_t expected_removed_sid = {.sat = 9, .code = CODE_GPS_L1CA}; + + const navigation_measurement_t nms[10] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + GPS_L1CA_WHEN_POSSIBLE, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 1); + EXPECT_EQ(soln.n_sigs_used, n_gps_l1ca - 1); + EXPECT_EQ(soln.n_sats_used, n_gps_l1ca - 1); + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid)) + << "Unexpected RAIM removed SID!\n"; +} + +TEST(TestPvt, PvtOutlierGpsL1caOnly) { + /* 9 L1CA signals and 1 (broken) L2CM signal */ + u8 n_used = 9; + u8 n_gps_l1ca = 8; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + const navigation_measurement_t nms[9] = { + nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10b}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + GPS_L1CA_WHEN_POSSIBLE, + &soln, + &dops, + &raim_removed_sids); + + EXPECT_EQ(code, 0); + EXPECT_EQ(soln.n_sigs_used, n_gps_l1ca); + EXPECT_EQ(soln.n_sats_used, n_gps_l1ca); +} + +// Regression test for PIKSI-191 +TEST(TestPvt, CalcPvtExcludeGal) { + u8 n_used = 8; + u8 n_gps_l1ca = 6; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + // Mixing GPS and GAL satellites + navigation_measurement_t nms[9] = { + nm3, gal_nm1, gal_nm2, nm5, nm6, nm7, nm8, nm9}; + + // Now using predicate GPS_ONLY would trigger an assert in outlier detection + // which is called from within calc_PVT() + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + GPS_ONLY, + &soln, + &dops, + &raim_removed_sids); + + EXPECT_EQ(code, 0); + EXPECT_EQ(soln.n_sigs_used, n_gps_l1ca); + EXPECT_EQ(soln.n_sats_used, n_gps_l1ca); +} + +TEST(TestPvt, PvtFlagOutlierBias) { + /* 8 L1CA signals and 2 L2CM signals */ + u8 n_used = 9; + u8 n_gps_l1ca = 7; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + navigation_measurement_t nm10_bias = nm10; + navigation_measurement_t nm11_bias = nm11; + + /* add a common bias of 120 m to the L2CM measurements */ + nm10_bias.raw_pseudorange += 120; + nm11_bias.raw_pseudorange += 120; + + /* healthy measurements, with bias on L2 */ + navigation_measurement_t nms[9] = { + nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm10_bias, nm11_bias}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + GPS_L1CA_WHEN_POSSIBLE, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 0); + EXPECT_EQ(soln.n_sigs_used, n_gps_l1ca); + EXPECT_EQ(soln.n_sats_used, n_gps_l1ca); + + /* add outlier to one of the L2 measurements */ + nm11_bias.raw_pseudorange += 1000; + nms[8] = nm11_bias; + + code = calc_PVT(n_used, + nms, + &tor, + false, + false, + &obs_mask_config, + GPS_L1CA_WHEN_POSSIBLE, + &soln, + &dops, + &raim_removed_sids); + + EXPECT_EQ(code, 0); + EXPECT_EQ(soln.n_sigs_used, n_gps_l1ca); + EXPECT_EQ(soln.n_sats_used, n_gps_l1ca); +} + +TEST(TestPvt, DisablePvtRaim) { + u8 n_used = 6; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{false, 25}}; + + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; + + /* disable raim check */ + s8 code = calc_PVT(n_used, + nms, + &tor, + true, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_EQ(code, 2); + EXPECT_EQ(soln.valid, 1) << "Solution should be valid!"; +} + +TEST(TestPvt, DisablePvtVelocity) { + u8 n_used = 6; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + const navigation_measurement_t nms[9] = { + nm1_no_doppler, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9}; + + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_GE(code, 0); + EXPECT_EQ(soln.valid, 1); + EXPECT_EQ(soln.vel_ned[0], 0.0); + EXPECT_EQ(soln.vel_ned[1], 0.0); + EXPECT_EQ(soln.vel_ned[2], 0.0); + EXPECT_EQ(soln.vel_ecef[0], 0.0); + EXPECT_EQ(soln.vel_ecef[1], 0.0); + EXPECT_EQ(soln.vel_ecef[2], 0.0); +} + +TEST(TestPvt, CountSats) { + u8 n_used = 10; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{false, 25}}; + + const navigation_measurement_t nms[10] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; + + /* disable raim check */ + s8 code = calc_PVT(n_used, + nms, + &tor, + true, + false, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_GE(code, 0); + EXPECT_EQ(soln.valid, 1); + EXPECT_EQ(soln.n_sigs_used, 10); + EXPECT_EQ(soln.n_sats_used, 9); +} + +TEST(TestPvt, CountSatsL1caOnly) { + u8 n_used = 10; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + /* 10 signals of which one is GPS L2 and others GPS L1 */ + const navigation_measurement_t nms[10] = { + nm1, nm2, nm3, nm4, nm5, nm6, nm7, nm8, nm9, nm10}; + + /* disable raim check */ + s8 code = calc_PVT(n_used, + nms, + &tor, + true, + false, + &obs_mask_config, + GPS_L1CA_WHEN_POSSIBLE, + &soln, + &dops, + &raim_removed_sids); + EXPECT_GE(code, 0); + EXPECT_EQ(soln.valid, 1); + EXPECT_EQ(soln.n_sigs_used, 9); + EXPECT_EQ(soln.n_sats_used, 9); +} + +TEST(TestPvt, Dops) { + u8 n_used = 6; + gnss_solution soln; + dops_t dops = {.pdop = 22, .gdop = 22, .tdop = 22, .hdop = 22, .vdop = 22}; + dops_t truedops = {.pdop = 2.69955, + .gdop = 3.07696, + .tdop = 1.47652, + .hdop = 1.76157, + .vdop = 2.04559}; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{false, 25}}; + + const double dop_tol = 1e-3; + + const navigation_measurement_t nms[6] = {nm1, nm2, nm3, nm4, nm5, nm6}; + + /* disable raim check */ + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + EXPECT_GE(code, 0); + EXPECT_EQ(soln.valid, 1); + EXPECT_TRUE(fabs(dops.pdop * dops.pdop - + (dops.vdop * dops.vdop + dops.hdop * dops.hdop)) < dop_tol); + double dop_err = + fabs(dops.pdop - truedops.pdop) + fabs(dops.gdop - truedops.gdop) + + fabs(dops.tdop - truedops.tdop) + fabs(dops.hdop - truedops.hdop) + + fabs(dops.vdop - truedops.vdop); + EXPECT_LT(dop_err, dop_tol); +} + +TEST(TestPvt, PvtSuccessfulRepairWithCn0Mask) { + /* Emulate TES-238 scenario */ + + u8 n_used = 9; + gnss_solution soln; + dops_t dops; + gnss_sid_set_t raim_removed_sids; + obs_mask_config_t obs_mask_config = {{true, 25}}; + + // Given erroneous pseudorange measurement with C/N0 above mask threshold (sat + // 2) + navigation_measurement_t nm3_bias = nm3; + nm3_bias.raw_pseudorange += 4350; + + // Given erroneous measurement with C/N0 below mask threshold and pseudorange + // that if processed, does not allow for successful RAIM repair + navigation_measurement_t nm4_bias_low_cn0 = nm4; + nm4_bias_low_cn0.raw_pseudorange += 4134; + nm4_bias_low_cn0.cn0 = 21; + + // When calc_PVT is attempted with measurements defined above and C/N0 + // observation masking enabled + const navigation_measurement_t nms[9] = { + nm1, nm2, nm3_bias, nm4_bias_low_cn0, nm5, nm6, nm7, nm8, nm9}; + s8 code = calc_PVT(n_used, + nms, + &tor, + false, + true, + &obs_mask_config, + ALL_CONSTELLATIONS, + &soln, + &dops, + &raim_removed_sids); + + // Expect successful RAIM repair + gnss_signal_t expected_removed_sid_1 = {.sat = 9, .code = CODE_GPS_L1CA}; + gnss_signal_t expected_removed_sid_2 = {.sat = 2, .code = CODE_GPS_L1CA}; + EXPECT_EQ(code, 1); + EXPECT_EQ(soln.n_sigs_used, n_used - 3); + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid_1)) + << "Unexpected RAIM removed SID!\n"; + EXPECT_TRUE(sid_set_contains(&raim_removed_sids, expected_removed_sid_2)) + << "Unexpected RAIM removed SID!\n"; +} + +} // namespace diff --git a/tests/check_set.c b/tests/test_set.cc similarity index 62% rename from tests/check_set.c rename to tests/test_set.cc index 9a91c21..17517c8 100644 --- a/tests/check_set.c +++ b/tests/test_set.cc @@ -1,175 +1,164 @@ -#include +#include #include #include #include #include -#include "check_suites.h" -#include "common/check_utils.h" +#include "check_utils.h" #define LEN(x) (sizeof(x) / sizeof(x[0])) +namespace { + void test_map_f(void *context, u32 n, const void *a_, const void *b_) { const s32 *a = (const s32 *)a_; const s32 *b = (const s32 *)b_; s32 *c = (s32 *)context; - fail_unless(context != NULL); - fail_unless(*a == *b, "Intersection items not equal"); + EXPECT_NE(context, nullptr); + EXPECT_EQ(*a, *b); c[n] = *a; } -#define TEST_INTERSECTION(a, b, c) \ - { \ - s32 c_result[LEN(c)]; \ - \ - qsort(a, LEN(a), sizeof(a[0]), cmp_s32_s32); \ - qsort(b, LEN(b), sizeof(b[0]), cmp_s32_s32); \ - qsort(c, LEN(c), sizeof(c[0]), cmp_s32_s32); \ - \ - s32 ret = intersection_map(LEN(a), \ - sizeof(a[0]), \ - a, \ - LEN(b), \ - sizeof(b[0]), \ - b, \ - cmp_s32_s32, \ - c_result, \ - test_map_f); \ - \ - fail_unless(ret == LEN(c), \ - "Intersection length does not match test data"); \ - \ - fail_unless(memcmp(c, c_result, sizeof(c)) == 0, \ - "Output of intersection does not match test data"); \ - \ - ret = intersection(LEN(a), \ - sizeof(a[0]), \ - a, \ - c_result, \ - LEN(b), \ - sizeof(b[0]), \ - b, \ - NULL, \ - cmp_s32_s32); \ - \ - fail_unless(ret == LEN(c), \ - "Intersection length does not match test data (2)"); \ - \ - fail_unless(memcmp(c, c_result, sizeof(c)) == 0, \ - "Output of intersection does not match test data (2)"); \ - \ - ret = intersection(LEN(a), \ - sizeof(a[0]), \ - a, \ - NULL, \ - LEN(b), \ - sizeof(b[0]), \ - b, \ - c_result, \ - cmp_s32_s32); \ - \ - fail_unless(ret == LEN(c), \ - "Intersection length does not match test data (3)"); \ - \ - fail_unless(memcmp(c, c_result, sizeof(c)) == 0, \ - "Output of intersection does not match test data (3)"); \ +#define TEST_INTERSECTION(a, b, c) \ + { \ + s32 c_result[LEN(c)]; \ + \ + qsort(a, LEN(a), sizeof(a[0]), cmp_s32_s32); \ + qsort(b, LEN(b), sizeof(b[0]), cmp_s32_s32); \ + qsort(c, LEN(c), sizeof(c[0]), cmp_s32_s32); \ + \ + s32 ret = intersection_map(LEN(a), \ + sizeof(a[0]), \ + a, \ + LEN(b), \ + sizeof(b[0]), \ + b, \ + cmp_s32_s32, \ + c_result, \ + test_map_f); \ + \ + EXPECT_EQ(ret, LEN(c)) << "Intersection length does not match test data"; \ + \ + EXPECT_EQ(memcmp(c, c_result, sizeof(c)), 0) \ + << "Output of intersection does not match test data"; \ + \ + ret = intersection(LEN(a), \ + sizeof(a[0]), \ + a, \ + c_result, \ + LEN(b), \ + sizeof(b[0]), \ + b, \ + nullptr, \ + cmp_s32_s32); \ + \ + EXPECT_EQ(ret, LEN(c)) \ + << "Intersection length does not match test data (2)"; \ + \ + EXPECT_EQ(memcmp(c, c_result, sizeof(c)), 0) \ + << "Output of intersection does not match test data (2)"; \ + \ + ret = intersection(LEN(a), \ + sizeof(a[0]), \ + a, \ + nullptr, \ + LEN(b), \ + sizeof(b[0]), \ + b, \ + c_result, \ + cmp_s32_s32); \ + \ + EXPECT_EQ(ret, LEN(c)) \ + << "Intersection length does not match test data (3)"; \ + \ + EXPECT_EQ(memcmp(c, c_result, sizeof(c)), 0) \ + << "Output of intersection does not match test data (3)"; \ } -START_TEST(test_intersection_map_1) { +TEST(TestSet, IntersectionMap1) { /* Empty first set */ s32 a[] = {}; s32 b[] = {1, 2, 3}; s32 c[] = {}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_2) { +TEST(TestSet, IntersectionMap2) { /* Empty second set */ s32 a[] = {1, 2, 3}; s32 b[] = {}; s32 c[] = {}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_3) { +TEST(TestSet, IntersectionMap3) { /* Beginning intersects */ s32 a[] = {1, 2, 3, 4, 5, 6, 7}; s32 b[] = {1, 2, 3}; s32 c[] = {1, 2, 3}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_4) { +TEST(TestSet, IntersectionMap4) { /* End intersects */ s32 a[] = {1, 2, 3, 4, 5, 6, 7}; s32 b[] = {5, 6, 7}; s32 c[] = {5, 6, 7}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_5) { +TEST(TestSet, IntersectionMap5) { /* Same set */ s32 a[] = {1, 2, 3, 4, 5, 6, 7}; s32 b[] = {1, 2, 3, 4, 5, 6, 7}; s32 c[] = {1, 2, 3, 4, 5, 6, 7}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_6) { +TEST(TestSet, IntersectionMap6) { /* Disjoint */ s32 a[] = {1, 2, 3, 4}; s32 b[] = {5, 6, 7, 8}; s32 c[] = {}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_7) { +TEST(TestSet, IntersectionMap7) { /* Middle overlaps */ s32 a[] = {1, 2, 3, 4, 5, 6, 7}; s32 b[] = {5, 6}; s32 c[] = {5, 6}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_8) { +TEST(TestSet, IntersectionMap8) { /* Overlapping but not subset */ s32 a[] = {1, 2, 3, 4, 5}; s32 b[] = {4, 5, 6, 7, 8}; s32 c[] = {4, 5}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_9) { +TEST(TestSet, IntersectionMap9) { /* Alternating disjoint */ s32 a[] = {2, 4, 6, 8, 10}; s32 b[] = {1, 3, 7, 9, 11}; s32 c[] = {}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_intersection_map_10) { +TEST(TestSet, IntersectionMap10) { /* Alternating with overlap */ s32 a[] = {2, 4, 6, 8, 9, 10}; s32 b[] = {1, 3, 7, 8, 9, 11}; s32 c[] = {8, 9}; TEST_INTERSECTION(a, b, c) } -END_TEST -START_TEST(test_is_prn_set) { -#define TEST_IS_SET(set, result) \ - fail_unless(is_sid_set(sizeof(set) / sizeof(set[0]), set) == result, \ - "is_sid_set(" #set ") != " #result); +TEST(TestSet, IsPrnSet) { +#define TEST_IS_SET(set, result) \ + EXPECT_EQ(is_sid_set(sizeof(set) / sizeof(set[0]), set), result) /* Normal set. */ gnss_signal_t prns1[] = {{.sat = 0}, @@ -181,7 +170,7 @@ START_TEST(test_is_prn_set) { TEST_IS_SET(prns1, true); /* Empty set. */ - fail_unless(is_sid_set(0, prns1) == true); + EXPECT_TRUE(is_sid_set(0, prns1)); /* Single element set. */ gnss_signal_t prns2[] = {{.sat = 22}}; @@ -214,26 +203,5 @@ START_TEST(test_is_prn_set) { {.sat = 0}, {.sat = 1}, {.sat = 22}, {.sat = 3}, {.sat = 4}}; TEST_IS_SET(prns8, false); } -END_TEST - -Suite *set_suite(void) { - Suite *s = suite_create("Set"); - - TCase *tc_intersection = tcase_create("Intersection"); - tcase_add_test(tc_intersection, test_intersection_map_1); - tcase_add_test(tc_intersection, test_intersection_map_2); - tcase_add_test(tc_intersection, test_intersection_map_3); - tcase_add_test(tc_intersection, test_intersection_map_4); - tcase_add_test(tc_intersection, test_intersection_map_5); - tcase_add_test(tc_intersection, test_intersection_map_6); - tcase_add_test(tc_intersection, test_intersection_map_7); - tcase_add_test(tc_intersection, test_intersection_map_8); - tcase_add_test(tc_intersection, test_intersection_map_9); - tcase_add_test(tc_intersection, test_intersection_map_10); - TCase *tc_set = tcase_create("Set"); - tcase_add_test(tc_set, test_is_prn_set); - suite_add_tcase(s, tc_intersection); - suite_add_tcase(s, tc_set); - - return s; -} + +} // namespace diff --git a/tests/check_shm.c b/tests/test_shm.cc similarity index 52% rename from tests/check_shm.c rename to tests/test_shm.cc index 5c89f57..ab1535c 100644 --- a/tests/check_shm.c +++ b/tests/test_shm.cc @@ -10,49 +10,32 @@ * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. */ -#include +#include #include #include #include #include #include -#include "check_suites.h" +namespace { -START_TEST(test_shm_gps_decode_shi_ephemeris) { +TEST(TestShm, ShmGpsDecodeShiEphemeris) { u32 sf1w3 = 0x3f122c34; u8 shi_ephemeris; shm_gps_decode_shi_ephemeris(sf1w3, &shi_ephemeris); - fail_unless(shi_ephemeris == 0x2c, - "shm_gps_decode_shi_ephemeris() returns 0x%x for 0x%x\n", - shi_ephemeris, - sf1w3); + EXPECT_EQ(shi_ephemeris, 0x2c); } -END_TEST -START_TEST(test_check_nav_dhi) { +TEST(TestShm, CheckNavDhi) { for (u8 dhi = 0; dhi < NAV_DHI_COUNT; ++dhi) { for (u16 ignored = 0; ignored <= UCHAR_MAX; ++ignored) { bool res = check_nav_dhi((dhi << 5), ignored); - bool expected = (NAV_DHI_OK == dhi) || (ignored & 1 << dhi); - fail_unless(res == expected, - "check_nav_dhi(%" PRIu8 ", %" PRIu8 ") failed", - (u8)(dhi << 5), - (u8)ignored); + bool expected = (NAV_DHI_OK == dhi) || ((ignored & 1 << dhi) != 0); + EXPECT_EQ(res, expected); } } } -END_TEST -Suite *shm_suite(void) { - Suite *s = suite_create("SHM"); - - TCase *tc_core = tcase_create("Core"); - tcase_add_test(tc_core, test_shm_gps_decode_shi_ephemeris); - tcase_add_test(tc_core, test_check_nav_dhi); - suite_add_tcase(s, tc_core); - - return s; -} +} // namespace diff --git a/tests/test_sid_set.cc b/tests/test_sid_set.cc new file mode 100644 index 0000000..5dc5ced --- /dev/null +++ b/tests/test_sid_set.cc @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#include "check_utils.h" + +namespace { + +TEST(TestSidSet, SidSetEmpty) { + gnss_sid_set_t sid_set; + sid_set_init(&sid_set); + u32 count = sid_set_get_sat_count(&sid_set); + + EXPECT_EQ(count, 0); +} + +TEST(TestSidSet, SidSet) { + gnss_signal_t sids[] = { + {.sat = 1, .code = CODE_GPS_L1CA}, + {.sat = 1, .code = CODE_GPS_L2CM}, + {.sat = 2, .code = CODE_GPS_L1CA}, + {.sat = 3, .code = CODE_GPS_L2CM}, + {.sat = 1, .code = CODE_GLO_L1OF}, + }; + + gnss_sid_set_t sid_set; + sid_set_init(&sid_set); + + for (u32 i = 0; i < sizeof(sids) / sizeof(sids[0]); i++) { + const gnss_signal_t sid = sids[i]; + sid_set_add(&sid_set, sid); + EXPECT_EQ(sid_set_get_sig_count(&sid_set), i + 1); + } + + u32 count = sid_set_get_sat_count(&sid_set); + + EXPECT_EQ(count, 4); +} + +TEST(TestSidSet, SidSetContains) { + gnss_signal_t sids[] = { + {.sat = 1, .code = CODE_GPS_L1CA}, + {.sat = 1, .code = CODE_GPS_L2CM}, + {.sat = 2, .code = CODE_GPS_L1CA}, + {.sat = 3, .code = CODE_GPS_L2CM}, + {.sat = 1, .code = CODE_GLO_L1OF}, + }; + + gnss_sid_set_t sid_set; + sid_set_init(&sid_set); + + /* check that add works by adding sids one by one */ + for (auto sid : sids) { + EXPECT_FALSE(sid_set_contains(&sid_set, sid)); + sid_set_add(&sid_set, sid); + EXPECT_TRUE(sid_set_contains(&sid_set, sid)); + } + + /* check that remove works by removing sids one by one */ + for (auto sid : sids) { + EXPECT_TRUE(sid_set_contains(&sid_set, sid)); + sid_set_remove(&sid_set, sid); + EXPECT_FALSE(sid_set_contains(&sid_set, sid)); + } +} + +} // namespace diff --git a/tests/test_signal.cc b/tests/test_signal.cc new file mode 100644 index 0000000..35db784 --- /dev/null +++ b/tests/test_signal.cc @@ -0,0 +1,543 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "check_utils.h" + +/* Include singal.c here to have a chance to turn off asserts. + Otherwise the code lines with asserts cannot be covered by + tests and it will lower test coverage statistics. */ +#ifndef NDEBUG +#define NDEBUG +#endif +#include "../src/signal.c" + +namespace { + +#define ARRAY_COUNT(arr) ((sizeof(arr) / sizeof(arr[0]))) + +const struct code_data_element { + code_t code; + u16 sat_count; +} code_data[] = { + {CODE_GPS_L1CA, NUM_SIGNALS_GPS_L1CA}, + {CODE_AUX_GPS, NUM_SIGNALS_GPS_L1CA}, + {CODE_GPS_L2CM, NUM_SIGNALS_GPS_L2C}, + {CODE_GPS_L2CL, NUM_SIGNALS_GPS_L2C}, + {CODE_GPS_L2CX, NUM_SIGNALS_GPS_L2C}, + {CODE_GPS_L5I, NUM_SIGNALS_GPS_L5}, + {CODE_GPS_L5Q, NUM_SIGNALS_GPS_L5}, + {CODE_GPS_L5X, NUM_SIGNALS_GPS_L5}, + {CODE_GPS_L1CI, NUM_SIGNALS_GPS_L1C}, + {CODE_GPS_L1CQ, NUM_SIGNALS_GPS_L1C}, + {CODE_GPS_L1CX, NUM_SIGNALS_GPS_L1C}, + {CODE_GPS_L1P, NUM_SIGNALS_GPS_L1P}, + {CODE_GPS_L2P, NUM_SIGNALS_GPS_L2P}, + + {CODE_SBAS_L1CA, NUM_SIGNALS_SBAS_L1CA}, + {CODE_AUX_SBAS, NUM_SIGNALS_SBAS_L1CA}, + {CODE_SBAS_L5I, NUM_SIGNALS_SBAS_L5}, + {CODE_SBAS_L5Q, NUM_SIGNALS_SBAS_L5}, + {CODE_SBAS_L5X, NUM_SIGNALS_SBAS_L5}, + + {CODE_GLO_L1OF, NUM_SIGNALS_GLO_L1OF}, + {CODE_GLO_L2OF, NUM_SIGNALS_GLO_L2OF}, + {CODE_GLO_L1P, NUM_SIGNALS_GLO_L1P}, + {CODE_GLO_L2P, NUM_SIGNALS_GLO_L2P}, + + {CODE_BDS2_B1, NUM_SIGNALS_BDS2_B1}, + {CODE_AUX_BDS, NUM_SIGNALS_BDS2_B1}, + {CODE_BDS2_B2, NUM_SIGNALS_BDS2_B2}, + {CODE_BDS3_B1CI, NUM_SIGNALS_BDS3_B1C}, + {CODE_BDS3_B1CQ, NUM_SIGNALS_BDS3_B1C}, + {CODE_BDS3_B1CX, NUM_SIGNALS_BDS3_B1C}, + {CODE_BDS3_B5I, NUM_SIGNALS_BDS3_B5}, + {CODE_BDS3_B5Q, NUM_SIGNALS_BDS3_B5}, + {CODE_BDS3_B5X, NUM_SIGNALS_BDS3_B5}, + {CODE_BDS3_B7I, NUM_SIGNALS_BDS3_B7}, + {CODE_BDS3_B7Q, NUM_SIGNALS_BDS3_B7}, + {CODE_BDS3_B7X, NUM_SIGNALS_BDS3_B7}, + {CODE_BDS3_B3I, NUM_SIGNALS_BDS3_B3}, + {CODE_BDS3_B3Q, NUM_SIGNALS_BDS3_B3}, + {CODE_BDS3_B3X, NUM_SIGNALS_BDS3_B3}, + + {CODE_GAL_E1B, NUM_SIGNALS_GAL_E1}, + {CODE_GAL_E1C, NUM_SIGNALS_GAL_E1}, + {CODE_GAL_E1X, NUM_SIGNALS_GAL_E1}, + {CODE_AUX_GAL, NUM_SIGNALS_GAL_E1}, + {CODE_GAL_E6B, NUM_SIGNALS_GAL_E6}, + {CODE_GAL_E6C, NUM_SIGNALS_GAL_E6}, + {CODE_GAL_E6X, NUM_SIGNALS_GAL_E6}, + {CODE_GAL_E7I, NUM_SIGNALS_GAL_E7}, + {CODE_GAL_E7Q, NUM_SIGNALS_GAL_E7}, + {CODE_GAL_E7X, NUM_SIGNALS_GAL_E7}, + {CODE_GAL_E8I, NUM_SIGNALS_GAL_E8}, + {CODE_GAL_E8Q, NUM_SIGNALS_GAL_E8}, + {CODE_GAL_E8X, NUM_SIGNALS_GAL_E8}, + {CODE_GAL_E5I, NUM_SIGNALS_GAL_E5}, + {CODE_GAL_E5Q, NUM_SIGNALS_GAL_E5}, + {CODE_GAL_E5X, NUM_SIGNALS_GAL_E5}, + + {CODE_QZS_L1CA, NUM_SIGNALS_QZS_L1}, + {CODE_AUX_QZS, NUM_SIGNALS_QZS_L1}, + {CODE_QZS_L1CI, NUM_SIGNALS_QZS_L1C}, + {CODE_QZS_L1CQ, NUM_SIGNALS_QZS_L1C}, + {CODE_QZS_L1CX, NUM_SIGNALS_QZS_L1C}, + {CODE_QZS_L2CM, NUM_SIGNALS_QZS_L2C}, + {CODE_QZS_L2CL, NUM_SIGNALS_QZS_L2C}, + {CODE_QZS_L2CX, NUM_SIGNALS_QZS_L2C}, + {CODE_QZS_L5I, NUM_SIGNALS_QZS_L5}, + {CODE_QZS_L5Q, NUM_SIGNALS_QZS_L5}, + {CODE_QZS_L5X, NUM_SIGNALS_QZS_L5}, +}; + +const struct constellation_data_element { + constellation_t constellation; + u16 sat_count; + u16 code_count; +} constellation_data[] = { + {CONSTELLATION_GPS, NUM_SIGNALS_GPS, NUM_CODES_GPS}, + {CONSTELLATION_SBAS, NUM_SIGNALS_SBAS, NUM_CODES_SBAS}, + {CONSTELLATION_GLO, NUM_SIGNALS_GLO, NUM_CODES_GLO}, + {CONSTELLATION_BDS, NUM_SIGNALS_BDS, NUM_CODES_BDS}, + {CONSTELLATION_QZS, NUM_SIGNALS_QZS, NUM_CODES_QZS}, + {CONSTELLATION_GAL, NUM_SIGNALS_GAL, NUM_CODES_GAL}, +}; + +TEST(TestSignal, CodeTableConsistency) { + for (size_t i = 0; i < CODE_COUNT; i++) { + EXPECT_EQ((code_e)i, code_table[i].code); + } +} + +TEST(TestSignal, SignalAggregates) { + EXPECT_EQ(ARRAY_COUNT(code_data), CODE_COUNT); + + EXPECT_EQ(ARRAY_COUNT(constellation_data), CONSTELLATION_COUNT); + + u16 constellation_code_counts[CONSTELLATION_COUNT]; + memset(constellation_code_counts, 0, sizeof(constellation_code_counts)); + for (const auto &i : code_data) { + const struct code_data_element *e = &i; + constellation_t constellation = code_to_constellation(e->code); + constellation_code_counts[constellation]++; + } + for (const auto &i : constellation_data) { + const struct constellation_data_element *e = &i; + EXPECT_EQ(e->code_count, constellation_code_counts[e->constellation]); + } +} + +TEST(TestSignal, SignalFromIndex) { + for (const auto &i : code_data) { + const struct code_data_element *e = &i; + code_t code = e->code; + u16 sat_count = e->sat_count; + for (u16 code_index = 0; code_index < sat_count; code_index++) { + gnss_signal_t sid = sid_from_code_index(code, code_index); + + EXPECT_TRUE(sid_valid(sid)); + EXPECT_EQ(sid_to_code_index(sid), code_index); + } + } +} + +TEST(TestSignal, SignalProperties) { + const struct test_case { + gnss_signal_t sid; + bool valid; + const char *str; + } test_cases[] = { + {.sid = {.sat = 0, .code = CODE_INVALID}, .valid = false}, + {.sid = {.sat = 0, .code = CODE_COUNT}, .valid = false}, + {.sid = {.sat = 1, .code = CODE_INVALID}, .valid = false}, + { + .sid = {.sat = 0, .code = CODE_GPS_L1CA}, + .valid = false, + }, + {.sid = {.sat = 1, .code = CODE_GPS_L1CA}, + .valid = true, + .str = "GPS L1CA 1"}, + {.sid = {.sat = 1, .code = CODE_GPS_L2CM}, + .valid = true, + .str = "GPS L2CM 1"}, + {.sid = {.sat = 1, .code = CODE_SBAS_L1CA}, .valid = false}, + {.sid = {.sat = 32, .code = CODE_GPS_L1CA}, + .valid = true, + .str = "GPS L1CA 32"}, + {.sid = {.sat = 33, .code = CODE_GPS_L1CA}, .valid = false}, + {.sid = {.sat = 0, .code = CODE_SBAS_L1CA}, .valid = false}, + {.sid = {.sat = 119, .code = CODE_SBAS_L1CA}, .valid = false}, + {.sid = {.sat = 120, .code = CODE_SBAS_L1CA}, + .valid = true, + .str = "SBAS L1 120"}, + {.sid = {.sat = 120, .code = CODE_GPS_L1CA}, .valid = false}, + {.sid = {.sat = 138, .code = CODE_SBAS_L1CA}, + .valid = true, + .str = "SBAS L1 138"}, + {.sid = {.sat = 139, .code = CODE_SBAS_L1CA}, .valid = false}, + { + .sid = {.sat = 0, .code = CODE_GLO_L1OF}, + .valid = false, + }, + {.sid = {.sat = 1, .code = CODE_GLO_L1OF}, + .valid = true, + .str = "GLO L1OF 1"}, + {.sid = {.sat = 28, .code = CODE_GLO_L1OF}, + .valid = true, + .str = "GLO L1OF 28"}, + {.sid = {.sat = 29, .code = CODE_GLO_L1OF}, .valid = false}, + { + .sid = {.sat = 0, .code = CODE_GLO_L2OF}, + .valid = false, + }, + {.sid = {.sat = 1, .code = CODE_GLO_L2OF}, + .valid = true, + .str = "GLO L2OF 1"}, + {.sid = {.sat = 28, .code = CODE_GLO_L2OF}, + .valid = true, + .str = "GLO L2OF 28"}, + {.sid = {.sat = 29, .code = CODE_GLO_L2OF}, .valid = false}, + {.sid = {.sat = 0, .code = CODE_GPS_L1P}, + .valid = false, + .str = "GPS L1P 0"}, + {.sid = {.sat = 1, .code = CODE_GPS_L1P}, + .valid = true, + .str = "GPS L1P 1"}, + {.sid = {.sat = 24, .code = CODE_GPS_L1P}, + .valid = true, + .str = "GPS L1P 24"}, + {.sid = {.sat = 0, .code = CODE_GPS_L2P}, + .valid = false, + .str = "GPS L2P 0"}, + {.sid = {.sat = 1, .code = CODE_GPS_L2P}, + .valid = true, + .str = "GPS L2P 1"}, + {.sid = {.sat = 24, .code = CODE_GPS_L2P}, + .valid = true, + .str = "GPS L2P 24"}, + {.sid = {.sat = 0, .code = CODE_BDS2_B1}, .valid = false}, + {.sid = {.sat = 1, .code = CODE_BDS2_B1}, + .valid = true, + .str = "BDS B1 1"}, + {.sid = {.sat = 41, .code = CODE_BDS2_B1}, + .valid = true, + .str = "BDS B1 41"}, + {.sid = {.sat = 65, .code = CODE_BDS2_B1}, .valid = false}, + }; + + for (const auto &i : test_cases) { + const struct test_case *t = &i; + bool valid = sid_valid(t->sid); + EXPECT_EQ(t->valid, valid); + if (valid) { + gnss_signal_t sid = + sid_from_code_index(t->sid.code, sid_to_code_index(t->sid)); + EXPECT_TRUE(sid_is_equal(t->sid, sid)); + char str[SID_STR_LEN_MAX] = {0}; + u32 ret = sid_to_string(str, sizeof(str), sid); + EXPECT_EQ(ret, strlen(t->str)); + EXPECT_STREQ(str, t->str); + EXPECT_TRUE(constellation_valid(code_to_constellation(sid.code))); + } + EXPECT_FALSE(constellation_valid(CONSTELLATION_COUNT)) + << "constellation_valid failed for constellation " + << CONSTELLATION_COUNT; + } +} + +TEST(TestSignal, SignalCompare) { + gnss_signal_t sids[NUM_SIGNALS]; + u32 signal_index = 0; + + for (const auto &i : code_data) { + const struct code_data_element *e = &i; + code_t code = e->code; + u16 sat_count = e->sat_count; + for (u16 sat_index = 0; sat_index < sat_count; sat_index++) { + gnss_signal_t sid = sid_from_code_index(code, sat_index); + sids[signal_index++] = sid; + } + } + + qsort(sids, NUM_SIGNALS, sizeof(gnss_signal_t), cmp_sid_sid); + + for (u32 i = 1; i < NUM_SIGNALS; i++) { + EXPECT_FALSE((sid_is_equal(sids[i], sids[i - 1]))) + << "signal index " << i << " not unique"; + EXPECT_TRUE(sid_compare(sids[i], sids[i - 1]) >= 0) + << "signal index " << i << " not in order"; + } +} + +TEST(TestSignal, SignalConstruction) { + for (const auto &i : code_data) { + const struct code_data_element *e = &i; + code_t code = e->code; + u16 sat_count = e->sat_count; + for (u16 code_index = 0; code_index < sat_count; code_index++) { + gnss_signal_t sid = sid_from_code_index(code, code_index); + gnss_signal_t csid = construct_sid(sid.code, sid.sat); + EXPECT_TRUE(sid_valid(csid)); + EXPECT_TRUE(sid_is_equal(sid, csid)); + } + } +} + +void glo_map_lock(void) {} +void glo_map_unlock(void) {} + +TEST(TestSignal, SignalSidToCarrFreq) { + /* We do not test thread safety here. + Therefore lock & unlock functions are just stubs. */ + glo_map_init(glo_map_lock, glo_map_unlock); + + double carr_freq; + gnss_signal_t sid = construct_sid(CODE_GPS_L2CM, GPS_FIRST_PRN); + carr_freq = sid_to_carr_freq(sid); + EXPECT_EQ(GPS_L2_HZ, carr_freq); + + sid = construct_sid(CODE_GPS_L1CA, GPS_FIRST_PRN); + carr_freq = sid_to_carr_freq(sid); + EXPECT_EQ(GPS_L1_HZ, carr_freq); + + /* check all GLO frequency and orbital slots */ + for (u16 sat = GLO_FIRST_PRN; sat <= NUM_SATS_GLO; sat++) { + for (u16 fcn = GLO_MIN_FCN; fcn <= GLO_MAX_FCN; fcn++) { + /* L2 first */ + /* map orb and fcn slots */ + glo_map_set_slot_id(fcn, sat); + sid = construct_sid(CODE_GLO_L2OF, sat); + carr_freq = sid_to_carr_freq(sid); + EXPECT_EQ((GLO_L2_HZ + + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * GLO_L2_DELTA_HZ), + carr_freq); + /* now L1 */ + sid = construct_sid(CODE_GLO_L1OF, sat); + carr_freq = sid_to_carr_freq(sid); + EXPECT_EQ((GLO_L1_HZ + + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * GLO_L1_DELTA_HZ), + carr_freq); + } + } +} + +TEST(TestSignal, SignalSidToLambda) { + /* We do not test thread safety here. + Therefore lock & unlock functions are just stubs. */ + glo_map_init(glo_map_lock, glo_map_unlock); + + double lambda; + + gnss_signal_t sid = construct_sid(CODE_GPS_L2CM, GPS_FIRST_PRN); + lambda = sid_to_lambda(sid); + EXPECT_EQ((GPS_C / GPS_L2_HZ), lambda); + + sid = construct_sid(CODE_GPS_L1CA, GPS_FIRST_PRN); + lambda = sid_to_lambda(sid); + EXPECT_EQ((GPS_C / GPS_L1_HZ), lambda); + + /* check all GLO frequency and orbital slots */ + for (u16 orb_slot = GLO_FIRST_PRN; orb_slot <= NUM_SATS_GLO; orb_slot++) { + for (u16 fcn = GLO_MIN_FCN; fcn <= GLO_MAX_FCN; fcn++) { + /* L2 first */ + /* map orb and fcn slots */ + glo_map_set_slot_id(fcn, orb_slot); + sid = construct_sid(CODE_GLO_L2OF, orb_slot); + lambda = sid_to_lambda(sid); + EXPECT_EQ((GPS_C / (GLO_L2_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * + GLO_L2_DELTA_HZ)), + lambda); + /* now L1 */ + sid = construct_sid(CODE_GLO_L1OF, orb_slot); + lambda = sid_to_lambda(sid); + EXPECT_EQ((GPS_C / (GLO_L1_HZ + (glo_map_get_fcn(sid) - GLO_FCN_OFFSET) * + GLO_L1_DELTA_HZ)), + lambda); + } + } +} + +TEST(TestSignal, SignalCodeToChipCount) { + u32 chip_count; + + chip_count = code_to_chip_count(CODE_GPS_L1CA); + EXPECT_EQ(GPS_L1CA_CHIPS_NUM, chip_count); + + chip_count = code_to_chip_count(CODE_SBAS_L1CA); + EXPECT_EQ(GPS_L1CA_CHIPS_NUM, chip_count); + + chip_count = code_to_chip_count(CODE_GPS_L2CM); + EXPECT_EQ(GPS_L2CM_CHIPS_NUM, chip_count); + + chip_count = code_to_chip_count(CODE_GPS_L2CL); + EXPECT_EQ(GPS_L2CL_CHIPS_NUM, chip_count); + + /* check unsupported branch for code coverage stats */ + chip_count = code_to_chip_count(CODE_GLO_L2OF); +} + +TEST(TestSignal, SignalCodeToChipRate) { + double chip_rate; + + chip_rate = code_to_chip_rate(CODE_GPS_L1CA); + EXPECT_EQ(GPS_CA_CHIPPING_RATE, chip_rate); + + chip_rate = code_to_chip_rate(CODE_SBAS_L1CA); + EXPECT_EQ(GPS_CA_CHIPPING_RATE, chip_rate); + + chip_rate = code_to_chip_rate(CODE_GPS_L2CM); + EXPECT_EQ(GPS_CA_CHIPPING_RATE, chip_rate); + + /* check unsupported branch for code coverage stats */ + chip_rate = code_to_chip_rate(CODE_GLO_L2OF); +} + +TEST(TestSignal, SignalCodeRequiresDirectAcq) { + bool req; + + req = code_requires_direct_acq(CODE_GPS_L1CA); + EXPECT_EQ(true, req); + + req = code_requires_direct_acq(CODE_GPS_L2CM); + EXPECT_EQ(false, req); +} + +TEST(TestSignal, SignalCodeToPrnPeriod) { + u16 period; + + period = code_to_prn_period_ms(CODE_GPS_L1CA); + EXPECT_EQ(1, period); + + period = code_to_prn_period_ms(CODE_GPS_L2CM); + EXPECT_EQ(20, period); + + period = code_to_prn_period_ms(CODE_GLO_L1OF); + EXPECT_EQ(1, period); +} + +TEST(TestSignal, SidSystemCheck) { + for (u8 i = 0; i < CODE_COUNT; i++) { + bool gps = + (i == CODE_GPS_L1CA) || (i == CODE_AUX_GPS) || (i == CODE_GPS_L2CM) || + (i == CODE_GPS_L2CL) || (i == CODE_GPS_L2CX) || (i == CODE_GPS_L1P) || + (i == CODE_GPS_L2P) || (i == CODE_GPS_L5I) || (i == CODE_GPS_L5Q) || + (i == CODE_GPS_L5X) || (i == CODE_GPS_L1CI) || (i == CODE_GPS_L1CQ) || + (i == CODE_GPS_L1CX); + + EXPECT_EQ(gps, IS_GPS(construct_sid((code_t)i, GPS_FIRST_PRN))); + + bool glo = (i == CODE_GLO_L1OF) || (i == CODE_GLO_L2OF) || + (i == CODE_GLO_L1P) || (i == CODE_GLO_L2P); + + EXPECT_EQ(glo, IS_GLO(construct_sid((code_t)i, GLO_FIRST_PRN))); + + bool sbas = (i == CODE_SBAS_L1CA) || (i == CODE_AUX_SBAS) || + (i == CODE_SBAS_L5I) || (i == CODE_SBAS_L5Q) || + (i == CODE_SBAS_L5X); + + EXPECT_EQ(sbas, IS_SBAS(construct_sid((code_t)i, SBAS_FIRST_PRN))); + } +} + +TEST(TestSignal, SbasPrnList) { + for (u8 prn = SBAS_FIRST_PRN; prn < SBAS_FIRST_PRN + NUM_SATS_SBAS; prn++) { + gnss_signal_t sid = {prn, CODE_SBAS_L1CA}; + sbas_system_t sbas_system = get_sbas_system(sid); + EXPECT_LE(sbas_system, SBAS_COUNT); + } + + for (sbas_system_t sbas_system = static_cast(0); + sbas_system < SBAS_COUNT; + sbas_system = static_cast(sbas_system + 1)) { + for (unsigned char prn : sbas_prn_table[sbas_system].prn_list) { + EXPECT_TRUE(prn == 0 || prn >= SBAS_FIRST_PRN); + EXPECT_LT(prn, SBAS_FIRST_PRN + NUM_SATS_SBAS); + + if (prn >= SBAS_FIRST_PRN) { + gnss_signal_t sid = {prn, CODE_SBAS_L1CA}; + EXPECT_EQ(get_sbas_system(sid), sbas_system); + } + } + } +} + +TEST(TestSignal, SignalHashes) { + for (s32 test_round = 0; test_round < 1000000; ++test_round) { + gnss_signal_t sid1, sid2; + sid1.code = (code_t)(rand() % CODE_COUNT); + sid1.sat = static_cast(rand() % MAX_NUM_SATS); + sid2.code = static_cast(rand() % CODE_COUNT); + sid2.sat = (u16)(rand() % MAX_NUM_SATS); + + constellation_t sid1_constellation = sid_to_constellation(sid1); + constellation_t sid2_constellation = sid_to_constellation(sid2); + + int comparison = sid_compare(sid1, sid2); + + if (sid1_constellation < sid2_constellation) { + EXPECT_LT(comparison, 0); + } else if (sid1_constellation > sid2_constellation) { + EXPECT_GT(comparison, 0); + } else { + if (sid1.code < sid2.code) { + EXPECT_LT(comparison, 0); + } else if (sid1.code > sid2.code) { + EXPECT_GT(comparison, 0); + } else { + if (sid1.sat < sid2.sat) { + EXPECT_LT(comparison, 0); + } else if (sid1.sat > sid2.sat) { + EXPECT_GT(comparison, 0); + } else { + EXPECT_EQ(comparison, 0); + } + } + } + } +} + +TEST(TestSignal, ConstellationToString) { + EXPECT_STREQ("GPS", constellation_to_string(CONSTELLATION_GPS)); + EXPECT_STREQ("GPS", constellation_to_string(CONSTELLATION_GPS)); + EXPECT_STREQ("SBAS", constellation_to_string(CONSTELLATION_SBAS)); + EXPECT_STREQ("GLO", constellation_to_string(CONSTELLATION_GLO)); + EXPECT_STREQ("BDS", constellation_to_string(CONSTELLATION_BDS)); + EXPECT_STREQ("QZS", constellation_to_string(CONSTELLATION_QZS)); + EXPECT_STREQ("GAL", constellation_to_string(CONSTELLATION_GAL)); +} + +TEST(TestSignal, SubConstellationToString) { + EXPECT_STREQ("GPS", sub_constellation_to_string(SUB_CONSTELLATION_GPS)); + EXPECT_STREQ("SBAS", sub_constellation_to_string(SUB_CONSTELLATION_SBAS)); + EXPECT_STREQ("GLO", sub_constellation_to_string(SUB_CONSTELLATION_GLO)); + EXPECT_STREQ("BDS2", sub_constellation_to_string(SUB_CONSTELLATION_BDS2)); + EXPECT_STREQ("BDS3", sub_constellation_to_string(SUB_CONSTELLATION_BDS3)); + EXPECT_STREQ("QZS", sub_constellation_to_string(SUB_CONSTELLATION_QZS)); + EXPECT_STREQ("GAL", sub_constellation_to_string(SUB_CONSTELLATION_GAL)); +} + +TEST(TestSignal, SubConstellationToConstellation) { + EXPECT_EQ(CONSTELLATION_GPS, + sub_constellation_to_constellation(SUB_CONSTELLATION_GPS)); + EXPECT_EQ(CONSTELLATION_GPS, + sub_constellation_to_constellation(SUB_CONSTELLATION_GPS)); + EXPECT_EQ(CONSTELLATION_SBAS, + sub_constellation_to_constellation(SUB_CONSTELLATION_SBAS)); + EXPECT_EQ(CONSTELLATION_GLO, + sub_constellation_to_constellation(SUB_CONSTELLATION_GLO)); + EXPECT_EQ(CONSTELLATION_BDS, + sub_constellation_to_constellation(SUB_CONSTELLATION_BDS2)); + EXPECT_EQ(CONSTELLATION_BDS, + sub_constellation_to_constellation(SUB_CONSTELLATION_BDS3)); + EXPECT_EQ(CONSTELLATION_QZS, + sub_constellation_to_constellation(SUB_CONSTELLATION_QZS)); + EXPECT_EQ(CONSTELLATION_GAL, + sub_constellation_to_constellation(SUB_CONSTELLATION_GAL)); +} + +} // namespace diff --git a/tests/test_subsystem_status_report.cc b/tests/test_subsystem_status_report.cc new file mode 100644 index 0000000..c4a1de4 --- /dev/null +++ b/tests/test_subsystem_status_report.cc @@ -0,0 +1,238 @@ +/** + * Copyright (C) 2022 Swift Navigation Inc. + * Contact: Swift Navigation + * + * This source is subject to the license found in the file 'LICENSE' which must + * distributed together with this source. All other rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include + +namespace { + +typedef struct { + size_t invocations; + uint16_t component; + uint8_t generic; + uint8_t specific; + void *context; + size_t counter; +} callback_tracker_t; + +size_t counter_tracker; +callback_tracker_t callback_tracker[3]; + +void callback1(uint16_t component, + uint8_t generic, + uint8_t specific, + void *context) { + const size_t index = 0; + callback_tracker[index].invocations++; + callback_tracker[index].component = component; + callback_tracker[index].generic = generic; + callback_tracker[index].specific = specific; + callback_tracker[index].context = context; + callback_tracker[index].counter = counter_tracker++; +} + +void callback2(uint16_t component, + uint8_t generic, + uint8_t specific, + void *context) { + const size_t index = 1; + callback_tracker[index].invocations++; + callback_tracker[index].component = component; + callback_tracker[index].generic = generic; + callback_tracker[index].specific = specific; + callback_tracker[index].context = context; + callback_tracker[index].counter = counter_tracker++; +} + +void callback3(uint16_t component, + uint8_t generic, + uint8_t specific, + void *context) { + const size_t index = 2; + callback_tracker[index].invocations++; + callback_tracker[index].component = component; + callback_tracker[index].generic = generic; + callback_tracker[index].specific = specific; + callback_tracker[index].context = context; + callback_tracker[index].counter = counter_tracker++; +} + +void fixture_teardown() { + counter_tracker = 0; + memset(&callback_tracker, 0, sizeof(callback_tracker)); + swiftnav_subsystem_status_report_callback_reset(); +} + +class TestSubsystemStatusReport : public ::testing::Test { + protected: + void TearDown() override { fixture_teardown(); } +}; + +TEST_F(TestSubsystemStatusReport, TestNoRegisteredCallbacks) { + swiftnav_send_subsystem_status_report(0, 1, 2); + + EXPECT_EQ(callback_tracker[0].invocations, 0); + EXPECT_EQ(callback_tracker[1].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 0); +} + +TEST_F(TestSubsystemStatusReport, TestOneRegisteredCallbacks) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_send_subsystem_status_report(0, 1, 2); + + EXPECT_EQ(callback_tracker[0].invocations, 1); + EXPECT_EQ(callback_tracker[0].component, 0); + EXPECT_EQ(callback_tracker[0].generic, 1); + EXPECT_EQ(callback_tracker[0].specific, 2); + EXPECT_EQ(callback_tracker[0].context, (void *)0xff); + EXPECT_EQ(callback_tracker[0].counter, 0); + + EXPECT_EQ(callback_tracker[1].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 0); +} + +TEST_F(TestSubsystemStatusReport, TwoRegisteredCallbacks) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_subsystem_status_report_callback_node_t node2; + swiftnav_subsystem_status_report_callback_register( + &node2, callback2, (void *)0xee); + + swiftnav_send_subsystem_status_report(0, 1, 2); + + EXPECT_EQ(callback_tracker[0].invocations, 1); + EXPECT_EQ(callback_tracker[0].component, 0); + EXPECT_EQ(callback_tracker[0].generic, 1); + EXPECT_EQ(callback_tracker[0].specific, 2); + EXPECT_EQ(callback_tracker[0].context, (void *)0xff); + EXPECT_EQ(callback_tracker[0].counter, 0); + + EXPECT_EQ(callback_tracker[1].invocations, 1); + EXPECT_EQ(callback_tracker[1].component, 0); + EXPECT_EQ(callback_tracker[1].generic, 1); + EXPECT_EQ(callback_tracker[1].specific, 2); + EXPECT_EQ(callback_tracker[1].context, (void *)0xee); + EXPECT_EQ(callback_tracker[1].counter, 1); + + EXPECT_EQ(callback_tracker[2].invocations, 0); +} + +TEST_F(TestSubsystemStatusReport, NoRegisteredCallbacksDeregister) { + swiftnav_subsystem_status_report_callback_deregister(nullptr); + + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_deregister(&node1); +} + +TEST_F(TestSubsystemStatusReport, OneRegisteredCallbacksDeregister) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_subsystem_status_report_callback_deregister(nullptr); + swiftnav_subsystem_status_report_callback_deregister(&node1); + + swiftnav_send_subsystem_status_report(0, 1, 2); + EXPECT_EQ(callback_tracker[0].invocations, 0); + EXPECT_EQ(callback_tracker[1].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 0); +} + +TEST_F(TestSubsystemStatusReport, TwoRegisteredCallbacksDeregisterFirst) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_subsystem_status_report_callback_node_t node2; + swiftnav_subsystem_status_report_callback_register( + &node2, callback2, (void *)0xee); + + swiftnav_subsystem_status_report_callback_deregister(nullptr); + swiftnav_subsystem_status_report_callback_deregister(&node1); + + swiftnav_send_subsystem_status_report(0, 1, 2); + EXPECT_EQ(callback_tracker[0].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 0); + + EXPECT_EQ(callback_tracker[1].invocations, 1); + EXPECT_EQ(callback_tracker[1].component, 0); + EXPECT_EQ(callback_tracker[1].generic, 1); + EXPECT_EQ(callback_tracker[1].specific, 2); + EXPECT_EQ(callback_tracker[1].context, (void *)0xee); + EXPECT_EQ(callback_tracker[1].counter, 0); +} + +TEST_F(TestSubsystemStatusReport, TwoRegisteredCallbacksDeregisterSecond) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_subsystem_status_report_callback_node_t node2; + swiftnav_subsystem_status_report_callback_register( + &node2, callback2, (void *)0xee); + + swiftnav_subsystem_status_report_callback_deregister(nullptr); + swiftnav_subsystem_status_report_callback_deregister(&node2); + + swiftnav_send_subsystem_status_report(0, 1, 2); + EXPECT_EQ(callback_tracker[0].invocations, 1); + EXPECT_EQ(callback_tracker[1].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 0); + + EXPECT_EQ(callback_tracker[0].component, 0); + EXPECT_EQ(callback_tracker[0].generic, 1); + EXPECT_EQ(callback_tracker[0].specific, 2); + EXPECT_EQ(callback_tracker[0].context, (void *)0xff); + EXPECT_EQ(callback_tracker[0].counter, 0); +} + +TEST_F(TestSubsystemStatusReport, ThreeRegisteredCallbacksDeregisterSecond) { + swiftnav_subsystem_status_report_callback_node_t node1; + swiftnav_subsystem_status_report_callback_register( + &node1, callback1, (void *)0xff); + + swiftnav_subsystem_status_report_callback_node_t node2; + swiftnav_subsystem_status_report_callback_register( + &node2, callback2, (void *)0xee); + + swiftnav_subsystem_status_report_callback_node_t node3; + swiftnav_subsystem_status_report_callback_register( + &node3, callback3, (void *)0xdd); + + swiftnav_subsystem_status_report_callback_deregister(nullptr); + swiftnav_subsystem_status_report_callback_deregister(&node2); + + swiftnav_send_subsystem_status_report(0, 1, 2); + EXPECT_EQ(callback_tracker[0].invocations, 1); + EXPECT_EQ(callback_tracker[1].invocations, 0); + EXPECT_EQ(callback_tracker[2].invocations, 1); + + EXPECT_EQ(callback_tracker[0].component, 0); + EXPECT_EQ(callback_tracker[0].generic, 1); + EXPECT_EQ(callback_tracker[0].specific, 2); + EXPECT_EQ(callback_tracker[0].context, (void *)0xff); + EXPECT_EQ(callback_tracker[0].counter, 0); + + EXPECT_EQ(callback_tracker[2].component, 0); + EXPECT_EQ(callback_tracker[2].generic, 1); + EXPECT_EQ(callback_tracker[2].specific, 2); + EXPECT_EQ(callback_tracker[2].context, (void *)0xdd); + EXPECT_EQ(callback_tracker[2].counter, 1); +} + +} // namespace diff --git a/tests/test_troposphere.cc b/tests/test_troposphere.cc new file mode 100644 index 0000000..d7a939f --- /dev/null +++ b/tests/test_troposphere.cc @@ -0,0 +1,83 @@ + +#include +#include +#include +#include + +namespace { + +TEST(TestTroposphere, CalcTroposphere) { + const double d_tol = 1e-4; + + /* some tests against "true" values computed with UNB3M.f */ + /* http://www2.unb.ca/gge/Personnel/Santos/UNB_pack.pdf */ + + double lat = 40 * D2R; + double h = 1300.0; + double doy = 32.5; + double el = 45 * D2R; + double d_true = 2.8567; + + double d_tropo = calc_troposphere(doy, lat, h, el); + + EXPECT_TRUE(fabs(d_tropo - d_true) < d_tol) + << "Distance didn't match hardcoded correct values " << d_true + << ". Saw: " << d_tropo; + + lat = -10 * D2R; + h = 0.0; + doy = 180.5; + el = 20 * D2R; + d_true = 7.4942; + + d_tropo = calc_troposphere(doy, lat, h, el); + + EXPECT_TRUE(fabs(d_tropo - d_true) < d_tol) + << "Distance didn't match hardcoded correct values " << d_true + << ". Saw: " << d_tropo; + + lat = 75 * D2R; + h = 0.0; + doy = 50.5; + el = 10 * D2R; + d_true = 12.90073; + + d_tropo = calc_troposphere(doy, lat, h, el); + + EXPECT_TRUE(fabs(d_tropo - d_true) < d_tol) + << "Distance didn't match hardcoded correct values " << d_true + << ". Saw: " << d_tropo; + + /* altitude sanity tests */ + double max_tropo_correction = 30.0; + h = -5000; + d_tropo = calc_troposphere(doy, lat, h, el); + + EXPECT_TRUE(fabs(d_tropo) < max_tropo_correction) + << "Sanity test fail at altitude " << h << ". : Correction was " + << d_tropo; + + h = 12000; + d_tropo = calc_troposphere(doy, lat, h, el); + + EXPECT_TRUE(fabs(d_tropo) < max_tropo_correction) + << "Sanity test fail at altitude " << h << ". : Correction was " + << d_tropo; + + /* satellite elevation sanity tests */ + h = 100; + double elevation_testcases[] = {1e-3, 1e-4, 1e-5, 0, -1e3, -0.1}; + max_tropo_correction = 100.0; + + for (double elevation_testcase : elevation_testcases) { + el = elevation_testcase; + d_tropo = calc_troposphere(doy, lat, h, el); + EXPECT_LT(fabs(d_tropo), max_tropo_correction) + << "Sanity test fail at satellite elevation " << el + << ". : Correction " + "was " + << d_tropo; + } +} + +} // namespace diff --git a/third_party/check b/third_party/check deleted file mode 160000 index e057665..0000000 --- a/third_party/check +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e05766540eba58ae46da2e2bfce4cc4212488c98 diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 0000000..e2239ee --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit e2239ee6043f73722e7aa812a459f54a28552929