From 99e99733ab191ba29c078f9bdd4324b551fca1d7 Mon Sep 17 00:00:00 2001 From: Joseph Angelo Date: Mon, 21 Jul 2025 13:18:38 -0700 Subject: [PATCH 1/2] open source publish 47524f3..78779a8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Move Common Build Files to Submodule Replaces the in-tree bazel files with a submodule, so that these common build scripts can be reused by other projects. Co-authored-by: Isaac Torres --- cmake - Add find module for Auk Triggered-By: cmake f4d30717858c709c4c2f711cd715cd643f32f349 Upstream-PR: http://github.com/swift-nav/cmake/pull/135 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- updated leap second Co-authored-by: Rodrigo Reichert --- bazel: add swift_cc* wrappers Adds wrappers for native cc rules. Co-authored-by: Isaac Torres --- bazel: Fix code-coverage Co-authored-by: Krzysztof Naglik --- Remove bazel submodule Fetches swift's bazel rules via http_archive instead of using a submodule Co-authored-by: Isaac Torres --- cmake - Add find module for libuv Triggered-By: cmake 2adba8029f2a1a476b963a32cd494cd03142b6df Upstream-PR: http://github.com/swift-nav/cmake/pull/137 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- cmake - gRCP mark protobuf as system include Triggered-By: cmake 3aab0ecf02c33b3af8a67b6ac4fd2e057f596c18 Upstream-PR: http://github.com/swift-nav/cmake/pull/139 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Add cn0 mask to calc-pvt * apply C/N0 masking in calc_pvt * add unit test * format * behavior unit test * obs mask config as struct * fix unit tests * fix build * pass by pointer Co-authored-by: Ivan Smolyakov Co-authored-by: Ivan Smolyakov <39203083+ismolyakov@users.noreply.github.com> --- bazel: Require build passes in CI Requires that the bazel build passes for changes to be merged. Co-authored-by: Isaac Torres --- cmake - Support issue suppression for runtime sanitizers Triggered-By: cmake b2d67a6993f4c3252b35a9018830ab920db9bd77 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- CMake submodule update Co-authored-by: Rodrigo Reichert --- Export decode_sisa_index(), decode_bds_ura_index() and decode_fit_interval() functions This allows external code to reuse libswiftnav's functions for decoding URA/SISA index values. Also includes a couple of minor improvements: 1. Move definition of GAL_WEEK_TO_GPS_WEEK from ephemeris.h to gnss_time.h to be consistent with BDS_WEEK_TO_GPS_WEEK 2. Fix test for platforms where time_t is a signed 32-bit integer Co-authored-by: dgburr --- Added Makefile with bazel wrappers * Added Makefile with bazel wrappers * Update rules_swiftnav Co-authored-by: Wiktor Więcław <110531204+wwieclaw@users.noreply.github.com> --- Fix group delay type for B1C * Fix group delay type for B1C * format fix Co-authored-by: Altti Jokinen <117776995+AlttiJokinen@users.noreply.github.com> --- bazel: fix optimized compilation Co-authored-by: Krzysztof Naglik --- cmake - Exclude unit test header files from coverage reporting Triggered-By: cmake 3f59bcdceec39ec25ec4c6c62420ebee4ea7c7ab Upstream-PR: http://github.com/swift-nav/cmake/pull/145 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- cmake - Add plantuml Triggered-By: cmake 4047c088796bd88511bba6d17362a8e95847a88c Upstream-PR: http://github.com/swift-nav/cmake/pull/146 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- clean up iono handling and navigation_measurement_t * clean up iono handling and navigation_measurement_t * Change function names * Move correcting clock error to nav_meas.c Co-authored-by: Altti Jokinen <117776995+AlttiJokinen@users.noreply.github.com> --- Install the leap seconds header Co-authored-by: Joseph Angelo --- bazel add gen_compile_commands target Co-authored-by: Wiktor Więcław <110531204+wwieclaw@users.noreply.github.com> --- bazel: make max_channels a configurable attribute * bazel: make max_channels a configurable attribute * make MAX_CHANNELS param lowercase Co-authored-by: Wiktor Więcław <110531204+wwieclaw@users.noreply.github.com> --- bazel: use c only macros to build libswiftnav Updates libswiftnav to use the new rules for building c only code. Co-authored-by: Isaac Torres --- Handle endianess in bytestreams * Helper functions to byteswapping and detecting endianess * Add helpers for bytestream struct to decode different kinds of ints and handle endianess * format Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> --- Byte swap inline function Co-authored-by: Rodrigo Reichert --- Regenerate leap second Co-authored-by: Lucas Le <97814268+lucasle-sn@users.noreply.github.com> --- cmake - Turn SonarCloud duplicate detection off for unit tests Triggered-By: cmake d7a20ec514ef83525d734d8ee39e03c712adbedc Upstream-PR: http://github.com/swift-nav/cmake/pull/150 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- cmake - Add official MKL cmake finder. Triggered-By: cmake d165dd7c259d40d08805a0e0bdda6ee04c7a54ff Upstream-PR: http://github.com/swift-nav/cmake/pull/147 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- ci: fix Ubuntu 18.04 build Co-authored-by: Jason Mobarak --- ci: remove deprecated os image Co-authored-by: Jason Mobarak --- cmake - fix: make test_srcs target project specific This fixes a duplicate target being defined when enabling the test suite of a projects submodule Triggered-By: cmake 04c7c1444a696987e1345b234696cf5a7dc4fa0f Upstream-PR: http://github.com/swift-nav/cmake/pull/158 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- bazel: Add bazeliskrc file * bazel: Add bazeliskrc file * Use bazel 6.2 * Update docker images * Fix Co-authored-by: Krzysztof Naglik --- cmake - rename starling-core-utils Triggered-By: cmake d3c41e72599789dae172161c50ffe76ea34f7e83 Upstream-PR: http://github.com/swift-nav/cmake/pull/156 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Use float whenever possible in satellite attitude and std Use float in satellite attitude when possible, and in transmitted standard-deviations Co-authored-by: Marco Co-authored-by: Altti Jokinen Co-authored-by: Marco Mendonca --- Revert "Use float whenever possible in satellite attitude and std " Co-authored-by: Marco Mendonca --- fix: bazel: make stderr logging configurable Adds a config option in bazel to disable stderr logging so that it is equivalent to cmake. Co-authored-by: Isaac Torres --- Use float is satellite vel and acc when possible Changed satellite acceleration and velocity to float when possible Co-authored-by: Marco Co-authored-by: Altti Jokinen Co-authored-by: Marco Mendonca --- Handle BDS B3I in get_tgd_correction() Co-authored-by: Leith Bade --- fix: bazel: make stderr logging public ake the flag --//:enable_stderr_logging public so it can be invoked from other projects. Co-authored-by: Isaac Torres --- Revert "Use float is satellite vel and acc when possible " Co-authored-by: Marco Co-authored-by: Marco Mendonca --- cmake - Clang tidy Google Style Guide Triggered-By: cmake 38e6b7a8367673c03b2d5b320a6f7e9f79d286b8 Upstream-PR: http://github.com/swift-nav/cmake/pull/155 Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Decouple unb3m model from gps_time_t * from gps_time_t to doy * fix unit tests * unit test and format * doy is explicitly non-optional Co-authored-by: Ivan Smolyakov Co-authored-by: Ivan Smolyakov <39203083+ismolyakov@users.noreply.github.com> --- Add support for GPS L1CQ in health check - [ ] Remove assert when code not recognised and add support for GPS L1CQ code Co-authored-by: Antriksh Srivastava Co-authored-by: antrikshsrivastava --- Auto submodule update: cmake - Add zstd finder file Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 38e6b7a8 -> 48d4af0f - 48d4af0f : Add zstd finder file (swift-nav/cmake - be886cac : Update CODEOWNERS (swift-nav/cmake - 040253ff : Lint include guards copyright (swift-nav/cmake - 5df87a24 : add CODEOWNERS (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Update the expiration date to end of June 2024 The IETF leap seconds file appears to not have been updated very quickly, so we've now hit this expiration date without a new IETF file to reference. But fear not! The IETF doesn't decide when leap seconds are, the IERS does and they very reliably release their "Bulletin C" announcing the presence or absence of a leap seconds 6 months later. They also provide a NTP leap seconds file, so I've updated the leap seconds script to use that instead of the file from the IETF. TL;DR Update the leap second expiration. Co-authored-by: Joseph Angelo --- Fix sonarcloud analysis warning Co-authored-by: Arnaud Mallen --- Fix potential out of bounds array access when converting constellation enum to string `constellation_to_string` uses a lookup table to convert an enum value to a string. No validation is performed on the input value which can lead to out of bounds array access. This is actually already happening in some unit tests elsewhere in Swift code but by pure chance is not causing problems at the moment. Remove use of the lookup table and use a switch statement where all possible values are handled. The lookup table is still used elsewhere so can't be removed immediately Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> --- Improvements to libswiftnav to support gnss-converters refactoring 1. Only set clock_drift_var if a velocity solution is estimated 2. Partial revert of fa4a2296d5ce5e6b9e26121409ed31eb8aabd4f5 to restore correct_iono_tropo.h and correct_iono_tropo.c 3. Add tests for correct_iono_tropo 4. Add get_bds_orbit_type() and get_bds_generation() functions Co-authored-by: dgburr --- Bump cmake to be consistent with gnss-converters Co-authored-by: dgburr --- Increased code coverage of libswiftnav - added test cases written in **googletest** for the increased code coverage - updated CMakeLists.txt to include googletest - *implemented unit tests for libswiftnav files* Co-authored-by: Ashish Singh Co-authored-by: Matt Woodward Co-authored-by: Ashish Singh --- Remove compile time selection of geoid model, make both available at runtime The geoid model is controlled at compile time by a cmake option. The default is the model with 1 degree resolution but can be changed to 15 minute resolution by setting a cmake option. This method is abused in gnss-converters in a way which results in pollution of the global compile flags in certain situations. Rather undesierable Alter this repo so that both geoid models are compiled and available in the compiled output. `get_geoid_offset()` always uses the 1 degree resolution model (the default) 2 new functions `get_geoid_offset_1_degree` and `get_geoid_offset_15_minute` do exactly as their name suggests and can be used by consumers of the library if they need a specific version. Technically `get_geoid_offset_1_degree` isn't need since `get_geoid_offset` is just a wrapper, but it's there for completeness sake This will increase the size of a shared object and static archive which is a regression from the status quo. However, since consumers can choose to call into only 1 of the models then the size of the final binary will not increase when using static archives (the linker will remove any uncalled functions/data from the final executable) The code changes here are pretty simple, the geoid offset functions in `geoid_model.c` needed to change a little to be a bit more generic. Also they have to operate on a pointer rather than multidimentional array now but the accesses are few and easy to verify. Implementation of the models are performed in 2 new files with appropriate names The biggest changes are in the test suite which previously was including `geoid_model.c` twice in different namespaces while manipulating `#define` to control which model gets compiled. All of that messiness can be removed now, some of the testing helper functions just needed to be generalise a little bit and the test cases can call in to the new functions directly. Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> --- Auto submodule update: cmake - ASIO C++ and ASIO Grpc libraries Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 954e6ceb -> f5113f7d - f5113f7d : ASIO C++ and ASIO Grpc libraries (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Add CI stage to run tests under ASAN and UBSAN Similar to other PRs in this series. Add bazel config to build with ASAN, UBSAN, TSAN, MSAN Add CI stages to run tests with ASAN and UBSAN. Other sanitizer don't need to be run in CI due to the nature of code in this repository, but are left in config for consistency with other repos and so they can be run manually if required Some minor fixes to code to address issues detected by the sanitisers. Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> --- Add ephemeris_validity_window type and get function * Adds a new ephemeris_validity_window type which contains the beginning and end of an ephemeris validity window. * Adds unittests based on the constellation of the ephemeris. The intention of this change is to allow us to check how far outside of the validity window a reference gps time is and decide whether or not to prune ephemeris. Co-authored-by: John-Michael Burke <43353147+john-michaelburke@users.noreply.github.com> --- Restrict SWIFT_ATTR_NO_SANITIZE_ENUM to clang only Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> --- Auto submodule update: cmake - ASIO gRPC boost container fix Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** f5113f7d -> 68306be8 - 68306be8 : ASIO gRPC boost container fix (swift-nav/cmake - 117b3f73 : enable c++20 (swift-nav/cmake - 436d339a : Handle new suitesparse version (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Update leap seconds for 2024 Co-authored-by: Rodrigo Reichert --- Update bazel image Update the bazel build docker image to use the new [bazel-remote](https://github.com/buchgr/bazel-remote) cache solution. **NOTE**: doing away with Dockerfiles as Isaac mentioned that it adds extra build time for no benefit for us. We should be using docker image directly. Co-authored-by: Rodrigo Reichert --- Auto submodule update: cmake - orion cmake configuration fails when CMAKE_C_COMPILED_ID is not set Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 68306be8 -> 5b65c03c - 65c9a396 : Update compiler options to support IAR (swift-nav/cmake Co-authored-by: Arnaud Mallen Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- CI Pipeline Update Co-authored-by: Arnaud Mallen --- Auto submodule update: cmake - Add codeowners to guideline relevant files Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 5b65c03c -> 30226aa8 - 30226aa8 : Add codeowners to guideline relevant files (swift-nav/cmake - 87ff9dfa : Revert "CMake patches for QNX Support " (swift-nav/cmake - 5d9f739e : CMake patches for QNX Support (swift-nav/cmake - fa934097 : Static bundles for the IAR toolchain[DRAFT] (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Update leap seconds 2025 Co-authored-by: Ivan Smolyakov <39203083+ismolyakov@users.noreply.github.com> --- Update leap seconds file for January 2025 IERS has made their [leap second non-announcement](https://datacenter.iers.org/data/16/bulletinc-069.txt) for end of June, this PR should keep us from having issues for several more months. Co-authored-by: Joseph Angelo --- Move the dependabot config file to the proper location Co-authored-by: Joseph Angelo --- Add definitions for KASS Co-authored-by: dgburr --- Auto submodule update: cmake - Fix importing of latest protobuf version Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 30226aa8 -> 8920f2a3 - 8920f2a3 : Fix importing of latest protobuf version (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Update CMake minimum required version to 3.13 Update CMake version to newer version Co-authored-by: Adharsh Babu --- Auto submodule update: cmake - Update protobuf to v25.1 Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 8920f2a3 -> 361035b6 - 361035b6 : Update protobuf to v25.1 (swift-nav/cmake - 5bf22f7d : Update CMake minimum required version to 3.13 (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Bump jinja2 from 3.0.3 to 3.1.6 in /scripts Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.3 to 3.1.6.
Release notes

Sourced from jinja2's releases.

3.1.6

This is the Jinja 3.1.6 security release, which fixes security issues but does not otherwise change behavior and should not result in breaking changes compared to the latest feature release.

PyPI: https://pypi.org/project/Jinja2/3.1.6/ Changes: https://jinja.palletsprojects.com/en/stable/changes/#-1-6

3.1.5

This is the Jinja 3.1.5 security fix release, which fixes security issues and bugs but does not otherwise change behavior and should not result in breaking changes compared to the latest feature release.

PyPI: https://pypi.org/project/Jinja2/3.1.5/ Changes: https://jinja.palletsprojects.com/changes/#-1-5 Milestone: https://github.com/pallets/jinja/milestone/16?closed=1

  • The sandboxed environment handles indirect calls to str.format, such as by passing a stored reference to a filter that calls its argument. GHSA-q7h
  • Escape template name before formatting it into error messages, to avoid issues with names that contain f-string syntax. , GHSA-
  • Sandbox does not allow clear and pop on known mutable sequence types.
  • Calling sync render for an async template uses asyncio.run.
  • Avoid unclosed auto_aiter warnings.
  • Return an aclose-able AsyncGenerator from Template.generate_async.
  • Avoid leaving root_render_func() unclosed in Template.generate_async.
  • Avoid leaving async generators unclosed in blocks, includes and extends.
  • The runtime uses the correct concat function for the current environment when calling block references.
  • Make |unique async-aware, allowing it to be used after another async-aware filter.
  • |int filter handles OverflowError from scientific notation.
  • Make compiling deterministic for tuple unpacking in a {% set ... %} call.
  • Fix dunder protocol (copy/pickle/etc) interaction with Undefined objects.
  • Fix copy/pickle support for the internal missing object.
  • Environment.overlay(enable_async) is applied correctly.
  • The error message from FileSystemLoader includes the paths that were searched.
  • PackageLoader shows a clearer error message when the package does not contain the templates directory.
  • Improve annotations for methods returning copies.
  • urlize does not add mailto: to values like @a@b.
  • Tests decorated with @pass_context can be used with the |select filter.
  • Using set for multiple assignment (a, b = 1, 2) does not fail when the target is a namespace attribute.
  • Using set in all branches of {% if %}{% elif %}{% else %} blocks does not cause the variable to be considered initially undefined.

3.1.4

This is the Jinja 3.1.4 security release, which fixes security issues and bugs but does not otherwise change behavior and should not result in breaking changes.

PyPI: https://pypi.org/project/Jinja2/3.1.4/ Changes: https://jinja.palletsprojects.com/en/3.1.x/changes/#-1-4

  • The xmlattr filter does not allow keys with / solidus, > greater-than sign, or = equals sign, in addition to disallowing spaces. Regardless of any validation done by Jinja, user input should never be used as keys to this filter, or must be separately validated first. GHSA-mfj

3.1.3

This is a fix release for the 3.1.x feature branch.

  • Fix for GHSA-h5c8-rqwp-cp95. You are affected if you are using xmlattr and passing user input as attribute keys.

... (truncated)

Changelog

Sourced from jinja2's changelog.

Version 3.1.6

Released 2025-03-05

  • The |attr filter does not bypass the environment's attribute lookup, allowing the sandbox to apply its checks. :ghsa:cpwx-pq7

Version 3.1.5

Released 2024-12-21

  • The sandboxed environment handles indirect calls to str.format, such as by passing a stored reference to a filter that calls its argument. :ghsa:q7h
  • Escape template name before formatting it into error messages, to avoid issues with names that contain f-string syntax. :issue:1792, :ghsa:
  • Sandbox does not allow clear and pop on known mutable sequence types. :issue:2032
  • Calling sync render for an async template uses asyncio.run. :pr:1952
  • Avoid unclosed auto_aiter warnings. :pr:1960
  • Return an aclose-able AsyncGenerator from Template.generate_async. :pr:1960
  • Avoid leaving root_render_func() unclosed in Template.generate_async. :pr:1960
  • Avoid leaving async generators unclosed in blocks, includes and extends. :pr:1960
  • The runtime uses the correct concat function for the current environment when calling block references. :issue:1701
  • Make |unique async-aware, allowing it to be used after another async-aware filter. :issue:1781
  • |int filter handles OverflowError from scientific notation. :issue:1921
  • Make compiling deterministic for tuple unpacking in a {% set ... %} call. :issue:2021
  • Fix dunder protocol (copy/pickle/etc) interaction with Undefined objects. :issue:2025
  • Fix copy/pickle support for the internal missing object. :issue:2027
  • Environment.overlay(enable_async) is applied correctly. :pr:2061
  • The error message from FileSystemLoader includes the paths that were searched. :issue:1661
  • PackageLoader shows a clearer error message when the package does not contain the templates directory. :issue:1705
  • Improve annotations for methods returning copies. :pr:1880
  • urlize does not add mailto: to values like @a@b. :pr:1870

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jinja2&package-manager=pip&previous-version=3.0.3&new-version=3.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/swift-nav/libswiftnav-private/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Auto submodule update: cmake - Add option for suppression file in valgrind memcheck Automated PR by Jenkins. If CI has passed successfully, merge away! **cmake** 361035b6 -> 61cdba17 - 61cdba17 : Add option for suppression file in valgrind memcheck (swift-nav/cmake Co-authored-by: swiftnav-svc-jenkins <42622338+swiftnav-svc-jenkins@users.noreply.github.com> --- Update deprecated windows build image Co-authored-by: Sebastian Müller --- Implement new decimal year functions - [x] Move `decimal_years_to_gps_time` to `libswiftnav->gnss_time`. - [x] Move `gps_time_to_decimal_years` to `libswiftnav->gnss_time`. - [x] Move `decimal_year_to_mjd` to `libswiftnav->gnss_time`. - Nothing. - Nothing. Co-authored-by: Alejandro Duarte <32496764+JADC362@users.noreply.github.com> --- Use docker compose instead Co-authored-by: Alejandro Duarte <32496764+JADC362@users.noreply.github.com> --- Update leap seconds file for July 2025 Update per IERS bulletin C released last week: https://datacenter.iers.org/data/html/.html Co-authored-by: Joseph Angelo --- Co-authored-by: Isaac Torres Co-authored-by: Alejandro Duarte <32496764+JADC362@users.noreply.github.com> Co-authored-by: Sebastian Müller Co-authored-by: Adharsh Babu Co-authored-by: dgburr Co-authored-by: Ivan Smolyakov <39203083+ismolyakov@users.noreply.github.com> Co-authored-by: Arnaud Mallen Co-authored-by: Rodrigo Reichert Co-authored-by: Matt Woodward <46688854+woodfell@users.noreply.github.com> Co-authored-by: John-Michael Burke <43353147+john-michaelburke@users.noreply.github.com> Co-authored-by: Ashish Singh Co-authored-by: antrikshsrivastava Co-authored-by: Marco Mendonca Co-authored-by: Leith Bade Co-authored-by: Krzysztof Naglik Co-authored-by: Jason Mobarak Co-authored-by: Lucas Le <97814268+lucasle-sn@users.noreply.github.com> Co-authored-by: Wiktor Więcław <110531204+wwieclaw@users.noreply.github.com> Co-authored-by: Altti Jokinen <117776995+AlttiJokinen@users.noreply.github.com> --- .bazelrc | 95 + .github/dependabot.yaml | 6 + .github/workflows/auto-cancellation.yaml | 11 - .github/workflows/ci.yaml | 109 +- .gitmodules | 6 +- BUILD.bazel | 64 +- CMakeLists.txt | 8 +- Dockerfile.modern | 4 - Jenkinsfile | 58 +- Makefile | 8 +- WORKSPACE.bazel | 17 +- cmake/common | 2 +- include/swiftnav/bits.h | 9 +- include/swiftnav/correct_iono_tropo.h | 37 + include/swiftnav/ephemeris.h | 20 +- include/swiftnav/geoid_model.h | 2 + include/swiftnav/get_unaligned.h | 13 + include/swiftnav/gnss_time.h | 8 + include/swiftnav/leap_seconds.h | 10 +- include/swiftnav/macros.h | 10 + include/swiftnav/signal.h | 39 + scripts/gtx_convert.pl | 4 +- scripts/leap_seconds_generator.py | 2 +- scripts/requirements.txt | 2 +- src/bits.c | 30 +- src/correct_iono_tropo.c | 135 ++ src/ephemeris.c | 35 + src/geoid_model.c | 97 +- src/geoid_model.h | 25 + src/geoid_model_15_minute.c | 13 + src/geoid_model_15_minute.inc | 4 +- src/geoid_model_1_degree.c | 13 + src/geoid_model_1_degree.inc | 4 +- src/gnss_time.c | 47 + src/signal.c | 1948 +++++++++-------- src/single_epoch_solver.c | 4 +- tests/CMakeLists.txt | 81 +- tests/check_almanac.c | 213 -- tests/check_bits.c | 403 ---- tests/check_coord_system.c | 395 ---- tests/check_edc.c | 40 - tests/check_geoid_model.cc | 218 -- tests/check_glo_map.c | 109 - tests/check_gnss_time_cpp.cc | 99 - tests/check_linear_algebra.c | 756 ------- tests/check_log.c | 106 - tests/check_main.c | 41 - tests/check_nav_meas.c | 171 -- tests/check_pvt.c | 792 ------- tests/check_sid_set.c | 97 - tests/check_signal.c | 618 ------ tests/check_subsystem_status_report.c | 257 --- tests/check_suites.h | 35 - tests/check_troposphere.c | 100 - tests/check_utils.h | 32 + tests/common/CMakeLists.txt | 2 - tests/common/check_utils.c | 44 - tests/common/check_utils.h | 8 - tests/test_almanac.cc | 203 ++ tests/test_bits.cc | 340 +++ tests/test_coord_system.cc | 317 +++ tests/test_correct_iono_tropo.cc | 153 ++ tests/test_data.h | 184 ++ ...{check_decode_glo.c => test_decode_glo.cc} | 109 +- tests/test_edc.cc | 28 + .../{check_ephemeris.c => test_ephemeris.cc} | 787 +++---- tests/test_geoid_model.cc | 148 ++ tests/test_glo_map.cc | 71 + .../{check_gnss_time.c => test_gnss_time.cc} | 750 +++---- tests/test_gnss_time_cpp.cc | 73 + ...{check_ionosphere.c => test_ionosphere.cc} | 89 +- tests/test_linear_algebra.cc | 713 ++++++ tests/test_log.cc | 75 + tests/test_nav_meas.cc | 134 ++ tests/test_pvt.cc | 489 +++++ tests/{check_set.c => test_set.cc} | 184 +- tests/{check_shm.c => test_shm.cc} | 33 +- tests/test_sid_set.cc | 69 + tests/test_signal.cc | 543 +++++ tests/test_subsystem_status_report.cc | 238 ++ tests/test_troposphere.cc | 83 + third_party/check | 1 - third_party/googletest | 1 + 83 files changed, 6595 insertions(+), 6736 deletions(-) create mode 100644 .github/dependabot.yaml delete mode 100644 .github/workflows/auto-cancellation.yaml delete mode 100644 Dockerfile.modern create mode 100644 include/swiftnav/correct_iono_tropo.h create mode 100644 include/swiftnav/get_unaligned.h create mode 100644 src/correct_iono_tropo.c create mode 100644 src/geoid_model.h create mode 100644 src/geoid_model_15_minute.c create mode 100644 src/geoid_model_1_degree.c delete mode 100644 tests/check_almanac.c delete mode 100644 tests/check_bits.c delete mode 100644 tests/check_coord_system.c delete mode 100644 tests/check_edc.c delete mode 100644 tests/check_geoid_model.cc delete mode 100644 tests/check_glo_map.c delete mode 100644 tests/check_gnss_time_cpp.cc delete mode 100644 tests/check_linear_algebra.c delete mode 100644 tests/check_log.c delete mode 100644 tests/check_main.c delete mode 100644 tests/check_nav_meas.c delete mode 100644 tests/check_pvt.c delete mode 100644 tests/check_sid_set.c delete mode 100644 tests/check_signal.c delete mode 100644 tests/check_subsystem_status_report.c delete mode 100644 tests/check_suites.h delete mode 100644 tests/check_troposphere.c create mode 100644 tests/check_utils.h delete mode 100644 tests/common/CMakeLists.txt delete mode 100644 tests/common/check_utils.c delete mode 100644 tests/common/check_utils.h create mode 100644 tests/test_almanac.cc create mode 100644 tests/test_bits.cc create mode 100644 tests/test_coord_system.cc create mode 100644 tests/test_correct_iono_tropo.cc create mode 100644 tests/test_data.h rename tests/{check_decode_glo.c => test_decode_glo.cc} (68%) create mode 100644 tests/test_edc.cc rename tests/{check_ephemeris.c => test_ephemeris.cc} (51%) create mode 100644 tests/test_geoid_model.cc create mode 100644 tests/test_glo_map.cc rename tests/{check_gnss_time.c => test_gnss_time.cc} (54%) create mode 100644 tests/test_gnss_time_cpp.cc rename tests/{check_ionosphere.c => test_ionosphere.cc} (52%) create mode 100644 tests/test_linear_algebra.cc create mode 100644 tests/test_log.cc create mode 100644 tests/test_nav_meas.cc create mode 100644 tests/test_pvt.cc rename tests/{check_set.c => test_set.cc} (62%) rename tests/{check_shm.c => test_shm.cc} (52%) create mode 100644 tests/test_sid_set.cc create mode 100644 tests/test_signal.cc create mode 100644 tests/test_subsystem_status_report.cc create mode 100644 tests/test_troposphere.cc delete mode 160000 third_party/check create mode 160000 third_party/googletest 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 From 3a2bc1acc8b2780261bf72a6cabec76c97d41a30 Mon Sep 17 00:00:00 2001 From: Joseph Angelo Date: Tue, 22 Jul 2025 14:36:55 -0700 Subject: [PATCH 2/2] Fix quality warning --- scripts/leap_seconds_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/leap_seconds_generator.py b/scripts/leap_seconds_generator.py index 2365cd7..baf96be 100755 --- a/scripts/leap_seconds_generator.py +++ b/scripts/leap_seconds_generator.py @@ -144,7 +144,7 @@ def offset_seconds(self, seconds): utc_leap_list.append(UtcClock(int(entries[0]), int(entries[1]))) utc_leap_list_expires = UtcClock(utc_leap_list_expires_utc_time, utc_leap_list[-1].tai_utc_offset()) -now = UtcClock((datetime.datetime.utcnow() - UTC_EPOCH).total_seconds(), utc_leap_list[-1].tai_utc_offset()) +now = UtcClock((datetime.datetime.now(datetime.timezone.utc) - UTC_EPOCH).total_seconds(), utc_leap_list[-1].tai_utc_offset()) gnss_time_latest_template = jinja2.Template(JINJA_GNSS_TIME_LATEST_TEMPLATE) gnss_time_latest_render = gnss_time_latest_template.render({