diff --git a/.autotools b/.autotools new file mode 100644 index 0000000000000000000000000000000000000000..52426e6ee238273b9bedea21c0da52873d4b0bfb --- /dev/null +++ b/.autotools @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configurations> +<configuration id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1456028388"> +<option id="configure" value="configure"/> +<option id="configdir" value=""/> +<option id="cache-file" value=""/> +<option id="help" value="false"/> +<option id="no-create" value="false"/> +<option id="quiet" value="false"/> +<option id="version" value="false"/> +<option id="host" value=""/> +<option id="build" value=""/> +<option id="target" value=""/> +<option id="prefix" value=""/> +<option id="exec-prefix" value=""/> +<option id="libdir" value=""/> +<option id="bindir" value=""/> +<option id="sbindir" value=""/> +<option id="includedir" value=""/> +<option id="datadir" value=""/> +<option id="sysconfdir" value=""/> +<option id="infodir" value=""/> +<option id="mandir" value=""/> +<option id="srcdir" value=""/> +<option id="localstatedir" value=""/> +<option id="sharedstatedir" value=""/> +<option id="libexecdir" value=""/> +<option id="oldincludedir" value=""/> +<option id="program-prefix" value=""/> +<option id="program-suffix" value=""/> +<option id="program-transform-name" value=""/> +<option id="env_vars" value=""/> +<option id="enable-maintainer-mode" value="false"/> +<flag id="CFLAGS" value="CFLAGS|CXXFLAGS"> +<flagvalue id="cflags-debug" value="false"/> +<flagvalue id="cflags-gprof" value="false"/> +<flagvalue id="cflags-gcov" value="false"/> +</flag> +<option id="user" value=""/> +<option id="autogen" value="autogen.sh"/> +<option id="autogenOpts" value=""/> +</configuration> +<configuration id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.116232725"> +<option id="configure" value="configure"/> +<option id="configdir" value=""/> +<option id="cache-file" value=""/> +<option id="help" value="false"/> +<option id="no-create" value="false"/> +<option id="quiet" value="false"/> +<option id="version" value="false"/> +<option id="host" value=""/> +<option id="build" value=""/> +<option id="target" value=""/> +<option id="prefix" value=""/> +<option id="exec-prefix" value=""/> +<option id="libdir" value=""/> +<option id="bindir" value=""/> +<option id="sbindir" value=""/> +<option id="includedir" value=""/> +<option id="datadir" value=""/> +<option id="sysconfdir" value=""/> +<option id="infodir" value=""/> +<option id="mandir" value=""/> +<option id="srcdir" value=""/> +<option id="localstatedir" value=""/> +<option id="sharedstatedir" value=""/> +<option id="libexecdir" value=""/> +<option id="oldincludedir" value=""/> +<option id="program-prefix" value=""/> +<option id="program-suffix" value=""/> +<option id="program-transform-name" value=""/> +<option id="env_vars" value=""/> +<option id="enable-maintainer-mode" value="false"/> +<flag id="CFLAGS" value="CFLAGS|CXXFLAGS"> +<flagvalue id="cflags-debug" value="false"/> +<flagvalue id="cflags-gprof" value="false"/> +<flagvalue id="cflags-gcov" value="false"/> +</flag> +<option id="user" value="CFLAGS='-g -O0' CXXFLAGS='-g -O0'"/> +<option id="autogen" value="autogen.sh"/> +<option id="autogenOpts" value=""/> +</configuration> +</configurations> diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..697259cc7409fa76ecc080bad109554c15c9c16e --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,99 @@ +stages: + - license + - build_and_test + +############ YAML ANCHORS ############ +.preliminaries_template: &preliminaries_definition + ## Install ssh-agent if not already installed, it is required by Docker. + ## (change apt-get to yum if you use an RPM-based image) + - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' + + ## Run ssh-agent (inside the build environment) + - eval $(ssh-agent -s) + + ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store + ## We're using tr to fix line endings which makes ed25519 keys work + ## without extra base64 encoding. + ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null + # - echo "$SSH_KNOWN_HOSTS" > $HOME/.ssh/known_hosts + - ssh-keyscan -H -p 2202 gitlab.iri.upc.edu >> $HOME/.ssh/known_hosts + + # update apt + - apt-get update + +.license_header_template: &license_header_definition + - cd $CI_PROJECT_DIR + + # configure git + - export CI_NEW_BRANCH=ci_processing$RANDOM + - echo creating new temporary branch... $CI_NEW_BRANCH + #- export CI_NEW_BRANCH=ci_processing$CI_COMMIT_SHORT_SHA + - git config --global user.email "${CI_EMAIL}" + - git config --global user.name "${CI_USERNAME}" + - git checkout -b $CI_NEW_BRANCH # temporary branch + + # license headers + - export CURRENT_YEAR=$( date +'%Y' ) + - echo "current year:" ${CURRENT_YEAR} + - cd scripts + - if [ -f license_header_${CURRENT_YEAR}.txt ]; then + # add license headers to new files + - echo "File license_header_${CURRENT_YEAR}.txt already exists. License headers are assumed to be updated. Adding headers to new files..." + - ./license_manager.sh --add --path=${CI_PROJECT_DIR} --license-header=license_header_${CURRENT_YEAR}.txt + - else + # update license headers of all files + - export PREV_YEAR=$(( CURRENT_YEAR-1 )) + - echo "Creating new file license_header_${CURRENT_YEAR}.txt..." + - git mv license_header_${PREV_YEAR}.txt license_header_${CURRENT_YEAR}.txt + - sed -i "s/${PREV_YEAR}/${PREV_YEAR},${CURRENT_YEAR}/g" license_header_${CURRENT_YEAR}.txt + - ./license_manager.sh --update --path=${CI_PROJECT_DIR} --license-header=license_header_${CURRENT_YEAR}.txt + - fi + - cd .. + + # push changes (if any) + - if git commit -a -m "[skip ci] license headers added or modified" ; then + - git remote set-url --push origin "ssh://git@gitlab.iri.upc.edu:2202/${CI_PROJECT_PATH}.git" + - git push origin $CI_NEW_BRANCH:${CI_COMMIT_REF_NAME} + - else + - echo "No changes, nothing to commit!" + - fi + +.build_and_test_template: &build_and_test_definition + - cd $CI_PROJECT_DIR + - git submodule update --init + - mkdir -pv build + - cd build + - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON .. + - make -j2 + - ctest -j2 + - make install + +############ LICENSE HEADERS ############ +license_headers: + stage: license + image: labrobotica/wolf_deps:16.04 + before_script: + - *preliminaries_definition + script: + - *license_header_definition + +############ UBUNTU 16.04 TESTS ############ +build_and_test_none:xenial: + stage: build_and_test + image: labrobotica/wolf_vision_deps:16.04 + before_script: + - *preliminaries_definition + script: + - *build_and_test_definition + +############ UBUNTU 18.04 TESTS ############ +build_and_test_none:bionic: + stage: build_and_test + image: labrobotica/wolf_vision_deps:18.04 + before_script: + - *preliminaries_definition + script: + - *build_and_test_definition \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 2999783577610c44e39242e31c38b22559de61b4..67ac92ab606b3e7908bd301c76b63e9fa78b0b58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "deps/RTKLIB"] path = deps/RTKLIB - url = ssh://git@gitlab.iri.upc.edu:2202/mobile_robotics/gauss_project/RTKLIB.git - branch = demo5 + url = https://gitlab.iri.upc.edu/mobile_robotics/gauss_project/RTKLIB.git + branch = devel diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ddfb998b6c1640d5fb46d6a57d8a469f5b74b06..5d81f04804bcc35bc58b360cc50f69f49eb91898 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,8 @@ +#Start gnss-utils build +MESSAGE("Starting gnss-utils CMakeLists ...") + # Pre-requisites about cmake itself -CMAKE_MINIMUM_REQUIRED(VERSION 2.4) +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) if(COMMAND cmake_policy) cmake_policy(SET CMP0005 NEW) @@ -7,8 +10,11 @@ if(COMMAND cmake_policy) endif(COMMAND cmake_policy) +# MAC OSX RPATH +set(CMAKE_MACOSX_RPATH true) + # The project name and the type of project -PROJECT(gnss-utils) +PROJECT(gnss_utils) SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) @@ -35,7 +41,127 @@ else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() -ADD_SUBDIRECTORY(src) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") + +FIND_PACKAGE(Eigen3 3.3 REQUIRED) +IF(Eigen3_FOUND) + INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIRS}) +ENDIF(Eigen3_FOUND) + + +# Testing +IF(NOT BUILD_TESTS) + OPTION(BUILD_TESTS "Build Unit tests" ON) +ENDIF(NOT BUILD_TESTS) +if(BUILD_TESTS) + # Enables testing for this directory and below. + # Note that ctest expects to find a test file in the build directory root. + # Therefore, this command should be in the source directory root. + #include(CTest) # according to http://public.kitware.com/pipermail/cmake/2012-June/050853.html + MESSAGE("Building tests.") + enable_testing() + set(_GNSS_UTILS_ROOT_DIR ${CMAKE_SOURCE_DIR}) +endif() + +# Define the directory where will be the configured config.h +SET(GNSS_UTILS_CONFIG_DIR ${PROJECT_BINARY_DIR}/conf/gnss_utils/internal) + +# Create the specified output directory if it does not exist. +IF(NOT EXISTS "${GNSS_UTILS_CONFIG_DIR}") + message(STATUS "Creating config output directory: ${GNSS_UTILS_CONFIG_DIR}") + file(MAKE_DIRECTORY "${GNSS_UTILS_CONFIG_DIR}") +ENDIF() +IF(EXISTS "${GNSS_UTILS_CONFIG_DIR}" AND NOT IS_DIRECTORY "${GNSS_UTILS_CONFIG_DIR}") + message(FATAL_ERROR "Bug: Specified CONFIG_DIR: " + "${GNSS_UTILS_CONFIG_DIR} exists, but is not a directory.") +ENDIF() +# Configure config.h +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/internal/config.h.in "${GNSS_UTILS_CONFIG_DIR}/config.h") +message("GNSS_UTILS CONFIG ${GNSS_UTILS_CONFIG_DIR}/config.h") +message("CONFIG DIRECTORY ${PROJECT_BINARY_DIR}") +include_directories("${PROJECT_BINARY_DIR}/conf") + +# rtklib path +SET(RTKLIB_DIR deps/RTKLIB) +SET(RTKLIB_SRC_DIR ${RTKLIB_DIR}/src) + +# driver source files +SET(SOURCES + src/navigation.cpp + src/observations.cpp + src/range.cpp + src/receiver_raw_base.cpp + src/receivers/ublox_raw.cpp + src/receivers/novatel_raw.cpp + src/snapshot.cpp + src/tdcp.cpp + src/utils/utils.cpp + src/utils/transformations.cpp + src/utils/rcv_position.cpp + src/utils/satellite.cpp) + +SET(RTKLIB_SRC + ${RTKLIB_SRC_DIR}/pntpos.c + ${RTKLIB_SRC_DIR}/rtkcmn.c + ${RTKLIB_SRC_DIR}/sbas.c + ${RTKLIB_SRC_DIR}/ephemeris.c + ${RTKLIB_SRC_DIR}/preceph.c + ${RTKLIB_SRC_DIR}/qzslex.c + ${RTKLIB_SRC_DIR}/rtcm.c + ${RTKLIB_SRC_DIR}/rtcm2.c + ${RTKLIB_SRC_DIR}/rtcm3.c + ${RTKLIB_SRC_DIR}/rtcm3e.c + ${RTKLIB_SRC_DIR}/ionex.c + ${RTKLIB_SRC_DIR}/rinex.c + ${RTKLIB_SRC_DIR}/rcvraw.c + ${RTKLIB_SRC_DIR}/rcv/binex.c + ${RTKLIB_SRC_DIR}/rcv/cmr.c + ${RTKLIB_SRC_DIR}/rcv/comnav.c + ${RTKLIB_SRC_DIR}/rcv/crescent.c + ${RTKLIB_SRC_DIR}/rcv/gw10.c + ${RTKLIB_SRC_DIR}/rcv/javad.c + ${RTKLIB_SRC_DIR}/rcv/novatel.c + ${RTKLIB_SRC_DIR}/rcv/nvs.c + ${RTKLIB_SRC_DIR}/rcv/rt17.c + ${RTKLIB_SRC_DIR}/rcv/septentrio.c + ${RTKLIB_SRC_DIR}/rcv/skytraq.c + ${RTKLIB_SRC_DIR}/rcv/swiftnav.c + ${RTKLIB_SRC_DIR}/rcv/tersus.c + ${RTKLIB_SRC_DIR}/rcv/ublox.c) + +# application header files +# NOT SET SINCE WE COPY THE WHOLE include FOLDER + +# Eigen ####### +FIND_PACKAGE(Eigen3 REQUIRED) + +# Adding libraries' directories +link_directories(/usr/lib/x86_64-linux-gnu/) + +# Adding include directories +INCLUDE_DIRECTORIES(include/ ${EIGEN3_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${RTKLIB_SRC_DIR}) + +# create the shared library +ADD_LIBRARY(gnss_utils SHARED ${SOURCES} ${RTKLIB_SRC}) +TARGET_LINK_LIBRARIES(gnss_utils ${Boost_LIBRARIES}) + +# Examples +ADD_SUBDIRECTORY(src/examples) +# Testing +if(BUILD_TESTS) + add_subdirectory(test) +endif() + +# Installing +INSTALL(TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) +INSTALL(DIRECTORY include/gnss_utils DESTINATION include/iri-algorithms) +INSTALL(FILES ${RTKLIB_SRC_DIR}/rtklib.h DESTINATION include/iri-algorithms/gnss_utils) +INSTALL(FILES Findgnss_utils.cmake DESTINATION ${CMAKE_ROOT}/Modules/) +INSTALL(FILES "${GNSS_UTILS_CONFIG_DIR}/config.h" + DESTINATION include/iri-algorithms/gnss_utils/internal) FIND_PACKAGE(Doxygen) diff --git a/Findgnss_utils.cmake b/Findgnss_utils.cmake index 85306e55c56ff0aea7537dcf31d091f2a426d9dc..9d725b5ebf40de6df5168645034348bba16c464f 100644 --- a/Findgnss_utils.cmake +++ b/Findgnss_utils.cmake @@ -1,17 +1,36 @@ -#edit the following line to add the librarie's header files -FIND_PATH(gnss_utils_INCLUDE_DIR gnss_utils.h obervation.h /usr/include/iridrivers /usr/local/lib) +FIND_PATH( + gnss_utils_INCLUDE_DIRS + NAMES gnss_utils.h + PATHS /usr/local/include/iri-algorithms/gnss_utils /usr/local/lib/iri-algorithms/gnss_utils) +#change INCLUDE_DIRS to its parent directory (to force includes with libname in front +get_filename_component(gnss_utils_INCLUDE_DIRS ${gnss_utils_INCLUDE_DIRS} DIRECTORY) +IF(gnss_utils_INCLUDE_DIRS) + MESSAGE("Found gnss_utils include dirs: ${gnss_utils_INCLUDE_DIRS}") +ELSE(gnss_utils_INCLUDE_DIRS) + MESSAGE("Couldn't find gnss_utils include dirs") +ENDIF(gnss_utils_INCLUDE_DIRS) -FIND_LIBRARY(gnss_utils_LIBRARY - NAMES gnss_utils - PATHS /usr/lib /usr/local/lib /usr/local/lib) +FIND_LIBRARY( + gnss_utils_LIBRARIES + NAMES libgnss_utils.so libgnss_utils.dylib + PATHS /usr/local/lib/iri-algorithms) +IF(gnss_utils_LIBRARIES) + MESSAGE("Found gnss_utils lib: ${gnss_utils_LIBRARIES}") +ELSE(gnss_utils_LIBRARIES) + MESSAGE("Couldn't find gnss_utils lib") +ENDIF(gnss_utils_LIBRARIES) -IF (gnss_utils_INCLUDE_DIR AND gnss_utils_LIBRARY) +IF (gnss_utils_INCLUDE_DIRS AND gnss_utils_LIBRARIES) SET(gnss_utils_FOUND TRUE) -ENDIF (gnss_utils_INCLUDE_DIR AND gnss_utils_LIBRARY) + SET(gnss_utils_INCLUDE_DIR ${gnss_utils_INCLUDE_DIRS}) + SET(gnss_utils_LIBRARY ${gnss_utils_LIBRARIES}) + ELSE(gnss_utils_INCLUDE_DIRS AND gnss_utils_LIBRARIES) + set(gnss_utils_FOUND FALSE) +ENDIF (gnss_utils_INCLUDE_DIRS AND gnss_utils_LIBRARIES) IF (gnss_utils_FOUND) IF (NOT gnss_utils_FIND_QUIETLY) - MESSAGE(STATUS "Found gnss_utils: ${gnss_utils_LIBRARY}") + MESSAGE(STATUS "Found gnss_utils: ${gnss_utils_LIBRARIES}") ENDIF (NOT gnss_utils_FIND_QUIETLY) ELSE (gnss_utils_FOUND) IF (gnss_utils_FIND_REQUIRED) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..281d399f13dfd23087acc56303dd38d68162587c --- /dev/null +++ b/LICENSE @@ -0,0 +1,619 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/README.md b/README.md index 39d71593705405be0126418a087a434893f8c33b..8db7c470a0e95d47e2f01e033e8636ba77959a91 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,11 @@ cd <your/path> git clone <repo-link> ``` +Clone the RTKLIB lib as a sub-module: +``` +git submodule update --init +``` + Create a directory to build all the source files: ``` $ mkdir build diff --git a/include/gnss_utils/gnss_utils.h b/include/gnss_utils/gnss_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..c00bf50fd66b5e23484af5f8bcc75d5768425ecd --- /dev/null +++ b/include/gnss_utils/gnss_utils.h @@ -0,0 +1,234 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_GNSS_UTILS_H_ +#define INCLUDE_GNSS_UTILS_GNSS_UTILS_H_ + +extern "C" { +#include "rtklib.h" +} + +// std includes +#include <set> +#include <map> +#include <vector> +#include <iostream> +#include <memory> +#include <cassert> +// eigen +#include <Eigen/Dense> + +#ifndef RAD2DEG +#define RAD2DEG 180.0 / 3.14159265358979323846 +#endif + +#ifndef DEG2RAD +#define DEG2RAD 3.14159265358979323846 / 180.0 +#endif + +namespace GnssUtils +{ + +// Structs +struct ComputePosOutput +{ + time_t time; + double sec; + Eigen::Vector3d pos; // position (m) + Eigen::Vector3d vel; // velocity (m/s) + Eigen::Matrix3d pos_covar; // position covariance (m^2) + // {c_xx,c_yy,c_zz,c_xy,c_yz,c_zx} + Eigen::VectorXd rcv_bias; // receiver clock bias to time systems (s) 0: receiver clock bias (s), 1: glo-gps time offset (s), 2: gal-gps time offset (s), 3: bds-gps time offset (s) + int type; // coordinates used (0:xyz-ecef,1:enu-baseline) + int stat; // solution status (SOLQ_???) + int ns; // number of valid satellites + double age; // age of differential (s) + double ratio; // AR ratio factor for valiation + + bool success; // return from pntpos true/false + std::string msg; // message returned (in case of error) + Eigen::Vector3d lat_lon; // latitude_longitude_altitude + + std::map<int,Eigen::Vector2d> sat_azel; // azimuth and elevation of each satellite provided + std::set<int> used_sats; // used sats for computing fix (applying RAIM if the case and discarding wrong data) + std::set<int> discarded_sats; // discarded sats when computing fix (applying RAIM if the case and discarding wrong data) + std::map<int,double> prange_residuals; // residuals of used pseudoranges (applying RAIM if the case and discarding wrong data) +}; + +/* defaults processing options */ +const prcopt_t default_prcopt = { + PMODE_SINGLE, /* mode: positioning mode (PMODE_???) */ + 0, /* soltype: solution type (0:forward,1:backward,2:combined) */ + 2, /* nf: number of frequencies (1:L1,2:L1+L2,3:L1+L2+L3,4:L1+L2+L3+L4) */ + SYS_GPS|SYS_GLO|SYS_GAL, /* navsys */ + 15.0*D2R,{{0,0}}, /* elmin (rad) ,snrmask */ + 0, /* satellite ephemeris option: EPHOPT_BRDC(0):broadcast ephemeris, EPHOPT_PREC(1): precise ephemeris, EPHOPT_SBAS(2): broadcast + SBAS, EPHOPT_SSRAPC(3): broadcast + SSR_APC, EPHOPT_SSRCOM(4): broadcast + SSR_COM, EPHOPT_LEX(5): QZSS LEX ephemeris */ + 3,3,1,0,1, /* modear,glomodear,gpsmodear,bdsmodear,arfilter */ + 20,0,4,5,10,20, /* maxout,minlock,minfixsats,minholdsats,mindropsats,minfix */ + 0,1, /* rcvstds,armaxiter */ + IONOOPT_BRDC, /* ionoopt: IONOOPT_OFF(0):correction off, IONOOPT_BRDC(1):broadcast mode, IONOOPT_SBAS(2):SBAS model, IONOOPT_IFLC(3):L1/L2 or L1/L5, IONOOPT_EST(4):estimation, IONOOPT_TEC(5):IONEX TEC model, IONOOPT_QZS(6):QZSS broadcast, IONOOPT_LEX(7):QZSS LEX ionosphere, IONOOPT_STEC(8):SLANT TEC mode */ + TROPOPT_SAAS, /* tropopt: TROPOPT_OFF(0):correction off, TROPOPT_SAAS(1):Saastamoinen model, TROPOPT_SBAS(2):SBAS model, TROPOPT_EST(3):troposphere option: ZTD estimation, TROPOPT_ESTG(4):ZTD+grad estimation, TROPOPT_ZTD(5):ZTD correction,6:ZTD+grad correction */ + 1,0, /* dynamics,tidecorr */ + 3, /* niter: number of filter iteration */ + 0,0, /* codesmooth,intpref */ + 0, /* sbascorr: SBAS correction options (can be added): SBSOPT_LCORR(1): long term correction, SBSOPT_FCORR(2): fast correction, SBSOPT_ICORR(4): ionosphere correction, SBSOPT_RANGE(8): ranging */ + 0, /* sbasssatsel: SBAS satellite selection (0:all) */ + 0,0, /* rovpos,refpos */ + WEIGHTOPT_ELEVATION, /* weightmode */ + {300.0,300.0,300.0}, /* eratio[]: measurement error factor [0]:reserved [1-3]:error factor a/b/c of phase (m) [4]:doppler frequency (hz) [5]: snr max value (dB.Hz) */ + {100.0,0.003,0.003,0.0,1.0,52.0}, /* err[]: */ + {30.0,0.03,0.3}, /* std[] initial-state std [0]bias,[1]iono [2]trop */ + {1E-4,1E-3,1E-4,1E-1,1E-2,0.0}, /* prn[] */ + 5E-12, /* sclkstab */ + {3.0,0.25,0.0,1E-9,1E-5,0.0,0.0,0.0}, /* thresar */ + 0.0,0.0,0.05,0.1,0.01, /* elmaskar,elmaskhold,thresslip,varholdamb,gainholdamb */ + 30.0, /* maxtdiff: max difference of time (sec) */ + 5.0, /* maxinno: reject threshold of innovation (m) */ + 30.0, /* maxgdop: reject threshold of gdop */ + {0},{0},{0}, /* baseline,ru,rb */ + {"",""}, /* anttype */ + {{0}},{{0}},{0}, /* antdel,pcvr,exsats */ + 1,1, /* maxaveep,initrst */ + 0, /* outsingle */ + {{0}}, /* rnxopt */ + {0,0,0,0,1,0}, /* posopt: positioning options [0-1]:PPP pcv [2]:PPP phase windup [3]:PPP exclude eclipsing [4]:PNTPOS RAIM, [5]:PPP clock jump */ + 0, /* syncsol */ + {{0}}, /* odisp */ + {{0}}, /* exterr */ + 0, /* freqopt */ + {0} /* pppopt */ +}; + +struct TdcpOptions +{ + bool loss_function; // apply loss function in TDCP factors + double sigma_atm; + double sigma_carrier; + bool use_old_nav; + bool multi_freq; + double time_window; // window of time in which we perform TDCP + + // Batch TDCP params + bool batch; // precompute global displacement between 2 epochs + int min_common_sats; + int raim_n; + bool validate_residual; + double max_residual_ci; // Confidence interval that defines the max residual to be considered good solution. RAIM applied if enabled in this case. + bool relinearize_jacobian; + int max_iterations; + int residual_opt; // 0: Normalized RMS of residual vector. 1: Max residual in Mahalanobis squared distance + int sagnac_correction; // 0 deactivated, 1/2 substraction/addition +}; + +struct CarrierPhaseOptions +{ + bool corr_iono; + bool corr_tropo; + bool corr_clock; +}; + +struct Options +{ + int sateph; // satellite ephemeris option: EPHOPT_BRDC(0):broadcast ephemeris, EPHOPT_PREC(1): precise ephemeris, EPHOPT_SBAS(2): broadcast + SBAS, EPHOPT_SSRAPC(3): broadcast + SSR_APC, EPHOPT_SSRCOM(4): broadcast + SSR_COM, EPHOPT_LEX(5): QZSS LEX ephemeris, EPHOPT_SBAS2(6):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), EPHOPT_SBAS3(7):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_SBAS2), EPHOPT_SBAS4(8):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_BRDC) + int ionoopt; // ionosphere option: IONOOPT_OFF(0):correction off, IONOOPT_BRDC(1):broadcast mode, IONOOPT_SBAS(2):SBAS model, IONOOPT_IFLC(3):L1/L2 or L1/L5, IONOOPT_EST(4):estimation, IONOOPT_TEC(5):IONEX TEC model, IONOOPT_QZS(6):QZSS broadcast, IONOOPT_LEX(7):QZSS LEX ionosphere, IONOOPT_STEC(8):SLANT TEC mode, IONOOPT_SBAS2(9):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), IONOOPT_SBAS3(10):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_SBAS2), IONOOPT_SBAS4(11):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_BRDC) + int tropopt; // troposphere option: TROPOPT_OFF(0):correction off, TROPOPT_SAAS(1):Saastamoinen model, TROPOPT_SBAS(2):SBAS model, TROPOPT_EST(3):troposphere option: ZTD estimation, TROPOPT_ESTG(4):ZTD+grad estimation, TROPOPT_ZTD(5):ZTD correction,6:ZTD+grad correction + int sbascorr; // SBAS correction options (can be added): SBSOPT_LCORR(1): long term correction, SBSOPT_FCORR(2): fast correction, SBSOPT_ICORR(4): ionosphere correction, SBSOPT_RANGE(8): ranging + int raim; // RAIM removed sats + double elmin; // min elevation (rad) + double maxgdop; // maxgdop: reject threshold of gdop + bool GPS,SBS,GLO,GAL,QZS,CMP,IRN,LEO; // constellations used + CarrierPhaseOptions carrier_opt; // Carrier phase correction options + + // compute navsys int + int getNavSys() const + { + return GPS*SYS_GPS + SBS*SYS_SBS + GLO*SYS_GLO + GAL*SYS_GAL + QZS*SYS_QZS + CMP*SYS_CMP + IRN*SYS_IRN + LEO*SYS_LEO; + } + + snrmask_t getSnrMask() const + { + return default_prcopt.snrmask; + } + + // create a rtklib option struct from this + prcopt_t getPrcopt() const + { + prcopt_t opt{default_prcopt}; + opt.sateph = sateph; + opt.ionoopt = ionoopt; + opt.tropopt = tropopt; + opt.sbascorr = sbascorr; + opt.posopt[4] = raim; + opt.elmin = elmin; + opt.maxgdop = maxgdop; + opt.navsys = getNavSys(); + return opt; + } +}; + +const Options default_options = +{ + EPHOPT_BRDC, // satellite ephemeris option: EPHOPT_BRDC(0):broadcast ephemeris, EPHOPT_PREC(1): precise ephemeris, EPHOPT_SBAS(2): broadcast + SBAS, EPHOPT_SSRAPC(3): broadcast + SSR_APC, EPHOPT_SSRCOM(4): broadcast + SSR_COM, EPHOPT_LEX(5): QZSS LEX ephemeris, EPHOPT_SBAS2(6):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), EPHOPT_SBAS3(7):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_SBAS2), EPHOPT_SBAS4(8):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_BRDC) + IONOOPT_BRDC, // ionosphere option: IONOOPT_OFF(0):correction off, IONOOPT_BRDC(1):broadcast mode, IONOOPT_SBAS(2):SBAS model, IONOOPT_IFLC(3):L1/L2 or L1/L5, IONOOPT_EST(4):estimation, IONOOPT_TEC(5):IONEX TEC model, IONOOPT_QZS(6):QZSS broadcast, IONOOPT_LEX(7):QZSS LEX ionosphere, IONOOPT_STEC(8):SLANT TEC mode, IONOOPT_SBAS2(9):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), IONOOPT_SBAS3(10):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_SBAS2), IONOOPT_SBAS4(8):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_BRDC) + TROPOPT_SAAS, // troposphere option: TROPOPT_OFF(0):correction off, TROPOPT_SAAS(1):Saastamoinen model, TROPOPT_SBAS(2):SBAS model, TROPOPT_EST(3):troposphere option: ZTD estimation, TROPOPT_ESTG(4):ZTD+grad estimation, TROPOPT_ZTD(5):ZTD correction,6:ZTD+grad correction + 15, // SBAS correction options (can be added): SBSOPT_LCORR(1): long term correction, SBSOPT_FCORR(2): fast correction, SBSOPT_ICORR(4): ionosphere correction, SBSOPT_RANGE(8): ranging + 1, // RAIM enabled + D2R*15.0, // min elevation (degrees) + 30.0, // maxgdop: reject threshold of gdop + true, false, true, true, false, false, false, false, //GPS,SBS,GLO,GAL,QZS,CMP,IRN,LEO; // constellations used + {true, true, true} // carrier phase options +}; + +// forward declarations +class Observations; +class Navigation; +class Snapshot; +struct Satellite; +class Range; + +// Typedefs +typedef std::map<int,Satellite> Satellites; +typedef std::map<int,Range> Ranges; +typedef std::map<int,Eigen::Vector2d> Azels; // Azimuth and elevation (rad) + +// pointer typedefs +typedef std::shared_ptr<Observations> ObservationsPtr; +typedef std::shared_ptr<const Observations> ObservationsConstPtr; + +typedef std::shared_ptr<Navigation> NavigationPtr; +typedef std::shared_ptr<const Navigation> NavigationConstPtr; + +typedef std::shared_ptr<Snapshot> SnapshotPtr; +typedef std::shared_ptr<const Snapshot> SnapshotConstPtr; + +enum Combination +{ + CODE_L1, ///< only L1 code + CARRIER_L1, ///< only L1 carrier phase + CODE_IONO_FREE, ///< iono-free combination for code + CARRIER_IONO_FREE ///< iono-free combination for carrier phase +}; + + + +} + +#endif diff --git a/include/gnss_utils/navigation.h b/include/gnss_utils/navigation.h new file mode 100644 index 0000000000000000000000000000000000000000..ca4580b8fa9f61230477df71797f701c67ad0189 --- /dev/null +++ b/include/gnss_utils/navigation.h @@ -0,0 +1,251 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_NAVIGATION_H_ +#define INCLUDE_GNSS_UTILS_NAVIGATION_H_ + +#include "gnss_utils/gnss_utils.h" +#include "gnss_utils/utils/utils.h" + +namespace GnssUtils +{ + +class Navigation +{ +public: + // Constructor & Destructor + Navigation(); + Navigation(const Navigation& nav); + ~Navigation(); + + // Public objects + + // Public methods + void setNavigation(nav_t nav); + void clearNavigation(); + void + loadFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt = 0.0, const char* opt = ""); + + const nav_t& getNavigation() const; + nav_t& getNavigation(); + + void uniqueNavigation(); // remove duplicated ephemerides and update carrier phase wave lengths + + /****************** Array memory management ******************/ + bool addEphemeris(const eph_t& eph); + bool addGlonassEphemeris(const geph_t& geph); + bool addSbasEphemeris(const seph_t& seph); + bool addPreciseEphemeris(const peph_t& peph); + bool addPreciseClock(const pclk_t& pclk); + bool addAlmanac(const alm_t& alm); + bool addTec(const tec_t& tec); + bool addFcb(const fcbd_t& fcb); + + void addSbasMessage(const sbsmsg_t& sbas_msg); + + void copyAllArrays(const nav_t& nav); + void copyEphemeris(const nav_t& nav); + void copyAlmanac(const nav_t& nav); + void copyIonUtc(const nav_t& nav); + void copySbasCorrections(const nav_t& nav); + void copyBias(const nav_t& nav); + + void freeNavigationArrays(); + void freeEphemeris(); + void freeGlonassEphemeris(); + void freeSbasEphemeris(); + void freePreciseEphemeris(); + void freeAlmanac(); + void freePreciseClock(); + void freeTecData(); + void freeFcbData(); + + void print(); + + //////////////////////////////// nav UTILS ////////////////////////////////////// + static void freeEph(nav_t& nav); + static void freeGeph(nav_t& nav); + static void freeSeph(nav_t& nav); + static void freePeph(nav_t& nav); + static void freeAlm(nav_t& nav); + static void freePclk(nav_t& nav); + static void freeTec(nav_t& nav); + static void freeFcb(nav_t& nav); + static void freeNavArrays(nav_t& nav); + +private: + // rtklib-like attribute to represent the different navigation msgs for a given epoch + nav_t nav_; + + // Private methods +}; + +inline void Navigation::uniqueNavigation() // remove duplicated ephemerides and update wave lengths +{ + uniqnav(&nav_); +} + +inline const nav_t& Navigation::getNavigation() const +{ + return nav_; +} + +inline nav_t& Navigation::getNavigation() +{ + return nav_; +} + +inline bool Navigation::addEphemeris(const eph_t& eph) +{ + return addToArray<eph_t>(eph, nav_.eph, nav_.n, nav_.nmax); +} + +inline bool Navigation::addGlonassEphemeris(const geph_t& geph) +{ + return addToArray<geph_t>(geph, nav_.geph, nav_.ng, nav_.ngmax); +} + +inline bool Navigation::addSbasEphemeris(const seph_t& seph) +{ + return addToArray<seph_t>(seph, nav_.seph, nav_.ns, nav_.nsmax); +} + +inline bool Navigation::addPreciseEphemeris(const peph_t& peph) +{ + return addToArray<peph_t>(peph, nav_.peph, nav_.ne, nav_.nemax); +} + +inline bool Navigation::addPreciseClock(const pclk_t& pclk) +{ + return addToArray<pclk_t>(pclk, nav_.pclk, nav_.nc, nav_.ncmax); +} + +inline bool Navigation::addAlmanac(const alm_t& alm) +{ + return addToArray<alm_t>(alm, nav_.alm, nav_.na, nav_.namax); +} + +inline bool Navigation::addTec(const tec_t& tec) +{ + return addToArray<tec_t>(tec, nav_.tec, nav_.nt, nav_.ntmax); +} + +inline bool Navigation::addFcb(const fcbd_t& fcb) +{ + return addToArray<fcbd_t>(fcb, nav_.fcb, nav_.nf, nav_.nfmax); +} + +inline void Navigation::freeEphemeris() +{ + freeEph(nav_); +} + +inline void Navigation::freeGlonassEphemeris() +{ + freeGeph(nav_); +} + +inline void Navigation::freePreciseEphemeris() +{ + freePeph(nav_); +} + +inline void Navigation::freeSbasEphemeris() +{ + freeSeph(nav_); +} + +inline void Navigation::freePreciseClock() +{ + freePclk(nav_); +} + +inline void Navigation::freeTecData() +{ + freeTec(nav_); +} + +inline void Navigation::freeFcbData() +{ + freeFcb(nav_); +} + +inline void Navigation::freeAlmanac() +{ + freeAlm(nav_); +} + +//////////////////////////////// nav UTILS ////////////////////////////////////// +inline void Navigation::freeEph(nav_t& nav) +{ + freeArray<eph_t>(nav.eph, nav.n, nav.nmax); +} + +inline void Navigation::freeGeph(nav_t& nav) +{ + freeArray<geph_t>(nav.geph, nav.ng, nav.ngmax); +} + +inline void Navigation::freeSeph(nav_t& nav) +{ + freeArray<seph_t>(nav.seph, nav.ns, nav.nsmax); +} + +inline void Navigation::freePeph(nav_t& nav) +{ + freeArray<peph_t>(nav.peph, nav.ne, nav.nemax); +} + +inline void Navigation::freeAlm(nav_t& nav) +{ + freeArray<alm_t>(nav.alm, nav.na, nav.namax); +} + +inline void Navigation::freePclk(nav_t& nav) +{ + freeArray<pclk_t>(nav.pclk, nav.nc, nav.ncmax); +} + +inline void Navigation::freeTec(nav_t& nav) +{ + freeArray<tec_t>(nav.tec, nav.nt, nav.ntmax); +} + +inline void Navigation::freeFcb(nav_t& nav) +{ + freeArray<fcbd_t>(nav.fcb, nav.nf, nav.nfmax); +} + +inline void Navigation::freeNavArrays(nav_t& nav) +{ + // RTKLIB "freenav(&nav_,255)" doesn't check if is NULL before freeing + freeEph(nav); + freeGeph(nav); + freeSeph(nav); + freePeph(nav); + freePclk(nav); + freeAlm(nav); + freeTec(nav); + freeFcb(nav); +} + +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_NAVIGATION_H_ diff --git a/include/gnss_utils/observations.h b/include/gnss_utils/observations.h new file mode 100644 index 0000000000000000000000000000000000000000..52b66bff06e0870ecd968b453feb4bdf6a98a116 --- /dev/null +++ b/include/gnss_utils/observations.h @@ -0,0 +1,182 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_OBSERVATIONS_H_ +#define INCLUDE_GNSS_UTILS_OBSERVATIONS_H_ + +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ + +class Observations +{ +public: + // Constructor & Destructor + Observations(); + Observations(const Observations& obs); + ~Observations(); + + void clearObservations(); + void addObservation(const obsd_t& obs); + void removeObservationByIdx(const int& _idx); + void removeObservationBySat(const int& _sat); + std::vector<obsd_t>& getObservations(); + const std::vector<obsd_t>& getObservations() const; + obsd_t& getObservationBySat(const unsigned char& sat_number); + const obsd_t& getObservationBySat(const unsigned char& sat_number) const; + obsd_t& getObservationByIdx(const int& idx); + const obsd_t& getObservationByIdx(const int& idx) const; + + obsd_t* data(); + const obsd_t* data() const; + size_t size() const; + + bool hasSatellite(const unsigned char& i) const; + + static void print(obsd_t& _obs); + void printBySat(const int& _sat); + void printByIdx(const int& _idx); + void print(); + + // Filter observations + std::set<int> filterByEphemeris(const Satellites& sats); + std::set<int> filterBySatellites(const std::set<int>& discarded_sats); + std::set<int> filterByCode(); + std::set<int> filterByCarrierPhase(); + std::set<int> filterByConstellations(const int& navsys); + std::set<int> filterByElevationSnr(const Azels& azels, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq); + std::set<int> filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt); + std::set<int> filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const int& navsys, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq); + std::set<int> filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt); + std::set<int> filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const int& navsys, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq); + + // Others + static std::set<int> findCommonObservations(const Observations& obs_1, const Observations& obs_2); + + bool operator==(const Observations& other_obs) const; + bool operator!=(const Observations& other_obs) const; + +private: + // Private objects + std::map<unsigned char, int> sat_2_idx_; //< key: corresponding sat number, value: idx in obs_ vector + std::vector<unsigned char> idx_2_sat_; //< key: idx in obs_ vector, value: corresponding sat number + std::vector<obsd_t> obs_; //< vector of RTKLIB observations for a given epoch +}; + +} // namespace GnssUtils + +// IMPLEMENTATION +#include "gnss_utils/utils/utils.h" +#include "gnss_utils/utils/satellite.h" + +namespace GnssUtils +{ +inline const std::vector<obsd_t>& Observations::getObservations() const +{ + return this->obs_; +} + +inline std::vector<obsd_t>& Observations::getObservations() +{ + return this->obs_; +} + +inline obsd_t& Observations::getObservationBySat(const unsigned char& sat_number) +{ + assert(sat_2_idx_.count(sat_number) != 0); + return obs_.at(sat_2_idx_.at(sat_number)); +} + +inline const obsd_t& Observations::getObservationBySat(const unsigned char& sat_number) const +{ + assert(sat_2_idx_.count(sat_number) != 0); + return obs_.at(sat_2_idx_.at(sat_number)); +} + +inline obsd_t& Observations::getObservationByIdx(const int& idx) +{ + assert(obs_.size() > idx && idx >= 0); + return obs_.at(idx); +} + +inline const obsd_t& Observations::getObservationByIdx(const int& idx) const +{ + assert(obs_.size() > idx && idx >= 0); + return obs_.at(idx); +} + +inline obsd_t* Observations::data() +{ + return obs_.data(); +} + +inline const obsd_t* Observations::data() const +{ + return obs_.data(); +} + +inline size_t Observations::size() const +{ + return obs_.size(); +} + +inline bool Observations::hasSatellite(const unsigned char& i) const +{ + return sat_2_idx_.count(i) != 0; +} + +inline bool Observations::operator!=(const Observations& other_obs) const +{ + return !(*this == other_obs); +} + +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_OBSERVATIONS_H_ diff --git a/include/gnss_utils/range.h b/include/gnss_utils/range.h new file mode 100644 index 0000000000000000000000000000000000000000..d783793f6024859988bd867caab49ac6c274264f --- /dev/null +++ b/include/gnss_utils/range.h @@ -0,0 +1,75 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * range.h + * + * Created on: May 28, 2020 + * Author: joanvallve + */ + +#ifndef INCLUDE_GNSS_UTILS_RANGE_H_ +#define INCLUDE_GNSS_UTILS_RANGE_H_ + +#include "gnss_utils/gnss_utils.h" +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" + +namespace GnssUtils +{ + +class Range +{ + public: + int sys = SYS_GPS; + int sat = 0; + double P = -1; + double P_var = 1; + double P_corrected = -1; + double iono_corr = 0; + double tropo_corr = 0; + double sat_clock_corr = 0; + double L = -1; + double L_corrected = -1; + double L_var = 1; + double L2 = -1; + double L2_corrected = -1; + double L2_var = 1; + + public: + Range(); + virtual ~Range(); + + static Ranges computeRanges(ObservationsPtr obs, + NavigationPtr nav, + const Satellites& sats, + const Azels& azel, + const Eigen::Vector3d& latlonalt, + const Options& opt); + + static std::set<int> findCommonSatellites(const Ranges& ranges_1, const Ranges& ranges_2); +}; + +double computeSagnacCorrection(const Eigen::Vector3d& rcv_ECEF, const Eigen::Vector3d& sat_ECEF); + +} /* namespace GnssUtils */ + +#endif /* INCLUDE_GNSS_UTILS_RANGE_H_ */ diff --git a/include/gnss_utils/receiver_raw_base.h b/include/gnss_utils/receiver_raw_base.h new file mode 100644 index 0000000000000000000000000000000000000000..01161ed0747bea499e5490521da407e231b90c61 --- /dev/null +++ b/include/gnss_utils/receiver_raw_base.h @@ -0,0 +1,101 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_RECEIVER_RAW_BASE_H_ +#define INCLUDE_GNSS_UTILS_RECEIVER_RAW_BASE_H_ + +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" + +namespace GnssUtils +{ +enum RawDataType : int +{ + NO = 0, + OBS = 1, + NAV_EPH = 2, + NAV_SBAS = 3, + NAV_ALM_IONUTC = 9, + NAV_ANT = 5, + NAV_DGPS = 7, + NAV_SSR = 10, + NAV_LEX = 31, + ERROR = -1 +}; + +// pointer typedefs +class ReceiverRawAbstract; +typedef std::shared_ptr<ReceiverRawAbstract> ReceiverRawAbstractPtr; +typedef std::shared_ptr<const ReceiverRawAbstract> ReceiverRawAbstractConstPtr; + +class ReceiverRawAbstract +{ +public: + ReceiverRawAbstract(); + ~ReceiverRawAbstract(); + + virtual RawDataType addDataStream(const std::vector<uint8_t>& data_stream) = 0; + + const Observations& getObservations() const; + const Navigation& getNavigation() const; + + Observations getObservations(); + Navigation getNavigation(); + + RawDataType getRawDataType() const; + +protected: + void updateObservations(); + + raw_t raw_data_; + RawDataType raw_data_type_; + + Observations obs_; + Navigation nav_; +}; + +inline const Observations& ReceiverRawAbstract::getObservations() const +{ + return obs_; +} + +inline Observations ReceiverRawAbstract::getObservations() +{ + return obs_; +} + +inline const Navigation& ReceiverRawAbstract::getNavigation() const +{ + return nav_; +} + +inline Navigation ReceiverRawAbstract::getNavigation() +{ + return nav_; +} + +inline RawDataType ReceiverRawAbstract::getRawDataType() const +{ + return raw_data_type_; +} + +} // namespace GnssUtils +#endif diff --git a/include/gnss_utils/receivers/novatel_raw.h b/include/gnss_utils/receivers/novatel_raw.h new file mode 100644 index 0000000000000000000000000000000000000000..d27680351c47f5d937631d7ad9d625f7219df89f --- /dev/null +++ b/include/gnss_utils/receivers/novatel_raw.h @@ -0,0 +1,39 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_NOVATEL_RAW_H_ +#define INCLUDE_GNSS_UTILS_NOVATEL_RAW_H_ + +#include "gnss_utils/receiver_raw_base.h" + +namespace GnssUtils +{ +class NovatelRaw : public ReceiverRawAbstract +{ +public: + NovatelRaw(); + ~NovatelRaw(); + + RawDataType addDataStream(const std::vector<uint8_t>& data_stream) override; +}; +} // namespace GnssUtils + +#endif diff --git a/include/gnss_utils/receivers/ublox_raw.h b/include/gnss_utils/receivers/ublox_raw.h new file mode 100644 index 0000000000000000000000000000000000000000..7cf92bbceba357f5af314bc72ed321cdf0e6d59e --- /dev/null +++ b/include/gnss_utils/receivers/ublox_raw.h @@ -0,0 +1,43 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_UBLOX_RAW_H_ +#define INCLUDE_GNSS_UTILS_UBLOX_RAW_H_ + +#include "gnss_utils/receiver_raw_base.h" + +namespace GnssUtils +{ +class UBloxRaw : public ReceiverRawAbstract +{ +public: + UBloxRaw(); + ~UBloxRaw(); + + RawDataType addDataStream(const std::vector<uint8_t>& data_stream) override; + +private: + +}; + +} // namespace GnssUtils + +#endif // INCLUDE_GNSS_UTILS_UBLOX_RAW_H_ diff --git a/include/gnss_utils/snapshot.h b/include/gnss_utils/snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..2381038cfd24efd7151bcbeca8017badc12e24dc --- /dev/null +++ b/include/gnss_utils/snapshot.h @@ -0,0 +1,201 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_SNAPSHOT_H_ +#define INCLUDE_GNSS_UTILS_SNAPSHOT_H_ + +#include <map> +#include <set> +#include <iostream> +#include <memory> +#include <cassert> + +#include <Eigen/Dense> + +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ + +class Snapshot +{ + +public: + // Constructor & Destructor + Snapshot(); + Snapshot(ObservationsPtr obs, NavigationPtr nav); + ~Snapshot(){}; + + // Public objects + + // Public methods + void loadFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt = 0.0, const char* opt = ""); + double getGPST() const; + + // Navigation + NavigationPtr getNavigation() const; + void setNavigation(NavigationPtr nav); + + // Observations + ObservationsPtr getObservations() const; + void setObservations(ObservationsPtr obs); + + std::set<int> filterObservations(const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt); + std::set<int> filterObservations(const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt); + + // Satellites + const Satellites& getSatellites() const; + Satellites& getSatellites(); + void computeSatellites(const int& eph_opt); // see rtklib.h L396); + bool satellitesComputed() const; + + // Ranges + Ranges& getRanges(); + const Ranges& getRanges() const; + bool rangesComputed() const; + void computeRanges(const Azels& azel, + const Eigen::Vector3d& latlonalt, + const Options& opt); + void computeRanges(const Eigen::Vector3d& x_ecef, + const Options& opt); + void print(); + +private: + // Private objects + Satellites sats_; + ObservationsPtr obs_; + NavigationPtr nav_; + Ranges ranges_; + + // Private methods +}; + +} // namespace GnssUtils + +// IMPLEMENTATION +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" +#include "gnss_utils/utils/utils.h" +#include "gnss_utils/range.h" + +namespace GnssUtils +{ + +inline double Snapshot::getGPST() const +{ + return obs_->getObservations()[0].time.time + obs_->getObservations()[0].time.sec; +} + +inline GnssUtils::ObservationsPtr Snapshot::getObservations() const +{ + return obs_; +} + +inline GnssUtils::NavigationPtr Snapshot::getNavigation() const +{ + return nav_; +} + +inline Satellites& Snapshot::getSatellites() +{ + return sats_; +} + +inline const Satellites& Snapshot::getSatellites() const +{ + return sats_; +} + +inline void Snapshot::setObservations(ObservationsPtr obs) +{ + obs_ = obs; +} + +inline void Snapshot::setNavigation(NavigationPtr nav) +{ + nav_ = nav; +} + +inline bool Snapshot::satellitesComputed() const +{ + return !sats_.empty(); +} + +inline std::set<int> Snapshot::filterObservations(const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt) +{ + std::set<int> filtered_sats = obs_->filter(sats_, discarded_sats, x_r, check_code, check_carrier_phase, opt); + + for (auto sat : filtered_sats) + { + sats_.erase(sat); + ranges_.erase(sat); + } + + return filtered_sats; +} + +inline std::set<int> Snapshot::filterObservations(const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt) +{ + std::set<int> filtered_sats = obs_->filter(sats_, discarded_sats, azels, check_code, check_carrier_phase, opt); + + for (auto sat : filtered_sats) + { + sats_.erase(sat); + ranges_.erase(sat); + } + + return filtered_sats; +} + +inline const Ranges& Snapshot::getRanges() const +{ + return ranges_; +} + +inline Ranges& Snapshot::getRanges() +{ + return ranges_; +} + +inline bool Snapshot::rangesComputed() const +{ + return !ranges_.empty(); +} + +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_SNAPSHOT_H_ diff --git a/include/gnss_utils/tdcp.h b/include/gnss_utils/tdcp.h new file mode 100644 index 0000000000000000000000000000000000000000..04a5748455e57ed1b018bd1df678b0ec47f983c4 --- /dev/null +++ b/include/gnss_utils/tdcp.h @@ -0,0 +1,90 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_TDCP_H_ +#define INCLUDE_GNSS_UTILS_TDCP_H_ + +#define GNSS_UTILS_TDCP_DEBUG 0 + +#include "gnss_utils/gnss_utils.h" +#include "gnss_utils/utils/chisquare_ci.h" + +namespace GnssUtils +{ + +struct TdcpOutput +{ + bool success = false; + std::string msg = ""; + std::set<int> raim_discarded_sats; + std::set<int> used_sats; + Eigen::Vector4d d = Eigen::Vector4d::Zero(); + Eigen::Matrix4d cov_d = Eigen::Matrix4d::Zero(); + double dt = 0; + double residual = 0; + double residual_ci = 0; +}; + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpOptions& tdcp_params, + const Options& opt); + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpOptions& tdcp_params, + const Options& opt); + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const std::set<int>& tracked_sats, + const TdcpOptions& tdcp_params, + const Options& opt); + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const std::set<int>& common_sats, + const Eigen::Vector4d& d_0, + const TdcpOptions& tdcp_params); + +TdcpOutput Tdcp(const Eigen::Vector3d& x_r, + Eigen::MatrixXd& A, + Eigen::VectorXd& r, + Eigen::VectorXd& drho_m, + const Eigen::MatrixXd& s_k, + const Eigen::MatrixXd& s_r, + const Eigen::Vector4d& d_0, + const double& var_tdcp, + std::set<int>& raim_discarded_rows, + const TdcpOptions& tdcp_params); + +} // namespace GnssUtils + +#endif /* INCLUDE_GNSS_UTILS_Tdcp_H_ */ diff --git a/include/gnss_utils/utils/chisquare_ci.h b/include/gnss_utils/utils/chisquare_ci.h new file mode 100644 index 0000000000000000000000000000000000000000..29e2d44d4823a5bffa78411cb2ce65949dcd1622 --- /dev/null +++ b/include/gnss_utils/utils/chisquare_ci.h @@ -0,0 +1,79 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * chisquare_ci.h + * + * Created on: Jul 28, 2021 + * Author: joanvallve + */ + +#ifndef INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_H_ +#define INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_H_ + +#include "chisquare_ci_maps.h" + +namespace GnssUtils +{ + +inline double chisq2ci(double chisq, int dof) +{ + assert(dof > 0); + + if (dof > 30) + dof = 30; + + if (chisq_2_CI.at(dof).count(chisq) == 1) + return chisq_2_CI.at(dof).at(chisq); + + auto upper = chisq_2_CI.at(dof).upper_bound(chisq); + + if (upper == chisq_2_CI.at(dof).begin()) + return upper->second; + + auto lower = std::prev(upper); + + return lower->second + (chisq - lower->first) / (upper->first - lower->first) * (upper->second - lower->second); +}; + +inline double ci2chisq(double ci, int dof) +{ + assert(dof > 0); + + if (dof > 30) + dof = 30; + + if (CI_2_chisq.at(dof).count(ci) == 1) + return CI_2_chisq.at(dof).at(ci); + + auto upper = CI_2_chisq.at(dof).upper_bound(ci); + + if (upper == CI_2_chisq.at(dof).begin()) + return upper->second; + + auto lower = std::prev(upper); + + return lower->second + (ci - lower->first) / (upper->first - lower->first) * (upper->second - lower->second); +}; + +} // namespace GnssUtils + +#endif /* INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_H_ */ diff --git a/include/gnss_utils/utils/chisquare_ci_maps.h b/include/gnss_utils/utils/chisquare_ci_maps.h new file mode 100644 index 0000000000000000000000000000000000000000..c3413ff8ea5f29c0e089f39c9b5bedc0b8de58e3 --- /dev/null +++ b/include/gnss_utils/utils/chisquare_ci_maps.h @@ -0,0 +1,1418 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_MAPS_H_ +#define INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_MAPS_H_ + +#include <map> + +namespace GnssUtils +{ + +static std::map<int, std::map<double, double>> CI_2_chisq = { + // 1 dof + {1, { + {0.10, 0.0157908}, + {0.15, 0.0357658}, + {0.20, 0.0641848}, + {0.25, 0.101531}, + {0.30, 0.148472}, + {0.35, 0.2059}, + {0.40, 0.274996}, + {0.45, 0.357317}, + {0.50, 0.454936}, + {0.55, 0.570652}, + {0.60, 0.708326}, + {0.65, 0.873457}, + {0.70, 1.07419}, + {0.75, 1.3233}, + {0.80, 1.64237}, + {0.85, 2.07225}, + {0.90, 2.70554}, + {0.95, 3.84146}, + {0.99, 6.6349}, + {1.00, 1e+12}, + }}, + // 2 dof + {2, { + {0.10, 0.210721}, + {0.15, 0.325038}, + {0.20, 0.446287}, + {0.25, 0.575364}, + {0.30, 0.71335}, + {0.35, 0.861566}, + {0.40, 1.02165}, + {0.45, 1.19567}, + {0.50, 1.38629}, + {0.55, 1.59702}, + {0.60, 1.83258}, + {0.65, 2.09964}, + {0.70, 2.40795}, + {0.75, 2.77259}, + {0.80, 3.21888}, + {0.85, 3.79424}, + {0.90, 4.60517}, + {0.95, 5.99146}, + {0.99, 9.21034}, + {1.00, 1e+12}, + }}, + // 3 dof + {3, { + {0.10, 0.584374}, + {0.15, 0.797771}, + {0.20, 1.00517}, + {0.25, 1.21253}, + {0.30, 1.42365}, + {0.35, 1.64158}, + {0.40, 1.86917}, + {0.45, 2.10947}, + {0.50, 2.36597}, + {0.55, 2.64301}, + {0.60, 2.94617}, + {0.65, 3.28311}, + {0.70, 3.66487}, + {0.75, 4.10834}, + {0.80, 4.64163}, + {0.85, 5.31705}, + {0.90, 6.25139}, + {0.95, 7.81473}, + {0.99, 11.3449}, + {1.00, 1e+12}, + }}, + // 4 dof + {4, { + {0.10, 1.06362}, + {0.15, 1.36648}, + {0.20, 1.64878}, + {0.25, 1.92256}, + {0.30, 2.1947}, + {0.35, 2.47009}, + {0.40, 2.75284}, + {0.45, 3.04695}, + {0.50, 3.35669}, + {0.55, 3.68713}, + {0.60, 4.04463}, + {0.65, 4.43769}, + {0.70, 4.87843}, + {0.75, 5.38527}, + {0.80, 5.98862}, + {0.85, 6.74488}, + {0.90, 7.77944}, + {0.95, 9.48773}, + {0.99, 13.2767}, + {1.00, 1e+12}, + }}, + // 5 dof + {5, { + {0.10, 1.61031}, + {0.15, 1.99382}, + {0.20, 2.34253}, + {0.25, 2.6746}, + {0.30, 2.99991}, + {0.35, 3.32511}, + {0.40, 3.6555}, + {0.45, 3.99594}, + {0.50, 4.35146}, + {0.55, 4.72776}, + {0.60, 5.13187}, + {0.65, 5.57307}, + {0.70, 6.06443}, + {0.75, 6.62568}, + {0.80, 7.28928}, + {0.85, 8.1152}, + {0.90, 9.23636}, + {0.95, 11.0705}, + {0.99, 15.0863}, + {1.00, 1e+12}, + }}, + // 6 dof + {6, { + {0.10, 2.20413}, + {0.15, 2.66127}, + {0.20, 3.07009}, + {0.25, 3.4546}, + {0.30, 3.82755}, + {0.35, 4.19727}, + {0.40, 4.57015}, + {0.45, 4.95188}, + {0.50, 5.34812}, + {0.55, 5.7652}, + {0.60, 6.21076}, + {0.65, 6.69476}, + {0.70, 7.23114}, + {0.75, 7.8408}, + {0.80, 8.55806}, + {0.85, 9.4461}, + {0.90, 10.6446}, + {0.95, 12.5916}, + {0.99, 16.8119}, + {1.00, 1e+12}, + }}, + // 7 dof + {7, { + {0.10, 2.83311}, + {0.15, 3.35828}, + {0.20, 3.82232}, + {0.25, 4.25485}, + {0.30, 4.67133}, + {0.35, 5.08165}, + {0.40, 5.49323}, + {0.45, 5.91252}, + {0.50, 6.34581}, + {0.55, 6.79997}, + {0.60, 7.28321}, + {0.65, 7.80612}, + {0.70, 8.38343}, + {0.75, 9.03715}, + {0.80, 9.80325}, + {0.85, 10.7479}, + {0.90, 12.017}, + {0.95, 14.0671}, + {0.99, 18.4753}, + {1.00, 1e+12}, + }}, + // 8 dof + {8, { + {0.10, 3.48954}, + {0.15, 4.0782}, + {0.20, 4.59357}, + {0.25, 5.07064}, + {0.30, 5.52742}, + {0.35, 5.97529}, + {0.40, 6.42265}, + {0.45, 6.87663}, + {0.50, 7.34412}, + {0.55, 7.83251}, + {0.60, 8.35053}, + {0.65, 8.90936}, + {0.70, 9.52446}, + {0.75, 10.2189}, + {0.80, 11.0301}, + {0.85, 12.0271}, + {0.90, 13.3616}, + {0.95, 15.5073}, + {0.99, 20.0902}, + {1.00, 1e+12}, + }}, + // 9 dof + {9, { + {0.10, 4.16816}, + {0.15, 4.81652}, + {0.20, 5.38005}, + {0.25, 5.89883}, + {0.30, 6.39331}, + {0.35, 6.87627}, + {0.40, 7.35703}, + {0.45, 7.84342}, + {0.50, 8.34283}, + {0.55, 8.86317}, + {0.60, 9.41364}, + {0.65, 10.006}, + {0.70, 10.6564}, + {0.75, 11.3888}, + {0.80, 12.2421}, + {0.85, 13.288}, + {0.90, 14.6837}, + {0.95, 16.919}, + {0.99, 21.666}, + {1.00, 1e+12}, + }}, + // 10 dof + {10, { + {0.10, 4.86518}, + {0.15, 5.57006}, + {0.20, 6.17908}, + {0.25, 6.7372}, + {0.30, 7.26722}, + {0.35, 7.78324}, + {0.40, 8.29547}, + {0.45, 8.81235}, + {0.50, 9.34182}, + {0.55, 9.89222}, + {0.60, 10.4732}, + {0.65, 11.0971}, + {0.70, 11.7807}, + {0.75, 12.5489}, + {0.80, 13.442}, + {0.85, 14.5339}, + {0.90, 15.9872}, + {0.95, 18.307}, + {0.99, 23.2093}, + {1.00, 1e+12}, + }}, + // 11 dof + {11, { + {0.10, 5.57778}, + {0.15, 6.33643}, + {0.20, 6.98867}, + {0.25, 7.58414}, + {0.30, 8.14787}, + {0.35, 8.69524}, + {0.40, 9.23729}, + {0.45, 9.78306}, + {0.50, 10.341}, + {0.55, 10.9199}, + {0.60, 11.5298}, + {0.65, 12.1836}, + {0.70, 12.8987}, + {0.75, 13.7007}, + {0.80, 14.6314}, + {0.85, 15.7671}, + {0.90, 17.275}, + {0.95, 19.6751}, + {0.99, 24.725}, + {1.00, 1e+12}, + }}, + // 12 dof + {12, { + {0.10, 6.3038}, + {0.15, 7.11384}, + {0.20, 7.80733}, + {0.25, 8.43842}, + {0.30, 9.03428}, + {0.35, 9.61152}, + {0.40, 10.182}, + {0.45, 10.7553}, + {0.50, 11.3403}, + {0.55, 11.9463}, + {0.60, 12.5838}, + {0.65, 13.2661}, + {0.70, 14.0111}, + {0.75, 14.8454}, + {0.80, 15.812}, + {0.85, 16.9893}, + {0.90, 18.5493}, + {0.95, 21.0261}, + {0.99, 26.217}, + {1.00, 1e+12}, + }}, + // 13 dof + {13, { + {0.10, 7.0415}, + {0.15, 7.90084}, + {0.20, 8.63386}, + {0.25, 9.29907}, + {0.30, 9.92568}, + {0.35, 10.5315}, + {0.40, 11.1291}, + {0.45, 11.7288}, + {0.50, 12.3398}, + {0.55, 12.9717}, + {0.60, 13.6356}, + {0.65, 14.3451}, + {0.70, 15.1187}, + {0.75, 15.9839}, + {0.80, 16.9848}, + {0.85, 18.202}, + {0.90, 19.8119}, + {0.95, 22.362}, + {0.99, 27.6882}, + {1.00, 1e+12}, + }}, + // 14 dof + {14, { + {0.10, 7.78953}, + {0.15, 8.6963}, + {0.20, 9.46733}, + {0.25, 10.1653}, + {0.30, 10.8215}, + {0.35, 11.4548}, + {0.40, 12.0785}, + {0.45, 12.7034}, + {0.50, 13.3393}, + {0.55, 13.9961}, + {0.60, 14.6853}, + {0.65, 15.4209}, + {0.70, 16.2221}, + {0.75, 17.1169}, + {0.80, 18.1508}, + {0.85, 19.4062}, + {0.90, 21.0641}, + {0.95, 23.6848}, + {0.99, 29.1412}, + {1.00, 1e+12}, + }}, + // 15 dof + {15, { + {0.10, 8.54676}, + {0.15, 9.49928}, + {0.20, 10.307}, + {0.25, 11.0365}, + {0.30, 11.7212}, + {0.35, 12.3809}, + {0.40, 13.0297}, + {0.45, 13.679}, + {0.50, 14.3389}, + {0.55, 15.0197}, + {0.60, 15.7332}, + {0.65, 16.494}, + {0.70, 17.3217}, + {0.75, 18.2451}, + {0.80, 19.3107}, + {0.85, 20.603}, + {0.90, 22.3071}, + {0.95, 24.9958}, + {0.99, 30.5779}, + {1.00, 1e+12}, + }}, + // 16 dof + {16, { + {0.10, 9.31224}, + {0.15, 10.309}, + {0.20, 11.1521}, + {0.25, 11.9122}, + {0.30, 12.6243}, + {0.35, 13.3096}, + {0.40, 13.9827}, + {0.45, 14.6555}, + {0.50, 15.3385}, + {0.55, 16.0425}, + {0.60, 16.7795}, + {0.65, 17.5646}, + {0.70, 18.4179}, + {0.75, 19.3689}, + {0.80, 20.4651}, + {0.85, 21.7931}, + {0.90, 23.5418}, + {0.95, 26.2962}, + {0.99, 31.9999}, + {1.00, 1e+12}, + }}, + // 17 dof + {17, { + {0.10, 10.0852}, + {0.15, 11.1249}, + {0.20, 12.0023}, + {0.25, 12.7919}, + {0.30, 13.5307}, + {0.35, 14.2407}, + {0.40, 14.9373}, + {0.45, 15.6328}, + {0.50, 16.3382}, + {0.55, 17.0646}, + {0.60, 17.8244}, + {0.65, 18.633}, + {0.70, 19.511}, + {0.75, 20.4887}, + {0.80, 21.6146}, + {0.85, 22.977}, + {0.90, 24.769}, + {0.95, 27.5871}, + {0.99, 33.4087}, + {1.00, 1e+12}, + }}, + // 18 dof + {18, { + {0.10, 10.8649}, + {0.15, 11.9463}, + {0.20, 12.857}, + {0.25, 13.6753}, + {0.30, 14.4399}, + {0.35, 15.1738}, + {0.40, 15.8932}, + {0.45, 16.6108}, + {0.50, 17.3379}, + {0.55, 18.086}, + {0.60, 18.8679}, + {0.65, 19.6993}, + {0.70, 20.6014}, + {0.75, 21.6049}, + {0.80, 22.7595}, + {0.85, 24.1555}, + {0.90, 25.9894}, + {0.95, 28.8693}, + {0.99, 34.8053}, + {1.00, 1e+12}, + }}, + // 19 dof + {19, { + {0.10, 11.6509}, + {0.15, 12.7727}, + {0.20, 13.7158}, + {0.25, 14.562}, + {0.30, 15.3517}, + {0.35, 16.1089}, + {0.40, 16.8504}, + {0.45, 17.5894}, + {0.50, 18.3377}, + {0.55, 19.1069}, + {0.60, 19.9102}, + {0.65, 20.7638}, + {0.70, 21.6891}, + {0.75, 22.7178}, + {0.80, 23.9004}, + {0.85, 25.3289}, + {0.90, 27.2036}, + {0.95, 30.1435}, + {0.99, 36.1909}, + {1.00, 1e+12}, + }}, + // 20 dof + {20, { + {0.10, 12.4426}, + {0.15, 13.6039}, + {0.20, 14.5784}, + {0.25, 15.4518}, + {0.30, 16.2659}, + {0.35, 17.0458}, + {0.40, 17.8088}, + {0.45, 18.5687}, + {0.50, 19.3374}, + {0.55, 20.1272}, + {0.60, 20.9514}, + {0.65, 21.8265}, + {0.70, 22.7745}, + {0.75, 23.8277}, + {0.80, 25.0375}, + {0.85, 26.4976}, + {0.90, 28.412}, + {0.95, 31.4104}, + {0.99, 37.5662}, + {1.00, 1e+12}, + }}, + // 21 dof + {21, { + {0.10, 13.2396}, + {0.15, 14.4393}, + {0.20, 15.4446}, + {0.25, 16.3444}, + {0.30, 17.1823}, + {0.35, 17.9843}, + {0.40, 18.7683}, + {0.45, 19.5485}, + {0.50, 20.3372}, + {0.55, 21.147}, + {0.60, 21.9915}, + {0.65, 22.8876}, + {0.70, 23.8578}, + {0.75, 24.9348}, + {0.80, 26.1711}, + {0.85, 27.662}, + {0.90, 29.6151}, + {0.95, 32.6706}, + {0.99, 38.9322}, + {1.00, 1e+12}, + }}, + // 22 dof + {22, { + {0.10, 14.0415}, + {0.15, 15.2788}, + {0.20, 16.314}, + {0.25, 17.2396}, + {0.30, 18.1007}, + {0.35, 18.9243}, + {0.40, 19.7288}, + {0.45, 20.5288}, + {0.50, 21.337}, + {0.55, 22.1663}, + {0.60, 23.0307}, + {0.65, 23.9473}, + {0.70, 24.939}, + {0.75, 26.0393}, + {0.80, 27.3015}, + {0.85, 28.8225}, + {0.90, 30.8133}, + {0.95, 33.9244}, + {0.99, 40.2894}, + {1.00, 1e+12}, + }}, + // 23 dof + {23, { + {0.10, 14.848}, + {0.15, 16.1219}, + {0.20, 17.1865}, + {0.25, 18.1373}, + {0.30, 19.0211}, + {0.35, 19.8657}, + {0.40, 20.6902}, + {0.45, 21.5096}, + {0.50, 22.3369}, + {0.55, 23.1852}, + {0.60, 24.0689}, + {0.65, 25.0055}, + {0.70, 26.0184}, + {0.75, 27.1413}, + {0.80, 28.4288}, + {0.85, 29.9792}, + {0.90, 32.0069}, + {0.95, 35.1725}, + {0.99, 41.6384}, + {1.00, 1e+12}, + }}, + // 24 dof + {24, { + {0.10, 15.6587}, + {0.15, 16.9686}, + {0.20, 18.0618}, + {0.25, 19.0373}, + {0.30, 19.9432}, + {0.35, 20.8084}, + {0.40, 21.6525}, + {0.45, 22.4908}, + {0.50, 23.3367}, + {0.55, 24.2037}, + {0.60, 25.1063}, + {0.65, 26.0625}, + {0.70, 27.096}, + {0.75, 28.2412}, + {0.80, 29.5533}, + {0.85, 31.1325}, + {0.90, 33.1962}, + {0.95, 36.415}, + {0.99, 42.9798}, + {1.00, 1e+12}, + }}, + // 25 dof + {25, { + {0.10, 16.4734}, + {0.15, 17.8184}, + {0.20, 18.9398}, + {0.25, 19.9393}, + {0.30, 20.867}, + {0.35, 21.7524}, + {0.40, 22.6156}, + {0.45, 23.4724}, + {0.50, 24.3366}, + {0.55, 25.2218}, + {0.60, 26.143}, + {0.65, 27.1183}, + {0.70, 28.1719}, + {0.75, 29.3389}, + {0.80, 30.6752}, + {0.85, 32.2825}, + {0.90, 34.3816}, + {0.95, 37.6525}, + {0.99, 44.3141}, + {1.00, 1e+12}, + }}, + // 26 dof + {26, { + {0.10, 17.2919}, + {0.15, 18.6714}, + {0.20, 19.8202}, + {0.25, 20.8434}, + {0.30, 21.7924}, + {0.35, 22.6975}, + {0.40, 23.5794}, + {0.45, 24.4544}, + {0.50, 25.3365}, + {0.55, 26.2395}, + {0.60, 27.1789}, + {0.65, 28.173}, + {0.70, 29.2463}, + {0.75, 30.4346}, + {0.80, 31.7946}, + {0.85, 33.4295}, + {0.90, 35.5632}, + {0.95, 38.8851}, + {0.99, 45.6417}, + {1.00, 1e+12}, + }}, + // 27 dof + {27, { + {0.10, 18.1139}, + {0.15, 19.5272}, + {0.20, 20.703}, + {0.25, 21.7494}, + {0.30, 22.7192}, + {0.35, 23.6437}, + {0.40, 24.544}, + {0.45, 25.4368}, + {0.50, 26.3363}, + {0.55, 27.2569}, + {0.60, 28.2141}, + {0.65, 29.2266}, + {0.70, 30.3193}, + {0.75, 31.5284}, + {0.80, 32.9117}, + {0.85, 34.5736}, + {0.90, 36.7412}, + {0.95, 40.1133}, + {0.99, 46.9629}, + {1.00, 1e+12}, + }}, + // 28 dof + {28, { + {0.10, 18.9392}, + {0.15, 20.3857}, + {0.20, 21.588}, + {0.25, 22.6572}, + {0.30, 23.6475}, + {0.35, 24.5909}, + {0.40, 25.5093}, + {0.45, 26.4195}, + {0.50, 27.3362}, + {0.55, 28.274}, + {0.60, 29.2486}, + {0.65, 30.2791}, + {0.70, 31.3909}, + {0.75, 32.6205}, + {0.80, 34.0266}, + {0.85, 35.715}, + {0.90, 37.9159}, + {0.95, 41.3371}, + {0.99, 48.2782}, + {1.00, 1e+12}, + }}, + // 29 dof + {29, { + {0.10, 19.7677}, + {0.15, 21.2468}, + {0.20, 22.4751}, + {0.25, 23.5666}, + {0.30, 24.577}, + {0.35, 25.5391}, + {0.40, 26.4751}, + {0.45, 27.4025}, + {0.50, 28.3361}, + {0.55, 29.2908}, + {0.60, 30.2825}, + {0.65, 31.3308}, + {0.70, 32.4612}, + {0.75, 33.7109}, + {0.80, 35.1394}, + {0.85, 36.8538}, + {0.90, 39.0875}, + {0.95, 42.557}, + {0.99, 49.5879}, + {1.00, 1e+12}, + }}, + // 30 dof + {30, { + {0.10, 20.5992}, + {0.15, 22.1103}, + {0.20, 23.3641}, + {0.25, 24.4776}, + {0.30, 25.5078}, + {0.35, 26.4881}, + {0.40, 27.4416}, + {0.45, 28.3858}, + {0.50, 29.336}, + {0.55, 30.3073}, + {0.60, 31.3159}, + {0.65, 32.3815}, + {0.70, 33.5302}, + {0.75, 34.7997}, + {0.80, 36.2502}, + {0.85, 37.9903}, + {0.90, 40.256}, + {0.95, 43.773}, + {0.99, 50.8922}, + {1.00, 1e+12}, + }}, +}; + +static std::map<int, std::map<double, double>> chisq_2_CI = { + // 1 dof + {1, { + {0.0157908, 0.10}, + {0.0357658, 0.15}, + {0.0641848, 0.20}, + {0.101531, 0.25}, + {0.148472, 0.30}, + {0.2059, 0.35}, + {0.274996, 0.40}, + {0.357317, 0.45}, + {0.454936, 0.50}, + {0.570652, 0.55}, + {0.708326, 0.60}, + {0.873457, 0.65}, + {1.07419, 0.70}, + {1.3233, 0.75}, + {1.64237, 0.80}, + {2.07225, 0.85}, + {2.70554, 0.90}, + {3.84146, 0.95}, + {6.6349, 0.99}, + { 1e+12, 1.00}, + }}, + // 2 dof + {2, { + {0.210721, 0.10}, + {0.325038, 0.15}, + {0.446287, 0.20}, + {0.575364, 0.25}, + {0.71335, 0.30}, + {0.861566, 0.35}, + {1.02165, 0.40}, + {1.19567, 0.45}, + {1.38629, 0.50}, + {1.59702, 0.55}, + {1.83258, 0.60}, + {2.09964, 0.65}, + {2.40795, 0.70}, + {2.77259, 0.75}, + {3.21888, 0.80}, + {3.79424, 0.85}, + {4.60517, 0.90}, + {5.99146, 0.95}, + {9.21034, 0.99}, + { 1e+12, 1.00}, + }}, + // 3 dof + {3, { + {0.584374, 0.10}, + {0.797771, 0.15}, + {1.00517, 0.20}, + {1.21253, 0.25}, + {1.42365, 0.30}, + {1.64158, 0.35}, + {1.86917, 0.40}, + {2.10947, 0.45}, + {2.36597, 0.50}, + {2.64301, 0.55}, + {2.94617, 0.60}, + {3.28311, 0.65}, + {3.66487, 0.70}, + {4.10834, 0.75}, + {4.64163, 0.80}, + {5.31705, 0.85}, + {6.25139, 0.90}, + {7.81473, 0.95}, + {11.3449, 0.99}, + { 1e+12, 1.00}, + }}, + // 4 dof + {4, { + {1.06362, 0.10}, + {1.36648, 0.15}, + {1.64878, 0.20}, + {1.92256, 0.25}, + {2.1947, 0.30}, + {2.47009, 0.35}, + {2.75284, 0.40}, + {3.04695, 0.45}, + {3.35669, 0.50}, + {3.68713, 0.55}, + {4.04463, 0.60}, + {4.43769, 0.65}, + {4.87843, 0.70}, + {5.38527, 0.75}, + {5.98862, 0.80}, + {6.74488, 0.85}, + {7.77944, 0.90}, + {9.48773, 0.95}, + {13.2767, 0.99}, + { 1e+12, 1.00}, + }}, + // 5 dof + {5, { + {1.61031, 0.10}, + {1.99382, 0.15}, + {2.34253, 0.20}, + {2.6746, 0.25}, + {2.99991, 0.30}, + {3.32511, 0.35}, + {3.6555, 0.40}, + {3.99594, 0.45}, + {4.35146, 0.50}, + {4.72776, 0.55}, + {5.13187, 0.60}, + {5.57307, 0.65}, + {6.06443, 0.70}, + {6.62568, 0.75}, + {7.28928, 0.80}, + {8.1152, 0.85}, + {9.23636, 0.90}, + {11.0705, 0.95}, + {15.0863, 0.99}, + { 1e+12, 1.00}, + }}, + // 6 dof + {6, { + {2.20413, 0.10}, + {2.66127, 0.15}, + {3.07009, 0.20}, + {3.4546, 0.25}, + {3.82755, 0.30}, + {4.19727, 0.35}, + {4.57015, 0.40}, + {4.95188, 0.45}, + {5.34812, 0.50}, + {5.7652, 0.55}, + {6.21076, 0.60}, + {6.69476, 0.65}, + {7.23114, 0.70}, + {7.8408, 0.75}, + {8.55806, 0.80}, + {9.4461, 0.85}, + {10.6446, 0.90}, + {12.5916, 0.95}, + {16.8119, 0.99}, + { 1e+12, 1.00}, + }}, + // 7 dof + {7, { + {2.83311, 0.10}, + {3.35828, 0.15}, + {3.82232, 0.20}, + {4.25485, 0.25}, + {4.67133, 0.30}, + {5.08165, 0.35}, + {5.49323, 0.40}, + {5.91252, 0.45}, + {6.34581, 0.50}, + {6.79997, 0.55}, + {7.28321, 0.60}, + {7.80612, 0.65}, + {8.38343, 0.70}, + {9.03715, 0.75}, + {9.80325, 0.80}, + {10.7479, 0.85}, + {12.017, 0.90}, + {14.0671, 0.95}, + {18.4753, 0.99}, + { 1e+12, 1.00}, + }}, + // 8 dof + {8, { + {3.48954, 0.10}, + {4.0782, 0.15}, + {4.59357, 0.20}, + {5.07064, 0.25}, + {5.52742, 0.30}, + {5.97529, 0.35}, + {6.42265, 0.40}, + {6.87663, 0.45}, + {7.34412, 0.50}, + {7.83251, 0.55}, + {8.35053, 0.60}, + {8.90936, 0.65}, + {9.52446, 0.70}, + {10.2189, 0.75}, + {11.0301, 0.80}, + {12.0271, 0.85}, + {13.3616, 0.90}, + {15.5073, 0.95}, + {20.0902, 0.99}, + { 1e+12, 1.00}, + }}, + // 9 dof + {9, { + {4.16816, 0.10}, + {4.81652, 0.15}, + {5.38005, 0.20}, + {5.89883, 0.25}, + {6.39331, 0.30}, + {6.87627, 0.35}, + {7.35703, 0.40}, + {7.84342, 0.45}, + {8.34283, 0.50}, + {8.86317, 0.55}, + {9.41364, 0.60}, + {10.006, 0.65}, + {10.6564, 0.70}, + {11.3888, 0.75}, + {12.2421, 0.80}, + {13.288, 0.85}, + {14.6837, 0.90}, + {16.919, 0.95}, + {21.666, 0.99}, + { 1e+12, 1.00}, + }}, + // 10 dof + {10, { + {4.86518, 0.10}, + {5.57006, 0.15}, + {6.17908, 0.20}, + {6.7372, 0.25}, + {7.26722, 0.30}, + {7.78324, 0.35}, + {8.29547, 0.40}, + {8.81235, 0.45}, + {9.34182, 0.50}, + {9.89222, 0.55}, + {10.4732, 0.60}, + {11.0971, 0.65}, + {11.7807, 0.70}, + {12.5489, 0.75}, + {13.442, 0.80}, + {14.5339, 0.85}, + {15.9872, 0.90}, + {18.307, 0.95}, + {23.2093, 0.99}, + { 1e+12, 1.00}, + }}, + // 11 dof + {11, { + {5.57778, 0.10}, + {6.33643, 0.15}, + {6.98867, 0.20}, + {7.58414, 0.25}, + {8.14787, 0.30}, + {8.69524, 0.35}, + {9.23729, 0.40}, + {9.78306, 0.45}, + {10.341, 0.50}, + {10.9199, 0.55}, + {11.5298, 0.60}, + {12.1836, 0.65}, + {12.8987, 0.70}, + {13.7007, 0.75}, + {14.6314, 0.80}, + {15.7671, 0.85}, + {17.275, 0.90}, + {19.6751, 0.95}, + {24.725, 0.99}, + { 1e+12, 1.00}, + }}, + // 12 dof + {12, { + {6.3038, 0.10}, + {7.11384, 0.15}, + {7.80733, 0.20}, + {8.43842, 0.25}, + {9.03428, 0.30}, + {9.61152, 0.35}, + {10.182, 0.40}, + {10.7553, 0.45}, + {11.3403, 0.50}, + {11.9463, 0.55}, + {12.5838, 0.60}, + {13.2661, 0.65}, + {14.0111, 0.70}, + {14.8454, 0.75}, + {15.812, 0.80}, + {16.9893, 0.85}, + {18.5493, 0.90}, + {21.0261, 0.95}, + {26.217, 0.99}, + { 1e+12, 1.00}, + }}, + // 13 dof + {13, { + {7.0415, 0.10}, + {7.90084, 0.15}, + {8.63386, 0.20}, + {9.29907, 0.25}, + {9.92568, 0.30}, + {10.5315, 0.35}, + {11.1291, 0.40}, + {11.7288, 0.45}, + {12.3398, 0.50}, + {12.9717, 0.55}, + {13.6356, 0.60}, + {14.3451, 0.65}, + {15.1187, 0.70}, + {15.9839, 0.75}, + {16.9848, 0.80}, + {18.202, 0.85}, + {19.8119, 0.90}, + {22.362, 0.95}, + {27.6882, 0.99}, + { 1e+12, 1.00}, + }}, + // 14 dof + {14, { + {7.78953, 0.10}, + {8.6963, 0.15}, + {9.46733, 0.20}, + {10.1653, 0.25}, + {10.8215, 0.30}, + {11.4548, 0.35}, + {12.0785, 0.40}, + {12.7034, 0.45}, + {13.3393, 0.50}, + {13.9961, 0.55}, + {14.6853, 0.60}, + {15.4209, 0.65}, + {16.2221, 0.70}, + {17.1169, 0.75}, + {18.1508, 0.80}, + {19.4062, 0.85}, + {21.0641, 0.90}, + {23.6848, 0.95}, + {29.1412, 0.99}, + { 1e+12, 1.00}, + }}, + // 15 dof + {15, { + {8.54676, 0.10}, + {9.49928, 0.15}, + {10.307, 0.20}, + {11.0365, 0.25}, + {11.7212, 0.30}, + {12.3809, 0.35}, + {13.0297, 0.40}, + {13.679, 0.45}, + {14.3389, 0.50}, + {15.0197, 0.55}, + {15.7332, 0.60}, + {16.494, 0.65}, + {17.3217, 0.70}, + {18.2451, 0.75}, + {19.3107, 0.80}, + {20.603, 0.85}, + {22.3071, 0.90}, + {24.9958, 0.95}, + {30.5779, 0.99}, + { 1e+12, 1.00}, + }}, + // 16 dof + {16, { + {9.31224, 0.10}, + {10.309, 0.15}, + {11.1521, 0.20}, + {11.9122, 0.25}, + {12.6243, 0.30}, + {13.3096, 0.35}, + {13.9827, 0.40}, + {14.6555, 0.45}, + {15.3385, 0.50}, + {16.0425, 0.55}, + {16.7795, 0.60}, + {17.5646, 0.65}, + {18.4179, 0.70}, + {19.3689, 0.75}, + {20.4651, 0.80}, + {21.7931, 0.85}, + {23.5418, 0.90}, + {26.2962, 0.95}, + {31.9999, 0.99}, + { 1e+12, 1.00}, + }}, + // 17 dof + {17, { + {10.0852, 0.10}, + {11.1249, 0.15}, + {12.0023, 0.20}, + {12.7919, 0.25}, + {13.5307, 0.30}, + {14.2407, 0.35}, + {14.9373, 0.40}, + {15.6328, 0.45}, + {16.3382, 0.50}, + {17.0646, 0.55}, + {17.8244, 0.60}, + {18.633, 0.65}, + {19.511, 0.70}, + {20.4887, 0.75}, + {21.6146, 0.80}, + {22.977, 0.85}, + {24.769, 0.90}, + {27.5871, 0.95}, + {33.4087, 0.99}, + { 1e+12, 1.00}, + }}, + // 18 dof + {18, { + {10.8649, 0.10}, + {11.9463, 0.15}, + {12.857, 0.20}, + {13.6753, 0.25}, + {14.4399, 0.30}, + {15.1738, 0.35}, + {15.8932, 0.40}, + {16.6108, 0.45}, + {17.3379, 0.50}, + {18.086, 0.55}, + {18.8679, 0.60}, + {19.6993, 0.65}, + {20.6014, 0.70}, + {21.6049, 0.75}, + {22.7595, 0.80}, + {24.1555, 0.85}, + {25.9894, 0.90}, + {28.8693, 0.95}, + {34.8053, 0.99}, + { 1e+12, 1.00}, + }}, + // 19 dof + {19, { + {11.6509, 0.10}, + {12.7727, 0.15}, + {13.7158, 0.20}, + {14.562, 0.25}, + {15.3517, 0.30}, + {16.1089, 0.35}, + {16.8504, 0.40}, + {17.5894, 0.45}, + {18.3377, 0.50}, + {19.1069, 0.55}, + {19.9102, 0.60}, + {20.7638, 0.65}, + {21.6891, 0.70}, + {22.7178, 0.75}, + {23.9004, 0.80}, + {25.3289, 0.85}, + {27.2036, 0.90}, + {30.1435, 0.95}, + {36.1909, 0.99}, + { 1e+12, 1.00}, + }}, + // 20 dof + {20, { + {12.4426, 0.10}, + {13.6039, 0.15}, + {14.5784, 0.20}, + {15.4518, 0.25}, + {16.2659, 0.30}, + {17.0458, 0.35}, + {17.8088, 0.40}, + {18.5687, 0.45}, + {19.3374, 0.50}, + {20.1272, 0.55}, + {20.9514, 0.60}, + {21.8265, 0.65}, + {22.7745, 0.70}, + {23.8277, 0.75}, + {25.0375, 0.80}, + {26.4976, 0.85}, + {28.412, 0.90}, + {31.4104, 0.95}, + {37.5662, 0.99}, + { 1e+12, 1.00}, + }}, + // 21 dof + {21, { + {13.2396, 0.10}, + {14.4393, 0.15}, + {15.4446, 0.20}, + {16.3444, 0.25}, + {17.1823, 0.30}, + {17.9843, 0.35}, + {18.7683, 0.40}, + {19.5485, 0.45}, + {20.3372, 0.50}, + {21.147, 0.55}, + {21.9915, 0.60}, + {22.8876, 0.65}, + {23.8578, 0.70}, + {24.9348, 0.75}, + {26.1711, 0.80}, + {27.662, 0.85}, + {29.6151, 0.90}, + {32.6706, 0.95}, + {38.9322, 0.99}, + { 1e+12, 1.00}, + }}, + // 22 dof + {22, { + {14.0415, 0.10}, + {15.2788, 0.15}, + {16.314, 0.20}, + {17.2396, 0.25}, + {18.1007, 0.30}, + {18.9243, 0.35}, + {19.7288, 0.40}, + {20.5288, 0.45}, + {21.337, 0.50}, + {22.1663, 0.55}, + {23.0307, 0.60}, + {23.9473, 0.65}, + {24.939, 0.70}, + {26.0393, 0.75}, + {27.3015, 0.80}, + {28.8225, 0.85}, + {30.8133, 0.90}, + {33.9244, 0.95}, + {40.2894, 0.99}, + { 1e+12, 1.00}, + }}, + // 23 dof + {23, { + {14.848, 0.10}, + {16.1219, 0.15}, + {17.1865, 0.20}, + {18.1373, 0.25}, + {19.0211, 0.30}, + {19.8657, 0.35}, + {20.6902, 0.40}, + {21.5096, 0.45}, + {22.3369, 0.50}, + {23.1852, 0.55}, + {24.0689, 0.60}, + {25.0055, 0.65}, + {26.0184, 0.70}, + {27.1413, 0.75}, + {28.4288, 0.80}, + {29.9792, 0.85}, + {32.0069, 0.90}, + {35.1725, 0.95}, + {41.6384, 0.99}, + { 1e+12, 1.00}, + }}, + // 24 dof + {24, { + {15.6587, 0.10}, + {16.9686, 0.15}, + {18.0618, 0.20}, + {19.0373, 0.25}, + {19.9432, 0.30}, + {20.8084, 0.35}, + {21.6525, 0.40}, + {22.4908, 0.45}, + {23.3367, 0.50}, + {24.2037, 0.55}, + {25.1063, 0.60}, + {26.0625, 0.65}, + {27.096, 0.70}, + {28.2412, 0.75}, + {29.5533, 0.80}, + {31.1325, 0.85}, + {33.1962, 0.90}, + {36.415, 0.95}, + {42.9798, 0.99}, + { 1e+12, 1.00}, + }}, + // 25 dof + {25, { + {16.4734, 0.10}, + {17.8184, 0.15}, + {18.9398, 0.20}, + {19.9393, 0.25}, + {20.867, 0.30}, + {21.7524, 0.35}, + {22.6156, 0.40}, + {23.4724, 0.45}, + {24.3366, 0.50}, + {25.2218, 0.55}, + {26.143, 0.60}, + {27.1183, 0.65}, + {28.1719, 0.70}, + {29.3389, 0.75}, + {30.6752, 0.80}, + {32.2825, 0.85}, + {34.3816, 0.90}, + {37.6525, 0.95}, + {44.3141, 0.99}, + { 1e+12, 1.00}, + }}, + // 26 dof + {26, { + {17.2919, 0.10}, + {18.6714, 0.15}, + {19.8202, 0.20}, + {20.8434, 0.25}, + {21.7924, 0.30}, + {22.6975, 0.35}, + {23.5794, 0.40}, + {24.4544, 0.45}, + {25.3365, 0.50}, + {26.2395, 0.55}, + {27.1789, 0.60}, + {28.173, 0.65}, + {29.2463, 0.70}, + {30.4346, 0.75}, + {31.7946, 0.80}, + {33.4295, 0.85}, + {35.5632, 0.90}, + {38.8851, 0.95}, + {45.6417, 0.99}, + { 1e+12, 1.00}, + }}, + // 27 dof + {27, { + {18.1139, 0.10}, + {19.5272, 0.15}, + {20.703, 0.20}, + {21.7494, 0.25}, + {22.7192, 0.30}, + {23.6437, 0.35}, + {24.544, 0.40}, + {25.4368, 0.45}, + {26.3363, 0.50}, + {27.2569, 0.55}, + {28.2141, 0.60}, + {29.2266, 0.65}, + {30.3193, 0.70}, + {31.5284, 0.75}, + {32.9117, 0.80}, + {34.5736, 0.85}, + {36.7412, 0.90}, + {40.1133, 0.95}, + {46.9629, 0.99}, + { 1e+12, 1.00}, + }}, + // 28 dof + {28, { + {18.9392, 0.10}, + {20.3857, 0.15}, + {21.588, 0.20}, + {22.6572, 0.25}, + {23.6475, 0.30}, + {24.5909, 0.35}, + {25.5093, 0.40}, + {26.4195, 0.45}, + {27.3362, 0.50}, + {28.274, 0.55}, + {29.2486, 0.60}, + {30.2791, 0.65}, + {31.3909, 0.70}, + {32.6205, 0.75}, + {34.0266, 0.80}, + {35.715, 0.85}, + {37.9159, 0.90}, + {41.3371, 0.95}, + {48.2782, 0.99}, + { 1e+12, 1.00}, + }}, + // 29 dof + {29, { + {19.7677, 0.10}, + {21.2468, 0.15}, + {22.4751, 0.20}, + {23.5666, 0.25}, + {24.577, 0.30}, + {25.5391, 0.35}, + {26.4751, 0.40}, + {27.4025, 0.45}, + {28.3361, 0.50}, + {29.2908, 0.55}, + {30.2825, 0.60}, + {31.3308, 0.65}, + {32.4612, 0.70}, + {33.7109, 0.75}, + {35.1394, 0.80}, + {36.8538, 0.85}, + {39.0875, 0.90}, + {42.557, 0.95}, + {49.5879, 0.99}, + { 1e+12, 1.00}, + }}, + // 30 dof + {30, { + {20.5992, 0.10}, + {22.1103, 0.15}, + {23.3641, 0.20}, + {24.4776, 0.25}, + {25.5078, 0.30}, + {26.4881, 0.35}, + {27.4416, 0.40}, + {28.3858, 0.45}, + {29.336, 0.50}, + {30.3073, 0.55}, + {31.3159, 0.60}, + {32.3815, 0.65}, + {33.5302, 0.70}, + {34.7997, 0.75}, + {36.2502, 0.80}, + {37.9903, 0.85}, + {40.256, 0.90}, + {43.773, 0.95}, + {50.8922, 0.99}, + { 1e+12, 1.00}, + }}, +}; + +}// namespace GnssUtils + +#endif /* INCLUDE_GNSS_UTILS_UTILS_CHISQUARE_CI_MAPS_H_ */ diff --git a/include/gnss_utils/utils/rcv_position.h b/include/gnss_utils/utils/rcv_position.h new file mode 100644 index 0000000000000000000000000000000000000000..bf84a9911387d3b5c834212811f39dd63a3331c5 --- /dev/null +++ b/include/gnss_utils/utils/rcv_position.h @@ -0,0 +1,69 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * transfromation.h + * + * Created on: April 3, 2020 + * Author: Joan Vallvé, Pep MartÃ-Saumell + */ + +#ifndef INCLUDE_GNSS_UTILS_UTILS_RCV_POSITION_H_ +#define INCLUDE_GNSS_UTILS_UTILS_RCV_POSITION_H_ + +#include <Eigen/Dense> +#include <Eigen/Geometry> +#include <Eigen/Sparse> + +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" +#include "gnss_utils/utils/transformations.h" +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ + +ComputePosOutput computePos(const Observations& _observations, const Navigation& _navigation, const Options& _opt); +ComputePosOutput computePos(const Observations& _observations, const Navigation& _navigation, const prcopt_t& _prcopt); + +// ComputePosOutput computePosOwn(const Observations & _observations, +// Navigation & _navigation, +// const prcopt_t & _prcopt); + +// int pntposOwn(const obsd_t *obs, int n, const nav_t *nav, +// const prcopt_t *opt, sol_t *sol, double *azel, ssat_t *ssat, +// char *msg); + +int estposOwn(const obsd_t* obs, + int n, + const double* rs, + const double* dts, + const double* vare, + const int* svh, + const nav_t* nav, + const prcopt_t* opt, + sol_t* sol, + double* azel, + int* vsat, + double* resp, + char* msg); +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_UTILS_RCV_POSITION_H_ diff --git a/include/gnss_utils/utils/satellite.h b/include/gnss_utils/utils/satellite.h new file mode 100644 index 0000000000000000000000000000000000000000..0162a11eebf598e71f70d082fe15c53cc0298ce5 --- /dev/null +++ b/include/gnss_utils/utils/satellite.h @@ -0,0 +1,66 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * sat_position.h + * + * Created on: April 3, 2020 + * Author: Joan Vallvé, Pep MartÃ-Saumell + */ + +#ifndef INCLUDE_GNSS_UTILS_UTILS_SAT_POSITION_H_ +#define INCLUDE_GNSS_UTILS_UTILS_SAT_POSITION_H_ + +#include <Eigen/Dense> +#include <Eigen/Geometry> + +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ + // forward declarations + class Observations; + class Navigation; + + Satellites computeSatellites(const Observations& obs, + const Navigation& nav, + const int& eph_opt); // see rtklib.h L396 + + Eigen::Vector2d computeAzel(const Eigen::Vector3d& sat_ecef, const Eigen::Vector3d& receiver_ecef); + + Eigen::Vector2d computeAzel(const Satellite& sat, const Eigen::Vector3d& receiver_ecef); + + Azels computeAzels(const Satellites& sats, const Eigen::Vector3d& receiver_ecef); + + struct Satellite + { + int sys; + int sat; + Eigen::Vector3d pos; + Eigen::Vector3d vel; + double var; + double clock_bias; + double clock_drift; + int svh; + }; + +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_UTILS_SAT_POSITION_H_ diff --git a/include/gnss_utils/utils/transformations.h b/include/gnss_utils/utils/transformations.h new file mode 100644 index 0000000000000000000000000000000000000000..92533d1ae2c82b38b852fa762cb48b6662f6eb5a --- /dev/null +++ b/include/gnss_utils/utils/transformations.h @@ -0,0 +1,52 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * transfromation.h + * + * Created on: April 3, 2020 + * Author: Joan Vallvé, Pep MartÃ-Saumell + */ + +#ifndef INCLUDE_GNSS_UTILS_UTILS_TRANSFORMATIONS_H_ +#define INCLUDE_GNSS_UTILS_UTILS_TRANSFORMATIONS_H_ + +#include <Eigen/Dense> +#include <Eigen/Geometry> +#include <Eigen/Sparse> +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ +Eigen::Vector3d ecefToLatLonAlt(const Eigen::Vector3d& _ecef); +Eigen::Vector3d latLonAltToEcef(const Eigen::Vector3d& _latlon); +Eigen::Matrix3d ecefToEnuCov(const Eigen::Vector3d& _latlon, const Eigen::Matrix3d& _cov_ecef); +Eigen::Matrix3d enuToEcefCov(const Eigen::Vector3d& _latlon, const Eigen::Matrix3d& _cov_enu); + +void computeEnuEcefFromEcef(const Eigen::Vector3d& _t_ECEF_ENU, + Eigen::Matrix3d& R_ENU_ECEF, + Eigen::Vector3d& t_ENU_ECEF); +void computeEnuEcefFromLatLonAlt(const Eigen::Vector3d& _ENU_latlonalt, + Eigen::Matrix3d& R_ENU_ECEF, + Eigen::Vector3d& t_ENU_ECEF); + +} // namespace GnssUtils +#endif // INCLUDE_GNSS_UTILS_UTILS_TRANSFORMATIONS_H_ diff --git a/include/gnss_utils/utils/utils.h b/include/gnss_utils/utils/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..7c29dd40319343a388a6b62da45ae1e7b8a266cb --- /dev/null +++ b/include/gnss_utils/utils/utils.h @@ -0,0 +1,238 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#ifndef INCLUDE_GNSS_UTILS_UTILS_UTILS_H_ +#define INCLUDE_GNSS_UTILS_UTILS_UTILS_H_ + +#include <vector> +#include <iostream> +#include <memory> +#include <string> + +#include "gnss_utils/internal/config.h" +#include "gnss_utils/gnss_utils.h" +#include "gnss_utils/observations.h" + +#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0]) +#define ARRAY2D_SIZE(arr) sizeof(arr) / sizeof(arr[0][0]) +#define ARRAY3D_SIZE(arr) sizeof(arr) / sizeof(arr[0][0][0]) +#define ARRAY2D_NROWS(arr) sizeof(arr) / sizeof(arr[0]) +#define ARRAY2D_NCOLS(arr) sizeof(arr[0]) / sizeof(arr[0][0]) +#define GNSSUTILS_MSG "--GnssUtils--" + +namespace GnssUtils +{ +void print(std::string& _msg); + +template <typename T, size_t size> +void printArray(std::string name, T (&array)[size]) +{ + std::cout << name << "["; + for (int ii = 0; ii < size; ++ii) + { + if (std::is_same<T, char>::value or std::is_same<T, unsigned char>::value) + std::cout << (int)array[ii]; + else + std::cout << array[ii]; + if (ii == size - 1) + std::cout << "] \n"; + else + std::cout << ","; + } +} + +template <typename T> +inline bool addToArray(const T& new_element, T*& array, int& n, int& nmax) +{ + // std::cout << "addToArray: n = " << n << " nmax = " << nmax << "\n"; + // "inspired" from RTKLIB rinex.c + T* array_ref; + if (nmax <= n) + { + // std::cout << "addToArray: nmax <= n\n"; + nmax += 1024; + if (!(array_ref = (T*)realloc(array, sizeof(T) * nmax))) + { + printf("addToArray malloc error: n=%d\n", nmax); + free(array); + array = NULL; + n = nmax = 0; + return false; + } + // std::cout << "addToArray: assigning reallocated array\n"; + array = array_ref; + } + // std::cout << "addToArray: adding element " << n << "\n"; + array[n++] = new_element; + // std::cout << "addToArray: added!\n"; + return true; +} + +template <typename T> +inline bool copyArray(const T* array_in, const int& n_in, T*& array_out, int& n_out, int& nmax_out) +{ + // std::cout << "copyArray: " << n_in << " elements\n"; + if (array_in == NULL) + return false; + + // std::cout << "copyArray: array in not null\n"; + + for (int i = 0; i < n_in; i++) + { + // std::cout << "copyArray: adding element " << i << "\n"; + if (!addToArray<T>(array_in[i], array_out, n_out, nmax_out)) + { + // std::cout << "copyArray: failed to add..\n"; + return false; + } + // std::cout << "copyArray: n_out = " << n_out << " nmax_out = " << nmax_out << "\n"; + } + + // std::cout << "copyArray: all copied\n"; + + return true; +} + +template <typename T> +void freeArray(T*& array, int& n, int& nmax) +{ + if (array != NULL) + free(array); + array = NULL; + n = nmax = 0; +} + +template <typename T> +bool equalArray(const T* array_1, const T* array_2, const int& n1, const int& n2) +{ + if (n1 != n2) + return false; + + for (int i = 0; i < n1; ++i) + { + if (array_1[i] != array_2[i]) + return false; + } + + return true; +} + +template <typename T> +bool equalArray2d(const T* array_1, + const T* array_2, + const int& nrow_1, + const int& ncol_1, + const int& nrow_2, + const int& ncol_2) +{ + if (nrow_1 != nrow_2) + return false; + if (ncol_1 != ncol_2) + return false; + + for (int i = 0; i < nrow_1; ++i) + { + for (int j = 0; j < ncol_1; ++j) + { + if (*(array_1 + i * ncol_1 + j) != *(array_2 + i * ncol_1 + j)) + return false; + } + } + + return true; +} + +template <typename T> +bool equalArray3d(const T* array_1, + const T* array_2, + const int& nmat_1, + const int& nrow_1, + const int& ncol_1, + const int& nmat_2, + const int& nrow_2, + const int& ncol_2) +{ + if (nmat_1 != nmat_2) + return false; + if (nrow_1 != nrow_2) + return false; + if (ncol_1 != ncol_2) + return false; + + for (int i = 0; i < nmat_1; ++i) + { + for (int j = 0; j < nrow_1; ++j) + { + for (int k = 0; k < ncol_1; ++k) + { + if (*(array_1 + i * ncol_1 * nrow_1 + j * ncol_1 + k) != *(array_2 + i * ncol_1 * nrow_1 + j * ncol_1 + k)) + return false; + } + } + } + + return true; +} + +std::vector<Observations> +loadObservationsFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt, const char* opt); + +} // namespace GnssUtils + +bool operator==(const gtime_t& time1, const gtime_t& time2); +bool operator!=(const gtime_t& time1, const gtime_t& time2); + +bool operator==(const obsd_t& obs1, const obsd_t& obs2); +bool operator!=(const obsd_t& obs1, const obsd_t& obs2); +bool operator==(const obs_t& obs1, const obs_t& obs2); +bool operator!=(const obs_t& obs1, const obs_t& obs2); + +bool operator==(const eph_t& eph1, const eph_t& eph2); +bool operator!=(const eph_t& eph1, const eph_t& eph2); + +bool operator==(const geph_t& geph1, const geph_t& geph2); +bool operator!=(const geph_t& geph1, const geph_t& geph2); + +bool operator==(const seph_t& seph1, const seph_t& seph2); +bool operator!=(const seph_t& seph1, const seph_t& seph2); + +bool operator==(const peph_t& peph1, const peph_t& peph2); +bool operator!=(const peph_t& peph1, const peph_t& peph2); + +bool operator==(const pclk_t& pclk1, const pclk_t& pclk2); +bool operator!=(const pclk_t& pclk1, const pclk_t& pclk2); + +bool operator==(const alm_t& alm1, const alm_t& alm2); +bool operator!=(const alm_t& alm1, const alm_t& alm2); + +bool operator==(const tec_t& tec1, const tec_t& tec2); +bool operator!=(const tec_t& tec1, const tec_t& tec2); + +bool operator==(const fcbd_t& fcbd1, const fcbd_t& fcbd2); +bool operator!=(const fcbd_t& fcbd1, const fcbd_t& fcbd2); + +bool operator==(const erp_t& erp1, const erp_t& erp2); +bool operator!=(const erp_t& erp1, const erp_t& erp2); + +bool operator==(const nav_t& nav1, const nav_t& nav2); +bool operator!=(const nav_t& nav1, const nav_t& nav2); + +#endif // INCLUDE_GNSS_UTILS_UTILS_UTILS_H_ diff --git a/internal/config.h.in b/internal/config.h.in new file mode 100644 index 0000000000000000000000000000000000000000..90ad35cc6c35569f52a370d2c6624e0bed11c030 --- /dev/null +++ b/internal/config.h.in @@ -0,0 +1,10 @@ +#ifndef GNSS_UTILS_INTERNAL_CONFIG_H_ +#define GNSS_UTILS_INTERNAL_CONFIG_H_ + +#cmakedefine _GNSS_UTILS_DEBUG + +#cmakedefine _GNSS_UTILS_TRACE + +#define _GNSS_UTILS_ROOT_DIR "${_GNSS_UTILS_ROOT_DIR}" + +#endif /* GNSS_UTILS_INTERNAL_CONFIG_H_ */ diff --git a/scripts/license_header_2022.txt b/scripts/license_header_2022.txt new file mode 100644 index 0000000000000000000000000000000000000000..3e36f9be0f73cf4dac051a75d96d72b372fdf464 --- /dev/null +++ b/scripts/license_header_2022.txt @@ -0,0 +1,17 @@ +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. diff --git a/scripts/license_manager.sh b/scripts/license_manager.sh new file mode 100755 index 0000000000000000000000000000000000000000..74f790a95133bb09f07b016ed55c3a7b67e2a9bc --- /dev/null +++ b/scripts/license_manager.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# ------ WOLF license_manager script ------ +# +# This script is used for managing the license headers of code files (.h, .c, .cpp, .hpp) +# This file is automatically called by the CI in gitlab. + +line_start_mark="//--------LICENSE_START--------" +line_end_mark="//--------LICENSE_END--------" + +#options +tmp=false +mode="none" +path="" +exclude_folder="" +#recursive=true +license="" + +for i in "$@"; do + case $i in + --temp) + tmp=true + shift # past argument=value + ;; + --path=*) + path="${i#*=}" + shift # past argument=value + ;; + --license-header=*) + license="${i#*=}" + shift # past argument=value + ;; + --add) + mode="add" + if [ $mode == "update" ]; then + echo "Error: Script cannot be called with both options: --update or --add" + exit 1 + fi + shift # past argument=value + ;; + --update) + mode="update" + if [ $mode == "add" ]; then + echo "Error: Script cannot be called with both options: --update or --add" + exit 1 + fi + shift # past argument=value + ;; + --exclude=*) + exclude_folder="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; + esac +done + +# check options +if [ "$path" == "" ]; then + echo 'Please, provide the path to the folder containing the code with --path=/my/folder/code' + exit 1 +else + if [ -d "$path" ]; then + echo "Valid path: ${path}" + else + echo "Error: ${path} not found. Can not continue." + exit 1 + fi +fi + +if [ "$license" == "" ]; then + echo 'Error: Please, provide the path to the folder containing the code with --license-header=/my/license/header/file.txt' + exit 1 +else + if [ -f "$license" ]; then + echo "Valid license header file: ${license} containing:" + cat ${license} + else + echo "Error: License header file ${license} not found. Can not continue." + exit 1 + fi +fi + +if [ "$exclude_folder" == "" ]; then + echo "No folders will be excluded" +else + if [ -d "${path}/${exclude_folder}" ]; then + echo "Valid remove folder path: ${path}/${exclude_folder}" + fi +fi + +if [ $mode == "none" ]; then + echo "Error: Script should be called with one of the following options: --update or --add" + exit 1 +else + echo "mode: ${mode}" +fi + +# PATH (AND tmp FOLDER) +folder=$path +if [ $tmp == true ]; then + echo "Creating temporary folder: ${path}_tmp" + mkdir -pv ${path}_tmp + cp -a $path/. ${path}_tmp + folder=${path}_tmp +fi + +# LIST OF FILES TO BE ITERATED ON +if [ "$exclude_folder" == "" ]; then + file_list=$(find $folder -name '*.c' -or -name '*.cpp' -or -name '*.h' -or -name '*.hpp') +else + file_list=$(find $folder -path ${folder}/${exclude_folder} -prune -name '*.c' -or -name '*.cpp' -or -name '*.h' -or -name '*.hpp') +fi + +# DETECT AND REMOVE EXISTING LICENSE +if [ "$mode" == "update" ]; then + echo "Recursely removing license header from all files (.c, .cpp, .h, .hpp)" + for i in $file_list + do + if grep -Fxq ${line_start_mark} $i + then + echo " Removing license header from file ${i}" + line_start="$(grep -wn $line_start_mark ${i} | head -n 1 | cut -d: -f1)" + line_end="$(grep -wn $line_end_mark ${i} | head -n 1 | cut -d: -f1)" + #echo ${line_start} + #echo ${line_end} + awk -v m=$line_start -v n=$line_end 'm <= NR && NR <= n {next} {print}' $i > tmpfile && mv tmpfile $i + #cat $i + fi + done +fi + +# ADD CONTENT OF license-file AT THE BEGINNING OF CODE FILES +echo "Recursively adding license header to all files (.c, .cpp, .h, .hpp)" +for i in $file_list +do + if grep -Fxq ${line_start_mark} $i; then + echo "skippping ${i}" + else + ( echo ${line_start_mark}$'\n//'; cat ${license}; echo $'//\n'${line_end_mark}; cat $i ) > temp_file + mv temp_file $i + fi +done diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index cc0421b22732bbc4d4f41d7d6415b8bdb0175d6f..0000000000000000000000000000000000000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# driver source files -SET(sources gnss_utils.cpp observation.cpp) - -# application header files -SET(headers ../include/gnss_utils.h ../include/observation.h) - -# RTKLIB -INCLUDE (${PROJECT_SOURCE_DIR}/cmake_modules/FindRTKLIB.cmake) - -# Eigen ####### -FIND_PACKAGE(Eigen3 REQUIRED) - -# Boost ####### -set(Boost_USE_STATIC_LIBS OFF) -set(Boost_USE_MULTITHREADED OFF) -set(Boost_USE_STATIC_RUNTIME OFF) -FIND_PACKAGE(Boost REQUIRED) - -# Adding libraries' directories -link_directories(/usr/lib/x86_64-linux-gnu/) - -# Adding include directories -INCLUDE_DIRECTORIES(. ${EIGEN3_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${RTK_LIB_PATH}) - -# create the shared library -ADD_LIBRARY(gnss_utils SHARED ${sources}) -TARGET_LINK_LIBRARIES(gnss_utils ${Boost_LIBRARIES}) - -# Installing -INSTALL(TARGETS gnss_utils - RUNTIME DESTINATION bin - LIBRARY DESTINATION /usr/local/lib - ARCHIVE DESTINATION lib) -INSTALL(FILES ${headers} DESTINATION include/gnss_utils) -# INSTALL(FILES ../gnss_utils.cmake DESTINATION ${CMAKE_ROOT}/Modules/) -INSTALL(FILES ../Findgnss_utils.cmake DESTINATION ${CMAKE_ROOT}/Modules/) -ADD_SUBDIRECTORY(examples) diff --git a/src/examples/gnss_utils_test.cpp b/src/examples/gnss_utils_test.cpp index 8fd4243b068ee72957dfd3bf6a81682e9b367b6a..4689c4a7a89bf12afea1620e3d2bc86d0aa0f58b 100644 --- a/src/examples/gnss_utils_test.cpp +++ b/src/examples/gnss_utils_test.cpp @@ -1,40 +1,126 @@ -#include "../../include/gnss_utils.h" -#include "../../include/observation.h" +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" +#include "gnss_utils/utils/transformations.h" +#include "gnss_utils/utils/utils.h" + +#include <typeinfo> #include <iostream> -using namespace GNSSUtils; +extern "C" { +#include "../deps/RTKLIB/src/rinex.c" +} + +using namespace GnssUtils; -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { // header std::cout << "--------------------------" << std::endl; std::cout << "GNSS Utils Library Example" << std::endl; std::cout << "--------------------------" << std::endl; - // create a Receiver object - GNSSUtils::Receiver test_rcv; - - // create a Observation object - gtime_t ts={0}; - unsigned char q=1, w=2, e=3; - std::vector<unsigned char> vu({q, w, e}); - std::vector<double> vd({1., 2., 3.}); - std::vector<float> vf({1., 2., 3.}); - Observation test_obs(ts, 1, 1, vu, vu, vu, vd, vd, vf); - - // Display current Observation - std::cout << "Current observation (sat): " << "(" << test_obs.getSat() << ")" << std::endl; - std::cout << "Current observation (SNR): " << "(" ; - - - - for (unsigned char i=0; i < 3; ++i) - std::cout << test_obs.getSNR()[i] << ", "; - std::cout << ")" << std::endl; - - - obsd_t obs = test_obs.convert2rtk(); - std::cout << "Observation SNR in RTKLIB format: " << "("; - for (unsigned char i=0; i < 3; ++i) - std::cout << obs.SNR[i] << ", "; - std::cout << ")" << std::endl; + + // create Observations object + Observations observations; + + // create Navigation object + Navigation navigation; + + gtime_t t_start{ 0, 0 }; // no limit + gtime_t t_end{ 0, 0 }; // no limit + double dt = 0.0; // no limit + const char* opt = ""; // only GPS | GPS+GAL: "-SYS=G,L" | ALL: "" + + // load observations from RINEX file + std::vector<Observations> obs_rinex = + loadObservationsFromRinex("../src/examples/sample_data.obs", t_start, t_end, dt, opt); + observations = obs_rinex[0]; + observations.print(); + + // RTKLIB trace + char str_file[80]; + snprintf(str_file, sizeof str_file, "../src/examples/trace"); + tracelevel(5); + traceopen(str_file); + + // load navigation from RINEX file + navigation.loadFromRinex("../src/examples/sample_data.nav", t_start, t_end, dt, opt); + navigation.print(); + + // Trace close + // traceclose(); + + /* Set processing options */ + + /* header */ + std::cout << "------------------" << std::endl; + std::cout << "Processing options" << std::endl; + std::cout << "------------------" << std::endl; + + prcopt_t prcopt = prcopt_default; + prcopt.mode = PMODE_SINGLE; + prcopt.soltype = 0; + prcopt.nf = 1; + prcopt.navsys = SYS_GPS; + prcopt.elmin = 0.525; // 60 degrees = 1.05 rad + prcopt.sateph = EPHOPT_BRDC; + prcopt.ionoopt = IONOOPT_OFF; + prcopt.tropopt = TROPOPT_OFF; + prcopt.dynamics = 0; + prcopt.tidecorr = 0; + prcopt.sbascorr = SBSOPT_FCORR; + prcopt.ru[0] = 0; // 4789374.0336; + prcopt.ru[1] = 0; // 177048.3292; + prcopt.ru[2] = 0; // 4194542.6444; + + std::cout << "Processing options defined" << std::endl; + + Eigen::Vector3d lla_gt(41.383293114 * M_PI / 180.0, 2.116101115 * M_PI / 180.0, -91.6641); + + // Compute spp + + /* header */ + std::cout << "-----------" << std::endl; + std::cout << "pntpos call" << std::endl; + std::cout << "-----------" << std::endl; + + int stat; + + sol_t solb = { { 0 } }; + + char msg[128] = ""; + + stat = pntpos(observations.data(), observations.size(), &navigation.getNavigation(), &prcopt, &solb, NULL, NULL, + msg); + + std::cout << "msg: " << msg << std::endl; + std::cout << "sol.stat: " << int(solb.stat) << std::endl; + std::cout << "Position: " << solb.rr[0] << ", " << solb.rr[1] << ", " << solb.rr[2] << std::endl; + std::cout << "Position (GT): " << latLonAltToEcef(lla_gt).transpose() << std::endl; + std::cout << "Position LLA: " << ecefToLatLonAlt(Eigen::Vector3d(solb.rr[0], solb.rr[1], + solb.rr[2])).transpose() + << std::endl; + std::cout << "Position LLA (GT): " << lla_gt.transpose() << std::endl; + + // traceclose(); } diff --git a/src/examples/sample_data.nav b/src/examples/sample_data.nav new file mode 100644 index 0000000000000000000000000000000000000000..5c4c418dc876fd45e7fddca388b4cc98b0202441 --- /dev/null +++ b/src/examples/sample_data.nav @@ -0,0 +1,406 @@ + 3.04 N: GNSS NAV DATA M: MIXED RINEX VERSION / TYPE +sbf2rin-13.4.3 20200205 113410 UTC PGM / RUN BY / DATE +GPSA 9.3132E-09 -1.4901E-08 -5.9605E-08 1.1921E-07 IONOSPHERIC CORR +GPSB 9.6256E+04 -1.4746E+05 -1.3107E+05 9.1750E+05 IONOSPHERIC CORR +GAL 2.9250E+01 3.8281E-01 3.2959E-03 0.0000E+00 IONOSPHERIC CORR +GPUT -9.3132257462E-10 8.881784197E-16 503808 2083 TIME SYSTEM CORR +GAUT 9.3132257462E-10 0.000000000E+00 345600 2083 TIME SYSTEM CORR +GAGP 1.2514647096E-09-5.329070518E-15 345600 2083 TIME SYSTEM CORR + 18 LEAP SECONDS + END OF HEADER +G02 2019 12 12 12 00 00-3.648423589766E-04-7.503331289627E-12 0.000000000000E+00 + 4.700000000000E+01-1.208437500000E+02 4.378039505776E-09-1.891689129337E+00 + -6.053596735001E-06 1.953727600630E-02 9.275972843170E-06 5.153594808578E+03 + 3.888000000000E+05 3.371387720108E-07-2.768163608668E-01 2.346932888031E-07 + 9.575379914494E-01 1.915312500000E+02-1.691294764827E+00-7.654961717025E-09 + 2.000083311498E-11 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.769512891769E-08 4.700000000000E+01 + 3.868920000000E+05 4.000000000000E+00 +G04 2019 12 12 12 00 00-1.993030309677E-05-5.002220859751E-12 0.000000000000E+00 + 1.570000000000E+02-1.387500000000E+01 4.959135139313E-09-2.339868507292E+00 + -8.419156074524E-07 1.249722088687E-03 5.329027771950E-06 5.150576126099E+03 + 3.888000000000E+05 5.587935447693E-09 1.915372002909E+00 1.303851604462E-08 + 9.594040575671E-01 2.769062500000E+02-1.281079249179E+00-8.132124449911E-09 + -3.685867816904E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 4.096000000000E+03 6.300000000000E+01-4.190951585770E-09 6.690000000000E+02 + 3.873960000000E+05 4.000000000000E+00 +G06 2019 12 12 12 00 00-1.512947492301E-04-1.114131009672E-11 0.000000000000E+00 + 1.000000000000E+02-1.239062500000E+02 3.927663602954E-09-1.824134113525E+00 + -6.347894668579E-06 1.658447668888E-03 9.786337614059E-06 5.153719285965E+03 + 3.888000000000E+05-1.490116119385E-08-2.122724576342E-01 5.401670932770E-08 + 9.781077241470E-01 2.010000000000E+02-1.200768673201E+00-7.640318249923E-09 + 1.214336296267E-11 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 4.656612873077E-09 1.000000000000E+02 + 3.858180000000E+05 4.000000000000E+00 +G07 2019 12 12 12 00 00-1.683332957327E-04-8.185452315956E-12 0.000000000000E+00 + 5.700000000000E+01 3.909375000000E+01 4.250534194668E-09-2.882882750792E+00 + 1.888722181320E-06 1.320131728426E-02 1.172721385956E-05 5.153718780518E+03 + 3.888000000000E+05-7.450580596924E-09 2.943673467473E+00-1.341104507446E-07 + 9.545190186113E-01 1.482812500000E+02-2.422380694519E+00-7.540671242082E-09 + -4.639478967207E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.117587089539E-08 5.700000000000E+01 + 3.858180000000E+05 4.000000000000E+00 +G09 2019 12 12 12 00 00-1.062308438122E-04-9.436007530894E-12 0.000000000000E+00 + 7.800000000000E+01-1.206250000000E+01 5.168786729286E-09 4.992301981873E-01 + -7.078051567078E-07 1.692383317277E-03 5.021691322327E-06 5.153534730911E+03 + 3.888000000000E+05-2.793967723846E-08 1.868752621939E+00 1.490116119385E-08 + 9.520568016730E-01 2.776875000000E+02 1.684705661839E+00-8.283559329210E-09 + -2.914407111040E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 1.396983861923E-09 7.800000000000E+01 + 3.857520000000E+05 4.000000000000E+00 +G13 2019 12 12 12 00 00-2.714386209846E-05 2.387423592154E-12 0.000000000000E+00 + 8.700000000000E+01-1.987500000000E+01 4.779841956746E-09-8.793798521920E-01 + -1.072883605957E-06 4.130274290219E-03 5.709007382393E-06 5.153656044006E+03 + 3.888000000000E+05-6.332993507385E-08 2.009100851183E+00 9.313225746155E-08 + 9.674158381471E-01 2.706562500000E+02 1.183260297004E+00-8.083193840326E-09 + -5.078782980268E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.117587089539E-08 8.700000000000E+01 + 3.870420000000E+05 4.000000000000E+00 +G23 2019 12 12 12 00 00-1.500290818512E-04 2.387423592154E-12 0.000000000000E+00 + 3.000000000000E+01-1.009375000000E+01 5.335222233421E-09-1.452196357053E+00 + -4.973262548447E-07 1.326873130165E-02 4.552304744720E-06 5.153694892883E+03 + 3.888000000000E+05 2.980232238770E-07 1.861752114203E+00 4.470348358154E-08 + 9.428683609718E-01 2.778750000000E+02-2.215284453592E+00-8.261415549690E-09 + -2.478674675321E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-2.048909664154E-08 3.000000000000E+01 + 3.858420000000E+05 4.000000000000E+00 +G30 2019 12 12 12 00 00-1.042019575834E-04-8.640199666843E-12 0.000000000000E+00 + 2.500000000000E+01 4.368750000000E+01 4.663408535398E-09-2.906933186921E+00 + 2.210959792137E-06 4.164319136180E-03 1.142919063568E-05 5.153722436905E+03 + 3.888000000000E+05-2.793967723846E-08 2.968865819418E+00-3.725290298462E-09 + 9.399056183160E-01 1.480312500000E+02-2.921787544453E+00-7.888185717455E-09 + -4.778770483544E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 3.725290298462E-09 2.500000000000E+01 + 3.858180000000E+05 4.000000000000E+00 +R03 2019 12 12 11 15 00 6.369315087795E-06 0.000000000000E+00 3.858000000000E+05 + 9.652274414063E+03 3.457994461060E-01 9.313225746155E-10 0.000000000000E+00 + 1.708528710938E+04 2.202743530273E+00-1.862645149231E-09 5.000000000000E+00 + 1.629986035156E+04-2.522974967957E+00-2.793967723846E-09 0.000000000000E+00 +R03 2019 12 12 11 45 00 6.371177732944E-06 0.000000000000E+00 3.870000000000E+05 + 1.043946044922E+04 4.914197921753E-01 9.313225746155E-10 0.000000000000E+00 + 2.039817236328E+04 1.453030586243E+00-1.862645149231E-09 5.000000000000E+00 + 1.118693066406E+04-3.121288299561E+00-1.862645149231E-09 0.000000000000E+00 +R05 2019 12 12 11 15 00 4.108343273401E-05 9.094947017729E-13 3.862800000000E+05 + 5.985222167969E+03 1.195173263550E-01-1.862645149231E-09 0.000000000000E+00 + -1.828012353516E+04 2.295486450195E+00 1.862645149231E-09 1.000000000000E+00 + 1.676849218750E+04 2.457766532898E+00-1.862645149231E-09 0.000000000000E+00 +R05 2019 12 12 11 45 00 4.108529537916E-05 9.094947017729E-13 3.871800000000E+05 + 6.598400390625E+03 5.781021118164E-01-9.313225746155E-10 0.000000000000E+00 + -1.370180371094E+04 2.745733261108E+00 9.313225746155E-10 1.000000000000E+00 + 2.048785058594E+04 1.648019790649E+00-1.862645149231E-09 0.000000000000E+00 +R14 2019 12 12 11 15 00 4.597380757332E-05 0.000000000000E+00 3.858000000000E+05 + 2.135816357422E+04-6.865911483765E-01 3.725290298462E-09 0.000000000000E+00 + 1.261080761719E+04-4.694700241089E-01-0.000000000000E+00-7.000000000000E+00 + 5.991423828125E+03 3.442845344543E+00-2.793967723846E-09 0.000000000000E+00 +R14 2019 12 12 11 45 00 4.597473889589E-05 0.000000000000E+00 3.870000000000E+05 + 1.936734765625E+04-1.519350051880E+00 1.862645149231E-09 0.000000000000E+00 + 1.162212011719E+04-5.869359970093E-01-9.313225746155E-10-7.000000000000E+00 + 1.187716162109E+04 3.054467201233E+00-2.793967723846E-09 0.000000000000E+00 +R15 2019 12 12 11 15 00 1.051910221577E-04 0.000000000000E+00 3.860100000000E+05 + 2.249286962891E+04 1.470921516418E+00 5.587935447693E-09 0.000000000000E+00 + 4.179755859375E+03 4.950771331787E-01 9.313225746155E-10 0.000000000000E+00 + -1.125261865234E+04 3.118181228638E+00-1.862645149231E-09 0.000000000000E+00 +R15 2019 12 12 11 45 00 1.051910221577E-04 0.000000000000E+00 3.870000000000E+05 + 2.451276953125E+04 7.425718307495E-01 4.656612873077E-09 0.000000000000E+00 + 4.643450683594E+03 4.951381683350E-02 0.000000000000E+00 0.000000000000E+00 + -5.276882812500E+03 3.478320121765E+00-2.793967723846E-09 0.000000000000E+00 +R19 2019 12 12 11 15 00-6.853323429823E-05-1.818989403546E-12 3.859500000000E+05 + 2.008951708984E+04 2.060537338257E-01 2.793967723846E-09 0.000000000000E+00 + -1.541425244141E+04-4.377956390381E-01 2.793967723846E-09 3.000000000000E+00 + 3.049126953125E+03-3.578741073608E+00-1.862645149231E-09 0.000000000000E+00 +R19 2019 12 12 11 45 00-6.853695958853E-05-1.818989403546E-12 3.870000000000E+05 + 1.978871044922E+04-5.135507583618E-01 3.725290298462E-09 0.000000000000E+00 + -1.571828076172E+04 1.333026885986E-01 1.862645149231E-09 3.000000000000E+00 + -3.427280273438E+03-3.570507049561E+00-1.862645149231E-09 0.000000000000E+00 +R13 2019 12 12 11 45 00-3.215298056602E-05-0.000000000000E+00 3.876000000000E+05 + 3.100402832031E+03-2.939124107361E+00-1.862645149231E-09 0.000000000000E+00 + 1.186301953125E+04-8.695030212402E-01-9.313225746155E-10-2.000000000000E+00 + 2.235348193359E+04 8.685455322266E-01-1.862645149231E-09 0.000000000000E+00 +R20 2019 12 12 11 45 00-3.986386582255E-04-0.000000000000E+00 3.877800000000E+05 + 9.837636718750E+03 1.428291320801E+00-0.000000000000E+00 0.000000000000E+00 + -1.795626220703E+04-1.534766197205E+00 1.862645149231E-09 2.000000000000E+00 + 1.525312060547E+04-2.726587295532E+00-1.862645149231E-09 0.000000000000E+00 +E01 2019 12 12 11 10 00-7.507699192502E-04-7.986500349944E-12 0.000000000000E+00 + 3.000000000000E+00 2.013125000000E+02 2.449744898851E-09 7.981291655576E-01 + 9.473413228989E-06 1.731918891892E-04 9.533017873764E-06 5.440622966766E+03 + 3.858000000000E+05 3.166496753693E-08-2.605880888987E+00-1.117587089539E-08 + 9.855193908613E-01 1.463750000000E+02-1.069210219000E+00-5.272719629937E-09 + 1.914365455291E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.864650000000E+05 +E01 2019 12 12 11 20 00-7.507746340707E-04-7.972289495228E-12 0.000000000000E+00 + 4.000000000000E+00 2.002500000000E+02 2.449744898851E-09 8.736282654153E-01 + 9.419396519661E-06 1.731686061248E-04 9.588897228241E-06 5.440622581482E+03 + 3.864000000000E+05 3.725290298462E-08-2.605884063520E+00-1.862645149231E-09 + 9.855195488564E-01 1.452500000000E+02-1.070324887968E+00-5.265933632987E-09 + 1.896507568581E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.870650000000E+05 +E09 2019 12 12 11 10 00 6.223833654076E-03-1.216449163621E-11 0.000000000000E+00 + 3.000000000000E+00 7.218750000000E+00 3.823373544569E-09-3.442271110123E-02 + 3.892928361893E-07 2.964780433103E-04 3.403052687645E-06 5.440603132248E+03 + 3.858000000000E+05-9.313225746155E-09 1.575307486314E+00-6.146728992462E-08 + 9.586413959478E-01 2.625000000000E+02 2.794206052069E-01-5.815242228181E-09 + 8.571785620706E-12 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-4.656612873077E-10-6.984919309616E-10 + 3.869030000000E+05 +E12 2019 12 12 10 40 00 6.055364210624E-03-1.870148480521E-11 0.000000000000E+00 + 0.000000000000E+00-2.075312500000E+02 2.603322724555E-09-2.414195903395E+00 + -9.659677743912E-06 2.996901748702E-04 6.495043635368E-06 5.440614477158E+03 + 3.840000000000E+05-2.421438694000E-08-5.219517721978E-01 5.774199962616E-08 + 9.864495053986E-01 2.151250000000E+02-9.348391204646E-01-5.376652530588E-09 + -2.257236880119E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.327134668827E-08-1.327134668827E-08 + 3.858050000000E+05 +E19 2019 12 12 11 00 00-2.469634637237E-06 2.273736754432E-13 0.000000000000E+00 + 2.000000000000E+00 1.103125000000E+01 3.765871149364E-09-1.685770007887E-01 + 4.824250936508E-07 1.212444622070E-04 3.688037395477E-06 5.440600940704E+03 + 3.852000000000E+05-5.401670932770E-08 1.580615112564E+00 5.215406417847E-08 + 9.583897608719E-01 2.595937500000E+02 1.996512379669E+00-5.770954669140E-09 + 1.160762636137E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09-5.820766091347E-09 + 3.858660000000E+05 +E19 2019 12 12 11 10 00-2.469576429576E-06 2.273736754432E-13 0.000000000000E+00 + 3.000000000000E+00 1.168750000000E+01 3.765156833895E-09-9.388288915917E-02 + 5.066394805908E-07 1.211694907397E-04 3.693625330925E-06 5.440600774765E+03 + 3.858000000000E+05-5.960464477539E-08 1.580611582543E+00 4.097819328308E-08 + 9.583898398694E-01 2.594687500000E+02 1.996204499781E+00-5.764168672191E-09 + 1.207193141583E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09-5.820766091347E-09 + 3.864650000000E+05 +E21 2019 12 12 11 10 00-5.709171527997E-04-2.131628207280E-12 0.000000000000E+00 + 3.000000000000E+00 2.180312500000E+02 2.333311477503E-09 1.537835498027E+00 + 1.022964715958E-05 9.841390419751E-05 9.685754776001E-06 5.440636165619E+03 + 3.858000000000E+05-2.980232238770E-08-2.608584029515E+00 4.842877388000E-08 + 9.850865499746E-01 1.424062500000E+02-1.025413854454E+00-5.217717338871E-09 + 1.871506527187E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.867350000000E+05 +E21 2019 12 12 11 20 00-5.709183751605E-04-2.131628207280E-12 0.000000000000E+00 + 4.000000000000E+00 2.213125000000E+02 2.317953694933E-09 1.607657241034E+00 + 1.038052141666E-05 9.810912888497E-05 9.816139936447E-06 5.440637243271E+03 + 3.864000000000E+05-4.097819328308E-08-2.608587130902E+00 4.470348358154E-08 + 9.850867050439E-01 1.395312500000E+02-1.020851268976E+00-5.213788603794E-09 + 1.800074980348E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.870650000000E+05 +E01 2019 12 12 11 10 00-7.507689879276E-04-7.986500349944E-12 0.000000000000E+00 + 3.000000000000E+00 2.013125000000E+02 2.449744898851E-09 7.981291655576E-01 + 9.473413228989E-06 1.731918891892E-04 9.533017873764E-06 5.440622966766E+03 + 3.858000000000E+05 3.166496753693E-08-2.605880888987E+00-1.117587089539E-08 + 9.855193908613E-01 1.463750000000E+02-1.069210219000E+00-5.272719629937E-09 + 1.914365455291E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.865400000000E+05 +E01 2019 12 12 11 20 00-7.507736445405E-04-7.972289495228E-12 0.000000000000E+00 + 4.000000000000E+00 2.002500000000E+02 2.449744898851E-09 8.736282654153E-01 + 9.419396519661E-06 1.731686061248E-04 9.588897228241E-06 5.440622581482E+03 + 3.864000000000E+05 3.725290298462E-08-2.605884063520E+00-1.862645149231E-09 + 9.855195488564E-01 1.452500000000E+02-1.070324887968E+00-5.265933632987E-09 + 1.896507568581E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.871400000000E+05 +E12 2019 12 12 10 40 00 6.055364909116E-03-1.871569565992E-11 0.000000000000E+00 + 0.000000000000E+00-2.075312500000E+02 2.603322724555E-09-2.414195903395E+00 + -9.659677743912E-06 2.996901748702E-04 6.495043635368E-06 5.440614477158E+03 + 3.840000000000E+05-2.421438694000E-08-5.219517721978E-01 5.774199962616E-08 + 9.864495053986E-01 2.151250000000E+02-9.348391204646E-01-5.376652530588E-09 + -2.257236880119E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.327134668827E-08 0.000000000000E+00 + 3.858400000000E+05 +E19 2019 12 12 11 00 00-2.468528691679E-06 2.273736754432E-13 0.000000000000E+00 + 2.000000000000E+00 1.103125000000E+01 3.765871149364E-09-1.685770007887E-01 + 4.824250936508E-07 1.212444622070E-04 3.688037395477E-06 5.440600940704E+03 + 3.852000000000E+05-5.401670932770E-08 1.580615112564E+00 5.215406417847E-08 + 9.583897608719E-01 2.595937500000E+02 1.996512379669E+00-5.770954669140E-09 + 1.160762636137E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09 0.000000000000E+00 + 3.859400000000E+05 +E19 2019 12 12 11 10 00-2.468470484018E-06 2.273736754432E-13 0.000000000000E+00 + 3.000000000000E+00 1.168750000000E+01 3.765156833895E-09-9.388288915917E-02 + 5.066394805908E-07 1.211694907397E-04 3.693625330925E-06 5.440600774765E+03 + 3.858000000000E+05-5.960464477539E-08 1.580611582543E+00 4.097819328308E-08 + 9.583898398694E-01 2.594687500000E+02 1.996204499781E+00-5.764168672191E-09 + 1.207193141583E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09 0.000000000000E+00 + 3.865400000000E+05 +E21 2019 12 12 11 10 00-5.709161632694E-04-2.117417352565E-12 0.000000000000E+00 + 3.000000000000E+00 2.180312500000E+02 2.333311477503E-09 1.537835498027E+00 + 1.022964715958E-05 9.841390419751E-05 9.685754776001E-06 5.440636165619E+03 + 3.858000000000E+05-2.980232238770E-08-2.608584029515E+00 4.842877388000E-08 + 9.850865499746E-01 1.424062500000E+02-1.025413854454E+00-5.217717338871E-09 + 1.871506527187E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.867900000000E+05 +E21 2019 12 12 11 20 00-5.709173856303E-04-2.131628207280E-12 0.000000000000E+00 + 4.000000000000E+00 2.213125000000E+02 2.317953694933E-09 1.607657241034E+00 + 1.038052141666E-05 9.810912888497E-05 9.816139936447E-06 5.440637243271E+03 + 3.864000000000E+05-4.097819328308E-08-2.608587130902E+00 4.470348358154E-08 + 9.850867050439E-01 1.395312500000E+02-1.020851268976E+00-5.213788603794E-09 + 1.800074980348E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.871400000000E+05 +E04 2019 12 12 11 10 00-4.236891982146E-04-7.531752999057E-12 0.000000000000E+00 + 3.000000000000E+00 9.781250000000E+00 3.935521073107E-09-4.009111796513E-01 + 5.532056093216E-07 8.811207953840E-05 3.596767783165E-06 5.440604427338E+03 + 3.858000000000E+05 9.499490261078E-08 1.582511472789E+00 3.166496753693E-08 + 9.526713574660E-01 2.577187500000E+02 1.440283619102E+00-5.908817554540E-09 + 7.071723137082E-11 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.355104804039E-09-5.820766091347E-09 + 3.875670000000E+05 +E04 2019 12 12 11 10 00-4.236878594384E-04-7.531752999057E-12 0.000000000000E+00 + 3.000000000000E+00 9.781250000000E+00 3.935521073107E-09-4.009111796513E-01 + 5.532056093216E-07 8.811207953840E-05 3.596767783165E-06 5.440604427338E+03 + 3.858000000000E+05 9.499490261078E-08 1.582511472789E+00 3.166496753693E-08 + 9.526713574660E-01 2.577187500000E+02 1.440283619102E+00-5.908817554540E-09 + 7.071723137082E-11 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.355104804039E-09 0.000000000000E+00 + 3.875900000000E+05 +E11 2019 12 12 11 00 00 3.295102505945E-04 1.526245796413E-10 5.204170427930E-18 + 2.000000000000E+00-2.123750000000E+02 2.665468170305E-09 2.480654590844E+00 + -9.858980774879E-06 1.651302445680E-04 7.150694727898E-06 5.440609029770E+03 + 3.852000000000E+05-3.352761268616E-08-5.219456059981E-01 2.048909664154E-08 + 9.864349859366E-01 1.975937500000E+02-2.012781103955E-01-5.400939256513E-09 + -2.103659054415E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.583248376846E-08-1.699663698673E-08 + 3.876330000000E+05 +E21 2019 12 12 11 30 00-5.709196557291E-04-2.131628207280E-12 0.000000000000E+00 + 5.000000000000E+00 2.240625000000E+02 2.303667385565E-09 1.677767938158E+00 + 1.050904393196E-05 9.777839295566E-05 9.970739483833E-06 5.440638303757E+03 + 3.870000000000E+05-5.401670932770E-08-2.608590226436E+00 3.725290298462E-08 + 9.850868571874E-01 1.362812500000E+02-1.016577701982E+00-5.210574184187E-09 + 1.721500278825E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.876650000000E+05 +E11 2019 12 12 11 00 00 3.295124042779E-04 1.526387904960E-10 5.204170427930E-18 + 2.000000000000E+00-2.123750000000E+02 2.665468170305E-09 2.480654590844E+00 + -9.858980774879E-06 1.651302445680E-04 7.150694727898E-06 5.440609029770E+03 + 3.852000000000E+05-3.352761268616E-08-5.219456059981E-01 2.048909664154E-08 + 9.864349859366E-01 1.975937500000E+02-2.012781103955E-01-5.400939256513E-09 + -2.103659054415E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.583248376846E-08 0.000000000000E+00 + 3.876800000000E+05 +E01 2019 12 12 11 30 00-7.507792906836E-04-7.972289495228E-12 0.000000000000E+00 + 5.000000000000E+00 1.994687500000E+02 2.447601952446E-09 9.486982138657E-01 + 9.378418326378E-06 1.731649972498E-04 9.616836905479E-06 5.440622346878E+03 + 3.870000000000E+05 3.911554813385E-08-2.605887229274E+00 7.450580596924E-09 + 9.855197039257E-01 1.446875000000E+02-1.071010335308E+00-5.259504793772E-09 + 1.871506527187E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.877250000000E+05 +E21 2019 12 12 11 30 00-5.709186661988E-04-2.117417352565E-12 0.000000000000E+00 + 5.000000000000E+00 2.240625000000E+02 2.303667385565E-09 1.677767938158E+00 + 1.050904393196E-05 9.777839295566E-05 9.970739483833E-06 5.440638303757E+03 + 3.870000000000E+05-5.401670932770E-08-2.608590226436E+00 3.725290298462E-08 + 9.850868571874E-01 1.362812500000E+02-1.016577701982E+00-5.210574184187E-09 + 1.721500278825E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.877400000000E+05 +E01 2019 12 12 11 30 00-7.507783011533E-04-7.972289495228E-12 0.000000000000E+00 + 5.000000000000E+00 1.994687500000E+02 2.447601952446E-09 9.486982138657E-01 + 9.378418326378E-06 1.731649972498E-04 9.616836905479E-06 5.440622346878E+03 + 3.870000000000E+05 3.911554813385E-08-2.605887229274E+00 7.450580596924E-09 + 9.855197039257E-01 1.446875000000E+02-1.071010335308E+00-5.259504793772E-09 + 1.871506527187E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.878200000000E+05 +E36 2019 12 12 10 30 00 6.253067986108E-04-4.803268893738E-12 0.000000000000E+00 + 1.270000000000E+02-2.396562500000E+02 2.521890761159E-09 3.060908164528E+00 + -1.121312379837E-05 3.076605498791E-04 7.288530468941E-06 5.440609262466E+03 + 3.834000000000E+05 2.980232238770E-08-5.264936383676E-01 5.587935447693E-08 + 9.896539850266E-01 1.944062500000E+02-1.782833699345E+00-5.428440402046E-09 + -2.457245211269E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 3.725290298462E-09 4.423782229424E-09 + 3.878450000000E+05 +S36 2019 12 12 11 36 48 0.000000000000E+00 0.000000000000E+00 3.874820000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.530000000000E+02 +S25 2019 12 11 23 59 44 0.000000000000E+00 0.000000000000E+00 3.874840000000E+05 + 4.053076976000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + -1.162201120000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 +S23 2019 12 12 11 37 04 0.000000000000E+00 0.000000000000E+00 3.874940000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.460000000000E+02 +S36 2019 12 12 11 38 56 0.000000000000E+00 0.000000000000E+00 3.875460000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.540000000000E+02 +S23 2019 12 12 11 39 12 0.000000000000E+00 0.000000000000E+00 3.875580000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.470000000000E+02 +S23 2019 12 12 11 41 20 0.000000000000E+00 0.000000000000E+00 3.876860000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.480000000000E+02 +S36 2019 12 12 11 41 04 0.000000000000E+00 0.000000000000E+00 3.877380000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.550000000000E+02 +S36 2019 12 12 11 43 12 0.000000000000E+00 0.000000000000E+00 3.878020000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 +S23 2019 12 12 11 43 28 0.000000000000E+00 0.000000000000E+00 3.878780000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.490000000000E+02 +C05 2019 12 12 11 00 00-2.244751667604E-04-5.808153957787E-11 0.000000000000E+00 + 1.000000000000E+00-4.986718750000E+02 1.785788670980E-12-5.169766818568E-01 + -1.630326732993E-05 6.773804780096E-04 2.061109989882E-05 6.493346771240E+03 + 3.852000000000E+05 1.271255314350E-07-1.766797704845E-01 3.259629011154E-09 + 1.180676987021E-01-6.241562500000E+02-1.608095995036E+00 1.192549674481E-09 + 7.575315542299E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-8.000000000000E-10-9.200000000000E-09 + 3.858276000000E+05 0.000000000000E+00 +C10 2019 12 12 11 00 00-4.537283675745E-04-3.685762806072E-11 0.000000000000E+00 + 1.000000000000E+00 9.611718750000E+02 9.743262988869E-10-3.040647920928E+00 + 3.135204315186E-05 6.156359100714E-03 7.578637450933E-06 6.493785018921E+03 + 3.852000000000E+05-1.676380634308E-08-2.379959881122E+00-4.004687070847E-08 + 9.058375010021E-01-1.493750000000E+01-2.570309406835E+00-2.115088101909E-09 + 4.782342060886E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 5.000000000000E-09 2.400000000000E-09 + 3.858180000000E+05 0.000000000000E+00 +C23 2019 12 12 11 00 00-8.696540025994E-04 2.136069099379E-12 0.000000000000E+00 + 1.000000000000E+00 1.034062500000E+02 3.498002848716E-09 1.631067082891E+00 + 5.179084837437E-06 2.014992060140E-04 1.188227906823E-05 5.282628129959E+03 + 3.852000000000E+05 3.725290298462E-08-3.016985033742E+00 6.565824151039E-08 + 9.546959600159E-01 1.175156250000E+02-1.126531243634E+00-6.488127399406E-09 + -1.864363372504E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 2.430000000000E-08 2.430000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C30 2019 12 12 11 00 00 2.388220746070E-04 6.104450278599E-12 0.000000000000E+00 + 1.000000000000E+00-1.393437500000E+02 3.927663602954E-09-2.732742750025E+00 + -6.837770342827E-06 3.649030113593E-04 1.750886440277E-06 5.282614929199E+03 + 3.852000000000E+05-3.864988684654E-08-9.521190897989E-01-5.587935447693E-09 + 9.644793109832E-01 3.275781250000E+02-4.157902561103E-01-7.082080711374E-09 + -2.339383158984E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-1.040000000000E-08-1.040000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C32 2019 12 12 11 00 00-8.566775359213E-04 2.992273095970E-12 0.000000000000E+00 + 1.000000000000E+00 2.875000000000E+01 4.228033257413E-09 2.867304095205E+00 + 1.456588506699E-06 1.842749770731E-04 6.427057087421E-06 5.282614164352E+03 + 3.852000000000E+05 1.862645149231E-09 1.148173467908E+00 6.705522537231E-08 + 9.608588315814E-01 2.297500000000E+02-1.050918258529E+00-6.857785654299E-09 + 5.228789228631E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-9.900000000000E-09-9.900000000000E-09 + 3.857580000000E+05 1.000000000000E+00 +C37 2019 12 12 11 00 00-8.880557725206E-04-5.954881032721E-11 0.000000000000E+00 + 1.000000000000E+00 1.086562500000E+02 3.521932416908E-09 8.066563687499E-01 + 5.388632416725E-06 7.037379546091E-04 1.127412542701E-05 5.282630052567E+03 + 3.852000000000E+05-2.142041921616E-08-3.020306267399E+00 1.536682248116E-08 + 9.560827215058E-01 1.306718750000E+02-1.080448983277E+00-6.580988410297E-09 + -2.328668426958E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-1.390000000000E-08-1.390000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C20 2019 12 12 11 00 00-4.067538538948E-04-5.008171655163E-11 0.000000000000E+00 + 1.000000000000E+00 2.850000000000E+01 4.229461888350E-09 1.463145840983E+00 + 1.523178070784E-06 5.460308166221E-04 6.177462637424E-06 5.282614208221E+03 + 3.852000000000E+05 3.445893526077E-08 1.153432718382E+00-1.210719347000E-08 + 9.622213291121E-01 2.341406250000E+02-4.480160490039E-01-6.852785446020E-09 + 4.753769442150E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 2.090000000000E-08 2.090000000000E-08 + 3.875880000000E+05 1.000000000000E+00 diff --git a/src/examples/sample_data.obs b/src/examples/sample_data.obs new file mode 100644 index 0000000000000000000000000000000000000000..d773d3b4d57556afcd38b64ef4167981a00de992 --- /dev/null +++ b/src/examples/sample_data.obs @@ -0,0 +1,58 @@ + 3.04 OBSERVATION DATA M RINEX VERSION / TYPE +sbf2rin-13.4.3 20200205 113408 UTC PGM / RUN BY / DATE +SEPT MARKER NAME +Unknown MARKER NUMBER +Unknown Unknown OBSERVER / AGENCY +3021420 SEPT ASTERX-M2 4.4.0 REC # / TYPE / VERS +Unknown Unknown ANT # / TYPE + 4789398.3686 176958.8129 4194502.0999 APPROX POSITION XYZ + 0.0000 0.0000 0.0000 ANTENNA: DELTA H/E/N +G 7 X1 C1C L1C C2W L2W C2L L2L SYS / # / OBS TYPES +E 7 X1 C1C L1C C5Q L5Q C7Q L7Q SYS / # / OBS TYPES +S 3 X1 C1C L1C SYS / # / OBS TYPES +R 5 X1 C1C L1C C2C L2C SYS / # / OBS TYPES +C 5 X1 C2I L2I C7I L7I SYS / # / OBS TYPES +SEPTENTRIO RECEIVERS OUTPUT ALIGNED CARRIER PHASES. COMMENT +NO FURTHER PHASE SHIFT APPLIED IN THE RINEX ENCODER. COMMENT +G L1C SYS / PHASE SHIFT +G L2W SYS / PHASE SHIFT +G L2L 0.00000 SYS / PHASE SHIFT +E L1C 0.00000 SYS / PHASE SHIFT +E L5Q 0.00000 SYS / PHASE SHIFT +E L7Q 0.00000 SYS / PHASE SHIFT +S L1C SYS / PHASE SHIFT +R L1C SYS / PHASE SHIFT +R L2C SYS / PHASE SHIFT +C L2I SYS / PHASE SHIFT +C L7I SYS / PHASE SHIFT + 0.050 INTERVAL + 2019 12 12 11 37 42.0000000 GPS TIME OF FIRST OBS + 2019 12 12 11 45 12.9000000 GPS TIME OF LAST OBS + 36 # OF SATELLITES + C1C 0.000 C2C 0.000 GLONASS COD/PHS/BIS + 8 R03 5 R05 1 R13 -2 R14 -7 R15 0 R19 3 R20 2 R21 4 GLONASS SLOT / FRQ # + END OF HEADER +> 2019 12 12 11 37 42.0000000 0 23 +C05 16.000 40138840.659 6 209013478.40606 40138832.256 6 161622441.87406 +C10 17.000 39633104.355 5 206379985.90205 39633100.852 6 159586077.69206 +C23 18.000 22865676.301 8 119067585.90908 +C32 19.000 22153454.599 8 115358921.19508 +C37 20.000 23690566.611 7 123363010.77907 +E01 11.000 26311074.676 7 138265636.53007 26311074.470 5 103250320.19005 26311070.977 7 105943790.57907 +E09 12.000 25723493.482 5 +E12 13.000 24803095.627 5 130341141.55605 24803092.768 4 97332661.71504 24803092.407 6 99871767.93506 +E19 14.000 24540665.652 6 128962068.93606 24540664.837 5 96302837.53105 24540661.694 6 98815075.63606 +E21 15.000 25609802.251 7 134580416.73507 25609802.381 7 100498367.38007 25609799.519 7 103120055.01507 +G02 1.000 22032430.127 5 +G06 2.000 22147435.410 6 116385570.45206 22147437.095 4 90690045.24704 22147437.422 7 90690046.16907 +G07 3.000 20902411.481 8 109842911.60108 20902406.002 6 85591857.15006 20902406.930 7 85591857.16507 +G09 4.000 21908403.525 6 115129430.37406 21908399.544 1 89711233.32401 21908399.320 5 +G23 5.000 24104714.314 6 126671129.49306 24104706.816 2 98704739.67602 +G30 6.000 21404145.095 8 112479542.93308 21404142.953 6 87646388.26706 21404143.916 7 87646383.27307 +R03 7.000 21945110.617 7 117474090.63807 21945109.720 6 91368580.68606 +R14 8.000 20214975.526 8 107757316.39008 20214976.579 7 83811176.41707 +R15 9.000 22699008.378 5 121296675.96005 22699009.712 5 94341922.34705 +R19 10.000 22853592.745 4 22853590.888 4 +S23 21.000 38309228.895 7 201316353.13407 +S25 22.000 37834172.957 6 198818604.39206 +S36 23.000 37630702.258 7 197750698.01307 diff --git a/src/examples/trace b/src/examples/trace new file mode 100644 index 0000000000000000000000000000000000000000..362c7af58c323df3a6d40b9e316a4b74679ddb30 --- /dev/null +++ b/src/examples/trace @@ -0,0 +1,86 @@ +3 readrnxt: file=../src/examples/sample_data.nav rcv=1 +3 expath : path=../src/examples/sample_data.nav nmax=1024 +3 expath : file=../src/examples/sample_data.nav +3 readrnxfile: file=../src/examples/sample_data.nav flag=0 index=1 +3 rtk_uncompress: file=../src/examples/sample_data.nav +3 rtk_uncompress: stat=0 +3 readrnxfp: flag=0 index=1 +3 readrnxh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +4 decode_navh: +3 readrnxnav: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=49 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=38 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=36 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=49 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=36 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=36 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=49 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=49 +4 readrnxnavb: ver=3.04 sys=0 +4 decode_seph: ver=3.04 sat=36 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +4 readrnxnavb: ver=3.04 sys=0 +3 uniqnav: neph=0 ngeph=0 nseph=9 +3 uniqeph: n=0 +3 uniqgeph: ng=0 +3 uniqseph: ns=9 +4 uniqseph: ns=9 diff --git a/src/gnss_utils.cpp b/src/gnss_utils.cpp deleted file mode 100644 index 16f173b9ee18f2a690af3dbd67574c852d222b89..0000000000000000000000000000000000000000 --- a/src/gnss_utils.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "../include/gnss_utils.h" - -using namespace GNSSUtils; - -/******************************* RECEIVER CLASS *******************************************/ - -Receiver::Receiver() -{ - -} - -Receiver::~Receiver() -{ - -} - - -/* - Observation - */ - -void Receiver::clearObservations() -{ - _obsVector.clear(); -} - -void Receiver::pushObservation(obsd_t obs) -{ - _obsVector.push_back(obs); -} - -std::vector<obsd_t> Receiver::getObservations() -{ - return this->_obsVector; -} - - -/* - Navigation - */ - -void Receiver::clearNavigation() -{ - _navVector.clear(); -} - -void Receiver::pushNavigation(nav_t nav) -{ - _navVector.push_back(nav); -} - -std::vector<nav_t> Receiver::getNavigation() -{ - return this->_navVector; -} - - - - - - - -int Receiver::getSPP(double *azel, char *msg) -{ - obsd_t *obs = &this->_obsVector[0]; - int n = this->_obsVector.size(); - - return int pntpos(*obs, n, nav_t *nav, *opt, *sol, *azel, *ssat, *msg) -} - - - - - - diff --git a/src/navigation.cpp b/src/navigation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9d86a92b95700462279c3558dcb4008d913e0df --- /dev/null +++ b/src/navigation.cpp @@ -0,0 +1,459 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/navigation.h" + +using namespace GnssUtils; + +Navigation::Navigation() +{ + // array initialization + nav_.n = nav_.nmax = 0; + nav_.ng = nav_.ngmax = 0; + nav_.ns = nav_.nsmax = 0; + nav_.ne = nav_.nemax = 0; + nav_.nc = nav_.ncmax = 0; + nav_.na = nav_.namax = 0; + nav_.nt = nav_.ntmax = 0; + nav_.nf = nav_.nfmax = 0; + nav_.eph = NULL; + nav_.geph = NULL; + nav_.seph = NULL; + nav_.peph = NULL; + nav_.pclk = NULL; + nav_.alm = NULL; + nav_.tec = NULL; + nav_.fcb = NULL; + clearNavigation(); +} + +Navigation::Navigation(const Navigation& nav) +{ + // array initialization + nav_.n = nav_.nmax = 0; + nav_.ng = nav_.ngmax = 0; + nav_.ns = nav_.nsmax = 0; + nav_.ne = nav_.nemax = 0; + nav_.nc = nav_.ncmax = 0; + nav_.na = nav_.namax = 0; + nav_.nt = nav_.ntmax = 0; + nav_.nf = nav_.nfmax = 0; + nav_.eph = NULL; + nav_.geph = NULL; + nav_.seph = NULL; + nav_.peph = NULL; + nav_.pclk = NULL; + nav_.alm = NULL; + nav_.tec = NULL; + nav_.fcb = NULL; + clearNavigation(); + setNavigation(nav.getNavigation()); +} + +Navigation::~Navigation() +{ + freeNavigationArrays(); +} + +void Navigation::setNavigation(nav_t _nav) +{ + freeNavigationArrays(); + + copyAllArrays(_nav); + // int n,nmax; /* number of broadcast ephemeris */ + // int ng,ngmax; /* number of glonass ephemeris */ + // int ns,nsmax; /* number of sbas ephemeris */ + // int ne,nemax; /* number of precise ephemeris */ + // int nc,ncmax; /* number of precise clock */ + // int na,namax; /* number of almanac data */ + // int nt,ntmax; /* number of tec grid data */ + // int nf,nfmax; /* number of satellite fcb data */ + // eph_t *eph; /* GPS/QZS/GAL ephemeris */ + // geph_t *geph; /* GLONASS ephemeris */ + // seph_t *seph; /* SBAS ephemeris */ + // peph_t *peph; /* precise ephemeris */ + // pclk_t *pclk; /* precise clock */ + // alm_t *alm; /* almanac data */ + // tec_t *tec; /* tec grid data */ + // fcbd_t *fcb; /* satellite fcb data */ + // double lam[MAXSAT][NFREQ]; /* carrier wave lengths (m) */ + + copyIonUtc(_nav); + // double utc_gps[4]; /* GPS delta-UTC parameters {A0,A1,T,W} */ + // double utc_glo[4]; /* GLONASS UTC GPS time parameters */ + // double utc_gal[4]; /* Galileo UTC GPS time parameters */ + // double utc_qzs[4]; /* QZS UTC GPS time parameters */ + // double utc_cmp[4]; /* BeiDou UTC parameters */ + // double utc_irn[4]; /* IRNSS UTC parameters */ + // double utc_sbs[4]; /* SBAS UTC parameters */ + // double ion_gps[8]; /* GPS iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} */ + // double ion_gal[4]; /* Galileo iono model parameters {ai0,ai1,ai2,0} */ + // double ion_qzs[8]; /* QZSS iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} */ + // double ion_cmp[8]; /* BeiDou iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} */ + // double ion_irn[8]; /* IRNSS iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} */ + // int leaps; /* leap seconds (s) */ + + copySbasCorrections(_nav); + // sbssat_t sbssat; /* SBAS satellite corrections */ + // sbsion_t sbsion[MAXBAND+1]; /* SBAS ionosphere corrections */ + // pcv_t pcvs[MAXSAT]; /* satellite antenna pcv */ + + copyBias(_nav); + // double cbias[MAXSAT][3]; /* satellite dcb (0:p1-p2,1:p1-c1,2:p2-c2) (m) */ + // double rbias[MAXRCV][2][3]; /* receiver dcb (0:p1-p2,1:p1-c1,2:p2-c2) (m) */ + // double wlbias[MAXSAT]; /* wide-lane bias (cycle) */ + // double glo_cpbias[4]; /* glonass code-phase bias {1C,1P,2C,2P} (m) */ + // char glo_fcn[MAXPRNGLO+1]; /* glonass frequency channel number + 8 */ + + + // ********** other not copied nav_t content: *********** + // erp_t erp; /* earth rotation parameters */ + // dgps_t dgps[MAXSAT]; /* DGPS corrections */ + // ssr_t ssr[MAXSAT]; /* SSR corrections */ + // lexeph_t lexeph[MAXSAT]; /* LEX ephemeris */ + // lexion_t lexion; /* LEX ionosphere correction */ + // pppcorr_t pppcorr; /* ppp corrections */ +} + +void Navigation::clearNavigation() +{ + freeNavigationArrays(); + + memset(nav_.utc_gps, 0, sizeof(nav_.utc_gps)); + memset(nav_.utc_glo, 0, sizeof(nav_.utc_glo)); + memset(nav_.utc_gal, 0, sizeof(nav_.utc_gal)); + memset(nav_.utc_qzs, 0, sizeof(nav_.utc_qzs)); + memset(nav_.utc_cmp, 0, sizeof(nav_.utc_cmp)); + memset(nav_.utc_irn, 0, sizeof(nav_.utc_irn)); + memset(nav_.utc_sbs, 0, sizeof(nav_.utc_sbs)); + + memset(nav_.ion_gps, 0, sizeof(nav_.ion_gps)); + memset(nav_.ion_gal, 0, sizeof(nav_.ion_gal)); + memset(nav_.ion_qzs, 0, sizeof(nav_.ion_qzs)); + memset(nav_.ion_cmp, 0, sizeof(nav_.ion_cmp)); + memset(nav_.ion_irn, 0, sizeof(nav_.ion_irn)); + + memset(nav_.cbias, 0, sizeof(nav_.cbias)); + memset(nav_.rbias, 0, sizeof(nav_.rbias)); + memset(nav_.wlbias, 0, sizeof(nav_.wlbias)); + memset(nav_.glo_cpbias, 0, sizeof(nav_.glo_cpbias)); + memset(nav_.glo_fcn, 0, sizeof(nav_.glo_fcn)); + + nav_.leaps = 0; + sbssat_t sbssat_zero = { 0 }; + for (auto&& sat : sbssat_zero.sat) + sat.lcorr.iode = -1; + nav_.sbssat = sbssat_zero; +} + +void Navigation::loadFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt, const char* opt) +{ + auto stat = readrnxt(rnx_file.c_str(), 1, t_start, t_end, dt, opt, NULL, &nav_, NULL); + if (stat == 1) + { + std::cout << "Navigation file loaded." << std::endl; + std::cout << "GPS satellites in navigation file: " << nav_.n << std::endl; + std::cout << "GLONASS satellites in navigation file: " << nav_.ng << std::endl; + std::cout << "SBAS satellites in navigation file: " << nav_.ns << std::endl; + std::cout << "Almanac satellites in navigation file: " << nav_.na << std::endl; + uniqueNavigation(); + } + else + std::cout << "Navigation: couldn't load provided observation file, reason: " << (stat == 0 ? "no data" : "error") + << std::endl; +} + +void Navigation::addSbasMessage(const sbsmsg_t& sbas_msg) +{ + // init seph with array of size NSATSBS*2 + if (nav_.seph == NULL) + { + //std::cout << "Navigation::addSbasMessage: initializing sbas ephemerides array...\n"; + if (!(nav_.seph =(seph_t *)malloc(sizeof(seph_t)*NSATSBS*2))) + { + std::cout << "Navigation::addSbasMessage: could not malloc sbas ephemerides when initializing sbas ephemerides array!\n"; + return; + } + nav_.ns=NSATSBS*2; + nav_.nsmax=NSATSBS*2; + for (auto i=0; i<NSATSBS*2; i++) + nav_.seph[i]=seph_t{0}; + } + sbsupdatecorr(&sbas_msg, &nav_); +} + +void Navigation::copyAllArrays(const nav_t& nav) +{ + // GPS/QZS/GAL ephemeris + copyArray<eph_t>(nav.eph, nav.n, nav_.eph, nav_.n, nav_.nmax); + // GLONASS ephemeris + copyArray<geph_t>(nav.geph, nav.ng, nav_.geph, nav_.ng, nav_.ngmax); + // SBAS ephemeris + copyArray<seph_t>(nav.seph, nav.ns, nav_.seph, nav_.ns, nav_.nsmax); + // precise ephemeris + copyArray<peph_t>(nav.peph, nav.ne, nav_.peph, nav_.ne, nav_.nemax); + // precise clock + copyArray<pclk_t>(nav.pclk, nav.nc, nav_.pclk, nav_.nc, nav_.ncmax); + // almanac data + copyArray<alm_t>(nav.alm, nav.na, nav_.alm, nav_.na, nav_.namax); + // tec grid data + copyArray<tec_t>(nav.tec, nav.nt, nav_.tec, nav_.nt, nav_.ntmax); + // satellite fcb data + copyArray<fcbd_t>(nav.fcb, nav.nf, nav_.fcb, nav_.nf, nav_.nfmax); + + uniqueNavigation(); +} + +void Navigation::copyEphemeris(const nav_t& nav) +{ + // Not SBAS ephemeris! + + // copy + copyArray<eph_t>(nav.eph, nav.n, nav_.eph, nav_.n, nav_.nmax); + copyArray<geph_t>(nav.geph, nav.ng, nav_.geph, nav_.ng, nav_.ngmax); + + // unique + uniqeph (&nav_); + uniqgeph(&nav_); + + // wavelengths + updatenav(&nav_); +} + +void Navigation::copyAlmanac(const nav_t& nav) +{ + copyArray<alm_t>(nav.alm, nav.na, nav_.alm, nav_.na, nav_.namax); +} + +void Navigation::copyIonUtc(const nav_t& nav) +{ + std::copy(nav.utc_gps, nav.utc_gps + ARRAY_SIZE(nav.utc_gps), nav_.utc_gps); + std::copy(nav.utc_gal, nav.utc_gal + ARRAY_SIZE(nav.utc_gal), nav_.utc_gal); + std::copy(nav.utc_qzs, nav.utc_qzs + ARRAY_SIZE(nav.utc_qzs), nav_.utc_qzs); + std::copy(nav.utc_glo, nav.utc_glo + ARRAY_SIZE(nav.utc_glo), nav_.utc_glo); + std::copy(nav.utc_cmp, nav.utc_cmp + ARRAY_SIZE(nav.utc_cmp), nav_.utc_cmp); + std::copy(nav.utc_irn, nav.utc_irn + ARRAY_SIZE(nav.utc_irn), nav_.utc_irn); + std::copy(nav.utc_sbs, nav.utc_sbs + ARRAY_SIZE(nav.utc_sbs), nav_.utc_sbs); + + std::copy(nav.ion_gps, nav.ion_gps + ARRAY_SIZE(nav.ion_gps), nav_.ion_gps); + std::copy(nav.ion_gal, nav.ion_gal + ARRAY_SIZE(nav.ion_gal), nav_.ion_gal); + std::copy(nav.ion_qzs, nav.ion_qzs + ARRAY_SIZE(nav.ion_qzs), nav_.ion_qzs); + std::copy(nav.ion_cmp, nav.ion_cmp + ARRAY_SIZE(nav.ion_cmp), nav_.ion_cmp); + std::copy(nav.ion_irn, nav.ion_irn + ARRAY_SIZE(nav.ion_irn), nav_.ion_irn); + + nav_.leaps = nav.leaps; +} + +void Navigation::copySbasCorrections(const nav_t& nav) +{ + nav_.sbssat.iodp = nav.sbssat.iodp; + nav_.sbssat.nsat = nav.sbssat.nsat; + assert(nav.sbssat.nsat < MAXSAT); + nav_.sbssat.tlat = nav.sbssat.tlat; + for (int i = 0; i < MAXSAT; i++) + { + nav_.sbssat.sat[i].sat = nav.sbssat.sat[i].sat; + nav_.sbssat.sat[i].fcorr = nav.sbssat.sat[i].fcorr; + nav_.sbssat.sat[i].lcorr.daf0 = nav.sbssat.sat[i].lcorr.daf0; + nav_.sbssat.sat[i].lcorr.daf1 = nav.sbssat.sat[i].lcorr.daf1; + nav_.sbssat.sat[i].lcorr.iode = nav.sbssat.sat[i].lcorr.iode; + nav_.sbssat.sat[i].lcorr.t0 = nav.sbssat.sat[i].lcorr.t0; + std::copy(nav.sbssat.sat[i].lcorr.dpos, nav.sbssat.sat[i].lcorr.dpos + 3, nav_.sbssat.sat[i].lcorr.dpos); + std::copy(nav.sbssat.sat[i].lcorr.dvel, nav.sbssat.sat[i].lcorr.dvel + 3, nav_.sbssat.sat[i].lcorr.dvel); + } + for (int i = 0; i < MAXBAND + 1; i++) + { + nav_.sbsion[i].iodi = nav.sbsion[i].iodi; + nav_.sbsion[i].nigp = nav.sbsion[i].nigp; + assert(nav.sbsion[i].nigp <= MAXNIGP); + std::copy(nav.sbsion[i].igp, nav.sbsion[i].igp + MAXNIGP, nav_.sbsion[i].igp); + } + for (int i = 0; i < MAXSAT; i++) + { + std::copy(nav.pcvs[i].code, nav.pcvs[i].code + ARRAY_SIZE(nav.pcvs[i].code), nav_.pcvs[i].code); + std::copy(&nav.pcvs[i].off[0][0], &nav.pcvs[i].off[0][0] + ARRAY2D_SIZE(nav.pcvs[i].off), &nav_.pcvs[i].off[0][0]); + nav_.pcvs[i].sat = nav.pcvs[i].sat; + nav_.pcvs[i].te = nav.pcvs[i].te; + nav_.pcvs[i].ts = nav.pcvs[i].ts; + // std::cout << "copySbasCorrections : " << nav.pcvs[i].te.time << " + " << nav.pcvs[i].te.sec << "\n"; + // std::cout << "copySbasCorrections : " << nav_.pcvs[i].te.time << " + " << nav_.pcvs[i].te.sec << "\n"; + std::copy(nav.pcvs[i].type, nav.pcvs[i].type + ARRAY_SIZE(nav.pcvs[i].type), nav_.pcvs[i].type); + std::copy(&nav.pcvs[i].var[0][0], &nav.pcvs[i].var[0][0] + ARRAY2D_SIZE(nav.pcvs[i].var), &nav_.pcvs[i].var[0][0]); + } +} + +void Navigation::copyBias(const nav_t& nav) +{ + std::copy(&nav.cbias[0][0], &nav.cbias[0][0] + ARRAY2D_SIZE(nav.cbias), &nav_.cbias[0][0]); + std::copy(&nav.rbias[0][0][0], &nav.rbias[0][0][0] + ARRAY3D_SIZE(nav.rbias), &nav_.rbias[0][0][0]); + + std::copy(nav.wlbias, nav.wlbias + ARRAY_SIZE(nav.wlbias), nav_.wlbias); + std::copy(nav.glo_cpbias, nav.glo_cpbias + ARRAY_SIZE(nav.glo_cpbias), nav_.glo_cpbias); + std::copy(nav.glo_fcn, nav.glo_fcn + ARRAY_SIZE(nav.glo_fcn), nav_.glo_fcn); +} + +void Navigation::freeNavigationArrays() +{ + // RTKLIB "freenav(&nav_,255)" doesn't check if is NULL before freeing + freeEphemeris(); + // std::cout << "freeing glonass ephemeris...\n"; + freeGlonassEphemeris(); + // std::cout << "freeing sbas ephemeris...\n"; + freeSbasEphemeris(); + // std::cout << "freeing precise ephemeris...\n"; + freePreciseEphemeris(); + // std::cout << "freeing precise clock...\n"; + freePreciseClock(); + // std::cout << "freeing almanac...\n"; + freeAlmanac(); + // std::cout << "freeing tec...\n"; + freeTecData(); + // std::cout << "freeing fcb...\n"; + freeFcbData(); +} + +void Navigation::print() +{ + std::cout << "n: " << nav_.n << "\t(nmax: " << nav_.nmax << ")\n"; + std::cout << "ng: " << nav_.ng << "\t(nfmax: " << nav_.nfmax << ")\n"; + std::cout << "ns: " << nav_.ns << "\t(nsmax: " << nav_.nsmax << ")\n"; + std::cout << "ne: " << nav_.ne << "\t(nemax: " << nav_.nemax << ")\n"; + std::cout << "nc: " << nav_.nc << "\t(ncmax: " << nav_.ncmax << ")\n"; + std::cout << "na: " << nav_.na << "\t(namax: " << nav_.namax << ")\n"; + std::cout << "nt: " << nav_.nt << "\t(ntmax: " << nav_.ntmax << ")\n"; + std::cout << "nf: " << nav_.nf << "\t(nfmax: " << nav_.nfmax << ")\n"; + + // TODO + // eph_t *eph; /* GPS/QZS/GAL ephemeris */ + // geph_t *geph; /* GLONASS ephemeris */ + // seph_t *seph; /* SBAS ephemeris */ + // peph_t *peph; /* precise ephemeris */ + // pclk_t *pclk; /* precise clock */ + // alm_t *alm; /* almanac data */ + // tec_t *tec; /* tec grid data */ + // fcbd_t *fcb; /* satellite fcb data */ + // double lam[MAXSAT][NFREQ]; /* carrier wave lengths (m) */ + + printArray<double, ARRAY_SIZE(nav_.utc_gps)>("utc_gps: ", nav_.utc_gps); + printArray<double, ARRAY_SIZE(nav_.utc_glo)>("utc_glo: ", nav_.utc_glo); + printArray<double, ARRAY_SIZE(nav_.utc_gal)>("utc_gal: ", nav_.utc_gal); + printArray<double, ARRAY_SIZE(nav_.utc_qzs)>("utc_qzs: ", nav_.utc_qzs); + printArray<double, ARRAY_SIZE(nav_.utc_cmp)>("utc_cmp: ", nav_.utc_cmp); + printArray<double, ARRAY_SIZE(nav_.utc_irn)>("utc_irn: ", nav_.utc_irn); + printArray<double, ARRAY_SIZE(nav_.utc_sbs)>("utc_sbs: ", nav_.utc_sbs); + printArray<double, ARRAY_SIZE(nav_.ion_gps)>("ion_gps: ", nav_.ion_gps); + printArray<double, ARRAY_SIZE(nav_.ion_gal)>("ion_gal: ", nav_.ion_gal); + printArray<double, ARRAY_SIZE(nav_.ion_qzs)>("ion_qzs: ", nav_.ion_qzs); + printArray<double, ARRAY_SIZE(nav_.ion_cmp)>("ion_cmp: ", nav_.ion_cmp); + printArray<double, ARRAY_SIZE(nav_.ion_irn)>("ion_irn: ", nav_.ion_irn); + + std::cout << "leaps: " << nav_.leaps << "\n"; + + std::cout << "lam: \n"; + for (int i = 0; i < ARRAY2D_NROWS(nav_.lam); i++) + printArray<double, ARRAY2D_NCOLS(nav_.lam)>("\t", nav_.lam[i]); + + std::cout << "cbias: \n"; + for (int i = 0; i < ARRAY2D_NROWS(nav_.cbias); i++) + printArray<double, ARRAY2D_NCOLS(nav_.cbias)>("\t", nav_.cbias[i]); + + printArray<double, ARRAY_SIZE(nav_.wlbias)> ("wlbias: ", nav_.wlbias); + printArray<double, ARRAY_SIZE(nav_.glo_cpbias)>("glo_cpbias: ", nav_.glo_cpbias); + printArray<char, ARRAY_SIZE(nav_.glo_fcn)> ("glo_fcn: ", nav_.glo_fcn); + + std::cout << "pcvs: \n"; + for (int i = 0; i < ARRAY_SIZE(nav_.pcvs); i++) + { + std::cout << "\tsat: " << nav_.pcvs[i].sat << "\n"; + printArray<char, ARRAY_SIZE(nav_.pcvs[i].type)>("\t\ttype: ", nav_.pcvs[i].type); + printArray<char, ARRAY_SIZE(nav_.pcvs[i].code)>("\t\tcode: ", nav_.pcvs[i].code); + + std::cout << "\t\tte: " << nav_.pcvs[i].te.time << " + "<< nav_.pcvs[i].te.sec << "\n"; + std::cout << "\t\tts: " << nav_.pcvs[i].ts.time << " + "<< nav_.pcvs[i].ts.sec << "\n"; + std::cout << "\t\toff: \n"; + for (int j = 0; j < ARRAY2D_NROWS(nav_.pcvs[i].off); j++) + printArray<double, ARRAY2D_NCOLS(nav_.pcvs[i].off)>("\t\t\t", nav_.pcvs[i].off[j]); + std::cout << "\t\tvar: \n"; + for (int j = 0; j < ARRAY2D_NROWS(nav_.pcvs[i].var); j++) + printArray<double, ARRAY2D_NCOLS(nav_.pcvs[i].var)>("\t\t\t", nav_.pcvs[i].var[j]); + } + + if (nav_.sbssat.nsat == 0) + std::cout << "sbssat: empty\n"; + else + { + std::cout << "sbssat: \n"; + std::cout << "\tiodp: " << nav_.sbssat.iodp << "\n"; + std::cout << "\tnsat: " << nav_.sbssat.nsat << "\n"; + std::cout << "\ttlat: " << nav_.sbssat.tlat << "\n"; + for (int i = 0; i < nav_.sbssat.nsat; i++) + { + std::cout << "\tsat: " << i << "\n"; + std::cout << "\t\tsat " << nav_.sbssat.sat[i].sat << "\n"; + + std::cout << "\t\tfcorr.t0 " << nav_.sbssat.sat[i].fcorr.t0.time << " + "<< nav_.sbssat.sat[i].fcorr.t0.sec << "\n"; + std::cout << "\t\tfcorr.prc " << nav_.sbssat.sat[i].fcorr.prc << "\n"; + std::cout << "\t\tfcorr.rrc " << nav_.sbssat.sat[i].fcorr.rrc << "\n"; + std::cout << "\t\tfcorr.dt " << nav_.sbssat.sat[i].fcorr.dt << "\n"; + std::cout << "\t\tfcorr.iodf " << nav_.sbssat.sat[i].fcorr.iodf << "\n"; + std::cout << "\t\tfcorr.udre " << nav_.sbssat.sat[i].fcorr.udre << "\n"; + std::cout << "\t\tfcorr.ai " << nav_.sbssat.sat[i].fcorr.ai << "\n"; + + std::cout << "\t\tlcorr.daf0 " << nav_.sbssat.sat[i].lcorr.daf0 << "\n"; + std::cout << "\t\tlcorr.daf1 " << nav_.sbssat.sat[i].lcorr.daf1 << "\n"; + std::cout << "\t\tlcorr.iode " << nav_.sbssat.sat[i].lcorr.iode << "\n"; + std::cout << "\t\tlcorr.t0 " << nav_.sbssat.sat[i].lcorr.t0.time << " + "<< nav_.sbssat.sat[i].lcorr.t0.sec << "\n"; + printArray<double, ARRAY_SIZE(nav_.sbssat.sat[i].lcorr.dpos)>("\t\tlcorr.dpos: ", nav_.sbssat.sat[i].lcorr.dpos); + printArray<double, ARRAY_SIZE(nav_.sbssat.sat[i].lcorr.dvel)>("\t\tlcorr.dvel: ", nav_.sbssat.sat[i].lcorr.dvel); + } + } + for (int i = 0; i < MAXBAND + 1; i++) + { + if (nav_.sbsion[i].nigp == 0) + std::cout << "sbsion: " << i << " empty\n"; + else + { + std::cout << "sbsion: " << i << "\n"; + std::cout << "\tiodi: " << nav_.sbsion[i].iodi << "\n"; + std::cout << "\tnigp: " << nav_.sbsion[i].nigp << "\n"; + for (int j = 0; j < nav_.sbsion[i].nigp; j++) + { + std::cout << "\tigp: " << j << "\n"; + std::cout << "\t\tt0: " << nav_.sbsion[i].igp[j].t0.time << " + "<< nav_.sbsion[i].igp[j].t0.sec << "\n"; + std::cout << "\t\tlat: " << nav_.sbsion[i].igp[j].lat << "\n"; + std::cout << "\t\tlon: " << nav_.sbsion[i].igp[j].lon << "\n"; + std::cout << "\t\tgive: " << nav_.sbsion[i].igp[j].give << "\n"; + std::cout << "\t\tdelay: " << nav_.sbsion[i].igp[j].delay << "\n"; + } + } + } + + // TODO: + // double rbias[MAXRCV][2][3]; /* receiver dcb (0:p1-p2,1:p1-c1,2:p2-c2) (m) */ + + // ********** other not copied nav_t content: *********** + // erp_t erp; /* earth rotation parameters */ + // dgps_t dgps[MAXSAT]; /* DGPS corrections */ + // ssr_t ssr[MAXSAT]; /* SSR corrections */ + // lexeph_t lexeph[MAXSAT]; /* LEX ephemeris */ + // lexion_t lexion; /* LEX ionosphere correction */ + // pppcorr_t pppcorr; /* ppp corrections */ +} diff --git a/src/observations.cpp b/src/observations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4df0dbbd1ffafa49cdcd5a5868854426583b60c1 --- /dev/null +++ b/src/observations.cpp @@ -0,0 +1,536 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" +#include "gnss_utils/utils/satellite.h" + +using namespace GnssUtils; + +Observations::Observations() +{ +} + +Observations::Observations(const Observations& obs) + : sat_2_idx_(obs.sat_2_idx_), idx_2_sat_(obs.idx_2_sat_), obs_(obs.obs_.size()) +{ + // copy all elements + for (auto i = 0; i < obs.obs_.size(); i++) + { + obs_[i].time = obs.obs_[i].time; + obs_[i].eventime = obs.obs_[i].eventime; + obs_[i].timevalid = obs.obs_[i].timevalid; + obs_[i].sat = obs.obs_[i].sat; + obs_[i].rcv = obs.obs_[i].rcv; + std::copy(obs.obs_[i].SNR, obs.obs_[i].SNR + NFREQ + NEXOBS, obs_[i].SNR); + std::copy(obs.obs_[i].LLI, obs.obs_[i].LLI + NFREQ + NEXOBS, obs_[i].LLI); + std::copy(obs.obs_[i].code, obs.obs_[i].code + NFREQ + NEXOBS, obs_[i].code); + std::copy(obs.obs_[i].qualL, obs.obs_[i].qualL + NFREQ + NEXOBS, obs_[i].qualL); + std::copy(obs.obs_[i].qualP, obs.obs_[i].qualP + NFREQ + NEXOBS, obs_[i].qualP); + obs_[i].freq = obs.obs_[i].freq; + std::copy(obs.obs_[i].L, obs.obs_[i].L + NFREQ + NEXOBS, obs_[i].L); + std::copy(obs.obs_[i].P, obs.obs_[i].P + NFREQ + NEXOBS, obs_[i].P); + std::copy(obs.obs_[i].D, obs.obs_[i].D + NFREQ + NEXOBS, obs_[i].D); + } +} + +Observations::~Observations() +{ +} + +void Observations::clearObservations() +{ + this->obs_.clear(); + this->idx_2_sat_.clear(); + this->sat_2_idx_.clear(); +} + +void Observations::addObservation(const obsd_t& obs) +{ + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); + + // copy obsd_t object + obsd_t copy_obs; + copy_obs.time = obs.time; + copy_obs.eventime = obs.eventime; + copy_obs.timevalid = obs.timevalid; + copy_obs.sat = obs.sat; + copy_obs.rcv = obs.rcv; + std::copy(obs.SNR, obs.SNR + NFREQ + NEXOBS, copy_obs.SNR); + std::copy(obs.LLI, obs.LLI + NFREQ + NEXOBS, copy_obs.LLI); + std::copy(obs.code, obs.code + NFREQ + NEXOBS, copy_obs.code); + std::copy(obs.qualL, obs.qualL + NFREQ + NEXOBS, copy_obs.qualL); + std::copy(obs.qualP, obs.qualP + NFREQ + NEXOBS, copy_obs.qualP); + copy_obs.freq = obs.freq; + std::copy(obs.L, obs.L + NFREQ + NEXOBS, copy_obs.L); + std::copy(obs.P, obs.P + NFREQ + NEXOBS, copy_obs.P); + std::copy(obs.D, obs.D + NFREQ + NEXOBS, copy_obs.D); + + assert(!hasSatellite(copy_obs.sat) && "adding an observation of a satellite already stored!"); + this->obs_.push_back(copy_obs); + idx_2_sat_.push_back(copy_obs.sat); + sat_2_idx_[copy_obs.sat] = obs_.size() - 1; + + // std::cout << "added observation of sat " << (int)obs.sat << " stored in idx " << sat_2_idx_[obs.sat] << std::endl; + + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); +} + +void Observations::removeObservationByIdx(const int& _idx) +{ + // std::cout << "removing observation of idx " << _idx << std::endl; + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); + assert(obs_.size() > _idx); + assert(idx_2_sat_.size() > _idx); + assert(sat_2_idx_.count(idx_2_sat_.at(_idx)) != 0); + + // remove obs from sat_2_idx map + sat_2_idx_.erase(idx_2_sat_.at(_idx)); + // decrease the idx of the observations after the removed one + for (auto&& sat_idx : sat_2_idx_) + if (sat_idx.second > _idx) + sat_idx.second--; + // remove obs from obs and idx_2_sat vectors + obs_.erase(obs_.begin() + _idx); + idx_2_sat_.erase(idx_2_sat_.begin() + _idx); + + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); +} + +void Observations::removeObservationBySat(const int& _sat) +{ + // std::cout << "removing observation of sat " << _sat << std::endl; + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); + assert(sat_2_idx_.count(_sat) != 0); + assert(hasSatellite(_sat)); + assert(obs_.size() > sat_2_idx_.at(_sat)); + assert(idx_2_sat_.size() > sat_2_idx_.at(_sat)); + + int idx = sat_2_idx_.at(_sat); + // remove obs from sat_2_idx map + sat_2_idx_.erase(_sat); + // decrease the idx of the observations after the removed one + for (auto&& sat_idx : sat_2_idx_) + if (sat_idx.second > idx) + sat_idx.second--; + // remove obs from obs and idx_2_sat vectors + obs_.erase(obs_.begin() + idx); + idx_2_sat_.erase(idx_2_sat_.begin() + idx); + + assert(sat_2_idx_.size() == idx_2_sat_.size()); + assert(sat_2_idx_.size() == obs_.size()); +} + +void Observations::print(obsd_t& _obs) +{ + std::string msg = "Observation of satellite #" + std::to_string(_obs.sat); + GnssUtils::print(msg); + std::cout << "\tTime [s]: " << _obs.time.time << " + " << _obs.time.sec << "\n"; + std::cout << "\tTime valid: " << _obs.timevalid << std::endl; + printArray<unsigned char, ARRAY_SIZE(_obs.SNR)>("\tSNR: ", _obs.SNR); + printArray<unsigned char, ARRAY_SIZE(_obs.LLI)>("\tLLI: ", _obs.LLI); + printArray<unsigned char, ARRAY_SIZE(_obs.code)>("\tcode: ", _obs.code); + printArray<unsigned char, ARRAY_SIZE(_obs.qualL)>("\tqualL: ", _obs.qualL); + printArray<unsigned char, ARRAY_SIZE(_obs.qualP)>("\tqualP: ", _obs.qualP); + printf("\tFreq. channel: %uc \n", (int)_obs.freq); + printArray<double, ARRAY_SIZE(_obs.L)>("\tL: ", _obs.L); + printArray<double, ARRAY_SIZE(_obs.P)>("\tP: ", _obs.P); + printArray<float, ARRAY_SIZE(_obs.D)>("\tD: ", _obs.D); +} + +void Observations::printBySat(const int& _sat_number) +{ + print(getObservationBySat(_sat_number)); +} + +void Observations::printByIdx(const int& _idx) +{ + print(getObservationByIdx(_idx)); +} + +void Observations::print() +{ + for (auto obs : obs_) + { + print(obs); + } +} + +std::set<int> Observations::filterByEphemeris(const Satellites& sats) +{ + std::set<int> remove_sats; + + for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + { + auto&& obs_sat = getObservationByIdx(obs_i); + const int& sat_number = obs_sat.sat; + + // inexistent satellite (satellite is not included in the discarded list) + if (sats.count(sat_number) == 0) + { + //std::cout << "Discarding sat " << sat_number << ": no information available" << std::endl; + remove_sats.insert(sat_number); + } + // bad satellite position (satellite is not included in the discarded list) + if (sats.at(sat_number).pos.isApprox(Eigen::Vector3d::Zero(), 1e-3) or + sats.at(sat_number).pos.norm() < 2.5e7) + { + //std::cout << "Discarding sat " << sat_number << ": wrong satellite position: " << sats.at(sat_number).pos.transpose() << std::endl; + remove_sats.insert(sat_number); + } + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filterBySatellites(const std::set<int>& discarded_sats) +{ + std::set<int> remove_sats; + + for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + { + auto&& obs_sat = getObservationByIdx(obs_i); + const int& sat_number = obs_sat.sat; + + // discard sats + if (discarded_sats.count(sat_number) != 0) + remove_sats.insert(sat_number); + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filterByCode() +{ + std::set<int> remove_sats; + + for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + { + auto&& obs_sat = getObservationByIdx(obs_i); + const int& sat_number = obs_sat.sat; + + // wrong data + if (std::abs(obs_sat.P[0]) < 1e-12) + { + //std::cout << "Discarding sat " << sat_number << ": wrong code data: " << obs_sat.P[0] << std::endl; + remove_sats.insert(sat_number); + } + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filterByCarrierPhase() +{ + std::set<int> remove_sats; + + for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + { + auto&& obs_sat = getObservationByIdx(obs_i); + const int& sat_number = obs_sat.sat; + + // wrong data + if (std::abs(obs_sat.L[0]) < 1e-12) + { + //std::cout << "Discarding sat " << sat_number << ": wrong carrier phase data: " << obs_sat.L[0] << std::endl; + remove_sats.insert(sat_number); + } + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filterByConstellations(const int& navsys) +{ + std::set<int> remove_sats; + + for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + { + auto&& obs_sat = getObservationByIdx(obs_i); + const int& sat_number = obs_sat.sat; + + // constellation + int sys = satsys(obs_sat.sat, NULL); + if (!(sys & navsys)) + { + //std::cout << "Discarding sat " << sat_number << ": not selected constellation " << sys << " - mask: " << navsys << std::endl; + remove_sats.insert(sat_number); + continue; + } + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filterByElevationSnr(const Azels& azels, + const snrmask_t& snrmask, + const double& elmin, + const bool &multi_freq) +{ + std::set<int> remove_sats; + + for (auto&& sat_idx_pair : sat_2_idx_) + { + const int& sat_number = sat_idx_pair.first; + assert(azels.count(sat_number) && "azel missing for a satellite of this observation"); + const double& elevation(azels.at(sat_number)(1)); + auto&& obs_sat = getObservationByIdx(sat_idx_pair.second); + + // check elevation + if (elevation < elmin) + { + //std::cout << "Discarding sat " << sat_number << ": low elevation " << elevation << " - min: " << elmin << std::endl; + remove_sats.insert(sat_number); + continue; + } + + // snr L1 + if (testsnr(0, 0, elevation, obs_sat.SNR[0] * 0.25, &snrmask) == 1) + { + //std::cout << "Discarding sat " << sat_number << ": snr test L1" << std::endl; + remove_sats.insert(sat_number); + } + if (multi_freq) + { + int L25 = (satsys(sat_number,NULL)&(SYS_GAL|SYS_SBS) ? 2 : 1); + //srn L2/L + if (testsnr(0, L25, elevation, obs_sat.SNR[L25] * 0.25, &snrmask) == 1) + { + //std::cout << "Discarding sat " << sat_number << ": snr test L2/L5" << std::endl; + remove_sats.insert(sat_number); + } + } + } + + // remove sats + // std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl; + for (auto sat : remove_sats) + { + assert(hasSatellite(sat)); + removeObservationBySat(sat); + } + + return remove_sats; +} + +std::set<int> Observations::filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt) +{ + return filter(sats, + discarded_sats, + x_r, + check_code, + check_carrier_phase, + opt.getNavSys(), + opt.getSnrMask(), + opt.elmin, + opt.ionoopt == IONOOPT_IFLC); +} + +std::set<int> Observations::filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Eigen::Vector3d& x_r, + const bool& check_code, + const bool& check_carrier_phase, + const int& navsys, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq) +{ + assert(!x_r.isApprox(Eigen::Vector3d::Zero(), 1e-3)); + + Azels azels = computeAzels(sats, x_r); + + return filter(sats, + discarded_sats, + azels, + check_code, + check_carrier_phase, + navsys, + snrmask, + elmin, + multi_freq); +} + +std::set<int> Observations::filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const Options& opt) +{ + return filter(sats, + discarded_sats, + azels, + check_code, + check_carrier_phase, + opt.getNavSys(), + opt.getSnrMask(), + opt.elmin, + opt.ionoopt == IONOOPT_IFLC); +} + +std::set<int> Observations::filter(const Satellites& sats, + const std::set<int>& discarded_sats, + const Azels& azels, + const bool& check_code, + const bool& check_carrier_phase, + const int& navsys, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq) +{ + //std::cout << "filter: initial size: " << obs_.size() << std::endl; + // Ephemeris + std::set<int> remove_sats = filterByEphemeris(sats); + + // Discarded sats + std::set<int> remove_sats_discarded = filterBySatellites(discarded_sats); + remove_sats.insert(remove_sats_discarded.begin(), remove_sats_discarded.end()); + + // Code + if (check_code) + { + std::set<int> remove_sats_code = filterByCode(); + remove_sats.insert(remove_sats_code.begin(), remove_sats_code.end()); + } + + // Carrier phase + if (check_carrier_phase) + { + std::set<int> remove_sats_carrier = filterByCarrierPhase(); + remove_sats.insert(remove_sats_carrier.begin(), remove_sats_carrier.end()); + } + + // satellite flag and ephemeris variance + for (auto sat_pair : sats) + if (remove_sats.count(sat_pair.first) == 0) + if (satexclude(sat_pair.first,sat_pair.second.var, sat_pair.second.svh, NULL)) + { + //std::cout << "Discarding sat " << sat_pair.first << ": unhealthy or huge variance svh = " << sat_pair.second.svh << std::endl; + removeObservationBySat(sat_pair.first); + remove_sats.insert(sat_pair.first); + } + + // Constellations + std::set<int> remove_sats_constellations = filterByConstellations(navsys); + remove_sats.insert(remove_sats_constellations.begin(), remove_sats_constellations.end()); + + // Elevation and SNR + std::set<int> remove_sats_elevation = filterByElevationSnr(azels, snrmask, elmin, multi_freq); + remove_sats.insert(remove_sats_elevation.begin(), remove_sats_elevation.end()); + + return remove_sats; + // std::cout << "final size: " << obs_.size() << std::endl; +} + +std::set<int> Observations::findCommonObservations(const Observations& obs_1, + const Observations& obs_2) +{ + // std::cout << "obs 1 sats: "; + // for (auto&& obs_1_ref : obs_1.getObservations()) + // std::cout << (int)obs_1_ref.sat << " "; + // std::cout << std::endl; + // std::cout << "obs 2 sats: "; + // for (auto&& obs_2_ref : obs_2.getObservations()) + // std::cout << (int)obs_2_ref.sat << " "; + // std::cout << std::endl; + + std::set<int> common_sats; + + // iterate 1 + for (auto&& obs_1_ref : obs_1.getObservations()) + if (obs_2.hasSatellite(obs_1_ref.sat)) + common_sats.insert(obs_1_ref.sat); + + // std::cout << "common sats: "; + // for (auto sat : common_sats) + // std::cout << sat << " "; + // std::cout << std::endl; + + return common_sats; +} + + +bool Observations::operator ==(const Observations &other_obs) const +{ + + if (sat_2_idx_ != other_obs.sat_2_idx_) + return false; + + if (idx_2_sat_ != other_obs.idx_2_sat_) + return false; + + for (auto i = 0; i < obs_.size(); i++) + if (obs_.at(i) != other_obs.obs_.at(i)) + return false; + + return true; +} diff --git a/src/range.cpp b/src/range.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e52ee8043c0c40408365ca019af2aa296b671d85 --- /dev/null +++ b/src/range.cpp @@ -0,0 +1,219 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * pseudo_range.cpp + * + * Created on: May 28, 2020 + * Author: joanvallve + */ + +#include "gnss_utils/range.h" + +namespace GnssUtils +{ + +Range::Range() +{ + // TODO Auto-generated constructor stub + +} + +Range::~Range() +{ + // TODO Auto-generated destructor stub +} + +Ranges Range::computeRanges(ObservationsPtr obs, + NavigationPtr nav, + const Satellites& sats, + const Azels& azel, + const Eigen::Vector3d& latlonalt, + const Options& opt) +{ + Ranges ranges; + + double dion,dtrp,vmeas,vion,vtrp,P,lam_L1; + prcopt_t prcopt = opt.getPrcopt(); + + //std::cout << "compute pseudo ranges: \n"; + + for (auto i = 0; i<obs->size(); i++) + { + const obsd_t& obs_i(obs->getObservationByIdx(i)); + int sat = obs_i.sat; + const Satellite& sat_i(sats.at(sat)); + assert(azel.count(sat) != 0 && "azimuth and elevation not provided for this satellite"); + double azel_i[2] = {azel.at(sat)(0),azel.at(sat)(1)}; + + //std::cout << "\tsat " << sat << "..."; + + /* ------------------- Pseudo range ------------------- */ + /* psudorange with code bias correction */ + //std::cout << "prange...\n"; + if ((P=prange(&obs_i,&nav->getNavigation(),azel_i,1,&prcopt,&vmeas))==0.0) + { + //std::cout << " error in prange\n"; + continue; + } + + /* ionospheric corrections */ + //std::cout << "iono...\n"; + //std::cout << "\ttime: " << obs_i.time.time << " + " << obs_i.time.sec << "\n"; + //std::cout << "\tnav: \n"; + //nav->print(); + //std::cout << "\tsat: " << sat << "\n"; + //std::cout << "\tlatlonalt: " << latlonalt.transpose() << "\n"; + //std::cout << "\tazel_i: " << azel_i[0] << " " << azel_i[1] << "\n"; + //std::cout << "\topt.ionoopt: " << opt.ionoopt << "\n"; + if (!ionocorr(obs_i.time,&nav->getNavigation(),sat,latlonalt.data(),azel_i,opt.ionoopt,&dion,&vion)) + { + //std::cout << " error in iono\n"; + continue; + } + + /* GPS-L1 -> L1/B1 */ + //std::cout << "iono2...\n"; + if ((lam_L1=nav->getNavigation().lam[sat-1][0])>0.0) + dion*=std::pow(lam_L1/lam_carr[0],2); + + /* tropospheric corrections */ + //std::cout << "tropo...\n"; + if (!tropcorr(obs_i.time,&nav->getNavigation(),latlonalt.data(),azel_i,opt.tropopt,&dtrp,&vtrp)) + { + //std::cout << " error in tropcorr\n"; + continue; + } + + /* Store in PseudoRange struct */ + // initialize with error values + ranges.emplace(sat,Range()); + + //std::cout << "storing\n"; + ranges[sat].sat = sat; + ranges[sat].P = P; + ranges[sat].iono_corr = -dion; + ranges[sat].tropo_corr = -dtrp; + ranges[sat].sat_clock_corr = CLIGHT*sat_i.clock_bias; + + /* pseudorange corrected */ + ranges[sat].P_corrected = ranges[sat].P + + ranges[sat].iono_corr + + ranges[sat].tropo_corr + + ranges[sat].sat_clock_corr; + + /* error variance */ + ranges[sat].P_var = varerr(&prcopt,azel_i[1],prcopt.err[5],sat_i.sys)+sat_i.var+vmeas+vion+vtrp; + + /* ------------------- Carrier phase ------------------- */ + // L1 + ranges[sat].L = obs_i.L[0]*nav->getNavigation().lam[sat-1][0]; + ranges[sat].L_corrected = ranges[sat].L; + + int sys = satsys(sat, NULL); + // L2 (GPS/GLO/QZS) + if (sys & (SYS_GPS | SYS_GLO | SYS_QZS)) + ranges[sat].L2 = obs_i.L[1]*nav->getNavigation().lam[sat-1][1]; + // E5 (GAL) + else if (sys & SYS_GAL) + ranges[sat].L2 = obs_i.L[2]*nav->getNavigation().lam[sat-1][2]; + else + ranges[sat].L2 = 0; + ranges[sat].L2_corrected = ranges[sat].L2; + + /* ionospheric corrections */ + if (opt.carrier_opt.corr_iono) + { + if (opt.ionoopt==IONOOPT_IFLC) + { + // TODO formulation + //ranges[sat].L_corrected = obs_i.L[0]*nav->getNavigation().lam[sat-1][0]; + } + else + { + ranges[sat].L_corrected -= ranges[sat].iono_corr; + ranges[sat].L2_corrected -= ranges[sat].iono_corr; //FIXME: different? + } + } + + /* tropospheric corrections */ + if (opt.carrier_opt.corr_tropo) + { + ranges[sat].L_corrected += ranges[sat].tropo_corr; + ranges[sat].L2_corrected += ranges[sat].tropo_corr; //FIXME: different? + } + + /* sat clock corrections */ + if (opt.carrier_opt.corr_clock) + { + ranges[sat].L_corrected += ranges[sat].sat_clock_corr; + ranges[sat].L2_corrected += ranges[sat].sat_clock_corr; //FIXME: different? + } + + /* carrier phase variance */ + ranges[sat].L_var = 1.0; // FIXME + ranges[sat].L2_var = 1.0; // FIXME + + //std::cout << std::endl + // << "\t\tprange: " << pranges[sat].prange << std::endl + // << "\t\tvar: " << pranges[sat].var << std::endl + // << "\t\tP: " << P << std::endl + // << "\t\tsat_i.clock_bias: " << sat_i.clock_bias << std::endl + // << "\t\tdion: " << dion << std::endl + // << "\t\tdtrp: " << dtrp << std::endl; + } + + return ranges; +} + + + +std::set<int> Range::findCommonSatellites(const Ranges& ranges_1, const Ranges& ranges_2) +{ + std::set<int> common_sats; + + // std::cout << "ranges_1: "; + // for (auto&& range_pair : ranges_1) + // std::cout << range_pair.first << " "; + // std::cout << std::endl; + // std::cout << "ranges_2: "; + // for (auto&& range_pair : ranges_2) + // std::cout << range_pair.first << " "; + // std::cout << std::endl; + + for (auto&& range_pair : ranges_1) + if (ranges_2.count(range_pair.first)) + common_sats.insert(range_pair.first); + + // std::cout << "common sats: "; + // for (auto sat : common_sats) + // std::cout << sat << " "; + // std::cout << std::endl; + + return common_sats; +} + +double computeSagnacCorrection(const Eigen::Vector3d& rcv_ECEF, const Eigen::Vector3d& sat_ECEF) +{ + return OMGE*(sat_ECEF(0)*rcv_ECEF(1)-sat_ECEF(1)*rcv_ECEF(0))/CLIGHT; +} + +} /* namespace GnssUtils */ diff --git a/src/receiver_raw_base.cpp b/src/receiver_raw_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f21b8e6d05b1fd0a68a333aad2a0abe2132d858a --- /dev/null +++ b/src/receiver_raw_base.cpp @@ -0,0 +1,50 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/receiver_raw_base.h" + +namespace GnssUtils +{ +ReceiverRawAbstract::ReceiverRawAbstract() : raw_data_type_(NO) +{ + if (init_raw(&raw_data_, STRFMT_UBX) == 0) + { + assert("Failed when allocating memory for raw_t"); + return; + } +} + +ReceiverRawAbstract::~ReceiverRawAbstract() +{ + free_raw(&raw_data_); +} + +void ReceiverRawAbstract::updateObservations() +{ + // sort and remove duplicated observations + sortobs(&raw_data_.obs); + + obs_.clearObservations(); + for (int ii = 0; ii < raw_data_.obs.n; ++ii) + obs_.addObservation(raw_data_.obs.data[ii]); +} + +} // namespace GnssUtils diff --git a/src/receivers/novatel_raw.cpp b/src/receivers/novatel_raw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa4de46b21b6ed078c9f12a30eb622aced2201ca --- /dev/null +++ b/src/receivers/novatel_raw.cpp @@ -0,0 +1,90 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/receivers/novatel_raw.h" + +using namespace GnssUtils; + +NovatelRaw::NovatelRaw() +{ +} + +NovatelRaw::~NovatelRaw() +{ +} + +RawDataType NovatelRaw::addDataStream(const std::vector<uint8_t>& data_stream) +{ + + for (auto data_byte = data_stream.begin(); data_byte != data_stream.end(); ++data_byte) + { + raw_data_type_ = static_cast<RawDataType> ( input_oem4(&raw_data_, (unsigned char)*data_byte) ); + + switch (raw_data_type_) + { + case NO: // + // std::cout << "0 received!\n"; + break; + + case OBS: // Observations + // std::cout << "Observations Novatel received!\n"; + updateObservations(); + break; + + case NAV_EPH: // Ephemeris + //std::cout << "Ephemeris received!\n"; + nav_.copyEphemeris(raw_data_.nav); + nav_.uniqueNavigation(); + break; + + case NAV_SBAS: // SBAS + //std::cout << "SBAS received!\n"; + nav_.copySbasCorrections(raw_data_.nav); + break; + + case NAV_ALM_IONUTC: // Almanac and ion/utc parameters + //std::cout << "Almanac and ion/utc parameters received!\n"; + nav_.copyIonUtc(raw_data_.nav); + break; + + // Not handled messages + case NAV_ANT: + //std::cout << "NovatelRaw: Received antenna postion parameters. Not handled.\n"; + break; + case NAV_DGPS: + //std::cout << "NovatelRaw: Received dgps correction. Not handled.\n"; + break; + case NAV_SSR: + //std::cout << "NovatelRaw: Received ssr message. Not handled.\n"; + break; + case NAV_LEX: + //std::cout << "NovatelRaw: Received lex message. Not handled.\n"; + break; + case ERROR: + //std::cout << "NovatelRaw: Received error message. Not handled.\n"; + break; + default: + //std::cout << "NovatelRaw: Received unknown message. Not handled.\n"; + break; + } + } + return raw_data_type_; +} diff --git a/src/receivers/ublox_raw.cpp b/src/receivers/ublox_raw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6e03fee2b5d76a9e85172e8dbf403588da79356 --- /dev/null +++ b/src/receivers/ublox_raw.cpp @@ -0,0 +1,94 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/receivers/ublox_raw.h" + +using namespace GnssUtils; + +UBloxRaw::UBloxRaw() +{ +} + +UBloxRaw::~UBloxRaw() +{ +} + +RawDataType UBloxRaw::addDataStream(const std::vector<uint8_t>& data_stream) +{ + // Update type based on RTKLIB + for (auto data_byte = data_stream.begin(); data_byte != data_stream.end(); ++data_byte) + { + raw_data_type_ = static_cast<RawDataType>(input_ubx(&raw_data_, (unsigned char)*data_byte)); + + switch (raw_data_type_) + { + case NO: // + // std::cout << "0 received!\n"; + break; + + case OBS: // Observations + // std::cout << "Observations received!\n"; + updateObservations(); + break; + + case NAV_EPH: // Ephemeris + //std::cout << "Ephemeris received!\n"; + nav_.copyEphemeris(raw_data_.nav); + break; + + case NAV_SBAS: // SBAS + //std::cout << "SBAS received!\n"; + if (raw_data_.sbsmsg.prn == 123 or raw_data_.sbsmsg.prn == 136) // only EGNOS corrections + nav_.addSbasMessage(raw_data_.sbsmsg); + // std::cout << "SBAS added!\n"; + break; + + case NAV_ALM_IONUTC: // Almanac and ion/utc parameters + //std::cout << "Almanac and ion/utc parameters received!\n"; + nav_.freeAlmanac(); + nav_.copyAlmanac(raw_data_.nav); + nav_.copyIonUtc(raw_data_.nav); + break; + + // Not handled messages + case NAV_ANT: + //std::cout << "UBloxRaw: Received antenna postion parameters. Not handled.\n"; + break; + case NAV_DGPS: + //std::cout << "UBloxRaw: Received dgps correction. Not handled.\n"; + break; + case NAV_SSR: + //std::cout << "UBloxRaw: Received ssr message. Not handled.\n"; + break; + case NAV_LEX: + //std::cout << "UBloxRaw: Received lex message. Not handled.\n"; + break; + case ERROR: + //std::cout << "UBloxRaw: Received error message. Not handled.\n"; + break; + default: + //std::cout << "UBloxRaw: Received unknown message. Not handled.\n"; + break; + } + } + + return raw_data_type_; +} diff --git a/src/snapshot.cpp b/src/snapshot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9de0e176dbf69b719617016e4b4c44cb8315eb4f --- /dev/null +++ b/src/snapshot.cpp @@ -0,0 +1,74 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/snapshot.h" +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/utils/transformations.h" + +using namespace GnssUtils; + +Snapshot::Snapshot() + : obs_(nullptr) + , nav_(nullptr) +{ + // +} + +Snapshot::Snapshot(ObservationsPtr obs, NavigationPtr nav) + : obs_(obs) + , nav_(nav) +{ + assert(obs_ != nullptr); + assert(nav_ != nullptr); + // +} + +void Snapshot::loadFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt, const char* opt) +{ + throw std::runtime_error("not implemented!"); +} + +void Snapshot::computeSatellites(const int& eph_opt) +{ + assert(obs_!=nullptr && "null obs"); + assert(nav_!=nullptr && "null nav"); + //assert(!satellitesComputed() && "satellites positions already computed"); + + sats_ = GnssUtils::computeSatellites(*obs_, *nav_, eph_opt); +} + +void Snapshot::computeRanges(const Azels& azel, + const Eigen::Vector3d& latlonalt, + const Options& opt) +{ + //assert(!rangesComputed() && "pseudo ranges already computed!"); + + ranges_ = Range::computeRanges(obs_, nav_, sats_, azel, latlonalt, opt); +} + +void Snapshot::computeRanges(const Eigen::Vector3d& x_ecef, + const Options& opt) +{ + ranges_ = Range::computeRanges(obs_, nav_, sats_, + computeAzels(sats_, x_ecef), + ecefToLatLonAlt(x_ecef), + opt); +} diff --git a/src/tdcp.cpp b/src/tdcp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb4fc40a7908bca237da9a3d5567e5b06a206af3 --- /dev/null +++ b/src/tdcp.cpp @@ -0,0 +1,659 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include <iomanip> +#include "gnss_utils/tdcp.h" +#include "gnss_utils/utils/rcv_position.h" +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/snapshot.h" +#include "gnss_utils/utils/chisquare_ci.h" + +namespace GnssUtils +{ +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpOptions& tdcp_params, + const Options& opt) +{ + auto pos_output = computePos(*snapshot_r->getObservations(), *snapshot_r->getNavigation(), opt); + + if (not pos_output.success) + { + TdcpOutput output; + output.success = false; + output.msg = "Couldn't compute fix for snapshot_r: " + pos_output.msg; + return output; + } + + return Tdcp(snapshot_r, + snapshot_k, + pos_output.pos, + d_0, + discarded_sats, + tdcp_params, + opt); +} +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpOptions& tdcp_params, + const Options& opt) +{ + return Tdcp(snapshot_r, + snapshot_k, + x_r, + d_0, + discarded_sats, + std::set<int>({}), + tdcp_params, + opt); +} + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const std::set<int>& tracked_sats, + const TdcpOptions& tdcp_params, + const Options& opt) +{ + // If use old nav temporary change navigation to (re)compute satellites positions + auto nav_k = snapshot_k->getNavigation(); + if (tdcp_params.use_old_nav) + { + auto new_snapshot_k = std::make_shared<Snapshot>(std::make_shared<Observations>(*snapshot_k->getObservations()), + std::make_shared<Navigation>(*snapshot_r->getNavigation())); + snapshot_k = new_snapshot_k; + } + + // COMPUTE SATELLITES POSITION + if (not snapshot_r->satellitesComputed()) + snapshot_r->computeSatellites(opt.sateph); + if (not snapshot_k->satellitesComputed()) + snapshot_k->computeSatellites(opt.sateph); + + // FILTER SATELLITES (ALREADY DISCARDED, CORRUPTED DATA, WRONG POSITION, CONSTELLATION, ELEVATION and SNR) + snapshot_r->filterObservations(discarded_sats, x_r, false, true, opt); + snapshot_k->filterObservations(discarded_sats, x_r, false, true, opt); + + // COMPUTE RANGES + if (not snapshot_r->rangesComputed()) + snapshot_r->computeRanges(x_r, opt); + if (not snapshot_k->rangesComputed()) + snapshot_k->computeRanges(x_r, opt); // the x_r is only used to compute Azels -> iono&tropo corrections + + // FIND COMMON SATELLITES + std::set<int> common_sats = Range::findCommonSatellites(snapshot_r->getRanges(), + snapshot_k->getRanges()); + + // FILTER OUT SATELLITES NOT IN tracked_sats (if not empty) + if (not tracked_sats.empty()) + { + auto com_sat_it = common_sats.begin(); + while (com_sat_it != common_sats.end()) + if (tracked_sats.count(*com_sat_it) == 0) + com_sat_it = common_sats.erase(com_sat_it); + else + com_sat_it++; + } + + if (common_sats.empty()) + { + TdcpOutput output; + output.success = false; + output.msg = "No common satellites after filtering observations."; + return output; + } + + // COMPUTE TDCP + return Tdcp(snapshot_r, + snapshot_k, + x_r, + common_sats, + d_0, + tdcp_params); +} + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const std::set<int>& common_sats, + const Eigen::Vector4d& d_0, + const TdcpOptions& tdcp_params) +{ + + TdcpOutput output; + + assert(snapshot_r->satellitesComputed() && "this TDCP assumes satellites already computed"); + assert(snapshot_k->satellitesComputed() && "this TDCP assumes satellites already computed"); + assert(snapshot_r->rangesComputed() && "this TDCP assumes ranges already computed"); + assert(snapshot_k->rangesComputed() && "this TDCP assumes ranges already computed"); + assert(snapshot_r->getSatellites().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_k->getSatellites().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_r->getRanges().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_k->getRanges().size() >= common_sats.size() && "Too many common sats"); + for (auto sat : common_sats) + { + assert(snapshot_r->getSatellites().count(sat) && "common sat not stored in satellites"); + assert(snapshot_k->getSatellites().count(sat) && "common sat not stored in satellites"); + assert(snapshot_r->getRanges().count(sat) && "common sat not stored in ranges"); + assert(snapshot_k->getRanges().count(sat) && "common sat not stored in ranges"); + } + + int n_common_sats = common_sats.size(); + if (n_common_sats < std::max(tdcp_params.min_common_sats, 4)) + { + #if GNSS_UTILS_TDCP_DEBUG == 1 + printf("Tdcp: NOT ENOUGH COMMON SATS\n"); + printf("Tdcp: %i common sats available, %i sats are REQUIRED. [ SKIPPING Tdcp ]\n", n_common_sats, std::max(tdcp_params.min_common_sats, 4)); + #endif + output.msg = "Not enough common sats"; + output.success = false; + return output; + } + + // Times + double tr = snapshot_r->getGPST(); + double tk = snapshot_k->getGPST(); + + // MULTI-FREQUENCY + std::map<int, std::pair<int, int>> row_2_sat_freq; + int row = 0; + for (auto sat : common_sats) + { + assert(snapshot_r->getRanges().count(sat)); + assert(snapshot_k->getRanges().count(sat)); + + auto&& range_r = snapshot_r->getRanges().at(sat); + auto&& range_k = snapshot_k->getRanges().at(sat); + + // Carrier phase + // L1/E1 + if (std::abs(range_r.L_corrected) > 1e-12 and std::abs(range_k.L_corrected) > 1e-12) + row_2_sat_freq[row++] = std::make_pair(sat, 0); + + if (!tdcp_params.multi_freq) + continue; + + // L2 (GPS/GLO/QZS) / E5 (GAL) + if (std::abs(range_r.L2_corrected) > 1e-12 and std::abs(range_k.L2_corrected) > 1e-12) + row_2_sat_freq[row++] = std::make_pair(sat, 1); + } + int n_differences = row_2_sat_freq.size(); + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "Receiver at t_r: " << x_r.transpose() << std::endl; + std::cout << "d initial guess: " << d_0.transpose() << std::endl; + std::cout << "common sats: "; + for (auto row_sat_freq_it : row_2_sat_freq) + std::cout << row_sat_freq_it.second.first << " "; + std::cout << std::endl; + #endif + + // FILL SATELLITES POSITIONS AND MEASUREMENTS ======================================================================= + Eigen::MatrixXd A(Eigen::MatrixXd::Zero(n_differences, 4)); + Eigen::VectorXd r(Eigen::VectorXd::Zero(n_differences)); + Eigen::VectorXd drho_m(Eigen::VectorXd::Zero(n_differences)); + Eigen::MatrixXd s_k(Eigen::MatrixXd::Zero(3, n_differences)); + Eigen::MatrixXd s_r(Eigen::MatrixXd::Zero(3, n_differences)); + + for (auto row_sat_freq_it : row_2_sat_freq) + { + const int& row = row_sat_freq_it.first; + const int& sat_number = row_sat_freq_it.second.first; + const int& freq = row_sat_freq_it.second.second; + + auto&& range_r = snapshot_r->getRanges().at(sat_number); + auto&& range_k = snapshot_k->getRanges().at(sat_number); + + // Satellite's positions + s_r.col(row) = snapshot_r->getSatellites().at(sat_number).pos; + s_k.col(row) = snapshot_k->getSatellites().at(sat_number).pos; + + // time differenced carrier phase measurements + if (freq == 0) + drho_m(row) = range_k.L_corrected - range_r.L_corrected; + else + drho_m(row) = range_k.L2_corrected - range_r.L2_corrected; + + // sagnac corrections + if (tdcp_params.sagnac_correction != 0) + { + double sagnac_corr_r = computeSagnacCorrection(x_r,s_r.col(row)); //OMGE*(s_r.col(row)(0)*x_r(1)-s_r.col(row)(1)*x_r(0))/CLIGHT; + double sagnac_corr_k = computeSagnacCorrection(x_r+d_0.head(3),s_k.col(row)); //OMGE*(s_k.col(row)(0)*(x_r(1)+d_0(1))-s_k.col(row)(1)*(x_r(0)+d_0(0)))/CLIGHT; + + drho_m(row) += -sagnac_corr_k + sagnac_corr_r; + } + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "\tsat " << sat_number; + std::cout << std::setprecision(10) + << "\tpositions:" << std::endl + << "\t\ts_r: " << s_r.col(row).transpose() << std::endl + << "\t\ts_k: " << s_k.col(row).transpose() << std::endl; + std::cout << "\trange_r.L: " << (freq == 0 ? range_r.L_corrected : range_r.L2_corrected) << std::endl; + std::cout << "\trange_k.L: " << (freq == 0 ? range_k.L_corrected : range_k.L2_corrected) << std::endl; + std::cout << "\tdrho_m: " << drho_m(row) << std::endl; + #endif + + if (!tdcp_params.relinearize_jacobian) + { + // Unit vectors from receivers to satellites + Eigen::Vector3d u_k = (s_k.col(row) - x_r - d_0.head(3)).normalized(); + + // Fill Jacobian matrix + A(row, 0) = u_k(0); + A(row, 1) = u_k(1); + A(row, 2) = u_k(2); + A(row, 3) = -1.0; + } + } + // Compute TDCP measurement noise variance (proportional to time) + double var_tdcp = (tk - tr) * tdcp_params.sigma_atm * tdcp_params.sigma_atm + + 2 * tdcp_params.sigma_carrier * tdcp_params.sigma_carrier; + + // LEAST SQUARES SOLUTION ======================================================================= + std::set<int> raim_discarded_rows; + output = Tdcp(x_r, A, r, drho_m, s_k, s_r, d_0, var_tdcp, raim_discarded_rows, tdcp_params); + + // FILL OUTPUT + output.used_sats = common_sats; + for (auto disc_row : raim_discarded_rows) + { + output.raim_discarded_sats.insert(row_2_sat_freq[disc_row].first); + output.used_sats.erase(row_2_sat_freq[disc_row].first); + } + output.dt = tk - tr; + + return output; +} + +TdcpOutput Tdcp(const Eigen::Vector3d& x_r, + Eigen::MatrixXd& A, + Eigen::VectorXd& r, + Eigen::VectorXd& drho_m, + const Eigen::MatrixXd& s_k, + const Eigen::MatrixXd& s_r, + const Eigen::Vector4d& d_0, + const double& var_tdcp, + std::set<int>& raim_discarded_rows, + const TdcpOptions& tdcp_params) +{ + assert(A.cols() == 4); + assert(s_k.rows() == 3); + assert(s_r.rows() == 3); + + TdcpOutput output; + Eigen::Vector4d& d(output.d); + Eigen::Matrix4d& cov_d(output.cov_d); + double& residual(output.residual); + double& residual_ci(output.residual_ci); + + const int n_rows = A.rows(); + + // Initial guess + Eigen::Vector4d delta_d(Eigen::Vector4d::Zero()); + d = d_0; + + // initial sagnac correction + Eigen::VectorXd prev_sagnac_corr(s_k.cols()); + if (tdcp_params.sagnac_correction == 2 ) + for (int row = 0; row < n_rows; row++) + { + double sagnac_corr_r = computeSagnacCorrection(x_r,s_r.col(row)); //OMGE*(s_r.col(row)(0)*x_r(1)-s_r.col(row)(1)*x_r(0))/CLIGHT; + double sagnac_corr_k = computeSagnacCorrection(x_r+d_0.head(3),s_k.col(row)); //OMGE*(s_k.col(row)(0)*(x_r(1)+d_0(1))-s_k.col(row)(1)*(x_r(0)+d_0(0)))/CLIGHT; + + prev_sagnac_corr(row) = -sagnac_corr_k + sagnac_corr_r; + } + + for (int j = 0; j < tdcp_params.max_iterations; j++) + { + // fill A and r + for (int row = 0; row < n_rows; row++) + { + // skip discarded rows + if (raim_discarded_rows.count(row) != 0) + continue; + + // update sagnac correction + if (tdcp_params.sagnac_correction == 2 and j > 0) + { + // undo prev_correction + drho_m(row) -= prev_sagnac_corr(row); + + // apply new correction + double sagnac_corr_r = computeSagnacCorrection(x_r,s_r.col(row)); //OMGE*(s_r.col(row)(0)*x_r(1)-s_r.col(row)(1)*x_r(0))/CLIGHT; + double sagnac_corr_k = computeSagnacCorrection(x_r+d.head(3),s_k.col(row)); //OMGE*(s_k.col(row)(0)*(x_r(1)+d_0(1))-s_k.col(row)(1)*(x_r(0)+d_0(0)))/CLIGHT; + drho_m(row) += -sagnac_corr_k + sagnac_corr_r; + + // store correction + prev_sagnac_corr(row) = -sagnac_corr_k + sagnac_corr_r; + } + + // Evaluate r + r(row) = drho_m(row) - (s_k.col(row) - x_r - d.head(3)).norm() + (s_r.col(row) - x_r).norm() - d(3); + + // Evaluate A + if (tdcp_params.relinearize_jacobian) + { + // Unit vectors from rcvrs to sats + Eigen::Vector3d u_k = (s_k.col(row) - x_r - d.head(3)).normalized(); + + // Fill Jacobian matrix + A(row, 0) = u_k(0); + A(row, 1) = u_k(1); + A(row, 2) = u_k(2); + A(row, 3) = -1.0; + } + } + + // Solve + cov_d = (A.transpose() * A).inverse(); + delta_d = -cov_d * A.transpose() * r; + d += delta_d; + + // wrong solution + if (d.array().isNaN().any()) + { + std::cout << "Tdcp: NLS DID NOT WORK. NAN values appeared. ABORTING!!" << std::endl; + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "Displacement vector =" << d.head<3>().transpose() << std::endl; + std::cout << "Displacement update =" << delta_d.head<3>().transpose() << std::endl; + printf("Ref distance = %7.3f m\n", d_0.norm()); + printf("Computed distance = %7.3f m\n", d.head<3>().norm()); + printf("Tdcp: rows = %lu\n", n_rows); + std::cout << "Tdcp: r = " << r.transpose() << std::endl; + std::cout << "Tdcp: drho_m = " << drho_m.transpose() << std::endl; + std::cout << "Tdcp: A = \n" << A << std::endl; + std::cout << "Tdcp: H = \n" << A.transpose() * A << std::endl; + std::cout << "Tdcp: cov_d = \n" << cov_d << std::endl; + printf("Tdcp: dT = %8.3f secs\n", d(3)); + #endif + output.msg = "Na" + "N values in NLS"; + output.success = false; + return output; + } + + // residual + if (tdcp_params.residual_opt == 0) // all problem + residual = (r + A * delta_d).squaredNorm() / var_tdcp; + else if (tdcp_params.residual_opt == 1) // worst sat + residual = (r + A * delta_d).cwiseAbs2().maxCoeff() / var_tdcp; + else + throw std::runtime_error("unknown value for residual_opt"); + // residual = (r + A * delta_d).norm(); + + // residual_ci + residual_ci = chisq2ci(residual, tdcp_params.residual_opt == 0 ? n_rows : 1); + + #if GNSS_UTILS_TDCP_DEBUG == 1 + if (j == 0) + { + std::cout << "\n//////////////// Initial solution at first iteration ////////////////\n"; + std::cout << "Displacement vector =" << d.head<3>().transpose() << std::endl; + std::cout << "Displacement update =" << delta_d.head<3>().transpose() << std::endl; + printf("Ref distance = %7.3f m\n", d_0.norm()); + printf("Computed distance = %7.3f m\n", d.head<3>().norm()); + printf("Tdcp: residual = %13.10f\n", residual); + printf("Tdcp: residual_ci = %13.10f\n", residual_ci); + printf("Tdcp: rows = %lu\n", n_rows); + std::cout << "Tdcp: r = " << r.transpose() << std::endl; + std::cout << "Tdcp: r+A*delta_d = " << (r + A * delta_d).transpose() << std::endl; + std::cout << "Tdcp: drho_m = " << drho_m.transpose() << std::endl; + std::cout << "Tdcp: A = \n" << A << std::endl; + std::cout << "Tdcp: H = \n" << A.transpose() * A << std::endl; + std::cout << "Tdcp: cov_d = \n" << cov_d << std::endl; + printf("Tdcp: dT = %8.3f secs\n", d(3)); + std::cout << "Check RAIM conditions...\n"; + std::cout << "\titeration == 0: " << (j==0 ? "OK\n":"FAILED\n"); + std::cout << "\tn_rows > tdcp_params.min_common_sats: " << (n_rows > tdcp_params.min_common_sats ? "OK\n":"FAILED\n"); + std::cout << "\ttdcp_params.raim_n > 0: " << (tdcp_params.raim_n > 0 ? "OK\n":"FAILED\n"); + std::cout << "\ttdcp_params.max_residual_ci: " << tdcp_params.max_residual_ci; + } + #endif + + // RAIM ====================================== (after first iteration) + if (j == 0 and + n_rows > tdcp_params.min_common_sats and + tdcp_params.raim_n > 0 and + residual_ci > tdcp_params.max_residual_ci) + { + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "//////////////// Performing RAIM ////////////////\n"; + #endif + + int worst_sat_row = -1; + double best_residual = 1e12; + Eigen::Vector4d best_d; + + // remove up to 'raim_n' observations (if enough satellites and residual condition holds) + while (raim_discarded_rows.size() < tdcp_params.raim_n and + n_rows - raim_discarded_rows.size() > tdcp_params.min_common_sats and + residual_ci > tdcp_params.max_residual_ci) + { + auto A_raim = A; + auto r_raim = r; + + // METHOD A: using normalized RMSE residual + if (tdcp_params.residual_opt == 0) + { + // solve removing each row + for (int row_removed = 0; row_removed < n_rows; row_removed++) + { + // skip already discarded rows + if (raim_discarded_rows.count(row_removed) != 0) + continue; + + // remove row + A_raim.row(row_removed) = Eigen::Vector4d::Zero().transpose(); + r_raim(row_removed) = 0; + + // solve + Eigen::Vector4d delta_d_raim = -(A_raim.transpose() * A_raim).inverse() * A_raim.transpose() * r_raim; + Eigen::Vector4d d_raim = d_0 + delta_d_raim; + + // no NaN + if (!d_raim.array().isNaN().any()) + { + // residual + if (tdcp_params.residual_opt == 0) // all problem + residual = (r_raim + A_raim * delta_d_raim).squaredNorm() / var_tdcp; + else if (tdcp_params.residual_opt == 1) // worst sat + residual = (r_raim + A_raim * delta_d_raim).cwiseAbs2().maxCoeff() / var_tdcp; + else + throw std::runtime_error("unknown value for residual_opt"); + // residual = (r_raim + A_raim * delta_d_raim).norm() / (n_rows - 1); + + // residual_ci + residual_ci = chisq2ci(residual, tdcp_params.residual_opt == 0 ? n_rows - raim_discarded_rows.size() : 1); + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "RAIM excluding row " << row_removed;// << std::endl; + //std::cout << "\tDisplacement vector =" << d_raim.head<3>().transpose() << std::endl; + printf("\tresidual = %13.10f\n", residual); + printf("\tresidual_ci = %13.10f\n", residual_ci); + //std::cout << "Tdcp: drho = " << r_raim.transpose() << std::endl; + //std::cout << "Tdcp: A = \n" << A_raim << std::endl; + //std::cout << "Tdcp: H = \n" << A_raim.transpose() * A_raim << std::endl; + //printf("Tdcp: dT = %8.3f secs\n", d_raim(3)); + #endif + + // store if best residual + if (residual < best_residual) + { + worst_sat_row = row_removed; + best_residual = residual; + best_d = d_raim; + } + } + // restore initial A and r + A_raim.row(row_removed) = A.row(row_removed); + r_raim(row_removed) = r(row_removed); + } + } + // METHOD B: using max residual in Mahalanobis distance + else if (tdcp_params.residual_opt == 1) + { + // find index of max residual + (r + A * delta_d).cwiseAbs2().maxCoeff(&worst_sat_row); + + // remove row + A_raim.row(worst_sat_row) = Eigen::Vector4d::Zero().transpose(); + r_raim(worst_sat_row) = 0; + + // solve + Eigen::Vector4d delta_d_raim = -(A_raim.transpose() * A_raim).inverse() * A_raim.transpose() * r_raim; + Eigen::Vector4d d_raim = d_0 + delta_d_raim; + + // no NaN + if (!d_raim.array().isNaN().any()) + { + // residual + if (tdcp_params.residual_opt == 0) // all problem + residual = (r_raim + A_raim * delta_d_raim).squaredNorm() / var_tdcp; + else if (tdcp_params.residual_opt == 1) // worst sat + residual = (r_raim + A_raim * delta_d_raim).cwiseAbs2().maxCoeff() / var_tdcp; + else + throw std::runtime_error("unknown value for residual_opt"); + // residual = (r_raim + A_raim * delta_d_raim).norm() / (n_rows - 1); + + // residual_ci + residual_ci = chisq2ci(residual, tdcp_params.residual_opt == 0 ? n_rows - raim_discarded_rows.size() : 1); + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "RAIM excluding row " << worst_sat_row;// << std::endl; + //std::cout << "\tDisplacement vector =" << d_raim.head<3>().transpose() << std::endl; + printf("\tresidual = %13.10f\n", residual); + printf("\tresidual_ci = %13.10f\n", residual_ci); + //std::cout << "Tdcp: drho = " << r_raim.transpose() << std::endl; + //std::cout << "Tdcp: A = \n" << A_raim << std::endl; + //std::cout << "Tdcp: H = \n" << A_raim.transpose() * A_raim << std::endl; + //printf("Tdcp: dT = %8.3f secs\n", d_raim(3)); + #endif + + // store as best residual + best_residual = residual; + best_d = d_raim; + } + } + else + throw std::runtime_error("unknown value for 'residual_opt'"); + + // No successful RAIM solution + if (worst_sat_row == -1) + { + printf("Tdcp: LS after RAIM DID NOT WORK. NAN values appeared. ABORTING!!\n"); + output.msg = "NaN values in NLS after RAIM"; + output.success = false; + return output; + } + + // store removed sat + raim_discarded_rows.insert(worst_sat_row); + + // remove row (set zero) + A.row(worst_sat_row) = Eigen::Vector4d::Zero().transpose(); + r(worst_sat_row) = 0; + + // Choose the best RAIM solution + d = best_d; + cov_d = (A.transpose() * A).inverse(); + residual= best_residual; + } + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "//////////////// Best solution after RAIM ////////////////" << std::endl; + std::cout << "\tExcluded rows : "; + for (auto excl_row : raim_discarded_rows) + std::cout << excl_row << " "; + std::cout << std::endl; + std::cout << "\tInitial guess = " << d_0.transpose() << " (" << d_0.head<3>().norm() << "m)" << std::endl; + std::cout << "\tSolution = " << d.transpose() << " (" << d.head<3>().norm() << "m)" << std::endl; + std::cout << "\tClock offset = " << d(3) << std::endl; + std::cout << "\tResidual = " << best_residual << std::endl; + std::cout << "\tA = \n" << A << std::endl; + std::cout << "\tH = \n" << A.transpose() * A << std::endl; + std::cout << "\tcov_d = \n" << cov_d << std::endl; + #endif + } + + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "\n//////////////// Final solution at iteration " << j << "////////////////\n"; + std::cout << "\tExcluded rows : "; + for (auto excl_row : raim_discarded_rows) + std::cout << excl_row << " "; + std::cout << std::endl; + std::cout << "\tInitial guess = " << d_0.transpose() << " (" << d_0.head<3>().norm() << "m)" << std::endl; + std::cout << "\tSolution = " << d.transpose() << " (" << d.head<3>().norm() << "m)" << std::endl; + std::cout << "\tClock offset = " << d(3) << std::endl; + std::cout << "\tResidual = " << residual << std::endl; + std::cout << "\tResidual_ci = " << residual_ci << std::endl; + std::cout << "\tA = \n" << A << std::endl; + std::cout << "\tH = \n" << A.transpose() * A << std::endl; + std::cout << "\tcov_d = \n" << cov_d << std::endl; + #endif + } + + // Computing error on measurement space + r.setZero(); + for (int row = 0; row < r.size(); row++) + if (raim_discarded_rows.count(row) == 0) + r(row) = drho_m(row) - (s_k.col(row) - x_r - d.head(3)).norm() + (s_r.col(row) - x_r).norm() - d(3); + // residual + if (tdcp_params.residual_opt == 0) // all problem + residual = r.squaredNorm() / var_tdcp; + else if (tdcp_params.residual_opt == 1) // worst sat + residual = r.cwiseAbs2().maxCoeff() / var_tdcp; + else + throw std::runtime_error("unknown value for residual_opt"); + //residual = sqrt(r.squaredNorm() / (r.size() - raim_discarded_rows.size())); + + // residual_ci + residual_ci = chisq2ci(residual, tdcp_params.residual_opt == 0 ? n_rows - raim_discarded_rows.size() : 1); + + // check residual condition + if (tdcp_params.validate_residual and + residual_ci > tdcp_params.max_residual_ci) + { + printf("Tdcp: Didn't success. Final residual=%f (CI %f) bigger than max_residual_ci=%f.\n", + residual, + residual_ci, + tdcp_params.max_residual_ci); + output.msg = "residual_ci bigger than max_residual_ci"; + output.success = false; + } + else + output.success = true; + + // covariance + cov_d *= var_tdcp; + + return output; +} + + +} // namespace GnssUtils diff --git a/src/utils/rcv_position.cpp b/src/utils/rcv_position.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b354333ac35c49054894c7a31382c91552d1a05c --- /dev/null +++ b/src/utils/rcv_position.cpp @@ -0,0 +1,314 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * transfromation.h + * + * Created on: April 3, 2020 + * Author: Joan Vallvé, Pep MartÃ-Saumell + */ + +#include "gnss_utils/utils/rcv_position.h" + +using namespace GnssUtils; + +namespace GnssUtils +{ +ComputePosOutput computePos(const GnssUtils::Observations& _observations, + const GnssUtils::Navigation& _navigation, + const Options& _opt) +{ + // Convert options to rtklib + prcopt_t prcopt = _opt.getPrcopt(); + + return computePos(_observations, _navigation, prcopt); +} + +ComputePosOutput computePos(const GnssUtils::Observations& _observations, + const GnssUtils::Navigation& _navigation, + const prcopt_t& _prcopt) +{ + // Define error msg + char msg[128] = ""; + + // output data + GnssUtils::ComputePosOutput output; + sol_t sol; + sol = { { 0 } }; + ssat_t sats_status[MAXSAT]; + std::vector<double> sat_elevations(2 * _observations.size()); + + // compute pose + output.success = pntpos(_observations.data(), + _observations.size(), + &(_navigation.getNavigation()), + &_prcopt, + &sol, + sat_elevations.data(), + sats_status, + msg); + + // Fill output struct + output.msg = std::string(msg); + output.time = sol.time.time; + output.sec = sol.time.sec; + output.pos = Eigen::Vector3d(sol.rr); + output.vel = Eigen::Vector3d(&sol.rr[3]); + output.pos_covar << sol.qr[0], sol.qr[3], sol.qr[5], sol.qr[3], sol.qr[1], sol.qr[4], sol.qr[5], sol.qr[4], sol.qr[2]; + // std::cout << "Compute pos: " << output.pos.transpose() << "\n"; + // std::cout << "Covariance:\n" << output.pos_covar << "\n"; + + if (sol.dtr != NULL) + output.rcv_bias = + (Eigen::Matrix<double, 6, 1>() << sol.dtr[0], sol.dtr[1], sol.dtr[2], sol.dtr[3], sol.dtr[4], sol.dtr[5]) + .finished(); + output.type = sol.type; + output.stat = sol.stat; + output.ns = sol.ns; + output.age = sol.age; + output.ratio = sol.ratio; + output.lat_lon = ecefToLatLonAlt(output.pos); + + for (auto i = 0; i < _observations.size(); i++) + output.sat_azel.emplace(_observations.getObservationByIdx(i).sat, + Eigen::Vector2d(sat_elevations.at(2 * i), sat_elevations.at(2 * i + 1))); + + for (auto i = 0; i < MAXSAT; i++) + { + int sat = i+1; + if (_observations.hasSatellite(sat)) + { + if (sats_status[i].vs) + { + output.prange_residuals.emplace(sat,sats_status[i].resp[0]); + output.used_sats.insert(sat); + } + else + output.discarded_sats.insert(sat); + } + } + + return output; +} + +// ComputePosOutput computePosOwn(const GnssUtils::Observations & _observations, +// GnssUtils::Navigation & _navigation, +// const prcopt_t & prcopt) +// { + +// // Remove duplicated satellites +// uniqnav(&(_navigation.getNavigation())); + +// // Define error msg +// char msg[128] = ""; + +// GnssUtils::ComputePosOutput output; +// sol_t sol; +// sol = {{0}}; + +// output.pos_stat = pntposOwn(_observations.data(), _observations.size(), +// &(_navigation.getNavigation()), +// &prcopt, &sol, NULL, NULL, msg); + +// if (output.pos_stat == 0) +// { +// std::cout << "Bad computing: " << msg << "\n"; +// } + +// output.time = sol.time.time; +// output.sec = sol.time.sec; +// output.pos = Eigen::Vector3d(sol.rr); +// // std::cout << "Compute pos: " << output.pos.transpose() << "\n"; +// output.vel = Eigen::Vector3d(&sol.rr[3]); +// output.pos_covar << sol.qr[0], sol.qr[3], sol.qr[5], +// sol.qr[3], sol.qr[1], sol.qr[4], +// sol.qr[5], sol.qr[3], sol.qr[2]; + +// // XXX: segmentation fault here. +// // if (sol.dtr != NULL) +// // { +// // output.rcv_bias << sol.dtr[0], sol.dtr[1], sol.dtr[2], sol.dtr[3], sol.dtr[4], sol.dtr[5]; +// // } + +// output.type = sol.type; +// output.stat = sol.stat; +// output.ns = sol.ns; +// output.age = sol.age; +// output.ratio = sol.ratio; +// output.lat_lon = ecefToLatLonAlt(output.pos); + +// return output; +// } + +/* single-point positioning COPIED FROM RTKLIB ------------------------------ + * compute receiver position, velocity, clock bias by single-point positioning + * with pseudorange and doppler observables + * args : obsd_t *obs I observation data + * int n I number of observation data + * nav_t *nav I navigation data + * prcopt_t *opt I processing options + * sol_t *sol IO solution + * double *azel IO azimuth/elevation angle (rad) (NULL: no output) + * ssat_t *ssat IO satellite status (NULL: no output) + * char *msg O error message for error exit + * return : status(1:ok,0:error) + * notes : assuming sbas-gps, galileo-gps, qzss-gps, compass-gps time offset and + * receiver bias are negligible (only involving glonass-gps time offset + * and receiver bias) + *-----------------------------------------------------------------------------*/ +// int pntposOwn(const obsd_t *obs, int n, const nav_t *nav, +// const prcopt_t *opt, sol_t *sol, double *azel, ssat_t *ssat, +// char *msg) +// { +// prcopt_t opt_=*opt; +// double *rs,*dts,*var,*azel_,*resp; +// int i,stat,vsat[MAXOBS]={0},svh[MAXOBS]; + +// sol->stat=SOLQ_NONE; + +// if (n<=0) {strcpy(msg,"no observation data"); return 0;} + +// trace(3,"pntpos : tobs=%s n=%d\n",time_str(obs[0].time,3),n); + +// sol->time=obs[0].time; msg[0]='\0'; + +// rs=mat(6,n); dts=mat(2,n); var=mat(1,n); azel_=zeros(2,n); resp=mat(1,n); + +// if (opt_.mode!=PMODE_SINGLE) { /* for precise positioning */ +// #if 0 +// opt_.sateph =EPHOPT_BRDC; +// #endif +// opt_.ionoopt=IONOOPT_BRDC; +// opt_.tropopt=TROPOPT_SAAS; +// } +// /* satellite positons, velocities and clocks */ +// satposs(sol->time,obs,n,nav,opt_.sateph,rs,dts,var,svh); + +// /* estimate receiver position with pseudorange */ +// stat=estposOwn(obs,n,rs,dts,var,svh,nav,&opt_,sol,azel_,vsat,resp,msg); + +// /* raim fde */ +// if (!stat&&n>=6&&opt->posopt[4]) { +// stat=raim_fde(obs,n,rs,dts,var,svh,nav,&opt_,sol,azel_,vsat,resp,msg); +// } +// /* estimate receiver velocity with doppler */ +// //if (stat) estvel(obs,n,rs,dts,nav,&opt_,sol,azel_,vsat); + +// if (azel) { +// for (i=0;i<n*2;i++) azel[i]=azel_[i]; +// } +// if (ssat) { +// for (i=0;i<MAXSAT;i++) { +// ssat[i].vs=0; +// ssat[i].azel[0]=ssat[i].azel[1]=0.0; +// ssat[i].resp[0]=ssat[i].resc[0]=0.0; +// ssat[i].snr[0]=0; +// } +// for (i=0;i<n;i++) { +// ssat[obs[i].sat-1].azel[0]=azel_[ i*2]; +// ssat[obs[i].sat-1].azel[1]=azel_[1+i*2]; +// ssat[obs[i].sat-1].snr[0]=obs[i].SNR[0]; +// if (!vsat[i]) continue; +// ssat[obs[i].sat-1].vs=1; +// ssat[obs[i].sat-1].resp[0]=resp[i]; +// } +// } +// free(rs); free(dts); free(var); free(azel_); free(resp); +// return stat; +// } + +int estposOwn(const obsd_t* obs, + int n, + const double* rs, + const double* dts, + const double* vare, + const int* svh, + const nav_t* nav, + const prcopt_t* opt, + sol_t* sol, + double* azel, + int* vsat, + double* resp, + char* msg) +{ + // double x[NX]={0},dx[NX],Q[NX*NX],*v,*H,*var,sig; + // int i,j,k,info,stat,nv,ns; + // + // trace(3,"estpos : n=%d\n",n); + // + // v=mat(n+4,1); H=mat(NX,n+4); var=mat(n+4,1); + // + // for (i=0;i<3;i++) x[i]=sol->rr[i]; + // + // for (i=0;i<MAXITR;i++) { + // + // /* pseudorange residuals */ + // nv=rescode(i,obs,n,rs,dts,vare,svh,nav,x,opt,v,H,var,azel,vsat,resp, + // &ns); + // + // if (nv<NX) { + // sprintf(msg,"lack of valid sats ns=%d",nv); + // break; + // } + // /* weight by variance */ + // for (j=0;j<nv;j++) { + // sig=sqrt(var[j]); + // v[j]/=sig; + // for (k=0;k<NX;k++) H[k+j*NX]/=sig; + // } + // /* least square estimation */ + // if ((info=lsq(H,v,NX,nv,dx,Q))) { + // sprintf(msg,"lsq error info=%d",info); + // break; + // } + // for (j=0;j<NX;j++) x[j]+=dx[j]; + // + // if (norm(dx,NX)<1E-4) { + // sol->type=0; + // sol->time=timeadd(obs[0].time,-x[3]/CLIGHT); + // sol->dtr[0]=x[3]/CLIGHT; /* receiver clock bias (s) */ + // sol->dtr[1]=x[4]/CLIGHT; /* glo-gps time offset (s) */ + // sol->dtr[2]=x[5]/CLIGHT; /* gal-gps time offset (s) */ + // sol->dtr[3]=x[6]/CLIGHT; /* bds-gps time offset (s) */ + // for (j=0;j<6;j++) sol->rr[j]=j<3?x[j]:0.0; + // for (j=0;j<3;j++) sol->qr[j]=(float)Q[j+j*NX]; + // sol->qr[3]=(float)Q[1]; /* cov xy */ + // sol->qr[4]=(float)Q[2+NX]; /* cov yz */ + // sol->qr[5]=(float)Q[2]; /* cov zx */ + // sol->ns=(unsigned char)ns; + // sol->age=sol->ratio=0.0; + // + // /* validate solution */ + // if ((stat=valsol(azel,vsat,n,opt,v,nv,NX,msg))) { + // sol->stat=opt->sateph==EPHOPT_SBAS?SOLQ_SBAS:SOLQ_SINGLE; + // } + // free(v); free(H); free(var); + // + // return stat; + // } + // } + // if (i>=MAXITR) sprintf(msg,"iteration divergent i=%d",i); + // + // free(v); free(H); free(var); + + return 0; +} +} // namespace GnssUtils diff --git a/src/utils/satellite.cpp b/src/utils/satellite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eff1d47d263f8e13ff6b4c8787837c5f8e4b2fcc --- /dev/null +++ b/src/utils/satellite.cpp @@ -0,0 +1,146 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/* + * sat_position.cpp + * + * Created on: April 3, 2020 + * Author: Joan Vallvé, Pep MartÃ-Saumell + */ + +#include "gnss_utils/utils/satellite.h" + +#include "gnss_utils/observations.h" +#include "gnss_utils/navigation.h" +#include "gnss_utils/utils/transformations.h" +#include "gnss_utils/gnss_utils.h" + +namespace GnssUtils +{ + +Eigen::Vector2d computeAzel(const Eigen::Vector3d& sat_ecef, const Eigen::Vector3d& receiver_ecef) +{ + // receiver position (geo) + Eigen::Vector3d receiver_geo; + ecef2pos(receiver_ecef.data(), receiver_geo.data()); + + // receiver-sat unit vector (ecef) + Eigen::Vector3d e_ecef = (sat_ecef - receiver_ecef).normalized(); + + // receiver-sat unit vector (enu) + Eigen::Vector3d e_enu; + ecef2enu(receiver_geo.data(), // geodetic position {lat,lon} (rad) + e_ecef.data(), // vector in ecef coordinate {x,y,z} + e_enu.data()); // vector in local tangental coordinate {e,n,u} + + Eigen::Vector2d azel; + // azimuth + azel(0) = atan2(e_enu(0), e_enu(1)); + + // elevation + azel(1) = asin(e_enu(2)); + + return azel; +} + +Eigen::Vector2d computeAzel(const Satellite& sat, const Eigen::Vector3d& receiver_ecef) +{ + return computeAzel(sat.pos, receiver_ecef); + /*// compute receiver-to-satellilte unit vector (ecef) + Eigen::Vector3d e; + geodist(sat.pos.data(), receiver_ecef.data(), e.data()); + + // compute receiver position in geodetic + Eigen::Vector3d receiver_geo; + ecef2pos(receiver_ecef.data(), receiver_geo.data()); + + // compute azimut and elevation + Eigen::Vector2d azel; + satazel(receiver_geo.data(), e.data(), azel.data()); + + return azel;*/ +} + +Azels computeAzels(const Satellites& sats, const Eigen::Vector3d& receiver_ecef) +{ + Azels azels; + for (auto sat : sats) + azels.emplace(sat.first, computeAzel(sat.second, receiver_ecef)); + return azels; +} + +Satellites computeSatellites(const Observations& obs, + const Navigation& nav, + const int& eph_opt) +{ + double rs[6 * obs.size()], dts[2 * obs.size()], var[obs.size()]; + int svh[obs.size()]; + + // std::cout << "computing position of sats: "; + // for (auto&& obs_ref : obs.getObservations()) + // std::cout << (int)obs_ref.sat << " "; + // std::cout << std::endl; + + // compute positions + if (not obs.getObservations().empty()) + satposs(obs.getObservations().front().time, + obs.data(), + obs.size(), + &nav.getNavigation(), + eph_opt, + rs, + dts, + var, + svh); + + // fill Satellites + Satellites sats; + //std::cout << "computeSatellites: filling satellites: \n"; + for (int i = 0; i < obs.size(); i++) + { + auto sat_pair = sats.emplace(obs.getObservationByIdx(i).sat, // Key + Satellite({satsys(obs.getObservationByIdx(i).sat,NULL), + obs.getObservationByIdx(i).sat, // Constructor... + (Eigen::Vector3d() << rs[6*i],rs[6*i+1],rs[6*i+2]).finished(), + (Eigen::Vector3d() << rs[6*i+3], rs[6*i+4], rs[6*i+5]).finished() , + var[i], + dts[2*i], + dts[2*i+1], + svh[i]})); + /*//DEBUG + if (sat_pair.first->second.pos == Eigen::Vector3d::Zero()) + std::cout << "\tsat: " << sat_pair.first->second.sat << " ephemeris not available" << std::endl; + else + { + std::cout << "\tsat: " << sat_pair.first->second.sat << std::endl + << "\t\tpos: " << sat_pair.first->second.pos.transpose() << std::endl + << "\t\tvel: " << sat_pair.first->second.vel.transpose() << std::endl + << "\t\tvar: " << sat_pair.first->second.var << std::endl + << "\t\tclock bias: " << sat_pair.first->second.clock_bias << std::endl + << "\t\tclock drift: " << sat_pair.first->second.clock_drift << std::endl + << "\t\tsvh: " << sat_pair.first->second.svh << std::endl; + }//*/ + } + + return sats; +} + +} // namespace GnssUtils diff --git a/src/utils/transformations.cpp b/src/utils/transformations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1850e0145212d883d0bf3649a3c61436444ce01 --- /dev/null +++ b/src/utils/transformations.cpp @@ -0,0 +1,173 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/utils/transformations.h" + +using namespace GnssUtils; + +namespace GnssUtils +{ + +Eigen::Vector3d ecefToLatLonAlt(const Eigen::Vector3d& _ecef) +{ + Eigen::Vector3d pos; + ecef2pos(_ecef.data(), pos.data()); + + return pos; +} + +Eigen::Vector3d latLonAltToEcef(const Eigen::Vector3d& _latlon) +{ + Eigen::Vector3d ecef; + pos2ecef(_latlon.data(), ecef.data()); + + return ecef; +} + +Eigen::Matrix3d ecefToEnuCov(const Eigen::Vector3d& _latlon, const Eigen::Matrix3d& _cov_ecef) +{ + Eigen::Matrix3d cov_enu; + + /* RTKLIB transform covariance to local tangental coordinate -------------------------- + * transform ecef covariance to local tangental coordinate + * args : double *pos I geodetic position {lat,lon} (rad) + * double *P I covariance in ecef coordinate + * double *Q O covariance in local tangental coordinate + * return : none + *-----------------------------------------------------------------------------*/ + // extern void covenu(const double *pos, const double *P, double *Q); + covenu(_latlon.data(), _cov_ecef.data(), cov_enu.data()); + + return cov_enu; +} + +Eigen::Matrix3d enuToEcefCov(const Eigen::Vector3d& _latlon, const Eigen::Matrix3d& _cov_enu) +{ + Eigen::Matrix3d cov_ecef; + + /* RTKLIB transform local enu coordinate covariance to xyz-ecef ----------------------- + * transform local enu covariance to xyz-ecef coordinate + * args : double *pos I geodetic position {lat,lon} (rad) + * double *Q I covariance in local enu coordinate + * double *P O covariance in xyz-ecef coordinate + * return : none + *-----------------------------------------------------------------------------*/ + // extern void covecef(const double *pos, const double *Q, double *P) + covecef(_latlon.data(), _cov_enu.data(), cov_ecef.data()); + + return cov_ecef; +} + +void computeEnuEcefFromEcef(const Eigen::Vector3d& _t_ECEF_ENU, + Eigen::Matrix3d& R_ENU_ECEF, + Eigen::Vector3d& t_ENU_ECEF) +{ + // Convert ECEF coordinates to geodetic coordinates. + // J. Zhu, "Conversion of Earth-centered Earth-fixed coordinates + // to geodetic coordinates," IEEE Transactions on Aerospace and + // Electronic Systems, vol. 30, pp. 957-961, 1994. + + // double r = std::sqrt(_t_ECEF_ENU(0) * _t_ECEF_ENU(0) + _t_ECEF_ENU(1) * _t_ECEF_ENU(1)); + // double Esq = kSemimajorAxis * kSemimajorAxis - kSemiminorAxis * kSemiminorAxis; + // double F = 54 * kSemiminorAxis * kSemiminorAxis * _t_ECEF_ENU(2) * _t_ECEF_ENU(2); + // double G = r * r + (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) * _t_ECEF_ENU(2) - kFirstEccentricitySquared + // * Esq; double C = (kFirstEccentricitySquared * kFirstEccentricitySquared * F * r * r) / pow(G, 3); double S = + // cbrt(1 + C + sqrt(C * C + 2 * C)); double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G); double Q = sqrt(1 + 2 * + // kFirstEccentricitySquared * kFirstEccentricitySquared * P); double r_0 = -(P * kFirstEccentricitySquared * r) / + // (1 + Q) + // + sqrt(0.5 * kSemimajorAxis * kSemimajorAxis * (1 + 1.0 / Q) + // - P * (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) * _t_ECEF_ENU(2) / (Q * (1 + Q)) - 0.5 * P * + // r * r); + // double V = sqrt(pow((r - kFirstEccentricitySquared * r_0), 2) + (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) + // * _t_ECEF_ENU(2)); double Z_0 = kSemiminorAxis * kSemiminorAxis * _t_ECEF_ENU(2) / (kSemimajorAxis * V); + // + // double latitude = atan((_t_ECEF_ENU(2) + kSecondEccentricitySquared * Z_0) / r); + // double longitude = atan2(_t_ECEF_ENU(1), _t_ECEF_ENU(0)); + // + // + // double sLat = sin(latitude); + // double cLat = cos(latitude); + // double sLon = sin(longitude); + // double cLon = cos(longitude); + // + // R_ENU_ECEF(0,0) = -sLon; + // R_ENU_ECEF(0,1) = cLon; + // R_ENU_ECEF(0,2) = 0.0; + // + // R_ENU_ECEF(1,0) = -sLat*cLon; + // R_ENU_ECEF(1,1) = -sLat * sLon; + // R_ENU_ECEF(1,2) = cLat; + // + // R_ENU_ECEF(2,0) = cLat * cLon; + // R_ENU_ECEF(2,1) = cLat * sLon; + // R_ENU_ECEF(2,2) = sLat; + // + // t_ENU_ECEF = -R_ENU_ECEF*_t_ECEF_ENU; + + Eigen::Vector3d ENU_lat_lon_alt = GnssUtils::ecefToLatLonAlt(_t_ECEF_ENU); + + double sLat = sin(ENU_lat_lon_alt(0)); + double cLat = cos(ENU_lat_lon_alt(0)); + double sLon = sin(ENU_lat_lon_alt(1)); + double cLon = cos(ENU_lat_lon_alt(1)); + + R_ENU_ECEF(0, 0) = -sLon; + R_ENU_ECEF(0, 1) = cLon; + R_ENU_ECEF(0, 2) = 0.0; + + R_ENU_ECEF(1, 0) = -sLat * cLon; + R_ENU_ECEF(1, 1) = -sLat * sLon; + R_ENU_ECEF(1, 2) = cLat; + + R_ENU_ECEF(2, 0) = cLat * cLon; + R_ENU_ECEF(2, 1) = cLat * sLon; + R_ENU_ECEF(2, 2) = sLat; + + t_ENU_ECEF = -R_ENU_ECEF * _t_ECEF_ENU; +} + +void computeEnuEcefFromLatLonAlt(const Eigen::Vector3d& _ENU_latlonalt, + Eigen::Matrix3d& R_ENU_ECEF, + Eigen::Vector3d& t_ENU_ECEF) +{ + double sLat = sin(_ENU_latlonalt(0)); + double cLat = cos(_ENU_latlonalt(0)); + double sLon = sin(_ENU_latlonalt(1)); + double cLon = cos(_ENU_latlonalt(1)); + + R_ENU_ECEF(0, 0) = -sLon; + R_ENU_ECEF(0, 1) = cLon; + R_ENU_ECEF(0, 2) = 0.0; + + R_ENU_ECEF(1, 0) = -sLat * cLon; + R_ENU_ECEF(1, 1) = -sLat * sLon; + R_ENU_ECEF(1, 2) = cLat; + + R_ENU_ECEF(2, 0) = cLat * cLon; + R_ENU_ECEF(2, 1) = cLat * sLon; + R_ENU_ECEF(2, 2) = sLat; + + Eigen::Vector3d t_ECEF_ENU = latLonAltToEcef(_ENU_latlonalt); + + t_ENU_ECEF = -R_ENU_ECEF * t_ECEF_ENU; +} + +} // namespace GnssUtils diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..954a53e44176259261c0c7b6be46e075b4a732c2 --- /dev/null +++ b/src/utils/utils.cpp @@ -0,0 +1,417 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gnss_utils/utils/utils.h" + +namespace GnssUtils +{ +void print(std::string& _msg) +{ + std::string msg = GNSSUTILS_MSG + _msg; + + std::cout << msg << "\n"; +} + +std::vector<Observations> +loadObservationsFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt, const char* opt) +{ + obs_t obs; + obs.data = (obsd_t*)malloc(sizeof(obsd_t) * MAXSAT); + obs.n = 0; + obs.nmax = MAXSAT; + + // const char* opt = ""; + auto stat = readrnxt(rnx_file.c_str(), 1, t_start, t_end, dt, opt, &obs, NULL, NULL); + int n_epochs; + if (stat == 1) + n_epochs = sortobs(&obs); + else + { + std::cout << "Observation: couldn't load provided observation file, reason: " << (stat == 0 ? "no data" : "error") + << stat << std::endl; + return std::vector<Observations>(); + } + + std::vector<Observations> obs_vector; + obs_vector.reserve(n_epochs); + + Observations obs_epoch; + for (int i = 0; i < obs.n; i++) + { + obs_epoch.addObservation(obs.data[i]); + + bool last_obs_epoch = false; + if (i < obs.n - 1) + { + double tt = timediff(obs.data[i].time, obs.data[i + 1].time); + if (fabs(tt) > DTTOL) + { + obs_vector.push_back(obs_epoch); + obs_epoch.clearObservations(); + } + } + else + { + obs_vector.push_back(obs_epoch); + } + } + + free(obs.data); + + return obs_vector; +} + +} // namespace GnssUtils + +bool operator==(const gtime_t& time1, const gtime_t& time2) +{ + return (difftime(time1.time, time2.time) == 0.0 && time1.sec == time2.sec); +} +bool operator!=(const gtime_t& time1, const gtime_t& time2) +{ + return not(time1 == time2); +} + +bool operator==(const obsd_t& obs1, const obsd_t& obs2) +{ + return obs1.time == obs2.time && obs1.eventime == obs2.eventime && obs1.timevalid == obs2.timevalid && + obs1.sat == obs2.sat && obs1.rcv == obs2.rcv && + GnssUtils::equalArray<unsigned char>(obs1.SNR, obs2.SNR, ARRAY_SIZE(obs1.SNR), ARRAY_SIZE(obs2.SNR)) && + GnssUtils::equalArray<unsigned char>(obs1.LLI, obs2.LLI, ARRAY_SIZE(obs1.LLI), ARRAY_SIZE(obs2.LLI)) && + GnssUtils::equalArray<unsigned char>(obs1.code, obs2.code, ARRAY_SIZE(obs1.code), ARRAY_SIZE(obs2.code)) && + GnssUtils::equalArray<unsigned char>(obs1.qualL, obs2.qualL, ARRAY_SIZE(obs1.qualL), ARRAY_SIZE(obs2.qualL)) && + GnssUtils::equalArray<unsigned char>(obs1.qualP, obs2.qualP, ARRAY_SIZE(obs1.qualP), ARRAY_SIZE(obs2.qualP)) && + obs1.freq == obs2.freq && + GnssUtils::equalArray<double>(obs1.L, obs2.L, ARRAY_SIZE(obs1.L), ARRAY_SIZE(obs2.L)) && + GnssUtils::equalArray<double>(obs1.P, obs2.P, ARRAY_SIZE(obs1.P), ARRAY_SIZE(obs2.P)) && + GnssUtils::equalArray<float>(obs1.D, obs2.D, ARRAY_SIZE(obs1.D), ARRAY_SIZE(obs2.D)); +} +bool operator!=(const obsd_t& obs1, const obsd_t& obs2) +{ + return not(obs1 == obs2); +} + +bool operator==(const obs_t& obs1, const obs_t& obs2) +{ + if (obs1.n != obs2.n) + return false; + for (int i = 0; i < obs1.n; ++i) + { + if (obs1.data[i] != obs2.data[i]) + return false; + } + + return obs1.nmax == obs2.nmax && obs1.flag == obs2.flag && obs1.rcvcount == obs2.rcvcount && + obs1.tmcount == obs2.tmcount; +} +bool operator!=(const obs_t& obs1, const obs_t& obs2) +{ + return not(obs1 == obs2); +} + +bool operator==(const eph_t& eph1, const eph_t& eph2) +{ + return eph1.sat == eph2.sat && eph1.iode == eph2.iode && eph1.iodc == eph2.iodc && eph1.sva == eph2.sva && + eph1.svh == eph2.svh && eph1.week == eph2.week && eph1.code == eph2.code && eph1.flag == eph2.flag && + eph1.toe == eph2.toe && eph1.toc == eph2.toc && eph1.ttr == eph2.ttr && eph1.A == eph2.A && eph1.e == eph2.e && + eph1.i0 == eph2.i0 && eph1.OMG0 == eph2.OMG0 && eph1.omg == eph2.omg && eph1.M0 == eph2.M0 && + eph1.deln == eph2.deln && eph1.OMGd == eph2.OMGd && eph1.idot == eph2.idot && eph1.crc == eph2.crc && + eph1.crs == eph2.crs && eph1.cuc == eph2.cuc && eph1.cus == eph2.cus && eph1.cic == eph2.cic && + eph1.cis == eph2.cis && eph1.toes == eph2.toes && eph1.fit == eph2.fit && eph1.f0 == eph2.f0 && + eph1.f1 == eph2.f1 && eph1.f2 == eph2.f2 && + GnssUtils::equalArray<double>(eph1.tgd, eph2.tgd, ARRAY_SIZE(eph1.tgd), ARRAY_SIZE(eph2.tgd)) && + eph1.Adot == eph2.Adot && eph1.ndot == eph2.ndot; +} +bool operator!=(const eph_t& eph1, const eph_t& eph2) +{ + return not(eph1 == eph2); +} + +bool operator==(const geph_t& geph1, const geph_t& geph2) +{ + return geph1.sat == geph2.sat && geph1.iode == geph2.iode && geph1.frq == geph2.frq && geph1.svh == geph2.svh && + geph1.sva == geph2.sva && geph1.age == geph2.age && geph1.toe == geph2.toe && geph1.tof == geph2.tof && + GnssUtils::equalArray<double>(geph1.pos, geph2.pos, ARRAY_SIZE(geph1.pos), ARRAY_SIZE(geph2.pos)) && + GnssUtils::equalArray<double>(geph1.vel, geph2.vel, ARRAY_SIZE(geph1.vel), ARRAY_SIZE(geph2.vel)) && + GnssUtils::equalArray<double>(geph1.acc, geph2.acc, ARRAY_SIZE(geph1.acc), ARRAY_SIZE(geph2.acc)) && + geph1.taun == geph2.taun && geph1.gamn == geph2.gamn && geph1.dtaun == geph2.dtaun; +} +bool operator!=(const geph_t& geph1, const geph_t& geph2) +{ + return not(geph1 == geph2); +} + +bool operator==(const seph_t& seph1, const seph_t& seph2) +{ + return seph1.sat == seph2.sat && seph1.t0 == seph2.t0 && seph1.tof == seph2.tof && seph1.sva == seph2.sva && + seph1.svh == seph2.svh && + GnssUtils::equalArray<double>(seph1.pos, seph2.pos, ARRAY_SIZE(seph1.pos), ARRAY_SIZE(seph2.pos)) && + GnssUtils::equalArray<double>(seph1.vel, seph2.vel, ARRAY_SIZE(seph1.vel), ARRAY_SIZE(seph2.vel)) && + GnssUtils::equalArray<double>(seph1.acc, seph2.acc, ARRAY_SIZE(seph1.acc), ARRAY_SIZE(seph2.acc)) && + seph1.af0 == seph2.af0 && seph1.af1 == seph2.af1; +} +bool operator!=(const seph_t& seph1, const seph_t& seph2) +{ + return not(seph1 == seph2); +} + +bool operator==(const peph_t& peph1, const peph_t& peph2) +{ + return peph1.time == peph2.time && peph1.index == peph2.index && + GnssUtils::equalArray2d<double>(&peph1.pos[0][0], + &peph2.pos[0][0], + ARRAY2D_NROWS(peph1.pos), + ARRAY2D_NCOLS(peph1.pos), + ARRAY2D_NROWS(peph2.pos), + ARRAY2D_NCOLS(peph2.pos)) && + GnssUtils::equalArray2d<float>(&peph1.std[0][0], + &peph2.std[0][0], + ARRAY2D_NROWS(peph1.std), + ARRAY2D_NCOLS(peph1.std), + ARRAY2D_NROWS(peph2.std), + ARRAY2D_NCOLS(peph2.std)) && + GnssUtils::equalArray2d<double>(&peph1.vel[0][0], + &peph2.vel[0][0], + ARRAY2D_NROWS(peph1.vel), + ARRAY2D_NCOLS(peph1.vel), + ARRAY2D_NROWS(peph2.vel), + ARRAY2D_NCOLS(peph2.vel)) && + GnssUtils::equalArray2d<float>(&peph1.vst[0][0], + &peph2.vst[0][0], + ARRAY2D_NROWS(peph1.vst), + ARRAY2D_NCOLS(peph1.vst), + ARRAY2D_NROWS(peph2.vst), + ARRAY2D_NCOLS(peph2.vst)) && + GnssUtils::equalArray2d<float>(&peph1.cov[0][0], + &peph2.cov[0][0], + ARRAY2D_NROWS(peph1.cov), + ARRAY2D_NCOLS(peph1.cov), + ARRAY2D_NROWS(peph2.cov), + ARRAY2D_NCOLS(peph2.cov)) && + GnssUtils::equalArray2d<float>(&peph1.vco[0][0], + &peph2.vco[0][0], + ARRAY2D_NROWS(peph1.vco), + ARRAY2D_NCOLS(peph1.vco), + ARRAY2D_NROWS(peph2.vco), + ARRAY2D_NCOLS(peph2.vco)); +} +bool operator!=(const peph_t& peph1, const peph_t& peph2) +{ + return not(peph1 == peph2); +} + +bool operator==(const pclk_t& pclk1, const pclk_t& pclk2) +{ + return pclk1.time == pclk2.time && pclk1.index == pclk2.index && + GnssUtils::equalArray2d<double>(&pclk1.clk[0][0], + &pclk2.clk[0][0], + ARRAY2D_NROWS(pclk1.clk), + ARRAY2D_NCOLS(pclk1.clk), + ARRAY2D_NROWS(pclk2.clk), + ARRAY2D_NCOLS(pclk2.clk)) && + GnssUtils::equalArray2d<float>(&pclk1.std[0][0], + &pclk2.std[0][0], + ARRAY2D_NROWS(pclk1.std), + ARRAY2D_NCOLS(pclk1.std), + ARRAY2D_NROWS(pclk2.std), + ARRAY2D_NCOLS(pclk2.std)); +} +bool operator!=(const pclk_t& pclk1, const pclk_t& pclk2) +{ + return not(pclk1 == pclk2); +} + +bool operator==(const alm_t& alm1, const alm_t& alm2) +{ + return alm1.sat == alm2.sat && alm1.svh == alm2.svh && alm1.svconf == alm2.svconf && alm1.week == alm2.week && + alm1.toa == alm2.toa && alm1.A == alm2.A && alm1.e == alm2.e && alm1.i0 == alm2.i0 && alm1.OMG0 == alm2.OMG0 && + alm1.omg == alm2.omg && alm1.M0 == alm2.M0 && alm1.OMGd == alm2.OMGd && alm1.toas == alm2.toas && + alm1.f0 == alm2.f0 && alm1.f1 == alm2.f1; +} +bool operator!=(const alm_t& alm1, const alm_t& alm2) +{ + return not(alm1 == alm2); +} + +bool operator==(const tec_t& tec1, const tec_t& tec2) +{ + return tec1.time == tec2.time && + GnssUtils::equalArray<int>(tec1.ndata, tec2.ndata, ARRAY_SIZE(tec1.ndata), ARRAY_SIZE(tec2.ndata)) && + tec1.rb == tec2.rb && + GnssUtils::equalArray<double>(tec1.lats, tec2.lats, ARRAY_SIZE(tec1.lats), ARRAY_SIZE(tec2.lats)) && + GnssUtils::equalArray<double>(tec1.lons, tec2.lons, ARRAY_SIZE(tec1.lons), ARRAY_SIZE(tec2.lons)) && + GnssUtils::equalArray<double>(tec1.hgts, tec2.hgts, ARRAY_SIZE(tec1.hgts), ARRAY_SIZE(tec2.hgts)); + // TODO: *data and *rms +} +bool operator!=(const tec_t& tec1, const tec_t& tec2) +{ + return not(tec1 == tec2); +} + +bool operator==(const fcbd_t& fcbd1, const fcbd_t& fcbd2) +{ + return fcbd1.ts == fcbd2.ts && fcbd1.te == fcbd2.te && + GnssUtils::equalArray2d<double>(&fcbd1.bias[0][0], + &fcbd2.bias[0][0], + ARRAY2D_NROWS(fcbd1.bias), + ARRAY2D_NCOLS(fcbd1.bias), + ARRAY2D_NROWS(fcbd2.bias), + ARRAY2D_NCOLS(fcbd2.bias)) && + GnssUtils::equalArray2d<double>(&fcbd1.std[0][0], + &fcbd2.std[0][0], + ARRAY2D_NROWS(fcbd1.std), + ARRAY2D_NCOLS(fcbd1.std), + ARRAY2D_NROWS(fcbd2.std), + ARRAY2D_NCOLS(fcbd2.std)); +} +bool operator!=(const fcbd_t& fcbd1, const fcbd_t& fcbd2) +{ + return not(fcbd1 == fcbd2); +} + +bool operator==(const erp_t& erp1, const erp_t& erp2) +{ + // TODO + return true; +} +bool operator!=(const erp_t& erp1, const erp_t& erp2) +{ + return not(erp1 == erp2); +} + +bool operator==(const nav_t& nav1, const nav_t& nav2) +{ + if (nav1.n != nav2.n) + return false; + for (int i = 0; i < nav1.n; ++i) + { + if (nav1.eph[i] != nav2.eph[i]) + return false; + } + + if (nav1.ng != nav2.ng) + return false; + for (int i = 0; i < nav1.ng; ++i) + { + if (nav1.geph[i] != nav2.geph[i]) + return false; + } + + if (nav1.ns != nav2.ns) + return false; + for (int i = 0; i < nav1.ns; ++i) + { + if (nav1.seph[i] != nav2.seph[i]) + return false; + } + + if (nav1.ne != nav2.ne) + return false; + for (int i = 0; i < nav1.ne; ++i) + { + if (nav1.peph[i] != nav2.peph[i]) + return false; + } + + if (nav1.nc != nav2.nc) + return false; + for (int i = 0; i < nav1.nc; ++i) + { + if (nav1.pclk[i] != nav2.pclk[i]) + return false; + } + + if (nav1.na != nav2.na) + return false; + for (int i = 0; i < nav1.na; ++i) + { + if (nav1.alm[i] != nav2.alm[i]) + return false; + } + + if (nav1.nt != nav2.nt) + return false; + for (int i = 0; i < nav1.nt; ++i) + { + if (nav1.tec[i] != nav2.tec[i]) + return false; + } + + if (nav1.nf != nav2.nf) + return false; + for (int i = 0; i < nav1.nf; ++i) + { + if (nav1.fcb[i] != nav2.fcb[i]) + return false; + } + + // erp to do + + return nav1.nmax == nav2.nmax && nav1.ngmax == nav2.ngmax && nav1.nsmax == nav2.nsmax && nav1.nemax == nav2.nemax && + nav1.ncmax == nav2.ncmax && nav1.namax == nav2.namax && nav1.ntmax == nav2.ntmax && nav1.nfmax == nav2.nfmax && + nav1.erp == nav2.erp && + GnssUtils::equalArray<double>( + nav1.utc_gps, nav2.utc_gps, ARRAY_SIZE(nav1.utc_gps), ARRAY_SIZE(nav2.utc_gps)) && + GnssUtils::equalArray<double>( + nav1.utc_glo, nav2.utc_glo, ARRAY_SIZE(nav1.utc_glo), ARRAY_SIZE(nav2.utc_glo)) && + GnssUtils::equalArray<double>( + nav1.utc_gal, nav2.utc_gal, ARRAY_SIZE(nav1.utc_gal), ARRAY_SIZE(nav2.utc_gal)) && + GnssUtils::equalArray<double>( + nav1.utc_qzs, nav2.utc_qzs, ARRAY_SIZE(nav1.utc_qzs), ARRAY_SIZE(nav2.utc_qzs)) && + GnssUtils::equalArray<double>( + nav1.utc_cmp, nav2.utc_cmp, ARRAY_SIZE(nav1.utc_cmp), ARRAY_SIZE(nav2.utc_cmp)) && + GnssUtils::equalArray<double>( + nav1.utc_irn, nav2.utc_irn, ARRAY_SIZE(nav1.utc_irn), ARRAY_SIZE(nav2.utc_irn)) && + GnssUtils::equalArray<double>( + nav1.utc_sbs, nav2.utc_sbs, ARRAY_SIZE(nav1.utc_sbs), ARRAY_SIZE(nav2.utc_sbs)) && + GnssUtils::equalArray<double>( + nav1.ion_gps, nav2.ion_gps, ARRAY_SIZE(nav1.ion_gps), ARRAY_SIZE(nav2.ion_gps)) && + GnssUtils::equalArray<double>( + nav1.ion_gal, nav2.ion_gal, ARRAY_SIZE(nav1.ion_gal), ARRAY_SIZE(nav2.ion_gal)) && + GnssUtils::equalArray<double>( + nav1.ion_qzs, nav2.ion_qzs, ARRAY_SIZE(nav1.ion_qzs), ARRAY_SIZE(nav2.ion_qzs)) && + GnssUtils::equalArray<double>( + nav1.ion_cmp, nav2.ion_cmp, ARRAY_SIZE(nav1.ion_cmp), ARRAY_SIZE(nav2.ion_cmp)) && + GnssUtils::equalArray<double>( + nav1.ion_irn, nav2.ion_irn, ARRAY_SIZE(nav1.ion_irn), ARRAY_SIZE(nav2.ion_irn)) && + nav1.leaps == nav2.leaps && + GnssUtils::equalArray2d<double>(&nav1.lam[0][0], + &nav2.lam[0][0], + ARRAY2D_NROWS(nav1.lam), + ARRAY2D_NCOLS(nav1.lam), + ARRAY2D_NROWS(nav2.lam), + ARRAY2D_NCOLS(nav2.lam)) && + GnssUtils::equalArray2d<double>(&nav1.cbias[0][0], + &nav2.cbias[0][0], + ARRAY2D_NROWS(nav1.cbias), + ARRAY2D_NCOLS(nav1.cbias), + ARRAY2D_NROWS(nav2.cbias), + ARRAY2D_NCOLS(nav2.cbias)) && + GnssUtils::equalArray3d<double>(&nav1.rbias[0][0][0], &nav2.rbias[0][0][0], MAXRCV, 2, 3, MAXRCV, 2, 3) && + GnssUtils::equalArray<double>(nav1.wlbias, nav2.wlbias, ARRAY_SIZE(nav1.wlbias), ARRAY_SIZE(nav2.wlbias)) && + GnssUtils::equalArray<double>( + nav1.glo_cpbias, nav2.glo_cpbias, ARRAY_SIZE(nav1.glo_cpbias), ARRAY_SIZE(nav2.glo_cpbias)) && + GnssUtils::equalArray<char>(nav1.glo_fcn, nav2.glo_fcn, ARRAY_SIZE(nav1.glo_fcn), ARRAY_SIZE(nav2.glo_fcn)); +} +bool operator!=(const nav_t& nav1, const nav_t& nav2) +{ + return not(nav1 == nav2); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c180b0041c8d6001ce578b5c25b9813061259dec --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,33 @@ +# Retrieve googletest from github & compile +add_subdirectory(gtest) + +# Include gtest directory. +include_directories(${GTEST_INCLUDE_DIRS}) + +############# USE THIS TEST AS AN EXAMPLE #################### +# # +# Create a specific test executable for gtest_example # +# gnss_utils_add_gtest(gtest_example gtest_example.cpp) # +# target_link_libraries(gtest_example ${PROJECT_NAME}) # +# # +############################################################## + +# Navigation test +gnss_utils_add_gtest(gtest_navigation gtest_navigation.cpp) +target_link_libraries(gtest_navigation libgtest ${PROJECT_NAME}) + +# Observations test +gnss_utils_add_gtest(gtest_observations gtest_observations.cpp) +target_link_libraries(gtest_observations libgtest ${PROJECT_NAME}) + +# TDCP test +gnss_utils_add_gtest(gtest_tdcp gtest_tdcp.cpp) +target_link_libraries(gtest_tdcp ${PROJECT_NAME}) + +# Transformations test +gnss_utils_add_gtest(gtest_transformations gtest_transformations.cpp) +target_link_libraries(gtest_transformations ${PROJECT_NAME}) + +# ChiSquare test +gnss_utils_add_gtest(gtest_chisquare gtest_chisquare.cpp) +target_link_libraries(gtest_chisquare ${PROJECT_NAME}) \ No newline at end of file diff --git a/test/data/sample_data.nav b/test/data/sample_data.nav new file mode 100644 index 0000000000000000000000000000000000000000..5c4c418dc876fd45e7fddca388b4cc98b0202441 --- /dev/null +++ b/test/data/sample_data.nav @@ -0,0 +1,406 @@ + 3.04 N: GNSS NAV DATA M: MIXED RINEX VERSION / TYPE +sbf2rin-13.4.3 20200205 113410 UTC PGM / RUN BY / DATE +GPSA 9.3132E-09 -1.4901E-08 -5.9605E-08 1.1921E-07 IONOSPHERIC CORR +GPSB 9.6256E+04 -1.4746E+05 -1.3107E+05 9.1750E+05 IONOSPHERIC CORR +GAL 2.9250E+01 3.8281E-01 3.2959E-03 0.0000E+00 IONOSPHERIC CORR +GPUT -9.3132257462E-10 8.881784197E-16 503808 2083 TIME SYSTEM CORR +GAUT 9.3132257462E-10 0.000000000E+00 345600 2083 TIME SYSTEM CORR +GAGP 1.2514647096E-09-5.329070518E-15 345600 2083 TIME SYSTEM CORR + 18 LEAP SECONDS + END OF HEADER +G02 2019 12 12 12 00 00-3.648423589766E-04-7.503331289627E-12 0.000000000000E+00 + 4.700000000000E+01-1.208437500000E+02 4.378039505776E-09-1.891689129337E+00 + -6.053596735001E-06 1.953727600630E-02 9.275972843170E-06 5.153594808578E+03 + 3.888000000000E+05 3.371387720108E-07-2.768163608668E-01 2.346932888031E-07 + 9.575379914494E-01 1.915312500000E+02-1.691294764827E+00-7.654961717025E-09 + 2.000083311498E-11 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.769512891769E-08 4.700000000000E+01 + 3.868920000000E+05 4.000000000000E+00 +G04 2019 12 12 12 00 00-1.993030309677E-05-5.002220859751E-12 0.000000000000E+00 + 1.570000000000E+02-1.387500000000E+01 4.959135139313E-09-2.339868507292E+00 + -8.419156074524E-07 1.249722088687E-03 5.329027771950E-06 5.150576126099E+03 + 3.888000000000E+05 5.587935447693E-09 1.915372002909E+00 1.303851604462E-08 + 9.594040575671E-01 2.769062500000E+02-1.281079249179E+00-8.132124449911E-09 + -3.685867816904E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 4.096000000000E+03 6.300000000000E+01-4.190951585770E-09 6.690000000000E+02 + 3.873960000000E+05 4.000000000000E+00 +G06 2019 12 12 12 00 00-1.512947492301E-04-1.114131009672E-11 0.000000000000E+00 + 1.000000000000E+02-1.239062500000E+02 3.927663602954E-09-1.824134113525E+00 + -6.347894668579E-06 1.658447668888E-03 9.786337614059E-06 5.153719285965E+03 + 3.888000000000E+05-1.490116119385E-08-2.122724576342E-01 5.401670932770E-08 + 9.781077241470E-01 2.010000000000E+02-1.200768673201E+00-7.640318249923E-09 + 1.214336296267E-11 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 4.656612873077E-09 1.000000000000E+02 + 3.858180000000E+05 4.000000000000E+00 +G07 2019 12 12 12 00 00-1.683332957327E-04-8.185452315956E-12 0.000000000000E+00 + 5.700000000000E+01 3.909375000000E+01 4.250534194668E-09-2.882882750792E+00 + 1.888722181320E-06 1.320131728426E-02 1.172721385956E-05 5.153718780518E+03 + 3.888000000000E+05-7.450580596924E-09 2.943673467473E+00-1.341104507446E-07 + 9.545190186113E-01 1.482812500000E+02-2.422380694519E+00-7.540671242082E-09 + -4.639478967207E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.117587089539E-08 5.700000000000E+01 + 3.858180000000E+05 4.000000000000E+00 +G09 2019 12 12 12 00 00-1.062308438122E-04-9.436007530894E-12 0.000000000000E+00 + 7.800000000000E+01-1.206250000000E+01 5.168786729286E-09 4.992301981873E-01 + -7.078051567078E-07 1.692383317277E-03 5.021691322327E-06 5.153534730911E+03 + 3.888000000000E+05-2.793967723846E-08 1.868752621939E+00 1.490116119385E-08 + 9.520568016730E-01 2.776875000000E+02 1.684705661839E+00-8.283559329210E-09 + -2.914407111040E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 1.396983861923E-09 7.800000000000E+01 + 3.857520000000E+05 4.000000000000E+00 +G13 2019 12 12 12 00 00-2.714386209846E-05 2.387423592154E-12 0.000000000000E+00 + 8.700000000000E+01-1.987500000000E+01 4.779841956746E-09-8.793798521920E-01 + -1.072883605957E-06 4.130274290219E-03 5.709007382393E-06 5.153656044006E+03 + 3.888000000000E+05-6.332993507385E-08 2.009100851183E+00 9.313225746155E-08 + 9.674158381471E-01 2.706562500000E+02 1.183260297004E+00-8.083193840326E-09 + -5.078782980268E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-1.117587089539E-08 8.700000000000E+01 + 3.870420000000E+05 4.000000000000E+00 +G23 2019 12 12 12 00 00-1.500290818512E-04 2.387423592154E-12 0.000000000000E+00 + 3.000000000000E+01-1.009375000000E+01 5.335222233421E-09-1.452196357053E+00 + -4.973262548447E-07 1.326873130165E-02 4.552304744720E-06 5.153694892883E+03 + 3.888000000000E+05 2.980232238770E-07 1.861752114203E+00 4.470348358154E-08 + 9.428683609718E-01 2.778750000000E+02-2.215284453592E+00-8.261415549690E-09 + -2.478674675321E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00-2.048909664154E-08 3.000000000000E+01 + 3.858420000000E+05 4.000000000000E+00 +G30 2019 12 12 12 00 00-1.042019575834E-04-8.640199666843E-12 0.000000000000E+00 + 2.500000000000E+01 4.368750000000E+01 4.663408535398E-09-2.906933186921E+00 + 2.210959792137E-06 4.164319136180E-03 1.142919063568E-05 5.153722436905E+03 + 3.888000000000E+05-2.793967723846E-08 2.968865819418E+00-3.725290298462E-09 + 9.399056183160E-01 1.480312500000E+02-2.921787544453E+00-7.888185717455E-09 + -4.778770483544E-10 1.000000000000E+00 2.083000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 3.725290298462E-09 2.500000000000E+01 + 3.858180000000E+05 4.000000000000E+00 +R03 2019 12 12 11 15 00 6.369315087795E-06 0.000000000000E+00 3.858000000000E+05 + 9.652274414063E+03 3.457994461060E-01 9.313225746155E-10 0.000000000000E+00 + 1.708528710938E+04 2.202743530273E+00-1.862645149231E-09 5.000000000000E+00 + 1.629986035156E+04-2.522974967957E+00-2.793967723846E-09 0.000000000000E+00 +R03 2019 12 12 11 45 00 6.371177732944E-06 0.000000000000E+00 3.870000000000E+05 + 1.043946044922E+04 4.914197921753E-01 9.313225746155E-10 0.000000000000E+00 + 2.039817236328E+04 1.453030586243E+00-1.862645149231E-09 5.000000000000E+00 + 1.118693066406E+04-3.121288299561E+00-1.862645149231E-09 0.000000000000E+00 +R05 2019 12 12 11 15 00 4.108343273401E-05 9.094947017729E-13 3.862800000000E+05 + 5.985222167969E+03 1.195173263550E-01-1.862645149231E-09 0.000000000000E+00 + -1.828012353516E+04 2.295486450195E+00 1.862645149231E-09 1.000000000000E+00 + 1.676849218750E+04 2.457766532898E+00-1.862645149231E-09 0.000000000000E+00 +R05 2019 12 12 11 45 00 4.108529537916E-05 9.094947017729E-13 3.871800000000E+05 + 6.598400390625E+03 5.781021118164E-01-9.313225746155E-10 0.000000000000E+00 + -1.370180371094E+04 2.745733261108E+00 9.313225746155E-10 1.000000000000E+00 + 2.048785058594E+04 1.648019790649E+00-1.862645149231E-09 0.000000000000E+00 +R14 2019 12 12 11 15 00 4.597380757332E-05 0.000000000000E+00 3.858000000000E+05 + 2.135816357422E+04-6.865911483765E-01 3.725290298462E-09 0.000000000000E+00 + 1.261080761719E+04-4.694700241089E-01-0.000000000000E+00-7.000000000000E+00 + 5.991423828125E+03 3.442845344543E+00-2.793967723846E-09 0.000000000000E+00 +R14 2019 12 12 11 45 00 4.597473889589E-05 0.000000000000E+00 3.870000000000E+05 + 1.936734765625E+04-1.519350051880E+00 1.862645149231E-09 0.000000000000E+00 + 1.162212011719E+04-5.869359970093E-01-9.313225746155E-10-7.000000000000E+00 + 1.187716162109E+04 3.054467201233E+00-2.793967723846E-09 0.000000000000E+00 +R15 2019 12 12 11 15 00 1.051910221577E-04 0.000000000000E+00 3.860100000000E+05 + 2.249286962891E+04 1.470921516418E+00 5.587935447693E-09 0.000000000000E+00 + 4.179755859375E+03 4.950771331787E-01 9.313225746155E-10 0.000000000000E+00 + -1.125261865234E+04 3.118181228638E+00-1.862645149231E-09 0.000000000000E+00 +R15 2019 12 12 11 45 00 1.051910221577E-04 0.000000000000E+00 3.870000000000E+05 + 2.451276953125E+04 7.425718307495E-01 4.656612873077E-09 0.000000000000E+00 + 4.643450683594E+03 4.951381683350E-02 0.000000000000E+00 0.000000000000E+00 + -5.276882812500E+03 3.478320121765E+00-2.793967723846E-09 0.000000000000E+00 +R19 2019 12 12 11 15 00-6.853323429823E-05-1.818989403546E-12 3.859500000000E+05 + 2.008951708984E+04 2.060537338257E-01 2.793967723846E-09 0.000000000000E+00 + -1.541425244141E+04-4.377956390381E-01 2.793967723846E-09 3.000000000000E+00 + 3.049126953125E+03-3.578741073608E+00-1.862645149231E-09 0.000000000000E+00 +R19 2019 12 12 11 45 00-6.853695958853E-05-1.818989403546E-12 3.870000000000E+05 + 1.978871044922E+04-5.135507583618E-01 3.725290298462E-09 0.000000000000E+00 + -1.571828076172E+04 1.333026885986E-01 1.862645149231E-09 3.000000000000E+00 + -3.427280273438E+03-3.570507049561E+00-1.862645149231E-09 0.000000000000E+00 +R13 2019 12 12 11 45 00-3.215298056602E-05-0.000000000000E+00 3.876000000000E+05 + 3.100402832031E+03-2.939124107361E+00-1.862645149231E-09 0.000000000000E+00 + 1.186301953125E+04-8.695030212402E-01-9.313225746155E-10-2.000000000000E+00 + 2.235348193359E+04 8.685455322266E-01-1.862645149231E-09 0.000000000000E+00 +R20 2019 12 12 11 45 00-3.986386582255E-04-0.000000000000E+00 3.877800000000E+05 + 9.837636718750E+03 1.428291320801E+00-0.000000000000E+00 0.000000000000E+00 + -1.795626220703E+04-1.534766197205E+00 1.862645149231E-09 2.000000000000E+00 + 1.525312060547E+04-2.726587295532E+00-1.862645149231E-09 0.000000000000E+00 +E01 2019 12 12 11 10 00-7.507699192502E-04-7.986500349944E-12 0.000000000000E+00 + 3.000000000000E+00 2.013125000000E+02 2.449744898851E-09 7.981291655576E-01 + 9.473413228989E-06 1.731918891892E-04 9.533017873764E-06 5.440622966766E+03 + 3.858000000000E+05 3.166496753693E-08-2.605880888987E+00-1.117587089539E-08 + 9.855193908613E-01 1.463750000000E+02-1.069210219000E+00-5.272719629937E-09 + 1.914365455291E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.864650000000E+05 +E01 2019 12 12 11 20 00-7.507746340707E-04-7.972289495228E-12 0.000000000000E+00 + 4.000000000000E+00 2.002500000000E+02 2.449744898851E-09 8.736282654153E-01 + 9.419396519661E-06 1.731686061248E-04 9.588897228241E-06 5.440622581482E+03 + 3.864000000000E+05 3.725290298462E-08-2.605884063520E+00-1.862645149231E-09 + 9.855195488564E-01 1.452500000000E+02-1.070324887968E+00-5.265933632987E-09 + 1.896507568581E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.870650000000E+05 +E09 2019 12 12 11 10 00 6.223833654076E-03-1.216449163621E-11 0.000000000000E+00 + 3.000000000000E+00 7.218750000000E+00 3.823373544569E-09-3.442271110123E-02 + 3.892928361893E-07 2.964780433103E-04 3.403052687645E-06 5.440603132248E+03 + 3.858000000000E+05-9.313225746155E-09 1.575307486314E+00-6.146728992462E-08 + 9.586413959478E-01 2.625000000000E+02 2.794206052069E-01-5.815242228181E-09 + 8.571785620706E-12 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-4.656612873077E-10-6.984919309616E-10 + 3.869030000000E+05 +E12 2019 12 12 10 40 00 6.055364210624E-03-1.870148480521E-11 0.000000000000E+00 + 0.000000000000E+00-2.075312500000E+02 2.603322724555E-09-2.414195903395E+00 + -9.659677743912E-06 2.996901748702E-04 6.495043635368E-06 5.440614477158E+03 + 3.840000000000E+05-2.421438694000E-08-5.219517721978E-01 5.774199962616E-08 + 9.864495053986E-01 2.151250000000E+02-9.348391204646E-01-5.376652530588E-09 + -2.257236880119E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.327134668827E-08-1.327134668827E-08 + 3.858050000000E+05 +E19 2019 12 12 11 00 00-2.469634637237E-06 2.273736754432E-13 0.000000000000E+00 + 2.000000000000E+00 1.103125000000E+01 3.765871149364E-09-1.685770007887E-01 + 4.824250936508E-07 1.212444622070E-04 3.688037395477E-06 5.440600940704E+03 + 3.852000000000E+05-5.401670932770E-08 1.580615112564E+00 5.215406417847E-08 + 9.583897608719E-01 2.595937500000E+02 1.996512379669E+00-5.770954669140E-09 + 1.160762636137E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09-5.820766091347E-09 + 3.858660000000E+05 +E19 2019 12 12 11 10 00-2.469576429576E-06 2.273736754432E-13 0.000000000000E+00 + 3.000000000000E+00 1.168750000000E+01 3.765156833895E-09-9.388288915917E-02 + 5.066394805908E-07 1.211694907397E-04 3.693625330925E-06 5.440600774765E+03 + 3.858000000000E+05-5.960464477539E-08 1.580611582543E+00 4.097819328308E-08 + 9.583898398694E-01 2.594687500000E+02 1.996204499781E+00-5.764168672191E-09 + 1.207193141583E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09-5.820766091347E-09 + 3.864650000000E+05 +E21 2019 12 12 11 10 00-5.709171527997E-04-2.131628207280E-12 0.000000000000E+00 + 3.000000000000E+00 2.180312500000E+02 2.333311477503E-09 1.537835498027E+00 + 1.022964715958E-05 9.841390419751E-05 9.685754776001E-06 5.440636165619E+03 + 3.858000000000E+05-2.980232238770E-08-2.608584029515E+00 4.842877388000E-08 + 9.850865499746E-01 1.424062500000E+02-1.025413854454E+00-5.217717338871E-09 + 1.871506527187E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.867350000000E+05 +E21 2019 12 12 11 20 00-5.709183751605E-04-2.131628207280E-12 0.000000000000E+00 + 4.000000000000E+00 2.213125000000E+02 2.317953694933E-09 1.607657241034E+00 + 1.038052141666E-05 9.810912888497E-05 9.816139936447E-06 5.440637243271E+03 + 3.864000000000E+05-4.097819328308E-08-2.608587130902E+00 4.470348358154E-08 + 9.850867050439E-01 1.395312500000E+02-1.020851268976E+00-5.213788603794E-09 + 1.800074980348E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.870650000000E+05 +E01 2019 12 12 11 10 00-7.507689879276E-04-7.986500349944E-12 0.000000000000E+00 + 3.000000000000E+00 2.013125000000E+02 2.449744898851E-09 7.981291655576E-01 + 9.473413228989E-06 1.731918891892E-04 9.533017873764E-06 5.440622966766E+03 + 3.858000000000E+05 3.166496753693E-08-2.605880888987E+00-1.117587089539E-08 + 9.855193908613E-01 1.463750000000E+02-1.069210219000E+00-5.272719629937E-09 + 1.914365455291E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.865400000000E+05 +E01 2019 12 12 11 20 00-7.507736445405E-04-7.972289495228E-12 0.000000000000E+00 + 4.000000000000E+00 2.002500000000E+02 2.449744898851E-09 8.736282654153E-01 + 9.419396519661E-06 1.731686061248E-04 9.588897228241E-06 5.440622581482E+03 + 3.864000000000E+05 3.725290298462E-08-2.605884063520E+00-1.862645149231E-09 + 9.855195488564E-01 1.452500000000E+02-1.070324887968E+00-5.265933632987E-09 + 1.896507568581E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.871400000000E+05 +E12 2019 12 12 10 40 00 6.055364909116E-03-1.871569565992E-11 0.000000000000E+00 + 0.000000000000E+00-2.075312500000E+02 2.603322724555E-09-2.414195903395E+00 + -9.659677743912E-06 2.996901748702E-04 6.495043635368E-06 5.440614477158E+03 + 3.840000000000E+05-2.421438694000E-08-5.219517721978E-01 5.774199962616E-08 + 9.864495053986E-01 2.151250000000E+02-9.348391204646E-01-5.376652530588E-09 + -2.257236880119E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.327134668827E-08 0.000000000000E+00 + 3.858400000000E+05 +E19 2019 12 12 11 00 00-2.468528691679E-06 2.273736754432E-13 0.000000000000E+00 + 2.000000000000E+00 1.103125000000E+01 3.765871149364E-09-1.685770007887E-01 + 4.824250936508E-07 1.212444622070E-04 3.688037395477E-06 5.440600940704E+03 + 3.852000000000E+05-5.401670932770E-08 1.580615112564E+00 5.215406417847E-08 + 9.583897608719E-01 2.595937500000E+02 1.996512379669E+00-5.770954669140E-09 + 1.160762636137E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09 0.000000000000E+00 + 3.859400000000E+05 +E19 2019 12 12 11 10 00-2.468470484018E-06 2.273736754432E-13 0.000000000000E+00 + 3.000000000000E+00 1.168750000000E+01 3.765156833895E-09-9.388288915917E-02 + 5.066394805908E-07 1.211694907397E-04 3.693625330925E-06 5.440600774765E+03 + 3.858000000000E+05-5.960464477539E-08 1.580611582543E+00 4.097819328308E-08 + 9.583898398694E-01 2.594687500000E+02 1.996204499781E+00-5.764168672191E-09 + 1.207193141583E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.820766091347E-09 0.000000000000E+00 + 3.865400000000E+05 +E21 2019 12 12 11 10 00-5.709161632694E-04-2.117417352565E-12 0.000000000000E+00 + 3.000000000000E+00 2.180312500000E+02 2.333311477503E-09 1.537835498027E+00 + 1.022964715958E-05 9.841390419751E-05 9.685754776001E-06 5.440636165619E+03 + 3.858000000000E+05-2.980232238770E-08-2.608584029515E+00 4.842877388000E-08 + 9.850865499746E-01 1.424062500000E+02-1.025413854454E+00-5.217717338871E-09 + 1.871506527187E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.867900000000E+05 +E21 2019 12 12 11 20 00-5.709173856303E-04-2.131628207280E-12 0.000000000000E+00 + 4.000000000000E+00 2.213125000000E+02 2.317953694933E-09 1.607657241034E+00 + 1.038052141666E-05 9.810912888497E-05 9.816139936447E-06 5.440637243271E+03 + 3.864000000000E+05-4.097819328308E-08-2.608587130902E+00 4.470348358154E-08 + 9.850867050439E-01 1.395312500000E+02-1.020851268976E+00-5.213788603794E-09 + 1.800074980348E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.871400000000E+05 +E04 2019 12 12 11 10 00-4.236891982146E-04-7.531752999057E-12 0.000000000000E+00 + 3.000000000000E+00 9.781250000000E+00 3.935521073107E-09-4.009111796513E-01 + 5.532056093216E-07 8.811207953840E-05 3.596767783165E-06 5.440604427338E+03 + 3.858000000000E+05 9.499490261078E-08 1.582511472789E+00 3.166496753693E-08 + 9.526713574660E-01 2.577187500000E+02 1.440283619102E+00-5.908817554540E-09 + 7.071723137082E-11 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.355104804039E-09-5.820766091347E-09 + 3.875670000000E+05 +E04 2019 12 12 11 10 00-4.236878594384E-04-7.531752999057E-12 0.000000000000E+00 + 3.000000000000E+00 9.781250000000E+00 3.935521073107E-09-4.009111796513E-01 + 5.532056093216E-07 8.811207953840E-05 3.596767783165E-06 5.440604427338E+03 + 3.858000000000E+05 9.499490261078E-08 1.582511472789E+00 3.166496753693E-08 + 9.526713574660E-01 2.577187500000E+02 1.440283619102E+00-5.908817554540E-09 + 7.071723137082E-11 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-5.355104804039E-09 0.000000000000E+00 + 3.875900000000E+05 +E11 2019 12 12 11 00 00 3.295102505945E-04 1.526245796413E-10 5.204170427930E-18 + 2.000000000000E+00-2.123750000000E+02 2.665468170305E-09 2.480654590844E+00 + -9.858980774879E-06 1.651302445680E-04 7.150694727898E-06 5.440609029770E+03 + 3.852000000000E+05-3.352761268616E-08-5.219456059981E-01 2.048909664154E-08 + 9.864349859366E-01 1.975937500000E+02-2.012781103955E-01-5.400939256513E-09 + -2.103659054415E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.583248376846E-08-1.699663698673E-08 + 3.876330000000E+05 +E21 2019 12 12 11 30 00-5.709196557291E-04-2.131628207280E-12 0.000000000000E+00 + 5.000000000000E+00 2.240625000000E+02 2.303667385565E-09 1.677767938158E+00 + 1.050904393196E-05 9.777839295566E-05 9.970739483833E-06 5.440638303757E+03 + 3.870000000000E+05-5.401670932770E-08-2.608590226436E+00 3.725290298462E-08 + 9.850868571874E-01 1.362812500000E+02-1.016577701982E+00-5.210574184187E-09 + 1.721500278825E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 2.328306436539E-10 + 3.876650000000E+05 +E11 2019 12 12 11 00 00 3.295124042779E-04 1.526387904960E-10 5.204170427930E-18 + 2.000000000000E+00-2.123750000000E+02 2.665468170305E-09 2.480654590844E+00 + -9.858980774879E-06 1.651302445680E-04 7.150694727898E-06 5.440609029770E+03 + 3.852000000000E+05-3.352761268616E-08-5.219456059981E-01 2.048909664154E-08 + 9.864349859366E-01 1.975937500000E+02-2.012781103955E-01-5.400939256513E-09 + -2.103659054415E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.583248376846E-08 0.000000000000E+00 + 3.876800000000E+05 +E01 2019 12 12 11 30 00-7.507792906836E-04-7.972289495228E-12 0.000000000000E+00 + 5.000000000000E+00 1.994687500000E+02 2.447601952446E-09 9.486982138657E-01 + 9.378418326378E-06 1.731649972498E-04 9.616836905479E-06 5.440622346878E+03 + 3.870000000000E+05 3.911554813385E-08-2.605887229274E+00 7.450580596924E-09 + 9.855197039257E-01 1.446875000000E+02-1.071010335308E+00-5.259504793772E-09 + 1.871506527187E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09-1.862645149231E-09 + 3.877250000000E+05 +E21 2019 12 12 11 30 00-5.709186661988E-04-2.117417352565E-12 0.000000000000E+00 + 5.000000000000E+00 2.240625000000E+02 2.303667385565E-09 1.677767938158E+00 + 1.050904393196E-05 9.777839295566E-05 9.970739483833E-06 5.440638303757E+03 + 3.870000000000E+05-5.401670932770E-08-2.608590226436E+00 3.725290298462E-08 + 9.850868571874E-01 1.362812500000E+02-1.016577701982E+00-5.210574184187E-09 + 1.721500278825E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 4.656612873077E-10 0.000000000000E+00 + 3.877400000000E+05 +E01 2019 12 12 11 30 00-7.507783011533E-04-7.972289495228E-12 0.000000000000E+00 + 5.000000000000E+00 1.994687500000E+02 2.447601952446E-09 9.486982138657E-01 + 9.378418326378E-06 1.731649972498E-04 9.616836905479E-06 5.440622346878E+03 + 3.870000000000E+05 3.911554813385E-08-2.605887229274E+00 7.450580596924E-09 + 9.855197039257E-01 1.446875000000E+02-1.071010335308E+00-5.259504793772E-09 + 1.871506527187E-10 2.580000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00-1.629814505577E-09 0.000000000000E+00 + 3.878200000000E+05 +E36 2019 12 12 10 30 00 6.253067986108E-04-4.803268893738E-12 0.000000000000E+00 + 1.270000000000E+02-2.396562500000E+02 2.521890761159E-09 3.060908164528E+00 + -1.121312379837E-05 3.076605498791E-04 7.288530468941E-06 5.440609262466E+03 + 3.834000000000E+05 2.980232238770E-08-5.264936383676E-01 5.587935447693E-08 + 9.896539850266E-01 1.944062500000E+02-1.782833699345E+00-5.428440402046E-09 + -2.457245211269E-10 5.170000000000E+02 2.083000000000E+03 + 3.120000000000E+00 0.000000000000E+00 3.725290298462E-09 4.423782229424E-09 + 3.878450000000E+05 +S36 2019 12 12 11 36 48 0.000000000000E+00 0.000000000000E+00 3.874820000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.530000000000E+02 +S25 2019 12 11 23 59 44 0.000000000000E+00 0.000000000000E+00 3.874840000000E+05 + 4.053076976000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + -1.162201120000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 +S23 2019 12 12 11 37 04 0.000000000000E+00 0.000000000000E+00 3.874940000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.460000000000E+02 +S36 2019 12 12 11 38 56 0.000000000000E+00 0.000000000000E+00 3.875460000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.540000000000E+02 +S23 2019 12 12 11 39 12 0.000000000000E+00 0.000000000000E+00 3.875580000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.470000000000E+02 +S23 2019 12 12 11 41 20 0.000000000000E+00 0.000000000000E+00 3.876860000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.480000000000E+02 +S36 2019 12 12 11 41 04 0.000000000000E+00 0.000000000000E+00 3.877380000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.550000000000E+02 +S36 2019 12 12 11 43 12 0.000000000000E+00 0.000000000000E+00 3.878020000000E+05 + 4.200368800000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 3.674846960000E+03 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 +S23 2019 12 12 11 43 28 0.000000000000E+00 0.000000000000E+00 3.878780000000E+05 + 3.594460000000E+04 0.000000000000E+00 0.000000000000E+00 6.300000000000E+01 + 2.204414000000E+04 0.000000000000E+00 0.000000000000E+00 3.276700000000E+04 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.490000000000E+02 +C05 2019 12 12 11 00 00-2.244751667604E-04-5.808153957787E-11 0.000000000000E+00 + 1.000000000000E+00-4.986718750000E+02 1.785788670980E-12-5.169766818568E-01 + -1.630326732993E-05 6.773804780096E-04 2.061109989882E-05 6.493346771240E+03 + 3.852000000000E+05 1.271255314350E-07-1.766797704845E-01 3.259629011154E-09 + 1.180676987021E-01-6.241562500000E+02-1.608095995036E+00 1.192549674481E-09 + 7.575315542299E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-8.000000000000E-10-9.200000000000E-09 + 3.858276000000E+05 0.000000000000E+00 +C10 2019 12 12 11 00 00-4.537283675745E-04-3.685762806072E-11 0.000000000000E+00 + 1.000000000000E+00 9.611718750000E+02 9.743262988869E-10-3.040647920928E+00 + 3.135204315186E-05 6.156359100714E-03 7.578637450933E-06 6.493785018921E+03 + 3.852000000000E+05-1.676380634308E-08-2.379959881122E+00-4.004687070847E-08 + 9.058375010021E-01-1.493750000000E+01-2.570309406835E+00-2.115088101909E-09 + 4.782342060886E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 5.000000000000E-09 2.400000000000E-09 + 3.858180000000E+05 0.000000000000E+00 +C23 2019 12 12 11 00 00-8.696540025994E-04 2.136069099379E-12 0.000000000000E+00 + 1.000000000000E+00 1.034062500000E+02 3.498002848716E-09 1.631067082891E+00 + 5.179084837437E-06 2.014992060140E-04 1.188227906823E-05 5.282628129959E+03 + 3.852000000000E+05 3.725290298462E-08-3.016985033742E+00 6.565824151039E-08 + 9.546959600159E-01 1.175156250000E+02-1.126531243634E+00-6.488127399406E-09 + -1.864363372504E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 2.430000000000E-08 2.430000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C30 2019 12 12 11 00 00 2.388220746070E-04 6.104450278599E-12 0.000000000000E+00 + 1.000000000000E+00-1.393437500000E+02 3.927663602954E-09-2.732742750025E+00 + -6.837770342827E-06 3.649030113593E-04 1.750886440277E-06 5.282614929199E+03 + 3.852000000000E+05-3.864988684654E-08-9.521190897989E-01-5.587935447693E-09 + 9.644793109832E-01 3.275781250000E+02-4.157902561103E-01-7.082080711374E-09 + -2.339383158984E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-1.040000000000E-08-1.040000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C32 2019 12 12 11 00 00-8.566775359213E-04 2.992273095970E-12 0.000000000000E+00 + 1.000000000000E+00 2.875000000000E+01 4.228033257413E-09 2.867304095205E+00 + 1.456588506699E-06 1.842749770731E-04 6.427057087421E-06 5.282614164352E+03 + 3.852000000000E+05 1.862645149231E-09 1.148173467908E+00 6.705522537231E-08 + 9.608588315814E-01 2.297500000000E+02-1.050918258529E+00-6.857785654299E-09 + 5.228789228631E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-9.900000000000E-09-9.900000000000E-09 + 3.857580000000E+05 1.000000000000E+00 +C37 2019 12 12 11 00 00-8.880557725206E-04-5.954881032721E-11 0.000000000000E+00 + 1.000000000000E+00 1.086562500000E+02 3.521932416908E-09 8.066563687499E-01 + 5.388632416725E-06 7.037379546091E-04 1.127412542701E-05 5.282630052567E+03 + 3.852000000000E+05-2.142041921616E-08-3.020306267399E+00 1.536682248116E-08 + 9.560827215058E-01 1.306718750000E+02-1.080448983277E+00-6.580988410297E-09 + -2.328668426958E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00-1.390000000000E-08-1.390000000000E-08 + 3.858180000000E+05 1.000000000000E+00 +C20 2019 12 12 11 00 00-4.067538538948E-04-5.008171655163E-11 0.000000000000E+00 + 1.000000000000E+00 2.850000000000E+01 4.229461888350E-09 1.463145840983E+00 + 1.523178070784E-06 5.460308166221E-04 6.177462637424E-06 5.282614208221E+03 + 3.852000000000E+05 3.445893526077E-08 1.153432718382E+00-1.210719347000E-08 + 9.622213291121E-01 2.341406250000E+02-4.480160490039E-01-6.852785446020E-09 + 4.753769442150E-10 0.000000000000E+00 7.270000000000E+02 + 2.000000000000E+00 0.000000000000E+00 2.090000000000E-08 2.090000000000E-08 + 3.875880000000E+05 1.000000000000E+00 diff --git a/test/data/sample_data.obs b/test/data/sample_data.obs new file mode 100644 index 0000000000000000000000000000000000000000..d773d3b4d57556afcd38b64ef4167981a00de992 --- /dev/null +++ b/test/data/sample_data.obs @@ -0,0 +1,58 @@ + 3.04 OBSERVATION DATA M RINEX VERSION / TYPE +sbf2rin-13.4.3 20200205 113408 UTC PGM / RUN BY / DATE +SEPT MARKER NAME +Unknown MARKER NUMBER +Unknown Unknown OBSERVER / AGENCY +3021420 SEPT ASTERX-M2 4.4.0 REC # / TYPE / VERS +Unknown Unknown ANT # / TYPE + 4789398.3686 176958.8129 4194502.0999 APPROX POSITION XYZ + 0.0000 0.0000 0.0000 ANTENNA: DELTA H/E/N +G 7 X1 C1C L1C C2W L2W C2L L2L SYS / # / OBS TYPES +E 7 X1 C1C L1C C5Q L5Q C7Q L7Q SYS / # / OBS TYPES +S 3 X1 C1C L1C SYS / # / OBS TYPES +R 5 X1 C1C L1C C2C L2C SYS / # / OBS TYPES +C 5 X1 C2I L2I C7I L7I SYS / # / OBS TYPES +SEPTENTRIO RECEIVERS OUTPUT ALIGNED CARRIER PHASES. COMMENT +NO FURTHER PHASE SHIFT APPLIED IN THE RINEX ENCODER. COMMENT +G L1C SYS / PHASE SHIFT +G L2W SYS / PHASE SHIFT +G L2L 0.00000 SYS / PHASE SHIFT +E L1C 0.00000 SYS / PHASE SHIFT +E L5Q 0.00000 SYS / PHASE SHIFT +E L7Q 0.00000 SYS / PHASE SHIFT +S L1C SYS / PHASE SHIFT +R L1C SYS / PHASE SHIFT +R L2C SYS / PHASE SHIFT +C L2I SYS / PHASE SHIFT +C L7I SYS / PHASE SHIFT + 0.050 INTERVAL + 2019 12 12 11 37 42.0000000 GPS TIME OF FIRST OBS + 2019 12 12 11 45 12.9000000 GPS TIME OF LAST OBS + 36 # OF SATELLITES + C1C 0.000 C2C 0.000 GLONASS COD/PHS/BIS + 8 R03 5 R05 1 R13 -2 R14 -7 R15 0 R19 3 R20 2 R21 4 GLONASS SLOT / FRQ # + END OF HEADER +> 2019 12 12 11 37 42.0000000 0 23 +C05 16.000 40138840.659 6 209013478.40606 40138832.256 6 161622441.87406 +C10 17.000 39633104.355 5 206379985.90205 39633100.852 6 159586077.69206 +C23 18.000 22865676.301 8 119067585.90908 +C32 19.000 22153454.599 8 115358921.19508 +C37 20.000 23690566.611 7 123363010.77907 +E01 11.000 26311074.676 7 138265636.53007 26311074.470 5 103250320.19005 26311070.977 7 105943790.57907 +E09 12.000 25723493.482 5 +E12 13.000 24803095.627 5 130341141.55605 24803092.768 4 97332661.71504 24803092.407 6 99871767.93506 +E19 14.000 24540665.652 6 128962068.93606 24540664.837 5 96302837.53105 24540661.694 6 98815075.63606 +E21 15.000 25609802.251 7 134580416.73507 25609802.381 7 100498367.38007 25609799.519 7 103120055.01507 +G02 1.000 22032430.127 5 +G06 2.000 22147435.410 6 116385570.45206 22147437.095 4 90690045.24704 22147437.422 7 90690046.16907 +G07 3.000 20902411.481 8 109842911.60108 20902406.002 6 85591857.15006 20902406.930 7 85591857.16507 +G09 4.000 21908403.525 6 115129430.37406 21908399.544 1 89711233.32401 21908399.320 5 +G23 5.000 24104714.314 6 126671129.49306 24104706.816 2 98704739.67602 +G30 6.000 21404145.095 8 112479542.93308 21404142.953 6 87646388.26706 21404143.916 7 87646383.27307 +R03 7.000 21945110.617 7 117474090.63807 21945109.720 6 91368580.68606 +R14 8.000 20214975.526 8 107757316.39008 20214976.579 7 83811176.41707 +R15 9.000 22699008.378 5 121296675.96005 22699009.712 5 94341922.34705 +R19 10.000 22853592.745 4 22853590.888 4 +S23 21.000 38309228.895 7 201316353.13407 +S25 22.000 37834172.957 6 198818604.39206 +S36 23.000 37630702.258 7 197750698.01307 diff --git a/test/gtest/CMakeLists.txt b/test/gtest/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3c3c48c74516488a8c4eb24c2492bd81a5edc7f9 --- /dev/null +++ b/test/gtest/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 2.8.8) +project(gtest_builder C CXX) + +# We need thread support +#find_package(Threads REQUIRED) + +# Enable ExternalProject CMake module +include(ExternalProject) + +set(GTEST_FORCE_SHARED_CRT ON) +set(GTEST_DISABLE_PTHREADS OFF) + +# For some reason I need to disable PTHREADS +# with g++ (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 +# This is a known issue for MinGW : +# https://github.com/google/shaderc/pull/174 +#if(MINGW) + set(GTEST_DISABLE_PTHREADS ON) +#endif() + +# Download GoogleTest +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.8.x + # TIMEOUT 1 # We'll try this + CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs + -DCMAKE_CXX_FLAGS=${MSVC_COMPILER_DEFS} + -Dgtest_force_shared_crt=${GTEST_FORCE_SHARED_CRT} + -Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS} + -DBUILD_GTEST=ON + PREFIX "${CMAKE_CURRENT_BINARY_DIR}" + # Disable install step + INSTALL_COMMAND "" + UPDATE_DISCONNECTED 1 # 1: do not update googletest; 0: update googletest via github +) + +# Get GTest source and binary directories from CMake project + +# Specify include dir +ExternalProject_Get_Property(googletest source_dir) +set(GTEST_INCLUDE_DIRS ${source_dir}/googletest/include PARENT_SCOPE) + +# Specify MainTest's link libraries +ExternalProject_Get_Property(googletest binary_dir) +set(GTEST_LIBS_DIR ${binary_dir}/googlemock/gtest PARENT_SCOPE) + +# Create a libgtest target to be used as a dependency by test programs +add_library(libgtest IMPORTED STATIC GLOBAL) +add_dependencies(libgtest googletest) + +# Set libgtest properties +set_target_properties(libgtest PROPERTIES + "IMPORTED_LOCATION" "${binary_dir}/googlemock/gtest/libgtest.a" + "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}" +) + +function(gnss_utils_add_gtest target) + add_executable(${target} ${ARGN}) + add_dependencies(${target} libgtest) + target_link_libraries(${target} libgtest) + + #WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + add_test(NAME ${target} COMMAND ${target}) +endfunction() diff --git a/test/gtest/utils_gtest.h b/test/gtest/utils_gtest.h new file mode 100644 index 0000000000000000000000000000000000000000..c4cf21c1624b72671c79bf162bdc67da7ffe9466 --- /dev/null +++ b/test/gtest/utils_gtest.h @@ -0,0 +1,167 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +/** + * \file utils_gtest.h + * \brief Some utils for gtest + * \author Jeremie Deray + * Created on: 26/09/2016 + * Eigen macros extension by: Joan Sola on 26/04/2017 + */ + +#ifndef GNSSUTILS_UTILS_GTEST_H +#define GNSSUTILS_UTILS_GTEST_H + +#include <gtest/gtest.h> + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// C String Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); +// +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); + +// http://stackoverflow.com/a/29155677 + +namespace testing +{ +namespace internal +{ +enum GTestColor +{ + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +extern void ColoredPrintf(GTestColor color, const char* fmt, ...); + +#define PRINTF(...) \ + do { testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN,\ + "[ ] "); \ + testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, __VA_ARGS__); } \ + while(0) + +#define PRINT_TEST_FINISHED \ +{ \ + const ::testing::TestInfo* const test_info = \ + ::testing::UnitTest::GetInstance()->current_test_info(); \ + PRINTF(std::string("Finished test case ").append(test_info->test_case_name()).\ + append(" of test ").append(test_info->name()).append(".\n").c_str()); \ +} + +// C++ stream interface +class TestCout : public std::stringstream +{ +public: + ~TestCout() override + { + PRINTF("%s\n", str().c_str()); + } +}; + +/* Usage : + +TEST(Test, Foo) +{ + // the following works but prints default stream + EXPECT_TRUE(false) << "Testing Stream."; + + // or you can play with AINSI color code + EXPECT_TRUE(false) << "\033[1;31m" << "Testing Stream."; + + // or use the above defined macros + + PRINTF("Hello world"); + + // or + + TEST_COUT << "Hello world"; +} + +*/ +#define TEST_COUT testing::internal::TestCout() + +} // namespace internal + +/* Macros related to testing Eigen classes: + */ +#define EXPECT_MATRIX_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ + return (lhs - rhs).isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define ASSERT_MATRIX_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ + return (lhs - rhs).isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) + +#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) + +#define EXPECT_POSE2D_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ + MatrixXd er = lhs - rhs; \ + er(2) = pi2pi((double)er(2)); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define ASSERT_POSE2D_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ + MatrixXd er = lhs - rhs; \ + er(2) = pi2pi((double)er(2)); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +} // namespace testing + +#endif /* GNSSUTILS_UTILS_GTEST_H */ diff --git a/test/gtest_chisquare.cpp b/test/gtest_chisquare.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c07e12a47b4e9a8f4964f6198a0e7283eef169b --- /dev/null +++ b/test/gtest_chisquare.cpp @@ -0,0 +1,85 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" +#include "gnss_utils/utils/chisquare_ci.h" + +using namespace GnssUtils; + +/* chisq_2_CI: + * 4 DOF + * {1.06362, 0.10} + * {1.36648, 0.15}, + * {1.64878, 0.20}, + * {1.92256, 0.25}, + */ +TEST(ChiSquareTest, chisq2ci) +{ + // (almost) exact + ASSERT_NEAR(chisq2ci(1.36648, 4), 0.15, 1e-12); + + // Before first value + ASSERT_NEAR(chisq2ci(0.8, 4), 0.10, 1e-12); + + // Interpolation + for (double r = 0; r <= 1; r+=0.1) + ASSERT_NEAR(chisq2ci(r*1.36648+(1-r)*1.64878, 4), r*0.15+(1-r)*0.2, 1e-12); + + // negative or zero dof + ASSERT_DEATH(chisq2ci(0.4, -2), ""); + ASSERT_DEATH(chisq2ci(0.4, 0), ""); + + // more than 30 dof + ASSERT_NEAR(chisq2ci(0.8, 34), chisq2ci(0.8, 30), 1e-12); +} + +/* CI_2_chisq + * 4 DOF + * {0.10, 1.06362}, + * {0.15, 1.36648}, + * {0.20, 1.64878}, + * {0.25, 1.92256}, + */ +TEST(ChiSquareTest, ci2chisq) +{ + // (almost) exact + ASSERT_NEAR(ci2chisq(0.15, 4), 1.36648, 1e-12); + + // Before first value + ASSERT_NEAR(ci2chisq(0.05, 4), 1.06362, 1e-12); + + // Interpolation + for (double r = 0; r <= 1; r+=0.1) + ASSERT_NEAR(ci2chisq(r*0.15+(1-r)*0.2, 4), r*1.36648+(1-r)*1.64878, 1e-12); + + // negative or zero dof + ASSERT_DEATH(ci2chisq(0.4, -2), ""); + ASSERT_DEATH(ci2chisq(0.4, 0), ""); + + // more than 30 dof + ASSERT_NEAR(ci2chisq(0.8, 34), ci2chisq(0.8, 30), 1e-12); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_example.cpp b/test/gtest_example.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7059c8f4c9bed9b335a8e18cb88c22241964e98 --- /dev/null +++ b/test/gtest_example.cpp @@ -0,0 +1,41 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" + +TEST(TestTest, DummyTestExample) +{ + EXPECT_FALSE(false); + + ASSERT_TRUE(true); + + int my_int = 5; + + ASSERT_EQ(my_int, 5); + +// PRINTF("All good at TestTest::DummyTestExample !\n"); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_navigation.cpp b/test/gtest_navigation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be3de35ec8cffa98fa3c4d012e1012f0f030d880 --- /dev/null +++ b/test/gtest_navigation.cpp @@ -0,0 +1,57 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" +#include "gnss_utils/utils/utils.h" +#include "gnss_utils/navigation.h" + +using namespace GnssUtils; +std::string rnx_file = std::string(_GNSS_UTILS_ROOT_DIR) + "/test/data/sample_data.nav"; +const gtime_t t_start{ 0, 0 }; // no limit +const gtime_t t_end{ 0, 0 }; // no limit +const double dt = 0.0; // no limit +const char* opt = ""; // only GPS | GPS+GAL: "-SYS=G,L" | ALL: "" + +void loadRinex(nav_t& nav) +{ + int stat = readrnxt(rnx_file.c_str(), 1, t_start, t_end, dt, opt, NULL, &nav, NULL); + + ASSERT_EQ(stat, 1); + uniqnav(&nav); +} + +TEST(NavigationTests, setgetNavigation) +{ + nav_t nav; + loadRinex(nav); + + Navigation navigation; + + navigation.setNavigation(nav); + ASSERT_TRUE(navigation.getNavigation() == nav); + Navigation::freeNavArrays(nav); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_observations.cpp b/test/gtest_observations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6508d77a7209cf190426a50a16b3c770634e7ca7 --- /dev/null +++ b/test/gtest_observations.cpp @@ -0,0 +1,236 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" +#include "gnss_utils/observations.h" + +using namespace GnssUtils; +std::string rnx_file = std::string(_GNSS_UTILS_ROOT_DIR) + "/test/data/sample_data.obs"; +const gtime_t t_start{ 0, 0 }; // no limit +const gtime_t t_end{ 0, 0 }; // no limit +const double dt = 0.0; // no limit +const char* opt = ""; // only GPS | GPS+GAL: "-SYS=G,L" | ALL: "" + +void loadRinex(obs_t& obs) +{ + // RTKLIB utilities + obs.data = (obsd_t*)malloc(sizeof(obsd_t) * MAXSAT); + obs.n = 0; + obs.nmax = MAXSAT; + + int stat = readrnxt(rnx_file.c_str(), 1, t_start, t_end, dt, opt, &obs, NULL, NULL); + + ASSERT_EQ(stat, 1); + sortobs(&obs); +} + +TEST(ObservationsTest, AddClearObservation) +{ + obs_t obs; + loadRinex(obs); + + Observations observations; + + // testing addition + for (int ii = 0; ii < obs.n; ++ii) + { + observations.addObservation(obs.data[ii]); + ASSERT_TRUE(obs.data[ii] == observations.getObservations()[ii]); + } + + ASSERT_TRUE(obs.n == observations.getObservations().size()); + ASSERT_TRUE(obs.n == observations.size()); + + // Testing clear + observations.clearObservations(); + ASSERT_TRUE(0 == observations.getObservations().size()); + + // Add duplicated observation (has to crash) + observations.addObservation(obs.data[0]); + ASSERT_DEATH(observations.addObservation(obs.data[0]), ""); +} + +TEST(ObservationsTest, LoadFromRinex) +{ + // RTKLIB utilities + obs_t obs; + loadRinex(obs); + + // GnssUtils utilities + Observations observations; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations = obs_rinex[0]; + + ASSERT_EQ(obs.n, 18); + + // Comparison + ASSERT_TRUE(obs.n == observations.getObservations().size()); + for (int ii = 0; ii < obs.n; ++ii) + { + ASSERT_TRUE(obs.data[ii] == observations.getObservations()[ii]); + } + + free(obs.data); +} + +TEST(ObservationsTest, GetObservationBySat) +{ + obs_t obs; + loadRinex(obs); + + Observations observations; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations = obs_rinex[0]; + + for (int ii = 0; ii < obs.n; ++ii) + { + ASSERT_TRUE(obs.data[ii] == observations.getObservationBySat(obs.data[ii].sat)); + } + free(obs.data); +} + +TEST(ObservationsTest, GetObservationByIdx) +{ + obs_t obs; + loadRinex(obs); + + Observations observations; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations = obs_rinex[0]; + + for (int ii = 0; ii < obs.n; ++ii) + { + ASSERT_TRUE(obs.data[ii] == observations.getObservationByIdx(ii)); + } +} + +TEST(ObservationsTest, data) +{ + obs_t obs; + loadRinex(obs); + + Observations observations; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations = obs_rinex[0]; + + for (int ii = 0; ii < obs.n; ++ii) + { + ASSERT_TRUE(obs.data[ii] == observations.data()[ii]); + } +} + +TEST(ObservationsTest, HasSatellite) +{ + obs_t obs; + loadRinex(obs); + + Observations observations; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations = obs_rinex[0]; + + for (int ii = 0; ii < obs.n; ++ii) + { + ASSERT_TRUE(observations.hasSatellite(obs.data[ii].sat)); + } +} + +TEST(ObservationsTest, FindCommonObservations) +{ + Observations observations1; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations1 = obs_rinex[0]; + + Observations observations2(observations1); + + ASSERT_TRUE(observations1 == observations2); + + std::set<int> common_sats = Observations::findCommonObservations(observations1, observations2); + + ASSERT_TRUE(common_sats.size() == observations1.size()); + ASSERT_TRUE(common_sats.size() == observations2.size()); + for (auto sat : common_sats) + { + ASSERT_TRUE(observations1.hasSatellite(sat)); + ASSERT_TRUE(observations2.hasSatellite(sat)); + } +} + +TEST(ObservationsTest, FindCommonObservationsRemoved) +{ + Observations observations1; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations1 = obs_rinex[0]; + Observations observations2(observations1); + + // Remove first observation of observations2 + int sat_removed = observations2.getObservationByIdx(0).sat; + observations2.removeObservationBySat(sat_removed); + + std::set<int> common_sats = Observations::findCommonObservations(observations1, observations2); + + ASSERT_FALSE(common_sats.size() == observations1.size()); + ASSERT_TRUE(common_sats.size() == observations2.size()); + for (auto sat : common_sats) + { + ASSERT_TRUE(observations1.hasSatellite(sat)); + ASSERT_TRUE(observations2.hasSatellite(sat)); + } +} + +TEST(ObservationsTest, FindCommonObservationsChangeTime) +{ + Observations observations1; + + std::vector<Observations> obs_rinex = loadObservationsFromRinex(rnx_file.c_str(), t_start, t_end, dt, opt); + observations1 = obs_rinex[0]; + + Observations observations2(observations1); + + // Change time + for (auto&& obs : observations2.getObservations()) + { + obs.time.sec += 10; + } + + std::set<int> common_sats = Observations::findCommonObservations(observations1, observations2); + + ASSERT_TRUE(common_sats.size() == observations1.size()); + ASSERT_TRUE(common_sats.size() == observations2.size()); + for (auto sat : common_sats) + { + ASSERT_TRUE(observations1.hasSatellite(sat)); + ASSERT_TRUE(observations2.hasSatellite(sat)); + } +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + // rnx_file = argv[1]; + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_tdcp.cpp b/test/gtest_tdcp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9db8f2b0fb79c9e127bdaa7ef2ca9dd6894e8732 --- /dev/null +++ b/test/gtest_tdcp.cpp @@ -0,0 +1,546 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" +#include "gnss_utils/tdcp.h" +#include "gnss_utils/snapshot.h" +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/utils/transformations.h" + +using namespace GnssUtils; +using namespace Eigen; + +int N_sats = 20; +int N_tests = 100; +double distortion1 = -30; +double distortion2 = -20; + +Vector3d computeRandomReceiverLatLonAlt() +{ + Vector3d receiver_LLA = Vector3d::Random(); // in [-1, 1] + receiver_LLA(0) *= M_PI / 2; // in [-PI/2, PI/2] + receiver_LLA(1) *= M_PI; // in [-PI, PI] + receiver_LLA(2) += 1; // ([0, 2]) + receiver_LLA(2) *= 5e2; // in [0, 1e3] + + return receiver_LLA; +} + +void computeVisibleSatellite(const Vector3d& receiver_latlonalt, + const int& n_sat, + Vector3d& sat_ENU, + Vector3d& sat_ECEF, + Vector2d& sat_azel, + double& range) +{ + Vector3d t_ECEF_ENU, t_ENU_ECEF; + Matrix3d R_ENU_ECEF; + + t_ECEF_ENU = latLonAltToEcef(receiver_latlonalt); + + computeEnuEcefFromLatLonAlt(receiver_latlonalt, R_ENU_ECEF, t_ENU_ECEF); + + // equidistributed azimuth at elevation 45º + if (n_sat < 6) + { + sat_azel(0) = -M_PI + n_sat * 2*M_PI/6 + 0.1; // in [-pi, pi] + sat_azel(1) = M_PI / 4; // in [0, pi/2] + } + // random elevation and azimuth + else + { + sat_azel = Vector2d::Random(); // in [-1, 1] + sat_azel(0) *= M_PI; // in [-pi, pi] + sat_azel(1) = (sat_azel(1)/2 + 0.5) * M_PI / 2; // in [0, pi/2] + } + + // random range + range = VectorXd::Random(1)(0) * 5e2 + 1e3; // in [500, 1500] + + // ENU + sat_ENU << sin(sat_azel(0)) * cos(sat_azel(1)) * range, + cos(sat_azel(0)) * cos(sat_azel(1)) * range, + sin(sat_azel(1)) * range; + + // ECEF + sat_ECEF = t_ECEF_ENU + R_ENU_ECEF.transpose() * sat_ENU; + + // Range + range = (sat_ECEF - t_ECEF_ENU).norm(); +} + +TEST(Tdcp, Tdcp) +{ + TdcpOptions tdcp_params; + tdcp_params.max_iterations = 5; + tdcp_params.min_common_sats = 6; + tdcp_params.raim_n = 0; + tdcp_params.residual_opt = 0; + tdcp_params.max_residual_ci = 1e20; + tdcp_params.validate_residual = false; + tdcp_params.sagnac_correction = false; + tdcp_params.relinearize_jacobian = true; + tdcp_params.multi_freq = false; + tdcp_params.sigma_atm = 1; + tdcp_params.sigma_carrier = 1; + + Vector3d sat_ENU, sat_ECEF; + Vector3d x_r_LLA, x_r_ECEF, x_k_LLA, x_k_ECEF, d_ECEF; + Matrix3d R_ENU_ECEF; + Vector2d azel, azel2; + Vector4d d_gt; + Matrix4d cov_d; + double range, residual_ci; + + // Snapshots + auto snapshot_r = std::make_shared<Snapshot>(); + auto snapshot_k = std::make_shared<Snapshot>(); + + // Observations (just needed for the GPST) + snapshot_r->setObservations(std::make_shared<Observations>()); + snapshot_k->setObservations(std::make_shared<Observations>()); + obsd_t obs_r; + obs_r.time.sec = 0; + obs_r.time.time = 0; + snapshot_r->getObservations()->addObservation(obs_r); + obsd_t obs_k; + obs_k.time.sec = 0; + obs_k.time.time = 1; + snapshot_k->getObservations()->addObservation(obs_k); + + // Random receiver position + for (auto i = 0; i<N_tests; i++) + { + // clear satellites and ranges + snapshot_r->getSatellites().clear(); + snapshot_k->getSatellites().clear(); + snapshot_r->getRanges().clear(); + snapshot_k->getRanges().clear(); + + // random setup + x_r_LLA = computeRandomReceiverLatLonAlt(); + x_r_ECEF = latLonAltToEcef(x_r_LLA); + d_ECEF = Vector3d::Random() * 10; + x_k_ECEF = x_r_ECEF + d_ECEF; + x_k_LLA = ecefToLatLonAlt(x_k_ECEF); + + double clock_r = Vector2d::Random()(0) * 1e-6; + double clock_k = Vector2d::Random()(0) * 1e-6; + + // groundtruth + d_gt.head<3>() = d_ECEF; + d_gt(3) = (clock_k - clock_r) * CLIGHT; + + std::cout << "Test " << i << ":\n"; + std::cout << std::setprecision(10) << "\tx_r_ECEF " << x_r_ECEF.transpose() << ":\n"; + std::cout << "\tclock_r " << clock_r << ":\n"; + std::cout << std::setprecision(10) << "\tx_k_ECEF " << x_k_ECEF.transpose() << ":\n"; + std::cout << "\tclock_k " << clock_k << ":\n"; + std::cout << "\td_gt " << d_gt.transpose() << ":\n"; + + std::set<int> common_sats; + std::set<int> raim_discarded_sats; + + // random visible satellites + for (auto j = 0; j<N_sats; j++) + { + common_sats.insert(j); + + // Satellite r + computeVisibleSatellite(x_r_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_r_ECEF), 1e-3); + + Satellite sat_r({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_r->getSatellites().emplace(j, sat_r); + + // Range r + Range range_r; + range_r.sat = j; + range_r.L = range + CLIGHT * clock_r; + range_r.L_corrected = range_r.L; + range_r.L_var = 1; + range_r.L2 = range_r.L; + range_r.L2_corrected = range_r.L; + range_r.L2_var = range_r.L_var; + + snapshot_r->getRanges().emplace(j, range_r); + + //std::cout << "\tsat: " << j << "\n"; + //std::cout << "\t\tsat_r_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_r.L_corrected " << range_r.L_corrected << ":\n"; + + // Satellite k (random) + computeVisibleSatellite(x_k_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_k_ECEF), 1e-3); + + Satellite sat_k({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_k->getSatellites().emplace(j, sat_k); + + // Range k + Range range_k; + range_k.sat = j; + range_k.L = range + CLIGHT * clock_k; + range_k.L_corrected = range_k.L; + range_k.L_var = 1; + range_k.L2 = range_k.L; + range_k.L2_corrected = range_k.L; + range_k.L2_var = range_k.L_var; + + snapshot_k->getRanges().emplace(j, range_k); + + //std::cout << "\t\tsat_k_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_k.L_corrected " << range_k.L_corrected << ":\n"; + } + + // TDCP + auto tdcp_out = Tdcp(snapshot_r, + snapshot_k, + x_r_ECEF, + common_sats, + Vector4d::Zero(), + tdcp_params); + + // CHECKS + std::cout << "CHECKS Test " << i << std::endl; + ASSERT_TRUE(tdcp_out.success); + ASSERT_LE(tdcp_out.residual_ci, 0.8); + ASSERT_MATRIX_APPROX(tdcp_out.d, d_gt, 1e-3); + } +} + +TEST(Tdcp, Tdcp_raim_residual_rmse) +{ + int N_sats = 20; + + TdcpOptions tdcp_params; + tdcp_params.max_iterations = 5; + tdcp_params.min_common_sats = 6; + tdcp_params.raim_n = 2; + tdcp_params.residual_opt = 0; // Normalized RMSE + tdcp_params.max_residual_ci = 0.1; // low threshold to detect outliers... + tdcp_params.validate_residual = false; + tdcp_params.sagnac_correction = false; + tdcp_params.relinearize_jacobian = true; + tdcp_params.multi_freq = false; + tdcp_params.sigma_atm = 1; + tdcp_params.sigma_carrier = 1; + + + Vector3d sat_ENU, sat_ECEF; + Vector3d x_r_LLA, x_r_ECEF, x_k_LLA, x_k_ECEF, d_ECEF; + Matrix3d R_ENU_ECEF; + Vector2d azel, azel2; + Vector4d d, d_gt; + Matrix4d cov_d; + double range, residual_ci; + + // Snapshots + auto snapshot_r = std::make_shared<Snapshot>(); + auto snapshot_k = std::make_shared<Snapshot>(); + + // Observations (just needed for the GPST) + snapshot_r->setObservations(std::make_shared<Observations>()); + snapshot_k->setObservations(std::make_shared<Observations>()); + obsd_t obs_r; + obs_r.time.sec = 0; + obs_r.time.time = 0; + snapshot_r->getObservations()->addObservation(obs_r); + obsd_t obs_k; + obs_k.time.sec = 0; + obs_k.time.time = 1; + snapshot_k->getObservations()->addObservation(obs_k); + + // Random receiver position + for (auto i = 0; i<N_tests; i++) + { + // clear satellites and ranges + snapshot_r->getSatellites().clear(); + snapshot_k->getSatellites().clear(); + snapshot_r->getRanges().clear(); + snapshot_k->getRanges().clear(); + + // random setup + x_r_LLA = computeRandomReceiverLatLonAlt(); + x_r_ECEF = latLonAltToEcef(x_r_LLA); + d_ECEF = Vector3d::Random() * 10; + x_k_ECEF = x_r_ECEF + d_ECEF; + x_k_LLA = ecefToLatLonAlt(x_k_ECEF); + + double clock_r = Vector2d::Random()(0) * 1e-6; + double clock_k = Vector2d::Random()(0) * 1e-6; + + // groundtruth + d_gt.head<3>() = d_ECEF; + d_gt(3) = (clock_k - clock_r) * CLIGHT; + + // distorted sats + int wrong_sat1 = i % N_sats; + int wrong_sat2 = (i + N_sats / 2) % N_sats; + + std::cout << "Test " << i << ":\n"; + std::cout << std::setprecision(10) << "\tx_r_ECEF " << x_r_ECEF.transpose() << ":\n"; + std::cout << "\tclock_r " << clock_r << ":\n"; + std::cout << std::setprecision(10) << "\tx_k_ECEF " << x_k_ECEF.transpose() << ":\n"; + std::cout << "\tclock_k " << clock_k << ":\n"; + std::cout << "\td_gt " << d_gt.transpose() << ":\n"; + std::cout << "\tdistorted sats: " << wrong_sat1 << ", " << wrong_sat2 << std::endl; + + std::set<int> common_sats; + std::set<int> raim_discarded_sats; + + // random visible satellites + for (auto j = 0; j<N_sats; j++) + { + common_sats.insert(j); + + // Satellite r (random) + computeVisibleSatellite(x_r_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_r_ECEF), 1e-3); + + Satellite sat_r({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_r->getSatellites().emplace(j, sat_r); + + // Range r + Range range_r; + range_r.sat = j; + range_r.L = range + CLIGHT * clock_r; + range_r.L_corrected = range_r.L; + range_r.L_var = 1; + range_r.L2 = range_r.L; + range_r.L2_corrected = range_r.L; + range_r.L2_var = range_r.L_var; + + snapshot_r->getRanges().emplace(j, range_r); + + //std::cout << "\tsat: " << j << "\n"; + //std::cout << "\t\tsat_r_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_r.L_corrected " << range_r.L_corrected << ":\n"; + + // Satellite k (random) + computeVisibleSatellite(x_k_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_k_ECEF), 1e-3); + + Satellite sat_k({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_k->getSatellites().emplace(j, sat_k); + + // Range k + Range range_k; + range_k.sat = j; + range_k.L = range + CLIGHT * clock_k; + range_k.L_corrected = range_k.L; + range_k.L_var = 1; + range_k.L2 = range_k.L; + range_k.L2_corrected = range_k.L; + range_k.L2_var = range_k.L_var; + + snapshot_k->getRanges().emplace(j, range_k); + + //std::cout << "\t\tsat_k_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_k.L_corrected " << range_k.L_corrected << ":\n"; + } + + // Distort 2 ranges -> to be detected by RAIM + snapshot_k->getRanges().at(wrong_sat1).L_corrected += distortion1; + snapshot_k->getRanges().at(wrong_sat1).L += distortion1; + snapshot_r->getRanges().at(wrong_sat2).L_corrected += distortion2; + snapshot_r->getRanges().at(wrong_sat2).L += distortion2; + + + // TDCP + auto tdcp_out = Tdcp(snapshot_r, + snapshot_k, + x_r_ECEF, + common_sats, + Vector4d::Zero(), + tdcp_params); + + // CHECKS + std::cout << "CHECKS iteration " << i << std::endl; + ASSERT_EQ(tdcp_out.raim_discarded_sats.size(), 2); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat1)); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat2)); + ASSERT_TRUE(tdcp_out.success); + ASSERT_LE(tdcp_out.residual_ci, 0.8); + ASSERT_MATRIX_APPROX(tdcp_out.d, d_gt, 1e-3); + } +} + +TEST(Tdcp, Tdcp_raim_residual_max_mah) +{ + int N_sats = 20; + + TdcpOptions tdcp_params; + tdcp_params.max_iterations = 5; + tdcp_params.min_common_sats = 6; + tdcp_params.raim_n = 2; + tdcp_params.residual_opt = 1; // Max residual_ci in Mahalanobis distance + tdcp_params.max_residual_ci = 0.95; // 95% of confidence + tdcp_params.validate_residual = false; + tdcp_params.sagnac_correction = false; + tdcp_params.relinearize_jacobian = true; + tdcp_params.multi_freq = false; + tdcp_params.sigma_atm = 1; + tdcp_params.sigma_carrier = 1; + + + Vector3d sat_ENU, sat_ECEF; + Vector3d x_r_LLA, x_r_ECEF, x_k_LLA, x_k_ECEF, d_ECEF; + Matrix3d R_ENU_ECEF; + Vector2d azel, azel2; + Vector4d d, d_gt; + Matrix4d cov_d; + double range, residual_ci; + + // Snapshots + auto snapshot_r = std::make_shared<Snapshot>(); + auto snapshot_k = std::make_shared<Snapshot>(); + + // Observations (just needed for the GPST) + snapshot_r->setObservations(std::make_shared<Observations>()); + snapshot_k->setObservations(std::make_shared<Observations>()); + obsd_t obs_r; + obs_r.time.sec = 0; + obs_r.time.time = 0; + snapshot_r->getObservations()->addObservation(obs_r); + obsd_t obs_k; + obs_k.time.sec = 0; + obs_k.time.time = 1; + snapshot_k->getObservations()->addObservation(obs_k); + + // Random receiver position + for (auto i = 0; i<N_tests; i++) + { + // clear satellites and ranges + snapshot_r->getSatellites().clear(); + snapshot_k->getSatellites().clear(); + snapshot_r->getRanges().clear(); + snapshot_k->getRanges().clear(); + + // random setup + x_r_LLA = computeRandomReceiverLatLonAlt(); + x_r_ECEF = latLonAltToEcef(x_r_LLA); + d_ECEF = Vector3d::Random() * 10; + x_k_ECEF = x_r_ECEF + d_ECEF; + x_k_LLA = ecefToLatLonAlt(x_k_ECEF); + + double clock_r = Vector2d::Random()(0) * 1e-6; + double clock_k = Vector2d::Random()(0) * 1e-6; + + // groundtruth + d_gt.head<3>() = d_ECEF; + d_gt(3) = (clock_k - clock_r) * CLIGHT; + + // distorted sats + int wrong_sat1 = i % N_sats; + int wrong_sat2 = (i + N_sats / 2) % N_sats; + + std::cout << "\nTest " << i << ":\n"; + std::cout << std::setprecision(10) << "\tx_r_ECEF " << x_r_ECEF.transpose() << ":\n"; + std::cout << "\tclock_r " << clock_r << ":\n"; + std::cout << std::setprecision(10) << "\tx_k_ECEF " << x_k_ECEF.transpose() << ":\n"; + std::cout << "\tclock_k " << clock_k << ":\n"; + std::cout << "\td_gt " << d_gt.transpose() << ":\n"; + std::cout << "\tdistorted sats: " << wrong_sat1 << ", " << wrong_sat2 << std::endl; + + std::set<int> common_sats; + std::set<int> raim_discarded_sats; + + // random visible satellites + for (auto j = 0; j<N_sats; j++) + { + common_sats.insert(j); + + // Satellite r (random) + computeVisibleSatellite(x_r_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_r_ECEF), 1e-3); + + Satellite sat_r({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_r->getSatellites().emplace(j, sat_r); + + // Range r + Range range_r; + range_r.sat = j; + range_r.L = range + CLIGHT * clock_r; + range_r.L_corrected = range_r.L; + range_r.L_var = 1; + range_r.L2 = range_r.L; + range_r.L2_corrected = range_r.L; + range_r.L2_var = range_r.L_var; + + snapshot_r->getRanges().emplace(j, range_r); + + //std::cout << "\tsat: " << j << "\n"; + //std::cout << "\t\tsat_r_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_r.L_corrected " << range_r.L_corrected << ":\n"; + + // Satellite k (random) + computeVisibleSatellite(x_k_LLA, j, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_k_ECEF), 1e-3); + + Satellite sat_k({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_k->getSatellites().emplace(j, sat_k); + + // Range k + Range range_k; + range_k.sat = j; + range_k.L = range + CLIGHT * clock_k; + range_k.L_corrected = range_k.L; + range_k.L_var = 1; + range_k.L2 = range_k.L; + range_k.L2_corrected = range_k.L; + range_k.L2_var = range_k.L_var; + + snapshot_k->getRanges().emplace(j, range_k); + + //std::cout << "\t\tsat_k_ECEF " << sat_ECEF.transpose() << ":\n"; + //std::cout << "\t\trange_k.L_corrected " << range_k.L_corrected << ":\n"; + } + + // Distort 2 ranges -> to be detected by RAIM + snapshot_k->getRanges().at(wrong_sat1).L_corrected += distortion1; + snapshot_k->getRanges().at(wrong_sat1).L += distortion1; + snapshot_r->getRanges().at(wrong_sat2).L_corrected += distortion2; + snapshot_r->getRanges().at(wrong_sat2).L += distortion2; + + // TDCP + auto tdcp_out = Tdcp(snapshot_r, + snapshot_k, + x_r_ECEF, + common_sats, + Vector4d::Zero(), + tdcp_params); + + // CHECKS + std::cout << "Checks iteration " << i << std::endl; + ASSERT_EQ(tdcp_out.raim_discarded_sats.size(), 2); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat1)); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat2)); + ASSERT_TRUE(tdcp_out.success); + ASSERT_LE(tdcp_out.residual_ci, 0.8); + ASSERT_MATRIX_APPROX(tdcp_out.d, d_gt, 1e-3); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_transformations.cpp b/test/gtest_transformations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76162d6c39f1295ce5cbec34a3fa919d50c88389 --- /dev/null +++ b/test/gtest_transformations.cpp @@ -0,0 +1,246 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Vallvé Navarro (jvallve@iri.upc.edu) +// All rights reserved. +// +// This file is part of gnss_utils +// gnss_utils is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#include "gtest/utils_gtest.h" +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/utils/transformations.h" + +// Geodetic system parameters +static double kSemimajorAxis = 6378137; +static double kSemiminorAxis = 6356752.3142; +static double kFirstEccentricitySquared = 6.69437999014 * 0.001; +static double kSecondEccentricitySquared = 6.73949674228 * 0.001; + +using namespace GnssUtils; + +Eigen::Vector3d computeRandomReceiverLatLonAlt() +{ + Eigen::Vector3d receiver_LLA = Eigen::Vector3d::Random(); // in [-1, 1] + receiver_LLA(0) *= M_PI / 2; // in [-PI/2, PI/2] + receiver_LLA(1) *= M_PI; // in [-PI, PI] + receiver_LLA(2) += 1; // ([0, 2]) + receiver_LLA(2) *= 5e2; // in [0, 1e3] + + return receiver_LLA; +} + +void computeRandomVisibleSatellite(const Eigen::Vector3d& receiver_latlonalt, + Eigen::Vector3d& sat_ENU, + Eigen::Vector3d& sat_ECEF, + Eigen::Vector2d& sat_azel, + double range) +{ + Eigen::Vector3d t_ECEF_ENU, t_ENU_ECEF; + Eigen::Matrix3d R_ENU_ECEF; + + t_ECEF_ENU = latLonAltToEcef(receiver_latlonalt); + + computeEnuEcefFromLatLonAlt(receiver_latlonalt, R_ENU_ECEF, t_ENU_ECEF); + + // random elevation and azimuth + sat_azel = Eigen::Vector2d::Random(); // in [-1, 1] + sat_azel(0) *= M_PI; // in [-pi, pi] + sat_azel(1) = (sat_azel(1)/2 + 0.5) * M_PI / 2; // in [0, pi/2] + range = Eigen::VectorXd::Random(1)(0) * 5e2 + 1e3; // in [500, 1500] + + // ENU + sat_ENU << sin(sat_azel(0)) * cos(sat_azel(1)) * range, + cos(sat_azel(0)) * cos(sat_azel(1)) * range, + sin(sat_azel(1)) * range; + + // ECEF + sat_ECEF = t_ECEF_ENU + R_ENU_ECEF.transpose() * sat_ENU; +} + + +TEST(TransformationsTest, ecefToLatLonAlt) +{ + Eigen::Vector3d ecef, latlonalt; + + // aligned with ECEF X axis + ecef << 1e3, 0, 0; + latlonalt = ecefToLatLonAlt(ecef); + + ASSERT_NEAR(latlonalt(0),0.0,1e-6); + ASSERT_NEAR(latlonalt(1),0.0,1e-6); + + // aligned with ECEF Y axis + ecef << 0, 1e3, 0; + latlonalt = ecefToLatLonAlt(ecef); + + ASSERT_NEAR(latlonalt(0),0.0,1e-6); + ASSERT_NEAR(latlonalt(1),M_PI / 2,1e-6); + + // aligned with ECEF Z axis + ecef << 0, 0, 1e3; + latlonalt = ecefToLatLonAlt(ecef); + + ASSERT_NEAR(latlonalt(0),M_PI / 2,1e-6); + ASSERT_NEAR(latlonalt(1),0.0,1e-6); +} + +TEST(TransformationsTest, latLonAltToEcef) +{ + Eigen::Vector3d ecef, latlonalt; + + // aligned with ECEF X axis + latlonalt << 0, 0, 0; + ecef = latLonAltToEcef(latlonalt); + + ASSERT_NEAR(ecef(1),0.0,1e-6); + ASSERT_NEAR(ecef(2),0.0,1e-6); + + // aligned with ECEF Y axis + latlonalt << 0, M_PI / 2, 0; + ecef = latLonAltToEcef(latlonalt); + + ASSERT_NEAR(ecef(0),0.0,1e-6); + ASSERT_NEAR(ecef(2),0.0,1e-6); + + // aligned with ECEF Z axis + latlonalt << M_PI / 2, 0, 0; + ecef = latLonAltToEcef(latlonalt); + + ASSERT_NEAR(ecef(0),0.0,1e-6); + ASSERT_NEAR(ecef(1),0.0,1e-6); +} + +TEST(TransformationsTest, latLonAltToEcef_ecefToLatLonAlt) +{ + Eigen::Vector3d ecef0, ecef1, latlonalt0, latlonalt1; + + // ecef -> latlon -> ecef + for (auto i = 0; i<100; i++) + { + ecef0 = 1e3 * Eigen::Vector3d::Random(); + + latlonalt0 = ecefToLatLonAlt(ecef0); + ecef1 = latLonAltToEcef(latlonalt0); + ASSERT_MATRIX_APPROX(ecef0,ecef1,1e-6); + } + // latlon -> ecef -> latlon + for (auto i = 0; i<100; i++) + { + latlonalt0 = computeRandomReceiverLatLonAlt(); + + ecef0 = latLonAltToEcef(latlonalt0); + latlonalt1 = ecefToLatLonAlt(ecef0); + ASSERT_MATRIX_APPROX(latlonalt0,latlonalt1,1e-6); + } +} + +TEST(TransformationsTest, computeEnuEcef) +{ + Eigen::Vector3d t_ECEF_ENU, t_ENU_latlonalt, latlonalt1; + Eigen::Vector3d t_ENU_ECEF1,t_ENU_ECEF2,t_ENU_ECEF3; + Eigen::Matrix3d R_ENU_ECEF1,R_ENU_ECEF2,R_ENU_ECEF3; + + // random + for (auto i = 0; i<100; i++) + { + t_ENU_latlonalt = computeRandomReceiverLatLonAlt(); + + t_ECEF_ENU = latLonAltToEcef(t_ENU_latlonalt); + + // 1 from ECEF + computeEnuEcefFromEcef(t_ECEF_ENU, R_ENU_ECEF1, t_ENU_ECEF1); + // 2 from latlonalt + computeEnuEcefFromLatLonAlt(t_ENU_latlonalt, R_ENU_ECEF2, t_ENU_ECEF2); + // 3 Hardcoded solution + /* Convert ECEF coordinates to geodetic coordinates. + * J. Zhu, "Conversion of Earth-centered Earth-fixed coordinates + * to geodetic coordinates," IEEE Transactions on Aerospace and + * Electronic Systems, vol. 30, pp. 957-961, 1994. + */ + double r = std::sqrt(t_ECEF_ENU(0) * t_ECEF_ENU(0) + t_ECEF_ENU(1) * t_ECEF_ENU(1)); + double Esq = kSemimajorAxis * kSemimajorAxis - kSemiminorAxis * kSemiminorAxis; + double F = 54 * kSemiminorAxis * kSemiminorAxis * t_ECEF_ENU(2) * t_ECEF_ENU(2); + double G = r * r + (1 - kFirstEccentricitySquared) * t_ECEF_ENU(2) * t_ECEF_ENU(2) - kFirstEccentricitySquared * Esq; + double C = (kFirstEccentricitySquared * kFirstEccentricitySquared * F * r * r) / pow(G, 3); + double S = cbrt(1 + C + sqrt(C * C + 2 * C)); + double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G); + double Q = sqrt(1 + 2 * kFirstEccentricitySquared * kFirstEccentricitySquared * P); + double r_0 = -(P * kFirstEccentricitySquared * r) / (1 + Q) + + sqrt(0.5 * kSemimajorAxis * kSemimajorAxis * (1 + 1.0 / Q) + - P * (1 - kFirstEccentricitySquared) * t_ECEF_ENU(2) * t_ECEF_ENU(2) / (Q * (1 + Q)) - 0.5 * P * r * r); + double V = sqrt(pow((r - kFirstEccentricitySquared * r_0), 2) + (1 - kFirstEccentricitySquared) * t_ECEF_ENU(2) * t_ECEF_ENU(2)); + double Z_0 = kSemiminorAxis * kSemiminorAxis * t_ECEF_ENU(2) / (kSemimajorAxis * V); + + double latitude = atan((t_ECEF_ENU(2) + kSecondEccentricitySquared * Z_0) / r); + double longitude = atan2(t_ECEF_ENU(1), t_ECEF_ENU(0)); + + double sLat = sin(latitude); + double cLat = cos(latitude); + double sLon = sin(longitude); + double cLon = cos(longitude); + + R_ENU_ECEF3(0,0) = -sLon; + R_ENU_ECEF3(0,1) = cLon; + R_ENU_ECEF3(0,2) = 0.0; + + R_ENU_ECEF3(1,0) = -sLat*cLon; + R_ENU_ECEF3(1,1) = -sLat * sLon; + R_ENU_ECEF3(1,2) = cLat; + + R_ENU_ECEF3(2,0) = cLat * cLon; + R_ENU_ECEF3(2,1) = cLat * sLon; + R_ENU_ECEF3(2,2) = sLat; + + t_ENU_ECEF3 = -R_ENU_ECEF3*t_ECEF_ENU; + + // CHECK + ASSERT_MATRIX_APPROX(t_ENU_ECEF1,t_ENU_ECEF2,1e-6); + ASSERT_MATRIX_APPROX(R_ENU_ECEF1,R_ENU_ECEF2,1e-6); + ASSERT_MATRIX_APPROX(t_ENU_ECEF1,t_ENU_ECEF3,1e-6); + ASSERT_MATRIX_APPROX(R_ENU_ECEF1,R_ENU_ECEF3,1e-6); + } +} + +TEST(TransformationsTest, computeAzel) +{ + Eigen::Vector3d t_ENU_ECEF,t_ECEF_ENU,t_ENU_latlonalt, sat_ENU, sat_ECEF; + Eigen::Matrix3d R_ENU_ECEF; + Eigen::Vector2d azel, azel2; + double range; + + // random receiver position + for (auto i = 0; i<100; i++) + { + t_ENU_latlonalt = computeRandomReceiverLatLonAlt(); + t_ECEF_ENU = latLonAltToEcef(t_ENU_latlonalt); + + // random elevation and heading + for (auto j = 0; j<100; j++) + { + computeRandomVisibleSatellite(t_ENU_latlonalt, sat_ENU, sat_ECEF, azel, range); + + azel2 = computeAzel(sat_ECEF, t_ECEF_ENU); + + ASSERT_MATRIX_APPROX(azel,azel2,1e-6); + } + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}