pax_global_header00006660000000000000000000000064135217650600014517gustar00rootroot0000000000000052 comment=cd3e51f6d77fbce698ba30d259f456b6680779c2 .clang-format000066400000000000000000000055561352176506000134510ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: Google # More info: https://clang.llvm.org/docs/ClangFormatStyleOptions.html AccessModifierOffset: -4 AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: true AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBinaryOperators: None BreakBeforeBraces: GNU BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: true DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IncludeBlocks: Merge IncludeCategories: - Regex: '^.*.h"' Priority: 1 - Regex: '^.*(boost|gflags|glog|gnsssdr|gnuradio|gpstk|gsl|gtest|pmt|uhd|volk)/' Priority: 2 - Regex: '^.*(armadillo|matio|pugixml)' Priority: 2 - Regex: '.*' Priority: 3 - Regex: '^<.*\.h>' Priority: 4 - Regex: '^<.*' Priority: 5 IndentCaseLabels: false IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 8 UseTab: Never ... .clang-tidy000066400000000000000000000133321352176506000131210ustar00rootroot00000000000000--- Checks: '-*, boost-use-to-string, cert-dcl21-cpp, cert-dcl58-cpp, cert-env33-c, cert-err52-cpp, cert-err60-cpp, cert-flp30-c, clang-analyzer-cplusplus*, cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-type-static-cast-downcast, cppcoreguidelines-slicing, cppcoreguidelines-special-member-functions, google-build-namespaces, google-runtime-int, hicpp-exception-baseclass, hicpp-explicit-conversions, misc-misplaced-const, misc-new-delete-overloads, misc-non-copyable-objects, misc-static-assert, misc-throw-by-value-catch-by-reference, misc-uniqueptr-reset-release, misc-unused-using-decls, modernize-loop-convert, modernize-pass-by-value, modernize-raw-string-literal, modernize-use-auto, modernize-use-bool-literals, modernize-use-emplace, modernize-use-equals-default, modernize-use-equals-delete, modernize-use-noexcept, modernize-use-nullptr, performance-faster-string-find, performance-for-range-copy, performance-implicit-conversion-in-loop, performance-inefficient-algorithm, performance-inefficient-string-concatenation, performance-inefficient-vector-operation, performance-move-const-arg, performance-move-constructor-init, performance-noexcept-move-constructor, performance-type-promotion-in-math-fn, performance-unnecessary-copy-initialization, performance-unnecessary-value-param, readability-container-size-empty, readability-identifier-naming, readability-inconsistent-declaration-parameter-name, readability-named-parameter, readability-non-const-parameter, readability-string-compare' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: 'file' CheckOptions: - key: google-build-namespaces.HeaderFileExtensions value: ',h' - key: google-readability-braces-around-statements.ShortStatementLines value: '1' - key: google-readability-function-size.StatementThreshold value: '800' - key: google-readability-namespace-comments.ShortNamespaceLines value: '10' - key: google-readability-namespace-comments.SpacesBeforeComments value: '2' - key: google-runtime-int.SignedTypePrefix value: int - key: google-runtime-int.TypeSuffix value: _t - key: google-runtime-int.UnsignedTypePrefix value: uint - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries value: '1' - key: modernize-loop-convert.MaxCopySize value: '16' - key: modernize-loop-convert.MinConfidence value: reasonable - key: modernize-loop-convert.NamingStyle value: CamelCase - key: modernize-pass-by-value.IncludeStyle value: llvm - key: modernize-pass-by-value.ValuesOnly value: '0' - key: modernize-raw-string-literal.ReplaceShorterLiterals value: '0' - key: modernize-replace-auto-ptr.IncludeStyle value: llvm - key: modernize-use-auto.MinTypeNameLength value: '5' - key: modernize-use-auto.RemoveStars value: '0' - key: modernize-use-equals-default.IgnoreMacros value: '1' - key: modernize-use-noexcept.ReplacementString value: '' - key: modernize-use-noexcept.UseNoexceptFalse value: '1' - key: modernize-use-nullptr.NullMacros value: 'NULL' - key: performance-faster-string-find.StringLikeClasses value: 'std::basic_string' - key: performance-move-const-arg.CheckTriviallyCopyableMove value: '1' - key: performance-type-promotion-in-math-fn.IncludeStyle value: llvm - key: performance-unnecessary-value-param.IncludeStyle value: llvm - key: readability-identifier-naming.AbstractClassCase value: CamelCase - key: readability-identifier-naming.AbstractClassPrefix value: '' - key: readability-identifier-naming.AbstractClassSuffix value: '' - key: readability-identifier-naming.ClassCase value: Camel_Snake_Case - key: readability-identifier-naming.ClassPrefix value: '' - key: readability-identifier-naming.ClassSuffix value: '' - key: readability-identifier-naming.GlobalConstantCase value: UPPER_CASE - key: readability-identifier-naming.GlobalConstantPrefix value: '' - key: readability-identifier-naming.GlobalConstantSuffix value: '' - key: readability-identifier-naming.IgnoreFailedSplit value: '0' - key: readability-identifier-naming.StructCase value: aNy_CasE - key: readability-identifier-naming.StructPrefix value: '' - key: readability-identifier-naming.StructSuffix value: '' - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros value: '1' - key: readability-inconsistent-declaration-parameter-name.Strict value: '0' ... .gitignore000066400000000000000000000003131352176506000130500ustar00rootroot00000000000000*~ .*.swp docs/doxygen/Doxyfile docs/html docs/latex docs/GNSS-SDR_manual.pdf src/tests/data/output.dat thirdparty/ .settings .project .cproject .idea cmake-build-debug/ /install .DS_Store .pydevproject AUTHORS000066400000000000000000000053111352176506000121330ustar00rootroot00000000000000GNSS-SDR Authorship ------------------------------------------------------------------------------ The GNSS-SDR project is hosted and sponsored by the Centre Tecnològic de Telecomunicacions de Catalunya (CTTC), a non-profit research foundation located in Castelldefels (41.27504 N, 1.987709 E), 20 km south of Barcelona, Spain. GNSS-SDR is the by-product of GNSS research conducted at the Communications Systems Division of CTTC, and it is the combined effort of students, software engineers and researchers from different institutions around the World. Contact Information ------------------------------------------------------------------------------ GNSS-SDR Homepage ---------------------------- https://gnss-sdr.org CTTC Homepage ---------------------------- http://www.cttc.cat Mailing Lists ---------------------------- gnss-sdr-developers@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/gnss-sdr-developers Email ---------------------------- Inquiries beyond the mailing list can be sent to carles.fernandez@cttc.cat List of authors ------------------------------------------------------------------------------ Carles Fernández-Prades carles.fernandez@cttc.cat Project manager Javier Arribas javier.arribas@cttc.es Developer Luis Esteve Elfau luis@epsilon-formacion.com Developer Antonio Ramos antonio.ramosdet@gmail.com Developer Marc Majoral marc.majoral@cttc.cat Developer Jordi Vilà-Valls jordi.vila@cttc.cat Consultant Pau Closas pau.closas@northeastern.edu Consultant Álvaro Cebrián Juan acebrianjuan@gmail.com Contributor Andres Cecilia Luque a.cecilia.luque@gmail.com Contributor Anthony Arnold anthony.arnold@uqconnect.edu.au Contributor Carlos Avilés carlos.avilesr@googlemail.com Contributor Cillian O'Driscoll cillian.odriscoll@gmail.com Contributor Damian Miralles dmiralles2009@gmail.com Contributor Daniel Fehr daniel.co@bluewin.ch Contributor David Pubill david.pubill@cttc.cat Contributor Fran Fabra fabra@ice.csic.es Contributor Gabriel Araujo gabriel.araujo.5000@gmail.com Contributor Gerald LaMountain gerald@gece.neu.edu Contributor Leonardo Tonetto tonetto.dev@gmail.com Contributor Mara Branzanti mara.branzanti@gmail.com Contributor Marc Molina marc.molina.pena@gmail.com Contributor Marc Sales marcsales92@gmail.com Contributor Carlos Paniego carpanie@hotmail.com Artwork CMakeLists.txt000066400000000000000000003317671352176506000136440ustar00rootroot00000000000000# Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # ################################################################################ # Project setup ################################################################################ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "Prevented in-tree build, it is bad practice.\nTry 'cd build && cmake ..' instead.") endif() cmake_minimum_required(VERSION 2.8.12...3.15) project(gnss-sdr CXX C) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) ################################################################################ # Determine optional blocks/libraries to be built (default: not built) # Enable them at the command line by doing 'cmake -DENABLE_XXX=ON ..' ################################################################################ include(FeatureSummary) # Support of optional RF front-ends option(ENABLE_UHD "Enable the use of UHD (driver for all USRP devices)" ON) option(ENABLE_OSMOSDR "Enable the use of OsmoSDR and other front-ends (RTL-based dongles, HackRF, bladeRF, etc.) as signal source" OFF) option(ENABLE_FMCOMMS2 "Enable the use of FMCOMMS4-EBZ + ZedBoard hardware, requires gr-iio" OFF) option(ENABLE_PLUTOSDR "Enable the use of ADALM-PLUTO Evaluation Boards (Analog Devices Inc.), requires gr-iio" OFF) option(ENABLE_AD9361 "Enable the use of AD9361 direct to FPGA hardware, requires libiio" OFF) option(ENABLE_RAW_UDP "Enable the use of high-optimized custom UDP packet sample source, requires libpcap" OFF) option(ENABLE_FLEXIBAND "Enable the use of the signal source adater for the Teleorbit Flexiband GNU Radio driver" OFF) option(ENABLE_GN3S "Enable the use of the GN3S dongle as signal source (experimental)" OFF) option(ENABLE_ARRAY "Enable the use of CTTC's antenna array front-end as signal source (experimental)" OFF) # Performance analysis tools option(ENABLE_GPERFTOOLS "Enable linking to Gperftools libraries (tcmalloc and profiler)" OFF) option(ENABLE_GPROF "Enable the use of the GNU profiler tool 'gprof'" OFF) # Code correctness option(ENABLE_CLANG_TIDY "Enable the use of clang-tidy when compiling" OFF) # Acceleration option(ENABLE_PROFILING "Enable execution of volk_gnsssdr_profile at the end of the building" OFF) option(ENABLE_OPENCL "Enable building of processing blocks implemented with OpenCL (experimental)" OFF) option(ENABLE_CUDA "Enable building of processing blocks implemented with CUDA (experimental, requires CUDA SDK)" OFF) option(ENABLE_FPGA "Enable building of processing blocks implemented with FPGA" OFF) # Building and packaging options option(ENABLE_GENERIC_ARCH "Builds a portable binary" OFF) option(ENABLE_PACKAGING "Enable software packaging" OFF) option(ENABLE_OWN_GLOG "Download glog and link it to gflags" OFF) option(ENABLE_OWN_ARMADILLO "Download and build Armadillo locally" OFF) option(ENABLE_LOG "Enable logging" ON) if(ENABLE_PACKAGING) set(ENABLE_GENERIC_ARCH ON) endif() # Testing option(ENABLE_UNIT_TESTING "Build unit tests" ON) option(ENABLE_UNIT_TESTING_MINIMAL "Build a minimal set of unit tests" OFF) option(ENABLE_UNIT_TESTING_EXTRA "Download external files and build extra unit tests" OFF) option(ENABLE_SYSTEM_TESTING "Build system tests" OFF) option(ENABLE_SYSTEM_TESTING_EXTRA "Download external tools and build extra system tests" OFF) option(ENABLE_GNSS_SIM_INSTALL "Enable the installation of gnss_sim on the fly" ON) if(NOT (ENABLE_UNIT_TESTING_EXTRA OR ENABLE_SYSTEM_TESTING_EXTRA OR ENABLE_FPGA)) set(ENABLE_GNSS_SIM_INSTALL OFF) endif() if(ENABLE_SYSTEM_TESTING_EXTRA) set(ENABLE_SYSTEM_TESTING ON) endif() option(ENABLE_OWN_GPSTK "Force to download, build and link GPSTk for system tests, even if it is already installed" OFF) option(ENABLE_INSTALL_TESTS "Install QA code system-wide" OFF) if(ENABLE_FPGA) set(ENABLE_INSTALL_TESTS ON) endif() ################################################################################ # GNSS-SDR version information ################################################################################ set(THIS_IS_A_RELEASE ON) # only related to version name, no further implications. if(NOT ${THIS_IS_A_RELEASE}) find_package(Git) set_package_properties(Git PROPERTIES URL "https://git-scm.com" DESCRIPTION "A free and open source distributed version control system (found: v${GIT_VERSION_STRING})" PURPOSE "Manage version control, get MINOR_VERSION name for version number." TYPE REQUIRED ) if(GIT_FOUND) # was this info set in the CMake commandline? if(NOT GIT_BRANCH) # no: try to find it execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE ) endif(NOT GIT_BRANCH) # was this info set in the CMake commandline? if(NOT GIT_COMMIT_HASH) # Get the latest abbreviated commit hash of the working branch execute_process( COMMAND ${GIT_EXECUTABLE} log -1 --format=%h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) endif(NOT GIT_COMMIT_HASH) endif() endif() set(VERSION_INFO_MAJOR_VERSION 0) set(VERSION_INFO_API_COMPAT 0) if(${THIS_IS_A_RELEASE}) set(VERSION_INFO_MINOR_VERSION 11) else() set(VERSION_INFO_MINOR_VERSION 11.git-${GIT_BRANCH}-${GIT_COMMIT_HASH}) endif() set(VERSION ${VERSION_INFO_MAJOR_VERSION}.${VERSION_INFO_API_COMPAT}.${VERSION_INFO_MINOR_VERSION}) ################################################################################ # Environment setup ################################################################################ include(ExternalProject) # Detect 64-bits machine if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCH_64BITS TRUE) endif() # Set prefix path for PyBOMBS and Snaps, if defined in environment variables if(NOT CMAKE_PREFIX_PATH) if(DEFINED ENV{PYBOMBS_PREFIX}) set(CMAKE_PREFIX_PATH $ENV{PYBOMBS_PREFIX}) endif() if(DEFINED ENV{SNAP}) set(CMAKE_PREFIX_PATH $ENV{SNAP}) endif() endif() # Detect Linux Distribution if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(OperatingSystem "Linux") set(OS_IS_LINUX TRUE) if(ARCH_64BITS) set(ARCH_ "(64 bits)") else() set(ARCH_ "(32 bits)") endif() if(EXISTS "/etc/lsb-release") execute_process(COMMAND cat /etc/lsb-release COMMAND grep DISTRIB_ID COMMAND awk -F= "{ print $2 }" COMMAND tr "\n" " " COMMAND sed "s/ //" OUTPUT_VARIABLE LINUX_DISTRIBUTION RESULT_VARIABLE LINUX_ID_RESULT ) execute_process(COMMAND cat /etc/lsb-release COMMAND grep DISTRIB_RELEASE COMMAND awk -F= "{ print $2 }" COMMAND tr "\n" " " COMMAND sed "s/ //" OUTPUT_VARIABLE LINUX_VER RESULT_VARIABLE LINUX_VER_RESULT ) endif() if(NOT LINUX_DISTRIBUTION) if(EXISTS "/etc/linuxmint/info") set(LINUX_DISTRIBUTION "LinuxMint") execute_process(COMMAND cat /etc/linuxmint/info COMMAND grep -m1 RELEASE COMMAND awk -F= "{ print $2 }" COMMAND tr "\n" " " COMMAND sed "s/ //" OUTPUT_VARIABLE LINUX_VER RESULT_VARIABLE LINUX_VER_RESULT ) endif() endif() if(NOT LINUX_DISTRIBUTION) if(EXISTS "/etc/os-release") execute_process(COMMAND cat /etc/os-release COMMAND grep -m1 NAME COMMAND awk -F= "{ print $2 }" COMMAND tr "\n" " " COMMAND sed "s/ //" OUTPUT_VARIABLE LINUX_DISTRIBUTION RESULT_VARIABLE LINUX_ID_RESULT ) execute_process(COMMAND cat /etc/os-release COMMAND grep VERSION_ID COMMAND awk -F= "{ print $2 }" COMMAND tr "\n" " " COMMAND sed "s/ //" OUTPUT_VARIABLE LINUX_VER RESULT_VARIABLE LINUX_VER_RESULT ) if(${LINUX_DISTRIBUTION} MATCHES "Debian") set(LINUX_DISTRIBUTION "Debian") file(READ /etc/debian_version LINUX_VER) endif() endif() endif() if(NOT LINUX_DISTRIBUTION) if(EXISTS "/etc/redhat-release") set(LINUX_DISTRIBUTION "Red Hat") file(READ /etc/redhat-release LINUX_VER) endif() endif() if(NOT LINUX_DISTRIBUTION) if(EXISTS "/etc/debian_version") set(LINUX_DISTRIBUTION "Debian") file(READ /etc/debian_version LINUX_VER) endif() endif() if(NOT LINUX_DISTRIBUTION) set(LINUX_DISTRIBUTION "Generic") set(LINUX_VER "Unknown") endif() message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on ${LINUX_DISTRIBUTION} GNU/Linux Release ${LINUX_VER} ${ARCH_}") endif() if(NOT LINUX_DISTRIBUTION) set(LINUX_DISTRIBUTION "Unknown") endif() # Detect macOS / Mac OS X Version if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(OperatingSystem "Mac OS X") set(OS_IS_MACOSX TRUE) execute_process(COMMAND uname -v OUTPUT_VARIABLE DARWIN_VERSION) string(REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION}) if(${DARWIN_VERSION} MATCHES "19") set(MACOS_CATALINA TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on macOS Catalina 10.15") endif() if(${DARWIN_VERSION} MATCHES "18") set(MACOS_MOJAVE TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on macOS Mojave 10.14") endif() if(${DARWIN_VERSION} MATCHES "17") set(MACOS_HIGH_SIERRA TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on macOS High Sierra 10.13") endif() if(${DARWIN_VERSION} MATCHES "16") set(MACOS_SIERRA TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on macOS Sierra 10.12") endif() if(${DARWIN_VERSION} MATCHES "15") set(MACOSX_EL_CAPITAN TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.11 El Capitan") endif() if(${DARWIN_VERSION} MATCHES "14") set(MACOSX_YOSEMITE TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.10 Yosemite") endif() if(${DARWIN_VERSION} MATCHES "13") set(MACOSX_MAVERICKS TRUE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION="com.apple.compilers.llvm.clang.1_0") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.9 Mavericks") endif() if(${DARWIN_VERSION} MATCHES "12") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.8 Mountain Lion") endif() if(${DARWIN_VERSION} MATCHES "11") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.7 Lion") endif() if(${DARWIN_VERSION} MATCHES "10") message(STATUS "Configuring GNSS-SDR v${VERSION} to be built on Mac OS X 10.6 Snow Leopard") endif() endif() # Define extra build types and select Release by default to get optimization flags include(GnsssdrBuildTypes) # Available options: # - None: nothing set # - Debug: -O2 -g # - Release: -O3 # - RelWithDebInfo: -O3 -g # - MinSizeRel: -Os # - Coverage: -Wall -pedantic -pthread -g -O0 -fprofile-arcs -ftest-coverage # - NoOptWithASM: -O0 -g -save-temps # - O2WithASM: -O2 -g -save-temps # - O3WithASM: -O3 -g -save-temps # - ASAN: -Wall -Wextra -g -O2 -fsanitize=address -fno-omit-frame-pointer if(NOT CMAKE_BUILD_TYPE) if(ENABLE_GPERFTOOLS OR ENABLE_GPROF) set(CMAKE_BUILD_TYPE "RelWithDebInfo") message(STATUS "Build type not specified: defaulting to RelWithDebInfo.") else() set(CMAKE_BUILD_TYPE "Release") message(STATUS "Build type not specified: defaulting to Release.") endif() else() message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}.") endif() gnsssdr_check_build_type(${CMAKE_BUILD_TYPE}) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") # allow 'large' files in 32 bit builds if(UNIX) add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES) endif() # Determine if we are using make or ninja if(CMAKE_MAKE_PROGRAM MATCHES "make") set(CMAKE_MAKE_PROGRAM_PRETTY_NAME "make") endif() if(CMAKE_MAKE_PROGRAM MATCHES "ninja") set(CMAKE_MAKE_PROGRAM_PRETTY_NAME "ninja") endif() if(NOT CMAKE_MAKE_PROGRAM_PRETTY_NAME) set(CMAKE_MAKE_PROGRAM_PRETTY_NAME "${CMAKE_MAKE_PROGRAM}") endif() ################################################################################ # Minimum required versions ################################################################################ set(GNSSSDR_GCC_MIN_VERSION "4.7.2") set(GNSSSDR_CLANG_MIN_VERSION "3.4.0") set(GNSSSDR_APPLECLANG_MIN_VERSION "500") set(GNSSSDR_GNURADIO_MIN_VERSION "3.7.3") set(GNSSSDR_BOOST_MIN_VERSION "1.53") set(GNSSSDR_PYTHON_MIN_VERSION "2.7") set(GNSSSDR_PYTHON3_MIN_VERSION "3.4") set(GNSSSDR_MAKO_MIN_VERSION "0.4.2") set(GNSSSDR_ARMADILLO_MIN_VERSION "5.300.0") set(GNSSSDR_MATIO_MIN_VERSION "1.5.3") set(GNSSSDR_PROTOBUF_MIN_VERSION "3.0.0") ################################################################################ # Versions to download and build (but not installed) if not found ################################################################################ set(GNSSSDR_GFLAGS_LOCAL_VERSION "2.2.2") set(GNSSSDR_GLOG_LOCAL_VERSION "0.4.0") set(GNSSSDR_ARMADILLO_LOCAL_VERSION "9.600.x") set(GNSSSDR_GTEST_LOCAL_VERSION "1.8.1") set(GNSSSDR_GNSS_SIM_LOCAL_VERSION "master") set(GNSSSDR_GPSTK_LOCAL_VERSION "2.10.6") set(GNSSSDR_MATIO_LOCAL_VERSION "1.5.17") set(GNSSSDR_PUGIXML_LOCAL_VERSION "1.9") set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.9.0") if(CMAKE_VERSION VERSION_LESS "3.0.2") # Fix for CentOS 7 set(GNSSSDR_GFLAGS_LOCAL_VERSION "2.2.1") endif() ################################################################################ # Check compiler version ################################################################################ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNSSSDR_GCC_MIN_VERSION}) message(STATUS "Your GCC version is too old and does not support some C++ features required by GNSS-SDR. GCC version must be at least ${GNSSSDR_GCC_MIN_VERSION}") message(FATAL_ERROR "Fatal error: GCC >= ${GNSSSDR_GCC_MIN_VERSION} required.") endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") execute_process(COMMAND ${CMAKE_CXX_COMPILER} -v RESULT_VARIABLE _res ERROR_VARIABLE _err ERROR_STRIP_TRAILING_WHITESPACE ) if(${_res} STREQUAL "0") # output is in error stream string(REGEX MATCH "^Apple.*" IS_APPLE ${_err}) if("${IS_APPLE}" STREQUAL "") set(MIN_VERSION ${GNSSSDR_CLANG_MIN_VERSION}) set(APPLE_STR "") # retrieve the compiler's version from it string(REGEX MATCH "clang version [0-9.]+" CLANG_OTHER_VERSION ${_err}) string(REGEX MATCH "[0-9.]+" CLANG_VERSION ${CLANG_OTHER_VERSION}) else() set(MIN_VERSION ${GNSSSDR_APPLECLANG_MIN_VERSION}) set(APPLE_STR "Apple ") # retrieve the compiler's version from it string(REGEX MATCH "(clang-[0-9.]+)" CLANG_APPLE_VERSION ${_err}) string(REGEX MATCH "[0-9.]+" CLANG_VERSION ${CLANG_APPLE_VERSION}) endif() if(${CLANG_VERSION} VERSION_LESS "${MIN_VERSION}") message(WARNING "\nThe compiler selected to build GNSS-SDR (${APPLE_STR}Clang version ${CLANG_VERSION} : ${CMAKE_CXX_COMPILER}) is older than that officially supported (${MIN_VERSION} minimum). This build may or not work. We highly recommend using Apple Clang version ${APPLECLANG_MIN_VERSION} or more recent, or Clang version ${CLANG_MIN_VERSION} or more recent.") endif() else() message(WARNING "\nCannot determine the version of the compiler selected to build GNSS-SDR (${APPLE_STR}Clang : ${CMAKE_CXX_COMPILER}). This build may or not work. We highly recommend using Apple Clang version ${APPLECLANG_MIN_VERSION} or more recent, or Clang version ${CLANG_MIN_VERSION} or more recent.") endif() endif() ################################################################################ # Set minimal C and C++ standards ################################################################################ if(NOT (CMAKE_VERSION VERSION_LESS "3.1")) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS OFF) else() add_compile_options("$<$,C>:-std=gnu11>") if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.1.1") add_compile_options("$<$,CXX>:-std=c++11>") else() add_compile_options("$<$,CXX>:-std=c++14>") endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if(CLANG_VERSION VERSION_LESS "600") add_compile_options("$<$,CXX>:-std=c++11>") else() add_compile_options("$<$,CXX>:-std=c++14>") endif() else() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.5.0") add_compile_options("$<$,CXX>:-std=c++11>") else() add_compile_options("$<$,CXX>:-std=c++14>") endif() endif() endif() endif() # Visibility # See https://gcc.gnu.org/wiki/Visibility if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) else() if((CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) add_definitions(-fvisibility=hidden) endif() endif() ################################################################################ # Check if the compiler defines the architecture as ARM ################################################################################ if(NOT OS_IS_MACOSX) if(CMAKE_CROSSCOMPILING) set(IS_ARM TRUE) if(NOT CMAKE_NO_SYSTEM_FROM_IMPORTED) set(CMAKE_NO_SYSTEM_FROM_IMPORTED TRUE) endif() else() include(TestForARM) endif() endif() ################################################################################ # Find the POSIX thread (pthread) libraries ################################################################################ if(CMAKE_VERSION VERSION_LESS 3.1) # Workaround for CMake < 3.1 find_package(Threads REQUIRED) add_library(Threads::Threads SHARED IMPORTED) set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") set_property(TARGET Threads::Threads PROPERTY IMPORTED_LINK_INTERFACE_LANGUAGES "CXX") include(GNUInstallDirs) # Fix bug in Debian 8.11 if(${LINUX_DISTRIBUTION} MATCHES "Debian") if(${LINUX_VER} VERSION_LESS 8.12) if(ARCH_64BITS) set(FIX_PTHREADS_LOCATION "x86_64-linux-gnu/") endif() endif() endif() set_property(TARGET Threads::Threads PROPERTY IMPORTED_LOCATION /usr/${CMAKE_INSTALL_LIBDIR}/${FIX_PTHREADS_LOCATION}${CMAKE_FIND_LIBRARY_PREFIXES}pthread${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(CMAKE_THREAD_PREFER_PTHREAD TRUE) if(CMAKE_CROSSCOMPILING) set(THREADS_PREFER_PTHREAD_FLAG FALSE) else() set(THREADS_PREFER_PTHREAD_FLAG TRUE) endif() find_package(Threads REQUIRED) endif() set_package_properties(Threads PROPERTIES URL "https://computing.llnl.gov/tutorials/pthreads/" DESCRIPTION "Implements the POSIX Threads execution model" PURPOSE "Used to implement parallelism." TYPE REQUIRED ) ################################################################################ # Googletest - https://github.com/google/googletest ################################################################################ enable_testing() if(ENABLE_UNIT_TESTING OR ENABLE_SYSTEM_TESTING) if(NOT GTEST_DIR) if(DEFINED ENV{GTEST_DIR}) set(GTEST_DIR $ENV{GTEST_DIR}) message(STATUS "Googletest root folder set at ${GTEST_DIR}") endif() endif() endif() find_package(GOOGLETEST) set_package_properties(GOOGLETEST PROPERTIES PURPOSE "Used for Unit and System Tests." TYPE REQUIRED ) if(NOT GOOGLETEST_FOUND) set_package_properties(GOOGLETEST PROPERTIES PURPOSE "Googletest v${GNSSSDR_GTEST_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # GNU Radio - https://gnuradio.org ################################################################################ set(GR_REQUIRED_COMPONENTS RUNTIME PMT BLOCKS FFT FILTER ANALOG) find_package(UHD) set_package_properties(UHD PROPERTIES PURPOSE "Used for communication with front-ends of the USRP family." TYPE OPTIONAL ) if(ENABLE_UHD) if(NOT UHD_FOUND) set(ENABLE_UHD OFF) else() set(GR_REQUIRED_COMPONENTS ${GR_REQUIRED_COMPONENTS} UHD) endif() endif() find_package(GNURADIO) set_package_properties(GNURADIO PROPERTIES PURPOSE "Implements flowgraph scheduler, provides some processing blocks and classes to create new ones." TYPE REQUIRED ) ################################################################################ # Log4cpp - http://log4cpp.sourceforge.net/ ################################################################################ find_package(LOG4CPP) set_package_properties(LOG4CPP PROPERTIES PURPOSE "Required by GNU Radio." TYPE REQUIRED ) if(NOT LOG4CPP_FOUND) message(FATAL_ERROR "*** Log4cpp is required to build gnss-sdr") endif() ################################################################################ # Detect availability of std::filesystem and set C++ standard accordingly ################################################################################ set(FILESYSTEM_FOUND FALSE) if(NOT (GNURADIO_VERSION VERSION_LESS 3.8) AND LOG4CPP_READY_FOR_CXX17) # Check if we have std::filesystem if(NOT (CMAKE_VERSION VERSION_LESS 3.8)) if((NOT ENABLE_UNIT_TESTING_EXTRA) AND (NOT ENABLE_SYSTEM_TESTING_EXTRA) AND (NOT ENABLE_FPGA)) # Workaround for GPSTk find_package(FILESYSTEM COMPONENTS Final Experimental) set_package_properties(FILESYSTEM PROPERTIES URL "https://en.cppreference.com/w/cpp/filesystem" DESCRIPTION "Provides facilities for performing operations on file systems and their components" PURPOSE "Work with paths, regular files, and directories." TYPE OPTIONAL ) if(${FILESYSTEM_FOUND}) if(CMAKE_VERSION VERSION_LESS 3.12) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() endif() endif() endif() ################################################################################ # Boost - https://www.boost.org ################################################################################ if(UNIX AND EXISTS "/usr/lib64") list(APPEND BOOST_LIBRARYDIR "/usr/lib64") # Fedora 64-bit fix endif() set(Boost_ADDITIONAL_VERSIONS "1.53.0" "1.53" "1.54.0" "1.54" "1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59" "1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64" "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" "1.70.0" "1.70" "1.71.0" "1.71" "1.72.0" "1.72" "1.73.0" "1.73" "1.74.0" "1.74" "1.75.0" "1.75" "1.76.0" "1.76" "1.77.0" "1.77" "1.78.0" "1.78" "1.79.0" "1.79" "1.80.0" "1.80" "1.81.0" "1.81" "1.82.0" "1.82" "1.83.0" "1.83" "1.84.0" "1.84" ) set(Boost_USE_MULTITHREAD ON) set(Boost_USE_STATIC_LIBS OFF) set(BOOST_COMPONENTS atomic chrono date_time serialization system thread) if(NOT ${FILESYSTEM_FOUND}) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} filesystem) endif() find_package(Boost ${GNSSSDR_BOOST_MIN_VERSION} COMPONENTS ${BOOST_COMPONENTS} REQUIRED) if(NOT Boost_FOUND) message(FATAL_ERROR "Fatal error: Boost (version >=${GNSSSDR_BOOST_MIN_VERSION}) required.") endif() set_package_properties(Boost PROPERTIES URL "https://www.boost.org" PURPOSE "Used widely across the source code." TYPE REQUIRED ) if(CMAKE_VERSION VERSION_LESS 3.14) set(Boost_VERSION_STRING "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") endif() if(POLICY CMP0093) cmake_policy(SET CMP0093 NEW) # FindBoost reports Boost_VERSION in x.y.z format. endif() set_package_properties(Boost PROPERTIES DESCRIPTION "Portable C++ source libraries (found: v${Boost_VERSION_STRING})" ) if(CMAKE_VERSION VERSION_LESS 3.5) if(NOT TARGET Boost::boost) add_library(Boost::boost SHARED IMPORTED) # Trick for CMake 2.8.12 set_target_properties(Boost::boost PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} IMPORTED_LOCATION ${Boost_DATE_TIME_LIBRARIES} ) endif() if(NOT TARGET Boost::date_time) add_library(Boost::date_time SHARED IMPORTED) set_target_properties(Boost::date_time PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_DATE_TIME_LIBRARIES} IMPORTED_LOCATION ${Boost_DATE_TIME_LIBRARIES} ) endif() if(NOT TARGET Boost::system) add_library(Boost::system SHARED IMPORTED) set_target_properties(Boost::system PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_SYSTEM_LIBRARIES} IMPORTED_LOCATION ${Boost_SYSTEM_LIBRARIES} ) endif() if(NOT TARGET Boost::thread) add_library(Boost::thread SHARED IMPORTED) set_target_properties(Boost::thread PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_THREAD_LIBRARIES} IMPORTED_LOCATION ${Boost_THREAD_LIBRARIES} ) endif() if(NOT TARGET Boost::serialization) add_library(Boost::serialization SHARED IMPORTED) set_target_properties(Boost::serialization PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_SERIALIZATION_LIBRARIES} IMPORTED_LOCATION ${Boost_SERIALIZATION_LIBRARIES} ) endif() if(NOT TARGET Boost::chrono) add_library(Boost::chrono SHARED IMPORTED) set_target_properties(Boost::chrono PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_CHRONO_LIBRARIES} IMPORTED_LOCATION ${Boost_CHRONO_LIBRARIES} ) endif() if(NOT TARGET Boost::atomic) add_library(Boost::atomic SHARED IMPORTED) set_target_properties(Boost::atomic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_ATOMIC_LIBRARIES} IMPORTED_LOCATION ${Boost_ATOMIC_LIBRARIES} ) endif() if(NOT ${FILESYSTEM_FOUND}) if(NOT TARGET Boost::filesystem) add_library(Boost::filesystem SHARED IMPORTED) set_target_properties(Boost::filesystem PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR} INTERFACE_LINK_LIBRARIES ${Boost_FILESYSTEM_LIBRARIES} IMPORTED_LOCATION ${Boost_FILESYSTEM_LIBRARIES} ) endif() endif() endif() # Fix for Boost Asio < 1.70 when using Clang in macOS if(Boost_VERSION_STRING VERSION_LESS 1.70.0) # Check if we have std::string_view include(CheckCXXSourceCompiles) check_cxx_source_compiles(" #include  int main() { std::string_view sv; }" has_string_view ) endif() ################################################################################ # VOLK - Vector-Optimized Library of Kernels ################################################################################ find_package(VOLK) if(NOT VOLK_FOUND) message(FATAL_ERROR "*** VOLK is required to build gnss-sdr") endif() set_package_properties(VOLK PROPERTIES PURPOSE "Provides an abstraction of optimized math routines targeting several SIMD processors." TYPE REQUIRED ) ################################################################################ # volk_gnsssdr module - GNSS-SDR's own VOLK library ################################################################################ find_package(VOLKGNSSSDR) set_package_properties(VOLKGNSSSDR PROPERTIES PURPOSE "Accelerates math routines targeting several SIMD processors." TYPE REQUIRED ) if(NOT VOLKGNSSSDR_FOUND) message(STATUS " volk_gnsssdr will be built along with gnss-sdr when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'.") ############################### # Find Python required modules ############################### include(SetupPython) # sets PYTHON_EXECUTABLE and search for required modules if(NOT PYTHON_MIN_VER_FOUND) message(FATAL_ERROR "Python ${GNSSSDR_PYTHON_MIN_VERSION} or greater required to build VOLK_GNSSSDR") endif() if(${PYTHON3}) set(PYTHON_NAME "python3") else() set(PYTHON_NAME "python") endif() # Mako if(NOT MAKO_FOUND) message(STATUS "Mako template library not found. See http://www.makotemplates.org/") message(STATUS " You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(STATUS " sudo yum install ${PYTHON_NAME}-mako") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(STATUS " sudo zypper install ${PYTHON_NAME}-Mako") else() message(STATUS " sudo apt-get install ${PYTHON_NAME}-mako") endif() message(FATAL_ERROR "Mako templates required to build VOLK_GNSSSDR") endif() # Six if(NOT SIX_FOUND) message(STATUS "python-six not found. See https://pythonhosted.org/six/") message(STATUS " You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(STATUS " sudo yum install ${PYTHON_NAME}-six") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(STATUS " sudo zypper install ${PYTHON_NAME}-six") else() message(STATUS " sudo apt-get install ${PYTHON_NAME}-six") endif() message(FATAL_ERROR "six - python 2 and 3 compatibility library required to build VOLK_GNSSSDR") endif() if(CMAKE_VERSION VERSION_GREATER 3.12) set_package_properties(Python3 PROPERTIES URL "https://www.python.org/" PURPOSE "Required to build volk_gnsssdr." TYPE REQUIRED ) if(Python3_FOUND) set_package_properties(Python3 PROPERTIES DESCRIPTION "An interpreted, high-level, general-purpose programming language (found: v${Python3_VERSION})" ) else() set_package_properties(Python3 PROPERTIES DESCRIPTION "An interpreted, high-level, general-purpose programming language" PURPOSE "Another Python version will be used." ) endif() if(Python2_FOUND) set_package_properties(Python2 PROPERTIES URL "https://www.python.org/" DESCRIPTION "An interpreted, high-level, general-purpose programming language (found: v${Python2_VERSION})" PURPOSE "Required to build volk_gnsssdr." TYPE REQUIRED ) endif() endif() if(PYTHONINTERP_FOUND) set_package_properties(PythonInterp PROPERTIES URL "https://www.python.org/" DESCRIPTION "An interpreted, high-level, general-purpose programming language (found: v${PYTHON_VERSION_STRING})" PURPOSE "Required to build volk_gnsssdr." TYPE REQUIRED ) endif() if(ENABLE_PACKAGING) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) set(STRIP_VOLK_GNSSSDR_PROFILE "-DENABLE_STRIP=ON -DCMAKE_VERBOSE_MAKEFILE=ON") endif() endif() set(VOLK_GNSSSDR_BUILD_COMMAND "${CMAKE_MAKE_PROGRAM}") if(PYTHON_EXECUTABLE) set(USE_THIS_PYTHON "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}") endif() if(OS_IS_MACOSX) if(CMAKE_GENERATOR STREQUAL Xcode) set(VOLK_GNSSSDR_BUILD_COMMAND "xcodebuild" "-configuration" "Debug" "-target") endif() endif() if(CMAKE_CROSSCOMPILING) set(VOLK_GNSSSDR_COMPILER "") else() set(VOLK_GNSSSDR_COMPILER -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) endif() set(VOLK_GNSSSDR_CMAKE_ARGS ${VOLK_GNSSSDR_COMPILER} -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install -DENABLE_STATIC_LIBS=ON -DENABLE_PROFILING=${ENABLE_PROFILING} -DCMAKE_INCLUDE_PATH=${Boost_INCLUDE_DIR} -DENABLE_ORC=OFF ${STRIP_VOLK_GNSSSDR_PROFILE} ${USE_THIS_PYTHON} ) if(DEFINED ENV{OECORE_TARGET_SYSROOT}) set(VOLK_GNSSSDR_CMAKE_ARGS ${VOLK_GNSSSDR_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/Toolchains/oe-sdk_cross.cmake -DCROSSCOMPILE_MULTILIB=TRUE ) endif() if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(volk_gnsssdr_module PREFIX ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/build CMAKE_ARGS ${VOLK_GNSSSDR_CMAKE_ARGS} DOWNLOAD_COMMAND "" UPDATE_COMMAND "" PATCH_COMMAND "" BUILD_COMMAND ${VOLK_GNSSSDR_BUILD_COMMAND} volk_gnsssdr_profile INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install ) else() ExternalProject_Add(volk_gnsssdr_module PREFIX ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/build CMAKE_ARGS ${VOLK_GNSSSDR_CMAKE_ARGS} DOWNLOAD_COMMAND "" UPDATE_COMMAND "" PATCH_COMMAND "" BUILD_COMMAND ${VOLK_GNSSSDR_BUILD_COMMAND} volk_gnsssdr_profile BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/lib/${CMAKE_FIND_LIBRARY_PREFIXES}volk_gnsssdr${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/bin/volk_gnsssdr_profile INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install ) endif() find_package(ORC) set_package_properties(ORC PROPERTIES PURPOSE "Used by volk_gnsssdr." TYPE OPTIONAL ) if(NOT ORC_FOUND) set(ORC_LIBRARIES "") set(ORC_INCLUDE_DIRS "") endif() add_library(volk_gnsssdr UNKNOWN IMPORTED) set_property(TARGET volk_gnsssdr PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/lib/libvolk_gnsssdr${CMAKE_STATIC_LIBRARY_SUFFIX}) set(VOLK_GNSSSDR_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/build/include/;${CMAKE_CURRENT_SOURCE_DIR}/src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/include;${ORC_INCLUDE_DIRS}") set(VOLK_GNSSSDR_LIBRARIES volk_gnsssdr ${ORC_LIBRARIES}) if(NOT TARGET Volkgnsssdr::volkgnsssdr) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/build/include) add_library(Volkgnsssdr::volkgnsssdr STATIC IMPORTED) add_dependencies(Volkgnsssdr::volkgnsssdr volk_gnsssdr_module) set_target_properties(Volkgnsssdr::volkgnsssdr PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/lib/libvolk_gnsssdr${CMAKE_STATIC_LIBRARY_SUFFIX}" INCLUDE_DIRECTORIES "${VOLK_GNSSSDR_INCLUDE_DIRS}" INTERFACE_INCLUDE_DIRECTORIES "${VOLK_GNSSSDR_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${VOLK_GNSSSDR_LIBRARIES}" ) endif() if(CMAKE_VERSION VERSION_LESS 3.2) add_custom_command(TARGET volk_gnsssdr_module POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/bin/volk_gnsssdr_profile ${CMAKE_SOURCE_DIR}/install/volk_gnsssdr_profile ) else() add_custom_command(TARGET volk_gnsssdr_module POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/bin/volk_gnsssdr_profile ${CMAKE_SOURCE_DIR}/install/volk_gnsssdr_profile BYPRODUCTS ${CMAKE_SOURCE_DIR}/install/volk_gnsssdr_profile ) endif() add_custom_command(TARGET volk_gnsssdr_module POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr_module/install/bin/volk_gnsssdr-config-info ${CMAKE_SOURCE_DIR}/install/volk_gnsssdr-config-info ) set_package_properties(VOLKGNSSSDR PROPERTIES PURPOSE "volk_gnsssdr will be built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # gflags - https://github.com/gflags/gflags ################################################################################ set(LOCAL_GFLAGS false) find_package(GFLAGS) set_package_properties(GFLAGS PROPERTIES PURPOSE "Used for commandline flags management." TYPE REQUIRED ) if(NOT GFLAGS_FOUND) message(STATUS " gflags library has not been found.") message(STATUS " gflags v${GNSSSDR_GFLAGS_LOCAL_VERSION} will be downloaded and built automatically") message(STATUS " when doing 'make'.") if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} GIT_REPOSITORY git://github.com/gflags/gflags.git GIT_TAG v${GNSSSDR_GFLAGS_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/gflags/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} CMAKE_ARGS -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_gflags_nothreads_LIB=OFF -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} UPDATE_COMMAND "" PATCH_COMMAND "" INSTALL_COMMAND "" ) else() ExternalProject_Add(gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} GIT_REPOSITORY git://github.com/gflags/gflags.git GIT_TAG v${GNSSSDR_GFLAGS_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/gflags/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} CMAKE_ARGS -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_gflags_nothreads_LIB=OFF -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gflags${CMAKE_STATIC_LIBRARY_SUFFIX} UPDATE_COMMAND "" PATCH_COMMAND "" INSTALL_COMMAND "" ) endif() set(GFlags_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/include CACHE PATH "Local Gflags headers" ) file(GLOB GFlags_SHARED_LIBS "${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gflags${CMAKE_SHARED_LIBRARY_SUFFIX}*" ) if(NOT TARGET Gflags::gflags) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/include) add_library(Gflags::gflags STATIC IMPORTED) add_dependencies(Gflags::gflags gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}) set_target_properties(Gflags::gflags PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gflags${CMAKE_STATIC_LIBRARY_SUFFIX}" INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/include" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/include" INTERFACE_LINK_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gflags${CMAKE_STATIC_LIBRARY_SUFFIX}" ) endif() set(LOCAL_GFLAGS true CACHE STRING "GFlags downloaded and built automatically" FORCE) set_package_properties(GFLAGS PROPERTIES PURPOSE "Gflags v${GNSSSDR_GFLAGS_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # glog - https://github.com/google/glog ################################################################################ if(NOT ${ENABLE_OWN_GLOG}) find_package(GLOG) if(GLOG_INCLUDE_DIRS) set(GLOG_FOUND ON) endif() endif() set_package_properties(GLOG PROPERTIES PURPOSE "Used for runtime internal logging." TYPE REQUIRED ) if(NOT GLOG_FOUND OR ${LOCAL_GFLAGS}) message(STATUS " glog library has not been found") if(NOT GFLAGS_FOUND) message(STATUS " or it is likely not linked to gflags.") endif() message(STATUS " glog v${GNSSSDR_GLOG_LOCAL_VERSION} will be downloaded and built automatically") message(STATUS " when doing 'make'.") if(NOT ${LOCAL_GFLAGS}) add_library(gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} UNKNOWN IMPORTED) set_property(TARGET gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION} PROPERTY IMPORTED_LOCATION "${GFlags_LIBS}") endif() set(TARGET_GFLAGS gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}) if(${LOCAL_GFLAGS}) set(GFLAGS_LIBRARIES_TO_LINK ${GFlags_SHARED_LIBS}) set(GFLAGS_LIBRARY_DIR_TO_LINK ${CMAKE_CURRENT_BINARY_DIR}/gflags-${GNSSSDR_GFLAGS_LOCAL_VERSION}/lib) else() set(GFLAGS_LIBRARIES_TO_LINK ${GFlags_LIBS}) set(GFLAGS_LIBRARY_DIR_TO_LINK ${GFlags_LIBRARY_DIRS}) endif() if(OS_IS_MACOSX) set(GFLAGS_LIBRARIES_TO_LINK "${GFLAGS_LIBRARIES_TO_LINK} -lc++") set(GLOG_EXPORT_CXX_LIBRARIES "export CXXFLAGS=\"-stdlib=libc++\"") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(GLOG_EXPORT_C_COMPILER "export CC=clang") set(GLOG_EXPORT_CXX_COMPILER "export CXX=clang++") endif() file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/tmp/configure_with_gflags "#!/bin/sh export CPPFLAGS=-I${GFlags_INCLUDE_DIRS} export LDFLAGS=-L${GFLAGS_LIBRARY_DIR_TO_LINK} export LIBS=\"${GFLAGS_LIBRARIES_TO_LINK}\" ${GLOG_EXPORT_CXX_LIBRARIES} ${GLOG_EXPORT_C_COMPILER} ${GLOG_EXPORT_CXX_COMPILER} cd ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/ aclocal automake --add-missing autoreconf -vfi cd ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/configure" ) file(COPY ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/tmp/configure_with_gflags DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) set(GLOG_CONFIGURE ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/configure_with_gflags) # Ensure that aclocal and libtool are present if(OS_IS_LINUX) if(EXISTS "/usr/bin/libtoolize") if(EXISTS "/usr/bin/aclocal" OR EXISTS "/usr/bin/aclocal-1.16" OR EXISTS "/usr/bin/aclocal-1.15" OR EXISTS "/usr/bin/aclocal-1.14" OR EXISTS "/usr/bin/aclocal-1.13" OR EXISTS "/usr/bin/aclocal-1.11" OR EXISTS "/usr/bin/aclocal-1.10") # Everything ok, we can move on else() message(" aclocal has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install automake") else() message(" sudo apt-get install automake") endif() message(FATAL_ERROR "aclocal is required to build glog from source") endif() else() message(" libtool has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install libtoool") else() message(" sudo apt-get install libtool") endif() message(FATAL_ERROR "libtool is required to build glog from source") endif() endif() set(GLOG_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM}) if(GLOG_MAKE_PROGRAM MATCHES "ninja") find_program(GLOG_MAKE_EXECUTABLE make HINTS /usr/bin/ /usr/local/bin/ ) if(NOT GLOG_MAKE_EXECUTABLE) message(FATAL_ERROR "make is required to build Glog from source.") endif() set(GLOG_MAKE_PROGRAM ${GLOG_MAKE_EXECUTABLE}) endif() if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(glog-${GNSSSDR_GLOG_LOCAL_VERSION} DEPENDS ${TARGET_GFLAGS} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} GIT_REPOSITORY https://github.com/google/glog/ GIT_TAG v${GNSSSDR_GLOG_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} CONFIGURE_COMMAND ${GLOG_CONFIGURE} --prefix= BUILD_COMMAND "${GLOG_MAKE_PROGRAM}" UPDATE_COMMAND "" PATCH_COMMAND "" INSTALL_COMMAND "" ) else() ExternalProject_Add(glog-${GNSSSDR_GLOG_LOCAL_VERSION} DEPENDS ${TARGET_GFLAGS} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} GIT_REPOSITORY https://github.com/google/glog/ GIT_TAG v${GNSSSDR_GLOG_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION} CONFIGURE_COMMAND ${GLOG_CONFIGURE} --prefix= BUILD_COMMAND "${GLOG_MAKE_PROGRAM}" BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/.libs/${CMAKE_FIND_LIBRARY_PREFIXES}glog${CMAKE_STATIC_LIBRARY_SUFFIX} UPDATE_COMMAND "" PATCH_COMMAND "" INSTALL_COMMAND "" ) endif() add_dependencies(glog-${GNSSSDR_GLOG_LOCAL_VERSION} Gflags::gflags) # Set up variables set(GLOG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/src/ ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/src ) set(GLOG_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/.libs/${CMAKE_FIND_LIBRARY_PREFIXES}glog${CMAKE_STATIC_LIBRARY_SUFFIX} ) # Create Glog::glog target if(NOT TARGET Glog::glog) file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glog/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/src) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/src) add_library(Glog::glog STATIC IMPORTED) add_dependencies(Glog::glog glog-${GNSSSDR_GLOG_LOCAL_VERSION}) set_target_properties(Glog::glog PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/.libs/${CMAKE_FIND_LIBRARY_PREFIXES}glog${CMAKE_STATIC_LIBRARY_SUFFIX}" INCLUDE_DIRECTORIES "${GLOG_INCLUDE_DIRS}" INTERFACE_INCLUDE_DIRECTORIES "${GLOG_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/glog-${GNSSSDR_GLOG_LOCAL_VERSION}/.libs/${CMAKE_FIND_LIBRARY_PREFIXES}glog${CMAKE_STATIC_LIBRARY_SUFFIX}" ) endif() set(LOCAL_GLOG true CACHE STRING "Glog downloaded and built automatically" FORCE) set_package_properties(GLOG PROPERTIES PURPOSE "Glog v${GNSSSDR_GLOG_LOCAL_VERSION} will be downloaded and built when doing 'make'." ) endif() if(NOT ENABLE_LOG) message(STATUS "Logging is not enabled") endif() ################################################################################ # Check that BLAS (Basic Linear Algebra Subprograms) is found in the system # See http://www.netlib.org/blas/ ################################################################################ if(OS_IS_MACOSX) # Avoid using the implementation that comes with the Accelerate framework include(AvoidAccelerate) else() if(NOT BLA_VENDOR) set(BLA_VENDOR "Generic") endif() find_package(BLAS) set_package_properties(BLAS PROPERTIES URL "http://www.netlib.org/blas/" DESCRIPTION "Basic Linear Algebra Subprograms" PURPOSE "Used for matrix algebra computations." TYPE REQUIRED ) endif() if(NOT BLAS_FOUND) message(" The BLAS library has not been found.") message(" You can try to install it by typing:") if(OS_IS_MACOSX) message(" 'sudo port install lapack' if you are using Macports, or") message(" 'brew install lapack' if you are using Homebrew.") else() if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum install blas-devel") else() message(" sudo apt-get install libblas-dev") endif() endif() message(FATAL_ERROR "BLAS is required to build gnss-sdr") endif() ################################################################################ # Check that LAPACK (Linear Algebra PACKage) is found in the system # See http://www.netlib.org/lapack/ ################################################################################ if(NOT OS_IS_MACOSX) find_package(LAPACK) set_package_properties(LAPACK PROPERTIES URL "http://www.netlib.org/lapack/" DESCRIPTION "Linear Algebra PACKage" PURPOSE "Used for matrix algebra computations." TYPE REQUIRED ) endif() if(NOT LAPACK_FOUND) message(" The LAPACK library has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum install lapack-devel") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install lapack-devel") else() message(" sudo apt-get install liblapack-dev") endif() message(FATAL_ERROR "LAPACK is required to build gnss-sdr") endif() ################################################################################ # Armadillo - http://arma.sourceforge.net/ ################################################################################ find_package(Armadillo) set_package_properties(Armadillo PROPERTIES URL "http://arma.sourceforge.net/" PURPOSE "Used for matrix computations." TYPE REQUIRED ) if(ARMADILLO_FOUND) set_package_properties(Armadillo PROPERTIES DESCRIPTION "C++ library for linear algebra and scientific computing (found: v${ARMADILLO_VERSION_STRING})" ) if(${ARMADILLO_VERSION_STRING} VERSION_LESS ${GNSSSDR_ARMADILLO_MIN_VERSION}) set(ARMADILLO_FOUND false) set(ENABLE_OWN_ARMADILLO true) else() add_library(Armadillo::armadillo SHARED IMPORTED) set_target_properties(Armadillo::armadillo PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${ARMADILLO_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${ARMADILLO_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${ARMADILLO_LIBRARIES}" ) endif() endif() if(NOT ARMADILLO_FOUND OR ENABLE_OWN_ARMADILLO) message(STATUS " Armadillo has not been found.") message(STATUS " Armadillo ${GNSSSDR_ARMADILLO_LOCAL_VERSION} will be downloaded and built automatically") message(STATUS " when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'.") set(armadillo_BRANCH ${GNSSSDR_ARMADILLO_LOCAL_VERSION}) set(armadillo_RELEASE ${armadillo_BRANCH}) ############################################# # Check if GFORTRAN is found in the system ############################################# if(NOT OS_IS_MACOSX) find_package(GFORTRAN) set_package_properties(GFORTRAN PROPERTIES PURPOSE "Required by Armadillo." TYPE REQUIRED ) if(NOT GFORTRAN) message(STATUS "The gfortran library has not been found.") message(STATUS " You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(STATUS " sudo yum install gcc-fortran") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(STATUS " sudo zypper install gcc-fortran") else() message(STATUS " sudo apt-get install gfortran") endif() message(FATAL_ERROR "gfortran is required to build gnss-sdr") endif() endif() ############################################# # Download and build Armadillo ############################################# if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(armadillo-${armadillo_RELEASE} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/armadillo-${armadillo_RELEASE} GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git GIT_TAG ${armadillo_BRANCH} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/armadillo/armadillo-${armadillo_RELEASE} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/armadillo-${armadillo_RELEASE} CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_FLAGS=-std=c++11 BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} UPDATE_COMMAND "" INSTALL_COMMAND "" ) else() ExternalProject_Add(armadillo-${armadillo_RELEASE} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/armadillo-${armadillo_RELEASE} GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git GIT_TAG ${armadillo_BRANCH} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/armadillo/armadillo-${armadillo_RELEASE} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/armadillo-${armadillo_RELEASE} CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_FLAGS=-std=c++11 BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/armadillo-${armadillo_RELEASE}/${CMAKE_FIND_LIBRARY_PREFIXES}armadillo${CMAKE_STATIC_LIBRARY_SUFFIX} UPDATE_COMMAND "" INSTALL_COMMAND "" ) endif() # Create imported target ExternalProject_Get_Property(armadillo-${armadillo_RELEASE} binary_dir) if(NOT GFORTRAN) set(GFORTRAN "") endif() set(ARMADILLO_LIBRARIES ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES} ${GFORTRAN} ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}armadillo${CMAKE_STATIC_LIBRARY_SUFFIX}) set(LOCAL_ARMADILLO true CACHE STRING "Armadillo downloaded and built automatically" FORCE) set(ARMADILLO_VERSION_STRING ${armadillo_RELEASE}) file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/armadillo/armadillo-${armadillo_RELEASE}/include) add_library(Armadillo::armadillo STATIC IMPORTED) add_dependencies(Armadillo::armadillo armadillo-${armadillo_RELEASE}) set_target_properties(Armadillo::armadillo PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}armadillo${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/armadillo/armadillo-${armadillo_RELEASE}/include" INTERFACE_LINK_LIBRARIES "${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}armadillo${CMAKE_STATIC_LIBRARY_SUFFIX}" ) set_package_properties(Armadillo PROPERTIES DESCRIPTION "C++ library for linear algebra and scientific computing" PURPOSE "Armadillo ${GNSSSDR_ARMADILLO_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # GnuTLS - https://www.gnutls.org/ ################################################################################ find_package(GnuTLS) set_package_properties(GnuTLS PROPERTIES URL "https://www.gnutls.org/" PURPOSE "Used for the SUPL protocol implementation." TYPE REQUIRED ) if(GnuTLS_FOUND AND GNUTLS_VERSION_STRING) set_package_properties(GnuTLS PROPERTIES DESCRIPTION "Transport Layer Security Library (found: v${GNUTLS_VERSION_STRING})" ) else() set_package_properties(GnuTLS PROPERTIES DESCRIPTION "Transport Layer Security Library" ) endif() find_library(GNUTLS_OPENSSL_LIBRARY NAMES gnutls-openssl libgnutls-openssl.so.27 HINTS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/aarch64-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/i386-linux-gnu /usr/lib/alpha-linux-gnu /usr/lib/hppa-linux-gnu /usr/lib/i386-gnu /usr/lib/i686-gnu /usr/lib/i686-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i686-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/mipsel-linux-gnu /usr/lib/powerpc-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/sh4-linux-gnu ) if(NOT GNUTLS_OPENSSL_LIBRARY) if(GnuTLS_FOUND) message(STATUS " But it was not built with openssl compatibility.") endif() message(STATUS " Looking for OpenSSL instead...") if(OS_IS_MACOSX) set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) # Trick for Homebrew endif() find_package(OpenSSL) set_package_properties(OpenSSL PROPERTIES URL "https://www.openssl.org" DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})" PURPOSE "Used for the SUPL protocol implementation." TYPE REQUIRED ) if(OPENSSL_FOUND) set_package_properties(GnuTLS PROPERTIES PURPOSE "Not found, but OpenSSL can replace it." ) set(GNUTLS_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) set(GNUTLS_LIBRARIES "") set(GNUTLS_OPENSSL_LIBRARY ${OPENSSL_SSL_LIBRARY}) else() message(" The GnuTLS library with openssl compatibility enabled has not been found.") message(" You can try to install the required libraries by typing:") if(OS_IS_LINUX) if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum install openssl-devel") else() message(" sudo apt-get install libgnutls28-dev") endif() endif() if(OS_IS_MACOSX) message(" 'sudo port install gnutls', if you are using Macports, or") message(" 'brew install openssl', if you are using Homebrew.") endif() message(FATAL_ERROR "GnuTLS libraries with openssl compatibility are required to build gnss-sdr") endif() endif() ################################################################################ # Matio - https://github.com/tbeu/matio ################################################################################ find_package(MATIO) set_package_properties(MATIO PROPERTIES PURPOSE "Used to store processing block's results in MAT files readable from MATLAB/Octave." TYPE REQUIRED ) if(NOT MATIO_FOUND OR MATIO_VERSION_STRING VERSION_LESS ${GNSSSDR_MATIO_MIN_VERSION}) if(MATIO_FOUND) message(STATUS " Matio installed version (${MATIO_VERSION_STRING}) is too old (>= ${GNSSSDR_MATIO_MIN_VERSION} is required).") endif() message(STATUS " Matio v${GNSSSDR_MATIO_LOCAL_VERSION} will be downloaded and built automatically") message(STATUS " when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'.") find_package(ZLIB) set_package_properties(ZLIB PROPERTIES URL "https://www.zlib.net/" PURPOSE "Required to build Matio." TYPE REQUIRED ) if(ZLIB_FOUND AND ZLIB_VERSION_STRING) set_package_properties(ZLIB PROPERTIES DESCRIPTION "A Massively Spiffy Yet Delicately Unobtrusive Compression Library (found: v${ZLIB_VERSION_STRING})" ) else() set_package_properties(ZLIB PROPERTIES DESCRIPTION "A Massively Spiffy Yet Delicately Unobtrusive Compression Library" ) endif() if(ZLIB_FOUND) get_filename_component(ZLIB_BASE_DIR ${ZLIB_INCLUDE_DIRS} DIRECTORY) if(OS_IS_LINUX) if(NOT EXISTS "/usr/bin/libtoolize") message(" libtool has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install libtoool") else() message(" sudo apt-get install libtool") endif() message(FATAL_ERROR "libtool is required to build matio from source") endif() if(EXISTS "/usr/bin/aclocal" OR EXISTS "/usr/bin/aclocal-1.16" OR EXISTS "/usr/bin/aclocal-1.15" OR EXISTS "/usr/bin/aclocal-1.14" OR EXISTS "/usr/bin/aclocal-1.13" OR EXISTS "/usr/bin/aclocal-1.11" OR EXISTS "/usr/bin/aclocal-1.10") message(STATUS "Automake found.") else() message(" aclocal has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install automake") else() message(" sudo apt-get install automake") endif() message(FATAL_ERROR "aclocal is required to build matio from source") endif() endif() find_package(HDF5) set_package_properties(HDF5 PROPERTIES URL "https://support.hdfgroup.org/HDF5/" PURPOSE "Required to build Matio." TYPE REQUIRED ) if(HDF5_FOUND AND HDF5_VERSION) set_package_properties(HDF5 PROPERTIES DESCRIPTION "A versatile data model, a portable file format and a software library (found: v${HDF5_VERSION})" ) else() set_package_properties(HDF5 PROPERTIES DESCRIPTION "A versatile data model, a portable file format and a software library" ) endif() if(HDF5_FOUND) list(GET HDF5_LIBRARIES 0 HDF5_FIRST_DIR) get_filename_component(HDF5_BASE_DIR2 ${HDF5_FIRST_DIR} DIRECTORY) get_filename_component(HDF5_BASE_DIR ${HDF5_BASE_DIR2} DIRECTORY) if(OS_IS_MACOSX) if(EXISTS /opt/local/include/hdf5.h) set(HDF5_BASE_DIR /opt/local) endif() if(EXISTS /usr/local/include/hdf5.h) set(HDF5_BASE_DIR /usr/local) endif() endif() set(MATIO_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM}) if(MATIO_MAKE_PROGRAM MATCHES "ninja") find_program(MATIO_MAKE_EXECUTABLE make HINTS /usr/bin/ /usr/local/bin/ ) if(NOT MATIO_MAKE_EXECUTABLE) message(FATAL_ERROR "make is required to build Matio from source.") endif() set(MATIO_MAKE_PROGRAM ${MATIO_MAKE_EXECUTABLE}) endif() if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(matio-${GNSSSDR_MATIO_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/matio GIT_REPOSITORY https://github.com/tbeu/matio GIT_TAG v${GNSSSDR_MATIO_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION} UPDATE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION}/autogen.sh CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION}/configure --with-hdf5=${HDF5_BASE_DIR} --with-zlib=${ZLIB_BASE_DIR} --with-default-file-ver=7.3 --enable-mat73=yes --prefix= BUILD_COMMAND ${MATIO_MAKE_PROGRAM} ) else() ExternalProject_Add(matio-${GNSSSDR_MATIO_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/matio GIT_REPOSITORY https://github.com/tbeu/matio GIT_TAG v${GNSSSDR_MATIO_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION} UPDATE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION}/autogen.sh CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/matio/matio-${GNSSSDR_MATIO_LOCAL_VERSION}/configure --with-hdf5=${HDF5_BASE_DIR} --with-zlib=${ZLIB_BASE_DIR} --with-default-file-ver=7.3 --enable-mat73=yes --prefix= BUILD_COMMAND ${MATIO_MAKE_PROGRAM} BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/matio/lib/${CMAKE_FIND_LIBRARY_PREFIXES}matio${CMAKE_SHARED_LIBRARY_SUFFIX} ) endif() set(MATIO_LOCAL true) if(NOT TARGET Matio::matio) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/matio/include) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/matio/lib) add_library(Matio::matio SHARED IMPORTED) add_dependencies(Matio::matio matio-${GNSSSDR_MATIO_LOCAL_VERSION}) set_target_properties(Matio::matio PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/matio/lib/${CMAKE_FIND_LIBRARY_PREFIXES}matio${CMAKE_SHARED_LIBRARY_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/matio/include INTERFACE_LINK_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/matio/lib/${CMAKE_FIND_LIBRARY_PREFIXES}matio${CMAKE_SHARED_LIBRARY_SUFFIX} ) endif() else() message(STATUS " The hdf5 library has not been found in your system.") message(STATUS " Please try to install it by doing:") if(OS_IS_MACOSX) message(STATUS " $ sudo port install hdf5") message(STATUS " or") message(STATUS " $ brew install hdf5") endif() if(OS_IS_LINUX) message(STATUS " $ sudo apt-get install libhdf5-dev") endif() message(FATAL_ERROR "*** The hdf5 library is required to build gnss-sdr") endif() else() message(FATAL_ERROR "*** The zlib library is required to build gnss-sdr") endif() set_package_properties(MATIO PROPERTIES PURPOSE "Matio v${GNSSSDR_MATIO_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # PugiXML - https://pugixml.org/ ################################################################################ find_package(PUGIXML) set_package_properties(PUGIXML PROPERTIES PURPOSE "Used to handle Galileo almanac XML files published by the European GNSS Service Centre." TYPE REQUIRED ) if(NOT PUGIXML_FOUND) message(STATUS " PugiXML v${GNSSSDR_PUGIXML_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'.") set(PUGIXML_COMPILER -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) set(TOOLCHAIN_ARG "") if(DEFINED ENV{OECORE_TARGET_SYSROOT}) set(PUGIXML_COMPILER "") set(TOOLCHAIN_ARG "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/Toolchains/oe-sdk_cross.cmake") endif() if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} GIT_REPOSITORY https://github.com/zeux/pugixml GIT_TAG v${GNSSSDR_PUGIXML_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/pugixml/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} CMAKE_ARGS ${PUGIXML_COMPILER} ${TOOLCHAIN_ARG} UPDATE_COMMAND "" PATCH_COMMAND "" INSTALL_COMMAND "" ) else() ExternalProject_Add(pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} GIT_REPOSITORY https://github.com/zeux/pugixml GIT_TAG v${GNSSSDR_PUGIXML_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/pugixml/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION} CMAKE_ARGS ${PUGIXML_COMPILER} ${TOOLCHAIN_ARG} UPDATE_COMMAND "" PATCH_COMMAND "" BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}/${CMAKE_FIND_LIBRARY_PREFIXES}pugixml${CMAKE_STATIC_LIBRARY_SUFFIX} INSTALL_COMMAND "" ) endif() if(NOT TARGET Pugixml::pugixml) file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/pugixml/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}/src) add_library(Pugixml::pugixml STATIC IMPORTED) add_dependencies(Pugixml::pugixml pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}) set_target_properties(Pugixml::pugixml PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}/${CMAKE_FIND_LIBRARY_PREFIXES}pugixml${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/pugixml/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}/src" INTERFACE_LINK_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/pugixml-${GNSSSDR_PUGIXML_LOCAL_VERSION}/${CMAKE_FIND_LIBRARY_PREFIXES}pugixml${CMAKE_STATIC_LIBRARY_SUFFIX}" ) endif() set_package_properties(PUGIXML PROPERTIES PURPOSE "PugiXML v${GNSSSDR_PUGIXML_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() ################################################################################ # Protocol Buffers https://github.com/protocolbuffers/protobuf ################################################################################ find_package(Protobuf) set_package_properties(Protobuf PROPERTIES URL "https://developers.google.com/protocol-buffers/" PURPOSE "Used to serialize output data in a way that can be read by other applications." TYPE REQUIRED ) if(NOT Protobuf_VERSION) set(Protobuf_VERSION "0.0.0") endif() if(CMAKE_VERSION VERSION_LESS 3.6) if(PROTOBUF_FOUND) set(Protobuf_FOUND ${PROTOBUF_FOUND}) endif() endif() if(Protobuf_FOUND AND CMAKE_VERSION VERSION_LESS 3.9) if(PROTOBUF_INCLUDE_DIR) set(Protobuf_INCLUDE_DIR ${PROTOBUF_INCLUDE_DIR}) endif() if(PROTOBUF_LIBRARY) set(Protobuf_LIBRARY ${PROTOBUF_LIBRARY}) endif() if(PROTOBUF_PROTOC_EXECUTABLE) set(Protobuf_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE}) endif() if(PROTOBUF_PROTOC_LIBRARY) set(Protobuf_PROTOC_LIBRARY ${PROTOBUF_PROTOC_EXECUTABLE}) endif() add_library(protobuf::libprotobuf SHARED IMPORTED) set_target_properties(protobuf::libprotobuf PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${Protobuf_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${Protobuf_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${Protobuf_LIBRARY}" ) add_executable(protobuf::protoc IMPORTED) set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${Protobuf_PROTOC_EXECUTABLE}" INTERFACE_LINK_LIBRARIES "${Protobuf_PROTOC_LIBRARY}" ) if(${Protobuf_VERSION} VERSION_EQUAL "0.0.0") set(_PROTOBUF_COMMON_HEADER ${Protobuf_INCLUDE_DIR}/google/protobuf/stubs/common.h) set(Protobuf_VERSION "") set(Protobuf_LIB_VERSION "") file(STRINGS ${_PROTOBUF_COMMON_HEADER} _PROTOBUF_COMMON_H_CONTENTS REGEX "#define[ \t]+GOOGLE_PROTOBUF_VERSION[ \t]+") if(_PROTOBUF_COMMON_H_CONTENTS MATCHES "#define[ \t]+GOOGLE_PROTOBUF_VERSION[ \t]+([0-9]+)") set(Protobuf_LIB_VERSION "${CMAKE_MATCH_1}") endif() unset(_PROTOBUF_COMMON_H_CONTENTS) math(EXPR _PROTOBUF_MAJOR_VERSION "${Protobuf_LIB_VERSION} / 1000000") math(EXPR _PROTOBUF_MINOR_VERSION "${Protobuf_LIB_VERSION} / 1000 % 1000") math(EXPR _PROTOBUF_SUBMINOR_VERSION "${Protobuf_LIB_VERSION} % 1000") set(Protobuf_VERSION "${_PROTOBUF_MAJOR_VERSION}.${_PROTOBUF_MINOR_VERSION}.${_PROTOBUF_SUBMINOR_VERSION}") endif() endif() if(Protobuf_FOUND) set_package_properties(Protobuf PROPERTIES DESCRIPTION "Structured data serialization mechanism (found: v${Protobuf_VERSION})" ) else() set_package_properties(Protobuf PROPERTIES DESCRIPTION "Structured data serialization mechanism" ) endif() if(Protobuf_FOUND AND CMAKE_CROSSCOMPILING) find_program(PROTOC_EXECUTABLE protoc) if(NOT PROTOC_EXECUTABLE) find_program(PROTOC_EXECUTABLE protoc HINTS /usr/bin/ /usr/local/bin/ ) endif() if(PROTOC_EXECUTABLE) set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION ${PROTOC_EXECUTABLE} ) set(Protobuf_PROTOC_EXECUTABLE ${PROTOC_EXECUTABLE}) else() message(FATAL_ERROR "Please install the Protocol Buffers compiler v${Protobuf_VERSION} in the host machine") endif() endif() if((NOT Protobuf_FOUND) OR (NOT Protobuf_PROTOC_EXECUTABLE) OR (${Protobuf_VERSION} VERSION_LESS ${GNSSSDR_PROTOBUF_MIN_VERSION})) unset(Protobuf_PROTOC_EXECUTABLE) if(CMAKE_CROSSCOMPILING) if(NOT Protobuf_FOUND) ExternalProject_Add(protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} GIT_REPOSITORY https://github.com/protocolbuffers/protobuf GIT_TAG v${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} UPDATE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/autogen.sh CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} --host=$ENV{OECORE_TARGET_ARCH} --with-protoc=${PROTOC_EXECUTABLE}" BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} INSTALL_COMMAND ${CMAKE_MAKE_PROGRAM} install BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}protobuf${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/bin/protoc ) endif() else() if(OS_IS_LINUX) if(NOT EXISTS "/usr/bin/libtoolize") message(" libtool has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install libtoool") else() message(" sudo apt-get install libtool") endif() message(FATAL_ERROR "libtool is required to build Protocol Buffers from source") endif() if(EXISTS "/usr/bin/aclocal" OR EXISTS "/usr/bin/aclocal-1.16" OR EXISTS "/usr/bin/aclocal-1.15" OR EXISTS "/usr/bin/aclocal-1.14" OR EXISTS "/usr/bin/aclocal-1.13" OR EXISTS "/usr/bin/aclocal-1.11" OR EXISTS "/usr/bin/aclocal-1.10") message(STATUS "Automake found.") else() message(" aclocal has not been found.") message(" You can try to install it by typing:") if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(" sudo yum groupinstall 'Development Tools'") elseif(${LINUX_DISTRIBUTION} MATCHES "openSUSE") message(" sudo zypper install automake") else() message(" sudo apt-get install automake") endif() message(FATAL_ERROR "aclocal is required to build Protocol Buffers from source") endif() endif() set(PROTOBUF_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM}) if(PROTOBUF_MAKE_PROGRAM MATCHES "ninja") find_program(MAKE_EXECUTABLE make HINTS /usr/bin/ /usr/local/bin/ ) if(NOT MAKE_EXECUTABLE) message(FATAL_ERROR "make is required to build Protocol Buffers from source.") endif() set(PROTOBUF_MAKE_PROGRAM ${MAKE_EXECUTABLE}) endif() if(CMAKE_VERSION VERSION_LESS 3.2) ExternalProject_Add(protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} GIT_REPOSITORY https://github.com/protocolbuffers/protobuf GIT_TAG v${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} UPDATE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/autogen.sh CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} BUILD_COMMAND ${PROTOBUF_MAKE_PROGRAM} INSTALL_COMMAND ${PROTOBUF_MAKE_PROGRAM} install ) else() ExternalProject_Add(protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} GIT_REPOSITORY https://github.com/protocolbuffers/protobuf GIT_TAG v${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} UPDATE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/autogen.sh CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/protobuf/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} BUILD_COMMAND ${PROTOBUF_MAKE_PROGRAM} INSTALL_COMMAND ${PROTOBUF_MAKE_PROGRAM} install BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}protobuf${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/bin/protoc ) endif() if(NOT TARGET protobuf::protoc) add_executable(protobuf::protoc IMPORTED) add_dependencies(protobuf::protoc protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}) endif() unset(Protobuf_PROTOC_EXECUTABLE) set(PROTOBUF_PROTOC_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/bin/protoc") set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/bin/protoc" INTERFACE_LINK_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}protoc${CMAKE_STATIC_LIBRARY_SUFFIX}" ) endif() file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/include) if(NOT TARGET protobuf::libprotobuf) add_library(protobuf::libprotobuf STATIC IMPORTED) add_dependencies(protobuf::libprotobuf protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}) endif() set_target_properties(protobuf::libprotobuf PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}protobuf${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/include" INTERFACE_LINK_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/protobuf-${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}protobuf${CMAKE_STATIC_LIBRARY_SUFFIX}" ) if(${Protobuf_VERSION} VERSION_LESS ${GNSSSDR_PROTOBUF_MIN_VERSION}) if(NOT (${Protobuf_VERSION} EQUAL "0.0.0")) set_package_properties(Protobuf PROPERTIES PURPOSE "Protocol Buffers found (v${Protobuf_VERSION}) is too old (> v${GNSSSDR_PROTOBUF_MIN_VERSION} needed)." ) endif() endif() set_package_properties(Protobuf PROPERTIES PURPOSE "Protocol Buffers v${GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION} will be downloaded and built when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME}'." ) endif() if(${Protobuf_VERSION} VERSION_EQUAL "0.0.0") unset(Protobuf_VERSION) endif() ################################################################################ # Doxygen - http://www.doxygen.nl (OPTIONAL, used if found) ################################################################################ find_package(Doxygen) set_package_properties(Doxygen PROPERTIES URL "http://www.doxygen.nl" PURPOSE "Used to generate code documentation by doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} doc'" TYPE OPTIONAL ) if(DOXYGEN_FOUND AND DOXYGEN_VERSION) set_package_properties(Doxygen PROPERTIES DESCRIPTION "Generates documentation from annotated C++ sources (found: v${DOXYGEN_VERSION})" ) else() set_package_properties(Doxygen PROPERTIES DESCRIPTION "Generates documentation from annotated C++ sources" ) endif() find_package(LATEX) set_package_properties(LATEX PROPERTIES URL "https://www.latex-project.org" DESCRIPTION "High-quality typesetting system" PURPOSE "Used to generate a PDF manual by doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} pdfmanual'" TYPE OPTIONAL ) if(DOXYGEN_FOUND) message(STATUS "Doxygen found.") message(STATUS " You can build the documentation with '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} doc'.") message(STATUS " When done, point your browser to ${CMAKE_BINARY_DIR}/html/index.html") set(HAVE_DOT ${DOXYGEN_DOT_FOUND}) file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} top_srcdir) file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} top_builddir) if(PDFLATEX_COMPILER) set(GENERATE_PDF_DOCUMENTATION "YES") set(GNSSSDR_USE_MATHJAX "NO") else() set(GENERATE_PDF_DOCUMENTATION "NO") set(GNSSSDR_USE_MATHJAX "YES") endif() configure_file(${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.in ${CMAKE_BINARY_DIR}/docs/doxygen/Doxyfile @ONLY ) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/docs/doxygen/Doxyfile WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Generating source code documentation with Doxygen." VERBATIM ) if(PDFLATEX_COMPILER) message(STATUS " '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} pdfmanual' will generate a manual at ${CMAKE_BINARY_DIR}/docs/GNSS-SDR_manual.pdf") add_custom_target(pdfmanual COMMAND ${CMAKE_MAKE_PROGRAM} COMMAND ${CMAKE_COMMAND} -E copy refman.pdf ${CMAKE_BINARY_DIR}/docs/GNSS-SDR_manual.pdf COMMAND ${CMAKE_MAKE_PROGRAM} clean DEPENDS doc WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/docs/latex COMMENT "Generating PDF manual with Doxygen." VERBATIM ) endif() message(STATUS " '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} doc-clean' will clean the documentation.") add_custom_target(doc-clean COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/docs/html COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/docs/latex COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/docs/GNSS-SDR_manual.pdf COMMENT "Cleaning documentation." VERBATIM ) else() message(STATUS " Doxygen has not been found in your system.") message(STATUS " You can get nice code documentation by using it!") message(STATUS " Get it from http://www.doxygen.nl/download.html") if(OS_IS_LINUX) if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat") message(STATUS " or simply by doing 'sudo yum install doxygen-latex'.") else() message(STATUS " or simply by doing 'sudo apt-get install doxygen-latex'.") endif() endif() if(OS_IS_MACOSX) message(STATUS " or simply by doing 'sudo port install doxygen +latex'.") endif() endif() ################################################################################ # OpenCL (OPTIONAL) ################################################################################ find_package(OPENCL QUIET) set_package_properties(OPENCL PROPERTIES PURPOSE "Used in some processing block implementations." TYPE OPTIONAL ) if(NOT OPENCL_FOUND) set(ENABLE_OPENCL OFF) endif() if(ENABLE_OPENCL) if(DEFINED ENV{DISABLE_OPENCL}) set(DISABLE_OPENCL TRUE) endif() if(DISABLE_OPENCL) set(ENABLE_OPENCL OFF) else() if(OPENCL_FOUND) message(STATUS "OpenCL has been found and will be used by some processing blocks") message(STATUS " You can disable OpenCL use by doing 'cmake -DENABLE_OPENCL=OFF ..'") endif() endif() if(ENABLE_GENERIC_ARCH) set(ENABLE_OPENCL OFF) message(STATUS "ENABLE_GENERIC_ARCH is set to ON so the use of OpenCL has been disabled.") endif() if(NOT OPENCL_FOUND) message(STATUS "Processing blocks using OpenCL will not be built.") endif() endif() ################################################################################ # CUDA (OPTIONAL) ################################################################################ if(DEFINED ENV{CUDA_GPU_ACCEL}) message(STATUS "CUDA_GPU_ACCEL environment variable found.") set(ENABLE_CUDA ON) endif() if(ENABLE_CUDA) if(CMAKE_VERSION VERSION_GREATER 3.11) include(CheckLanguage) check_language(CUDA) if(CMAKE_CUDA_COMPILER) enable_language(CUDA) set(CUDA_FOUND TRUE) if(NOT DEFINED CMAKE_CUDA_STANDARD) set(CMAKE_CUDA_STANDARD 11) set(CMAKE_CUDA_STANDARD_REQUIRED ON) endif() else() set(ENABLE_CUDA OFF) endif() else() find_package(CUDA REQUIRED) set_package_properties(CUDA PROPERTIES URL "https://developer.nvidia.com/cuda-downloads" DESCRIPTION "Library for parallel programming in Nvidia GPUs" PURPOSE "Used in some processing block implementations." TYPE REQUIRED ) if(NOT CUDA_FOUND) set(ENABLE_CUDA OFF) endif() endif() endif() if(ENABLE_CUDA) message(STATUS "NVIDIA CUDA GPU Acceleration will be enabled.") message(STATUS " You can disable it with 'cmake -DENABLE_CUDA=OFF ..'") else() message(STATUS "NVIDIA CUDA GPU Acceleration will be not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_CUDA=ON ..' to add support for GPU-based acceleration using CUDA.") endif() ################################################################################ # CUSTOM UDP PACKET SOURCE (OPTIONAL) ################################################################################ find_package(PCAP) set_package_properties(PCAP PROPERTIES PURPOSE "Used for the custom UDP IP packet source." TYPE OPTIONAL ) if(PCAP_FOUND) set(ENABLE_RAW_UDP ON) endif() if(ENABLE_RAW_UDP) message(STATUS "Highly-optimized custom UDP IP packet source is enabled.") message(STATUS " You can disable it with 'cmake -DENABLE_RAW_UDP=OFF ..'") if(NOT PCAP_FOUND) message(FATAL_ERROR "PCAP required to compile custom UDP packet sample source (with ENABLE_RAW_UDP=ON)") endif() endif() ################################################################################ # FPGA (OPTIONAL) ################################################################################ if(ENABLE_FPGA) message(STATUS "FPGA Acceleration will be enabled.") message(STATUS " You can disable it with 'cmake -DENABLE_FPGA=OFF ..'") else() message(STATUS "Fpga Acceleration will be not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_FPGA=ON ..' to add support for GPU-based acceleration using the FPGA.") endif() ################################################################################ # Setup of optional drivers ################################################################################ ########################################## # gr-osmosdr - OPTIONAL # https://github.com/osmocom/gr-osmosdr ########################################## if(DEFINED ENV{RTLSDR_DRIVER}) message(STATUS "RTLSDR_DRIVER environment variable found.") set(ENABLE_OSMOSDR ON) endif() find_package(GROSMOSDR) set_package_properties(GROSMOSDR PROPERTIES PURPOSE "Used for communication with OsmoSDR and other front-ends (HackRF, bladeRF, Realtek's RTL2832U-based dongles, etc.)." TYPE OPTIONAL ) if(ENABLE_OSMOSDR) if(GROSMOSDR_FOUND) message(STATUS "The driver for OsmoSDR and other front-ends (HackRF, bladeRF, Realtek's RTL2832U-based dongles, etc.) will be compiled.") message(STATUS " You can disable it with 'cmake -DENABLE_OSMOSDR=OFF ..'") else() if(ENABLE_PACKAGING) message(WARNING "gr-osmosdr has not been found. Source blocks depending on it will NOT be built.") else() message(FATAL_ERROR "gr-osmosdr required to build gnss-sdr with the optional OSMOSDR driver") endif() set(ENABLE_OSMOSDR OFF) endif() else() message(STATUS "The (optional) driver for OsmoSDR and related front-ends is not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_OSMOSDR=ON ..' to add support for OsmoSDR and other front-ends (HackRF, bladeRF, Realtek's RTL2832U-based USB dongles, etc.)") endif() ############################################## # gr-iio - OPTIONAL # IIO blocks for GNU Radio # https://github.com/analogdevicesinc/gr-iio ############################################## find_package(GRIIO) set_package_properties(GRIIO PROPERTIES PURPOSE "Used for communication with PlutoSDR and FMCOMMS devices." TYPE OPTIONAL ) ##################################################################### # libiio - OPTIONAL # A library for interfacing with local and remote Linux IIO devices # https://github.com/analogdevicesinc/libiio ##################################################################### find_package(LIBIIO) set_package_properties(LIBIIO PROPERTIES PURPOSE "Used for communication with the AD9361 chipset." TYPE OPTIONAL ) ############################################## # TELEORBIT FLEXIBAND FRONTEND - OPTIONAL ############################################## if(DEFINED ENV{FLEXIBAND_DRIVER}) message(STATUS "FLEXIBAND_DRIVER environment variable found.") set(ENABLE_FLEXIBAND ON) endif() if(FLEXIBAND_DRIVER) set(ENABLE_FLEXIBAND ON) endif() if(ENABLE_FLEXIBAND) message(STATUS "The Teleorbit Flexiband front-end source will be compiled.") message(STATUS " You can disable it with 'cmake -DENABLE_FLEXIBAND=OFF ..'") else() message(STATUS "The (optional) Teleorbit Flexiband front-end driver adapter is not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_FLEXIBAND=ON ..' to add support for the Teleorbit Flexiband front-end.") endif() find_package(TELEORBIT) set_package_properties(TELEORBIT PROPERTIES PURPOSE "Used for communication with the Flexiband front-end." TYPE OPTIONAL ) if(ENABLE_FLEXIBAND) if(NOT TELEORBIT_FOUND) message(FATAL_ERROR "Teleorbit Flexiband GNU Radio driver required to build gnss-sdr with the optional FLEXIBAND adapter") endif() endif() ###################### # GN3S - OPTIONAL ###################### if(DEFINED ENV{GN3S_DRIVER}) message(STATUS "GN3S_DRIVER environment variable found.") set(ENABLE_GN3S ON) endif() if(GN3S_DRIVER) set(ENABLE_GN3S ON) endif() find_package(GRGN3S QUIET) set_package_properties(GRGN3S PROPERTIES URL "https://github.com/gnss-sdr/gr-gn3s" DESCRIPTION "The GN3S v2 front-end GNU Radio block." PURPOSE "Used for communication with the GN3S v2 front-end." TYPE OPTIONAL ) if(ENABLE_GN3S) message(STATUS "The GN3S driver will be compiled.") message(STATUS " You can disable it with 'cmake -DENABLE_GN3S=OFF ..'") else() message(STATUS "The (optional and experimental) GN3S driver is not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_GN3S=ON ..' to add support for the GN3S dongle.") endif() ####################################################### # CTTC's digital array beamformer prototype - OPTIONAL ####################################################### if(DEFINED ENV{RAW_ARRAY_DRIVER}) message(STATUS "RAW_ARRAY_DRIVER environment variable found.") set(ENABLE_ARRAY ON) endif() if(RAW_ARRAY_DRIVER) set(ENABLE_ARRAY ON) endif() find_package(GRDBFCTTC QUIET) set_package_properties(GRDBFCTTC PROPERTIES URL "https://github.com/gnss-sdr/gr-dbfcttcs" DESCRIPTION "CTTC's array prototype GNU Radio block." PURPOSE "Used for communication with CTTC's antenna array." TYPE OPTIONAL ) if(ENABLE_ARRAY) message(STATUS "CTTC's Antenna Array front-end driver will be compiled.") message(STATUS " You can disable it with 'cmake -DENABLE_ARRAY=OFF ..'") else() message(STATUS "The (optional) CTTC's Antenna Array front-end driver is not enabled.") message(STATUS " Enable it with 'cmake -DENABLE_ARRAY=ON ..' to add support for the CTTC experimental array front-end.") endif() ################################################################################ # GPerftools - https://github.com/gperftools/gperftools - OPTIONAL) ################################################################################ find_package(GPERFTOOLS) set_package_properties(GPERFTOOLS PROPERTIES PURPOSE "Used for performance analysis." TYPE OPTIONAL ) if(ENABLE_GPERFTOOLS) if(NOT GPERFTOOLS_FOUND) message(STATUS "Although ENABLE_GPERFTOOLS has been set to ON, GPerftools has not been found.") message(STATUS " Binaries will be compiled without 'tcmalloc' and 'profiler' libraries.") message(STATUS " You can install GPerftools from https://github.com/gperftools/gperftools") else() message(STATUS "GPerftools libraries found.") message(STATUS " Binaries will be compiled with 'tcmalloc' and 'profiler' libraries.") endif() # Set GPerftools related flags if it is available # See https://github.com/gperftools/gperftools/blob/master/README if(GPERFTOOLS_FOUND) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) add_compile_options(-fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-fno-builtin) endif() endif() endif() ################################################################################ # GNU gprof (OPTIONAL) - https://sourceware.org/binutils/docs/gprof/ ################################################################################ if(ENABLE_GPROF) add_compile_options(-pg) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg") endif() ################################################################################ # Set compiler flags ################################################################################ if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) # Add warning flags # For "-Wall" see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html set(cxx_warning_flags -Wall -Wextra) if(NOT (CMAKE_VERSION VERSION_LESS "3.3")) add_compile_options("$<$:${cxx_warning_flags}>") else() add_compile_options("$<$,CXX>:${cxx_warning_flags}>") endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0") add_compile_options(-Wno-missing-field-initializers) endif() if(CMAKE_CROSSCOMPILING) add_compile_options(-Wno-psabi) endif() endif() # Processor-architecture related flags # See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) if(OS_IS_MACOSX) add_compile_options(-march=corei7 -mfpmath=sse) else() if(NOT ENABLE_GENERIC_ARCH) if(IS_ARM) # ARM-specific options # See https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html if(NOT CMAKE_CROSSCOMPILING) if(ARM_VERSION STREQUAL "arm") # Unknown arm version - try our best to detect add_compile_options(-mcpu=native) else() add_compile_options(-march=${ARM_VERSION}) endif() endif() else() add_compile_options(-march=native -mfpmath=sse) endif() endif() endif() endif() ################################################################################ # clang-tidy https://clang.llvm.org/extra/clang-tidy/index.html ################################################################################ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(NOT (CMAKE_VERSION VERSION_LESS "3.6")) find_program( CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable" ) if(NOT CLANG_TIDY_EXE) message(STATUS "clang-tidy not found.") else() message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}") if(ENABLE_CLANG_TIDY) message(STATUS " clang-tidy will be used in compilation. You can disable it with 'cmake -DENABLE_CLANG_TIDY=OFF ..'") else() message(STATUS " You can enable clang-tidy usage in compilation with 'cmake -DENABLE_CLANG_TIDY=ON ..'") endif() set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() endif() endif() ################################################################################ # Create uninstall target ################################################################################ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY ) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake ) ################################################################################ # Add subdirectories ################################################################################ add_subdirectory(src) ################################################################################ # Print summary ################################################################################ add_feature_info(ENABLE_UHD ENABLE_UHD "Enables UHD_Signal_Source for using RF front-ends from the USRP family. Requires gr-uhd.") add_feature_info(ENABLE_OSMOSDR ENABLE_OSMOSDR "Enables Osmosdr_Signal_Source and RtlTcp_Signal_Source for using RF front-ends compatible with the OsmoSDR driver. Requires gr-osmosdr.") add_feature_info(ENABLE_FMCOMMS2 ENABLE_FMCOMMS2 "Enables Fmcomms2_Signal_Source for FMCOMMS2/3/4 devices. Requires gr-iio.") add_feature_info(ENABLE_PLUTOSDR ENABLE_PLUTOSDR "Enables Plutosdr_Signal_Source for using ADALM-PLUTO boards. Requires gr-iio.") add_feature_info(ENABLE_AD9361 ENABLE_AD9361 "Enables Ad9361_Fpga_Signal_Source for devices with the AD9361 chipset. Requires libiio.") add_feature_info(ENABLE_RAW_UDP ENABLE_RAW_UDP "Enables Custom_UDP_Signal_Source for custom UDP packet sample source. Requires libpcap.") add_feature_info(ENABLE_FLEXIBAND ENABLE_FLEXIBAND "Enables Flexiband_Signal_Source for using Teleorbit's Flexiband RF front-end. Requires gr-teleorbit.") add_feature_info(ENABLE_GN3S ENABLE_GN3S "Enables Gn3s_Signal_Source for using the GN3S v2 dongle. Requires gr-gn3s.") add_feature_info(ENABLE_ARRAY ENABLE_ARRAY "Enables Raw_Array_Signal_Source and Array_Signal_Conditioner for using CTTC's antenna array. Requires gr-dbfcttc.") add_feature_info(ENABLE_GPERFTOOLS ENABLE_GPERFTOOLS "Enables performance analysis. Requires Gperftools.") add_feature_info(ENABLE_GPROF ENABLE_GPROF "Enables performance analysis with 'gprof'.") add_feature_info(ENABLE_CLANG_TIDY ENABLE_CLANG_TIDY "Runs clang-tidy along with the compiler. Requires Clang.") add_feature_info(ENABLE_PROFILING ENABLE_PROFILING "Runs volk_gnsssdr_profile at the end of the building.") add_feature_info(ENABLE_OPENCL ENABLE_OPENCL "Enables GPS_L1_CA_PCPS_OpenCl_Acquisition (experimental). Requires OpenCL.") add_feature_info(ENABLE_CUDA ENABLE_CUDA "Enables GPS_L1_CA_DLL_PLL_Tracking_GPU (experimental). Requires CUDA.") add_feature_info(ENABLE_FPGA ENABLE_FPGA "Enables building of processing blocks for FPGA off-loading.") add_feature_info(ENABLE_GENERIC_ARCH ENABLE_GENERIC_ARCH "When disabled, flags such as '-march=native' are passed to the compiler.") add_feature_info(ENABLE_PACKAGING ENABLE_PACKAGING "Enables software packaging.") add_feature_info(ENABLE_OWN_GLOG ENABLE_OWN_GLOG "Forces the downloading and building of Google glog.") add_feature_info(ENABLE_OWN_ARMADILLO ENABLE_OWN_ARMADILLO "Forces the downloading and building of Armadillo.") add_feature_info(ENABLE_LOG ENABLE_LOG "Enables runtime internal logging with Google glog.") add_feature_info(ENABLE_UNIT_TESTING ENABLE_UNIT_TESTING "Enables building of Unit Tests.") add_feature_info(ENABLE_UNIT_TESTING_MINIMAL ENABLE_UNIT_TESTING_MINIMAL "Enables building a minimal set of Unit Tests.") add_feature_info(ENABLE_UNIT_TESTING_EXTRA ENABLE_UNIT_TESTING_EXTRA "Enables building of Extra Unit Tests and downloading of external data files.") add_feature_info(ENABLE_SYSTEM_TESTING ENABLE_SYSTEM_TESTING "Enables building of System Tests.") add_feature_info(ENABLE_SYSTEM_TESTING_EXTRA ENABLE_SYSTEM_TESTING_EXTRA "Enables building of Extra System Tests and downloading of external tools.") add_feature_info(ENABLE_OWN_GPSTK ENABLE_OWN_GPSTK "Forces the downloading and building of GPSTk for system tests.") add_feature_info(ENABLE_GNSS_SIM_INSTALL ENABLE_GNSS_SIM_INSTALL "Enables downloading and building of gnss-sim.") add_feature_info(ENABLE_INSTALL_TESTS ENABLE_INSTALL_TESTS "Install test binaries when doing '${CMAKE_MAKE_PROGRAM_PRETTY_NAME} install'.") message(STATUS "") message(STATUS "***************************************") message(STATUS "* SUMMARY REPORT *") message(STATUS "***************************************") message(STATUS "") if(CMAKE_VERSION VERSION_LESS 3.4) feature_summary(WHAT ALL) feature_summary(FILENAME ${CMAKE_CURRENT_BINARY_DIR}/features.log WHAT ALL) else() feature_summary(WHAT REQUIRED_PACKAGES_FOUND REQUIRED_PACKAGES_NOT_FOUND OPTIONAL_PACKAGES_FOUND OPTIONAL_PACKAGES_NOT_FOUND ENABLED_FEATURES DISABLED_FEATURES ) feature_summary(FILENAME ${CMAKE_CURRENT_BINARY_DIR}/features.log WHAT REQUIRED_PACKAGES_FOUND REQUIRED_PACKAGES_NOT_FOUND OPTIONAL_PACKAGES_FOUND OPTIONAL_PACKAGES_NOT_FOUND ENABLED_FEATURES DISABLED_FEATURES ) endif() message(STATUS "GNSS-SDR v${VERSION} is ready to be built.") CODE_OF_CONDUCT.md000066400000000000000000000062431352176506000136670ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at carles.fernandez@cttc.es. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ CONTRIBUTING.md000066400000000000000000000146711352176506000133250ustar00rootroot00000000000000# Contributing to GNSS-SDR :+1::tada: Thanks for taking the time to contribute! :tada::+1: Third-party contributions are essential for keeping GNSS-SDR continuously improving. We simply cannot access the huge number of platforms and myriad configurations for running GNSS-SDR. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. The following is a set of guidelines for contributing to GNSS-SDR, which is hosted in the [GNSS-SDR Organization](https://github.com/gnss-sdr) on GitHub. These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a [pull request](#how-to-submit-a-pull-request). ## Code of Conduct This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior. ## Reporting an issue Have you found a bug in the code which is not in the [list of known bugs](https://github.com/gnss-sdr/gnss-sdr/issues)? Do you have a suggestion for improvement? Then by all means please [submit a new issue](https://github.com/gnss-sdr/gnss-sdr/issues/new), and do not hesitate to comment on existing [open issues](https://github.com/gnss-sdr/gnss-sdr/issues). When filling a new issue, please remember to: * **Use a clear and descriptive title** for the issue to identify the problem. * **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by describing your computing platform (Operating System and version, how did you installed GNSS-SDR and its dependencies, what file or front-end are you using as a signal source, etc.). You can also include the configuration file you are using, or a dump of the terminal output you are getting. The more information you provide, the more chances to get useful answers. * **Please be patient**. This organization is run on a volunteer basis, so it can take some time to the Developer Team to reach your issue. They will do their best to fix it as soon as possible. * If you opened an issue that is now solved, it is a good practice to **close it**. The list of [open issues](https://github.com/gnss-sdr/gnss-sdr/issues) can be a good starting point and a source of ideas if you are looking to contribute to the source code. ## Contributing to the source code ### Preliminaries 1. If you still have not done so, [create your personal account on GitHub](https://github.com/join). 2. [Fork GNSS-SDR from GitHub](https://github.com/gnss-sdr/gnss-sdr/fork). This will copy the whole gnss-sdr repository to your personal account. 3. Then, go to your favorite working folder in your computer and clone your forked repository by typing (replacing ```YOUR_USERNAME``` by the actual username of your GitHub account): $ git clone https://github.com/YOUR_USERNAME/gnss-sdr 4. Your forked repository https://github.com/YOUR_USERNAME/gnss-sdr will receive the default name of `origin`. You can also add the original gnss-sdr repository, which is usually referred to as `upstream`: $ cd gnss-sdr $ git remote add upstream https://github.com/gnss-sdr/gnss-sdr.git To verify the new upstream repository you have specified for your fork, type `git remote -v`. You should see the URL for your fork as `origin`, and the URL for the original repository as `upstream`: ``` $ git remote -v origin https://github.com/YOUR_USERNAME/gnss-sdr.git (fetch) origin https://github.com/YOUR_USERNAME/gnss-sdr.git (push) upstream https://github.com/gnss-sdr/gnss-sdr.git (fetch) upstream https://github.com/gnss-sdr/gnss-sdr.git (push) ``` ### Start working on your contribution Checkout the `next` branch of the git repository in order to get synchronized with the latest development code: ``` $ git checkout next $ git pull upstream next ``` When start working in a new improvement, please **always** branch off from `next`. Open a new branch and start working on it: ``` $ git checkout -b my_feature ``` Now you can do changes, add files, do commits (please take a look at [how to write good commit messages](https://chris.beams.io/posts/git-commit/)!) and push them to your repository: ``` $ git push origin my_feature ``` If there have been new pushes to the `next` branch of the `upstream` repository since the last time you pulled from it, you might want to put your commits on top of them (this is mandatory for pull requests): ``` $ git pull --rebase upstream next ``` ### How to submit a pull request Before submitting your code, please be sure to [apply clang-format](https://gnss-sdr.org/coding-style/#use-tools-for-automated-code-formatting). When the contribution is ready, you can [submit a pull request](https://github.com/gnss-sdr/gnss-sdr/compare/). Head to your GitHub repository, switch to your `my_feature` branch, and click the _**Pull Request**_ button, which will do all the work for you. Code comparison must be always to the `next` branch. Once a pull request is sent, the Developer Team can review the set of changes, discuss potential modifications, and even push follow-up commits if necessary. Some things that will increase the chance that your pull request is accepted: * Avoid platform-dependent code. If your code require external dependencies, they must be available as packages in [Debian OldStable](https://wiki.debian.org/DebianOldStable). * Write tests. * Follow our [coding style guide](https://gnss-sdr.org/coding-style/). * Write a descriptive and detailed summary. Please consider that reviewing pull requests is hard, so include as much information as possible to make your pull request's intent clear. For more details about Git usage, please check out [our tutorial](https://gnss-sdr.org/docs/tutorials/using-git/). ## Contributing to the website The content of https://gnss-sdr.org lives in a GitHub repository at https://github.com/gnss-sdr/geniuss-place You can fork that repository, reproduce the entire website on your computer using [Jekyll](https://jekyllrb.com/), do changes and submit pull requests, just as explained above. For more details, please check out [how to contribute](https://gnss-sdr.org/contribute/). Last but not the least, you can leave your comments on the website. ------ ![GeNiuSS contributes](https://gnss-sdr.org/assets/images/geniuss-contribute.png) Thanks for your contribution to GNSS-SDR! COPYING000066400000000000000000001057611352176506000121300ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . MANIFEST.md000066400000000000000000000022531352176506000126150ustar00rootroot00000000000000title: gnss-sdr brief: An open source global navigation satellite systems software defined receiver tags: - sdr - gnss - gps - Galileo - Glonass author: - Carles Fernandez-Prades - Javier Arribas - et altri (see AUTHORS file for a list of contributors) copyright_owner: - The Authors dependencies: - gnuradio (>= 3.7.3) - armadillo - gflags - glog - gnutls - matio license: GPLv3+ repo: https://github.com/gnss-sdr/gnss-sdr website: https://gnss-sdr.org icon: https://gnss-sdr.org/assets/images/logo400x400.jpg --- Global Navigation Satellite Systems receiver defined by software. It performs all the signal processing from raw signal samples up to the computation of the Position-Velocity-Time solution, including code and phase observables. It is able to work with raw data files or, if there is computational power enough, in real time with suitable radiofrequency front-ends. This software is mainly developed at [CTTC](http://www.cttc.es "Centre Tecnologic de Telecomunicacions de Catalunya") with contributions from around the world. More info at [gnss-sdr.org](https://gnss-sdr.org "GNSS-SDR's Homepage"). README.md000066400000000000000000002562641352176506000123610ustar00rootroot00000000000000[![](./docs/doxygen/images/gnss-sdr_logo.png)](https://gnss-sdr.org "GNSS-SDR website") [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) **Welcome to GNSS-SDR!** This program is a software-defined receiver which is able to process (that is, to perform detection, synchronization, demodulation and decoding of the navigation message, computation of observables and, finally, computation of position fixes) the following Global Navigation Satellite System's signals: In the L1 band: - 🛰 GLONASS L1 C/A (centered at 1602.00 MHz) :white_check_mark: - 🛰 GPS L1 C/A (centered at 1575.42 MHz) :white_check_mark: - 🛰 Galileo E1b/c (centered at 1575.42 MHz) :white_check_mark: - 🛰 BeiDou B1I (centered at 1561.098 MHz) :white_check_mark: In the L2 band: - 🛰 BeiDou B3I (centered at 1268.520 MHz) :white_check_mark: - 🛰 GLONASS L2 C/A (centered at 1246.00 MHz) :white_check_mark: - 🛰 GPS L2C (centered at 1227.60 MHz) :white_check_mark: In the L5 band: - 🛰 GPS L5 (centered at 1176.45 MHz) :white_check_mark: - 🛰 Galileo E5a (centered at 1176.45 MHz) :white_check_mark: GNSS-SDR provides interfaces for a wide range of radio frequency front-ends and raw sample file formats, generates processing outputs in standard formats, allows for the full inspection of the whole signal processing chain, and offers a framework for the development of new features. Please visit [https://gnss-sdr.org](https://gnss-sdr.org "GNSS-SDR website") for more information about this open source software-defined GNSS receiver. # How to build GNSS-SDR This section describes how to set up the compilation environment in GNU/Linux or [macOS / Mac OS X](#macosx), and to build GNSS-SDR. See also our [build and install page](https://gnss-sdr.org/build-and-install/ "GNSS-SDR's Build and Install"). GNU/Linux ---------- * Tested distributions: Ubuntu 14.04 LTS and above; Debian 8.0 "jessie" and above; Arch Linux; CentOS 7; Fedora 26 and above; OpenSUSE 42.3 and above. * Supported microprocessor architectures: * i386: Intel x86 instruction set (32-bit microprocessors). * amd64: also known as x86-64, the 64-bit version of the x86 instruction set, originally created by AMD and implemented by AMD, Intel, VIA and others. * armel: ARM embedded ABI, supported on ARM v4t and higher. * armhf: ARM hard float, ARMv7 + VFP3-D16 floating-point hardware extension + Thumb-2 instruction set and above. * arm64: ARM 64 bits or ARMv8. * mips: MIPS architecture (big-endian, such as those manufactured by SGI). * mipsel: MIPS architecture (little-endian, such as Loongson 3). * mips64el: 64-bit version of MIPS architecture. * powerpc: the RISC 32-bit microprocessor architecture developed by IBM, Motorola (now Freescale) and Apple. * ppc64: 64-bit big-endian PowerPC architecture. * ppc64el: 64-bit little-endian PowerPC architecture. * s390x: IBM System z architecture for mainframe computers. Older distribution releases might work as well, but you will need GCC 4.7 or newer. Before building GNSS-SDR, you need to install all the required dependencies. There are two alternatives here: through software packages or building them from the source code. It is in general not a good idea to mix both approaches. ### Alternative 1: Install dependencies using software packages If you want to start building and running GNSS-SDR as quick and easy as possible, the best option is to install all the required dependencies as binary packages. #### Debian / Ubuntu If you are using Debian 8, Ubuntu 14.10 or above, this can be done by copying and pasting the following line in a terminal: ~~~~~~ $ sudo apt-get install build-essential cmake git libboost-dev libboost-date-time-dev \ libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-chrono-dev \ libboost-serialization-dev liblog4cpp5-dev libuhd-dev gnuradio-dev gr-osmosdr \ libblas-dev liblapack-dev libarmadillo-dev libgflags-dev libgoogle-glog-dev \ libgnutls-openssl-dev libpcap-dev python-mako python-six libmatio-dev libpugixml-dev \ libgtest-dev libprotobuf-dev protobuf-compiler ~~~~~~ Please note that the required files from `libgtest-dev` were moved to `googletest` in Debian 9 "stretch" and Ubuntu 18.04 "bionic", and moved back again to `libgtest-dev` in Debian 10 "buster" and Ubuntu 18.10 "cosmic" (and above). **Note for Ubuntu 14.04 LTS "trusty" users:** you will need to build from source and install GNU Radio manually, as explained below, since GNSS-SDR requires `gnuradio-dev` >= 3.7.3, and Ubuntu 14.04 came with 3.7.2. Install all the packages above BUT EXCEPT `libuhd-dev`, `gnuradio-dev` and `gr-osmosdr` (and remove them if they are already installed in your machine), and install those dependencies using PyBOMBS. The same applies to `libmatio-dev`: Ubuntu 14.04 came with 1.5.2 and the minimum required version is 1.5.3. Please do not install the `libmatio-dev` package and install `libtool`, `automake` and `libhdf5-dev` instead. A recent version of the library will be downloaded and built automatically if CMake does not find it installed. **Note for Debian 8 "jessie" users:** please see the note about `libmatio-dev` above. Install `libtool`, `automake` and `libhdf5-dev` instead. Once you have installed these packages, you can jump directly to [download the source code and build GNSS-SDR](#download-and-build-linux). #### Arch Linux If you are using Arch Linux: ~~~~~~ $ pacman -S gcc make cmake git boost boost-libs log4cpp libvolk gnuradio \ gnuradio-osmosdr blas lapack gflags google-glog openssl pugixml \ python-mako python-six libmatio libpcap gtest protobuf ~~~~~~ Once you have installed these packages, you can jump directly to [download the source code and build GNSS-SDR](#download-and-build-linux). #### CentOS If you are using CentOS 7, you can install the dependencies via Extra Packages for Enterprise Linux ([EPEL](https://fedoraproject.org/wiki/EPEL)): ~~~~~~ $ sudo yum install wget $ wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm $ sudo rpm -Uvh epel-release-latest-7.noarch.rpm $ sudo yum install make automake gcc gcc-c++ kernel-devel libtool \ hdf5-devel cmake git boost-devel boost-date-time boost-system \ boost-filesystem boost-thread boost-chrono boost-serialization \ log4cpp-devel gnuradio-devel gr-osmosdr-devel blas-devel lapack-devel \ armadillo-devel openssl-devel libpcap-devel python-mako python-six pugixml-devel ~~~~~~ Once you have installed these packages, you can jump directly to [download the source code and build GNSS-SDR](#download-and-build-linux). #### Fedora If you are using Fedora 26 or above, the required software dependencies can be installed by doing: ~~~~~~ $ sudo yum install make automake gcc gcc-c++ kernel-devel cmake git boost-devel \ boost-date-time boost-system boost-filesystem boost-thread boost-chrono \ boost-serialization log4cpp-devel gnuradio-devel gr-osmosdr-devel \ blas-devel lapack-devel matio-devel armadillo-devel gflags-devel \ glog-devel openssl-devel libpcap-devel python-mako python-six \ pugixml-devel protobuf-devel protobuf-compiler ~~~~~~ Once you have installed these packages, you can jump directly to [download the source code and build GNSS-SDR](#download-and-build-linux). #### openSUSE If you are using openSUSE Leap: ~~~~~~ zypper install cmake git gcc-c++ boost-devel libboost_atomic-devel \ libboost_system-devel libboost_filesystem-devel libboost_chrono-devel \ libboost_thread-devel libboost_serialization-devel log4cpp-devel \ gnuradio-devel pugixml-devel libpcap-devel armadillo-devel libtool \ automake hdf5-devel openssl-devel python-Mako python-six protobuf-devel ~~~~~~ If you are using openSUSE Tumbleweed: ~~~~~~ zypper install cmake git gcc-c++ boost-devel libboost_atomic-devel \ libboost_system-devel libboost_filesystem-devel libboost_date_time-devel \ libboost_thread-devel libboost_chrono-devel libboost_serialization-devel \ log4cpp-devel gtest gnuradio-devel pugixml-devel libpcap-devel \ armadillo-devel libtool automake hdf5-devel libopenssl-devel \ python3-Mako python3-six protobuf-devel ~~~~~~ Once you have installed these packages, you can jump directly to [download the source code and build GNSS-SDR](#download-and-build-linux). ### Alternative 2: Install dependencies using PyBOMBS This option is adequate if you are interested in development, in working with the most recent versions of software dependencies, want more fine tuning on the installed versions, or simply in building everything from the scratch just for the fun of it. In such cases, we recommend to use [PyBOMBS](https://github.com/gnuradio/pybombs "Python Build Overlay Managed Bundle System") (Python Build Overlay Managed Bundle System), GNU Radio's meta-package manager tool that installs software from source, or whatever the local package manager is, that automatically does all the work for you. Please take a look at the configuration options and general PyBOMBS usage at https://github.com/gnuradio/pybombs. Here we provide a quick step-by-step tutorial. First of all, install some basic packages: ~~~~~~ $ sudo apt-get install git python-pip ~~~~~~ Download, build and install PyBOMBS: ~~~~~~ $ sudo pip install git+https://github.com/gnuradio/pybombs.git ~~~~~~ Apply a configuration: ~~~~~~ $ pybombs auto-config ~~~~~~ Add list of default recipes: ~~~~~~ $ pybombs recipes add-defaults ~~~~~~ Download, build and install GNU Radio, related drivers and some other extra modules into the directory ```/path/to/prefix``` (replace this path by your preferred one, for instance ```$HOME/sdr```): ~~~~~~ $ pybombs prefix init /path/to/prefix -a myprefix -R gnuradio-default ~~~~~~ This will perform a local installation of the dependencies under ```/path/to/prefix```, so they will not be visible when opening a new terminal. In order to make them available, you will need to set up the adequate environment variables: ~~~~~~ $ cd /path/to/prefix $ . ./setup_env.sh ~~~~~~ Now you are ready to use GNU Radio and to jump into building GNSS-SDR after installing a few other dependencies. Actually, those are steps that PyBOMBS can do for you as well: ~~~~~~ $ pybombs install gnss-sdr ~~~~~~ By default, PyBOMBS installs the ‘next’ branch of GNSS-SDR development, which is the most recent version of the source code. This behaviour can be modified by altering the corresponding recipe at ```$HOME/.pybombs/recipes/gr-recipes/gnss-sdr.lwr``` In case you do not want to use PyBOMBS and prefer to build and install GNSS-SDR step by step (i.e., cloning the repository and doing the usual ```cmake .. && make && make install``` dance), Armadillo, GFlags, Glog and GnuTLS can be installed either by using PyBOMBS: ~~~~~~ $ pybombs install armadillo gflags glog gnutls ~~~~~~ or manually as explained below, and then please follow instructions on how to [download the source code and build GNSS-SDR](#download-and-build-linux). ### Manual installation of other required dependencies #### Install [Armadillo](http://arma.sourceforge.net/ "Armadillo's Homepage"), a C++ linear algebra library: ~~~~~~ $ sudo apt-get install libblas-dev liblapack-dev # For Debian/Ubuntu/LinuxMint $ sudo yum install lapack-devel blas-devel # For Fedora/CentOS/RHEL $ sudo zypper install lapack-devel blas-devel # For OpenSUSE $ sudo pacman -S blas lapack # For Arch Linux $ wget http://sourceforge.net/projects/arma/files/armadillo-9.600.4.tar.xz $ tar xvfz armadillo-9.600.4.tar.xz $ cd armadillo-9.600.4 $ cmake . $ make $ sudo make install ~~~~~~ The full stop separated from ```cmake``` by a space is important. [CMake](https://cmake.org/ "CMake's Homepage") will figure out what other libraries are currently installed and will modify Armadillo's configuration correspondingly. CMake will also generate a run-time armadillo library, which is a combined alias for all the relevant libraries present on your system (eg. BLAS, LAPACK and ATLAS). #### Install [Gflags](https://github.com/gflags/gflags "Gflags' Homepage"), a commandline flags processing module for C++: ~~~~~~ $ wget https://github.com/gflags/gflags/archive/v2.2.2.tar.gz $ tar xvfz v2.2.2.tar.gz $ cd gflags-2.2.2 $ cmake -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF -DBUILD_gflags_nothreads_LIB=OFF . $ make $ sudo make install $ sudo ldconfig ~~~~~~ #### Install [Glog](https://github.com/google/glog "Glog's Homepage"), a library that implements application-level logging: ~~~~~~ $ wget https://github.com/google/glog/archive/v0.4.0.tar.gz $ tar xvfz v0.4.0.tar.gz $ cd glog-0.4.0 $ ./autogen.sh $ ./configure $ make $ sudo make install $ sudo ldconfig ~~~~~~ #### Build the [Google C++ Testing Framework](https://github.com/google/googletest "Googletest Homepage"), also known as Google Test: ~~~~~~ $ wget https://github.com/google/googletest/archive/release-1.8.1.zip $ unzip release-1.8.1.zip $ cd googletest-release-1.8.1 $ cmake -DINSTALL_GTEST=OFF -DBUILD_GMOCK=OFF . $ make ~~~~~~ Please **DO NOT install** Google Test (do *not* type ```sudo make install```). Every user needs to compile his tests using the same compiler flags used to compile the installed Google Test libraries; otherwise he may run into undefined behaviors (i.e. the tests can behave strangely and may even crash for no obvious reasons). The reason is that C++ has this thing called the One-Definition Rule: if two C++ source files contain different definitions of the same class/function/variable, and you link them together, you violate the rule. The linker may or may not catch the error (in many cases it is not required by the C++ standard to catch the violation). If it does not, you get strange run-time behaviors that are unexpected and hard to debug. If you compile Google Test and your test code using different compiler flags, they may see different definitions of the same class/function/variable (e.g. due to the use of ```#if``` in Google Test). Therefore, for your sanity, we recommend to avoid installing pre-compiled Google Test libraries. Instead, each project should compile Google Test itself such that it can be sure that the same flags are used for both Google Test and the tests. The building system of GNSS-SDR does the compilation and linking of googletest to its own tests; it is only required that you tell the system where the googletest folder that you downloaded resides. Just add to your ```$HOME/.bashrc``` file the following line: ~~~~~~ export GTEST_DIR=/home/username/googletest-release-1.8.1/googletest ~~~~~~ changing `/home/username/googletest-release-1.8.1/googletest` by the actual directory where you built googletest. #### Install the [GnuTLS](https://www.gnutls.org/ "GnuTLS's Homepage") or [OpenSSL](https://www.openssl.org/ "OpenSSL's Homepage") libraries: ~~~~~~ $ sudo apt-get install libgnutls-openssl-dev # For Debian/Ubuntu/LinuxMint $ sudo yum install openssl-devel # For Fedora/CentOS/RHEL $ sudo zypper install openssl-devel # For OpenSUSE $ sudo pacman -S openssl # For Arch Linux ~~~~~~ In case the GnuTLS library with openssl extensions package is not available in your GNU/Linux distribution, GNSS-SDR can also work well with OpenSSL. #### Install [Protocol Buffers](https://developers.google.com/protocol-buffers/ "Protocol Buffers' Homepage"), a portable mechanism for serialization of structured data: GNSS-SDR requires Protocol Buffers v3.0.0 or later. If the packages that come with your distribution are older than that (_e.g._, Ubuntu 16.04 Xenial and Debian 8 Jessie came with older versions), then you will need to install it manually. First, install the dependencies: ~~~~~~ $ sudo apt-get install autoconf automake libtool curl make g++ unzip ~~~~~~ and then: ~~~~~~ $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0/protobuf-cpp-3.8.0.tar.gz $ tar xvfz protobuf-cpp-3.8.0.tar.gz $ cd protobuf-3.8.0 $ ./autogen.sh $ ./configure $ make $ sudo make install $ sudo ldconfig ~~~~~~ ### Clone GNSS-SDR's Git repository: ~~~~~~ $ git clone https://github.com/gnss-sdr/gnss-sdr ~~~~~~ Cloning the GNSS-SDR repository as in the line above will create a folder named gnss-sdr with the following structure: ~~~~~~ |-gnss-sdr |---build <- where gnss-sdr is built. |---cmake <- CMake-related files. |---conf <- Configuration files. Each file defines one particular receiver. |---data <- Populate this folder with your captured data. |---docs <- Contains documentation-related files. |---install <- Executables will be placed here. |---src <- Source code folder. |-----algorithms <- Signal processing blocks. |-----core <- Control plane, interfaces, systems' parameters. |-----main <- Main function of the C++ program. |-----tests <- QA code. |-----utils <- some utilities (e.g. Matlab scripts). ~~~~~~ By default, you will be in the 'master' branch of the Git repository, which corresponds to the latest stable release. If you want to try the latest developments, you can use the 'next' branch by going to the newly created gnss-sdr folder doing: ~~~~~~ $ git checkout next ~~~~~~ More information about GNSS-SDR-specific Git usage and pointers to further readings can be found at our [Git tutorial](https://gnss-sdr.org/docs/tutorials/using-git/ "Using Git"). ### Build and install GNSS-SDR Go to GNSS-SDR's build directory: ~~~~~~ $ cd gnss-sdr/build ~~~~~~ Configure and build the application: ~~~~~~ $ cmake .. $ make ~~~~~~ By default, CMake will build the Release version, meaning that the compiler will generate a fast, optimized executable. This is the recommended build type when using an RF front-end and you need to attain real time. If working with a file (and thus without real-time constraints), you may want to obtain more information about the internals of the receiver, as well as more fine-grained logging. This can be done by building the Debug version, by doing: ~~~~~~ $ cmake -DCMAKE_BUILD_TYPE=Debug .. $ make ~~~~~~ This will create four executables at gnss-sdr/install, namely ```gnss-sdr```, ```run_tests```, ```front-end-cal``` and ```volk_gnsssdr_profile```. You can run them from that folder, but if you prefer to install ```gnss-sdr``` on your system and have it available anywhere else, do: ~~~~~~ $ sudo make install ~~~~~~ This will also make a copy of the conf/ folder into /usr/local/share/gnss-sdr/conf for your reference. We suggest to create a working directory at your preferred location and store your own configuration and data files there. You could be interested in creating the documentation by doing: ~~~~~~ $ make doc ~~~~~~ from the gnss-sdr/build folder. This will generate HTML documentation that can be retrieved pointing your browser of preference to build/docs/html/index.html. If a LaTeX installation is detected in your system, ~~~~~~ $ make pdfmanual ~~~~~~ will create a PDF manual at build/docs/GNSS-SDR_manual.pdf. Finally, ~~~~~~ $ make doc-clean ~~~~~~ will remove the content of previously-generated documentation. GNSS-SDR comes with a library which is a module of the Vector-Optimized Library of Kernels (so called [VOLK_GNSSSDR](./src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/README.md)) and a profiler that will build a config file for the best SIMD architecture for your processor. Run ```volk_gnsssdr_profile``` that is installed into ```$PREFIX/bin```. This program tests all known VOLK kernels for each architecture supported by the processor. When finished, it will write to ```$HOME/.volk_gnsssdr/volk_gnsssdr_config``` the best architecture for the VOLK function. This file is read when using a function to know the best version of the function to execute. It mimics GNU Radio's [VOLK](http://libvolk.org/) library, so if you still have not run ```volk_profile```, this is a good moment to do so. If you are using Eclipse as your development environment, CMake can create the project for you. Type: ~~~~~~ $ cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DECLIPSE_GENERATE_SOURCE_PROJECT=TRUE -DCMAKE_ECLIPSE_VERSION=4.5 . ~~~~~~ and then import the created project file into Eclipse: 1. Import project using Menu File -> Import. 2. Select General -> Existing projects into workspace. 3. Browse where your build tree is and select the root build tree directory. Keep "Copy projects into workspace" unchecked. 4. You get a fully functional Eclipse project. ###### Build GN3S V2 Custom firmware and driver (OPTIONAL): Install the GNU Radio module: ~~~~~~ $ git clone https://github.com/gnss-sdr/gr-gn3s $ cd gr-gn3s/build $ cmake .. $ make $ sudo make install $ sudo ldconfig ~~~~~~ Then configure GNSS-SDR to build the `GN3S_Signal_Source` by: ~~~~~~ $ cd gnss-sdr/build $ cmake -DENABLE_GN3S=ON .. $ make $ sudo make install ~~~~~~ In order to gain access to USB ports, gnss-sdr should be used as root. In addition, the driver requires access to the GN3S firmware binary file. It should be available in the same path where the application is called. GNSS-SDR comes with a pre-compiled custom GN3S firmware available at gr-gn3s/firmware/GN3S_v2/bin/gn3s_firmware.ihx. Please copy this file to the application path. (in order to disable the `GN3S_Signal_Source` compilation, you can pass `-DENABLE_GN3S=OFF` to cmake and build GNSS-SDR again). More info at https://github.com/gnss-sdr/gr-gn3s ###### Build OSMOSDR support (OPTIONAL): Install the [OsmoSDR](http://sdr.osmocom.org/trac/ "OsmoSDR's Homepage") library and GNU Radio's source block: ~~~~~~ $ git clone git://git.osmocom.org/osmo-sdr.git $ cd osmo-sdr/software/libosmosdr $ mkdir build $ cd build/ $ cmake .. $ make $ sudo make install $ sudo ldconfig $ cd ../.. $ git clone git://git.osmocom.org/gr-osmosdr $ cd gr-osmosdr $ mkdir build $ cd build $ cmake .. -Wno-dev $ make $ sudo make install $ sudo ldconfig ~~~~~~ Then, configure GNSS-SDR to build the `Osmosdr_Signal_Source` by: ~~~~~~ $ cmake -DENABLE_OSMOSDR=ON .. $ make $ sudo make install ~~~~~~ (in order to disable the `Osmosdr_Signal_Source` compilation, you can pass `DENABLE_OSMOSDR=OFF` to cmake and build GNSS-SDR again). ###### Build FMCOMMS2 based SDR Hardware support (OPTIONAL): Install the [libiio](https://github.com/analogdevicesinc/libiio.git) (>=v0.11), [libad9361](https://github.com/analogdevicesinc/libad9361-iio.git) (>=v0.1-1) libraries and [gr-iio](https://github.com/analogdevicesinc/gr-iio.git) (>v0.3) gnuradio block: ~~~~~~ $ sudo apt-get install libxml2-dev bison flex $ git clone https://github.com/analogdevicesinc/libiio.git $ cd libiio $ mkdir build $ cd build $ cmake .. $ make && sudo make install && sudo ldconfig $ cd ../.. $ git clone https://github.com/analogdevicesinc/libad9361-iio.git $ cd libad9361-iio $ mkdir build $ cd build $ cmake .. $ make && sudo make install && sudo ldconfig $ cd ../.. $ git clone https://github.com/analogdevicesinc/gr-iio.git $ cd gr-iio $ mkdir build $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=/usr .. $ make && sudo make install && sudo ldconfig $ cd ../.. ~~~~~~ Then configure GNSS-SDR to build the `Fmcomms2_Signal_Source` implementation: ~~~~~~ $ cd gnss-sdr/build $ cmake -DENABLE_FMCOMMS2=ON .. $ make $ sudo make install ~~~~~~ or configure it to build `Plutosdr_Signal_Source`: ~~~~~~ $ cmake -DENABLE_PLUTOSDR=ON .. $ make $ sudo make install ~~~~~~ With `Fmcomms2_Signal_Source` you can use any SDR hardware based on [FMCOMMS2](https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz), including the ADALM-PLUTO (PlutoSdr) by configuring correctly the .conf file. The `Plutosdr_Signal_Source` offers a simpler manner to use the ADALM-PLUTO because implements only a subset of FMCOMMS2's parameters valid for those devices. ###### Build OpenCL support (OPTIONAL): In order to enable the building of blocks that use OpenCL, type: ~~~~~~ $ cmake -DENABLE_OPENCL=ON .. $ make $ sudo make install ~~~~~~ ###### Build CUDA support (OPTIONAL): In order to enable the building of blocks that use CUDA, NVIDIA's parallel programming model that enables graphics processing unit (GPU) acceleration for data-parallel computations, first you need to install the CUDA Toolkit from [NVIDIA Developers Download page](https://developer.nvidia.com/cuda-downloads "CUDA Downloads"). Make sure that the SDK samples build well. Then, build GNSS-SDR by doing: ~~~~~~ $ cmake -DENABLE_CUDA=ON .. $ make $ sudo make install ~~~~~~ Of course, you will also need a GPU that [supports CUDA](https://developer.nvidia.com/cuda-gpus "CUDA GPUs"). ###### Build a portable binary In order to build an executable that not depends on the specific SIMD instruction set that is present in the processor of the compiling machine, so other users can execute it in other machines without those particular sets, use: ~~~~~~ $ cmake -DENABLE_GENERIC_ARCH=ON .. $ make $ sudo make install ~~~~~~ Using this option, all SIMD instructions are exclusively accessed via VOLK, which automatically includes versions of each function for different SIMD instruction sets, then detects at runtime which to use, or if there are none, substitutes a generic, non-SIMD implementation. More details can be found in our tutorial about [GNSS-SDR configuration options at building time](https://gnss-sdr.org/docs/tutorials/using-git/ "Configuration options at building time"). macOS and Mac OS X --------- GNSS-SDR can be built on MacOS or Mac OS X, starting from 10.9 (Mavericks) and including 10.14 (Mojave). If you still have not installed [Xcode](https://developer.apple.com/xcode/ "Xcode"), do it now from the App Store (it's free). You will also need the Xcode Command Line Tools. Launch the Terminal, found in /Applications/Utilities/, and type: ~~~~~~ $ xcode-select --install ~~~~~~ Agree to Xcode license: ~~~~~~ $ sudo xcodebuild -license ~~~~~~ Software pre-requisites can be installed using either [Macports](#macports) or [Homebrew](#homebrew). #### Macports First, [install Macports](https://www.macports.org/install.php). If you are upgrading from a previous installation, please follow the [migration rules](https://trac.macports.org/wiki/Migration). In a terminal, type: ~~~~~~ $ sudo port selfupdate $ sudo port upgrade outdated $ sudo port install gnuradio $ sudo port install lapack $ sudo port install armadillo $ sudo port install gnutls $ sudo port install google-glog +gflags $ sudo port install matio $ sudo port install pugixml $ sudo port install protobuf3-cpp $ sudo port install py27-mako $ sudo port install py27-six $ sudo port install doxygen +docs ~~~~~~ You also might need to activate a Python installation. The list of installed versions can be retrieved with: ~~~~~~ $ port select --list python ~~~~~~ and you can activate a certain version by typing: ~~~~~~ $ sudo port select --set python python27 ~~~~~~ #### Homebrew First, install [Homebrew](https://brew.sh/). Paste this in a terminal prompt: ~~~~~~ $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ~~~~~~ The script explains what it will do, and then it pauses before doing it. There are more installation options [here](https://docs.brew.sh/Installation.html). Install pip: ~~~~~~ $ sudo easy_install pip ~~~~~~ Install the required dependencies: ~~~~~~ $ brew install cmake $ brew install hdf5 $ brew install lapack $ brew install armadillo $ brew install gflags $ brew install glog $ brew install gnuradio $ brew install libmatio $ brew install log4cpp $ brew install openssl $ brew install pugixml $ brew install protobuf $ pip install mako $ pip install six ~~~~~~ #### Build GNSS-SDR Finally, you are ready to clone the GNSS-SDR repository, configure and build the software: ~~~~~~ $ git clone https://github.com/gnss-sdr/gnss-sdr $ cd gnss-sdr/build $ cmake .. $ make ~~~~~~ This will create three executables at gnss-sdr/install, namely ```gnss-sdr```, ```run_tests``` and ```volk_gnsssdr_profile```. You can install the software receiver on your system by doing: ~~~~~~ $ sudo make install ~~~~~~ Note, it is advisable not to run the install step in a homebrew environment. The documentation can be built by: ~~~~~~ $ make doc ~~~~~~ and can be viewed doing: ~~~~~~ $ open ./docs/html/index.html ~~~~~~ GNSS-SDR comes with a library which is a module of the Vector-Optimized Library of Kernels (so called [VOLK_GNSSSDR](./src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/README.md)) and a profiler that will build a config file for the best SIMD architecture for your processor. Run ```volk_gnsssdr_profile``` that is installed into ```$PREFIX/bin```. This program tests all known VOLK kernels for each architecture supported by the processor. When finished, it will write to ```$HOME/.volk_gnsssdr/volk_gnsssdr_config``` the best architecture for the VOLK function. This file is read when using a function to know the best version of the function to execute. It mimics GNU Radio's [VOLK](http://libvolk.org/) library, so if you still have not run ```volk_profile```, this is a good moment to do so. ###### Other package managers GNU Radio and other dependencies can also be installed using other package managers than Macports, such as [Fink](http://www.finkproject.org/ "Fink") or [Homebrew](https://brew.sh/ "Homebrew"). Since the version of Python that ships with OS X is great for learning but it is not good for development, you could have another Python executable in a non-standard location. If that is the case, you need to inform GNSS-SDR's configuration system by defining the `PYTHON_EXECUTABLE` variable as: ~~~~~~ cmake -DPYTHON_EXECUTABLE=/path/to/bin/python .. ~~~~~~ In case you have installed Macports in a non-standard location, you can use: ~~~~~~ $ cmake -DCMAKE_PREFIX_PATH=/opt/local -DUSE_MACPORTS_PYTHON=/opt/local/bin/python .. ~~~~~~ changing ```/opt/local``` by the base directory in which your software is installed. The CMake script will create Makefiles that download, build and link Armadillo, Gflags, Glog, Matio, Protocol Buffers, PugiXML and Google Test on the fly at compile time if they are not detected in your machine. Other builds --------- * **Docker image**: A technology providing operating-system-level virtualization to build, ship, and run distributed applications, whether on laptops, data center VMs, or the cloud. Visit [https://github.com/carlesfernandez/docker-gnsssdr](https://github.com/carlesfernandez/docker-gnsssdr) or [https://github.com/carlesfernandez/docker-pybombs-gnsssdr](https://github.com/carlesfernandez/docker-pybombs-gnsssdr) for instructions. * **Snap package**: [Snaps](https://snapcraft.io) are universal Linux packages aimed to work on any distribution or device, from IoT devices to servers, desktops to mobile devices. Visit [https://github.com/carlesfernandez/snapcraft-sandbox](https://github.com/carlesfernandez/snapcraft-sandbox) for instructions, or directly [get the software from the Snap Store](https://snapcraft.io/gnss-sdr-next):

Get GNSS-SDR from the Snap Store

* **GNSS-SDR in embedded platforms**: we provide a Software Development Kit (SDK) based on [OpenEmbedded](http://www.openembedded.org/wiki/Main_Page) for cross-compiling GNSS-SDR in your desktop computer and for producing executables that can run in embedded platforms, such as a Zedboard or a Raspberry Pi 3. Visit [Cross-compiling GNSS-SDR](https://gnss-sdr.org/docs/tutorials/cross-compiling/) for instructions. Updating GNSS-SDR ================= If you cloned or forked GNSS-SDR some time ago, it is possible that some developer has updated files at the Git repository. If you still have not done so, add the ```upstream``` repository to the list of remotes: ~~~~~~ $ git remote add upstream https://github.com/gnss-sdr/gnss-sdr.git ~~~~~~ and then you can update your working copy by doing: ~~~~~~ $ git checkout master # Switch to branch you want to update $ git pull upstream master # Download the newest code from our repository ~~~~~~ or, if you want to test the latest developments: ~~~~~~ $ git checkout next $ git pull upstream next ~~~~~~ Before rebuilding the source code, it is safe (and recommended) to remove the remainders of old compilations: ~~~~~~ $ rm -rf gnss-sdr/build/* ~~~~~~ If you are interested in contributing to the development of GNSS-SDR, please check out [how to do it](https://gnss-sdr.org/contribute/ "How to contribute to GNSS-SDR source code"). There is a more controlled way to upgrade your repository, which is to use the Git commands ```fetch``` and ```merge```, as described in our [Git Tutorial](https://gnss-sdr.org/docs/tutorials/using-git/ "Using Git"). Getting started =============== 1. After building the code, you will find the ```gnss-sdr``` executable file at gnss-sdr/install. You can make it available everywhere else by ```sudo make install```. Run the profilers ```volk_profile``` and ```volk_gnsssdr_profile``` for testing all available VOLK kernels for each architecture supported by your processor. This only has to be done once. 2. In post-processing mode, you have to provide a captured GNSS signal file. 1. The signal file can be easily recorded using the GNU Radio file sink in ```gr_complex``` mode. 2. You will need a GPS active antenna, a [USRP](https://www.ettus.com/product) and a suitable USRP daughter board to receive GPS L1 C/A signals. GNSS-SDR require to have at least 2 MHz of bandwidth in 1.57542 GHz. (remember to enable the DC bias with the daughter board jumper). We use a [DBSRX2](https://www.ettus.com/product/details/DBSRX2) to do the task, but you can try the newer Ettus' daughter boards as well. 3. The easiest way to capture a signal file is to use the GNU Radio Companion GUI. Only two blocks are needed: a USRP signal source connected to complex float file sink. You need to tune the USRP central frequency and decimation factor using USRP signal source properties box. We suggest using a decimation factor of 20 if you use the USRP2. This will give you 100/20 = 5 MSPS which will be enough to receive GPS L1 C/A signals. The front-end gain should also be configured. In our test with the DBSRX2 we obtained good results with ```G=50```. 4. Capture at least 80 seconds of signal in open sky conditions. During the process, be aware of USRP driver buffer underruns messages. If your hard disk is not fast enough to write data at this speed you can capture to a virtual RAM drive. 80 seconds of signal at 5 MSPS occupies less than 3 Gbytes using ```gr_complex```. 5. If you have no access to an RF front-end, you can download a sample raw data file (that contains GPS and Galileo signals) from [here](https://sourceforge.net/projects/gnss-sdr/files/data/). 3. You are ready to configure the receiver to use your captured file among other parameters: 1. The default configuration file resides at [/usr/local/share/gnss-sdr/conf/default.conf](./conf/gnss-sdr.conf). 2. You need to review/modify at least the following settings: * ```SignalSource.filename=``` (absolute or relative route to your GNSS signal captured file) * ```GNSS-SDR.internal_fs_sps=``` (captured file sampling rate in samples per second) * ```SignalSource.sampling_frequency=``` (captured file sampling rate in samples per second) * ```SignalConditioner.sample_freq_in=``` (captured file sampling rate in samples per second) * ```SignalConditioner.sample_freq_out=``` (captured file sampling rate in samples per second) 3. The configuration file has in-line documentation, you can try to tune the number of channels and several receiver parameters. Store your .conf file in some working directory of your choice. 4. Run the receiver invoking the configuration by ```$ gnss-sdr --config_file=/path/to/my_receiver.conf``` The program reports the current status in text mode, directly to the terminal window. If all goes well, and GNSS-SDR is able to successfully track and decode at least 4 satellites, you will get PVT fixes. The program will write .kml, .geojson and RINEX files in the folder from which ```gnss-sdr``` was run. In addition to the console output, GNSS-SDR also writes log files at /tmp/ (configurable with the commandline flag ```./gnss-sdr --log_dir=/path/to/log```). For more information, check out our [quick start guide](https://gnss-sdr.org/quick-start-guide/). Using GNSS-SDR ============== With GNSS-SDR, you can define your own receiver, work with captured raw data or from an RF front-end, dump into files intermediate signals, or tune every single algorithm used in the signal processing. All the configuration is done in a single file. Those configuration files reside at the [gnss-sdr/conf/](./conf/) folder (or at /usr/local/share/gnss-sdr/conf if you installed the program). By default, the executable ```gnss-sdr``` will read the configuration available at ```gnss-sdr/conf/gnss-sdr.conf``` (or at (usr/local/share/gnss-sdr/conf/default.conf if you installed the program). You can edit that file to fit your needs, or even better, define a new ```my_receiver.conf``` file with your own configuration. This new receiver can be generated by invoking gnss-sdr with the ```--config_file``` flag pointing to your configuration file: ~~~~~~ $ gnss-sdr --config_file=/path/to/my_receiver.conf ~~~~~~ You can use a single configuration file for processing different data files, specifying the file to be processed with the ```--signal_source``` flag: ~~~~~~ $ gnss-sdr --config_file=../conf/my_receiver.conf --signal_source=../data/my_captured_data.dat ~~~~~~ This will override the ```SignalSource.filename``` specified in the configuration file. Control plane ------------- ![](./docs/doxygen/images/GeneralBlockDiagram.png) GNSS-SDR's main method initializes the logging library, processes the command line flags, if any, provided by the user and instantiates a [ControlThread](./src/core/receiver/control_thread.h) object. Its constructor reads the configuration file, creates a control queue and creates a flowgraph according to the configuration. Then, the program's main method calls the run() method of the instantiated object, an action that connects the flowgraph and starts running it. After that, and until a stop message is received, it reads control messages sent by the receiver's modules through a safe-thread queue and processes them. Finally, when a stop message is received, the main method executes the destructor of the ControlThread object, which deallocates memory, does other cleanup and exits the program. The [GNSSFlowgraph](./src/core/receiver/gnss_flowgraph.h) class is responsible for preparing the graph of blocks according to the configuration, running it, modifying it during run-time and stopping it. Blocks are identified by its role. This class knows which roles it has to instantiate and how to connect them. It relies on the configuration to get the correct instances of the roles it needs and then it applies the connections between GNU Radio blocks to make the graph ready to be started. The complexity related to managing the blocks and the data stream is handled by GNU Radio's ```gr::top_block``` class. GNSSFlowgraph wraps the ```gr::top_block``` instance so we can take advantage of the ```gnss_block_factory``` (see below), the configuration system and the processing blocks. This class is also responsible for applying changes to the configuration of the flowgraph during run-time, dynamically reconfiguring channels: it selects the strategy for selecting satellites. This can range from a sequential search over all the satellites' ID to other more efficient approaches. The Control Plane is in charge of creating a flowgraph according to the configuration and then managing the modules. Configuration allows users to define in an easy way their own custom receiver by specifying the flowgraph (type of signal source, number of channels, algorithms to be used for each channel and each module, strategies for satellite selection, type of output format, etc.). Since it is difficult to foresee what future module implementations will be needed in terms of configuration, we used a very simple approach that can be extended without a major impact in the code. This can be achieved by simply mapping the names of the variables in the modules with the names of the parameters in the configuration. ### Configuration Properties are passed around within the program using the [ConfigurationInterface](./src/core/interfaces/configuration_interface.h) class. There are two implementations of this interface: [FileConfiguration](./src/core/receiver/file_configuration.h) and [InMemoryConfiguration](./src/core/receiver/in_memory_configuration.h). FileConfiguration reads the properties (pairs of property name and value) from a file and stores them internally. InMemoryConfiguration does not read from a file; it remains empty after instantiation and property values and names are set using the set property method. FileConfiguration is intended to be used in the actual GNSS-SDR application whereas InMemoryConfiguration is intended to be used in tests to avoid file-dependency in the file system. Classes that need to read configuration parameters will receive instances of ConfigurationInterface from where they will fetch the values. For instance, parameters related to SignalSource should look like this: ~~~~~~ SignalSource.parameter1=value1 SignalSource.parameter2=value2 ~~~~~~ The name of these parameters can be anything but one reserved word: implementation. This parameter indicates in its value the name of the class that has to be instantiated by the factory for that role. For instance, if our signal source is providing data already at baseband and thus we want to use the implementation [Pass_Through](./src/algorithms/libs/pass_through.h) for module SignalConditioner, the corresponding line in the configuration file would be ~~~~~~ SignalConditioner.implementation=Pass_Through ~~~~~~ Since the configuration is just a set of property names and values without any meaning or syntax, the system is very versatile and easily extendable. Adding new properties to the system only implies modifications in the classes that will make use of these properties. In addition, the configuration files are not checked against any strict syntax so it is always in a correct status (as long as it contains pairs of property names and values in the [INI format](https://en.wikipedia.org/wiki/INI_file)). ### GNSS block factory Hence, the application defines a simple accessor class to fetch the configuration pairs of values and passes them to a factory class called [GNSSBlockFactory](./src/core/receiver/gnss_block_factory.h). This factory decides, according to the configuration, which class needs to be instantiated and which parameters should be passed to the constructor. Hence, the factory encapsulates the complexity of blocks' instantiation. With that approach, adding a new block that requires new parameters will be as simple as adding the block class and modifying the factory to be able to instantiate it. This loose coupling between the blocks' implementations and the syntax of the configuration enables extending the application capacities in a high degree. It also allows producing fully customized receivers, for instance a testbed for acquisition algorithms, and to place observers at any point of the receiver chain. More information can be found at the [Control Plane page](https://gnss-sdr.org/docs/control-plane/). Signal Processing plane ----------------------- GNU Radio's class ```gr::basic_block``` is the abstract base class for all signal processing blocks, a bare abstraction of an entity that has a name and a set of inputs and outputs. It is never instantiated directly; rather, this is the abstract parent class of both ```gr::hier_block2```, which is a recursive container that adds or removes processing or hierarchical blocks to the internal graph, and ```gr::block```, which is the abstract base class for all the processing blocks. ![](./docs/doxygen/images/ClassHierarchy.png) A signal processing flow is constructed by creating a tree of hierarchical blocks, which at any level may also contain terminal nodes that actually implement signal processing functions. Class ```gr::top_block``` is the top-level hierarchical block representing a flowgraph. It defines GNU Radio runtime functions used during the execution of the program: run(), start(), stop(), wait(), etc. A subclass called [GNSSBlockInterface](./src/core/interfaces/gnss_block_interface.h) is the common interface for all the GNSS-SDR modules. It defines pure virtual methods, that are required to be implemented by a derived class. Subclassing GNSSBlockInterface, we defined interfaces for the GNSS receiver blocks depicted in the figure above. This hierarchy provides the definition of different algorithms and different implementations, which will be instantiated according to the configuration. This strategy allows multiple implementations sharing a common interface, achieving the objective of decoupling interfaces from implementations: it defines a family of algorithms, encapsulates each one, and makes them interchangeable. Hence, we let the algorithm vary independently of the program that uses it. Internally, GNSS-SDR makes use of the complex data types defined by [VOLK](http://libvolk.org/ "Vector-Optimized Library of Kernels home"). They are fundamental for handling sample streams in which samples are complex numbers with real and imaginary components of 8, 16 or 32 bits, common formats delivered by GNSS (and generic SDR) radio frequency front-ends. The following list shows the data type names that GNSS-SDR exposes through the configuration file: - **`byte`**: Signed integer, 8-bit two's complement number ranging from -128 to 127. C++ type name: `int8_t`. - **`short`**: Signed integer, 16-bit two's complement number ranging from -32768 to 32767. C++ type name: `int16_t` . - **`float`**: Defines numbers with fractional parts, can represent values ranging from approx. 1.5e-45 to 3.4e+38 with a precision of 7 digits (32 bits). C++ type name: `float`. - **`ibyte`**: Interleaved (I&Q) stream of samples of type `byte`. C++ type name: `int8_t`. - **`ishort`**: Interleaved (I&Q) stream of samples of type `short`. C++ type name: `int16_t`. - **`cbyte`**: Complex samples, with real and imaginary parts of type `byte`. C++ type name: `lv_8sc_t`. - **`cshort`**: Complex samples, with real and imaginary parts of type `short`. C++ type name: `lv_16sc_t`. - **`gr_complex`**: Complex samples, with real and imaginary parts of type `float`. C++ type name: `std::complex`. More information about the available processing blocks and their configuration parameters can be found at the [Signal Processing Blocks documentation page](https://gnss-sdr.org/docs/sp-blocks/). ### Signal Source The input of a software receiver are the raw bits that come out from the front-end's analog-to-digital converter (ADC). Those bits can be read from a file stored in the hard disk or directly in real-time from a hardware device through USB or Ethernet buses. The Signal Source module is in charge of implementing the hardware driver, that is, the portion of the code that communicates with the RF front-end and receives the samples coming from the ADC. This communication is usually performed through USB or Ethernet buses. Since real-time processing requires a highly optimized implementation of the whole receiver, this module also allows reading samples from a file stored in a hard disk, and thus processing without time constraints. Relevant parameters of those samples are the intermediate frequency (or baseband I&Q components), the sampling rate and number of bits per sample, that must be specified by the user in the configuration file. This module also performs bit-depth adaptation, since most of the existing RF front-ends provide samples quantized with 2 or 3 bits, while operations inside the processor are performed on 32- or 64-bit words, depending on its architecture. Although there are implementations of the most intensive computational processes (mainly correlation) that take advantage of specific data types and architectures for the sake of efficiency, the approach is processor-specific and hardly portable. We suggest to keep signal samples in standard data types and letting the compiler select the best library version (implemented using SIMD or any other processor-specific technology) of the required routines for a given processor. ***Example: File Signal Source*** The user can configure the receiver for reading from a file, setting in the configuration file the data file location, sample format, and the sampling frequency and intermediate frequency at what the signal was originally captured. ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/home/user/gnss-sdr/data/my_capture.dat SignalSource.item_type=gr_complex SignalSource.sampling_frequency=4000000 ; Sampling frequency in samples per second (Sps) ~~~~~~ Type ```gr_complex``` refers to a GNU Radio typedef equivalent to ```std::complex```. In order to save some storage space, you might want to store your signal in a more efficient format such as an I/Q interleaved ```short`` integer sample stream. In that case, change the corresponding line to: ~~~~~~ SignalSource.item_type=ishort ~~~~~~ In this latter case, you will need to convert the interleaved I/Q samples to a complex stream via Data Type Adapter block (see below). ***Example: Two-bit packed file source*** Sometimes, samples are stored in files with a format which is not in the list of _native_ types supported by the ```File_Signal_Source``` implementation (i.e, it is not among ```byte```, ```ibyte```, ```short```, ```ishort```, ```float``` or ```gr_complex```). This is the case of 2-bit samples, which is a common format delivered by GNSS RF front-ends. The ```Two_Bit_Packed_File_Signal_Source``` implementation allows reading two-bit length samples from a file. The data is assumed to be packed as bytes ```item_type=byte``` or shorts ```item_type=short``` so that there are 4 two bit samples in each byte. The two bit values are assumed to have the following interpretation: | **b_1** | **b_0** | **Value** | |:-------:|:--------:|:----------:| | 0 | 0 | +1 | | 0 | 1 | +3 | | 1 | 0 | -3 | | 1 | 1 | -1 | Within a byte the samples may be packed in big endian ```big_endian_bytes=true``` (if the most significant byte value is stored at the memory location with the lowest address, the next byte value in significance is stored at the following memory location, and so on) or little endian ```big_endian_bytes=false``` (if the least significant byte value is at the lowest address, and the other bytes follow in increasing order of significance). If the order is big endian then the most significant two bits will form the first sample output, otherwise the least significant two bits will be used. Additionally, the samples may be either real ```sample_type=real```, or complex. If the sample type is complex, then the samples are either stored in the order: real, imag, real, imag, ... ```sample_type=iq``` or in the order: imag, real, imag, real, ... ```sample_type=qi```. Finally, if the data is stored as shorts ```item_type=short```, then it may be stored in either big endian ```big_endian_items=true``` or little endian ```big_endian_items=false```. If the shorts are big endian then the 2nd byte in each short is output first. The output data type is either ```float``` or ```gr_complex``` depending on whether or not ```sample_type``` is real. Example: ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Two_Bit_Packed_File_Signal_Source SignalSource.filename=/data/my_capture.datz SignalSource.item_type=short SignalSource.sampling_frequency=60000000 SignalSource.freq=1575468750 SignalSource.samples=6000000000 ; Notice that 0 indicates the entire file. SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=./signal_source.dat SignalSource.enable_throttle_control=false SignalSource.sample_type=iq SignalSource.big_endian_items=true SignalSource.big_endian_bytes=false ~~~~~~ ***Example: UHD Signal Source*** The user may prefer to use a [UHD](https://files.ettus.com/manual/)-compatible RF front-end and try real-time processing. For instance, for a USRP1 + DBSRX daughterboard, use: ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=4000000 ; Sampling frequency in [Hz] SignalSource.freq=1575420000 ; RF front-end center frequency in [Hz] SignalSource.gain=60 ; Front-end gain in dB SignalSource.subdevice=B:0 ; UHD subdevice specification (for USRP1 use A:0 or B:0, for USRP B210 use A:0) ~~~~~~ ***Example: Configuring the USRP X300/X310 with two front-ends for receiving signals in L1 and L2 bands*** ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.device_address=192.168.40.2 ; Put your USRP IP address here SignalSource.item_type=gr_complex SignalSource.RF_channels=2 SignalSource.sampling_frequency=4000000 SignalSource.subdevice=A:0 B:0 ;######### RF Channels specific settings ###### SignalSource.freq0=1575420000 SignalSource.gain0=50 SignalSource.samples0=0 SignalSource.dump0=false SignalSource.freq1=1227600000 SignalSource.gain1=50 SignalSource.samples1=0 SignalSource.dump1=false ~~~~~~ ***Example: OsmoSDR-compatible Signal Source*** [OsmoSDR](http://sdr.osmocom.org/trac) is a small form-factor, inexpensive software defined radio project. It provides a driver for several front-ends, such as [RTL-based dongles](https://www.rtl-sdr.com/tag/v3/), [HackRF](https://greatscottgadgets.com/hackrf/), [bladeRF](https://www.nuand.com/), [LimeSDR](https://myriadrf.org/projects/limesdr/), [etc](https://github.com/osmocom/gr-osmosdr/blob/master/README). Note that not all the OsmoSDR-compatible devices can work as radio frequency front-ends for proper GNSS signal reception, please check the specifications. For suitable RF front-ends, you can use: ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Osmosdr_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 SignalSource.freq=1575420000 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.enable_throttle_control=false SignalSource.osmosdr_args=hackrf,bias=1 ~~~~~~ For [RTL-SDR Blog V3](https://www.rtl-sdr.com/tag/v3/) dongles, the arguments are: ~~~~~~ SignalSource.osmosdr_args=rtl,bias=1 ~~~~~~ and for [LimeSDR](https://myriadrf.org/projects/limesdr/): ~~~~~~ SignalSource.osmosdr_args=driver=lime,soapy=0 ~~~~~~ In case of using a Zarlink's RTL2832 based DVB-T receiver, you can even use the ```rtl_tcp``` I/Q server in order to use the USB dongle remotely. In a terminal, type: ~~~~~~ $ rtl_tcp -a 127.0.0.1 -p 1234 -f 1575420000 -g 0 -s 2000000 ~~~~~~ and use the following configuration: ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=RtlTcp_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=1200000 SignalSource.freq=1575420000 SignalSource.gain=40 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.AGC_enabled=false SignalSource.samples=0 SignalSource.enable_throttle_control=false SignalSource.address=127.0.0.1 SignalSource.port=1234 SignalSource.swap_iq=false SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat ~~~~~~ Example for a dual-frequency receiver: ~~~~~~ ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.device_address=192.168.40.2 ; Put your USRP IP address here SignalSource.item_type=gr_complex SignalSource.RF_channels=2 SignalSource.sampling_frequency=4000000 SignalSource.subdevice=A:0 B:0 ;######### RF Channels specific settings ###### SignalSource.freq0=1575420000 SignalSource.gain0=50 SignalSource.samples0=0 SignalSource.dump0=false SignalSource.freq1=1227600000 SignalSource.gain1=50 SignalSource.samples1=0 SignalSource.dump1=false ~~~~~~ More documentation and examples are available at the [Signal Source Blocks page](https://gnss-sdr.org/docs/sp-blocks/signal-source/). ### Signal Conditioner ![](./docs/doxygen/images/SignalConditioner.png) The signal conditioner is in charge of resampling the signal and delivering a reference sample rate to the downstream processing blocks, acting as a facade between the signal source and the synchronization channels, providing a simplified interface to the input signal. In case of multiband front-ends, this module would be in charge of providing a separated data stream for each band. If your signal source is providing baseband signal samples of type ```gr_complex``` at 4 Msps, you can bypass the Signal Conditioner block by: ~~~~~~ SignalConditioner.implementation=Pass_Through ~~~~~~ If you need to adapt some aspect of your signal, you can enable the Signal Conditioner and configure three internal blocks: a data type adapter, an input signal and a resampler. ~~~~~~ ;#[Signal_Conditioner] enables this block. Then you have to configure [DataTypeAdapter], [InputFilter] and [Resampler] blocks SignalConditioner.implementation=Signal_Conditioner ~~~~~~ More documentation at the [Signal Conditioner Blocks page](https://gnss-sdr.org/docs/sp-blocks/signal-conditioner/). #### Data type adapter This block changes the type of input data samples. If your signal source delivers data samples of type ```short```, you can use this block to convert them to ```gr_complex``` like this: ~~~~~~ ;######### DATA_TYPE_ADAPTER CONFIG ############ ;#implementation: [Pass_Through] disables this block DataTypeAdapter.implementation=Ishort_To_Complex ~~~~~~ More documentation at the [Data Type Adapter Blocks page](https://gnss-sdr.org/docs/sp-blocks/data-type-adapter/). #### Input filter This block filters the input data. It can be combined with frequency translation for IF signals. The computation of the filter taps is based on parameters of GNU Radio's function [pm_remez](https://gnuradio.org/doc/doxygen/pm__remez_8h.html), that calculates the optimal (in the Chebyshev/minimax sense) FIR filter impulse response given a set of band edges, the desired response on those bands, and the weight given to the error in those bands. The block can be configured like this: ~~~~~~ ;######### INPUT_FILTER CONFIG ############ ;#implementation: Use [Pass_Through] or [Fir_Filter] or [Freq_Xlating_Fir_Filter] ;#[Pass_Through] disables this block ;#[Fir_Filter] enables a FIR Filter ;#[Freq_Xlating_Fir_Filter] enables FIR filter and a composite frequency translation that shifts IF down to zero Hz. InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false ; #dump: Dump the filtered data to a file. InputFilter.dump_filename=../data/input_filter.dat ; #dump_filename: Log path and filename. InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 ; #number_of_taps: Number of taps in the filter. Increasing this parameter increases the processing time InputFilter.number_of_bands=2 ; #number_of_bands: Number of frequency bands in the filter. ; Frequency is in the range [0, 1], with 1 being the Nyquist frequency (Fs/2) ; The number of band_begin and band_end elements must match the number of bands InputFilter.band1_begin=0.0 InputFilter.band1_end=0.85 InputFilter.band2_begin=0.90 InputFilter.band2_end=1.0 ;#ampl: desired amplitude at the band edges. ;#The number of ampl_begin and ampl_end elements must match the number of bands InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 ;#band_error: weighting applied to each band (usually 1). ;#The number of band_error elements must match the number of bands InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 ;#filter_type: one of "bandpass", "hilbert" or "differentiator" InputFilter.filter_type=bandpass ;#grid_density: determines how accurately the filter will be constructed. ;The minimum value is 16; higher values are slower to compute the filter. InputFilter.grid_density=16 ;#The following options are used only in Freq_Xlating_Fir_Filter implementation. ;#InputFilter.IF is the intermediate frequency (in Hz) shifted down to zero Hz InputFilter.sampling_frequency=4000000 InputFilter.IF=0 InputFilter.decimation_factor=1 ~~~~~~ More documentation at the [Input Filter Blocks page](https://gnss-sdr.org/docs/sp-blocks/input-filter/). #### Resampler This block resamples the input data stream. The ```Direct_Resampler``` block implements a nearest neighbourhood interpolation: ~~~~~~ ;######### RESAMPLER CONFIG ############ ;#implementation: Use [Pass_Through] or [Direct_Resampler] ;#[Pass_Through] disables this block Resampler.implementation=Direct_Resampler Resampler.dump=false ; Dumps the resampled data to a file. Resampler.dump_filename=../data/resampler.dat ; log path and filename. Resampler.item_type=gr_complex Resampler.sample_freq_in=8000000 ; sample frequency of the input signal Resampler.sample_freq_out=4000000 ; desired sample frequency of the output signal ~~~~~~ More documentation at the [Resampler Blocks page](https://gnss-sdr.org/docs/sp-blocks/resampler/). ### Channel A channel encapsulates all signal processing devoted to a single satellite. Thus, it is a large composite object which encapsulates the acquisition, tracking and navigation data decoding modules. As a composite object, it can be treated as a single entity, meaning that it can be easily replicated. Since the number of channels is selectable by the user in the configuration file, this approach helps to improve the scalability and maintainability of the receiver. Each channel must be assigned to a GNSS signal, according to the following identifiers: | **Signal** | **Identifier** | |:------------------|:---------------:| | GPS L1 C/A | 1C | | Galileo E1b/c | 1B | | Glonass L1 C/A | 1G | | Beidou B1I | B1 | | Beidou B3I | B3 | | GPS L2 L2C(M) | 2S | | Glonass L2 C/A | 2G | | GPS L5 | L5 | | Galileo E5a | 5X | Example: Eight GPS L1 C/A channels. ~~~~~~ ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 ; Number of available GPS L1 C/A channels. Channels_1B.count=0 ; Number of available Galileo E1B channels. Channels.in_acquisition=1 ; Number of channels simultaneously acquiring Channel.signal=1C ; ~~~~~~ Example: Four GPS L1 C/A and four Galileo E1B channels. ~~~~~~ ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=4 ; Number of available GPS L1 C/A channels. Channels_1B.count=4 ; Number of available Galileo E1B channels. Channels.in_acquisition=1 ; Number of channels simultaneously acquiring Channel0.signal=1C ; Channel1.signal=1C ; Channel2.signal=1C ; Channel3.signal=1C ; Channel4.signal=1B ; Channel5.signal=1B ; Channel6.signal=1B ; Channel7.signal=1B ; ~~~~~~ This module is also in charge of managing the interplay between acquisition and tracking. Acquisition can be initialized in several ways, depending on the prior information available (called cold start when the receiver has no information about its position nor the satellites' almanac; warm start when a rough location and the approximate time of day are available, and the receiver has a recently recorded almanac broadcast; or hot start when the receiver was tracking a satellite and the signal line of sight broke for a short period of time, but the ephemeris and almanac data is still valid, or this information is provided by other means), and an acquisition process can finish deciding that the satellite is not present, that longer integration is needed in order to confirm the presence of the satellite, or declaring the satellite present. In the latter case, acquisition process should stop and trigger the tracking module with coarse estimations of the synchronization parameters. The mathematical abstraction used to design this logic is known as finite state machine (FSM), that is a behavior model composed of a finite number of states, transitions between those states, and actions. The abstract class [ChannelInterface](./src/core/interfaces/channel_interface.h) represents an interface to a channel GNSS block. Check [Channel](./src/algorithms/channel/adapters/channel.h) for an actual implementation. More documentation at the [Channels page](https://gnss-sdr.org/docs/sp-blocks/channels/). #### Acquisition The first task of a GNSS receiver is to detect the presence or absence of in-view satellites. This is done by the acquisition system process, which also provides a coarse estimation of two signal parameters: the frequency shift with respect to the nominal frequency, and a delay term which allows the receiver to create a local code aligned with the incoming code. [AcquisitionInterface](./src/core/interfaces/acquisition_interface.h) is the common interface for all the acquisition algorithms and their corresponding implementations. Algorithms' interface, that may vary depending on the use of information external to the receiver, such as in Assisted GNSS, is defined in classes referred to as *adapters*. These adapters wrap the GNU Radio blocks interface into a compatible interface expected by AcquisitionInterface. This allows the use of existing GNU Radio blocks derived from ```gr::block```, and ensures that newly developed implementations will also be reusable in other GNU Radio-based applications. Moreover, it adds still another layer of abstraction, since each given acquisition algorithm can have different implementations (for instance using different numerical libraries). In such a way, implementations can be continuously improved without having any impact neither on the algorithm interface nor the general acquisition interface. Check [GpsL1CaPcpsAcquisition](./src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h) and [GalileoE1PcpsAmbiguousAcquisition](./src/algorithms/acquisition/adapters/galileo_e1_pcps_ambiguous_acquisition.h) for examples of adapters from a Parallel Code Phase Search (PCPS) acquisition block, and [pcps_acquisition_cc](./src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_cc.h) for an example of a block implementation. The source code of all the available acquisition algorithms is located at: ~~~~~~ |-gnss-sdr |---src |-----algorithms |-------acquisition |---------adapters <- Adapters of the processing blocks to an AcquisitionInterface |---------gnuradio_blocks <- Signal processing blocks implementation ~~~~~~ The user can select a given implementation for the algorithm to be used in each receiver channel, as well as their parameters, in the configuration file. For a GPS L1 C/A receiver: ~~~~~~ ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition ; Acquisition algorithm selection for this channel Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 ; Signal block duration for the acquisition signal detection [ms] Acquisition_1C.threshold=0.005 ; Acquisition threshold Acquisition_1C.pfa=0.0001 ; Acquisition false alarm probability. This option overrides the threshold option. ; Only use with implementations: [GPS_L1_CA_PCPS_Acquisition] or [Galileo_E1_PCPS_Ambiguous_Acquisition] Acquisition_1C.doppler_max=10000 ; Maximum expected Doppler shift [Hz] Acquisition_1C.doppler_step=500 ; Doppler step in the grid search [Hz] Acquisition_1C.dump=false ; Enables internal data file logging [true] or [false] Acquisition_1C.dump_filename=./acq_dump.dat ; Log path and filename ~~~~~~ and, for Galileo E1B channels: ~~~~~~ ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 Acquisition_1B.pfa=0.0000008 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ~~~~~~ More documentation at the [Acquisition Blocks page](https://gnss-sdr.org/docs/sp-blocks/acquisition/). #### Tracking When a satellite is declared present, the parameters estimated by the acquisition module are then fed to the receiver tracking module, which represents the second stage of the signal processing unit, aiming to perform a local search for accurate estimates of code delay and carrier phase, and following their eventual variations. Again, a class hierarchy consisting of a [TrackingInterface](./src/core/interfaces/tracking_interface.h) class and subclasses implementing algorithms provides a way of testing different approaches, with full access to their parameters. Check [GpsL1CaDllPllTracking](./src/algorithms/tracking/adapters/gps_l1_ca_dll_pll_tracking.h) or [GalileoE1DllPllVemlTracking](./src/algorithms/tracking/adapters/galileo_e1_dll_pll_veml_tracking.h) for examples of adapters, and [Gps_L1_Ca_Dll_Pll_Tracking_cc](./src/algorithms/tracking/gnuradio_blocks/gps_l1_ca_dll_pll_tracking_cc.h) for an example of a signal processing block implementation. There are also available some useful classes and functions for signal tracking; take a look at [cpu_multicorrelator.h](./src/algorithms/tracking/libs/cpu_multicorrelator.h), [lock_detectors.h](./src/algorithms/tracking/libs/lock_detectors.h), [tracking_discriminators.h](./src/algorithms/tracking/libs/tracking_discriminators.h) or [tracking_2nd_DLL_filter.h](./src/algorithms/tracking/libs/tracking_2nd_DLL_filter.h). The source code of all the available tracking algorithms is located at: ~~~~~~ |-gnss-sdr |---src |-----algorithms |-------tracking |---------adapters <- Adapters of the processing blocks to a TrackingInterface |---------gnuradio_blocks <- Signal processing blocks implementation |---------libs <- libraries of tracking objects (e.g. correlators, discriminators, and so on) ~~~~~~ The user can select a given implementation for the algorithm to be used in all the tracking blocks, as well as its parameters, in the configuration file. For instance, for GPS l1 channels: ~~~~~~ ;######### TRACKING GPS L1 CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=50.0 ; PLL loop filter bandwidth [Hz] Tracking_1C.dll_bw_hz=2.0 ; DLL loop filter bandwidth [Hz] Tracking_1C.pll_filter_order=3 ; PLL loop filter order [2] or [3] Tracking_1C.dll_filter_order=2 ; DLL loop filter order [1], [2] or [3] Tracking_1C.early_late_space_chips=0.5 ; correlator early-late space [chips]. Tracking_1C.dump=false ; Enable internal binary data file logging [true] or [false] Tracking_1C.dump_filename=./tracking_ch_ ; Log path and filename. Notice that the tracking channel will add "x.dat" where x is the channel number. ~~~~~~ and, for Galileo E1B channels: ~~~~~~ ;######### TRACKING GALILEO E1B CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.pll_filter_order=3 ; PLL loop filter order [2] or [3] Tracking_1B.dll_filter_order=2 ; DLL loop filter order [1], [2] or [3] Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ~~~~~~ More documentation at the [Tracking Blocks page](https://gnss-sdr.org/docs/sp-blocks/tracking/). #### Decoding of the navigation message Most of GNSS signal links are modulated by a navigation message containing the time the message was transmitted, orbital parameters of satellites (also known as ephemeris) and an almanac (information about the general system health, rough orbits of all satellites in the network as well as data related to error correction). Navigation data bits are structured in words, pages, subframes, frames and superframes. Sometimes, bits corresponding to a single parameter are spread over different words, and values extracted from different frames are required for proper decoding. Some words are for synchronization purposes, others for error control and others contain actual information. There are also error control mechanisms, from parity checks to forward error correction (FEC) encoding and interleaving, depending on the system. All this decoding complexity is managed by a finite state machine. The common interface is [TelemetryDecoderInterface](./src/core/interfaces/telemetry_decoder_interface.h). Check [GpsL1CaTelemetryDecoder](./src/algorithms/telemetry_decoder/adapters/gps_l1_ca_telemetry_decoder.h) for an example of the GPS L1 NAV message decoding adapter, and [gps_l1_ca_telemetry_decoder_cc](./src/algorithms/telemetry_decoder/gnuradio_blocks/gps_l1_ca_telemetry_decoder_cc.h) for an actual implementation of a signal processing block. Configuration example: ~~~~~~ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ~~~~~~ In case you are configuring a multi-system receiver, you will need to decimate the one with the fastest code rate in order to get both data streams synchronized. For instance, for hybrid GPS L1 / Galileo E1B receivers: ~~~~~~ ;######### TELEMETRY DECODER GPS L1 CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO E1B CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ~~~~~~ More documentation at the [Telemetry Decoder Blocks page](https://gnss-sdr.org/docs/sp-blocks/telemetry-decoder/). #### Observables GNSS systems provide different kinds of observations. The most commonly used are the code observations, also called pseudoranges. The *pseudo* comes from the fact that on the receiver side the clock error is unknown and thus the measurement is not a pure range observation. High accuracy applications also use the carrier phase observations, which are based on measuring the difference between the carrier phase transmitted by the GNSS satellites and the phase of the carrier generated in the receiver. Both observables are computed from the outputs of the tracking module and the decoding of the navigation message. This module collects all the data provided by every tracked channel, aligns all received data into a coherent set, and computes the observables. The common interface is [ObservablesInterface](./src/core/interfaces/observables_interface.h). Configuration example: ~~~~~~ ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ~~~~~~ More documentation at the [Observables Blocks page](https://gnss-sdr.org/docs/sp-blocks/observables/). #### Computation of Position, Velocity and Time Although data processing for obtaining high-accuracy PVT solutions is out of the scope of GNSS-SDR, we provide a module that can compute position fixes (stored in GIS-friendly formats such as [GeoJSON](https://tools.ietf.org/html/rfc7946), [GPX](http://www.topografix.com/gpx.asp) and [KML](http://www.opengeospatial.org/standards/kml), or transmitted via serial port as [NMEA 0183](https://en.wikipedia.org/wiki/NMEA_0183) messages), and leaves room for more sophisticated positioning methods by storing observables and navigation data in [RINEX](https://en.wikipedia.org/wiki/RINEX) files (v2.11 or v3.02), and generating [RTCM](http://www.rtcm.org "Radio Technical Commission for Maritime Services") 3.2 messages that can be disseminated through the Internet in real time. The common interface is [PvtInterface](./src/core/interfaces/pvt_interface.h). Configuration example: ~~~~~~ ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen PVT.rinex_version=2 ; options: 2 or 3 PVT.output_rate_ms=100 ; Period in [ms] between two PVT outputs PVT.display_rate_ms=500 ; Position console print (std::out) interval [ms]. PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea ; NMEA log path and filename PVT.flag_nmea_tty_port=false ; Enables the NMEA log to a serial TTY port PVT.nmea_dump_devname=/dev/pts/4 ; serial device descriptor for NMEA logging PVT.flag_rtcm_server=true ; Enables or disables a TCP/IP server dispatching RTCM messages PVT.flag_rtcm_tty_port=false ; Enables the RTCM log to a serial TTY port PVT.rtcm_dump_devname=/dev/pts/1 ; serial device descriptor for RTCM logging PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 ~~~~~~ **Notes on the output formats:** * **GeoJSON** is a geospatial data interchange format based on JavaScript Object Notation (JSON) supported by numerous mapping and GIS software packages, including [OpenLayers](https://openlayers.org), [Leaflet](https://leafletjs.com), [MapServer](http://www.mapserver.org), [GeoServer](http://geoserver.org), [GeoDjango](https://www.djangoproject.com), [GDAL](http://www.gdal.org), and [CartoDB](https://cartodb.com). It is also possible to use GeoJSON with [PostGIS](https://postgis.net/) and [Mapnik](http://mapnik.org), both of which handle the format via the GDAL OGR conversion library. The [Google Maps Javascript API](https://developers.google.com/maps/documentation/javascript/) v3 directly supports the [integration of GeoJSON data layers](https://developers.google.com/maps/documentation/javascript/examples/layer-data-simple), and [GitHub also supports GeoJSON rendering](https://github.com/blog/1528-there-s-a-map-for-that). * **KML** (Keyhole Markup Language) is an XML grammar used to encode and transport representations of geographic data for display in an earth browser. KML is an open standard officially named the OpenGIS KML Encoding Standard (OGC KML), and it is maintained by the Open Geospatial Consortium, Inc. (OGC). KML files can be displayed in geobrowsers such as [Google Earth](https://www.google.com/earth/), [Marble](https://marble.kde.org), [osgEarth](http://osgearth.org), or used with the [NASA World Wind SDK for Java](https://worldwind.arc.nasa.gov/java/). * **GPX** (the GPS Exchange Format) is a light-weight XML data format for the interchange of GPS data (waypoints, routes, and tracks) between applications and Web services on the Internet. The format is open and can be used without the need to pay license fees, and it is supported by a [large list of software tools](http://www.topografix.com/gpx_resources.asp). * **NMEA 0183** is a combined electrical and data specification for communication between marine electronics such as echo sounder, sonars, anemometer, gyrocompass, autopilot, GPS receivers and many other types of instruments. It has been defined by, and is controlled by, the U.S. [National Marine Electronics Association](http://www.nmea.org/). The NMEA 0183 standard uses a simple ASCII, serial communications protocol that defines how data are transmitted in a *sentence* from one *talker* to multiple *listeners* at a time. Through the use of intermediate expanders, a talker can have a unidirectional conversation with a nearly unlimited number of listeners, and using multiplexers, multiple sensors can talk to a single computer port. At the application layer, the standard also defines the contents of each sentence (message) type, so that all listeners can parse messages accurately. Those messages can be sent through the serial port (that could be for instance a Bluetooth link) and be used/displayed by a number of software applications such as [gpsd](http://www.catb.org/gpsd/ "The UNIX GPS daemon"), [JOSM](https://josm.openstreetmap.de/ "The Java OpenStreetMap Editor"), [OpenCPN](https://opencpn.org/ "Open Chart Plotter Navigator"), and many others (and maybe running on other devices). * **RINEX** (Receiver Independent Exchange Format) is an interchange format for raw satellite navigation system data, covering observables and the information contained in the navigation message broadcast by GNSS satellites. This allows the user to post-process the received data to produce a more accurate result (usually with other data unknown to the original receiver, such as better models of the atmospheric conditions at time of measurement). RINEX files can be used by software packages such as [GPSTk](http://www.gpstk.org), [RTKLIB](http://www.rtklib.com/) and [gLAB](http://gage14.upc.es/gLAB/). GNSS-SDR by default generates RINEX version [3.02](https://igscb.jpl.nasa.gov/igscb/data/format/rinex302.pdf). If [2.11](https://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt) is needed, it can be requested through the `rinex_version` parameter in the configuration file: ~~~~~~ PVT.rinex_version=2 ~~~~~~ * **RTCM SC-104** provides standards that define the data structure for differential GNSS correction information for a variety of differential correction applications. Developed by the Radio Technical Commission for Maritime Services ([RTCM](http://www.rtcm.org/differential-global-navigation-satellite--dgnss--standards.html "Radio Technical Commission for Maritime Services")), they have become an industry standard for communication of correction information. GNSS-SDR implements RTCM version 3.2, defined in the document *RTCM 10403.2, Differential GNSS (Global Navigation Satellite Systems) Services - Version 3* (February 1, 2013), which can be [purchased online](https://ssl29.pair.com/dmarkle/puborder.php?show=3 "RTCM Online Publication Order Form"). By default, the generated RTCM binary messages are dumped into a text file in hexadecimal format. However, GNSS-SDR is equipped with a TCP/IP server, acting as an NTRIP source that can feed an NTRIP server. NTRIP (Networked Transport of RTCM via Internet Protocol) is an open standard protocol that can be freely downloaded from [BKG](https://igs.bkg.bund.de/root_ftp/NTRIP/documentation/NtripDocumentation.pdf "Networked Transport of RTCM via Internet Protocol (Ntrip) Version 1.0"), and it is designed for disseminating differential correction data (*e.g.* in the RTCM-104 format) or other kinds of GNSS streaming data to stationary or mobile users over the Internet. The TCP/IP server can be enabled by setting ```PVT.flag_rtcm_server=true``` in the configuration file, and will be active during the execution of the software receiver. By default, the server will operate on port 2101 (which is the recommended port for RTCM services according to the Internet Assigned Numbers Authority, [IANA](https://www.iana.org/assignments/service-names-port-numbers/ "Service Name and Transport Protocol Port Number Registry")), and will identify the Reference Station with ID=1234. This behaviour can be changed in the configuration file: ~~~~~~ PVT.flag_rtcm_server=true PVT.rtcm_tcp_port=2102 PVT.rtcm_station_id=1111 ~~~~~~ **Important note:** In order to get well-formatted GeoJSON, KML and RINEX files, always terminate ```gnss-sdr``` execution by pressing key ```q``` and then key ```ENTER```. Those files will be automatically deleted if no position fix have been obtained during the execution of the software receiver. More documentation at the [PVT Blocks page](https://gnss-sdr.org/docs/sp-blocks/pvt/). About the software license ========================== GNSS-SDR is released under the [General Public License (GPL) v3](https://www.gnu.org/licenses/gpl.html), thus securing practical usability, inspection, and continuous improvement by the research community, allowing the discussion based on tangible code and the analysis of results obtained with real signals. The GPL implies that: 1. Copies may be distributed free of charge or for money, but the source code has to be shipped or provided free of charge (or at cost price) on demand. The receiver of the source code has the same rights meaning he can share copies free of charge or resell. 2. The licensed material may be analyzed or modified. 3. Modified material may be distributed under the same licensing terms but *do not* have to be distributed. That means that modifications only have to be made available to the public if distribution happens. So it is perfectly fine to take the GNSS-SDR source code, modify it heavily and use it in a not distributed application / library. This is how companies like Google can run their own patched versions of Linux for example. But what this also means is that non-GPL code cannot use GPL code. This means that you cannot modify / use GNSS-SDR, blend it with non-GPL code, and make money with the resulting software. You cannot distribute the resulting software under a non-disclosure agreement or contract. Distributors under the GPL also grant a license for any of their patents practiced by the software, to practice those patents in GPL software. You can sell a device that runs with GNSS-SDR, but if you distribute the code, it has to remain under GPL. Publications and Credits ======================== If you use GNSS-SDR to produce a research paper or Thesis, we would appreciate if you reference the following article to credit the GNSS-SDR project: * C. Fernández-Prades, J. Arribas, P. Closas, C. Avilés, and L. Esteve, [GNSS-SDR: an open source tool for researchers and developers](http://www.cttc.es/publication/gnss-sdr-an-open-source-tool-for-researchers-and-developers/), in Proceedings of the 24th International Technical Meeting of The Satellite Division of the Institute of Navigation (ION GNSS), Portland, Oregon, Sept. 19-23, 2011, pp. 780-794. For LaTeX users, this is the BibTeX entry for your convenience: ~~~~~~ @INPROCEEDINGS{GNSS-SDR11, AUTHOR = {C.~{Fern\'{a}ndez--Prades} and J.~Arribas and P.~Closas and C.~Avil\'{e}s and L.~Esteve}, TITLE = {{GNSS-SDR}: An Open Source Tool For Researchers and Developers}, BOOKTITLE = {Proc. 24th Intl. Tech. Meeting Sat. Div. Inst. Navig.}, YEAR = {2011}, PAGES = {780--794}, ADDRESS = {Portland, Oregon}, MONTH = {Sept.} } ~~~~~~ There is a list of papers related to GNSS-SDR in our [publications page](https://gnss-sdr.org/publications/ "Publications"). Ok, now what? ============= In order to start using GNSS-SDR, you may want to populate ```gnss-sdr/data``` folder (or anywhere else on your system) with raw data files. By "raw data" we mean the output of a Radio Frequency front-end's Analog-to-Digital converter. GNSS-SDR needs signal samples already in baseband or in passband, at a suitable intermediate frequency (on the order of MHz). Prepare your configuration file, and then you are ready for running ```gnss-sdr --config_file=your_configuration.conf```, and seeing how the file is processed. Another interesting option is working in real-time with an RF front-end. We provide drivers for UHD-compatible hardware such as the [USRP family](https://www.ettus.com/product), for OsmoSDR and other front-ends (HackRF, bladeRF, LimeSDR), for the GN3S v2 USB dongle and for some DVB-T USB dongles. Start with a low number of channels and then increase it in order to test how many channels your processor can handle in real-time. You can find more information at the [GNSS-SDR Documentation page](https://gnss-sdr.org/docs/) or directly asking to the [GNSS-SDR Developers mailing list](https://lists.sourceforge.net/lists/listinfo/gnss-sdr-developers). You are also very welcome to contribute to the project, there are many ways to [participate in GNSS-SDR](https://gnss-sdr.org/contribute/). If you need some special feature not yet implemented, the Developer Team would love to be hired for developing it. Please do not hesitate to [contact them](https://gnss-sdr.org/team/). **Enjoy GNSS-SDR!** The Developer Team. build/000077500000000000000000000000001352176506000121625ustar00rootroot00000000000000build/.gitignore000066400000000000000000000001061352176506000141470ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignorecmake/000077500000000000000000000000001352176506000121435ustar00rootroot00000000000000cmake/Modules/000077500000000000000000000000001352176506000135535ustar00rootroot00000000000000cmake/Modules/AvoidAccelerate.cmake000066400000000000000000000031011352176506000175630ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # Avoid using the BLAS and LAPACK implementations that comes with the Accelerate # framework, which causes a bug when the BeiDou constellation is enabled find_library(BLAS_LIBRARIES libblas.dylib PATHS /opt/local/lib/lapack /usr/local/opt/lapack/lib /usr/local/lib ${BLAS_ROOT}/lib $ENV{BLAS_ROOT}/lib NO_DEFAULT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ) if(BLAS_LIBRARIES) set(BLAS_FOUND TRUE) endif() find_library(LAPACK_LIBRARIES liblapack.dylib PATHS /opt/local/lib/lapack /usr/local/opt/lapack/lib /usr/local/lib ${BLAS_ROOT}/lib $ENV{BLAS_ROOT}/lib NO_DEFAULT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ) if(LAPACK_LIBRARIES) set(LAPACK_FOUND TRUE) endif() cmake/Modules/FindFILESYSTEM.cmake000066400000000000000000000207201352176506000170430ustar00rootroot00000000000000# Copyright (C) 2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # Original Source: https://github.com/vector-of-bool/CMakeCM # Modified by C. Fernandez-Prades. #[=======================================================================[.rst: FindFILESYSTEM ############## This module supports the C++17 standard library's filesystem utilities. Use the :imp-target:`std::filesystem` imported target to Options ******* The ``COMPONENTS`` argument to this module supports the following values: .. find-component:: Experimental :name: fs.Experimental Allows the module to find the "experimental" Filesystem TS version of the Filesystem library. This is the library that should be used with the ``std::experimental::filesystem`` namespace. .. find-component:: Final :name: fs.Final Finds the final C++17 standard version of the filesystem library. If no components are provided, behaves as if the :find-component:`fs.Final` component was specified. If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are provided, first looks for ``Final``, and falls back to ``Experimental`` in case of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all :ref:`variables ` will refer to the ``Final`` version. Imported Targets **************** .. imp-target:: std::filesystem The ``std::filesystem`` imported target is defined when any requested version of the C++ filesystem library has been found, whether it is *Experimental* or *Final*. If no version of the filesystem library is available, this target will not be defined. .. note:: This target has ``cxx_std_17`` as an ``INTERFACE`` :ref:`compile language standard feature `. Linking to this target will automatically enable C++17 if no later standard version is already required on the linking target. .. _fs.variables: Variables ********* .. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++ filesystem library was found, otherwise ``FALSE``. .. variable:: CXX_FILESYSTEM_HAVE_FS Set to ``TRUE`` when a filesystem header was found. .. variable:: CXX_FILESYSTEM_HEADER Set to either ``filesystem`` or ``experimental/filesystem`` depending on whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was found. .. variable:: CXX_FILESYSTEM_NAMESPACE Set to either ``std::filesystem`` or ``std::experimental::filesystem`` depending on whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was found. Examples ******** Using `find_package(FILESYSTEM)` with no component arguments: .. code-block:: cmake find_package(FILESYSTEM REQUIRED) add_executable(my-program main.cpp) target_link_libraries(my-program PRIVATE std::filesystem) #]=======================================================================] if(TARGET std::filesystem) # This module has already been processed. Don't do it again. return() endif() include(CMakePushCheckState) include(CheckIncludeFileCXX) include(CheckCXXSourceCompiles) cmake_push_check_state() set(CMAKE_REQUIRED_QUIET ${FILESYSTEM_FIND_QUIETLY}) # All of our tests require C++17 or later set(OLD_CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD}) set(CMAKE_CXX_STANDARD 17) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9.0.0")) set(CMAKE_REQUIRED_FLAGS "-std=c++17") endif() if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.99")) set(CMAKE_REQUIRED_FLAGS "-std=c++17") endif() # Normalize and check the component list we were given set(want_components ${FILESYSTEM_FIND_COMPONENTS}) if(FILESYSTEM_FIND_COMPONENTS STREQUAL "") set(want_components Final) endif() # Warn on any unrecognized components set(extra_components ${want_components}) list(REMOVE_ITEM extra_components Final Experimental) foreach(component IN LISTS extra_components) message(WARNING "Extraneous find_package component for FILESYSTEM: ${component}") endforeach() # Detect which of Experimental and Final we should look for set(find_experimental TRUE) set(find_final TRUE) if(NOT "Final" IN_LIST want_components) set(find_final FALSE) endif() if(NOT "Experimental" IN_LIST want_components) set(find_experimental FALSE) endif() if(find_final) check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) if(_CXX_FILESYSTEM_HAVE_HEADER) # We found the non-experimental header. Don't bother looking for the # experimental one. set(find_experimental FALSE) endif() else() set(_CXX_FILESYSTEM_HAVE_HEADER FALSE) endif() if(find_experimental) check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) else() set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE) endif() if(_CXX_FILESYSTEM_HAVE_HEADER) set(_have_fs TRUE) set(_fs_header filesystem) set(_fs_namespace std::filesystem) elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) set(_have_fs TRUE) set(_fs_header experimental/filesystem) set(_fs_namespace std::experimental::filesystem) else() set(_have_fs FALSE) endif() set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers") set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs") set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs") set(_found FALSE) if(CXX_FILESYSTEM_HAVE_FS) # We have some filesystem library available. Do link checks string(CONFIGURE [[ #include <@CXX_FILESYSTEM_HEADER@> int main() { auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path(); return static_cast(cwd.string().size()); } ]] code @ONLY) # Try to compile a simple filesystem program without any linker flags check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED}) if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED) set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_FLAGS "-std=c++17") # Add the libstdc++ flag set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) # Try the libc++ flag set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED}) endif() endif() if(can_link) if(CMAKE_VERSION VERSION_LESS 3.12) add_library(std::filesystem INTERFACE IMPORTED GLOBAL) else() add_library(std::filesystem INTERFACE IMPORTED) target_compile_features(std::filesystem INTERFACE cxx_std_17) endif() set(_found TRUE) if(CXX_FILESYSTEM_NO_LINK_NEEDED) # Nothing to add... elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) target_link_libraries(std::filesystem INTERFACE -lstdc++fs) elseif(CXX_FILESYSTEM_CPPFS_NEEDED) target_link_libraries(std::filesystem INTERFACE -lc++fs) endif() endif() endif() if(NOT ${_found}) set(CMAKE_CXX_STANDARD ${OLD_CMAKE_CXX_STANDARD}) endif() cmake_pop_check_state() set(FILESYSTEM_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE) if(FILESYSTEM_FIND_REQUIRED AND NOT FILESYSTEM_FOUND) message(FATAL_ERROR "Cannot compile a simple program using std::filesystem") endif() cmake/Modules/FindGFLAGS.cmake000066400000000000000000000113251352176506000163230ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # - Try to find GFlags # # The following CMake and environment variables are optionally searched # for defaults: # GFLAGS_ROOT: Base directory where all GFlags components are found # # The following are set after configuration is done: # GFlags_FOUND # GFlags_INCLUDE_DIRS # GFlags_LIBS # GFlags_LIBRARY_DIRS # # Provides the following imported target: # Gflags::gflags # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() if(APPLE) find_path(GFlags_ROOT_DIR libgflags.dylib PATHS /opt/local/lib /usr/local/lib ${GFLAGS_ROOT}/lib $ENV{GFLAGS_ROOT}/lib ) else() find_path(GFlags_ROOT_DIR libgflags.so HINTS /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 /usr/lib ${GFLAGS_ROOT}/lib $ENV{GFLAGS_ROOT}/lib ${GFLAGS_ROOT}/lib64 $ENV{GFLAGS_ROOT}/lib64 ) endif() if(GFlags_ROOT_DIR) # We are testing only a couple of files in the include directories find_path(GFlags_INCLUDE_DIRS gflags/gflags.h HINTS /opt/local/include /usr/local/include /usr/include ${GFlags_ROOT_DIR}/src ${GFLAGS_ROOT}/include $ENV{GFLAGS_ROOT}/include ) # Find the libraries set(GFlags_LIBRARY_DIRS ${GFlags_ROOT_DIR}) find_library(GFlags_lib gflags ${GFlags_LIBRARY_DIRS}) if(EXISTS ${GFlags_INCLUDE_DIRS}/gflags/gflags_gflags.h) set(GFLAGS_GREATER_20 TRUE) else() set(GFLAGS_GREATER_20 FALSE) endif() message(STATUS "gflags library found at ${GFlags_lib}") set(GFlags_LIBS ${GFlags_lib}) set(GFlags_FOUND true) mark_as_advanced(GFlags_INCLUDE_DIRS) else() message(STATUS "Cannot find gflags") set(GFlags_FOUND false) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GFLAGS DEFAULT_MSG GFlags_LIBS GFlags_INCLUDE_DIRS) if(GFLAGS_FOUND) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) list(GET GFlags_LIBS 0 FIRST_DIR) get_filename_component(GFlags_LIBS_DIR ${FIRST_DIR} DIRECTORY) if(EXISTS ${GFlags_LIBS_DIR}/cmake/gflags/gflags-config-version.cmake) include(${GFlags_LIBS_DIR}/cmake/gflags/gflags-config-version.cmake) endif() if(PACKAGE_VERSION) set(GFLAGS_VERSION ${PACKAGE_VERSION}) set_package_properties(GFLAGS PROPERTIES DESCRIPTION "C++ library that implements commandline flags processing (found: v${GFLAGS_VERSION})" ) else() set_package_properties(GFLAGS PROPERTIES DESCRIPTION "C++ library that implements commandline flags processing" ) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) else() set_package_properties(GFLAGS PROPERTIES DESCRIPTION "C++ library that implements commandline flags processing" ) endif() set_package_properties(GFLAGS PROPERTIES URL "https://github.com/gflags/gflags" ) if(GFLAGS_FOUND AND NOT TARGET Gflags::gflags) add_library(Gflags::gflags SHARED IMPORTED) set_target_properties(Gflags::gflags PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GFlags_LIBS}" INTERFACE_INCLUDE_DIRECTORIES "${GFlags_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${GFlags_LIBS}" ) endif() cmake/Modules/FindGFORTRAN.cmake000066400000000000000000000200671352176506000166050ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . if(NOT COMMAND feature_summary) include(FeatureSummary) endif() find_library(GFORTRAN NAMES gfortran PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib/i386 /usr/lib/gcc/x86_64-linux-gnu /usr/lib/gcc/i686-linux-gnu /usr/lib/gcc/i386-linux-gnu /usr/lib/gcc/x86_64-linux-gnu/4.6 # Ubuntu 12.04 /usr/lib/gcc/i686-linux-gnu/4.6 /usr/lib/gcc/x86_64-linux-gnu/4.7 /usr/lib/gcc/i686-linux-gnu/4.7 /usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/gcc/i686-linux-gnu/4.8 /usr/lib/gcc/x86_64-linux-gnu/4.9 /usr/lib/gcc/i686-linux-gnu/4.9 /usr/lib/gcc/x86_64-redhat-linux/4.7.2 # Fedora 18 /usr/lib/gcc/i686-redhat-linux/4.7.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.1 # Fedora 19 /usr/lib/gcc/x86_64-redhat-linux/4.8.3 # Fedora 20 /usr/lib/gcc/x86_64-redhat-linux/4.9.1 # Fedora 21 /usr/lib/gcc/i686-redhat-linux/4.8.1 /usr/lib/gcc/i686-redhat-linux/4.8.3 /usr/lib/gcc/i686-redhat-linux/4.9.1 /usr/lib/gcc/x86_64-redhat-linux/4.4.4 # CentOS 6 /usr/lib/gcc/i686-redhat-linux/4.4.4 /usr/lib/gcc/x86_64-redhat-linux/4.8.2 /usr/lib/gcc/i686-redhat-linux/4.8.2 /usr/lib/gcc/x86_64-redhat-linux/7 /usr/lib/gcc/i686-redhat-linux/7 /usr/lib/gcc/x86_64-redhat-linux/8 /usr/lib/gcc/i686-redhat-linux/8 /usr/lib/gcc/x86_64-redhat-linux/9 /usr/lib/gcc/i686-redhat-linux/9 /usr/lib64/gcc/x86_64-redhat-linux/7 /usr/lib64/gcc/x86_64-redhat-linux/8 /usr/lib64/gcc/x86_64-redhat-linux/9 /usr/lib/gcc/armv7hl-redhat-linux-gnueabi/7 /usr/lib/gcc/aarch64-redhat-linux/7 /usr/lib/gcc/i586-suse-linux/4.8 # OpenSUSE 13.1 /usr/lib/gcc/i586-suse-linux/4.9 /usr/lib/gcc/i586-suse-linux/7 /usr/lib/gcc/i586-suse-linux/8 /usr/lib/gcc/i586-suse-linux/9 /usr/lib/gcc/x86_64-suse-linux/4.8 /usr/lib/gcc/x86_64-suse-linux/4.9 /usr/lib64/gcc/x86_64-suse-linux/7 /usr/lib64/gcc/x86_64-suse-linux/8 /usr/lib64/gcc/x86_64-suse-linux/9 /usr/lib/gcc/armv7hl-suse-linux-gnueabi/7 /usr/lib/gcc/armv7hl-suse-linux-gnueabi/8 /usr/lib/gcc/armv7hl-suse-linux-gnueabi/9 /usr/lib64/gcc/aarch64-suse-linux/7 /usr/lib64/gcc/aarch64-suse-linux/8 /usr/lib64/gcc/aarch64-suse-linux/9 /usr/lib64/gcc/powerpc64-suse-linux/7 /usr/lib64/gcc/powerpc64-suse-linux/8 /usr/lib64/gcc/powerpc64-suse-linux/9 /usr/lib64/gcc/powerpc64le-suse-linux/7 /usr/lib64/gcc/powerpc64le-suse-linux/8 /usr/lib64/gcc/powerpc64le-suse-linux/9 /usr/lib/gcc/i486-linux-gnu # Debian 7 /usr/lib/gcc/i486-linux-gnu/4.4 /usr/lib/gcc/i486-linux-gnu/4.6 /usr/lib/gcc/i486-linux-gnu/4.7 /usr/lib/gcc/i486-linux-gnu/4.8 /usr/lib/gcc/i486-linux-gnu/4.9 /usr/lib/gcc/i586-linux-gnu/4.9 /usr/lib/gcc/arm-linux-gnueabihf/4.4 # Debian armhf /usr/lib/gcc/arm-linux-gnueabihf/4.5 /usr/lib/gcc/arm-linux-gnueabihf/4.6 /usr/lib/gcc/arm-linux-gnueabihf/4.7 /usr/lib/gcc/arm-linux-gnueabihf/4.8 /usr/lib/gcc/arm-linux-gnueabihf/4.9 /usr/lib/gcc/aarch64-linux-gnu/4.9 # Debian arm64 /usr/lib/gcc/arm-linux-gnueabi/4.7 # Debian armel /usr/lib/gcc/arm-linux-gnueabi/4.9 /usr/lib/gcc/x86_64-linux-gnu/5 /usr/lib/gcc/i686-linux-gnu/5 /usr/lib/gcc/arm-linux-gnueabi/5 /usr/lib/gcc/arm-linux-gnueabihf/5 /usr/lib/gcc/aarch64-linux-gnu/5 /usr/lib/gcc/x86_64-linux-gnu/6 # Ubuntu 16.10 /usr/lib/gcc/alpha-linux-gnu/6 /usr/lib/gcc/aarch64-linux-gnu/6 /usr/lib/gcc/arm-linux-gnueabi/6 /usr/lib/gcc/arm-linux-gnueabihf/6 /usr/lib/gcc/hppa-linux-gnu/6 /usr/lib/gcc/i686-gnu/6 /usr/lib/gcc/i686-linux-gnu/6 /usr/lib/gcc/x86_64-kfreebsd-gnu/6 /usr/lib/gcc/i686-kfreebsd-gnu/6 /usr/lib/gcc/m68k-linux-gnu/6 /usr/lib/gcc/mips-linux-gnu/6 /usr/lib/gcc/mips64el-linux-gnuabi64/6 /usr/lib/gcc/mipsel-linux-gnu/6 /usr/lib/gcc/powerpc-linux-gnu/6 /usr/lib/gcc/powerpc-linux-gnuspe/6 /usr/lib/gcc/powerpc64-linux-gnu/6 /usr/lib/gcc/powerpc64le-linux-gnu/6 /usr/lib/gcc/s390x-linux-gnu/6 /usr/lib/gcc/sparc64-linux-gnu/6 /usr/lib/gcc/x86_64-linux-gnux32/6 /usr/lib/gcc/sh4-linux-gnu/6 /usr/lib/gcc/x86_64-linux-gnu/7 # Debian 9 Buster /usr/lib/gcc/alpha-linux-gnu/7 /usr/lib/gcc/aarch64-linux-gnu/7 /usr/lib/gcc/arm-linux-gnueabi/7 /usr/lib/gcc/arm-linux-gnueabihf/7 /usr/lib/gcc/hppa-linux-gnu/7 /usr/lib/gcc/i686-gnu/7 /usr/lib/gcc/i686-linux-gnu/7 /usr/lib/gcc/x86_64-kfreebsd-gnu/7 /usr/lib/gcc/i686-kfreebsd-gnu/7 /usr/lib/gcc/m68k-linux-gnu/7 /usr/lib/gcc/mips-linux-gnu/7 /usr/lib/gcc/mips64el-linux-gnuabi64/7 /usr/lib/gcc/mipsel-linux-gnu/7 /usr/lib/gcc/powerpc-linux-gnu/7 /usr/lib/gcc/powerpc-linux-gnuspe/7 /usr/lib/gcc/powerpc64-linux-gnu/7 /usr/lib/gcc/powerpc64le-linux-gnu/7 /usr/lib/gcc/s390x-linux-gnu/7 /usr/lib/gcc/sparc64-linux-gnu/7 /usr/lib/gcc/x86_64-linux-gnux32/7 /usr/lib/gcc/sh4-linux-gnu/7 /usr/lib/x86_64-linux-gnu # libgfortran4 /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabi /usr/lib/arm-linux-gnueabihf /usr/lib/aarch64-linux-gnu /usr/lib/i386-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/mipsel-linux-gnu /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib/gcc/x86_64-linux-gnu/8 # libgfortran8 /usr/lib/gcc/aarch64-linux-gnu/8 /usr/lib/gcc/arm-linux-gnueabihf/8 /usr/lib/gcc/i686-linux-gnu/8 /usr/lib/gcc/powerpc64le-linux-gnu/8 /usr/lib/gcc/s390x-linux-gnu/8 /usr/lib/gcc/alpha-linux-gnu/8 /usr/lib/gcc/x86_64-linux-gnu/9 /usr/lib/gcc/aarch64-linux-gnu/9 /usr/lib/gcc/arm-linux-gnueabi/9 /usr/lib/gcc/arm-linux-gnueabihf/9 /usr/lib/gcc/i686-linux-gnu/9 /usr/lib/gcc/powerpc64le-linux-gnu/9 /usr/lib/gcc/powerpc64-linux-gnu/9/ /usr/lib/gcc/s390x-linux-gnu/9 /usr/lib/gcc/alpha-linux-gnu/9 /usr/lib/gcc/hppa-linux-gnu/9 /usr/lib/gcc/m68k-linux-gnu/9 /usr/lib/gcc/mips-linux-gnu/9 /usr/lib/gcc/mips64el-linux-gnuabi64/9 /usr/lib/gcc/mipsel-linux-gnu/9 /usr/lib/gcc/riscv64-linux-gnu/9 /usr/lib/gcc/sh4-linux-gnu/9 /usr/lib/gcc/sparc64-linux-gnu/9 /usr/lib/gcc/x86_64-linux-gnux32/9 ${GFORTRAN_ROOT}/lib $ENV{GFORTRAN_ROOT}/lib ) set_package_properties(GFORTRAN PROPERTIES URL "http://gcc.gnu.org/wiki/GFortran" DESCRIPTION "GNU Fortran library" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GFORTRAN DEFAULT_MSG GFORTRAN) cmake/Modules/FindGLOG.cmake000066400000000000000000000125661352176506000161200ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # - Try to find the Google Glog library # # This module defines the following variables # # GLOG_FOUND - Was Glog found # GLOG_INCLUDE_DIRS - the Glog include directories # GLOG_LIBRARIES - Link to this # # This module accepts the following variables # # GLOG_ROOT - Can be set to Glog install path or Windows build path # # Provides the following imported target: # Glog::glog # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() if(NOT DEFINED GLOG_ROOT) set(GLOG_ROOT /usr /usr/local) endif() if(MSVC) set(LIB_PATHS ${GLOG_ROOT} ${GLOG_ROOT}/Release) else() set(LIB_PATHS ${GLOG_ROOT} ${GLOG_ROOT}/lib) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_GLOG libglog) macro(_FIND_GLOG_LIBRARIES _var) find_library(${_var} NAMES ${ARGN} PATHS ${LIB_PATHS} /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 /usr/lib ${GLOG_ROOT}/lib $ENV{GLOG_ROOT}/lib ${GLOG_ROOT}/lib64 $ENV{GLOG_ROOT}/lib64 ${PC_GLOG_LIBDIR} PATH_SUFFIXES lib ) mark_as_advanced(${_var}) endmacro() macro(_GLOG_APPEND_LIBRARIES _list _release) set(_debug ${_release}_DEBUG) if(${_debug}) set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}}) else() set(${_list} ${${_list}} ${${_release}}) endif() endmacro() if(MSVC) find_path(GLOG_INCLUDE_DIR NAMES raw_logging.h PATHS ${GLOG_ROOT}/src/windows ${GLOG_ROOT}/src/windows/glog ${PC_GLOG_INCLUDEDIR} ) else() # Linux/OS X builds find_path(GLOG_INCLUDE_DIR NAMES raw_logging.h PATHS ${GLOG_ROOT}/include/glog /usr/include/glog /opt/local/include/glog # default location in Macports ${PC_GLOG_INCLUDEDIR} ) endif() # Find the libraries if(MSVC) _find_glog_libraries(GLOG_LIBRARIES libglog.lib) else() # Linux/OS X builds if(UNIX) _find_glog_libraries(GLOG_LIBRARIES libglog.so) endif() if(APPLE) _find_glog_libraries(GLOG_LIBRARIES libglog.dylib) endif() endif() # handle the QUIETLY and REQUIRED arguments and set GLOG_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLOG DEFAULT_MSG GLOG_LIBRARIES) if(GLOG_FOUND) message(STATUS "glog library found at ${GLOG_LIBRARIES}") if(PC_GLOG_VERSION) set(GLOG_VERSION ${PC_GLOG_VERSION}) endif() endif() if(MSVC) string(REGEX REPLACE "/glog$" "" VAR_WITHOUT ${GLOG_INCLUDE_DIR}) string(REGEX REPLACE "/windows$" "" VAR_WITHOUT ${VAR_WITHOUT}) set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIRS} "${VAR_WITHOUT}") string(REGEX REPLACE "/libglog.lib" "" GLOG_LIBRARIES_DIR ${GLOG_LIBRARIES}) else() # Linux/OS X builds set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) string(REGEX REPLACE "/libglog.so" "" GLOG_LIBRARIES_DIR ${GLOG_LIBRARIES}) endif() if(GLOG_FOUND AND GLOG_VERSION) set_package_properties(GLOG PROPERTIES DESCRIPTION "C++ implementation of the Google logging module (found: v${GLOG_VERSION})" ) else() set_package_properties(GLOG PROPERTIES DESCRIPTION "C++ implementation of the Google logging module" ) endif() set_package_properties(GLOG PROPERTIES URL "https://github.com/google/glog" ) if(GLOG_FOUND AND NOT TARGET Glog::glog) add_library(Glog::glog SHARED IMPORTED) set_target_properties(Glog::glog PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GLOG_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${GLOG_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${GLOG_LIBRARIES}" ) endif() cmake/Modules/FindGNSSSIMULATOR.cmake000066400000000000000000000021521352176506000174300ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . find_program(SW_GENERATOR_BIN gnss_sim PATHS /usr/bin /usr/local/bin /opt/local/bin ${CMAKE_INSTALL_PREFIX}/bin ${GNSSSIMULATOR_ROOT}/bin $ENV{GNSSSIMULATOR_ROOT}/bin PATH_SUFFIXES bin ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GNSSSIMULATOR DEFAULT_MSG SW_GENERATOR_BIN) mark_as_advanced(SW_GENERATOR_BIN) cmake/Modules/FindGNURADIO.cmake000066400000000000000000000253371352176506000166000ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ######################################################################## # Find GNU Radio ######################################################################## if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) include(FindPackageHandleStandardArgs) # if GR_REQUIRED_COMPONENTS is not defined, it will be set to the following list if(NOT GR_REQUIRED_COMPONENTS) set(GR_REQUIRED_COMPONENTS RUNTIME PMT BLOCKS FFT FILTER ANALOG) endif() # Allows us to use all .cmake files in this directory list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_LIST_DIR}) # Easily access all libraries and includes of GNU Radio set(GNURADIO_ALL_LIBRARIES "") set(GNURADIO_ALL_INCLUDE_DIRS "") macro(LIST_CONTAINS var value) set(${var}) foreach(value2 ${ARGN}) if(${value} STREQUAL ${value2}) set(${var} TRUE) endif() endforeach() endmacro() function(GR_MODULE EXTVAR PCNAME INCFILE LIBFILE) list_contains(REQUIRED_MODULE ${EXTVAR} ${GR_REQUIRED_COMPONENTS}) if(NOT REQUIRED_MODULE) #message("Ignoring GNU Radio Module ${EXTVAR}") return() endif() message(STATUS "Checking for GNU Radio Module: ${EXTVAR}") # check for .pc hints pkg_check_modules(PC_GNURADIO_${EXTVAR} QUIET ${PCNAME}) if(NOT PC_GNURADIO_${EXTVAR}_FOUND) set(PC_GNURADIO_${EXTVAR}_LIBRARIES ${LIBFILE}) endif() set(INCVAR_NAME "GNURADIO_${EXTVAR}_INCLUDE_DIRS") set(LIBVAR_NAME "GNURADIO_${EXTVAR}_LIBRARIES") set(PC_INCDIR ${PC_GNURADIO_${EXTVAR}_INCLUDEDIR}) set(PC_LIBDIR ${PC_GNURADIO_${EXTVAR}_LIBDIR}) # look for include files find_path(${INCVAR_NAME} NAMES ${INCFILE} HINTS $ENV{GNURADIO_RUNTIME_DIR}/include ${PC_INCDIR} ${CMAKE_INSTALL_PREFIX}/include ${GNURADIO_INSTALL_PREFIX}/include PATHS /usr/local/include /usr/include ${GNURADIO_INSTALL_PREFIX}/include ${GNURADIO_ROOT}/include $ENV{GNURADIO_ROOT}/include ) # look for libs foreach(libname ${PC_GNURADIO_${EXTVAR}_LIBRARIES}) find_library(${LIBVAR_NAME}_${libname} NAMES ${libname} ${libname}-${PC_GNURADIO_RUNTIME_VERSION} HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib ${PC_LIBDIR} ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 ${GNURADIO_INSTALL_PREFIX}/lib ${GNURADIO_INSTALL_PREFIX}/lib64 PATHS /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 /usr/lib ${GNURADIO_INSTALL_PREFIX}/lib ${GNURADIO_ROOT}/lib $ENV{GNURADIO_ROOT}/lib ${GNURADIO_ROOT}/lib64 $ENV{GNURADIO_ROOT}/lib64 ) list(APPEND ${LIBVAR_NAME} ${${LIBVAR_NAME}_${libname}}) endforeach() set(${LIBVAR_NAME} ${${LIBVAR_NAME}} PARENT_SCOPE) # show results message(STATUS " * INCLUDES=${GNURADIO_${EXTVAR}_INCLUDE_DIRS}") message(STATUS " * LIBS=${GNURADIO_${EXTVAR}_LIBRARIES}") # append to all includes and libs list set(GNURADIO_ALL_INCLUDE_DIRS ${GNURADIO_ALL_INCLUDE_DIRS} ${GNURADIO_${EXTVAR}_INCLUDE_DIRS} PARENT_SCOPE) set(GNURADIO_ALL_LIBRARIES ${GNURADIO_ALL_LIBRARIES} ${GNURADIO_${EXTVAR}_LIBRARIES} PARENT_SCOPE) find_package_handle_standard_args(GNURADIO_${EXTVAR} DEFAULT_MSG GNURADIO_${EXTVAR}_LIBRARIES GNURADIO_${EXTVAR}_INCLUDE_DIRS) message(STATUS "GNURADIO_${EXTVAR}_FOUND = ${GNURADIO_${EXTVAR}_FOUND}") set(GNURADIO_${EXTVAR}_FOUND ${GNURADIO_${EXTVAR}_FOUND} PARENT_SCOPE) # generate an error if the module is missing if(NOT GNURADIO_${EXTVAR}_FOUND) message(STATUS "Required GNU Radio Component: ${EXTVAR} missing!") set(GNURADIO_FOUND FALSE) # Trick for feature_summary endif() # Create imported target string(TOLOWER ${EXTVAR} gnuradio_component) if(NOT TARGET Gnuradio::${gnuradio_component}) add_library(Gnuradio::${gnuradio_component} SHARED IMPORTED) set(GNURADIO_LIBRARY ${GNURADIO_${EXTVAR}_LIBRARIES}) list(GET GNURADIO_LIBRARY 0 FIRST_DIR) get_filename_component(GNURADIO_DIR ${FIRST_DIR} ABSOLUTE) set_target_properties(Gnuradio::${gnuradio_component} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GNURADIO_DIR}" INTERFACE_INCLUDE_DIRECTORIES "${GNURADIO_${EXTVAR}_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${GNURADIO_LIBRARY}" ) endif() mark_as_advanced(GNURADIO_${EXTVAR}_LIBRARIES GNURADIO_${EXTVAR}_INCLUDE_DIRS) endfunction() gr_module(RUNTIME gnuradio-runtime gnuradio/top_block.h gnuradio-runtime) gr_module(PMT gnuradio-runtime pmt/pmt.h gnuradio-pmt) gr_module(BLOCKS gnuradio-blocks gnuradio/blocks/api.h gnuradio-blocks) gr_module(FEC gnuradio-fec gnuradio/fec/api.h gnuradio-fec) gr_module(FFT gnuradio-fft gnuradio/fft/api.h gnuradio-fft) gr_module(FILTER gnuradio-filter gnuradio/filter/api.h gnuradio-filter) gr_module(ANALOG gnuradio-analog gnuradio/analog/api.h gnuradio-analog) gr_module(DIGITAL gnuradio-digital gnuradio/digital/api.h gnuradio-digital) gr_module(AUDIO gnuradio-audio gnuradio/audio/api.h gnuradio-audio) gr_module(CHANNELS gnuradio-channels gnuradio/channels/api.h gnuradio-channels) gr_module(QTGUI gnuradio-qtgui gnuradio/qtgui/api.h gnuradio-qtgui) gr_module(TRELLIS gnuradio-trellis gnuradio/trellis/api.h gnuradio-trellis) gr_module(UHD gnuradio-uhd gnuradio/uhd/api.h gnuradio-uhd) gr_module(VOCODER gnuradio-vocoder gnuradio/vocoder/api.h gnuradio-vocoder) gr_module(WAVELET gnuradio-wavelet gnuradio/wavelet/api.h gnuradio-wavelet) list(REMOVE_DUPLICATES GNURADIO_ALL_INCLUDE_DIRS) list(REMOVE_DUPLICATES GNURADIO_ALL_LIBRARIES) if(NOT PC_GNURADIO_RUNTIME_VERSION) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) list(GET GNURADIO_BLOCKS_LIBRARIES 0 FIRST_DIR) get_filename_component(GNURADIO_BLOCKS_DIR ${FIRST_DIR} DIRECTORY) if(EXISTS ${GNURADIO_BLOCKS_DIR}/cmake/gnuradio/GnuradioConfigVersion.cmake) set(PACKAGE_FIND_VERSION_MAJOR 3) set(PACKAGE_FIND_VERSION_MINOR 7) set(PACKAGE_FIND_VERSION_PATCH 4) include(${GNURADIO_BLOCKS_DIR}/cmake/gnuradio/GnuradioConfigVersion.cmake) endif() if(PACKAGE_VERSION) set(PC_GNURADIO_RUNTIME_VERSION ${PACKAGE_VERSION}) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) endif() # Trick to find out that GNU Radio is >= 3.7.4 if pkgconfig is not present if(NOT PC_GNURADIO_RUNTIME_VERSION) find_file(GNURADIO_VERSION_GREATER_THAN_373 NAMES gnuradio/blocks/tsb_vector_sink_f.h HINTS $ENV{GNURADIO_RUNTIME_DIR}/include ${CMAKE_INSTALL_PREFIX}/include ${GNURADIO_INSTALL_PREFIX}/include PATHS /usr/local/include /usr/include ${GNURADIO_INSTALL_PREFIX}/include ${GNURADIO_ROOT}/include $ENV{GNURADIO_ROOT}/include ) if(GNURADIO_VERSION_GREATER_THAN_373) set(PC_GNURADIO_RUNTIME_VERSION "3.7.4+") endif() find_file(GNURADIO_VERSION_GREATER_THAN_38 NAMES gnuradio/filter/mmse_resampler_cc.h HINTS $ENV{GNURADIO_RUNTIME_DIR}/include ${CMAKE_INSTALL_PREFIX}/include ${GNURADIO_INSTALL_PREFIX}/include PATHS /usr/local/include /usr/include ${GNURADIO_INSTALL_PREFIX}/include ${GNURADIO_ROOT}/include $ENV{GNURADIO_ROOT}/include ) if(GNURADIO_VERSION_GREATER_THAN_38) set(PC_GNURADIO_RUNTIME_VERSION "3.8.0+") endif() endif() # Trick for feature_summary if(NOT DEFINED GNURADIO_FOUND) set(GNURADIO_FOUND TRUE) endif() set(GNURADIO_VERSION ${PC_GNURADIO_RUNTIME_VERSION}) if(NOT GNSSSDR_GNURADIO_MIN_VERSION) set(GNSSSDR_GNURADIO_MIN_VERSION "3.7.3") endif() if(GNURADIO_VERSION) if(GNURADIO_VERSION VERSION_LESS ${GNSSSDR_GNURADIO_MIN_VERSION}) unset(GNURADIO_RUNTIME_FOUND) message(STATUS "The GNU Radio version installed in your system (v${GNURADIO_VERSION}) is too old.") if(OS_IS_LINUX) message("Go to https://github.com/gnuradio/pybombs") message("and follow the instructions to install GNU Radio in your system.") endif() if(OS_IS_MACOSX) message("You can install it easily via Macports:") message(" sudo port install gnuradio ") message("Alternatively, you can use homebrew:") message(" brew install gnuradio") endif() message(FATAL_ERROR "GNU Radio v${GNSSSDR_GNURADIO_MIN_VERSION} or later is required to build gnss-sdr.") endif() set_package_properties(GNURADIO PROPERTIES DESCRIPTION "The free and open software radio ecosystem (found: v${GNURADIO_VERSION})" ) else() set_package_properties(GNURADIO PROPERTIES DESCRIPTION "The free and open software radio ecosystem" ) endif() set_package_properties(GNURADIO PROPERTIES URL "https://www.gnuradio.org/" ) cmake/Modules/FindGOOGLETEST.cmake000066400000000000000000000044441352176506000170400ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # - Try to find Googletest source code # # The following environment variable is optionally searched for: # GTEST_DIR: Base directory where Googletest source code is found. # # The following are set after configuration is done: # GOOGLETEST_FOUND # LIBGTEST_DEV_DIR # GTEST_INCLUDE_DIRS if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_GTEST gtest) find_path(LIBGTEST_DEV_DIR NAMES src/gtest-all.cc PATHS ${GTEST_DIR} ${GTEST_DIR}/googletest /usr/src/googletest/googletest /usr/local/src/googletest/googletest /usr/src/gtest /usr/include/gtest /opt/local/src/gtest-1.7.0 ) find_path(GTEST_INCLUDE_DIRS NAMES gtest/gtest.h PATHS ${GTEST_DIR}/googletest/include /usr/include /opt/local/src/gtest-1.7.0/include ${PC_GTEST_INCLUDEDIR} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GOOGLETEST DEFAULT_MSG LIBGTEST_DEV_DIR GTEST_INCLUDE_DIRS) if(GOOGLETEST_FOUND AND PC_GTEST_VERSION) set(GOOGLETEST_VERSION ${PC_GTEST_VERSION}) set_package_properties(GOOGLETEST PROPERTIES DESCRIPTION "Source code of Google's Testing Framework (found: v${GOOGLETEST_VERSION})" ) else() set_package_properties(GOOGLETEST PROPERTIES DESCRIPTION "Source code of Google's Testing Framework" ) endif() set_package_properties(GOOGLETEST PROPERTIES URL "https://github.com/google/googletest" ) mark_as_advanced(LIBGTEST_DEV_DIR GTEST_INCLUDE_DIRS) cmake/Modules/FindGPERFTOOLS.cmake000066400000000000000000000103501352176506000170410ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # Tries to find Gperftools. # # Usage of this module as follows: # # find_package(GPERFTOOLS) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # GPERFTOOLS_ROOT Set this variable to the root installation of # Gperftools if the module has problems finding # the proper installation path. # # Variables defined by this module: # # GPERFTOOLS_FOUND System has Gperftools libs/headers # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler) # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers # # Provides the following imported targets: # Gperftools::tcmalloc # Gperftools::profiler # Gperftools::gperftools # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() find_library(GPERFTOOLS_TCMALLOC NAMES tcmalloc HINTS ${Gperftools_ROOT_DIR}/lib ${GPERFTOOLS_ROOT}/lib $ENV{GPERFTOOLS_ROOT}/lib ${GPERFTOOLS_ROOT}/lib64 $ENV{GPERFTOOLS_ROOT}/lib64 /usr/lib /usr/lib64 ) find_library(GPERFTOOLS_PROFILER NAMES profiler HINTS ${Gperftools_ROOT_DIR}/lib ${GPERFTOOLS_ROOT}/lib $ENV{GPERFTOOLS_ROOT}/lib ${GPERFTOOLS_ROOT}/lib64 $ENV{GPERFTOOLS_ROOT}/lib64 /usr/lib /usr/lib64 ) find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER NAMES tcmalloc_and_profiler HINTS ${Gperftools_ROOT_DIR}/lib ${GPERFTOOLS_ROOT}/lib $ENV{GPERFTOOLS_ROOT}/lib ${GPERFTOOLS_ROOT}/lib64 $ENV{GPERFTOOLS_ROOT}/lib64 /usr/lib /usr/lib64 ) find_path(GPERFTOOLS_INCLUDE_DIR NAMES gperftools/heap-profiler.h HINTS ${Gperftools_ROOT_DIR}/include ${GPERFTOOLS_ROOT}/include $ENV{GPERFTOOLS_ROOT}/include /usr/include ) set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( GPERFTOOLS DEFAULT_MSG GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR GPERFTOOLS_TCMALLOC GPERFTOOLS_PROFILER ) if(GPERFTOOLS_FOUND AND NOT TARGET Gperftools::tcmalloc) add_library(Gperftools::tcmalloc SHARED IMPORTED) set_target_properties(Gperftools::tcmalloc PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GPERFTOOLS_TCMALLOC}" INTERFACE_INCLUDE_DIRECTORIES "${GPERFTOOLS_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${GPERFTOOLS_TCMALLOC}" ) endif() if(GPERFTOOLS_FOUND AND NOT TARGET Gperftools::profiler) add_library(Gperftools::profiler SHARED IMPORTED) set_target_properties(Gperftools::profiler PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GPERFTOOLS_PROFILER}" INTERFACE_INCLUDE_DIRECTORIES "${GPERFTOOLS_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${GPERFTOOLS_PROFILER}" ) endif() if(GPERFTOOLS_FOUND AND NOT TARGET Gperftools::gperftools) add_library(Gperftools::gperftools SHARED IMPORTED) set_target_properties(Gperftools::gperftools PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GPERFTOOLS_TCMALLOC_AND_PROFILER}" INTERFACE_INCLUDE_DIRECTORIES "${GPERFTOOLS_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${GPERFTOOLS_TCMALLOC_AND_PROFILER}" ) endif() set_package_properties(GPERFTOOLS PROPERTIES URL "https://github.com/gperftools/gperftools" DESCRIPTION "Collection of performance analysis tools" ) mark_as_advanced( GPERFTOOLS_TCMALLOC GPERFTOOLS_PROFILER GPERFTOOLS_TCMALLOC_AND_PROFILER GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR) cmake/Modules/FindGPSTK.cmake000066400000000000000000000060621352176506000162520ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # - Find gpstk library # Find the native gpstk includes and library # This module defines # GPSTK_INCLUDE_DIR, where to find Rinex3ObsBase.hpp, etc. # GPSTK_FOUND, If false, do not try to use GPSTK. # GPSTK_LIBRARY, where to find the GPSTK library. if(NOT COMMAND feature_summary) include(FeatureSummary) endif() find_path(GPSTK_INCLUDE_DIR gpstk/Rinex3ObsBase.hpp HINTS /usr/include /usr/local/include /opt/local/include ${GPSTK_ROOT}/include $ENV{GPSTK_ROOT}/include ) set(GPSTK_NAMES ${GPSTK_NAMES} gpstk libgpstk) include(GNUInstallDirs) find_library(GPSTK_LIBRARY NAMES ${GPSTK_NAMES} HINTS /usr/lib /usr/local/lib /usr/${CMAKE_INSTALL_LIBDIR} /usr/local/${CMAKE_INSTALL_LIBDIR} /opt/local/lib ${GPSTK_ROOT}/${CMAKE_INSTALL_LIBDIR} $ENV{GPSTK_ROOT}/${CMAKE_INSTALL_LIBDIR} ) # handle the QUIET and REQUIRED arguments and set GPSTK_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GPSTK DEFAULT_MSG GPSTK_LIBRARY GPSTK_INCLUDE_DIR) if(GPSTK_FOUND) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) if(EXISTS ${CMAKE_INSTALL_FULL_DATADIR}/cmake/GPSTK/GPSTKConfigVersion.cmake) include(${CMAKE_INSTALL_FULL_DATADIR}/cmake/GPSTK/GPSTKConfigVersion.cmake) endif() if(PACKAGE_VERSION) set(GPSTK_VERSION ${PACKAGE_VERSION}) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) endif() if(GPSTK_FOUND AND GPSTK_VERSION) set_package_properties(GPSTK PROPERTIES DESCRIPTION "Library and suite of applications for satellite navigation (found: v${GPSTK_VERSION})" ) else() set_package_properties(GPSTK PROPERTIES DESCRIPTION "Library and suite of applications for satellite navigation" ) endif() set_package_properties(GPSTK PROPERTIES URL "http://www.gpstk.org" ) mark_as_advanced(GPSTK_LIBRARY GPSTK_INCLUDE_DIR) if(GPSTK_FOUND AND NOT TARGET Gpstk::gpstk) add_library(Gpstk::gpstk SHARED IMPORTED) set_target_properties(Gpstk::gpstk PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GPSTK_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${GPSTK_INCLUDE_DIR};${GPSTK_INCLUDE_DIR}/gpstk" INTERFACE_LINK_LIBRARIES "${GPSTK_LIBRARY}" ) endif() cmake/Modules/FindGRDBFCTTC.cmake000066400000000000000000000045131352176506000166630ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ######################################################################## # Find GR-DBFCTTC Module ######################################################################## set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_GR_DBFCTTC gr-dbfcttc) find_path( GR_DBFCTTC_INCLUDE_DIRS NAMES dbfcttc/api.h HINTS $ENV{GR_DBFCTTC_DIR}/include ${PC_GR_DBFCTTC_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/include /usr/local/include ${GRDBFCTTC_ROOT}/include $ENV{GRDBFCTTC_ROOT}/include ) find_library( GR_DBFCTTC_LIBRARIES NAMES gnuradio-dbfcttc HINTS $ENV{GR_DBFCTTC_DIR}/lib ${PC_GR_DBFCTTC_LIBDIR} PATHS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${GRDBFCTTC_ROOT}/lib $ENV{GRDBFCTTC_ROOT}/lib ${GRDBFCTTC_ROOT}/lib64 $ENV{GRDBFCTTC_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GRDBFCTTC DEFAULT_MSG GR_DBFCTTC_LIBRARIES GR_DBFCTTC_INCLUDE_DIRS) if(GRDBFCTTC_FOUND AND NOT TARGET Gnuradio::dbfcttc) add_library(Gnuradio::dbfcttc SHARED IMPORTED) set_target_properties(Gnuradio::dbfcttc PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GR_DBFCTTC_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${GR_DBFCTTC_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${GR_DBFCTTC_LIBRARIES}" ) endif() mark_as_advanced(GR_DBFCTTC_LIBRARIES GR_DBFCTTC_INCLUDE_DIRS) cmake/Modules/FindGRGN3S.cmake000066400000000000000000000043631352176506000163270ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ######################################################################## # Find GR-GN3S Module ######################################################################## set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_GR_GN3S gr-gn3s) find_path( GR_GN3S_INCLUDE_DIRS NAMES gn3s/gn3s_api.h HINTS $ENV{GR_GN3S_DIR}/include ${PC_GR_GN3S_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/local/include /usr/include ${GRGN3S_ROOT}/include $ENV{GRGN3S_ROOT}/include ) find_library( GR_GN3S_LIBRARIES NAMES gr-gn3s HINTS $ENV{GR_GN3S_DIR}/lib ${PC_GR_GN3S_LIBDIR} PATHS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 ${GRGN3S_ROOT}/lib $ENV{GRGN3S_ROOT}/lib ${GRGN3S_ROOT}/lib64 $ENV{GRGN3S_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GRGN3S DEFAULT_MSG GR_GN3S_LIBRARIES GR_GN3S_INCLUDE_DIRS) if(GRGN3S_FOUND AND NOT TARGET Gnuradio::gn3s) add_library(Gnuradio::gn3s SHARED IMPORTED) set_target_properties(Gnuradio::gn3s PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GR_GN3S_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${GR_GN3S_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${GR_GN3S_LIBRARIES}" ) endif() mark_as_advanced(GR_GN3S_LIBRARIES GR_GN3S_INCLUDE_DIRS) cmake/Modules/FindGRIIO.cmake000066400000000000000000000074631352176506000162410ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # Provides the following imported target: # Gnuradio::iio # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_IIO gnuradio-iio) find_path(IIO_INCLUDE_DIRS NAMES gnuradio/iio/api.h HINTS $ENV{IIO_DIR}/include ${PC_IIO_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/local/include /usr/include ${GRIIO_ROOT}/include $ENV{GRIIO_ROOT}/include ) if(IIO_INCLUDE_DIRS) set(GR_IIO_INCLUDE_HAS_GNURADIO TRUE) else() find_path(IIO_INCLUDE_DIRS NAMES iio/api.h HINTS $ENV{IIO_DIR}/include ${PC_IIO_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/local/include /usr/include ${GRIIO_ROOT}/include $ENV{GRIIO_ROOT}/include ) set(GR_IIO_INCLUDE_HAS_GNURADIO FALSE) endif() find_library(IIO_LIBRARIES NAMES gnuradio-iio HINTS $ENV{IIO_DIR}/lib ${PC_IIO_LIBDIR} PATHS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu /usr/lib/alpha-linux-gnu /usr/lib/aarch64-linux-gnu /usr/lib/arm-linux-gnueabi /usr/lib/arm-linux-gnueabihf /usr/lib/hppa-linux-gnu /usr/lib/i686-gnu /usr/lib/i686-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i686-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/mipsel-linux-gnu /usr/lib/powerpc-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/sh4-linux-gnu ${GRIIO_ROOT}/lib $ENV{GRIIO_ROOT}/lib ${GRIIO_ROOT}/lib64 $ENV{GRIIO_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GRIIO DEFAULT_MSG IIO_LIBRARIES IIO_INCLUDE_DIRS) if(PC_IIO_VERSION) set(GRIIO_VERSION ${PC_IIO_VERSION}) endif() set_package_properties(GRIIO PROPERTIES URL "https://github.com/analogdevicesinc/gr-iio" ) if(GRIIO_FOUND AND GRIIO_VERSION) set_package_properties(GRIIO PROPERTIES DESCRIPTION "IIO blocks for GNU Radio (found: v${GRIIO_VERSION})" ) else() set_package_properties(GRIIO PROPERTIES DESCRIPTION "IIO blocks for GNU Radio" ) endif() if(GRIIO_FOUND AND NOT TARGET Gnuradio::iio) add_library(Gnuradio::iio SHARED IMPORTED) set_target_properties(Gnuradio::iio PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${IIO_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${IIO_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${IIO_LIBRARIES}" ) endif() mark_as_advanced(IIO_LIBRARIES IIO_INCLUDE_DIRS GR_IIO_INCLUDE_HAS_GNURADIO) cmake/Modules/FindGROSMOSDR.cmake000066400000000000000000000076161352176506000167470ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # Tries to find gr-osmosdr. # # Usage of this module as follows: # # find_package(GROSMOSDR) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # GrOsmoSDR_ROOT_DIR Set this variable to the root installation of # gr-osmosdr if the module has problems finding # the proper installation path. # # Variables defined by this module: # # GROSMOSDR_FOUND System has gr-osmosdr libs/headers # GROSMOSDR_LIBRARIES The gr-osmosdr libraries (gnuradio-osmosdr) # GROSMOSDR_INCLUDE_DIR The location of gr-osmosdr headers # # Provides the following imported target: # Gnuradio::osmosdr # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(GROSMOSDR_PKG gnuradio-osmosdr) find_path(GROSMOSDR_INCLUDE_DIR NAMES osmosdr/source.h osmosdr/api.h PATHS /usr/include /usr/local/include /opt/local/include ${GROSMOSDR_ROOT}/include $ENV{GROSMOSDR_ROOT}/include ${GROSMOSDR_PKG_INCLUDEDIR} ) find_library(GROSMOSDR_LIBRARIES NAMES gnuradio-osmosdr PATHS /usr/lib /usr/local/lib /opt/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 ${GROSMOSDR_ROOT}/lib $ENV{GROSMOSDR_ROOT}/lib ${GROSMOSDR_ROOT}/lib64 $ENV{GROSMOSDR_ROOT}/lib64 ${GROSMOSDR_PKG_LIBDIR} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GROSMOSDR DEFAULT_MSG GROSMOSDR_LIBRARIES GROSMOSDR_INCLUDE_DIR) if(GROSMOSDR_PKG_VERSION) set(GROSMOSDR_VERSION_AUX ${GROSMOSDR_PKG_VERSION}) string(REGEX REPLACE "^v" "" GROSMOSDR_VERSION ${GROSMOSDR_VERSION_AUX}) endif() set_package_properties(GROSMOSDR PROPERTIES URL "https://osmocom.org/projects/gr-osmosdr/wiki" ) if(GROSMOSDR_FOUND AND GROSMOSDR_VERSION) set_package_properties(GROSMOSDR PROPERTIES DESCRIPTION "osmocom GNU Radio blocks (found: v${GROSMOSDR_VERSION})" ) else() set_package_properties(GROSMOSDR PROPERTIES DESCRIPTION "osmocom GNU Radio blocks" ) endif() if(GROSMOSDR_FOUND AND NOT TARGET Gnuradio::osmosdr) add_library(Gnuradio::osmosdr SHARED IMPORTED) set_target_properties(Gnuradio::osmosdr PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GROSMOSDR_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${GROSMOSDR_INCLUDE_DIR};${GROSMOSDR_INCLUDE_DIR}/osmosdr" INTERFACE_LINK_LIBRARIES "${GROSMOSDR_LIBRARIES}" ) endif() mark_as_advanced(GROSMOSDR_LIBRARIES GROSMOSDR_INCLUDE_DIR) cmake/Modules/FindLIBIIO.cmake000066400000000000000000000072331352176506000163320ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # Provides the following imported target: # Iio::iio # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_LIBIIO libiio) find_path( LIBIIO_INCLUDE_DIRS NAMES iio.h HINTS $ENV{LIBIIO_DIR}/include ${PC_LIBIIO_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/local/include /usr/include /opt/local/include ${LIBIIO_ROOT}/include $ENV{LIBIIO_ROOT}/include ) find_library( LIBIIO_LIBRARIES NAMES iio libiio.so.0 HINTS $ENV{LIBIIO_DIR}/lib ${PC_LIBIIO_LIBDIR} PATHS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu /usr/lib/alpha-linux-gnu /usr/lib/aarch64-linux-gnu /usr/lib/arm-linux-gnueabi /usr/lib/arm-linux-gnueabihf /usr/lib/hppa-linux-gnu /usr/lib/i686-gnu /usr/lib/i686-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i686-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/mipsel-linux-gnu /usr/lib/powerpc-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/sh4-linux-gnu /Library/Frameworks/iio.framework/ ${LIBIIO_ROOT}/lib $ENV{LIBIIO_ROOT}/lib ${LIBIIO_ROOT}/lib64 $ENV{LIBIIO_ROOT}/lib64 ) if(LIBIIO_LIBRARIES AND APPLE) if(LIBIIO_LIBRARIES MATCHES "framework") set(LIBIIO_LIBRARIES ${LIBIIO_LIBRARIES}/iio) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LIBIIO DEFAULT_MSG LIBIIO_LIBRARIES LIBIIO_INCLUDE_DIRS) if(PC_LIBIIO_VERSION) set(LIBIIO_VERSION ${PC_LIBIIO_VERSION}) endif() if(LIBIIO_FOUND AND LIBIIO_VERSION) set_package_properties(LIBIIO PROPERTIES DESCRIPTION "A library for interfacing with Linux IIO devices (found: v${LIBIIO_VERSION})" ) else() set_package_properties(LIBIIO PROPERTIES DESCRIPTION "A library for interfacing with Linux IIO devices" ) endif() set_package_properties(LIBIIO PROPERTIES URL "https://github.com/analogdevicesinc/libiio" ) if(LIBIIO_FOUND AND NOT TARGET Iio::iio) add_library(Iio::iio SHARED IMPORTED) set_target_properties(Iio::iio PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${LIBIIO_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${LIBIIO_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${LIBIIO_LIBRARIES}" ) endif() mark_as_advanced(LIBIIO_LIBRARIES LIBIIO_INCLUDE_DIRS) cmake/Modules/FindLOG4CPP.cmake000066400000000000000000000115231352176506000164300ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # - Find Log4cpp # Find the native LOG4CPP includes and library # # LOG4CPP_INCLUDE_DIR - where to find LOG4CPP.h, etc. # LOG4CPP_LIBRARIES - List of libraries when using LOG4CPP. # LOG4CPP_FOUND - True if LOG4CPP found. # # Provides the following imported target: # Log4cpp::log4cpp # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_LOG4CPP log4cpp QUIET) if(LOG4CPP_INCLUDE_DIR) # Already in cache, be silent set(LOG4CPP_FIND_QUIETLY TRUE) endif() find_path(LOG4CPP_INCLUDE_DIR log4cpp/Category.hh /opt/local/include /usr/local/include /usr/include ${LOG4CPP_ROOT}/include $ENV{LOG4CPP_ROOT}/include ${PC_LOG4CPP_INCLUDEDIR} ) if(LOG4CPP_INCLUDE_DIR) file(STRINGS ${LOG4CPP_INCLUDE_DIR}/log4cpp/Priority.hh _log4cpp_Priority) set(_log4cpp_cxx17 TRUE) foreach(_loop_var IN LISTS _log4cpp_Priority) string(STRIP "${_loop_var}" _file_line) if("throw(std::invalid_argument);" STREQUAL "${_file_line}") set(_log4cpp_cxx17 FALSE) endif() endforeach() if(${_log4cpp_cxx17}) set(LOG4CPP_READY_FOR_CXX17 TRUE) endif() endif() set(LOG4CPP_NAMES log4cpp) find_library(LOG4CPP_LIBRARY NAMES ${LOG4CPP_NAMES} HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib ${PC_LOG4CPP_LIBDIR} ${CMAKE_INSTALL_PREFIX}/lib/ PATHS /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 /usr/lib /opt/local/lib ${LOG4CPP_ROOT}/lib $ENV{LOG4CPP_ROOT}/lib ${LOG4CPP_ROOT}/lib64 $ENV{LOG4CPP_ROOT}/lib64 ) if(LOG4CPP_INCLUDE_DIR AND LOG4CPP_LIBRARY) set(LOG4CPP_FOUND TRUE) set(LOG4CPP_LIBRARIES ${LOG4CPP_LIBRARY} CACHE INTERNAL "" FORCE) set(LOG4CPP_INCLUDE_DIRS ${LOG4CPP_INCLUDE_DIR} CACHE INTERNAL "" FORCE) else() set(LOG4CPP_FOUND FALSE CACHE INTERNAL "" FORCE) set(LOG4CPP_LIBRARY "" CACHE INTERNAL "" FORCE) set(LOG4CPP_LIBRARIES "" CACHE INTERNAL "" FORCE) set(LOG4CPP_INCLUDE_DIR "" CACHE INTERNAL "" FORCE) set(LOG4CPP_INCLUDE_DIRS "" CACHE INTERNAL "" FORCE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LOG4CPP DEFAULT_MSG LOG4CPP_INCLUDE_DIRS LOG4CPP_LIBRARIES) set_package_properties(LOG4CPP PROPERTIES URL "http://log4cpp.sourceforge.net/" ) if(LOG4CPP_FOUND AND PC_LOG4CPP_VERSION) set(LOG4CPP_VERSION ${PC_LOG4CPP_VERSION}) endif() if(LOG4CPP_FOUND AND LOG4CPP_VERSION) if(LOG4CPP_READY_FOR_CXX17) set_package_properties(LOG4CPP PROPERTIES DESCRIPTION "Library of C++ classes for flexible logging (found: v${LOG4CPP_VERSION}, C++17-ready)" ) else() set_package_properties(LOG4CPP PROPERTIES DESCRIPTION "Library of C++ classes for flexible logging (found: v${LOG4CPP_VERSION})" ) endif() else() set_package_properties(LOG4CPP PROPERTIES DESCRIPTION "Library of C++ classes for flexible logging" ) endif() if (LOG4CPP_FOUND AND NOT TARGET Log4cpp::log4cpp) add_library(Log4cpp::log4cpp SHARED IMPORTED) set_target_properties(Log4cpp::log4cpp PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${LOG4CPP_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${LOG4CPP_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${LOG4CPP_LIBRARIES}" ) endif() mark_as_advanced( LOG4CPP_LIBRARIES LOG4CPP_INCLUDE_DIRS ) cmake/Modules/FindMATIO.cmake000066400000000000000000000130431352176506000162300ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # FindMATIO # # Try to find MATIO library # # Once done this will define: # # MATIO_FOUND - True if MATIO found. # MATIO_LIBRARIES - MATIO libraries. # MATIO_INCLUDE_DIRS - where to find matio.h, etc.. # MATIO_VERSION_STRING - version number as a string (e.g.: "1.3.4") # # Provides the following imported target: # Matio::matio # #============================================================================= # Copyright 2015 Avtech Scientific # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() # Look for the header file. find_path(MATIO_INCLUDE_DIR NAMES matio.h HINTS ${MATIO_ROOT}/include $ENV{MATIO_ROOT}/include DOC "The MATIO include directory" ) # Look for the library. find_library(MATIO_LIBRARY NAMES matio HINTS ${MATIO_ROOT}/lib $ENV{MATIO_ROOT}/lib ${MATIO_ROOT}/lib64 $ENV{MATIO_ROOT}/lib64 DOC "The MATIO library" ) if(MATIO_INCLUDE_DIR) # --------------------------------------------------- # Extract version information from MATIO # --------------------------------------------------- # If the file is missing, set all values to 0 set(MATIO_MAJOR_VERSION 0) set(MATIO_MINOR_VERSION 0) set(MATIO_RELEASE_LEVEL 0) # new versions of MATIO have `matio_pubconf.h` if(EXISTS ${MATIO_INCLUDE_DIR}/matio_pubconf.h) set(MATIO_CONFIG_FILE "matio_pubconf.h") else() set(MATIO_CONFIG_FILE "matioConfig.h") endif() if(MATIO_CONFIG_FILE) # Read and parse MATIO config header file for version number file(STRINGS "${MATIO_INCLUDE_DIR}/${MATIO_CONFIG_FILE}" _matio_HEADER_CONTENTS REGEX "#define MATIO_((MAJOR|MINOR)_VERSION)|(RELEASE_LEVEL) ") foreach(line ${_matio_HEADER_CONTENTS}) if(line MATCHES "#define ([A-Z_]+) ([0-9]+)") set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}") endif() endforeach() unset(_matio_HEADER_CONTENTS) endif() set(MATIO_VERSION_STRING "${MATIO_MAJOR_VERSION}.${MATIO_MINOR_VERSION}.${MATIO_RELEASE_LEVEL}") endif() mark_as_advanced(MATIO_INCLUDE_DIR MATIO_LIBRARY) # handle the QUIETLY and REQUIRED arguments and set MATIO_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MATIO REQUIRED_VARS MATIO_LIBRARY MATIO_INCLUDE_DIR VERSION_VAR MATIO_VERSION_STRING) if(MATIO_FOUND) set(MATIO_LIBRARIES ${MATIO_LIBRARY}) set(MATIO_INCLUDE_DIRS ${MATIO_INCLUDE_DIR}) else() set(MATIO_LIBRARIES) set(MATIO_INCLUDE_DIRS) endif() if(MATIO_FOUND AND MATIO_VERSION_STRING) set_package_properties(MATIO PROPERTIES DESCRIPTION "MATLAB MAT File I/O Library (found: v${MATIO_VERSION_STRING})" ) else() set_package_properties(MATIO PROPERTIES DESCRIPTION "MATLAB MAT File I/O Library" ) endif() set_package_properties(MATIO PROPERTIES URL "https://github.com/tbeu/matio" ) if(MATIO_FOUND AND NOT TARGET Matio::matio) add_library(Matio::matio SHARED IMPORTED) set_target_properties(Matio::matio PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${MATIO_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${MATIO_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${MATIO_LIBRARY}" ) endif() cmake/Modules/FindOPENCL.cmake000066400000000000000000000146451352176506000163500ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # This file taken from FindOpenCL project @ http://gitorious.com/findopencl # # - Try to find OpenCL # This module tries to find an OpenCL implementation on your system. It supports # AMD / ATI, Apple and NVIDIA implementations. # # Once done this will define # OPENCL_FOUND - system has OpenCL # OPENCL_INCLUDE_DIRS - the OpenCL include directory # OPENCL_LIBRARIES - link these to use OpenCL # # WIN32 should work, but is untested if(NOT COMMAND feature_summary) include(FeatureSummary) endif() include(FindPackageHandleStandardArgs) function(_FIND_OPENCL_VERSION) include(CheckSymbolExists) include(CMakePushCheckState) set(CMAKE_REQUIRED_QUIET ${OPENCL_FIND_QUIETLY}) cmake_push_check_state() foreach(VERSION "2_2" "2_1" "2_0" "1_2" "1_1" "1_0") set(CMAKE_REQUIRED_INCLUDES "${OPENCL_INCLUDE_DIR}") if(APPLE) check_symbol_exists( CL_VERSION_${VERSION} "${OPENCL_INCLUDE_DIR}/Headers/cl.h" OPENCL_VERSION_${VERSION} ) else() check_symbol_exists( CL_VERSION_${VERSION} "${OPENCL_INCLUDE_DIR}/CL/cl.h" OPENCL_VERSION_${VERSION} ) endif() if(OPENCL_VERSION_${VERSION}) string(REPLACE "_" "." VERSION "${VERSION}") set(OPENCL_VERSION_STRING ${VERSION} PARENT_SCOPE) string(REGEX MATCHALL "[0-9]+" version_components "${VERSION}") list(GET version_components 0 major_version) list(GET version_components 1 minor_version) set(OPENCL_VERSION_MAJOR ${major_version} PARENT_SCOPE) set(OPENCL_VERSION_MINOR ${minor_version} PARENT_SCOPE) break() endif() endforeach() cmake_pop_check_state() endfunction() find_path(OPENCL_INCLUDE_DIR NAMES CL/cl.h OpenCL/cl.h PATHS ENV "PROGRAMFILES(X86)" ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV NVSDKCOMPUTE_ROOT ENV CUDA_PATH ENV ATISTREAMSDKROOT ENV OCL_ROOT /usr/local/cuda/include PATH_SUFFIXES include OpenCL/common/inc "AMD APP/include" ) find_path(_OPENCL_CPP_INCLUDE_DIRS NAMES CL/cl.hpp OpenCL/cl.hpp PATHS ENV "PROGRAMFILES(X86)" ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV NVSDKCOMPUTE_ROOT ENV CUDA_PATH ENV ATISTREAMSDKROOT ENV OCL_ROOT /usr/local/cuda/include PATH_SUFFIXES include OpenCL/common/inc "AMD APP/include" ) set(OPENCL_INCLUDE_DIRS ${OPENCL_INCLUDE_DIR}) if(_OPENCL_CPP_INCLUDE_DIRS) set(OPENCL_HAS_CPP_BINDINGS TRUE) list(APPEND OPENCL_INCLUDE_DIRS ${_OPENCL_CPP_INCLUDE_DIRS}) # This is often the same, so clean up list(REMOVE_DUPLICATES OPENCL_INCLUDE_DIRS) endif() _FIND_OPENCL_VERSION() if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 4) find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ENV "PROGRAMFILES(X86)" ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV CUDA_PATH ENV NVSDKCOMPUTE_ROOT ENV ATISTREAMSDKROOT ENV OCL_ROOT PATH_SUFFIXES "AMD APP/lib/x86" lib/x86 lib/Win32 OpenCL/common/lib/Win32 ) elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ENV "PROGRAMFILES(X86)" ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV CUDA_PATH ENV NVSDKCOMPUTE_ROOT ENV ATISTREAMSDKROOT ENV OCL_ROOT PATH_SUFFIXES "AMD APP/lib/x86_64" lib/x86_64 lib/x64 OpenCL/common/lib/x64 ) endif() else() if(CMAKE_SIZEOF_VOID_P EQUAL 4) find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ENV AMDAPPSDKROOT ENV CUDA_PATH ENV LD_LIBRARY_PATH PATH_SUFFIXES lib/x86 lib ) elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ENV AMDAPPSDKROOT ENV CUDA_PATH ENV LD_LIBRARY_PATH PATH_SUFFIXES lib/x86_64 lib/x64 lib lib64 ) endif() endif() set(OPENCL_LIBRARIES ${OPENCL_LIBRARY}) find_package_handle_standard_args(OPENCL DEFAULT_MSG OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS) mark_as_advanced( OPENCL_INCLUDE_DIRS OPENCL_LIBRARIES ) set_package_properties(OPENCL PROPERTIES URL "https://www.khronos.org/opencl/" ) if(OPENCL_FOUND AND OPENCL_VERSION_STRING) set_package_properties(OPENCL PROPERTIES DESCRIPTION "Library for parallel programming (found: v${OPENCL_VERSION_STRING})" ) else() set_package_properties(OPENCL PROPERTIES DESCRIPTION "Library for parallel programming" ) endif() if(OPENCL_FOUND AND NOT TARGET OpenCL::OpenCL) if(OPENCL_LIBRARY MATCHES "/([^/]+)\\.framework$") add_library(OpenCL::OpenCL INTERFACE IMPORTED) set_target_properties(OpenCL::OpenCL PROPERTIES INTERFACE_LINK_LIBRARIES "${OPENCL_LIBRARY}" ) else() add_library(OpenCL::OpenCL UNKNOWN IMPORTED) set_target_properties(OpenCL::OpenCL PROPERTIES IMPORTED_LOCATION "${OPENCL_LIBRARY}" ) endif() set_target_properties(OpenCL::OpenCL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OPENCL_INCLUDE_DIRS}" ) endif() cmake/Modules/FindORC.cmake000066400000000000000000000057131352176506000160070ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_ORC "orc-0.4 > 0.4.22") find_program(ORCC_EXECUTABLE orcc HINTS ${PC_ORC_TOOLSDIR} PATHS ${ORC_ROOT}/bin ${CMAKE_INSTALL_PREFIX}/bin ) find_path(ORC_INCLUDE_DIR NAMES orc/orc.h HINTS ${PC_ORC_INCLUDEDIR} PATHS ${ORC_ROOT}/include/orc-0.4 ${CMAKE_INSTALL_PREFIX}/include/orc-0.4 ) find_path(ORC_LIBRARY_DIR NAMES ${CMAKE_SHARED_LIBRARY_PREFIX}orc-0.4${CMAKE_SHARED_LIBRARY_SUFFIX} HINTS ${PC_ORC_LIBDIR} /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib64 /usr/lib ${ORC_ROOT}/lib $ENV{ORC_ROOT}/lib PATHS ${ORC_ROOT}/lib${LIB_SUFFIX} ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} ) find_library(ORC_LIB orc-0.4 HINTS ${PC_ORC_LIBRARY_DIRS} PATHS ${ORC_ROOT}/lib${LIB_SUFFIX} ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} ) if(PC_ORC_VERSION) set(ORC_VERSION ${PC_ORC_VERSION}) endif() list(APPEND ORC_LIBRARY ${ORC_LIB}) set(ORC_INCLUDE_DIRS ${ORC_INCLUDE_DIR}) set(ORC_LIBRARIES ${ORC_LIBRARY}) set(ORC_LIBRARY_DIRS ${ORC_LIBRARY_DIR}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ORC "orc files" ORC_LIBRARY ORC_INCLUDE_DIR ORCC_EXECUTABLE) set_package_properties(ORC PROPERTIES URL "https://gstreamer.freedesktop.org/modules/orc.html" ) if(ORC_FOUND AND ORC_VERSION) set_package_properties(ORC PROPERTIES DESCRIPTION "The Optimized Inner Loops Runtime Compiler (found: v${ORC_VERSION})" ) else() set_package_properties(ORC PROPERTIES DESCRIPTION "The Optimized Inner Loops Runtime Compiler" ) endif() mark_as_advanced(ORC_INCLUDE_DIR ORC_LIBRARY ORCC_EXECUTABLE) cmake/Modules/FindPCAP.cmake000066400000000000000000000125461352176506000161110ustar00rootroot00000000000000################################################################### # # Copyright (c) 2006 Frederic Heem, # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the Telsey nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ################################################################### # - Find pcap # Find the PCAP includes and library # http://www.tcpdump.org/ # # The environment variable PCAPDIR allows to specficy where to find # libpcap in non standard location. # # PCAP_INCLUDE_DIRS - where to find pcap.h, etc. # PCAP_LIBRARIES - List of libraries when using pcap. # PCAP_FOUND - True if pcap found. # # Provides the following imported target: # Pcap::pcap # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_PCAP libpcap QUIET) if(EXISTS $ENV{PCAPDIR}) find_path(PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h PATHS $ENV{PCAPDIR} ${PCAP_ROOT}/include $ENV{PCAP_ROOT}/include ${PC_PCAP_INCLUDEDIR} NO_DEFAULT_PATH ) find_library(PCAP_LIBRARY NAMES pcap PATHS $ENV{PCAPDIR} ${PCAP_ROOT}/lib $ENV{PCAP_ROOT}/lib ${PC_PCAP_LIBDIR} NO_DEFAULT_PATH ) else() find_path(PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h HINTS ${PCAP_ROOT}/include $ENV{PCAP_ROOT}/include ${PC_PCAP_INCLUDEDIR} ) find_library(PCAP_LIBRARY NAMES pcap HINTS ${PCAP_ROOT}/lib $ENV{PCAP_ROOT}/lib ${PC_PCAP_LIBDIR} ) endif() set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR}) set(PCAP_LIBRARIES ${PCAP_LIBRARY}) if(PCAP_INCLUDE_DIRS) message(STATUS "Pcap include dirs set to ${PCAP_INCLUDE_DIRS}") else() message(STATUS "Pcap include dirs cannot be found.") endif() if(PCAP_LIBRARIES) message(STATUS "Pcap library set to ${PCAP_LIBRARIES}") else() message(STATUS "Pcap library cannot be found.") endif() #Functions include(CheckFunctionExists) set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARIES}) check_function_exists("pcap_breakloop" HAVE_PCAP_BREAKLOOP) check_function_exists("pcap_datalink_name_to_val" HAVE_PCAP_DATALINK_NAME_TO_VAL) check_function_exists("pcap_datalink_val_to_name" HAVE_PCAP_DATALINK_VAL_TO_NAME) check_function_exists("pcap_findalldevs" HAVE_PCAP_FINDALLDEVS) check_function_exists("pcap_freecode" HAVE_PCAP_FREECODE) check_function_exists("pcap_get_selectable_fd" HAVE_PCAP_GET_SELECTABLE_FD) check_function_exists("pcap_lib_version" HAVE_PCAP_LIB_VERSION) check_function_exists("pcap_list_datalinks" HAVE_PCAP_LIST_DATALINKS) check_function_exists("pcap_open_dead" HAVE_PCAP_OPEN_DEAD) check_function_exists("pcap_set_datalink" HAVE_PCAP_SET_DATALINK) set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PCAP DEFAULT_MSG PCAP_INCLUDE_DIRS PCAP_LIBRARIES) if(PCAP_FOUND AND PC_PCAP_VERSION) set(PCAP_VERSION ${PC_PCAP_VERSION}) endif() set_package_properties(PCAP PROPERTIES URL "https://www.tcpdump.org" ) if(PCAP_FOUND AND PCAP_VERSION) set_package_properties(PCAP PROPERTIES DESCRIPTION "A portable C/C++ library for network traffic capture (found: v${PCAP_VERSION})" ) else() set_package_properties(PCAP PROPERTIES DESCRIPTION "A portable C/C++ library for network traffic capture" ) endif() if(PCAP_FOUND AND NOT TARGET Pcap::pcap) add_library(Pcap::pcap SHARED IMPORTED) set_target_properties(Pcap::pcap PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${PCAP_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${PCAP_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${PCAP_LIBRARIES}" ) endif() mark_as_advanced( PCAP_LIBRARIES PCAP_INCLUDE_DIRS ) cmake/Modules/FindPUGIXML.cmake000066400000000000000000000106341352176506000165070ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # Find the pugixml XML parsing library. # # Sets the usual variables expected for find_package scripts: # # PUGIXML_INCLUDE_DIR - header location # PUGIXML_LIBRARIES - library to link against # PUGIXML_FOUND - true if pugixml was found. # # Provides the following imported target: # Pugixml::pugixml # if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_PUGIXML pugixml QUIET) find_path(PUGIXML_INCLUDE_DIR NAMES pugixml.hpp PATHS ${PUGIXML_HOME}/include /usr/include /usr/local/include /usr/local/include/pugixml-1.9 /usr/local/include/pugixml-${PC_PUGIXML_VERSION} /opt/local/include ${PUGIXML_ROOT}/include $ENV{PUGIXML_ROOT}/include ${PUGIXML_ROOT}/include/pugixml-1.9 $ENV{PUGIXML_ROOT}/include/pugixml-1.9 ${PUGIXML_ROOT}/include/pugixml-${PC_PUGIXML_VERSION} $ENV{PUGIXML_ROOT}/include/pugixml-${PC_PUGIXML_VERSION} ${PC_PUGIXML_INCLUDEDIR} ) find_library(PUGIXML_LIBRARY NAMES pugixml PATHS ${PUGIXML_HOME}/lib /usr/lib/x86_64-linux-gnu /usr/lib/aarch64-linux-gnu /usr/lib/arm-linux-gnueabi /usr/lib/arm-linux-gnueabihf /usr/lib/i386-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/mipsel-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/s390x-linux-gnu /usr/local/lib /usr/local/lib/pugixml-1.9 /usr/local/lib/pugixml-${PC_PUGIXML_VERSION} /opt/local/lib /usr/lib /usr/lib64 /usr/local/lib64 ${PUGIXML_ROOT}/lib $ENV{PUGIXML_ROOT}/lib ${PUGIXML_ROOT}/lib64 $ENV{PUGIXML_ROOT}/lib64 ${PUGIXML_ROOT}/lib/pugixml-1.9 $ENV{PUGIXML_ROOT}/lib/pugixml-1.9 ${PUGIXML_ROOT}/lib64/pugixml-1.9 $ENV{PUGIXML_ROOT}/lib64/pugixml-1.9 ${PUGIXML_ROOT}/lib/pugixml-${PC_PUGIXML_VERSION} $ENV{PUGIXML_ROOT}/lib/pugixml-${PC_PUGIXML_VERSION} ${PUGIXML_ROOT}/lib64/pugixml-${PC_PUGIXML_VERSION} $ENV{PUGIXML_ROOT}/lib64/pugixml-${PC_PUGIXML_VERSION} ${PC_PUGIXML_LIBDIR} ) # Support the REQUIRED and QUIET arguments, and set PUGIXML_FOUND if found. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PUGIXML DEFAULT_MSG PUGIXML_LIBRARY PUGIXML_INCLUDE_DIR) if(PUGIXML_FOUND) set(PUGIXML_LIBRARIES ${PUGIXML_LIBRARY}) if(NOT PUGIXML_FIND_QUIETLY) message(STATUS "PugiXML include = ${PUGIXML_INCLUDE_DIR}") message(STATUS "PugiXML library = ${PUGIXML_LIBRARY}") endif() if(PC_PUGIXML_VERSION) set(PUGIXML_VERSION ${PC_PUGIXML_VERSION}) endif() else() message(STATUS "PugiXML not found.") endif() set_package_properties(PUGIXML PROPERTIES URL "https://pugixml.org/" ) if(PUGIXML_FOUND AND PUGIXML_VERSION) set_package_properties(PUGIXML PROPERTIES DESCRIPTION "Light-weight, simple and fast XML parser for C++ (found: v${PUGIXML_VERSION})" ) else() set_package_properties(PUGIXML PROPERTIES DESCRIPTION "Light-weight, simple and fast XML parser for C++" ) endif() mark_as_advanced(PUGIXML_LIBRARY PUGIXML_INCLUDE_DIR) if(PUGIXML_FOUND AND NOT TARGET Pugixml::pugixml) add_library(Pugixml::pugixml SHARED IMPORTED) set_target_properties(Pugixml::pugixml PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${PUGIXML_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${PUGIXML_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${PUGIXML_LIBRARY}" ) endif() cmake/Modules/FindTELEORBIT.cmake000066400000000000000000000063701352176506000167150ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_TELEORBIT teleorbit QUIET) find_path(TELEORBIT_INCLUDE_DIRS NAMES teleorbit/api.h HINTS $ENV{TELEORBIT_DIR}/include ${PC_TELEORBIT_INCLUDEDIR} PATHS ${CMAKE_INSTALL_PREFIX}/include /usr/local/include /usr/include /opt/local/include ${TELEORBIT_ROOT}/include $ENV{TELEORBIT_ROOT}/include ) find_library(TELEORBIT_LIBRARIES NAMES gnuradio-teleorbit HINTS $ENV{TELEORBIT_DIR}/lib ${PC_TELEORBIT_LIBDIR} PATHS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /opt/local/lib ${TELEORBIT_ROOT}/lib $ENV{TELEORBIT_ROOT}/lib ${TELEORBIT_ROOT}/lib64 $ENV{TELEORBIT_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(TELEORBIT DEFAULT_MSG TELEORBIT_LIBRARIES TELEORBIT_INCLUDE_DIRS) if(PC_TELEORBIT_VERSION) set(TELEORBIT_VERSION ${PC_TELEORBIT_VERSION}) endif() if(NOT TELEORBIT_VERSION) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) list(GET TELEORBIT_LIBRARIES 0 FIRST_DIR) get_filename_component(TELEORBIT_LIBRARIES_DIR ${FIRST_DIR} DIRECTORY) if(EXISTS ${TELEORBIT_LIBRARIES_DIR}/cmake/teleorbit/TeleorbitConfigVersion.cmake) include(${TELEORBIT_LIBRARIES_DIR}/cmake/teleorbit/TeleorbitConfigVersion.cmake) endif() if(PACKAGE_VERSION) set(TELEORBIT_VERSION ${PACKAGE_VERSION}) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) endif() if(TELEORBIT_FOUND AND TELEORBIT_VERSION) set_package_properties(TELEORBIT PROPERTIES DESCRIPTION "The Teleorbit's Flexiband GNU Radio block (found: v${TELEORBIT_VERSION})" ) else() set_package_properties(TELEORBIT PROPERTIES DESCRIPTION "The Teleorbit's Flexiband GNU Radio block." ) endif() if(TELEORBIT_FOUND AND NOT TARGET Gnuradio::teleorbit) add_library(Gnuradio::teleorbit SHARED IMPORTED) set_target_properties(Gnuradio::teleorbit PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${TELEORBIT_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${TELEORBIT_INCLUDE_DIRS};${TELEORBIT_INCLUDE_DIRS}/teleorbit" INTERFACE_LINK_LIBRARIES "${TELEORBIT_LIBRARIES}" ) endif() mark_as_advanced(TELEORBIT_LIBRARIES TELEORBIT_INCLUDE_DIRS) cmake/Modules/FindUHD.cmake000066400000000000000000000076771352176506000160170ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # Provides the following imported target: # Iio::iio # ######################################################################## # Find the library for the USRP Hardware Driver ######################################################################## if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_UHD uhd) find_path(UHD_INCLUDE_DIRS NAMES uhd/config.hpp HINTS $ENV{UHD_DIR}/include ${PC_UHD_INCLUDEDIR} PATHS /usr/local/include /usr/include ${GNURADIO_INSTALL_PREFIX}/include ${UHD_ROOT}/include $ENV{UHD_ROOT}/include ) find_library(UHD_LIBRARIES NAMES uhd HINTS $ENV{UHD_DIR}/lib ${PC_UHD_LIBDIR} PATHS /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 /usr/lib ${GNURADIO_INSTALL_PREFIX}/lib ${UHD_ROOT}/lib $ENV{UHD_ROOT}/lib ${UHD_ROOT}/lib64 $ENV{UHD_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(UHD DEFAULT_MSG UHD_LIBRARIES UHD_INCLUDE_DIRS) if(PC_UHD_VERSION) set(UHD_VERSION ${PC_UHD_VERSION}) endif() if(NOT PC_UHD_VERSION) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) list(GET UHD_LIBRARIES 0 FIRST_DIR) get_filename_component(UHD_LIBRARIES_DIR ${FIRST_DIR} DIRECTORY) if(EXISTS ${UHD_LIBRARIES_DIR}/cmake/uhd/UHDConfigVersion.cmake) include(${UHD_LIBRARIES_DIR}/cmake/uhd/UHDConfigVersion.cmake) endif() if(PACKAGE_VERSION) set(UHD_VERSION ${PACKAGE_VERSION}) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) endif() set_package_properties(UHD PROPERTIES URL "https://www.ettus.com/sdr-software/detail/usrp-hardware-driver" ) if(UHD_FOUND AND UHD_VERSION) set_package_properties(UHD PROPERTIES DESCRIPTION "USRP Hardware Driver (found: v${UHD_VERSION})" ) else() set_package_properties(UHD PROPERTIES DESCRIPTION "USRP Hardware Driver" ) endif() if(UHD_FOUND AND NOT TARGET Uhd::uhd) add_library(Uhd::uhd SHARED IMPORTED) set_target_properties(Uhd::uhd PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${UHD_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${UHD_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${UHD_LIBRARIES}" ) endif() mark_as_advanced(UHD_LIBRARIES UHD_INCLUDE_DIRS) cmake/Modules/FindVOLK.cmake000066400000000000000000000077671352176506000161520ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # Provides the following imported target: # Volk::volk # ######################################################################## # Find VOLK (Vector-Optimized Library of Kernels) ######################################################################## if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_VOLK volk QUIET) find_path(VOLK_INCLUDE_DIRS NAMES volk/volk.h HINTS $ENV{VOLK_DIR}/include ${PC_VOLK_INCLUDEDIR} PATHS /usr/local/include /usr/include ${CMAKE_INSTALL_PREFIX}/include ${VOLK_ROOT}/include $ENV{VOLK_ROOT}/include ) find_library(VOLK_LIBRARIES NAMES volk HINTS $ENV{VOLK_DIR}/lib ${PC_VOLK_LIBDIR} PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu /usr/lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabi /usr/lib/aarch64-linux-gnu /usr/lib/mipsel-linux-gnu /usr/lib/mips-linux-gnu /usr/lib/mips64el-linux-gnuabi64 /usr/lib/powerpc-linux-gnu /usr/lib/powerpc64-linux-gnu /usr/lib/powerpc64le-linux-gnu /usr/lib/powerpc-linux-gnuspe /usr/lib/hppa-linux-gnu /usr/lib/s390x-linux-gnu /usr/lib/i386-gnu /usr/lib/hppa-linux-gnu /usr/lib/x86_64-kfreebsd-gnu /usr/lib/i386-kfreebsd-gnu /usr/lib/m68k-linux-gnu /usr/lib/sh4-linux-gnu /usr/lib/sparc64-linux-gnu /usr/lib/x86_64-linux-gnux32 /usr/lib/alpha-linux-gnu /usr/lib64 ${CMAKE_INSTALL_PREFIX}/lib ${VOLK_ROOT}/lib $ENV{VOLK_ROOT}/lib ${VOLK_ROOT}/lib64 $ENV{VOLK_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(VOLK DEFAULT_MSG VOLK_LIBRARIES VOLK_INCLUDE_DIRS) if(PC_VOLK_VERSION) set(VOLK_VERSION ${PC_VOLK_VERSION}) endif() if(NOT VOLK_VERSION) set(OLD_PACKAGE_VERSION ${PACKAGE_VERSION}) unset(PACKAGE_VERSION) list(GET VOLK_LIBRARIES 0 FIRST_DIR) get_filename_component(VOLK_LIB_DIR ${FIRST_DIR} DIRECTORY) if(EXISTS ${VOLK_LIB_DIR}/cmake/volk/VolkConfigVersion.cmake) include(${VOLK_LIB_DIR}/cmake/volk/VolkConfigVersion.cmake) endif() if(PACKAGE_VERSION) set(VOLK_VERSION ${PACKAGE_VERSION}) endif() set(PACKAGE_VERSION ${OLD_PACKAGE_VERSION}) endif() set_package_properties(VOLK PROPERTIES URL "http://libvolk.org" ) if(VOLK_FOUND AND VOLK_VERSION) set_package_properties(VOLK PROPERTIES DESCRIPTION "Vector-Optimized Library of Kernels (found: v${VOLK_VERSION})" ) else() set_package_properties(VOLK PROPERTIES DESCRIPTION "Vector-Optimized Library of Kernels" ) endif() mark_as_advanced(VOLK_LIBRARIES VOLK_INCLUDE_DIRS VOLK_VERSION) if(VOLK_FOUND AND NOT TARGET Volk::volk) add_library(Volk::volk SHARED IMPORTED) set_target_properties(Volk::volk PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${VOLK_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${VOLK_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${VOLK_LIBRARIES}" ) endif() cmake/Modules/FindVOLKGNSSSDR.cmake000066400000000000000000000057451352176506000172100ustar00rootroot00000000000000# Copyright (C) 2011-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # # Provides the following imported target: # Volkgnsssdr::volkgnsssdr # ######################################################################## # Find VOLK (Vector-Optimized Library of Kernels) GNSS-SDR library ######################################################################## if(NOT COMMAND feature_summary) include(FeatureSummary) endif() set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) include(FindPkgConfig) pkg_check_modules(PC_VOLK_GNSSSDR QUIET volk_gnsssdr) find_path(VOLK_GNSSSDR_INCLUDE_DIRS NAMES volk_gnsssdr/volk_gnsssdr.h HINTS $ENV{VOLK_GNSSSDR_DIR}/include ${PC_VOLK_GNSSSDR_INCLUDEDIR} PATHS /usr/local/include /usr/include ${GNURADIO_INSTALL_PREFIX}/include ${VOLKGNSSSDR_ROOT}/include $ENV{VOLKGNSSSDR_ROOT}/include ) find_library(VOLK_GNSSSDR_LIBRARIES NAMES volk_gnsssdr HINTS $ENV{VOLK_GNSSSDR_DIR}/lib ${PC_VOLK_GNSSSDR_LIBDIR} PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 ${GNURADIO_INSTALL_PREFIX}/lib ${VOLKGNSSSDR_ROOT}/lib $ENV{VOLKGNSSSDR_ROOT}/lib ${VOLKGNSSSDR_ROOT}/lib64 $ENV{VOLKGNSSSDR_ROOT}/lib64 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(VOLKGNSSSDR DEFAULT_MSG VOLK_GNSSSDR_LIBRARIES VOLK_GNSSSDR_INCLUDE_DIRS) mark_as_advanced(VOLK_GNSSSDR_LIBRARIES VOLK_GNSSSDR_INCLUDE_DIRS) if(PC_VOLK_GNSSSDR_VERSION) set(VOLKGNSSSDR_VERSION ${PC_VOLK_GNSSSDR_VERSION}) endif() if(VOLKGNSSSDR_FOUND AND VOLKGNSSSDR_VERSION) set_package_properties(VOLKGNSSSDR PROPERTIES DESCRIPTION "Vector-Optimized Library of Kernels for GNSS-SDR (found: v${VOLKGNSSSDR_VERSION})." ) else() set_package_properties(VOLKGNSSSDR PROPERTIES DESCRIPTION "Vector-Optimized Library of Kernels for GNSS-SDR." ) endif() if(VOLKGNSSSDR_FOUND AND NOT TARGET Volkgnsssdr::volkgnsssdr) add_library(Volkgnsssdr::volkgnsssdr SHARED IMPORTED) set_target_properties(Volkgnsssdr::volkgnsssdr PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${VOLK_GNSSSDR_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${VOLK_GNSSSDR_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${VOLK_GNSSSDR_LIBRARIES}" ) endif() cmake/Modules/GnsssdrBuildTypes.cmake000066400000000000000000000206521352176506000202120ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . if(DEFINED __INCLUDED_GNSSSDR_BUILD_TYPES_CMAKE) return() endif() set(__INCLUDED_GNSSSDR_BUILD_TYPES_CMAKE TRUE) # Standard CMake Build Types and their basic CFLAGS: # - None: nothing set # - Debug: -O2 -g # - Release: -O3 # - RelWithDebInfo: -O3 -g # - MinSizeRel: -Os # Additional Build Types, defined below: # - NoOptWithASM: -O0 -g -save-temps # - O2WithASM: -O2 -g -save-temps # - O3WithASM: -O3 -g -save-temps # Defines the list of acceptable cmake build types. When adding a new # build type below, make sure to add it to this list. list(APPEND AVAIL_BUILDTYPES None Debug Release RelWithDebInfo MinSizeRel Coverage NoOptWithASM O2WithASM O3WithASM ASAN ) ######################################################################## # GNSSSDR_CHECK_BUILD_TYPE(build type) # # Use this to check that the build type set in CMAKE_BUILD_TYPE on the # commandline is one of the valid build types used by this project. It # checks the value set in the cmake interface against the list of # known build types in AVAIL_BUILDTYPES. If the build type is found, # the function exits immediately. If nothing is found by the end of # checking all available build types, we exit with an error and list # the available build types. ######################################################################## function(GNSSSDR_CHECK_BUILD_TYPE settype) string(TOUPPER ${settype} _settype) foreach(btype ${AVAIL_BUILDTYPES}) string(TOUPPER ${btype} _btype) if(${_settype} STREQUAL ${_btype}) return() # found it; exit cleanly endif() endforeach() # Build type not found; error out message(FATAL_ERROR "Build type '${settype}' not valid, must be one of: ${AVAIL_BUILDTYPES}") endfunction() ######################################################################## # For GCC and Clang, we can set a build type: # # -DCMAKE_BUILD_TYPE=Coverage # # This type uses no optimization (-O0), outputs debug symbols (-g) and # outputs all intermediary files the build system produces, including # all assembly (.s) files. Look in the build directory for these # files. # NOTE: This is not defined on Windows systems. ######################################################################## if(NOT WIN32) set(CMAKE_CXX_FLAGS_COVERAGE "-Wall -pedantic -pthread -g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C++ compiler during Coverage builds." FORCE) set(CMAKE_C_FLAGS_COVERAGE "-Wall -pedantic -pthread -g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C compiler during Coverage builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "-W" CACHE STRING "Flags used for linking binaries during Coverage builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "-W" CACHE STRING "Flags used by the shared lib linker during Coverage builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE) endif() ######################################################################## # For GCC and Clang, we can set a build type: # # -DCMAKE_BUILD_TYPE=NoOptWithASM # # This type uses no optimization (-O0), outputs debug symbols (-g) and # outputs all intermediary files the build system produces, including # all assembly (.s) files. Look in the build directory for these # files. # NOTE: This is not defined on Windows systems. ######################################################################## if(NOT WIN32) set(CMAKE_CXX_FLAGS_NOOPTWITHASM "-Wall -save-temps -g -O0" CACHE STRING "Flags used by the C++ compiler during NoOptWithASM builds." FORCE) set(CMAKE_C_FLAGS_NOOPTWITHASM "-Wall -save-temps -g -O0" CACHE STRING "Flags used by the C compiler during NoOptWithASM builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_NOOPTWITHASM "-W" CACHE STRING "Flags used for linking binaries during NoOptWithASM builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_NOOPTWITHASM "-W" CACHE STRING "Flags used by the shared lib linker during NoOptWithASM builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_NOOPTWITHASM CMAKE_C_FLAGS_NOOPTWITHASM CMAKE_EXE_LINKER_FLAGS_NOOPTWITHASM CMAKE_SHARED_LINKER_FLAGS_NOOPTWITHASM) endif() ######################################################################## # For GCC and Clang, we can set a build type: # # -DCMAKE_BUILD_TYPE=O2WithASM # # This type uses level 2 optimization (-O2), outputs debug symbols # (-g) and outputs all intermediary files the build system produces, # including all assembly (.s) files. Look in the build directory for # these files. # NOTE: This is not defined on Windows systems. ######################################################################## if(NOT WIN32) set(CMAKE_CXX_FLAGS_O2WITHASM "-Wall -save-temps -g -O2" CACHE STRING "Flags used by the C++ compiler during O2WithASM builds." FORCE) set(CMAKE_C_FLAGS_O2WITHASM "-Wall -save-temps -g -O2" CACHE STRING "Flags used by the C compiler during O2WithASM builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_O2WITHASM "-W" CACHE STRING "Flags used for linking binaries during O2WithASM builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_O2WITHASM "-W" CACHE STRING "Flags used by the shared lib linker during O2WithASM builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_O2WITHASM CMAKE_C_FLAGS_O2WITHASM CMAKE_EXE_LINKER_FLAGS_O2WITHASM CMAKE_SHARED_LINKER_FLAGS_O2WITHASM) endif() ######################################################################## # For GCC and Clang, we can set a build type: # # -DCMAKE_BUILD_TYPE=O3WithASM # # This type uses level 3 optimization (-O3), outputs debug symbols # (-g) and outputs all intermediary files the build system produces, # including all assembly (.s) files. Look in the build directory for # these files. # NOTE: This is not defined on Windows systems. ######################################################################## if(NOT WIN32) set(CMAKE_CXX_FLAGS_O3WITHASM "-Wall -save-temps -g -O3" CACHE STRING "Flags used by the C++ compiler during O3WithASM builds." FORCE) set(CMAKE_C_FLAGS_O3WITHASM "-Wall -save-temps -g -O3" CACHE STRING "Flags used by the C compiler during O3WithASM builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_O3WITHASM "-W" CACHE STRING "Flags used for linking binaries during O3WithASM builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_O3WITHASM "-W" CACHE STRING "Flags used by the shared lib linker during O3WithASM builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_O3WITHASM CMAKE_C_FLAGS_O3WITHASM CMAKE_EXE_LINKER_FLAGS_O3WITHASM CMAKE_SHARED_LINKER_FLAGS_O3WITHASM) endif() ######################################################################## # For GCC and Clang, we can set a build type: # # -DCMAKE_BUILD_TYPE=ASAN # # This type creates an address sanitized build (-fsanitize=address) # and defaults to the DebugParanoid linker flags. # NOTE: This is not defined on Windows systems. ######################################################################## if(NOT WIN32) set(CMAKE_CXX_FLAGS_ASAN "-Wall -Wextra -g -O2 -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the C++ compiler during Address Sanitized builds." FORCE) set(CMAKE_C_FLAGS_ASAN "-Wall -Wextra -g -O2 -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the C compiler during Address Sanitized builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_ASAN "-W" CACHE STRING "Flags used for linking binaries during Address Sanitized builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_ASAN "-W" CACHE STRING "Flags used by the shared lib linker during Address Sanitized builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_ASAN CMAKE_C_FLAGS_ASAN CMAKE_EXE_LINKER_FLAGS_ASAN CMAKE_SHARED_LINKER_ASAN) endif() cmake/Modules/SetupPython.cmake000066400000000000000000000141241352176506000170610ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ######################################################################## # Check for the existence of a python module: # - desc a string description of the check # - mod the name of the module to import # - cmd an additional command to run # - have the result variable to set ######################################################################## macro(GNSSSDR_PYTHON_CHECK_MODULE_RAW desc python_code have) execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "${python_code}" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE return_code ) if(return_code EQUAL 0) message(STATUS "Python checking for ${desc} - found") set(${have} TRUE) else() message(STATUS "Python checking for ${desc} - not found") set(${have} FALSE) endif() endmacro() macro(GNSSSDR_PYTHON_CHECK_MODULE desc mod cmd have) gnsssdr_python_check_module_raw( "${desc}" " ######################################### try: import ${mod} assert ${cmd} except (ImportError, AssertionError): exit(-1) except: pass #########################################" "${have}") endmacro() ######################################################################## # Setup the python interpreter: # This allows the user to specify a specific interpreter, # or finds the interpreter via the built-in cmake module. ######################################################################## if(CMAKE_VERSION VERSION_LESS 3.12) if(PYTHON_EXECUTABLE) message(STATUS "User set python executable ${PYTHON_EXECUTABLE}") string(FIND "${PYTHON_EXECUTABLE}" "python3" IS_PYTHON3) if(IS_PYTHON3 EQUAL -1) find_package(PythonInterp ${GNSSSDR_PYTHON_MIN_VERSION} REQUIRED) else() find_package(PythonInterp ${GNSSSDR_PYTHON3_MIN_VERSION} REQUIRED) endif() gnsssdr_python_check_module("python >= ${GNSSSDR_PYTHON_MIN_VERSION}" sys "sys.version.split()[0] >= '${GNSSSDR_PYTHON_MIN_VERSION}'" PYTHON_MIN_VER_FOUND) gnsssdr_python_check_module("mako >= ${GNSSSDR_MAKO_MIN_VERSION}" mako "mako.__version__ >= '${GNSSSDR_MAKO_MIN_VERSION}'" MAKO_FOUND) gnsssdr_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) else() message(STATUS "PYTHON_EXECUTABLE not set - trying by default python2") message(STATUS "Use -DPYTHON_EXECUTABLE=/path/to/python3 to build for python3.") find_package(PythonInterp ${GNSSSDR_PYTHON_MIN_VERSION}) if(NOT PYTHONINTERP_FOUND) message(STATUS "python2 not found - trying with python3") find_package(PythonInterp ${GNSSSDR_PYTHON3_MIN_VERSION} REQUIRED) endif() gnsssdr_python_check_module("python >= ${GNSSSDR_PYTHON_MIN_VERSION}" sys "sys.version.split()[0] >= '${GNSSSDR_PYTHON_MIN_VERSION}'" PYTHON_MIN_VER_FOUND) gnsssdr_python_check_module("mako >= ${GNSSSDR_MAKO_MIN_VERSION}" mako "mako.__version__ >= '${GNSSSDR_MAKO_MIN_VERSION}'" MAKO_FOUND) gnsssdr_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) endif() else() find_package(Python3 COMPONENTS Interpreter) if(Python3_FOUND) set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) set(PYTHON_VERSION_MAJOR ${Python3_VERSION_MAJOR}) gnsssdr_python_check_module("python >= ${GNSSSDR_PYTHON_MIN_VERSION}" sys "sys.version.split()[0] >= '${GNSSSDR_PYTHON_MIN_VERSION}'" PYTHON_MIN_VER_FOUND) gnsssdr_python_check_module("mako >= ${GNSSSDR_MAKO_MIN_VERSION}" mako "mako.__version__ >= '${GNSSSDR_MAKO_MIN_VERSION}'" MAKO_FOUND) gnsssdr_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) endif() if(NOT Python3_FOUND OR NOT MAKO_FOUND OR NOT SIX_FOUND) find_package(Python2 COMPONENTS Interpreter) if(Python2_FOUND) set(PYTHON_EXECUTABLE ${Python2_EXECUTABLE}) set(PYTHON_VERSION_MAJOR ${Python2_VERSION_MAJOR}) gnsssdr_python_check_module("python >= ${GNSSSDR_PYTHON_MIN_VERSION}" sys "sys.version.split()[0] >= '${GNSSSDR_PYTHON_MIN_VERSION}'" PYTHON_MIN_VER_FOUND) gnsssdr_python_check_module("mako >= ${GNSSSDR_MAKO_MIN_VERSION}" mako "mako.__version__ >= '${GNSSSDR_MAKO_MIN_VERSION}'" MAKO_FOUND) gnsssdr_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) endif() if(NOT MAKO_FOUND OR NOT SIX_FOUND) unset(PYTHON_EXECUTABLE) find_package(PythonInterp ${GNSSSDR_PYTHON_MIN_VERSION}) gnsssdr_python_check_module("python >= ${GNSSSDR_PYTHON_MIN_VERSION}" sys "sys.version.split()[0] >= '${GNSSSDR_PYTHON_MIN_VERSION}'" PYTHON_MIN_VER_FOUND) gnsssdr_python_check_module("mako >= ${GNSSSDR_MAKO_MIN_VERSION}" mako "mako.__version__ >= '${GNSSSDR_MAKO_MIN_VERSION}'" MAKO_FOUND) gnsssdr_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) endif() endif() endif() if(${PYTHON_VERSION_MAJOR} VERSION_EQUAL 3) set(PYTHON3 TRUE) endif() if(CMAKE_CROSSCOMPILING) set(QA_PYTHON_EXECUTABLE "/usr/bin/python") else() set(QA_PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE}) endif() # make the path to the executable appear in the cmake gui set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter") set(QA_PYTHON_EXECUTABLE ${QA_PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter for QA tests") cmake/Modules/TestForARM.cmake000066400000000000000000000112771352176506000165130ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ############################################################################## # check if the compiler defines the architecture as ARM and set the # version, if found. # # - Anthony Arnold ############################################################################## if(__TEST_FOR_ARM_INCLUDED) return() endif() set(__TEST_FOR_ARM_INCLUDED TRUE) # Function checks if the input string defines ARM version and sets the # output variable if found. function(check_arm_version ppdef input_string version output_var) string(REGEX MATCH "${ppdef}" _VERSION_MATCH "${input_string}") if(NOT _VERSION_MATCH STREQUAL "") set(${output_var} "${version}" PARENT_SCOPE) endif() endfunction() message(STATUS "Checking for ARM") set(IS_ARM NO) set(ARM_VERSION "") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") execute_process(COMMAND echo "int main(){}" COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} -dM -E - OUTPUT_VARIABLE TEST_FOR_ARM_RESULTS) string(REGEX MATCH "__arm" ARM_FOUND "${TEST_FOR_ARM_RESULTS}") if(ARM_FOUND STREQUAL "") string(REGEX MATCH "__aarch64" ARM_FOUND "${TEST_FOR_ARM_RESULTS}") endif() if(NOT ARM_FOUND STREQUAL "") set(IS_ARM YES) message(STATUS "ARM system detected") # detect the version check_arm_version("__ARM_ARCH_2__" ${TEST_FOR_ARM_RESULTS} "armv2" ARM_VERSION) check_arm_version("__ARM_ARCH_2A__" ${TEST_FOR_ARM_RESULTS} "armv2a" ARM_VERSION) check_arm_version("__ARM_ARCH_3__" ${TEST_FOR_ARM_RESULTS} "armv3" ARM_VERSION) check_arm_version("__ARM_ARCH_3M__" ${TEST_FOR_ARM_RESULTS} "armv3m" ARM_VERSION) check_arm_version("__ARM_ARCH_4__" ${TEST_FOR_ARM_RESULTS} "armv4" ARM_VERSION) check_arm_version("__ARM_ARCH_4T__" ${TEST_FOR_ARM_RESULTS} "armv4t" ARM_VERSION) check_arm_version("__ARM_ARCH_5__" ${TEST_FOR_ARM_RESULTS} "armv5" ARM_VERSION) check_arm_version("__ARM_ARCH_5T__" ${TEST_FOR_ARM_RESULTS} "armv5t" ARM_VERSION) check_arm_version("__ARM_ARCH_5E__" ${TEST_FOR_ARM_RESULTS} "armv5e" ARM_VERSION) check_arm_version("__ARM_ARCH_5TE__" ${TEST_FOR_ARM_RESULTS} "armv5te" ARM_VERSION) check_arm_version("__ARM_ARCH_6__" ${TEST_FOR_ARM_RESULTS} "armv6" ARM_VERSION) check_arm_version("__ARM_ARCH_6J__" ${TEST_FOR_ARM_RESULTS} "armv6j" ARM_VERSION) check_arm_version("__ARM_ARCH_6K__" ${TEST_FOR_ARM_RESULTS} "armv6k" ARM_VERSION) check_arm_version("__ARM_ARCH_6T2__" ${TEST_FOR_ARM_RESULTS} "armv6t2" ARM_VERSION) check_arm_version("__ARM_ARCH_6Z__" ${TEST_FOR_ARM_RESULTS} "armv6z" ARM_VERSION) check_arm_version("__ARM_ARCH_6ZK__" ${TEST_FOR_ARM_RESULTS} "armv6zk" ARM_VERSION) check_arm_version("__ARM_ARCH_6M__" ${TEST_FOR_ARM_RESULTS} "armv6-m" ARM_VERSION) check_arm_version("__ARM_ARCH_7__" ${TEST_FOR_ARM_RESULTS} "armv7" ARM_VERSION) check_arm_version("__ARM_ARCH_7A__" ${TEST_FOR_ARM_RESULTS} "armv7-a" ARM_VERSION) check_arm_version("__ARM_ARCH_7M__" ${TEST_FOR_ARM_RESULTS} "armv7-m" ARM_VERSION) check_arm_version("__ARM_ARCH_7R__" ${TEST_FOR_ARM_RESULTS} "armv7-r" ARM_VERSION) check_arm_version("__ARM_ARCH_7EM_" ${TEST_FOR_ARM_RESULTS} "armv7e-m" ARM_VERSION) check_arm_version("__ARM_ARCH_7VE__" ${TEST_FOR_ARM_RESULTS} "armv7ve" ARM_VERSION) check_arm_version("__ARM_ARCH_8A__" ${TEST_FOR_ARM_RESULTS} "armv8-a" ARM_VERSION) check_arm_version("__ARM_ARCH_8A" ${TEST_FOR_ARM_RESULTS} "armv8-a" ARM_VERSION) # anything else just define as arm if(ARM_VERSION STREQUAL "") message(STATUS "Couldn't detect ARM version. Setting to 'arm'") set(ARM_VERSION "arm") else() message(STATUS "ARM version ${ARM_VERSION} detected") endif() else() message(STATUS "System is not ARM") endif() else() # TODO: Other compilers message(STATUS "Not detecting ARM on non-GNUCXX compiler. Defaulting to false") message(STATUS "If you are compiling for ARM, set IS_ARM=ON manually") endif() set(IS_ARM ${IS_ARM} CACHE BOOL "Compiling for ARM") set(ARM_VERSION ${ARM_VERSION} CACHE STRING "ARM version") cmake/Toolchains/000077500000000000000000000000001352176506000142465ustar00rootroot00000000000000cmake/Toolchains/oe-sdk_cross.cmake000066400000000000000000000033031352176506000176420ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ########################################################## # Toolchain file for Open Embedded ########################################################## set(CMAKE_SYSTEM_NAME Linux) string(REGEX MATCH "sysroots/([a-zA-Z0-9]+)" CMAKE_SYSTEM_PROCESSOR $ENV{SDKTARGETSYSROOT}) string(REGEX REPLACE "sysroots/" "" CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) set(CMAKE_CXX_FLAGS $ENV{CXXFLAGS} CACHE STRING "" FORCE) set(CMAKE_C_FLAGS $ENV{CFLAGS} CACHE STRING "" FORCE) # same flags for C sources set(CMAKE_LDFLAGS_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE) # same flags for C sources set(CMAKE_LIBRARY_PATH $ENV{OECORE_TARGET_SYSROOT}/usr/lib) set(CMAKE_FIND_ROOT_PATH $ENV{OECORE_TARGET_SYSROOT} $ENV{OECORE_NATIVE_SYSROOT}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(ORC_INCLUDE_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/include/orc-0.4) set(ORC_LIBRARY_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/lib) cmake/Toolchains/zynq-7000.cmake000066400000000000000000000033641352176506000166430ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . ########################################################## # Toolchain file for Zynq-7000 devices ########################################################## set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSROOT /home/carlesfernandez/binary) ### POINT THIS TO YOUR ROOTFS set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++) set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT} ) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(ZYNQ_FLAGS "-march=armv7-a -mthumb-interwork -mfloat-abi=hard -mfpu=neon -mtune=cortex-a7") set(CMAKE_ASM_FLAGS ${ZYNQ_FLAGS} CACHE STRING "" FORCE) set(CMAKE_C_FLAGS ${ZYNQ_FLAGS} CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS ${ZYNQ_FLAGS} CACHE STRING "" FORCE) set(CMAKE_LIBRARY_PATH ${CMAKE_SYSROOT}/usr/lib ${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf) set(CMAKE_INSTALL_PREFIX ${CMAKE_SYSROOT}/usr CACHE STRING "" FORCE) cmake/cmake_uninstall.cmake.in000066400000000000000000000030111352176506000167160ustar00rootroot00000000000000# Copyright (C) 2011-2018 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif() file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove \"$ENV{DESTDIR}${file}\" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif() else() message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif() endforeach() conf/000077500000000000000000000000001352176506000120105ustar00rootroot00000000000000conf/front-end-cal.conf000066400000000000000000000174641352176506000153240ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Default configuration file ; You can define your own front-end calibration tool configuration and invoke it by doing ; ./front-end-cal --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### INITIAL RECEIVER POSITIION ###### ; san francisco scenario ;GNSS-SDR.init_latitude_deg=40.74846557442795 ;GNSS-SDR.init_longitude_deg=-73.98593961814200 ;GNSS-SDR.init_altitude_m=329.11968943169342 ; Barcelona CTTC GNSS-SDR.init_latitude_deg=41.27719585553101 GNSS-SDR.init_longitude_deg=1.988782985790802 GNSS-SDR.init_altitude_m=10 ; Mozoncillo ;GNSS-SDR.init_latitude_deg=41.14534824586196 ;GNSS-SDR.init_longitude_deg=-4.187125019737464 ;GNSS-SDR.init_altitude_m=900 ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=true GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=217 GNSS-SDR.SUPL_MNC=7 GNSS-SDR.SUPL_LAC=861 GNSS-SDR.SUPL_CI=40184 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Osmosdr_Signal_Source ;#freq: RF front-end center frequency in [Hz] SignalSource.freq=1575420000 ;#item_type: Type and resolution for each of the signal samples. Use only gr_complex in this version. SignalSource.item_type=gr_complex ;#sampling_frequency: Original Signal sampling frequency in samples per second SignalSource.sampling_frequency=2000000 ;#gain: Front-end Gain in [dB] SignalSource.gain=40 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.AGC_enabled=false ;# Please note that the new RTL-SDR Blog V3 dongles ship a < 1 PPM ;# temperature compensated oscillator (TCXO), which is well suited for GNSS ;# signal processing, and a 4.5 V powered bias-tee to feed an active antenna. ;# Whether the bias-tee is turned off before reception depends on which version ;# of gr-osmosdr was used when compiling GNSS-SDR. With an old version ;# (for example, v0.1.4-8), the utility rtl_biast may be used to switch the ;# bias-tee, and then call gnss-sdr. ;# See https://github.com/rtlsdrblog/rtl_biast ;# After reception the bias-tee is switched off automatically by the program. ;# With newer versions of gr-osmosdr (>= 0.1.4-13), the bias-tee can be ;# activated by uncommenting the following line: ;SignalSource.osmosdr_args=rtl,bias=1 ;#samples: Number of samples to be processed. Notice that 0 means infinite samples. SignalSource.samples=0 ;#repeat: Repeat the processing file. SignalSource.repeat=false ;#dump: Dump the Signal source data to a file. SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat ;######### SIGNAL_CONDITIONER CONFIG ############ ;## It holds blocks to change data type, filter and resample input data. ;#implementation: Use [Pass_Through] or [Signal_Conditioner] ;#[Pass_Through] disables this block and the [DataTypeAdapter], [InputFilter] and [Resampler] blocks ;#[Signal_Conditioner] enables this block. Then you have to configure [DataTypeAdapter], [InputFilter] and [Resampler] blocks SignalConditioner.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER CONFIG ############ ;## Changes the type of input data. ;#implementation: Use [Ishort_To_Complex] or [Pass_Through] DataTypeAdapter.implementation=Pass_Through ;#dump: Dump the filtered data to a file. DataTypeAdapter.dump=false ;#dump_filename: Log path and filename. DataTypeAdapter.dump_filename=../data/data_type_adapter.dat ;######### INPUT_FILTER CONFIG ############ ;## Filter the input data. Can be combined with frequency translation for IF signals ;#implementation: Use [Pass_Through] or [Fir_Filter] or [Freq_Xlating_Fir_Filter] ;#[Pass_Through] disables this block ;#[Fir_Filter] enables a FIR Filter ;#[Freq_Xlating_Fir_Filter] enables FIR filter and a composite frequency translation that shifts IF down to zero Hz. InputFilter.implementation=Freq_Xlating_Fir_Filter ;#The following options are used in the filter design of Fir_Filter and Freq_Xlating_Fir_Filter implementation. ;#These options are based on parameters of gnuradio's function: gr_remez. ;#This function calculates the optimal (in the Chebyshev/minimax sense) FIR filter impulse response given a set of band edges, ;#the desired response on those bands, and the weight given to the error in those bands. ;#input_item_type: Type and resolution for input signal samples. InputFilter.input_item_type=gr_complex ;#outut_item_type: Type and resolution for output filtered signal samples. InputFilter.output_item_type=gr_complex ;#taps_item_type: Type and resolution for the taps of the filter. Use only float in this version. InputFilter.taps_item_type=float ;#number_of_taps: Number of taps in the filter. Increasing this parameter increases the processing time InputFilter.number_of_taps=5 ;#number_of _bands: Number of frequency bands in the filter. InputFilter.number_of_bands=2 ;#bands: frequency at the band edges [ b1 e1 b2 e2 b3 e3 ...]. ;#Frequency is in the range [0, 1], with 1 being the Nyquist frequency (Fs/2) ;#The number of band_begin and band_end elements must match the number of bands InputFilter.band1_begin=0.0 ;InputFilter.band1_end=0.8 InputFilter.band1_end=0.85 InputFilter.band2_begin=0.90 InputFilter.band2_end=1.0 ;#ampl: desired amplitude at the band edges [ a(b1) a(e1) a(b2) a(e2) ...]. ;#The number of ampl_begin and ampl_end elements must match the number of bands InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 ;#band_error: weighting applied to each band (usually 1). ;#The number of band_error elements must match the number of bands InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 ;#filter_type: one of "bandpass", "hilbert" or "differentiator" InputFilter.filter_type=bandpass ;#grid_density: determines how accurately the filter will be constructed. ;The minimum value is 16; higher values are slower to compute the filter. InputFilter.grid_density=16 ;#The following options are used only in Freq_Xlating_Fir_Filter implementation. ;#InputFilter.IF is the intermediate frequency (in Hz) shifted down to zero Hz InputFilter.sampling_frequency=2000000 InputFilter.IF=0 InputFilter.decimation_factor=1 ;#dump: Dump the filtered data to a file. InputFilter.dump=false ;#dump_filename: Log path and filename. InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ ;## Resamples the input data. ;#implementation: Use [Pass_Through] or [Direct_Resampler] ;#[Pass_Through] disables this block Resampler.implementation=Pass_Through ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler ;#item_type: Type and resolution for each of the signal samples. Use only gr_complex in this version. Acquisition.item_type=gr_complex ;#sampled_ms: Signal block duration for the acquisition signal detection [ms] Acquisition.sampled_ms=1 ;#threshold: Acquisition threshold Acquisition.threshold=0.015 ;#doppler_max: Maximum expected Doppler shift [Hz] Acquisition.doppler_max=100000 ;#doppler_max: Maximum expected Doppler shift [Hz] Acquisition.doppler_min=-100000 ;#doppler_step Doppler step in the grid search [Hz] Acquisition.doppler_step=500 ;#maximum dwells Acquisition.max_dwells=15 ;#dump: Enable or disable the acquisition internal data file logging [true] or [false] Acquisition.dump=false ;#filename: Log path and filename Acquisition.dump_filename=./acq_dump.dat conf/gnss-sdr-kalman-bayes.conf000066400000000000000000000041131352176506000167600ustar00rootroot00000000000000[GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 GNSS-SDR.internal_fs_hz=2000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/home/glamountain/gnss-sdr/data/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.freq=1575420000 SignalSource.samples=0 ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ishort_To_Complex InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Direct_Resampler Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=2000000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=0.008 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=../data/kalman/acq_dump ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_KF_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.dump=true Tracking_1C.dump_filename=../data/kalman/epl_tracking_ch_ Tracking_1C.bce_run = true; Tracking_1C.p_transient = 0; Tracking_1C.s_transient = 100; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=GPS_L1_CA_Observables ;######### PVT CONFIG ############ PVT.implementation=GPS_L1_CA_PVT PVT.averaging_depth=100 PVT.flag_averaging=true PVT.output_rate_ms=10 PVT.display_rate_ms=500 conf/gnss-sdr.conf000066400000000000000000000107011352176506000144160ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Default configuration file ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ishort_To_Complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through ; or Fir_Filter InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.44 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=4000000 InputFilter.IF=0 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=6 Channels_1B.count=0 Channels.in_acquisition=1 ;######### SPECIFIC CHANNELS CONFIG ###### ;######### CHANNEL 0 CONFIG ############ ;Channel0.signal=1C ;Channel0.satellite=11 ;######### CHANNEL 1 CONFIG ############ ;Channel1.signal=1C ;Channel1.satellite=18 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=5 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.AR_GPS=PPP-AR ; options: OFF, Continuous, Instantaneous, Fix-and-Hold, PPP-AR PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_BDS_B1I_byte.conf000066400000000000000000000077061352176506000165770ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=25000000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/BDS3_datasets/BdsB1IStr01.dat SignalSource.item_type=byte SignalSource.sampling_frequency=25000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Byte_To_Short InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=short InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.70 InputFilter.band2_begin=0.80 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=25000000 InputFilter.IF=6250000 InputFilter.dump = false InputFilter.dump_filename=/home/dmiralles/Documents/gnss-sdr/src/tests/signal_samples/BdsB1IStr01_fs25e6_if0_4ms.dat Resampler.implementation=Pass_Through Resampler.sample_freq_in=25000000 Resampler.sample_freq_out=25000000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_B1.count=10 Channels.in_acquisition=1 Channel.signal=B1 Channel0.satellite = 6; Channel1.satellite = 8; Channel2.satellite = 9; Channel3.satellite = 13; Channel4.satellite = 17; Channel5.satellite = 1; Channel6.satellite = 2; Channel7.satellite = 3; Channel8.satellite = 4; Channel9.satellite = 5; ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_B1.implementation=BEIDOU_B1I_PCPS_Acquisition Acquisition_B1.item_type=gr_complex Acquisition_B1.coherent_integration_time_ms=1 Acquisition_B1.threshold=0.0038 ;Acquisition_B1.pfa=0.0000001; Acquisition_B1.doppler_max=10000 Acquisition_B1.doppler_step=100 Acquisition_B1.dump=true Acquisition_B1.dump_filename=./bds_acq Acquisition_B1.blocking=false; Acquisition_B1.use_CFAR_algorithm=true; Acquisition_B1.bit_transition_flag = false; ;######### TRACKING GLOBAL CONFIG ############ Tracking_B1.implementation=BEIDOU_B1I_DLL_PLL_Tracking Tracking_B1.item_type=gr_complex Tracking_B1.pll_bw_hz=25.0; Tracking_B1.dll_bw_hz=2.50; Tracking_B1.dump=false; Tracking_B1.dump_filename=./epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_B1.implementation=BEIDOU_B1I_Telemetry_Decoder TelemetryDecoder_B1.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true PVT.rinex_version=3 PVT.rinex_output_enabled=true PVT.gpx_output_enabled=true conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf000066400000000000000000000156271352176506000203630ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=30000000 Receiver.sources_count=2 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ ;# Signal Source config for GPS, Galileo signals SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/archive/BDS3_datasets/long/20180713_211400_3.dat SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=10000000 SignalSource0.samples=0 SignalSource0.repeat=false SignalSource0.dump=false SignalSource0.enable_throttle_control=false ;# Signal Source config for BDS signals SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/archive/BDS3_datasets/long/20180713_211400_2.dat SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=30000000 SignalSource1.samples=0 SignalSource1.repeat=false SignalSource1.dump=false SignalSource1.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ ;# Signal Conditioner config for GPS, Galileo signals SignalConditioner0.implementation=Signal_Conditioner DataTypeAdapter0.implementation=Ibyte_To_Complex InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.70 InputFilter0.band2_begin=0.80 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=10000000 InputFilter0.IF=420000 Resampler0.implementation=Direct_Resampler Resampler0.sample_freq_in=10000000 Resampler0.sample_freq_out=30000000 Resampler0.item_type=gr_complex ;# Signal Conditioner config for BDS signals SignalConditioner1.implementation=Signal_Conditioner DataTypeAdapter1.implementation=Ibyte_To_Complex InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.70 InputFilter1.band2_begin=0.80 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=10000000 InputFilter1.IF=1020000 Resampler1.implementation=Pass_Through Resampler1.sample_freq_in=30000000 Resampler1.sample_freq_out=30000000 Resampler1.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=7 Channels_B3.count=4 Channels.in_acquisition=11 ;# Preparing collection for GPS satellites Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel0.signal=1C Channel0.satellite = 2 Channel1.signal=1C Channel1.satellite = 5 Channel2.signal=1C Channel2.satellite = 25 Channel3.signal=1C Channel3.satellite = 31 Channel4.signal=1C Channel4.satellite = 24 Channel5.signal=1C Channel5.satellite = 6 Channel6.signal=1C Channel6.satellite = 29 ;# Preparing collection for BDS satellites Channel7.RF_channel_ID=1 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 Channel10.RF_channel_ID=1 Channel7.signal=B3 Channel7.satellite = 29 Channel8.signal=B3 Channel8.satellite = 19 Channel9.signal=B3 Channel9.satellite = 20 Channel10.signal=B3 Channel10.satellite = 30 ;######### ACQUISITION GLOBAL CONFIG ############ ;# Acquisition config for BDS signals Acquisition_B3.implementation=BEIDOU_B3I_PCPS_Acquisition Acquisition_B3.item_type=gr_complex Acquisition_B3.coherent_integration_time_ms=1 Acquisition_B3.threshold=0.00025 Acquisition_B3.doppler_max=15000 Acquisition_B3.doppler_step=50 Acquisition_B3.dump=false Acquisition_B3.dump_filename=/home/dmiralles/Documents/Research/Publications/INSIDE_GNSS/bds_leg_pvt/Data/bds_b1i_acq Acquisition_B3.blocking=false; Acquisition_B3.use_CFAR_algorithm=true; Acquisition_B3.bit_transition_flag = false; ;# Acquisition config for GPS signals Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.001 Acquisition_1C.doppler_max=15000 Acquisition_1C.doppler_step=50 Acquisition_1C.dump=true Acquisition_1C.dump_filename=/home/dmiralles/Documents/Research/Publications/INSIDE_GNSS/bds_leg_pvt/Data/gps_l1ca_acq Acquisition_1C.blocking=false; Acquisition_1C.use_CFAR_algorithm=true; Acquisition_1C.bit_transition_flag = false; ;######### TRACKING GLOBAL CONFIG ############ Tracking_B3.implementation=BEIDOU_B3I_DLL_PLL_Tracking Tracking_B3.item_type=gr_complex Tracking_B3.pll_bw_hz=25.0; Tracking_B3.dll_bw_hz=2.50; Tracking_B3.dump=true; Tracking_B3.dump_filename=/home/dmiralles/Documents/Research/Publications/INSIDE_GNSS/bds_leg_pvt/Data/bds_b1i_trk_ch_ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=25.0; Tracking_1C.dll_bw_hz=2.50; Tracking_1C.dump=true; Tracking_1C.dump_filename=/home/dmiralles/Documents/Research/Publications/INSIDE_GNSS/bds_leg_pvt/Data/gps_l1ca_trk_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_B3.implementation=BEIDOU_B3I_Telemetry_Decoder TelemetryDecoder_B3.dump=false TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=OFF ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump=true PVT.dump_filename = /home/dmiralles/Documents/Research/Publications/INSIDE_GNSS/bds_leg_pvt/Data/pvt_l1 PVT.kml_output_enabled = false; PVT.xml_output_enabled = false; PVT.gpx_output_enabled = false; PVT.rinex_output_enabled = false; PVT.rtcm_output_enabled = false; PVT.nmea_output_enabled = false; PVT.geojson_output_enabled = false; conf/gnss-sdr_BDS_B3I_byte.conf000066400000000000000000000075461352176506000166030ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=50000000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/BDS3_datasets/BdsB3IStr01.dat SignalSource.item_type=byte SignalSource.sampling_frequency=50000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.enable_throttle_control=false ;SignalSource.samples=200000 ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Byte_To_Short InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=short InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.70 InputFilter.band2_begin=0.80 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=50000000 InputFilter.IF=12500000 InputFilter.dump = false InputFilter.dump_filename=/home/dmiralles/Documents/gnss-sdr/src/tests/signal_samples/BdsB3IStr01_fs50e6_if0_4ms.dat Resampler.implementation=Pass_Through Resampler.sample_freq_in=50000000 Resampler.sample_freq_out=50000000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_B3.count=10 Channels.in_acquisition=1 Channel.signal=B3 Channel0.satellite = 6; Channel1.satellite = 23; Channel2.satellite = 16; Channel3.satellite = 18; Channel4.satellite = 7; Channel5.satellite = 1; Channel6.satellite = 2; Channel7.satellite = 3; Channel8.satellite = 4; Channel9.satellite = 5; ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_B3.implementation=BEIDOU_B3I_PCPS_Acquisition Acquisition_B3.item_type=gr_complex Acquisition_B3.coherent_integration_time_ms=3 Acquisition_B3.max_dwells = 2 Acquisition_B3.threshold=0.0005 Acquisition_B3.doppler_max=10000 Acquisition_B3.doppler_step=100 Acquisition_B3.dump=false Acquisition_B3.dump_filename=./bds_acq ;######### TRACKING GLOBAL CONFIG ############ Tracking_B3.implementation=BEIDOU_B3I_DLL_PLL_Tracking Tracking_B3.item_type=gr_complex Tracking_B3.pll_bw_hz=40.0; Tracking_B3.dll_bw_hz=4.0; Tracking_B3.pll_bw_narrow_hz=20.0; Tracking_B3.dll_bw_narrow_hz=3.0; Tracking_B3.dump=false; Tracking_B3.dump_filename=./epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_B3.implementation=BEIDOU_B3I_Telemetry_Decoder TelemetryDecoder_B3.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true conf/gnss-sdr_BDS_B3I_ibyte.conf000066400000000000000000000077261352176506000167540ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=30000000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/BDS3_datasets/long/20180713_211400_2.dat SignalSource.item_type=ibyte SignalSource.sampling_frequency=30000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.70 InputFilter.band2_begin=0.80 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=30000000 InputFilter.IF=1020000 Resampler.implementation=Pass_Through Resampler.sample_freq_in=30000000 Resampler.sample_freq_out=30000000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_B3.count=4 Channels.in_acquisition=1 Channel.signal=B3 Channel0.satellite = 29; Channel1.satellite = 19; Channel2.satellite = 20; Channel3.satellite = 30; ;Channel0.satellite = 6; ;Channel1.satellite = 7; ;Channel2.satellite = 9; ;Channel3.satellite = 16; ;Channel4.satellite = 18; ;Channel5.satellite = 1; ;Channel6.satellite = 2; ;Channel7.satellite = 3; ;Channel8.satellite = 4; ;Channel9.satellite = 5; ;Channel10.satellite = 23; ;Channel11.satellite = 25; ;Channel12.satellite = 32; ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_B3.implementation=BEIDOU_B3I_PCPS_Acquisition Acquisition_B3.item_type=gr_complex Acquisition_B3.coherent_integration_time_ms=1 Acquisition_B3.max_dwells = 1 Acquisition_B3.threshold=0.0004 ;Acquisition_B3.pfa=0.0000001; Acquisition_B3.doppler_max=10000 Acquisition_B3.doppler_step=50 Acquisition_B3.dump=false Acquisition_B3.dump_filename=./bds_acq Acquisition_B3.blocking=false; Acquisition_B3.use_CFAR_algorithm=true; Acquisition_B3.bit_transition_flag = false; ;######### TRACKING GLOBAL CONFIG ############ Tracking_B3.implementation=BEIDOU_B3I_DLL_PLL_Tracking Tracking_B3.item_type=gr_complex Tracking_B3.pll_bw_hz=25.0; Tracking_B3.dll_bw_hz=2.50; Tracking_B3.dump=false; Tracking_B3.dump_filename=./epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_B3.implementation=BEIDOU_B3I_Telemetry_Decoder TelemetryDecoder_B3.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=OFF ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true conf/gnss-sdr_BDS_B3I_short.conf000066400000000000000000000054461352176506000167740ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; 5C is the channel identifier for BeiDou B2a, both the data signal and the pilot signal [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=30000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source ;SignalSource.filename=/home/dmiralles/Documents/gnss-sdr/src/tests/signal_samples/USRP_BDS_B2a_201805171115_fs_25e6_if0e3_ishort_200ms.bin SignalSource.filename=/archive/USRP_BDS_B3I_201805171118_fs_25e6_if0e3_ishort.bin SignalSource.item_type=ishort SignalSource.sampling_frequency=30000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner InputFilter.implementation=Pass_Through DataTypeAdapter.implementation=Ishort_To_Complex Resampler.implementation=Direct_Resampler Resampler.sample_freq_in=30000000 Resampler.sample_freq_out=30000000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=B3 Channels.in_acquisition=1 Channels_B3.count=5; Channel0.satellite = 27; Channel1.satellite = 22; Channel2.satellite = 21; Channel3.satellite = 28; Channel4.satellite = 30; ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_B3.implementation=BEIDOU_B3I_PCPS_Acquisition Acquisition_B3.item_type=gr_complex Acquisition_B3.coherent_integration_time_ms = 1 Acquisition_B3.max_dwells = 1 Acquisition_B3.threshold=0.0010 Acquisition_B3.doppler_max=10000 Acquisition_B3.doppler_step=50 Acquisition_B3.dump=true Acquisition_B3.dump_channel = 0; Acquisition_B3.dump_filename=/archive/bds_b3i_acq Acquisition_B3.blocking=false; Acquisition_B3.use_CFAR_algorithm=true; Acquisition_B3.bit_transition_flag = false; ;######### TRACKING GLOBAL CONFIG ############ Tracking_B3.implementation= BEIDOU_B3I_DLL_PLL_Tracking; Tracking_B3.item_type=gr_complex Tracking_B3.early_late_space_chips=0.5 Tracking_B3.pll_bw_hz=25.0; Tracking_B3.dll_bw_hz=2.0; Tracking_B3.dump=true; Tracking_B3.dump_filename=/archive/bds_b3i_trk_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_B3.implementation=BEIDOU_B3I_Telemetry_Decoder TelemetryDecoder_B3.dump=true TelemetryDecoder_B3.dump_filename=/archive/bds_b3i_tel_dec.dat ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true; Observables.dump_filename=/archive/bds_b3i_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.averaging_depth=100 PVT.flag_averaging=true PVT.output_rate_ms=10 PVT.display_rate_ms=500 conf/gnss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf000066400000000000000000000107351352176506000213160ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 Receiver.sources_count=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/archive/NT1065_L1_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=6625000 SignalSource0.samples=0 SignalSource0.dump=false; SignalSource0.dump_filename=/archive/signal_glonass.bin SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/archive/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=6625000 SignalSource1.samples=0 SignalSource1.dump=false; SignalSource1.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner DataTypeAdapter0.implementation=Ibyte_To_Complex InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.70 InputFilter0.band2_begin=0.80 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=6625000 InputFilter0.IF=60000 Resampler0.implementation=Direct_Resampler Resampler0.sample_freq_in=6625000 Resampler0.sample_freq_out=6625000 Resampler0.item_type=gr_complex SignalConditioner1.implementation=Signal_Conditioner DataTypeAdapter1.implementation=Ibyte_To_Complex InputFilter1.implementation=Pass_Through InputFilter1.item_type=gr_complex Resampler1.implementation=Pass_Through Resampler1.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels.in_acquisition=1 Channels_1G.count=5 Channels_1C.count=5 ;# Defining GLONASS satellites Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=1 Channel6.RF_channel_ID=1 Channel7.RF_channel_ID=1 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=0.0 Acquisition_1C.pfa=0.00001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false; Acquisition_1C.dump_filename=/archive/gps_acquisition.dat ;Acquisition_1C.coherent_integration_time_ms=10 Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1G.item_type=gr_complex Acquisition_1G.threshold=0.0 Acquisition_1G.pfa=0.00001 Acquisition_1G.doppler_max=10000 Acquisition_1G.doppler_step=250 Acquisition_1G.dump=false; Acquisition_1G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_1G.coherent_integration_time_ms=10 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.early_late_space_chips=0.5 Tracking_1C.pll_bw_hz=20.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dump=false; Tracking_1C.dump_filename=/archive/gps_tracking_ch_ Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_Tracking Tracking_1G.item_type=gr_complex Tracking_1G.early_late_space_chips=0.5 Tracking_1G.pll_bw_hz=25.0; Tracking_1G.dll_bw_hz=3.0; Tracking_1G.dump=false; Tracking_1G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false; Observables.dump_filename=/archive/gnss_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf000066400000000000000000000105521352176506000210540ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 Receiver.sources_count=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/archive/NT1065_L2_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=6625000 SignalSource0.samples=0 SignalSource0.dump=false; SignalSource0.dump_filename=/archive/signal_glonass.bin SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/archive/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=6625000 SignalSource1.samples=0 SignalSource1.dump=false; SignalSource1.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner DataTypeAdapter0.implementation=Ibyte_To_Complex InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.70 InputFilter0.band2_begin=0.80 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=6625000 InputFilter0.IF=60000 Resampler0.implementation=Pass_Through Resampler0.item_type=gr_complex SignalConditioner1.implementation=Signal_Conditioner DataTypeAdapter1.implementation=Ibyte_To_Complex InputFilter1.implementation=Pass_Through InputFilter1.item_type=gr_complex Resampler1.implementation=Pass_Through Resampler1.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels.in_acquisition=5 Channels_2S.count=5 Channels_1G.count=5 ;# Defining GLONASS satellites Channel0.RF_channel_ID=0 Channel0.signal=2S Channel1.RF_channel_ID=0 Channel1.signal=2S Channel2.RF_channel_ID=0 Channel2.signal=2S Channel3.RF_channel_ID=0 Channel3.signal=2S Channel4.RF_channel_ID=0 Channel4.signal=2S Channel5.RF_channel_ID=1 Channel6.RF_channel_ID=1 Channel7.RF_channel_ID=1 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0 Acquisition_2S.pfa=0.00001 Acquisition_2S.doppler_max=10000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1G.item_type=gr_complex Acquisition_1G.threshold=0.0 Acquisition_1G.pfa=0.00001 Acquisition_1G.doppler_max=10000 Acquisition_1G.doppler_step=250 Acquisition_1G.dump=false; Acquisition_1G.dump_filename=/archive/glo_acquisition.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.early_late_space_chips=0.5 Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.250; Tracking_2S.order=2; Tracking_2S.dump=false; Tracking_2S.dump_filename=/archive/gps_tracking_ch_ Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_Tracking Tracking_1G.item_type=gr_complex Tracking_1G.early_late_space_chips=0.5 Tracking_1G.pll_bw_hz=25.0; Tracking_1G.dll_bw_hz=3.0; Tracking_1G.dump=false; Tracking_1G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false; Observables.dump_filename=/archive/gnss_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=3 conf/gnss-sdr_GLONASS_L1_CA_ibyte.conf000066400000000000000000000051311352176506000177000ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/media/dmiralles/Seagate Backup Plus Drive/GNSS Data/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE ; <- PUT YOUR FILE HERE SignalSource.item_type=ibyte SignalSource.sampling_frequency=6625000 SignalSource.samples=0 SignalSource.dump=false; SignalSource.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Pass_Through Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=1G Channels.in_acquisition=1 Channels_1G.count=5 Channel0.satellite=24 ; k= Channel1.satellite=1 ; k=1 Channel2.satellite=2 ; k=-4 Channel3.satellite=20 ; k=-5 Channel4.satellite=21 ; k=4 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1G.item_type=gr_complex Acquisition_1G.threshold=0.0 Acquisition_1G.pfa=0.0001 Acquisition_1G.doppler_max=10000 Acquisition_1G.doppler_step=250 Acquisition_1G.dump=true; Acquisition_1G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_1G.coherent_integration_time_ms=1 ;Acquisition_1G.max_dwells = 5 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_Tracking Tracking_1G.item_type=gr_complex Tracking_1G.early_late_space_chips=0.5 Tracking_1G.pll_bw_hz=25.0; Tracking_1G.dll_bw_hz=3.0; Tracking_1G.dump=true; Tracking_1G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true; Observables.dump_filename=/archive/glo_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GLONASS_L1_CA_ibyte_coh_trk.conf000066400000000000000000000052231352176506000214130ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=ibyte SignalSource.sampling_frequency=6625000 SignalSource.samples=0 SignalSource.dump=false; SignalSource.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Pass_Through Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=1G Channels.in_acquisition=2 Channels_1G.count=8 ;Channel0.satellite=24 ; k=2 ;Channel1.satellite=1 ; k=1 ;Channel2.satellite=2 ; k=-4 ;Channel3.satellite=20 ; k=-5 ;Channel4.satellite=21 ; k=4 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1G.item_type=gr_complex Acquisition_1G.threshold=0.0 Acquisition_1G.pfa=0.0001 Acquisition_1G.doppler_max=10000 Acquisition_1G.doppler_step=250 Acquisition_1G.dump=false; Acquisition_1G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_1G.coherent_integration_time_ms=1 ;Acquisition_1G.max_dwells = 5 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_C_Aid_Tracking Tracking_1G.item_type=gr_complex Tracking_1G.early_late_space_chips=0.5 Tracking_1G.pll_bw_hz=40.0; Tracking_1G.dll_bw_hz=3.0; Tracking_1G.pll_bw_narrow_hz = 25.0; Tracking_1G.dll_bw_narrow_hz = 2.0; Tracking_1G.extend_correlation_ms = 1; Tracking_1G.dump=false; Tracking_1G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=/archive/glo_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GLONASS_L1_ibyte.conf000066400000000000000000000065561352176506000173510ustar00rootroot00000000000000; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [Hz]. GNSS-SDR.internal_fs_sps=6625000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/home/dmiralles/Documents/GSOC/GSOC2017/gnss-sdr/data/dmirallesNT1065_L2_20160831_fs6625e6_60e3_schar_1H.bin SignalSource.item_type=ibyte SignalSource.sampling_frequency=6625000 SignalSource.freq=1602000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.sample_type=iq SignalSource.seconds_to_skip=0 SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;DataTypeAdapter.implementation=Ishort_To_Complex DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Pass_Through ;InputFilter.input_item_type=gr_complex ;InputFilter.output_item_type=gr_complex InputFilter.item_type=gr_complex ;Resampler.implementation=Pass_Through ;Resampler.item_type=gr_complex Resampler.implementation=Pass_Through ;Resampler.sample_freq_in=4000000 ;Resampler.sample_freq_out=2000000 ;Resampler.item_type=gr_complex Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1R.count=8 ;Assuming here that identifier `1r=R` defines GLONASS SP signals Channels.in_acquisition=1 Channel.signal=1R ;Channel.item_type=cshort ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1R.dump=false Acquisition_1R.dump_filename=./acq_dump.dat Acquisition_1R.item_type=cshort Acquisition_1R.sampled_ms=1 Acquisition_1R.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1R.threshold=0.008 ;Acquisition_1C.pfa=0.000001 Acquisition_1R.doppler_max=10000 Acquisition_1R.doppler_step=250 Acquisition_1R.tong_init_val=2 Acquisition_1R.tong_max_val=10 Acquisition_1R.tong_max_dwells=20 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1R.implementation=GLONASS_L1_CA_DLL_PLL_C_Aid_Tracking Tracking_1R.item_type=cshort Tracking_1R.dump=false Tracking_1R.dump_filename=../data/epl_tracking_ch_ Tracking_1R.pll_bw_hz=40.0; Tracking_1R.dll_bw_hz=4.0; Tracking_1R.order=3; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1R.implementation=GLONASS_L1_CA_Telemetry_Decoder TelemetryDecoder_1R.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf000066400000000000000000000111211352176506000213050ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 Receiver.sources_count=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/media/dmiralles/Seagate Backup Plus Drive/GNSS Data/NT1065_L1_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=6625000 SignalSource0.samples=0 SignalSource0.dump=false; SignalSource0.dump_filename=/archive/signal_glonass.bin SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/media/dmiralles/Seagate Backup Plus Drive/GNSS Data/NT1065_GLONASS_L2_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=6625000 SignalSource1.samples=0 SignalSource1.dump=false; SignalSource1.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner DataTypeAdapter0.implementation=Ibyte_To_Complex InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.70 InputFilter0.band2_begin=0.80 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=6625000 InputFilter0.IF=60000 Resampler0.implementation=Direct_Resampler Resampler0.sample_freq_in=6625000 Resampler0.sample_freq_out=6625000 Resampler0.item_type=gr_complex SignalConditioner1.implementation=Signal_Conditioner DataTypeAdapter1.implementation=Ibyte_To_Complex InputFilter1.implementation=Pass_Through InputFilter1.item_type=gr_complex Resampler1.implementation=Pass_Through Resampler1.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels.in_acquisition=1 Channels_2G.count=5 Channels_1C.count=5 ;# Defining GLONASS satellites Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=1 Channel6.RF_channel_ID=1 Channel7.RF_channel_ID=1 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=0.0 Acquisition_1C.pfa=0.00001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false; Acquisition_1C.dump_filename=/archive/gps_acquisition.dat ;Acquisition_1C.coherent_integration_time_ms=10 Acquisition_2G.implementation=GLONASS_L2_CA_PCPS_Acquisition Acquisition_2G.item_type=gr_complex Acquisition_2G.threshold=0.0 Acquisition_2G.pfa=0.00001 Acquisition_2G.doppler_max=10000 Acquisition_2G.doppler_step=250 Acquisition_2G.dump=false; Acquisition_2G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_2G.coherent_integration_time_ms=10 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.early_late_space_chips=0.5 Tracking_1C.pll_bw_hz=20.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dump=false; Tracking_1C.dump_filename=/archive/gps_tracking_ch_ Tracking_2G.implementation=GLONASS_L2_CA_DLL_PLL_Tracking Tracking_2G.item_type=gr_complex Tracking_2G.early_late_space_chips=0.5 Tracking_2G.pll_bw_hz=25.0; Tracking_2G.dll_bw_hz=2.0; Tracking_2G.dump=false; Tracking_2G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_2G.implementation=GLONASS_L2_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false; Observables.dump_filename=/archive/gnss_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf000066400000000000000000000106061352176506000210550ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 Receiver.sources_count=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/archive/NT1065_L2_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=6625000 SignalSource0.samples=0 SignalSource0.dump=false; SignalSource0.dump_filename=/archive/signal_glonass.bin SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/archive/NT1065_GLONASS_L2_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=6625000 SignalSource1.samples=0 SignalSource1.dump=false; SignalSource1.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner DataTypeAdapter0.implementation=Ibyte_To_Complex InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.70 InputFilter0.band2_begin=0.80 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=6625000 InputFilter0.IF=60000 Resampler0.implementation=Pass_Through Resampler0.item_type=gr_complex SignalConditioner1.implementation=Signal_Conditioner DataTypeAdapter1.implementation=Ibyte_To_Complex InputFilter1.implementation=Pass_Through InputFilter1.item_type=gr_complex Resampler1.implementation=Pass_Through Resampler1.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels.in_acquisition=5 Channels_2S.count=5 Channels_2G.count=5 ;# Defining GLONASS satellites Channel0.RF_channel_ID=0 Channel0.signal=2S Channel1.RF_channel_ID=0 Channel1.signal=2S Channel2.RF_channel_ID=0 Channel2.signal=2S Channel3.RF_channel_ID=0 Channel3.signal=2S Channel4.RF_channel_ID=0 Channel4.signal=2S Channel5.RF_channel_ID=1 Channel6.RF_channel_ID=1 Channel7.RF_channel_ID=1 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0 Acquisition_2S.pfa=0.00001 Acquisition_2S.doppler_max=10000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_2G.implementation=GLONASS_L2_CA_PCPS_Acquisition Acquisition_2G.item_type=gr_complex Acquisition_2G.threshold=0.0 Acquisition_2G.pfa=0.00001 Acquisition_2G.doppler_max=10000 Acquisition_2G.doppler_step=250 Acquisition_2G.dump=false; Acquisition_2G.dump_filename=/archive/glo_acquisition.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.early_late_space_chips=0.5 Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.250; Tracking_2S.order=2; Tracking_2S.dump=false; Tracking_2S.dump_filename=/archive/gps_tracking_ch_ Tracking_2G.implementation=GLONASS_L2_CA_DLL_PLL_Tracking Tracking_2G.item_type=gr_complex Tracking_2G.early_late_space_chips=0.5 Tracking_2G.pll_bw_hz=25.0; Tracking_2G.dll_bw_hz=3.0; Tracking_2G.dump=false; Tracking_2G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2G.implementation=GLONASS_L2_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false; Observables.dump_filename=/archive/gnss_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=3 conf/gnss-sdr_GLONASS_L2_CA_ibyte.conf000066400000000000000000000046531352176506000177110ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/media/dmiralles/Seagate Backup Plus Drive/GNSS Data/NT1065_GLONASS_L2_20160831_fs6625e6_60e3_schar_1m.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=ibyte SignalSource.sampling_frequency=6625000 SignalSource.samples=0 SignalSource.dump=false; SignalSource.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Pass_Through Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=2G Channels.in_acquisition=1 Channels_2G.count=5 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_2G.implementation=GLONASS_L2_CA_PCPS_Acquisition Acquisition_2G.item_type=gr_complex Acquisition_2G.threshold=0.0 Acquisition_2G.pfa=0.0001 Acquisition_2G.doppler_max=10000 Acquisition_2G.doppler_step=250 Acquisition_2G.dump=true; Acquisition_2G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_2G.coherent_integration_time_ms=1 ;Acquisition_2G.max_dwells = 5 ;######### TRACKING GLOBAL CONFIG ############ Tracking_2G.implementation=GLONASS_L2_CA_DLL_PLL_Tracking Tracking_2G.item_type=gr_complex Tracking_2G.early_late_space_chips=0.5 Tracking_2G.pll_bw_hz=20.0; Tracking_2G.dll_bw_hz=2.0; Tracking_2G.dump=true; Tracking_2G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_2G.implementation=GLONASS_L2_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true; Observables.dump_filename=/archive/glo_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GLONASS_L2_CA_ibyte_coh_trk.conf000066400000000000000000000052231352176506000214140ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=ibyte SignalSource.sampling_frequency=6625000 SignalSource.samples=0 SignalSource.dump=false; SignalSource.dump_filename=/archive/signal_glonass.bin ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Pass_Through Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=1G Channels.in_acquisition=2 Channels_1G.count=8 ;Channel0.satellite=24 ; k=2 ;Channel1.satellite=1 ; k=1 ;Channel2.satellite=2 ; k=-4 ;Channel3.satellite=20 ; k=-5 ;Channel4.satellite=21 ; k=4 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition Acquisition_1G.item_type=gr_complex Acquisition_1G.threshold=0.0 Acquisition_1G.pfa=0.0001 Acquisition_1G.doppler_max=10000 Acquisition_1G.doppler_step=250 Acquisition_1G.dump=false; Acquisition_1G.dump_filename=/archive/glo_acquisition.dat ;Acquisition_1G.coherent_integration_time_ms=1 ;Acquisition_1G.max_dwells = 5 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_C_Aid_Tracking Tracking_1G.item_type=gr_complex Tracking_1G.early_late_space_chips=0.5 Tracking_1G.pll_bw_hz=40.0; Tracking_1G.dll_bw_hz=3.0; Tracking_1G.pll_bw_narrow_hz = 25.0; Tracking_1G.dll_bw_narrow_hz = 2.0; Tracking_1G.extend_correlation_ms = 1; Tracking_1G.dump=false; Tracking_1G.dump_filename=/archive/glo_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=/archive/glo_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GPS_L1_2ch_fmcomms2_realtime.conf000066400000000000000000000067231352176506000217150ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [Sps]. GNSS-SDR.internal_fs_sps=7000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Fmcomms2_Signal_Source SignalSource.item_type=gr_complex SignalSource.device_address=192.168.0.4 SignalSource.sampling_frequency=7000000 SignalSource.freq=1575420000 SignalSource.bandwidth=4000000 SignalSource.RF_channels=2 SignalSource.rx1_enable=true SignalSource.rx2_enable=true SignalSource.gain_mode_rx1=slow_attack SignalSource.gain_mode_rx2=slow_attack SignalSource.rf_port_select=A_BALANCED SignalSource.gain_rx1=64 SignalSource.gain_rx2=64 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false SignalSource.enable_dds_lo=false SignalSource.freq_rf_tx_hz=1260000000 SignalSource.freq_dds_tx_hz=1000 SignalSource.scale_dds_dbfs=0.0 SignalSource.phase_dds_deg=0.0 SignalSource.tx_attenuation_db=0.0 ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner0.implementation=Pass_Through SignalConditioner1.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel0.signal=1C Channel1.RF_channel_ID=0 Channel1.signal=1C Channel2.RF_channel_ID=0 Channel2.signal=1C Channel3.RF_channel_ID=0 Channel3.signal=1C Channel4.RF_channel_ID=1 Channel4.signal=1C Channel5.RF_channel_ID=1 Channel5.signal=1C Channel6.RF_channel_ID=1 Channel6.signal=1C Channel7.RF_channel_ID=1 Channel7.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=20 Acquisition_1C.use_CFAR_algorithm=false Acquisition_1C.blocking=true Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ Tracking_1C.pll_bw_hz=35.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.early_late_space_chips=0.5; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_2ch_udp.conf000066400000000000000000000061671352176506000171020ustar00rootroot00000000000000; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [Sps]. GNSS-SDR.internal_fs_sps=13250000 ;//66.25/5 ;GNSS-SDR.internal_fs_sps=6625000 ;//66.25/10 ;GNSS-SDR.internal_fs_sps=3312500 ;//66.25/20 ;GNSS-SDR.internal_fs_sps=2650000 ;//66.25/25 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Custom_UDP_Signal_Source SignalSource.item_type=gr_complex SignalSource.origin_address=0.0.0.0 SignalSource.capture_device=eth0 SignalSource.port=1234 SignalSource.payload_bytes=1472 ;SignalSource.sample_type=cbyte SignalSource.sample_type=c4bits SignalSource.IQ_swap=false SignalSource.RF_channels=1 SignalSource.channels_in_udp=2 SignalSource.dump=false SignalSource.dump_filename=./signal_source.dat ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;SignalConditioner0.implementation=Pass_Through ;SignalConditioner1.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel.signal=1C Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 ;Channel0.signal=1C ;Channel1.RF_channel_ID=1 ;Channel1.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=17 Acquisition_1C.use_CFAR_algorithm=false Acquisition_1C.blocking=false Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ Tracking_1C.pll_bw_hz=35.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.early_late_space_chips=0.5; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_CA_ibyte.conf000066400000000000000000000055651352176506000172360ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/NT1065_L1_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=ibyte ;SignalSource.samples=66250000 SignalSource.samples=0 SignalSource.dump=false; ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ibyte_To_Complex InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.70 InputFilter.band2_begin=0.80 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=6625000 InputFilter.IF=60000 Resampler.implementation=Direct_Resampler Resampler.sample_freq_in=6625000 Resampler.sample_freq_out=6625000 Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channel.signal=1C Channels.in_acquisition=1 Channels_1C.count=6 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=0.01 ;Acquisition_1C.pfa=0.00001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false; Acquisition_1C.dump_filename=/archive/gps_acquisition.dat ;Acquisition_1C.coherent_integration_time_ms=10 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.early_late_space_chips=0.5 Tracking_1C.pll_bw_hz=25.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.dump=false; Tracking_1C.dump_filename=/archive/gps_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true; Observables.dump_filename=/archive/gps_observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MT1097_rate_ms=1000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=3 conf/gnss-sdr_GPS_L1_FPGA.conf000066400000000000000000000050651352176506000162670ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Pass_Through SignalSource.filename=/datalogger/signals/Agilent/New York/4msps.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.freq=1575420000 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false SignalSource.enable_FPGA=true ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through SignalConditioner.item_type=cshort SignalConditioner.enable_FPGA=true ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C Channel.enable_FPGA=true ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fpga Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat Acquisition_1C.item_type=cshort Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.select_queue_Fpga=0; Acquisition_1C.threshold=0.005 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking_Fpga Tracking_1C.item_type=cshort Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=GPS_L1_CA_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=GPS_L1_CA_PVT PVT.averaging_depth=100 PVT.flag_averaging=false PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_GN3S_realtime.conf000066400000000000000000000073601352176506000201460ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2727933.33 ; 8183800/3 ;######### SIGNAL_SOURCE CONFIG ############ ;#implementation: ;#Notes for GN3S source: ; - The front-end sampling frequency is fixed to 8.1838 MSPS (8183800 Hz). ; - The baseband signal is shifted to an IF of 38400 Hz. It should be corrected with the signal conditioner block GNSS-SDR.internal_fs_sps=2727933.33 ; 8183800/3 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=GN3S_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=8183800 SignalSource.dump=false SignalSource.dump_filename=../signal_source.dat ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=8183800 InputFilter.IF=38400 InputFilter.decimation_factor=3 ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=5 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.008 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false. Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.dump=false PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 conf/gnss-sdr_GPS_L1_LimeSDR.conf000066400000000000000000000064111352176506000170050ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=2000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Osmosdr_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 ;# LimeSDR RX1 antennas: NONE,LNAH,LNAL,LNAW SignalSource.antenna=LNAW SignalSource.freq=1575420000 SignalSource.gain=40 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.AGC_enabled=false SignalSource.samples=0 SignalSource.repeat=false ;# Next line enables the LimeSDR SignalSource.osmosdr_args=driver=lime,soapy=0 SignalSource.enable_throttle_control=false SignalSource.dump=false SignalSource.dump_filename=./signal_source.dat ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.decimation_factor=1 InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.85 InputFilter.band2_begin=0.9 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.sampled_ms=1 Acquisition_1C.threshold=0.015 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_min=-10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=GPS_L1_CA_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ ;PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.iono_model=Broadcast PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GPS_L1_SPIR.conf000066400000000000000000000100001352176506000163100ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Spir_File_Signal_Source SignalSource.filename=/dtalogger/signals/spir/data/20Secs/20Secs_L1.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=int SignalSource.sampling_frequency=80000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=float InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=80000000 InputFilter.IF=10164 InputFilter.decimation_factor=20 ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.sample_freq_in=80000000 Resampler.sample_freq_out=4000000 Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=10 Channels_1B.count=0 Channels.in_acquisition=1 Channel.signal=1C ;######### CHANNEL 0 CONFIG ############ ;Channel0.satellite=20 ;######### CHANNEL 1 CONFIG ############ ;Channel1.satellite=12 ;######### CHANNEL 2 CONFIG ############ ;Channel2.satellite=11 ;######### CHANNEL 3 CONFIG ############ ;Channel3.satellite=19 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_min=-10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=5 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=20.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ ;#implementation: Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ ;#implementation: Position Velocity and Time (PVT) implementation algorithm PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=500 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=true; PVT.nmea_dump_devname=/dev/pts/4 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_GPS_L1_USRP_X300_realtime.conf000066400000000000000000000113651352176506000207370ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Configuration file for using USRP X300 as a RF front-end for GPS L1 signals. ; Set SignalSource.device_address to the IP address of your device ; and run: ; gnss-sdr --config_file=/path/to/gnss-sdr_GPS_L1_USRP_X300_realtime_new.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.device_address=192.168.40.2 ; <- PUT THE IP ADDRESS OF YOUR USRP HERE SignalSource.item_type=cshort SignalSource.sampling_frequency=4000000 SignalSource.freq=1575420000 SignalSource.gain=40 SignalSource.subdevice=A:0 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=cshort ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Fir_Filter InputFilter.input_item_type=cshort InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=11 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.48 InputFilter.band2_begin=0.52 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=4000000 InputFilter.IF=0 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=4000000 Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels_1B.count=0 Channels.in_acquisition=1 Channel.signal=1C ;Channel0.signal=1C ;Channel1.signal=1C ;Channel2.signal=1C ;Channel3.signal=1C ;Channel4.signal=1C ;Channel5.signal=1C ;Channel6.signal=1C ;Channel7.signal=1C ;Channel8.signal=1C ;Channel9.signal=1C ;Channel10.signal=1C ;Channel11.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.01 ;Acquisition_1C.pfa=0.00001 Acquisition_1C.doppler_max=8000 Acquisition_1C.doppler_step=500 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=30.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_GPS_L1_USRP_realtime.conf000066400000000000000000000071151352176506000202230ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Configuration file for using USRP 1 as a RF front-end for GPS L1 signals. ; Run: ; gnss-sdr --config_file=/path/to/gnss-sdr_GPS_L1_USRP_realtime.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source ;SignalSource.device_address=192.168.40.2 ; <- PUT THE IP ADDRESS OF YOUR USRP HERE SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 SignalSource.freq=1575420000 SignalSource.gain=60 SignalSource.subdevice=A:0 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=6 Channels_1B.count=0 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.01 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=30.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ ;#implementation: Position Velocity and Time (PVT) implementation: PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_GPS_L1_acq_QuickSync.conf000066400000000000000000000065651352176506000203150ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ishort_To_Complex DataTypeAdapter.dump=false DataTypeAdapter.dump_filename=../data/data_type_adapter.dat ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=4000000 Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=5 Channels.in_acquisition=1 ;######### ACQUISITION GLOBAL CONFIG ############_1C Acquisition_1C.implementation=GPS_L1_CA_PCPS_QuickSync_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent-integration_time_ms=4 Acquisition_1C.dump=true ;Acquisition_1C.dump_filename=./acq_dump.dat ;######### ACQUISITION CHANNELS CONFIG ###### Acquisition_1C.implementation=GPS_L1_CA_PCPS_QuickSync_Acquisition Acquisition_1C.threshold=0.4 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=50.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5 Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_GPS_L1_bladeRF.conf000066400000000000000000000063671352176506000170570ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=2000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Osmosdr_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 SignalSource.freq=1575420000 ;# RF Gain: LNA Gain {0, 3, 6} SignalSource.gain=6 ;# IF Gain: N/A SignalSource.rf_gain=40 ;# BB Gain: RX VGA1 + VGA2 [5, 60] SignalSource.if_gain=48 SignalSource.AGC_enabled=false SignalSource.samples=0 SignalSource.repeat=false SignalSource.osmosdr_args=bladerf=0 ; This line enables the bladeRF SignalSource.enable_throttle_control=false SignalSource.dump=false SignalSource.dump_filename=./signal_source.dat ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.decimation_factor=1 InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.85 InputFilter.band2_begin=0.9 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.015 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ ;PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.iono_model=Broadcast PVT.trop_model=Saastamoinen PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1019_rate_ms=5000 PVT.rtcm_MT1077_rate_ms=1000 PVT.rinex_version=2 conf/gnss-sdr_GPS_L1_fmcomms2_realtime.conf000066400000000000000000000102661352176506000211560ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=2000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Fmcomms2_Signal_Source SignalSource.item_type=gr_complex SignalSource.device_address=10.42.0.196 SignalSource.sampling_frequency=2000000 SignalSource.freq=1575420000 SignalSource.bandwidth=2000000 SignalSource.rx1_enable=true SignalSource.gain_mode_rx1=manual SignalSource.rf_port_select=A_BALANCED SignalSource.gain_rx1=64 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=2000000 InputFilter.IF=0; IF deviation due to front-end LO inaccuracies [Hz] ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=5 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.015 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_gr_complex.conf000066400000000000000000000060121352176506000177020ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.freq=1575420000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ishort_To_Complex DataTypeAdapter.dump=false DataTypeAdapter.dump_filename=../data/DataTypeAdapter.dat InputFilter.implementation=Pass_Through InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex Resampler.implementation=Pass_Through Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=5 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.008 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=true Tracking_1C.dump_filename=epl_tracking_ch_ Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_c ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=1 PVT.display_rate_ms=100 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_gr_complex_gpu.conf000066400000000000000000000051431352176506000205610ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/Agilent/New York/4msps.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.samples=250000000 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking_GPU Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_ishort.conf000066400000000000000000000057521352176506000170650ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/archive/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner DataTypeAdapter.implementation=Ishort_To_Cshort InputFilter.implementation=Pass_Through InputFilter.item_type=cshort Resampler.implementation=Direct_Resampler Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=2000000 Resampler.item_type=cshort ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=cshort Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.008 ;Acquisition_1C.pfa=0.000001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat Acquisition_1C.blocking=false; ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=cshort Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.dump=false; Tracking_1C.dump_filename=./epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_nsr.conf000066400000000000000000000123371352176506000163540ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Sample configuration file for IFEN SX-NSR software receiver front-end ; https://www.ifen.com/products/sx3-gnss-software-receiver/ ; This sample configuration is able to process directly .sream binary files ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2560000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Nsr_File_Signal_Source SignalSource.filename=/home/javier/Descargas/RoofTop_FE0_Band1.stream ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=20480000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=float InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=20480000 #InputFilter.IF=5499998.47412109 InputFilter.IF=5679999.2370605494 InputFilter.decimation_factor=8 ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=0 Channels_2S.count=8 Channels.in_acquisition=1 ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.scoherent_integration_time_ms=1 Acquisition_1C.threshold=0.0075 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.coherent_integration_time_ms=20 Acquisition_2S.threshold=0.00045 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_step=100 Acquisition_2S.bit_transition_flag=false Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### GPS L2C GENERIC TRACKING CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=1.5; Tracking_2S.dll_bw_hz=0.4; Tracking_2S.order=2; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=true Tracking_2S.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true conf/gnss-sdr_GPS_L1_nsr_kf.conf000066400000000000000000000175461352176506000170430ustar00rootroot00000000000000; Default configuration file ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. ;GNSS-SDR.internal_fs_sps=6826700 GNSS-SDR.internal_fs_sps=2560000 ;GNSS-SDR.internal_fs_sps=4096000 ;GNSS-SDR.internal_fs_sps=5120000 ;######### SIGNAL_SOURCE CONFIG ############ ;#implementation: Use [File_Signal_Source] [Nsr_File_Signal_Source] or [UHD_Signal_Source] or [GN3S_Signal_Source] (experimental) SignalSource.implementation=Nsr_File_Signal_Source ;#filename: path to file with the captured GNSS signal samples to be processed SignalSource.filename=/home/javier/signals/ifen/E1L1_FE0_Band0.stream ; <- PUT YOUR FILE HERE ;#item_type: Type and resolution for each of the signal samples. Use only gr_complex in this version. SignalSource.item_type=byte ;#sampling_frequency: Original Signal sampling frequency in [Hz] SignalSource.sampling_frequency=20480000 ;#freq: RF front-end center frequency in [Hz] SignalSource.freq=1575420000 ;#samples: Number of samples to be processed. Notice that 0 indicates the entire file. SignalSource.samples=0 ;#repeat: Repeat the processing file. Disable this option in this version SignalSource.repeat=false ;#dump: Dump the Signal source data to a file. Disable this option in this version SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat ;#enable_throttle_control: Enabling this option tells the signal source to keep the delay between samples in post processing. ; it helps to not overload the CPU, but the processing time will be longer. SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ ;## It holds blocks to change data type, filter and resample input data. ;#implementation: Use [Pass_Through] or [Signal_Conditioner] ;#[Pass_Through] disables this block and the [DataTypeAdapter], [InputFilter] and [Resampler] blocks ;#[Signal_Conditioner] enables this block. Then you have to configure [DataTypeAdapter], [InputFilter] and [Resampler] blocks SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ ;## Changes the type of input data. ;#implementation: [Pass_Through] disables this block DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ ;## Filter the input data. Can be combined with frequency translation for IF signals ;#implementation: Use [Pass_Through] or [Fir_Filter] or [Freq_Xlating_Fir_Filter] ;#[Freq_Xlating_Fir_Filter] enables FIR filter and a composite frequency translation ;# that shifts IF down to zero Hz. InputFilter.implementation=Freq_Xlating_Fir_Filter ;#dump: Dump the filtered data to a file. InputFilter.dump=false ;#dump_filename: Log path and filename. InputFilter.dump_filename=../data/input_filter.dat ;#The following options are used in the filter design of Fir_Filter and Freq_Xlating_Fir_Filter implementation. ;#These options are based on parameters of gnuradio's function: gr_remez. ;#These function calculates the optimal (in the Chebyshev/minimax sense) FIR filter inpulse ;#reponse given a set of band edges, the desired reponse on those bands, ;#and the weight given to the error in those bands. ;#input_item_type: Type and resolution for input signal samples. Use only gr_complex in this version. InputFilter.input_item_type=float ;#outut_item_type: Type and resolution for output filtered signal samples. Use only gr_complex in this version. InputFilter.output_item_type=gr_complex ;#taps_item_type: Type and resolution for the taps of the filter. Use only float in this version. InputFilter.taps_item_type=float ;#number_of_taps: Number of taps in the filter. Increasing this parameter increases the processing time InputFilter.number_of_taps=5 ;#number_of _bands: Number of frequency bands in the filter. InputFilter.number_of_bands=2 ;#bands: frequency at the band edges [ b1 e1 b2 e2 b3 e3 ...]. ;#Frequency is in the range [0, 1], with 1 being the Nyquist frequency (Fs/2) ;#The number of band_begin and band_end elements must match the number of bands InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 ;#ampl: desired amplitude at the band edges [ a(b1) a(e1) a(b2) a(e2) ...]. ;#The number of ampl_begin and ampl_end elements must match the number of bands InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 ;#band_error: weighting applied to each band (usually 1). ;#The number of band_error elements must match the number of bands InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 ;#filter_type: one of "bandpass", "hilbert" or "differentiator" InputFilter.filter_type=bandpass ;#grid_density: determines how accurately the filter will be constructed. ;The minimum value is 16; higher values are slower to compute the filter. InputFilter.grid_density=16 ;# Original sampling frequency stored in the signal file InputFilter.sampling_frequency=20480000 ;#The following options are used only in Freq_Xlating_Fir_Filter implementation. ;#InputFilter.IF is the intermediate frequency (in Hz) shifted down to zero Hz InputFilter.IF=5499998.47412109 ;# Decimation factor after the frequency tranaslating block InputFilter.decimation_factor=8 ;######### RESAMPLER CONFIG ############ ;## Resamples the input data. ;#implementation: Use [Pass_Through] or [Direct_Resampler] ;#[Pass_Through] disables this block ;#[Direct_Resampler] enables a resampler that implements a nearest neigbourhood interpolation Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ ;#count: Number of available GPS satellite channels. Channels_1C.count=8 Channels.in_acquisition=1 #Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat Acquisition_1C.item_type=gr_complex Acquisition_1C.if=0 Acquisition_1C.sampled_ms=1 Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition ;#use_CFAR_algorithm: If enabled, acquisition estimates the input signal power to implement CFAR detection algorithms ;#notice that this affects the Acquisition threshold range! Acquisition_1C.use_CFAR_algorithm=false; ;#threshold: Acquisition threshold Acquisition_1C.threshold=10 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=100 ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_KF_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.if=0 Tracking_1C.dump=true Tracking_1C.dump_filename=../data/epl_tracking_ch_ Tracking_1C.pll_bw_hz=15.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_1C.decimation_factor=1; ;######### OBSERVABLES CONFIG ############ ;#implementation: Observables.implementation=Hybrid_Observables ;#dump: Enable or disable the Observables internal binary data file logging [true] or [false] Observables.dump=false ;#dump_filename: Log path and filename. Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true conf/gnss-sdr_GPS_L1_nsr_twobit_packed.conf000066400000000000000000000121041352176506000212430ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Sample configuration file for IFEN SX-NSR software receiver front-end ; https://www.ifen.com/products/sx3-gnss-software-receiver/ ; This sample configuration is able to process directly .sream binary files ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2560000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Two_Bit_Packed_File_Signal_Source SignalSource.filename=/datalogger/signals/ifen/E1L1_FE0_Band0.stream ; <- PUT YOUR FILE HERE SignalSource.item_type=byte ; big_endian_items : not needed for byte inputs ; If the input were 'short' then this can either be big endian or little ; endian. If it is big endian then the second byte should be output ; first in each short. ; SignalSource.big_endian_items=false ; big_endian_bytes: true if the most signficiant two bits in the byte ; are the first two to be output. SignalSource.big_endian_bytes=false ; sample_type: one of 'real' 'iq' or 'qi' ; Data is either real or complex. ; if the data is complex there are two conventions for sample ordering: ; 1) Real first : 'iq' ; 2) Imaginary first: 'qi' ; This setting specifies which of the three cases holds for this data file SignalSource.sample_type=real SignalSource.sampling_frequency=20480000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=float InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=20480000 InputFilter.IF=5499998.47412109 InputFilter.decimation_factor=8 ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 Channel.signal=1C ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.sampled_ms=1 Acquisition_1C.threshold=0.0075 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true conf/gnss-sdr_GPS_L1_plutosdr_realtime.conf000066400000000000000000000067511352176506000213130ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [sps]. ;FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ GNSS-SDR.internal_fs_sps=2000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Plutosdr_Signal_Source SignalSource.item_type=gr_complex SignalSource.device_address=192.168.2.1 SignalSource.sampling_frequency=3000000 SignalSource.freq=1575420000 SignalSource.bandwidth=2600000 SignalSource.gain_mode=manual SignalSource.gain=30 SignalSource.samples=0 SignalSource.buffer_size=65000 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=./capture.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner InputFilter.implementation=Pass_Through InputFilter.item_type=gr_complex Resampler.implementation=Direct_Resampler Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=2000000 Resampler.item_type=gr_complex ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=6 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=0.008 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=4.0; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_pulse_blanking_gr_complex.conf000066400000000000000000000067541352176506000227740ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false ;######### SUPL RRLP GPS assistance configuration ##### GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.nokia.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/home/javier/signals/signal_source_int.dat SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=dump.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pulse_Blanking_Filter InputFilter.Pfa=0.001 InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=8 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=20 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.extend_correlation_ms=10 Tracking_1C.pll_bw_hz=35; Tracking_1C.pll_bw_narrow_hz=30; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dll_bw_narrow_hz=1.5; Tracking_1C.fll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=true Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=1 PVT.display_rate_ms=100 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L1_rtl_tcp_realtime.conf000066400000000000000000000116031352176506000210760ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. ;FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ GNSS-SDR.internal_fs_sps=1200000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ ;#implementation SignalSource.implementation=RtlTcp_Signal_Source SignalSource.sampling_frequency=1200000 SignalSource.freq=1575420000 SignalSource.gain=40 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.AGC_enabled = false SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false SignalSource.address=127.0.0.1 SignalSource.port=1234 SignalSource.swap_iq=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 ;#The following options are used only in Freq_Xlating_Fir_Filter implementation. ;#InputFilter.IF is the intermediate frequency (in Hz) shifted down to zero Hz ;FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ InputFilter.sampling_frequency=1200000 InputFilter.IF=80558 ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=4 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms =1 Acquisition_1C.threshold=0.015 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_min=-10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ ;#implementation: Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ ;#implementation: Position Velocity and Time (PVT) implementation: PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=true PVT.dump_filename=./PVT conf/gnss-sdr_GPS_L1_rtlsdr_realtime.conf000066400000000000000000000131141352176506000207400ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. ;FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ GNSS-SDR.internal_fs_sps=1999898 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Osmosdr_Signal_Source SignalSource.item_type=gr_complex ; FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ SignalSource.sampling_frequency=2000000 SignalSource.freq=1575420000 SignalSource.gain=40 SignalSource.rf_gain=40 SignalSource.if_gain=30 SignalSource.AGC_enabled = false SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;# Please note that the new RTL-SDR Blog V3 dongles ship a < 1 PPM ;# temperature compensated oscillator (TCXO), which is well suited for GNSS ;# signal processing, and a 4.5 V powered bias-tee to feed an active antenna. ;# Whether the bias-tee is turned off before reception depends on which version ;# of gr-osmosdr was used when compiling GNSS-SDR. With an old version ;# (for example, v0.1.4-8), the utility rtl_biast may be used to switch the ;# bias-tee, and then call gnss-sdr. ;# See https://github.com/rtlsdrblog/rtl_biast ;# After reception the bias-tee is switched off automatically by the program. ;# With newer versions of gr-osmosdr (>= 0.1.4-13), the bias-tee can be ;# activated by uncommenting the following line: ;SignalSource.osmosdr_args=rtl,bias=1 ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 ;FOR USE GNSS-SDR WITH RTLSDR DONGLES USER MUST SET THE CALIBRATED SAMPLE RATE HERE ; i.e. using front-end-cal as reported here:http://www.cttc.es/publication/turning-a-television-into-a-gnss-receiver/ InputFilter.sampling_frequency=1999898 InputFilter.IF=80558 ; IF deviation due to front-end LO inaccuracies [Hz] ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=4 Channels.in_acquisition=1 Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.015 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_min=-10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.dump=false PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 conf/gnss-sdr_GPS_L1_two_bits_cpx.conf000066400000000000000000000104271352176506000202540ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=3200000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=false GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Two_Bit_Cpx_File_Signal_Source SignalSource.filename=/datalogger/captures/ajith/test1_two_cpx_live.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=19200000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=gr_complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=19200000 InputFilter.IF=4024000 InputFilter.decimation_factor=6 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=6 Channels.in_acquisition=1 Channel.signal=1C ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition_Fine_Doppler Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.007 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_min=-10000 Acquisition_1C.doppler_step=500 Acquisition_1C.max_dwells=15 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=1.5; Tracking_1C.order=3; Tracking_1C.dump=true Tracking_1C.dump_filename=./tracking_ch ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L2C_USRP1_realtime.conf000066400000000000000000000110371352176506000204060ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Configuration file for using USRP1 as a RF front-end for GPS L2C signals ; Run: ; gnss-sdr --config_file=/path/to/gnss-sdr_GPS_L2C_USRP1_realtime.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=2000000 SignalSource.freq=1227600000 SignalSource.gain=60 SignalSource.subdevice=A:0 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=20000000 InputFilter.IF=-1600000 InputFilter.decimation_factor=1 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex Resampler.sample_freq_in=2000000 Resampler.sample_freq_out=2000000 ;######### CHANNELS GLOBAL CONFIG ############ Channels_2S.count=1 Channels.in_acquisition=1 Channel.signal=2S Channel0.signal=2S Channel1.signal=2S Channel2.signal=2S Channel3.signal=2S Channel4.signal=2S Channel5.signal=2S Channel6.signal=2S Channel7.signal=2S ;Channel8.signal=2S ;Channel9.signal=2S ;Channel10.signal=2S ;Channel11.signal=2S ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0013 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=10000 Acquisition_2S.doppler_step=100 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=1.5; Tracking_2S.dll_bw_hz=0.3; Tracking_2S.order=3; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=true Tracking_2S.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false ;######### OBSERVABLES CONFIG ############. Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false conf/gnss-sdr_GPS_L2C_USRP_X300_realtime.conf000066400000000000000000000113131352176506000210340ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Configuration file for using USRP X300 as a RF front-end for GPS L2C signals ; Set SignalSource.device_address to the IP address of your device ; and run: ; gnss-sdr --config_file=/path/to/gnss-sdr_GPS_L2C_USRP_X300_realtime.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.device_address=192.168.50.2 ; <- PUT THE IP ADDRESS OF YOUR USRP HERE SignalSource.item_type=cshort SignalSource.sampling_frequency=4000000 SignalSource.freq=1227600000 ;### Options: internal, external, or MIMO SignalSource.clock_source=internal SignalSource.gain=35 SignalSource.subdevice=A:0 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=cshort ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Fir_Filter InputFilter.input_item_type=cshort InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=11 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.48 InputFilter.band2_begin=0.52 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=4000000 InputFilter.IF=0 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=4000000 ;######### CHANNELS GLOBAL CONFIG ############ Channels_2S.count=1 Channels.in_acquisition=1 Channel.signal=2S Channel0.signal=2S Channel1.signal=2S Channel2.signal=2S Channel3.signal=2S Channel4.signal=2S Channel5.signal=2S Channel6.signal=2S Channel7.signal=2S ;Channel8.signal=2S ;Channel9.signal=2S ;Channel10.signal=2S ;Channel11.signal=2S ;######### ACQUISITION GLOBAL CONFIG ############ ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0015 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.25; Tracking_2S.order=2; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=true Tracking_2S.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=true ;######### OBSERVABLES CONFIG ############. Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Galileo_E1_USRP_X300_realtime.conf000066400000000000000000000056341352176506000216550ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; Configuration file for using USRP X300 as a RF front-end for Galileo E1 signals. ; Set SignalSource.device_address to the IP address of your device ; and run: ; gnss-sdr --config_file=/path/to/gnss-sdr_Galileo_E1_USRP_X300_realtime.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.item_type=gr_complex SignalSource.device_address=192.168.40.2 ; <- PUT THE IP ADDRESS OF YOUR USRP HERE SignalSource.sampling_frequency=4000000 SignalSource.freq=1575420000 SignalSource.gain=50 SignalSource.subdevice=A:0 SignalSource.samples=0 SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ###### Channels_1B.count=4 Channels.in_acquisition=1 Channel.signal=1B ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=1 Acquisition_1B.pfa=0.000008 Acquisition_1B.doppler_max=6000 Acquisition_1B.doppler_step=250 Acquisition_1B.cboc=false Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Galileo_E1_acq_QuickSync.conf000066400000000000000000000063441352176506000212240ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ishort_To_Complex DataTypeAdapter.dump=false DataTypeAdapter.dump_filename=../data/data_type_adapter.dat ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat Resampler.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ Channels_1B.count=4 Channels.in_acquisition=1 Channel.signal=1B ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 Acquisition_1B.threshold=0.05 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.coherent_integration_time_ms=8 Acquisition_1B.cboc=false Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.dump=false PVT.dump_filename=./PVT PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=true; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false; PVT.flag_rtcm_tty_port=false; PVT.rtcm_dump_devname=/dev/pts/1 conf/gnss-sdr_Galileo_E1_ishort.conf000066400000000000000000000066371352176506000200040ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=true ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ishort_To_Complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.sample_freq_in=4000000 Resampler.sample_freq_out=4000000 Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1B.count=8 Channels.in_acquisition=1 Channel.signal=1B ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.00001 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.cboc=false Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat Acquisition_1B.blocking=false ;######### TRACKING GLOBAL CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.dump=true Tracking_1B.dump_filename=./veml_tracking_ch_ Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=3.0; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.track_pilot=true Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true; PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1045_rate_ms=5000 PVT.rtcm_MSM_rate_ms=1000 PVT.flag_rtcm_tty_port=false; PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Galileo_E1_nsr.conf000066400000000000000000000077631352176506000172770ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. ;GNSS-SDR.internal_fs_sps=6826700 GNSS-SDR.internal_fs_sps=2560000 ;GNSS-SDR.internal_fs_sps=4096000 ;GNSS-SDR.internal_fs_sps=5120000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Nsr_File_Signal_Source SignalSource.filename=/datalogger/signals/ifen/E1L1_FE0_Band0.stream ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=20480000 SignalSource.samples=0 ; 0 means the entire file SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=float InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=20480000 InputFilter.IF=5499998.47412109 InputFilter.decimation_factor=8 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1B.count=8 Channels.in_acquisition=1 Channel.signal=1B ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 Acquisition_1B.pfa=0.0000008 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.cboc=false ; This option allows you to choose between acquiring with CBOC signal [true] or sinboc(1,1) signal [false]. Use only if GNSS-SDR.internal_fs_sps is greater than or equal to 6138000 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.dump=true PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 conf/gnss-sdr_Galileo_E5a.conf000066400000000000000000000073351352176506000165550ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=32000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en ;GNSS-SDR.SUPL_gps_enabled=false ;GNSS-SDR.SUPL_read_gps_assistance_xml=false ;GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com ;GNSS-SDR.SUPL_gps_ephemeris_port=7275 ;GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com ;GNSS-SDR.SUPL_gps_acquisition_port=7275 ;GNSS-SDR.SUPL_MCC=244 ;GNSS-SDR.SUPL_MNC=5 ;GNSS-SDR.SUPL_LAC=0x59e2 ;GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/ifen/32MS_complex.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.sampling_frequency=32000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_5X.count=1 Channels.in_acquisition=1 ;######### SPECIFIC CHANNELS CONFIG ###### ;#The following options are specific to each channel and overwrite the generic options ;######### CHANNEL 0 CONFIG ############ ;Channel0.satellite=19 ;######### CHANNEL 1 CONFIG ############ ;Channel1.satellite=12 ;######### CHANNEL 2 CONFIG ############ ;Channel2.satellite=11 ;######### CHANNEL 3 CONFIG ############ ;Channel3.system=Galileo ;Channel3.signal=5Q ;Channel3.satellite=20 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_5X.implementation=Galileo_E5a_Noncoherent_IQ_Acquisition_CAF Acquisition_5X.item_type=gr_complex Acquisition_5X.coherent_integration_time_ms=1 Acquisition_5X.threshold=0.001 Acquisition_5X.pfa=0.0003 Acquisition_5X.doppler_max=10000 Acquisition_5X.doppler_step=250 Acquisition_5X.bit_transition_flag=false Acquisition_5X.max_dwells=1 Acquisition_5X.CAF_window_hz=0 Acquisition_5X.Zero_padding=0 Acquisition_5X.dump=false Acquisition_5X.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking Tracking_5X.item_type=gr_complex Tracking_5X.pll_bw_hz=20.0; Tracking_5X.dll_bw_hz=20.0; Tracking_5X.pll_bw_narrow_hz=2.0; Tracking_5X.dll_bw_narrow_hz=5.0; Tracking_5X.order=2; Tracking_5X.early_late_space_chips=0.5; Tracking_5X.dump=false Tracking_5X.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder TelemetryDecoder_5X.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=true; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Galileo_E5a_IFEN_CTTC.conf000066400000000000000000000123501352176506000201640ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=50000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en ;GNSS-SDR.SUPL_gps_enabled=false ;GNSS-SDR.SUPL_read_gps_assistance_xml=false ;GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com ;GNSS-SDR.SUPL_gps_ephemeris_port=7275 ;GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com ;GNSS-SDR.SUPL_gps_acquisition_port=7275 ;GNSS-SDR.SUPL_MCC=244 ;GNSS-SDR.SUPL_MNC=5 ;GNSS-SDR.SUPL_LAC=0x59e2 ;GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/ifen/Galileo_E5ab_IFEN_CTTC_run1.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.sampling_frequency=50000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=50000000 InputFilter.IF=-15345000 InputFilter.decimation_factor=1 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_5X.count=8 Channels.in_acquisition=1 Channel.signal=5X ;######### SPECIFIC CHANNELS CONFIG ###### ;#The following options are specific to each channel and overwrite the generic options ;######### CHANNEL 0 CONFIG ############ Channel0.signal=5X ;Channel0.satellite=19 ;Channel0.repeat_satellite=true ;######### CHANNEL 1 CONFIG ############ Channel1.signal=5X ;Channel1.satellite=12 ;######### CHANNEL 2 CONFIG ############ Channel2.signal=5X ;Channel2.satellite=11 ;######### CHANNEL 3 CONFIG ############ Channel3.signal=5X ;Channel3.satellite=20 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_5X.implementation=Galileo_E5a_Noncoherent_IQ_Acquisition_CAF Acquisition_5X.item_type=gr_complex Acquisition_5X.coherent_integration_time_ms=1 Acquisition_5X.threshold=0.002 Acquisition_5X.doppler_max=10000 Acquisition_5X.doppler_step=250 Acquisition_5X.bit_transition_flag=false Acquisition_5X.max_dwells=1 Acquisition_5X.CAF_window_hz=0 ; **Only for E5a** Resolves doppler ambiguity averaging the specified BW in the winner code delay. If set to 0 CAF filter is desactivated. Recommended value 3000 Hz Acquisition_5X.Zero_padding=0 ; **Only for E5a** Avoids power loss and doppler ambiguity in bit transitions by correlating one code with twice the input data length, ensuring that at least one full code is present without transitions. If set to 1 it is ON, if set to 0 it is OFF. Acquisition_5X.dump=false Acquisition_5X.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking Tracking_5X.item_type=gr_complex Tracking_5X.pll_bw_hz=20.0; Tracking_5X.dll_bw_hz=20.0; Tracking_5X.pll_bw_narrow_hz=20.0; Tracking_5X.dll_bw_narrow_hz=20.0; Tracking_5X.order=2; Tracking_5X.early_late_space_chips=0.5; Tracking_5X.dump=false Tracking_5X.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder TelemetryDecoder_5X.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=OFF ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=OFF ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.dump=false PVT.dump_filename=./PVT PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea PVT.flag_nmea_tty_port=true PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 conf/gnss-sdr_Hybrid_byte.conf000066400000000000000000000112421352176506000167430ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=20000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/media/javier/Extreme 500/fraunhofer/L125_III1b_210s_L1.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=20000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ibyte_To_Complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.sample_freq_in=20000000 Resampler.sample_freq_out=20000000 Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=10 Channels_1B.count=10 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C Channel8.signal=1B Channel9.signal=1B Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B Channel16.signal=1B Channel17.signal=1B Channel18.signal=1B Channel19.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=18 Acquisition_1C.use_CFAR_algorithm=false Acquisition_1C.blocking=true Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.threshold=25 Acquisition_1B.use_CFAR_algorithm=false Acquisition_1B.blocking=true Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.extend_correlation_ms=1 Tracking_1C.pll_bw_hz=40; Tracking_1C.pll_bw_narrow_hz=30; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dll_bw_narrow_hz=1.5; Tracking_1C.order=2; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=3.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ ;#implementation: Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10; PVT.display_rate_ms=500; PVT.elevation_mask=15; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Hybrid_byte_sim.conf000066400000000000000000000104141352176506000176130ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2600000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/Users/carlesfernandez/git/cttc/build/signal_out.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ibyte_To_Complex DataTypeAdapter.dump=false DataTypeAdapter.dump_filename=../data/DataTypeAdapter.dat ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type = gr_complex; ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=11 Channels_1B.count=0 Channels.in_acquisition=1 ;#signal: Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C Channel8.signal=1C Channel9.signal=1C Channel10.signal=1C Channel11.signal=1C Channel12.signal=1C Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=15 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=6000 Acquisition_1C.doppler_step=100 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000008 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=20.0; Tracking_1C.dll_bw_hz=1.5; Tracking_1C.order=3; ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Hybrid_gr_complex.conf000066400000000000000000000076001352176506000201420ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4092000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/sim/GPS_sim1.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.sampling_frequency=4092000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=1 Channels_1B.count=0 Channels.in_acquisition=1 ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1B Channel2.signal=1B Channel3.signal=1B Channel4.signal=1B Channel5.signal=1B Channel6.signal=1B Channel7.signal=1B Channel8.signal=1B Channel9.signal=1B Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=30 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=100 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000002 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.extend_correlation_ms=10 Tracking_1C.pll_bw_hz=40; Tracking_1C.pll_bw_narrow_hz=25; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dll_bw_narrow_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=true Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.fll_bw_hz=10.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10; PVT.display_rate_ms=500; PVT.dump=false PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump_filename=./PVT conf/gnss-sdr_Hybrid_ishort.conf000066400000000000000000000120671352176506000173160ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ ;#implementation SignalSource.implementation=File_Signal_Source SignalSource.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource.item_type=ishort SignalSource.sampling_frequency=4000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ishort_To_Complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through Resampler.item_type=gr_complex Resampler.dump=false Resampler.dump_filename=../data/resampler.dat ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=0 Channels_1B.count=5 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "5X" GALILEO E5a I+Q Channel0.signal=1B Channel1.signal=1B Channel2.signal=1B Channel3.signal=1B Channel4.signal=1B Channel5.signal=1B Channel6.signal=1B Channel7.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.0075 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000008; 0.0000008 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.cboc=false; Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=50.0; Tracking_1C.dll_bw_hz=5.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.rtcm_tcp_port=2101 PVT.rtcm_MT1045_rate_ms=5000 ; Period (in ms) of Galileo ephemeris messages. 0 mutes this message PVT.rtcm_MT1045_rate_ms=5000 ; Period (in ms) of GPS ephemeris messages. 0 mutes this message PVT.rtcm_MT1097_rate_ms=1000 ; Period (in ms) of Galileo observables. 0 mutes this message PVT.rtcm_MT1077_rate_ms=1000 ; Period (in ms) of GPS observables. 0 mutes this message PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_Hybrid_nsr.conf000066400000000000000000000121661352176506000166100ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2560000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Nsr_File_Signal_Source SignalSource.filename=/home/javier/signals/ifen/E1L1_FE0_Band0.stream ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=20480000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=float ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.input_item_type=float InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=20480000 InputFilter.IF=5499998.47412109 InputFilter.decimation_factor=8 InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=10 Channels_1B.count=10 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C Channel8.signal=1C Channel9.signal=1C Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B Channel16.signal=1B Channel17signal=1B Channel18.signal=1B Channel19.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.threshold=25 Acquisition_1C.use_CFAR_algorithm=false Acquisition_1C.blocking=true Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.threshold=25 Acquisition_1B.use_CFAR_algorithm=false Acquisition_1B.blocking=true Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=250 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.extend_correlation_ms=1 Tracking_1C.pll_bw_hz=40; Tracking_1C.pll_bw_narrow_hz=30; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dll_bw_narrow_hz=1.5; Tracking_1C.order=2; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=20.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ ;#implementation: Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10; PVT.display_rate_ms=500; PVT.elevation_mask=20; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_galileo_E1_extended_correlator_byte.conf000066400000000000000000000103741352176506000236040ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=20000000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/media/javier/SISTEMA/signals/fraunhofer/L125_III1b_210s_L1.bin ; <- PUT YOUR FILE HERE SignalSource.item_type=byte SignalSource.sampling_frequency=20000000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Ibyte_To_Complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Pass_Through ;######### RESAMPLER CONFIG ############ Resampler.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=0 Channels_1B.count=8 Channels.in_acquisition=1 Channel1.signal=1B Channel2.signal=1B Channel3.signal=1B Channel4.signal=1B Channel5.signal=1B Channel6.signal=1B Channel7.signal=1B Channel8.signal=1B Channel9.signal=1B Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.scoherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=18 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 Acquisition_1B.acquire_pilot=true Acquisition_1B.use_CFAR_algorithm=false Acquisition_1B.threshold=21 Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.bit_transition_flag=true Acquisition_1B.dump=false Acquisition_1B.dump_filename=../data/acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=30.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.track_pilot=true Tracking_1B.pll_bw_hz=4.0; Tracking_1B.dll_bw_hz=0.5; Tracking_1B.pll_bw_narrow_hz=2.0; Tracking_1B.dll_bw_narrow_hz=0.25; Tracking_1B.extend_correlation_symbols=4; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.early_late_space_narrow_chips=0.06; Tracking_1B.very_early_late_space_narrow_chips=0.25; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_galileo_E1_extended_correlator_labsat.conf000066400000000000000000000123221352176506000241020ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=5456000 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Labsat_Signal_Source SignalSource.selected_channel=1 ;#filename: path to file with the captured GNSS signal samples to be processed ;# Labsat sile source automatically increments the file name when the signal is split in several files ;# the adapter adds "_0000.LS3" to this base path and filename. Next file will be "_0001.LS3" and so on ;# in this example, the first file complete path will be ../signals/GPS_025_0000.LS3 SignalSource.filename=../signals/GPS_025 ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.sampling_frequency=16368000 SignalSource.samples=0 SignalSource.repeat=false SignalSource.dump=false SignalSource.dump_filename=../data/signal_source.dat SignalSource.enable_throttle_control=false ;######### SIGNAL_CONDITIONER CONFIG ############ SignalConditioner.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ DataTypeAdapter.implementation=Pass_Through DataTypeAdapter.item_type=gr_complex ;######### INPUT_FILTER CONFIG ############ InputFilter.implementation=Freq_Xlating_Fir_Filter InputFilter.dump=false InputFilter.dump_filename=../data/input_filter.dat InputFilter.input_item_type=gr_complex InputFilter.output_item_type=gr_complex InputFilter.taps_item_type=float InputFilter.number_of_taps=5 InputFilter.number_of_bands=2 InputFilter.band1_begin=0.0 InputFilter.band1_end=0.45 InputFilter.band2_begin=0.55 InputFilter.band2_end=1.0 InputFilter.ampl1_begin=1.0 InputFilter.ampl1_end=1.0 InputFilter.ampl2_begin=0.0 InputFilter.ampl2_end=0.0 InputFilter.band1_error=1.0 InputFilter.band2_error=1.0 InputFilter.filter_type=bandpass InputFilter.grid_density=16 InputFilter.sampling_frequency=16368000 InputFilter.IF=0 InputFilter.decimation_factor=3 ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=0 Channels_1B.count=6 Channels.in_acquisition=1 Channel0.signal=1B Channel1.signal=1B Channel2.signal=1B Channel3.signal=1B Channel4.signal=1B Channel5.signal=1B Channel6.signal=1B Channel7.signal=1B Channel8.signal=1B Channel9.signal=1B Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=22 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 Acquisition_1B.acquire_pilot=true Acquisition_1B.use_CFAR_algorithm=false Acquisition_1B.threshold=22 Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.bit_transition_flag=true Acquisition_1B.dump=false Acquisition_1B.dump_filename=../data/acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.track_pilot=true Tracking_1B.pll_bw_hz=7.5; Tracking_1B.dll_bw_hz=0.5; Tracking_1B.pll_bw_narrow_hz=2.5; Tracking_1B.dll_bw_narrow_hz=0.25; Tracking_1B.extend_correlation_symbols=4; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.early_late_space_narrow_chips=0.15; Tracking_1B.very_early_late_space_narrow_chips=0.30; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_Flexiband_bin_file_III_1a.conf000066400000000000000000000134331352176506000251110ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.flag_read_file=true SignalSource.signal_file=/datalogger/signals/Fraunhofer/L125_III1b_210s.usb ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20000000 InputFilter0.IF=0; InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 ;#signal: Channel.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.use_CFAR_algorithm=false; Acquisition_1C.threshold=15 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.extend_correlation_ms=10 Tracking_1C.pll_bw_hz=40.0; Tracking_1C.pll_bw_narrow_hz=35; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.dll_bw_narrow_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=true Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_Flexiband_realtime_III_1a.conf000066400000000000000000000133161352176506000251440ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1a.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20000000 InputFilter0.IF=-205000 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 ;#signal: ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.012 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_Flexiband_realtime_III_1b.conf000066400000000000000000000132741352176506000251500ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 ;#frontend channels AGC SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.IF=-205000 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 ;#signal: ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.012 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_Flexiband_realtime_II_3b.conf000066400000000000000000000134441352176506000250400ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_II-3b.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=40000000 InputFilter0.IF=-205000 InputFilter0.decimation_factor=16 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 ;#signal: Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.012 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_Flexiband_realtime_I_1b.conf000066400000000000000000000131001352176506000247120ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=5000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_I-1b.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=40000000 InputFilter0.IF=-205000 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=4 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 ;Channel4.RF_channel_ID=0 ;Channel5.RF_channel_ID=0 ;Channel6.RF_channel_ID=0 ;Channel7.RF_channel_ID=0 ;#signal: Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.011 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_L2_Flexiband_realtime_III_1b.conf000066400000000000000000000203001352176506000254710ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=2 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;###################################################### ;######### RF CHANNEL 0 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20000000 InputFilter0.IF=-205000 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 1 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter_ch1.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=20000000 InputFilter1.IF=100000 InputFilter1.decimation_factor=8 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels_2S.count=8 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel0.signal=1C Channel1.RF_channel_ID=0 Channel1.signal=1C Channel2.RF_channel_ID=0 Channel2.signal=1C Channel3.RF_channel_ID=0 Channel3.signal=1C Channel4.RF_channel_ID=0 Channel4.signal=1C Channel5.RF_channel_ID=0 Channel5.signal=1C Channel6.RF_channel_ID=0 Channel6.signal=1C Channel7.RF_channel_ID=0 Channel7.signal=1C Channel8.RF_channel_ID=1 Channel8.signal=2S Channel9.RF_channel_ID=1 Channel9.signal=2S Channel10.RF_channel_ID=1 Channel10.signal=2S Channel11.RF_channel_ID=1 Channel11.signal=2S Channel12.RF_channel_ID=1 Channel12.signal=2S Channel13.RF_channel_ID=1 Channel13.signal=2S Channel14.RF_channel_ID=1 Channel14.signal=2S Channel15.RF_channel_ID=1 Channel15.signal=2S Channel8.RF_channel_ID=1 Channel8.signal=2S Channel9.RF_channel_ID=1 Channel9.signal=2S Channel10.RF_channel_ID=1 Channel10.signal=2S Channel11.RF_channel_ID=1 Channel11.signal=2S Channel12.RF_channel_ID=1 Channel12.signal=2S Channel13.RF_channel_ID=1 Channel13.signal=2S Channel14.RF_channel_ID=1 Channel14.signal=2S Channel15.RF_channel_ID=1 Channel15.signal=2S ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.008 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=true Tracking_1C.dump_filename=./tracking_ch_ ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0005 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=30 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=1.5; Tracking_2S.dll_bw_hz=0.3; Tracking_2S.order=3; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=true Tracking_2S.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS L1 CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GPS L2 CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.flag_averaging=true PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_L2_Galileo_E1B_Flexiband_bin_file_III_1b.conf000066400000000000000000000207361352176506000275360ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=2500000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.nokia.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.flag_read_file=true SignalSource.signal_file=/datalogger/signals/Fraunhofer/L125_III1b_210s.usb ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=2 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;###################################################### ;######### RF CHANNEL 0 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20000000 InputFilter0.IF=0 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 1 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter_ch1.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=20000000 InputFilter1.IF=0 InputFilter1.decimation_factor=8 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############. Channels_1C.count=2 Channels_1B.count=4 Channels_2S.count=4 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=1 Channel3.RF_channel_ID=1 Channel4.RF_channel_ID=1 Channel5.RF_channel_ID=1 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=0 Channel9.RF_channel_ID=0 Channel10.RF_channel_ID=1 Channel11.RF_channel_ID=1 Channel12.RF_channel_ID=1 Channel13.RF_channel_ID=1 Channel14.RF_channel_ID=1 Channel15.RF_channel_ID=1 ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.008 ;Acquisition_1C.pfa=0.0001 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=1.5; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.0005 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=30 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=1.5; Tracking_2S.dll_bw_hz=0.3; Tracking_2S.order=3; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=true Tracking_2S.dump_filename=../data/epl_tracking_ch_ ;# GALILEO E1B Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000005 Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=./veml_tracking_ch_ ;######### TELEMETRY DECODER GPS L1 CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GPS L2 CONFIG ############ TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false ;######### TELEMETRY DECODER GALILEO E1B CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=100 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L1_USRP_X300_realtime.conf000066400000000000000000000111141352176506000234720ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_1C_enabled=false GNSS-SDR.SUPL_read_1C_assistance_xml=true GNSS-SDR.SUPL_1C_ephemeris_server=supl.google.com GNSS-SDR.SUPL_1C_ephemeris_port=7275 GNSS-SDR.SUPL_1C_acquisition_server=supl.google.com GNSS-SDR.SUPL_1C_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ ;#implementation SignalSource.implementation=UHD_Signal_Source SignalSource.device_address=192.168.40.2 ; <- PUT THE IP ADDRESS OF YOUR USRP HERE SignalSource.item_type=gr_complex SignalSource.RF_channels=2 SignalSource.sampling_frequency=4000000 SignalSource.subdevice=A:0 B:0 ;######### RF Channels specific settings ###### ;## RF CHANNEL 0 ## SignalSource.freq0=1575420000 SignalSource.gain0=50 SignalSource.samples0=0 ;## RF CHANNEL 1 ## SignalSource.freq1=1575420000 SignalSource.gain1=50 SignalSource.samples1=0 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Pass_Through InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Pass_Through ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=4 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=1 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=1 ;#signal: Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C ;######### ACQUISITION GLOBAL CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.01 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=8000 Acquisition_1C.doppler_step=500 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### TRACKING GLOBAL CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=true PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L2_M_Flexiband_bin_file_III_1b.conf000066400000000000000000000267641352176506000254020ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=5000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.flag_read_file=true SignalSource.signal_file=/media/javier/SISTEMA/signals/fraunhofer/L125_III1b_210s.usb ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=1 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;###################################################### ;######### RF CHANNEL 0 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter_ch0.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20000000 InputFilter0.IF=0 InputFilter0.decimation_factor=4 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 1 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter_ch1.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=20000000 InputFilter1.IF=0 InputFilter1.decimation_factor=4 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 2 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Freq_Xlating_Fir_Filter InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter_ch2.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex InputFilter2.taps_item_type=float InputFilter2.number_of_taps=5 InputFilter2.number_of_bands=2 InputFilter2.band1_begin=0.0 InputFilter2.band1_end=0.45 InputFilter2.band2_begin=0.55 InputFilter2.band2_end=1.0 InputFilter2.ampl1_begin=1.0 InputFilter2.ampl1_end=1.0 InputFilter2.ampl2_begin=0.0 InputFilter2.ampl2_end=0.0 InputFilter2.band1_error=1.0 InputFilter2.band2_error=1.0 InputFilter2.filter_type=bandpass InputFilter2.grid_density=16 InputFilter2.sampling_frequency=40000000 InputFilter2.IF=0 InputFilter2.decimation_factor=8 ;######### RESAMPLER CONFIG 1 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=0 Channels_1B.count=10 Channels_2S.count=0 Channels_5X.count=0 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL NUMBERING ORDER: GPS L1 C/A, GPS L2 L2C (M), GALILEO E1 B, GALILEO E5a ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=0 Channel9.RF_channel_ID=0 Channel10.RF_channel_ID=0 Channel11.RF_channel_ID=0 Channel12.RF_channel_ID=0 Channel13.RF_channel_ID=0 Channel14.RF_channel_ID=0 Channel15.RF_channel_ID=0 Channel16.RF_channel_ID=0 Channel17.RF_channel_ID=0 Channel18.RF_channel_ID=0 Channel19.RF_channel_ID=0 Channel20.RF_channel_ID=0 Channel21.RF_channel_ID=0 Channel22.RF_channel_ID=0 Channel23.RF_channel_ID=0 Channel24.RF_channel_ID=0 Channel25.RF_channel_ID=0 Channel26.RF_channel_ID=0 Channel27.RF_channel_ID=0 Channel28.RF_channel_ID=0 Channel29.RF_channel_ID=0 Channel30.RF_channel_ID=2 Channel31.RF_channel_ID=2 Channel32.RF_channel_ID=2 Channel33.RF_channel_ID=2 Channel34.RF_channel_ID=2 Channel35.RF_channel_ID=2 Channel36.RF_channel_ID=2 Channel37.RF_channel_ID=2 Channel38.RF_channel_ID=2 Channel39.RF_channel_ID=2 ;######### ACQUISITION CONFIG ###### ;# GPS L1 CA Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;# Galileo E1 Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000002 Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.00074 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat ;# GALILEO E5a Acquisition_5X.implementation=Galileo_E5a_Noncoherent_IQ_Acquisition_CAF Acquisition_5X.item_type=gr_complex Acquisition_5X.coherent_integration_time_ms=1 Acquisition_5X.threshold=0.009 Acquisition_5X.doppler_max=5000 Acquisition_5X.doppler_step=125 Acquisition_5X.bit_transition_flag=false Acquisition_5X.max_dwells=1 Acquisition_5X.CAF_window_hz=0 ; **Only for E5a** Resolves doppler ambiguity averaging the specified BW in the winner code delay. If set to 0 CAF filter is desactivated. Recommended value 3000 Hz Acquisition_5X.Zero_padding=0 ; **Only for E5a** Avoids power loss and doppler ambiguity in bit transitions by correlating one code with twice the input data length, ensuring that at least one full code is present without transitions. If set to 1 it is ON, if set to 0 it is OFF. Acquisition_5X.dump=false Acquisition_5X.dump_filename=./acq_dump.dat ;######### TRACKING CONFIG ############ ;######### GPS L1 C/A GENERIC TRACKING CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### GALILEO E1 TRK CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### GPS L2C GENERIC TRACKING CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.25; Tracking_2S.order=2; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=false Tracking_2S.dump_filename=./tracking_ch_ ;######### GALILEO E5 TRK CONFIG ############ Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking Tracking_5X.item_type=gr_complex Tracking_5X.pll_bw_hz_init=20.0; **Only for E5a** PLL loop filter bandwidth during initialization [Hz] Tracking_5X.dll_bw_hz_init=20.0; **Only for E5a** DLL loop filter bandwidth during initialization [Hz] Tracking_5X.ti_ms=1; **Only for E5a** loop filter integration time after initialization (secondary code delay search)[ms] Tracking_5X.pll_bw_hz=20.0; Tracking_5X.dll_bw_hz=20.0; Tracking_5X.order=2; Tracking_5X.early_late_space_chips=0.5; Tracking_5X.dump=false Tracking_5X.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder TelemetryDecoder_5X.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=100 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_GPS_L2_M_Flexiband_bin_file_III_1b_real.conf000066400000000000000000000174061352176506000263760ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=5000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.flag_read_file=true SignalSource.signal_file=/home/javier/signals/20140923_20-24-17_L125_roof_210s.usb ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=2 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;###################################################### ;######### RF CHANNEL 0 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter_ch0.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.IF=0 InputFilter0.decimation_factor=4 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 1 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter_ch1.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=20000000 InputFilter1.IF=0 InputFilter1.decimation_factor=4 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Pass_Through ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Pass_Through InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=10 Channels_2S.count=4 ;#GPS.prns=7,8 Channels.in_acquisition=1 ;#signal: ;# "1C" GPS L1 C/A ;# "1B" GALILEO E1 B (I/NAV OS/CS/SoL) ;# "1G" GLONASS L1 C/A ;# "2S" GPS L2 L2C (M) ;# "5X" GALILEO E5a I+Q ;# "L5" GPS L5 ;# CHANNEL NUMBERING ORDER: GPS L1 C/A, GPS L2 L2C (M), GALILEO E1 B, GALILEO E5a ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=0 Channel9.RF_channel_ID=0 Channel10.RF_channel_ID=1 Channel11.RF_channel_ID=1 Channel12.RF_channel_ID=1 Channel13.RF_channel_ID=1 Channel14.RF_channel_ID=1 Channel15.RF_channel_ID=1 Channel16.RF_channel_ID=1 Channel17.RF_channel_ID=1 Channel18.RF_channel_ID=1 Channel19.RF_channel_ID=1 Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.00074 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat ;######### TRACKING CONFIG ############ ;######### GPS L1 C/A GENERIC TRACKING CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=40.0; Tracking_1C.dll_bw_hz=3.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### GPS L2C GENERIC TRACKING CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.25; Tracking_2S.order=2; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=false Tracking_2S.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=true Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=100 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multichannel_all_in_one_Flexiband_bin_file_III_1b.conf000066400000000000000000000274151352176506000262310ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=5000000 ;######### SUPL RRLP GPS assistance configuration ##### ; Check http://www.mcc-mnc.com/ ; On Android: https://play.google.com/store/apps/details?id=net.its_here.cellidinfo&hl=en GNSS-SDR.SUPL_gps_enabled=false GNSS-SDR.SUPL_read_gps_assistance_xml=true GNSS-SDR.SUPL_gps_ephemeris_server=supl.google.com GNSS-SDR.SUPL_gps_ephemeris_port=7275 GNSS-SDR.SUPL_gps_acquisition_server=supl.google.com GNSS-SDR.SUPL_gps_acquisition_port=7275 GNSS-SDR.SUPL_MCC=244 GNSS-SDR.SUPL_MNC=5 GNSS-SDR.SUPL_LAC=0x59e2 GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=Flexiband_Signal_Source SignalSource.flag_read_file=true SignalSource.signal_file=/media/javier/SISTEMA/signals/fraunhofer/L125_III1b_210s.usb ; <- PUT YOUR FILE HERE SignalSource.item_type=gr_complex SignalSource.firmware_file=flexiband_III-1b.bit SignalSource.RF_channels=3 ;#frontend channels gain. Not usable yet! SignalSource.gain1=0 SignalSource.gain2=0 SignalSource.gain3=0 SignalSource.AGC=true SignalSource.usb_packet_buffer=128 ;###################################################### ;######### RF CHANNEL 0 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=gr_complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter_ch0.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.IF=0 InputFilter0.decimation_factor=4 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 1 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=gr_complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter_ch1.dat InputFilter1.input_item_type=gr_complex InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.IF=0 InputFilter1.decimation_factor=4 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;###################################################### ;######### RF CHANNEL 2 SIGNAL CONDITIONER ############ ;###################################################### ;######### SIGNAL_CONDITIONER 2 CONFIG ############ SignalConditioner2.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 2 CONFIG ############ DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=gr_complex ;######### INPUT_FILTER 2 CONFIG ############ InputFilter2.implementation=Freq_Xlating_Fir_Filter InputFilter2.dump=false InputFilter2.dump_filename=../data/input_filter_ch2.dat InputFilter2.input_item_type=gr_complex InputFilter2.output_item_type=gr_complex InputFilter2.taps_item_type=float InputFilter2.number_of_taps=5 InputFilter2.number_of_bands=2 InputFilter2.band1_begin=0.0 InputFilter2.band1_end=0.45 InputFilter2.band2_begin=0.55 InputFilter2.band2_end=1.0 InputFilter2.ampl1_begin=1.0 InputFilter2.ampl1_end=1.0 InputFilter2.ampl2_begin=0.0 InputFilter2.ampl2_end=0.0 InputFilter2.band1_error=1.0 InputFilter2.band2_error=1.0 InputFilter2.filter_type=bandpass InputFilter2.grid_density=16 InputFilter2.IF=0 InputFilter2.decimation_factor=8 ;######### RESAMPLER CONFIG 2 ############ Resampler2.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=10 Channels_1B.count=10 Channels_2S.count=10 Channels_5X.count=2 Channels_L5.count=2 ;#GPS.prns=7,8 ;Channels.in_acquisition=2 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=0 Channel9.RF_channel_ID=0 Channel10.RF_channel_ID=1 Channel11.RF_channel_ID=1 Channel12.RF_channel_ID=1 Channel13.RF_channel_ID=1 Channel14.RF_channel_ID=1 Channel15.RF_channel_ID=1 Channel16.RF_channel_ID=1 Channel17.RF_channel_ID=1 Channel18.RF_channel_ID=1 Channel19.RF_channel_ID=1 Channel20.RF_channel_ID=0 Channel21.RF_channel_ID=0 Channel22.RF_channel_ID=0 Channel23.RF_channel_ID=0 Channel24.RF_channel_ID=0 Channel25.RF_channel_ID=0 Channel26.RF_channel_ID=0 Channel27.RF_channel_ID=0 Channel28.RF_channel_ID=0 Channel29.RF_channel_ID=0 Channel30.RF_channel_ID=2 Channel31.RF_channel_ID=2 Channel32.RF_channel_ID=2 Channel33.RF_channel_ID=2 Channel34.RF_channel_ID=2 Channel35.RF_channel_ID=2 Channel36.RF_channel_ID=2 Channel37.RF_channel_ID=2 Channel38.RF_channel_ID=2 Channel39.RF_channel_ID=2 Channel40.RF_channel_ID=2 Channel41.RF_channel_ID=2 Channel42.RF_channel_ID=2 ;Channel20.satellite=7 ;# GPS L1 CA Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.005 Acquisition_1C.doppler_max=5000 Acquisition_1C.doppler_step=250 Acquisition_1C.bit_transition_flag=false Acquisition_1C.max_dwells=1 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;# Galileo E1 Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000002 Acquisition_1B.doppler_max=5000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump_filename=./acq_dump.dat ;# GPS L2C M Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition Acquisition_2S.item_type=gr_complex Acquisition_2S.threshold=0.00074 ;Acquisition_2S.pfa=0.001 Acquisition_2S.doppler_max=5000 Acquisition_2S.doppler_min=-5000 Acquisition_2S.doppler_step=60 Acquisition_2S.max_dwells=1 Acquisition_2S.dump=false Acquisition_2S.dump_filename=./acq_dump.dat ;# GALILEO E5a Acquisition_5X.implementation=Galileo_E5a_Noncoherent_IQ_Acquisition_CAF Acquisition_5X.item_type=gr_complex Acquisition_5X.coherent_integration_time_ms=1 Acquisition_5X.threshold=0.009 Acquisition_5X.doppler_max=5000 Acquisition_5X.doppler_step=125 Acquisition_5X.bit_transition_flag=false Acquisition_5X.max_dwells=1 Acquisition_5X.CAF_window_hz=0 ; **Only for E5a** Resolves doppler ambiguity averaging the specified BW in the winner code delay. If set to 0 CAF filter is desactivated. Recommended value 3000 Hz Acquisition_5X.Zero_padding=0 ; **Only for E5a** Avoids power loss and doppler ambiguity in bit transitions by correlating one code with twice the input data length, ensuring that at least one full code is present without transitions. If set to 1 it is ON, if set to 0 it is OFF. Acquisition_5X.dump=false Acquisition_5X.dump_filename=./acq_dump.dat ;# GPS L5 Acquisition_L5.implementation=GPS_L5i_PCPS_Acquisition Acquisition_L5.item_type=gr_complex Acquisition_L5.threshold=0.00074 ;Acquisition_L5.pfa=0.001 Acquisition_L5.doppler_max=5000 Acquisition_L5.doppler_min=-5000 Acquisition_L5.doppler_step=125 Acquisition_L5.max_dwells=1 Acquisition_L5.dump=false Acquisition_L5.dump_filename=./acq_dump.dat ;######### TRACKING CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=35.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.early_late_space_chips=0.5; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### GALILEO E1 TRK CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### GPS L2C GENERIC TRACKING CONFIG ############ Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking Tracking_2S.item_type=gr_complex Tracking_2S.pll_bw_hz=2.0; Tracking_2S.dll_bw_hz=0.25; Tracking_2S.order=2; Tracking_2S.early_late_space_chips=0.5; Tracking_2S.dump=false Tracking_2S.dump_filename=./tracking_ch_ ;######### GALILEO E5 TRK CONFIG ############ Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking Tracking_5X.item_type=gr_complex Tracking_5X.track_pilot=true Tracking_5X.pll_bw_hz=15.0; Tracking_5X.dll_bw_hz=2.0; Tracking_5X.pll_bw_narrow_hz=5.0; Tracking_5X.dll_bw_narrow_hz=1.0; Tracking_5X.order=2; Tracking_5X.early_late_space_chips=0.5; Tracking_5X.dump=false Tracking_5X.dump_filename=./tracking_ch_ ;######### GALILEO E5 TRK CONFIG ############ Tracking_L5.implementation=GPS_L5_DLL_PLL_Tracking Tracking_L5.item_type=gr_complex Tracking_L5.track_pilot=true Tracking_L5.pll_bw_hz=15.0; Tracking_L5.dll_bw_hz=2.0; Tracking_L5.pll_bw_narrow_hz=4.0; Tracking_L5.dll_bw_narrow_hz=1.0; Tracking_L5.order=2; Tracking_L5.early_late_space_chips=0.5; Tracking_L5.dump=false Tracking_L5.dump_filename=./tracking_ch_ ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder TelemetryDecoder_2S.dump=false TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder TelemetryDecoder_5X.dump=false TelemetryDecoder_L5.implementation=GPS_L5_Telemetry_Decoder TelemetryDecoder_L5.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=PPP_Static ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=10 PVT.display_rate_ms=100 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=false; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT conf/gnss-sdr_multisource_Hybrid_ishort.conf000066400000000000000000000117571352176506000217560ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 Receiver.sources_count=2 SignalSource.enable_throttle_control=false SignalSource.repeat=false ;######### SIGNAL_SOURCE 0 CONFIG ############ SignalSource0.implementation=File_Signal_Source SignalSource0.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource0.item_type=ishort SignalSource0.sampling_frequency=4000000 SignalSource0.samples=0 ;######### SIGNAL_SOURCE 1 CONFIG ############ SignalSource1.implementation=File_Signal_Source SignalSource1.filename=/datalogger/signals/CTTC/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN/2013_04_04_GNSS_SIGNAL_at_CTTC_SPAIN.dat ; <- PUT YOUR FILE HERE SignalSource1.item_type=ishort SignalSource1.sampling_frequency=4000000 SignalSource1.freq=1575420000 SignalSource1.samples=0 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Ishort_To_Complex ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Pass_Through InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=gr_complex InputFilter0.output_item_type=gr_complex ;######### RESAMPLER 1 CONFIG ############ Resampler1.implementation=Pass_Through Resampler1.dump=false Resampler1.dump_filename=../data/resampler.dat Resampler1.item_type=gr_complex Resampler1.sample_freq_in=4000000 Resampler1.sample_freq_out=4000000 ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Ishort_To_Complex ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Pass_Through InputFilter1.dump=false ;######### RESAMPLER 1 CONFIG ############ Resampler1.implementation=Pass_Through Resampler1.dump=false Resampler1.dump_filename=../data/resampler.dat. Resampler1.item_type=gr_complex Resampler1.sample_freq_in=4000000 Resampler1.sample_freq_out=4000000 ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=2 Channels_1B.count=2 Channels.in_acquisition=1 ;# CHANNEL CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=1 Channel3.RF_channel_ID=1 ;#signal: ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.coherent_integration_time_ms=1 Acquisition_1C.threshold=0.0075 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000008 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=4.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.output_rate_ms=100; PVT.display_rate_ms=500; PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump_filename=./PVT PVT.dump=false conf/gnss-sdr_multisource_Hybrid_nsr.conf000066400000000000000000000153161352176506000212430ustar00rootroot00000000000000; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; You can define your own receiver and invoke it by doing ; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf ; [GNSS-SDR] Receiver.sources_count=2 ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. ;GNSS-SDR.internal_fs_sps=6826700 GNSS-SDR.internal_fs_sps=2560000 ;GNSS-SDR.internal_fs_sps=4096000 ;GNSS-SDR.internal_fs_sps=5120000 SignalSource.enable_throttle_control=false SignalSource.repeat=false ;######### SIGNAL_SOURCE 0 CONFIG ############ SignalSource0.implementation=Nsr_File_Signal_Source SignalSource0.filename=/datalogger/signals/ifen/E1L1_FE0_Band0.stream ; <- PUT YOUR FILE HERE SignalSource0.item_type=byte SignalSource0.sampling_frequency=20480000 SignalSource0.samples=0 ;######### SIGNAL_SOURCE 1 CONFIG ############ SignalSource1.implementation=Nsr_File_Signal_Source SignalSource1.filename=/datalogger/signals/ifen/E1L1_FE0_Band0.stream SignalSource1.item_type=byte SignalSource1.sampling_frequency=20480000 SignalSource1.samples=0 ;######### SIGNAL_CONDITIONER 0 CONFIG ############ SignalConditioner0.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 0 CONFIG ############ DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=float ;######### INPUT_FILTER 0 CONFIG ############ InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.dump_filename=../data/input_filter.dat InputFilter0.input_item_type=float InputFilter0.output_item_type=gr_complex InputFilter0.taps_item_type=float InputFilter0.number_of_taps=5 InputFilter0.number_of_bands=2 InputFilter0.band1_begin=0.0 InputFilter0.band1_end=0.45 InputFilter0.band2_begin=0.55 InputFilter0.band2_end=1.0 InputFilter0.ampl1_begin=1.0 InputFilter0.ampl1_end=1.0 InputFilter0.ampl2_begin=0.0 InputFilter0.ampl2_end=0.0 InputFilter0.band1_error=1.0 InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=20480000 InputFilter0.IF=5499998.47412109 InputFilter0.decimation_factor=8 ;######### RESAMPLER CONFIG 0 ############ Resampler0.implementation=Pass_Through ;######### SIGNAL_CONDITIONER 1 CONFIG ############ SignalConditioner1.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER 1 CONFIG ############ DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=float ;######### INPUT_FILTER 1 CONFIG ############ InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.dump_filename=../data/input_filter.dat InputFilter1.input_item_type=float InputFilter1.output_item_type=gr_complex InputFilter1.taps_item_type=float InputFilter1.number_of_taps=5 InputFilter1.number_of_bands=2 InputFilter1.band1_begin=0.0 InputFilter1.band1_end=0.45 InputFilter1.band2_begin=0.55 InputFilter1.band2_end=1.0 InputFilter1.ampl1_begin=1.0 InputFilter1.ampl1_end=1.0 InputFilter1.ampl2_begin=0.0 InputFilter1.ampl2_end=0.0 InputFilter1.band1_error=1.0 InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=20480000 InputFilter1.IF=5499998.47412109 InputFilter1.decimation_factor=8 ;######### RESAMPLER CONFIG 1 ############ Resampler1.implementation=Pass_Through ;######### CHANNELS GLOBAL CONFIG ############ Channels_1C.count=8 Channels_1B.count=8 Channels.in_acquisition=1 ;# SOURCE CONNECTION Channel0.RF_channel_ID=0 Channel1.RF_channel_ID=0 Channel2.RF_channel_ID=0 Channel3.RF_channel_ID=0 Channel4.RF_channel_ID=0 Channel5.RF_channel_ID=0 Channel6.RF_channel_ID=0 Channel7.RF_channel_ID=0 Channel8.RF_channel_ID=1 Channel9.RF_channel_ID=1 Channel10.RF_channel_ID=1 Channel11.RF_channel_ID=1 Channel12.RF_channel_ID=1 Channel13.RF_channel_ID=1 Channel14.RF_channel_ID=1 Channel15.RF_channel_ID=1 ;#signal: ;#if the option is disabled by default is assigned "1C" GPS L1 C/A Channel0.signal=1C Channel1.signal=1C Channel2.signal=1C Channel3.signal=1C Channel4.signal=1C Channel5.signal=1C Channel6.signal=1C Channel7.signal=1B Channel8.signal=1B Channel9.signal=1B Channel10.signal=1B Channel11.signal=1B Channel12.signal=1B Channel13.signal=1B Channel14.signal=1B Channel15.signal=1B ;######### GPS ACQUISITION CONFIG ############ Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex Acquisition_1C.scoherent_integration_time_ms=1 Acquisition_1C.threshold=0.0075 ;Acquisition_1C.pfa=0.01 Acquisition_1C.doppler_max=10000 Acquisition_1C.doppler_step=500 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;######### GALILEO ACQUISITION CONFIG ############ Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex Acquisition_1B.coherent_integration_time_ms=4 ;Acquisition_1B.threshold=0 Acquisition_1B.pfa=0.0000002 Acquisition_1B.doppler_max=15000 Acquisition_1B.doppler_step=125 Acquisition_1B.dump=false Acquisition_1B.dump_filename=./acq_dump.dat ;######### TRACKING GPS CONFIG ############ Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.pll_bw_hz=45.0; Tracking_1C.dll_bw_hz=2.0; Tracking_1C.order=3; Tracking_1C.dump=false Tracking_1C.dump_filename=../data/epl_tracking_ch_ ;######### TRACKING GALILEO CONFIG ############ Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex Tracking_1B.pll_bw_hz=15.0; Tracking_1B.dll_bw_hz=2.0; Tracking_1B.order=3; Tracking_1B.early_late_space_chips=0.15; Tracking_1B.very_early_late_space_chips=0.6; Tracking_1B.dump=false Tracking_1B.dump_filename=../data/veml_tracking_ch_ ;######### TELEMETRY DECODER GPS CONFIG ############ TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false ;######### TELEMETRY DECODER GALILEO CONFIG ############ TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder ;######### OBSERVABLES CONFIG ############ Observables.implementation=Hybrid_Observables Observables.dump=false Observables.dump_filename=./observables.dat ;######### PVT CONFIG ############ PVT.implementation=RTKLIB_PVT PVT.positioning_mode=Single ; options: Single, Static, Kinematic, PPP_Static, PPP_Kinematic PVT.iono_model=Broadcast ; options: OFF, Broadcast, SBAS, Iono-Free-LC, Estimate_STEC, IONEX PVT.trop_model=Saastamoinen ; options: OFF, Saastamoinen, SBAS, Estimate_ZTD, Estimate_ZTD_Grad PVT.output_rate_ms=100 PVT.display_rate_ms=500 PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; PVT.flag_nmea_tty_port=true; PVT.nmea_dump_devname=/dev/pts/4 PVT.flag_rtcm_server=false PVT.flag_rtcm_tty_port=false PVT.rtcm_dump_devname=/dev/pts/1 PVT.dump=false PVT.dump_filename=./PVT data/000077500000000000000000000000001352176506000117745ustar00rootroot00000000000000data/.gitignore000066400000000000000000000001061352176506000137610ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignoredebian/000077500000000000000000000000001352176506000123055ustar00rootroot00000000000000debian/.gitlab-ci.yml000066400000000000000000000024231352176506000147420ustar00rootroot00000000000000image: debian:sid-slim # Is performed before the scripts in the stages step before_script: - source /etc/profile # Defines stages which are to be executed stages: - build - test # Stage "build" run-build: stage: build script: - apt-get update - apt-get install -y devscripts git - export GITREV="$(git rev-parse --short HEAD)" - dch --distribution UNRELEASED --package "gnss-sdr" --newversion 0.0.11.git$GITREV-1 "Testing" - mk-build-deps -i debian/control --tool "apt-get -y" - tar -cvzf ../gnss-sdr_0.0.11.git$GITREV.orig.tar.gz . - dpkg-buildpackage -us -uc - rm -rf build/* - mv ../gnss-sdr_*.* build/ # The files which are to be made available in GitLab artifacts: paths: - build/* run-test: stage: test script: - apt-get update - apt-get install -y devscripts git - export GITREV="$(git rev-parse --short HEAD)" - dch --distribution UNRELEASED --package "gnss-sdr" --newversion 0.0.11.git$GITREV-1 "Testing" - mk-build-deps -i debian/control --tool "apt-get -y" - mkdir build-test - cd build-test - cmake .. - make -j2 - make check - ../install/run_tests --gtest_output=xml - mv *.xml ../build/ - cd .. - rm -rf build-test artifacts: paths: - build/*.xml debian/changelog000066400000000000000000000111071352176506000141570ustar00rootroot00000000000000gnss-sdr (0.0.11-1) unstable; urgency=medium * First release of upstream version 0.0.11 * Standards-Version updated to 4.4.0.0 * Removed debian/compat file, added debhelper-compat (= 12) in control file * Update copyright file with new code additions and copyright years * Add Protocol Buffers dependencies (libprotobuf-dev, protobuf-compiler) -- Carles Fernandez Sun, 04 Aug 2019 22:55:52 +0100 gnss-sdr (0.0.10-4) unstable; urgency=medium * Apply patch to deactivate problematic tests from the building stage -- Carles Fernandez Sat, 09 Feb 2019 12:35:25 +0100 gnss-sdr (0.0.10-3) unstable; urgency=medium * Standards-Version updated to 4.2.1 * Apply patch for building tests (Closes: bug#918055) -- Carles Fernandez Fri, 08 Feb 2019 12:19:52 +0100 gnss-sdr (0.0.10-2) unstable; urgency=medium * Team upload. * Set HOME to debian/home so the testsuite can create ~/.gnuradio/. -- Christoph Berg Sun, 30 Dec 2018 15:38:38 +0100 gnss-sdr (0.0.10-1) unstable; urgency=medium * First release of upstream version 0.0.10 * Standards-Version updated to 4.2.1 * Updated compat level to 11 * Updated Vcs links to Salsa servers, completing Alioth to Salsa migration * Added autopkgtest check * Added debian/upstream/metadata file * Added libpcap-dev as a new dependency for UDP streaming of signal samples * Added libiio-dev and gr-iio as new dependencies for using with AD front-ends * Added libmatio-dev for storing data in .mat format * Added libpugixml-dev dependency for XML file handling * Switched back from googletest to libgtest-dev dependency * Switched from python-mako and python-six to their python3 counterparts * Switched from http to https for project homepage * Updated copyright file for new contributions by Alvaro Cebrian, Antonio Ramos, Cillian O'Driscoll, Damian Miralles, Gabriel Araujo, Rodrigo Muñoz Lazo, T. Takasu, Phil Karn, Jeremy Conlin and Swift Navigation Inc. * Added LGPL-3+ license text for files under src/algorithms/libs/rtklib/ * Added -DENABLE_FMCOMMS2=ON, -DENABLE_PLUTOSDR=ON, -DENABLE_AD9361=ON and -DENABLE_RAW_UDP=ON building options in debian/rules * Fix building with boost 1.67 (Closes: bug#911882) -- Carles Fernandez Fri, 14 Dec 2018 19:37:40 +0100 gnss-sdr (0.0.9-5) unstable; urgency=medium * Standards-Version updated to 4.0.0 * Migrated from experimental to unstable -- Carles Fernandez Mon, 03 Jul 2017 22:08:24 +0200 gnss-sdr (0.0.9-4) experimental; urgency=medium * Set compat file value to 10, re-enabling parallel building (Closes: #860090) -- Carles Fernandez Tue, 11 Apr 2017 18:10:14 +0200 gnss-sdr (0.0.9-3) experimental; urgency=medium * Compat level updated to 10 * Removed minimum versions in control already available in oldstable * libarmadillo-dev minimum version updated to >= 1:5.300.0 due to interp1 * Added comment on why auto_test is disabled * dh is called without --parallel (it is now the default) and without --builddirectory=build (which was useless) * Thanks to Gianfranco Costamagna for spotting these issues -- Carles Fernandez Mon, 10 Apr 2017 20:27:31 +0200 gnss-sdr (0.0.9-2) experimental; urgency=medium * Removed libc6, dpkg-dev and libstdc++6 from build-dependencies since dependencies on build-essential binary packages can be omitted -- Carles Fernandez Sat, 08 Apr 2017 12:13:01 +0200 gnss-sdr (0.0.9-1) experimental; urgency=medium * First release of upstream version 0.0.9 * Added python-mako and python-six to build-dependencies required volk_gnsssdr -- Carles Fernandez Fri, 07 Apr 2017 16:31:42 +0200 gnss-sdr (0.0.8-1) unstable; urgency=medium * First release of upstream version 0.0.8 -- Carles Fernandez Mon, 04 Jul 2016 18:07:57 +0200 gnss-sdr (0.0.7-1) unstable; urgency=medium * First release of upstream version 0.0.7 -- Carles Fernandez Sun, 15 May 2016 22:16:35 +0200 gnss-sdr (0.0.6-2) unstable; urgency=medium * Bumps gnuradio version. * Added cryptographic signature verification. * Standards-Version bumped to 3.9.7 * Added hardening options. -- Carles Fernandez Tue, 26 Apr 2016 16:15:51 +0200 gnss-sdr (0.0.6-1) unstable; urgency=medium * Initial release. (Closes: #712838) -- Carles Fernandez Wed, 02 Sep 2015 18:21:45 +0200 debian/clean000066400000000000000000000000151352176506000133060ustar00rootroot00000000000000debian/home/ debian/control000066400000000000000000000040311352176506000137060ustar00rootroot00000000000000Source: gnss-sdr Maintainer: Debian Hamradio Maintainers Uploaders: Carles Fernandez Section: hamradio Priority: optional Build-Depends: debhelper-compat (= 12), cmake, libboost-dev, libboost-date-time-dev, libboost-system-dev, libboost-filesystem-dev, libboost-thread-dev, libboost-serialization-dev, libboost-chrono-dev, libuhd-dev (>= 3.7), gnuradio-dev (>> 3.7.8), gr-osmosdr, libpcap-dev, gr-iio (>= 0.3), libiio-dev, liblog4cpp5-dev, libarmadillo-dev (>= 1:5.300.0), liblapack-dev, libblas-dev, libgnutls28-dev, libgflags-dev, libgoogle-glog-dev (>= 0.3.3), libgtest-dev, libpugixml-dev, libprotobuf-dev, protobuf-compiler, python3-mako, python3-six, libmatio-dev Standards-Version: 4.4.0.0 Vcs-Browser: https://salsa.debian.org/debian-hamradio-team/gnss-sdr Vcs-Git: https://salsa.debian.org/debian-hamradio-team/gnss-sdr.git Homepage: https://gnss-sdr.org Package: gnss-sdr Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Global navigation satellite systems software defined receiver Global Navigation Satellite Systems receiver defined by software. It performs all the signal processing from raw signal samples up to the computation of the Position-Velocity-Time solution, including code and phase observables. It is able to work with raw data files or, if there is computational power enough, in real time with suitable radiofrequency front-ends. This software is mainly developed at CTTC (Centre Tecnologic de Telecomunicacions de Catalunya, http://www.cttc.es) with contributions from around the world. More info at https://gnss-sdr.org debian/copyright000066400000000000000000000357621352176506000142550ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: gnss-sdr Upstream-Contact: Carles Fernandez Source: git://github.com/gnss-sdr/gnss-sdr Comment: See the AUTHORS file for a more complete list of contributors. Copyright: (C) 2010-2019 Carles Fernandez (C) 2010-2019 Javier Arribas (C) 2010 Carlos Aviles (C) 2012-2017 Luis Esteve (C) 2017-2018 Antonio Ramos License: GPL-3+ Files: * Copyright: (C) 2010-2019 Carles Fernandez (C) 2010-2019 Javier Arribas (C) 2010 Carlos Aviles (C) 2012-2018 Luis Esteve License: GPL-3+ Files: src/core/libs/supl/supl.* Copyright: (C) 2007 Tatu Mannisto License: BSD-2-clause Files: src/core/libs/supl/asn-supl/* src/core/libs/supl/asn-rrlp/* Copyright: (C) 2004, 2006 Lev Walkin License: BSD-2-clause Files: src/core/libs/INIReader.* src/core/libs/ini.* Copyright: (C) 2009 Brush Technologies License: BSD-3-clause Files: src/algorithms/libs/gsl/include/gsl/* Copyright: (C) 2015-2018 Martin Moene (C) 2015-2018 Microsoft Corporation License: Expat Files: src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/* Copyright: (C) 2014 Andres Cecilia (C) 2015-2019 Carles Fernandez (C) 2015-2017 Javier Arribas License: GPL-3+ Files: src/algorithms/signal_source/adapters/rtl_tcp_signal_source.* src/algorithms/signal_source/gnuradio_blocks/rtl_tcp* Copyright: (C) 2015 Anthony Arnold License: GPL-3+ Files: src/algorithms/input_filter/adapters/notch_filter_lite.* src/algorithms/input_filter/adapters/notch_filter.* src/algorithms/input_filter/adapters/pulse_blanking_filter.* src/algorithms/input_filter/gnuradio_blocks/notch_* src/algorithms/input_filter/gnuradio_blocks/pulse_blanking_cc.* src/algorithms/resampler/adapters/mmse_resampler_conditioner.* src/algorithms/acquisition/adapters/galileo_e5a_pcps_acquisition.* Copyright: (C) 2017-2018 Antonio Ramos License: GPL-3+ Files: src/algorithms/signal_source/adapters/fmcomms2_signal_source.* src/algorithms/signal_source/adapters/plutosdr_signal_source.* Copyright: (C) 2017 Rodrigo Muñoz Lazo License: GPL-3+ Files: src/algorithms/signal_source/gnuradio_blocks/unpack_2bit_samples.* src/algorithms/signal_source/adapters/two_bit_packed_file_signal_source.* Copyright: (C) 2015 Cillian O'Driscoll License: GPL-3+ Files: src/algorithms/telemetry_decoder/libs/libswiftcnav/fec.h src/algorithms/telemetry_decoder/libs/libswiftcnav/viterbi27.c Copyright: (C) 2004 Phil Karn, KA9Q License: LGPL-3+ Files: src/algorithms/telemetry_decoder/libs/libswiftcnav/bits.* src/algorithms/telemetry_decoder/libs/libswiftcnav/cnav_msg.* src/algorithms/telemetry_decoder/libs/libswiftcnav/edc.* src/algorithms/telemetry_decoder/libs/libswiftcnav/swift_common.h Copyright: (C) 2012 Swift Navigation Inc. License: LGPL-3+ Files: src/algorithms/acquisition/adapters/glonass_l1* src/algorithms/tracking/adapters/glonass_l1* src/algorithms/tracking/gnuradio_blocks/glonass_l1* Copyright: (C) 2017 Gabriel Araujo (C) 2017 Luis Esteve (C) 2019 Damian Miralles License: GPL-3+ Files: src/algorithms/acquisition/adapters/beidou* src/algorithms/tracking/adapters/beidou* src/algorithms/libs/beidou* Copyright: (C) 2018 Sergi Segura (C) 2019 Damian Miralles (C) 2019 Carles Fernandez License: GPL-3+ Files: src/algorithms/telemetry_decoder/adapters/glonass_l1* src/algorithms/telemetry_decoder/gnuradio_blocks/glonass_l1* src/core/system_parameters/glonass* Copyright: (C) 2017 Damian Miralles License: GPL-3+ Files: src/tests/common-files/gnuplot_i.h Copyright: (C) 2009 Jeremy Conlin (C) 2019 Carles Fernandez License: GPL-3+ Files: src/core/monitor/* Copyright: (C) 2018 Alvaro Cebrian Juan License: GPL-3+ Files: src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/kernels/volk_gnsssdr/volk_gnsssdr_s32f_sincos_32fc.h src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/kernels/volk_gnsssdr/volk_gnsssdr_32f_sincos_32fc.h Copyright: (C) 2007 Julien Pommier License: Zlib This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. . Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: . 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Files: src/algorithms/libs/opencl/cl.hpp Copyright: (C) 2008-2013 The Khronos Group Inc. License: Permissive Permission is hereby granted, free of charge, to any person obtaining a copy of this software and/or associated documentation files (the "Materials"), to deal in the Materials without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials, and to permit persons to whom the Materials are furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Materials. . THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. Files: src/algorithms/libs/rtklib/* Copyright: (C) 2007-2013 T. Takasu (C) 2017 Javier Arribas (C) 2017-2019 Carles Fernandez License: BSD-2-clause Files: src/algorithms/libs/opencl/clFFT.h src/algorithms/libs/opencl/fft_execute.cc src/algorithms/libs/opencl/fft_base_kernels.h src/algorithms/libs/opencl/fft_internal.h src/algorithms/libs/opencl/fft_kernelstring.cc src/algorithms/libs/opencl/fft_setup.cc Copyright: (C) 2008 Apple Inc. License: Apple-Permissive This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. . In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non - exclusive license, under Apple's copyrights in this original Apple software ( the "Apple Software" ), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and / or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. . The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. . IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: GPL-3+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. . You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 3 can be found in the file `/usr/share/common-licenses/GPL-3'. License: LGPL-3+ This package 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 package 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the complete text of the GNU Lesser General Public License can be found in `/usr/share/common-licenses/LGPL-3'. License: BSD-2-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. debian/gbp.conf000066400000000000000000000000451352176506000137230ustar00rootroot00000000000000[DEFAULT] upstream-tag=v%(version)s debian/gitlab-ci.yml000066400000000000000000000021071352176506000146630ustar00rootroot00000000000000image: debian:sid-slim # Is performed before the scripts in the stages step before_script: - source /etc/profile # Defines stages which are to be executed stages: - build # Stage "build" run-build: stage: build script: - apt-get update - apt-get install -y dh-make build-essential git cmake libboost-dev libboost-date-time-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-chrono-dev libboost-serialization-dev libboost-program-options-dev libboost-test-dev liblog4cpp5-dev libuhd-dev gnuradio-dev gr-osmosdr libblas-dev liblapack-dev libarmadillo-dev libgflags-dev libgoogle-glog-dev libgnutls-openssl-dev libgtest-dev libmatio-dev python3-mako python3-six libpugixml-dev libprotobuf-dev protobuf-compiler libpcap-dev gr-iio debhelper libiio-dev - git archive --format=tar.gz -o ../gnss-sdr_0.0.11.orig.tar.gz HEAD - dpkg-buildpackage -us -uc - mv ../gnss-sdr*.deb build/ # This stage is only executed for new tags # only: # - tags # The files which are to be made available in GitLab artifacts: paths: - build/* debian/rules000077500000000000000000000006201352176506000133630ustar00rootroot00000000000000#!/usr/bin/make -f export DH_VERBOSE = 1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all # testsuite needs a place to put ~/.gnuradio/ export HOME=$(CURDIR)/debian/home %: dh $@ --buildsystem=cmake override_dh_auto_configure: mkdir -p debian/home dh_auto_configure -- -DENABLE_PACKAGING=ON -DENABLE_OSMOSDR=ON -DENABLE_FMCOMMS2=ON -DENABLE_PLUTOSDR=ON -DENABLE_AD9361=ON -DENABLE_RAW_UDP=ON .. debian/source/000077500000000000000000000000001352176506000136055ustar00rootroot00000000000000debian/source/format000066400000000000000000000000141352176506000150130ustar00rootroot000000000000003.0 (quilt) debian/tests/000077500000000000000000000000001352176506000134475ustar00rootroot00000000000000debian/tests/control000066400000000000000000000000451352176506000150510ustar00rootroot00000000000000Tests: testgnsssdr Depends: gnss-sdr debian/tests/testgnsssdr000077500000000000000000000002771352176506000157660ustar00rootroot00000000000000#!/bin/sh # autopkgtest check # (C) 2018 Carles Fernandez-Prades set -e WORKDIR=$(mktemp -d) trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM cd $WORKDIR gnss-sdr --version echo "run: OK" debian/upstream/000077500000000000000000000000001352176506000141455ustar00rootroot00000000000000debian/upstream/metadata000066400000000000000000000021521352176506000156500ustar00rootroot00000000000000Bug-Database: https://github.com/gnss-sdr/gnss-sdr Bug-Submit: https://github.com/gnss-sdr/gnss-sdr Cite-As: "Carles Fernandez Prades, Javier Arribas et al., GNSS-SDR, v0.0.11, 2019." Changelog: https://github.com/gnss-sdr/gnss-sdr/blob/master/docs/changelog Contact: https://sourceforge.net/projects/gnss-sdr/lists/gnss-sdr-developers Documentation: https://gnss-sdr.org/docs/ Funding: - TEC2015-69868-C2-2-R - EC H2020 687367 - ESA AO/2-1647/17/NL/CRS-TR1 Name: "GNSS-SDR" Other-References: https://gnss-sdr.org/publications/ Reference: Author: Fernandez Prades, Carles and Vila Valls, Jordi and Arribas, Javier and Ramos, Antonio DOI: 10.1109/ACCESS.2018.2822835 Journal: IEEE Access Number: 1 Pages: 20451-20463 Title: Continuous Reproducibility in GNSS Signal Processing Type: article URL: https://ieeexplore.ieee.org/document/8331069/ Volume: 6 Year: 2018 Repository: https://github.com/gnss-sdr/gnss-sdr.git Repository-Browse: https://github.com/gnss-sdr/gnss-sdr Security-Contact: "Carles Fernandez-Prades, carles.fernandez@cttc.es" debian/upstream/signing-key.asc000066400000000000000000000144731352176506000170720ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQINBFSP6m4BEAC2DUDkuewhhig4oMjm37BBpc0PQ1eh9lMS7k34lj1Ukkfn4tQp TVVV2oc3cko4y12ycFpuwPr8YL27W0qpy5usr8miawev7wqOQ2sY8OUrU68lF6Hf 4efYpQJ8vuT8mQR8o1Fdxlmopx5rWJ3gzC4Ppgf8HAerN/v5ZubSVnCcWazM3VvR M7t9Hq8B3ijOwYp9R+wHcpuzZ3Rj3rrqZrylMahrjvKZe0aGdkrIIzTl4VDM3/lO oXebyKpaAhRHXN1XNUoNwF/ItqZlpoS1zjjWXn5y4eDu2rYK6GHUDGKLBrSANXC/ HrTvrwHbHyh+9UQlMDtqYPQdc2hTEQm2BWHGnWuETtTGrsxtB6/43JBt0SYDbeGz 3tfvztvqTUBn/21Uw1GK4WiBZ7tC3X+uJsgjAUxBclzZqblnk7d2muka9oIY7pEl P3EPwed6ueA+k/MF0vHf5KURddgFhQBjJ6Ws1K55SOwqwMfH9VrXRS3oJhLSKLyh K+RK5388g0W1T5wQiAaL5Z6rUTGGEbl0KHGs/W5mk2rT4++Z0zoq2hawueY2FwEt FwmZfot1jxhpFD75OMNnGJWzaigDjTnUdIj75ISLUlJ9lVyvsXtXD7fyfR393EjX 3+HmyWR5NP8EwjWnq9p/xqsDDYmYcOo/Vrkh2pWmh0DsEZsM/ctS2fIG3wARAQAB tCtDYXJsZXMgRmVybmFuZGV6IDxjYXJsZXMuZmVybmFuZGV6QGN0dGMuZXM+iQI+ BBMBAgAoBQJUj+puAhsDBQkJZgGABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAK CRBMWDxSsMOHffp7EACnnNrRdHjxu2H0pfUUEbiqfAgRf0pHJ4v+S2xblDkFppEj dcgg+ZfnhOAq55G59yLSruCpMbWV/mAeSy4cS1dPqOa9Hv/Zi8pKKn/kvY6BUq9O djTwyYOMGjwI/dEBSMZYOd5+2ItamtCME8a8oZzmOTqvtHei8zgpEWd01k13TfzO +LtSgz6sbKOvrYllJK5cg+8OG7/xvNX0ZYE7EjNNl4ZHBjKpyjNhgHAU6m91CI9y 3n+Ff2z0Qy0PiaANcHudWRL6s3hppU5sZmEWXxMeJcabc/3AsrKS4rdGukTpJ9w0 EvcQTwIM9EeZjLBlwcsBIeS+qQMjI5+X8oCx0+7I/kwMibHVWhBTXQVwqtMrk8xG 74uKwXPVm1O41qw+fxhXsetF+6JVlb/6RON8djE13tNG4NCjEWoyuZB8kEN0fLKt XxD08BYJiH43KRjOvtv2HUjyEGafuMPWJXdojjCOGuT2xvnAv46dCV6ybZ+ASitE TEeKr14hTmruxHIGZdEatT44REqcRuIBOuG8gfWhs3vC65drNAic3ygL7yyEuEcP Iqua3YgW3OX6TD4mcOK2w9tdY3beNG7Jwc40v4vsZs5SSHpvwvwBdhlsmVV90hpr Awi4dUZtYnZ/+Y5Fpujh3INgCk8PJqRa+C4ezU/ZEROsQtHiLpAPCZs3Z3Ql57Qi aHR0cHM6Ly9naXRodWIuY29tL2Nhcmxlc2Zlcm5hbmRleokCPQQTAQoAJwUCVJCZ dgIbAwUJCWYBgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBMWDxSsMOHfcQV D/0c3Bj2amwSF6M1nXRijrvucth3pKY4N4B6kEBRQPbs6+hYS7Kh677E8bcggga0 ZpslKIY7HLZztqHy581Sc5KBQF0FbCzBs0mwv4C/PS1t3rQkXysDN66zE/p6m3v8 xND4kqqibWs7PG12Hhr1+SZHdJr1mJfDhkP9Dxq7cRumoiogn0WRsPEfurwnC7+D NQKayXkxFiSr7ed9+xB/R7pG5o9IkwRWunUCLLtVypp7wObEoPQ6+Os/AoriZuwc tmopPVbXdjfdfm8PaL6F/CMXeOsHf+CbuiRZ4LB6LVhpaVR8TPKidX9Wz1PG/CsI T8T2hOvH4hyWKickmaIHag5DZdYCQdeLe1WU4vXUoymNU4hWKh2NIAv9Gf9BzkYO NDXYLakHjiQ913Rcazp0wGzT2tJDOiiOt7GLarNZC1upLbQWJNau0uNTykNRuCJS HDvk5p4paXZJ7vQW+TGrkgbRLLRS5+bCIE6uPYDjzkB2cNPAs9nJZo6p0iVBXWFt SE20UqOHMOfLGh1q7Nv5NwBehdscDczAPlwEBNBivBdbeW0QgPCGfxbhufHINVr0 YzysBq+wC8HOLIEfEo/qkNRiT3sY6s7wsHCvpW+gmfdFD2pxY4Er4sPsrg6hffkd oUnxz+e2C/ERRstzJbgOuS6ffe9qtkBOEZ1NsTIiAc1GvLQkaHR0cHM6Ly90d2l0 dGVyLmNvbS9ARmVybmFuZGV6UHJhZGVziQI9BBMBCgAnBQJUkJpsAhsDBQkJZgGA BQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEExYPFKww4d9fasP/2mR800SILdS C6SvkcCmuFalEjcTh7ubJJO875uSW7SgyvUZrhw0oMzamfLzNBbS41BMc/9Zf7RQ S8UnHnWocWDPKLdlfWOvzSNHvyjXwcw4XGp5vo+FCqDNFxpKrO2ttnga8W7FoZta xqCO/9CqZOxw2gQtCmJT8x8O52yakH9gjMXVHrPCXeejI5OiKoj1D5Xxj7vEkRbo BQeCNT2mCRggLegj8qrz6yPsvwdfUTYrPOCuCWy+IZqhB7whTEoiOoaSz8SX0FT2 CeMge2H9Sn9pDLpPeNU/BrCpvHn45C3pUsG6dD7/HLLzMRlGznRmDTh+2QQy2VO/ u3vHZXeyHnHaKATFiMwjDp48frCZM33Tey+Ri7ZiOHogMVq9n3J0uVuxKvsf1dOZ kPF0sN5N/yLI38EGwuwz38xs9wvQaMekgu2Sa678SQVg2wiuV6ostbk2cJ/Uam4C vWkMAG5X43TJZD/31JwctJdD4ODF7ZPPVnL3zFpX5X2T8VXPLquoVlTtENwa0C8L SxX9EDiWRjbVuNP8IYSRKhNnIZkwvyCJoeH2cbilSB2T3+ypm35SyC1tJeQGVsMY 74ePb4rB7sofMNmb7QSbHxkk+MCkh5FEHuHUAFfEE2jqb+96I3mVHqQyUGWThKE7 Z92YtOCDQSnnZPsjxSkNaKmvUQNYRPnAtChodHRwczovL3BsdXMuZ29vZ2xlLmNv bS8rQ2FybGVzRmVybmFuZGV6iQI9BBMBCgAnBQJUkJtjAhsDBQkJZgGABQsJCAcD BRUKCQgLBRYCAwEAAh4BAheAAAoJEExYPFKww4d9bVIP/RkUINjZ5wFJtSHf3l+4 xyUDgHdVMNVN3h2dIYs2HAkzIwbfm6RPntHHddD66XjP17FNxmmVWpy5Ogf4UywG 7UVsrVv5jRP/mMJzVbfsSLVSnNeTYa07Osg/8+xGbCvKV26xrid9Qno1QP42/+VU uEUYZZut5Bch37xqg2zRwVejM/q5nIar0gYzMUL+zuik7ftWK/NIjtB1S9qSO6xC lHwup1T4j/RG3DAjr5AeuCm5xkrA2nxPPEvlzCi7q3ZBvcq30bFwE9ctgjBquMqM C9tGb/5lil0uv6S0J5yjM+8wVgQED+pbELj7AwlH+NSCgn//lt7YyGh30SX2Kzz3 W3WwnoqwIZeRil615J8KbLPDp402S2s1mpiOsWxi3Tkm/m4rI6z/GdVV9ig1fqTF OWIEZq5czL6NetIgMUzLV3sC9k8CQcAKCxyTHB8K60/UsamQgR5wH/99pEnaYvHu ebVcuQpp/+pVe4aNbPwSP8GJpkYD3bAt7fFMVIjqrxdK5mQ3cYYwdIWS7BkEzcSv 9prPCiWt5AJKM/0GCfmuCV82Acd5JrJyO2ecUI0U2axgTQkY0cXeUs2yOk6ENvwM Hg0mHmIgiqqjSn+Q2g21wIXQQ1P2/4OcdxJVg/Q9Nmhc0iYLnjI2E96FRxAmVreS Ohkk19WXoJDiEgVAy6i0PVj2tC1DYXJsZXMgRmVybmFuZGV6IDxjYXJsZXMuZmVy bmFuZGV6QGdtYWlsLmNvbT6JAj0EEwEKACcFAlSQm7kCGwMFCQlmAYAFCwkIBwMF FQoJCAsFFgIDAQACHgECF4AACgkQTFg8UrDDh33JvQ/8ClXhP6JYqxFZ5J3THhqo jFpjF9R9mnXs4GEJbAYnk+YXGSNCmQF4HkuF5jLWkx+NQIlZAQpe62SrwtCOpkUu /ZvKYWcnUc/zBTEF6uwbNxYvezRwrl8xEpxT0sAA4DT+tkKVUOp0r9zdk+T7VkgF CxDFjaUmoT/PtPAh1r5jOOuaEraeWhGQ0aCLfjqnMsM+wLz5tXq2guTGKukMwH4K pg7wW6/bTgBYShhVajKxgJXDmTF02UvJz3FoNBJxLRqJxoco6hvx31S9LutIkk7D FCEPz68lQ56TXIjXOSwkmm4p4o7BEMMVrF7IDWeBzktet3PcGXabj27GW75GWhD/ ETzCrOImjzhzZIMf0JlHMPto+eS21pvzykbGt4KfEKsR2dx9myyn2CzZRgWnHdnH FbJHi6n4dQw35UgCjAz308NNSW8afUXPyXAqvyDZkSEMydAGMxC5yoGQ8KAu/ld1 7ls9mJUgxaa7E3op2vQb55rRCekKV3rz0/Y6R70as7vajwb+Ow2+Bu53aQ3DHOno XSv/powZR+0m6lgBHH9zcZjhegDnWgfVyqpTjMO0SmfHnQ4gHmuYXupdeVRyJF6A wGDvtFWAzfBUvOqRPBsCjEqr67eBppYBEog3rM8k4rLMwDqU8xfy17MclnEJNBSJ r4TcCqX1E5qoo9zR2fyTgzK5Ag0EVI/qbgEQAK2Vu6E5kjnOc9lrkgXK+mmDBMU7 9TWW3VPZ/o2pNzivKpssdgGjKXKclnHtL4SsES+GzyGDLExQzviUHeqsUGWr06gp QczH6wqMFCTVmcmjtwjFueNcKxnhiWAqviKpwKjYzl2J+OiOTT6HgrbLqsfKOORR tSS15sp/R4Na+YUwESUApq5yiUBjDBIrsGkaFEXX0pZv3OR8Tt+PL/hAZ1eoc9aD aNNoKAkXLDNw0SityKuQBiQ0TgIFKOrX0dwnOdwcSPb3NDvCLxWGkCeAejzXho5H t5bQMqFMPr8MK7pFcbmK1cpV7Cc/cqP6eLMlADRMULjsa/vg3pqoMu9wftu7ro5d CcT+2r0ZHO9ppeq6yq4JkT+fyPHngmp/Pf3rglBbyTCxxEHwqKnDCRy62gBmD4Qz dIV1pmTlBSGZtgqo2kv7DubL6QHJef5bnec9b9ilnBhqQCWJZdfhqMet62jIbJIz w6ZeE0kBVNRkKkYG8A5u6wYyFHhMHXNOnovFpNNvi7Ad2xhTaqqw6UCFD6vmltPk CN3bAIoMptszCjnSewbn7VcLtZoPGOvXpLdR6nXPHud/Lhu4xK/xQbbP9ZxzKQxN kxIMssg2NbVHVHzUkksTT/DhMlUAJABGtZ1jZhZg8R+GY1mglh9HXLbdzr+Cc+q2 F6BgrSZ5bmTn2H5LABEBAAGJAiUEGAECAA8FAlSP6m4CGwwFCQlmAYAACgkQTFg8 UrDDh3308BAAhaW4V8gPRnNM0f8S/uIXpX13nwFfs1Zk24X0Ibks8wu9oMDCuqdD jm5462yyMeozyCpZotjyIRN6l+Z3QnV8Dnm3qiN+2N0N8pF61O4DSYNL6MrEQRyc IAj15v9mNR+LFoGWTceKLddOJDY95ZMGQMOXX3UwU1Zzy1QvsjW4svfRdJjbY3fO dJUBzKd2CKso64Kr99D8J51BptwyXQnanZwapo0Dg58wM/QC97m8qq36bNw8SaWg eRd7ldpUc64HA0gAOYzloiMIVu+wKnhW/qTJV4Yb4FJEH2/e9A7trSXq9dLjxzG2 18tQ0+zg5aAUH+LjNuoma7AOYqEThgJ5oDc0Dau1k45DIK8W+VCwMRTz5EtvDk/Z OyG64as5x+S5fk3rbUU9SE/QFkjuDG92+vxfGTrN7YzDgHQLpXMYMPh/xwI2/VRC +YNOVGD8vqS1hD34phf8gdSeekvDS6F/Zq93T0cTdm/hVk/4AeJm8bo7GCSEga0K oA1AL6rTnWyFiSQbkTAPgRTT8S6QcLDyivLampyUHNGqTRzhOZaOZZ/TzKAouJ90 CfEQJf8RXml2+Ist6GOPCkUXAIvprHYXAgH5Sz9sLsM9BhNz9kL/iyChYY8/t2+c odS+FvJcE1/W154abFOyIlEfXNMhKB53+SdTagZb/28UlECCY1nQ35I= =ttO+ -----END PGP PUBLIC KEY BLOCK----- debian/watch000066400000000000000000000010541352176506000133360ustar00rootroot00000000000000version=3 opts="filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/gnss-sdr-$1\.tar\.gz/,pgpsigurlmangle=s/archive\/v(\d\S*)\.tar\.gz/releases\/download\/v$1\/gnss-sdr-$1.tar.gz.asc/,uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/,dversionmangle=s/\+(debian|dsfg|ds|deb)(\.\d+)?$//" https://github.com/gnss-sdr/gnss-sdr/tags .*/v?(\d\S.*)\.(?:tgz|tbz2|txz|tar\.(?:gz|bz2|xz)) # if tweaking of source is needed # \ # debian debian/get-orig-source # if you need to repack and choose +dfsg prefix # opts=dversionmangle=s/[~\+]dfsg[0-9]*// \ # docs/000077500000000000000000000000001352176506000120135ustar00rootroot00000000000000docs/PULL_REQUEST_TEMPLATE.md000066400000000000000000000034451352176506000156220ustar00rootroot00000000000000:+1::tada: Hello, and thanks for contributing to [GNSS-SDR](https://gnss-sdr.org)! :tada::+1: Before submitting your pull request, please make sure the following is done: 1. You undertake the [Contributor Covenant Code of Conduct](https://github.com/gnss-sdr/gnss-sdr/blob/master/CODE_OF_CONDUCT.md). 2. If you are a first-time contributor, after your pull request you will be asked to sign an Individual Contributor License Agreement ([CLA](https://en.wikipedia.org/wiki/Contributor_License_Agreement)) before your code gets accepted into `master`. This license is for your protection as a Contributor as well as for the protection of [CTTC](http://www.cttc.es/); it does not change your rights to use your own contributions for any other purpose. Except for the license granted therein to CTTC and recipients of software distributed by CTTC, you reserve all right, title, and interest in and to your contributions. The information you provide in that CLA will be maintained in accordance with [CTTC's privacy policy](http://www.cttc.es/privacy/). 3. You have read the [Contributing Guidelines](https://github.com/gnss-sdr/gnss-sdr/blob/master/CONTRIBUTING.md). 4. You have read the [coding style guide](https://gnss-sdr.org/coding-style/). 5. Specifically, you have read [about clang-format](https://gnss-sdr.org/coding-style/#use-tools-for-automated-code-formatting) and you have applied it. 6. You have forked the [gnss-sdr upstream repository](https://github.com/gnss-sdr/gnss-sdr) and have created your branch from `next` (or any other currently living branch in the upstream repository). 7. Please include a description of your changes here. **Please feel free to delete this line and the above text once you have read it and in case you want to go on with your pull request, and explain your intend below.** docs/changelog000066400000000000000000001116711352176506000136740ustar00rootroot00000000000000## [0.0.11](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.11) This release has several improvements in different dimensions, addition of new features and bug fixes: ### Improvements in Accuracy - Local clock correction based on PVT solution, allowing the delivery of continuous observables. - Fix a bug in broadcast ionospheric parameters usage. ### Improvements in Availability - Improved mechanism for false lock detection in the Tracking loops. - Fixed bug in Galileo INAV/FNAV message decoding when PLL is locked at 180 degrees, which prevented from correct navigation message decoding in some situations. - Fixed bug that caused a random deadlock in the Observables block, preventing the computation of PVT fixes. - Fixed PVT computation continuity through the TOW rollover. - Improved signal acquisition and tracking mechanisms in high dynamic scenarios. ### Improvements in Efficiency - Added mechanism for assisted acquisition of signals on a secondary band when the primary has already been acquired. This allows a great reduction of the computational load in multi-frequency configurations. - Tracking loops now perform bit synchronization, simplifying the decoding process in Telemetry blocks and FPGA-offloading. - Improved preamble detection implementation in the decoding of navigation messages (acceleration by x1.6 on average per channel). - Shortened Acquisition to Tracking transition time. - Applied clang-tidy checks and fixes related to performance: performance-faster-string-find, performance-for-range-copy, performance-implicit-conversion-in-loop, performance-inefficient-algorithm, performance-inefficient-string-concatenation, performance-inefficient-vector-operation, performance-move-const-arg, performance-move-constructor-init, performance-noexcept-move-constructor, performance-type-promotion-in-math-fn, performance-unnecessary-copy-initialization, performance-unnecessary-value-param, readability-string-compare. ### Improvements in Flexibility: - Rewritten Control Thread and GNSS flow graph for increased control of channels' status and smarter assignation of satellites in multi-band configurations. - New Tracking parameters allow the configuration of PLL and DLL filters order. - Added parameter to enable FLL during pull-in time. - Configurable pull-in time in the Tracking loops. ### Improvements in Interoperability: - Added the BeiDou B1I and B3I receiver chains. - Fix bug in GLONASS dual frequency receiver. - Added a custom UDP/IP output for PVT data streaming. - Improved Monitor block with UDP/IP output for internal receiver's data streaming. - Custom output formats described with .proto files, making easier to other applications reading them in a forward and backward-compatible fashion upon future format changes. New dependency: Protocol Buffers >= 3.0.0 - Fixes in RINEX generation: week rollover, annotations are not repeated anymore in navigation files. Parameter rinexnav_rate_ms has been removed, annotations are made as new ephemeris arrive. - Fixes in RTCM messages generation: week rollover. ### Improvements in Maintainability: - The internal communication mechanism based on gr::msg_queue has been replaced by a thread-safe, built-in asynchronous message passing system based on GNU Radio's Polymorphic Types. This change is backwards-compatible and prevents from a failure in case of a possible future deprecation or removal of the gr::msg_queue API. - Deprecated boost::asio::io_service replaced by boost::asio::io_context if Boost > 1.65 - CMake turns all policies to ON according to the running version up to version 3.15. - Usage of clang-tidy integrated into CMake scripts. New option -DENABLE_CLANG_TIDY=ON executes clang-tidy along with compilation. Requires clang compiler. - Applied clang-tidy checks and fixes related to readability: readability-container-size-empty, readability-identifier-naming, readability-inconsistent-declaration-parameter-name, readability-named-parameter, readability-non-const-parameter, readability-string-compare. - Improved includes selection following suggestions by include-what-you-use (see https://include-what-you-use.org/), allowing faster compiles, fewer recompiles and making refactoring easier. - Massive reduction of warnings triggered by clang-tidy checks. - Throughout code cleaning and formatting performed with automated tools in order to reduce future commit noise. ### Improvements in Portability: - Added interfaces for FPGA off-loading in GPS L1 C/A, Galileo E1b/c, GPS L2C, GPS L5 and Galileo E5a receiver chains. - CMake scripts now follow a modern approach (targets and properties) but still work with 2.8.12. - Improvements for macOS users using Homebrew. - The software builds against GNU Radio >= 3.7.3, including 3.8.0. Automatically detected, no user intervention is required. - The volk_gnsssdr library can now be built without requiring Boost if the compiler supports C++17 or higher. - The Boost Filesystem library is not anymore a required dependency in cases where it can be replaced by std::filesystem. Automatically detected, no user intervention is required. - CMake scripts automatically select among C++11, C++14, C++17 or C++20 standards, the most recent as possible, depending on compiler and dependencies versions. - Drawback in portability: Protocol Buffers >= 3.0.0 is a new required dependency. ### Improvements in Reliability - Included the Guidelines Support Library. General improvement of memory management, replacement of raw pointers by containers or smart pointers. - Applied clang-tidy checks and fixes related to High Integrity C++: performance-move-const-arg, modernize-use-auto, modernize-use-equals-default, modernize-use-equals-delete, modernize-use-noexcept, modernize-use-nullptr, cert-dcl21-cpp, misc-new-delete-overloads, cert-dcl58-cpp, cert-err52-cpp, cert-err60-cpp, hicpp-exception-baseclass, hicpp-explicit-conversions. - Fixed a number of defects detected by Coverity Scan (version June 2019). ### Improvements in Usability - The receiver now admits FPGA off-loading, allowing for real time operation in embedded systems at high sampling rates and high number of signals and channels per signal in multiple bands. - Fixed program termination (avoiding hangs and segfaults in some platforms/configurations). - The Labsat_Signal_Source now terminates the receiver's execution when the end of file(s) is reached. It now accepts LabSat 2 filenames and series of LabSat 3 files. - Added configuration parameters to set the annotation rate in KML, GPX, GeoJSON and NMEA outputs, set by default to 1 s. - New parameter PVT.show_local_time_zone displays time in the local time zone. Subject to the proper system configuration of the machine running the software receiver. This feature is not available in old compilers. - CMake now generates a summary of required/optional dependency packages found and enabled/disabled features. This info is also stored in a file called features.log in the building directory. - Improved information provided to the user in case of building configuration and runtime failures. See the definitions of concepts and metrics at https://gnss-sdr.org/design-forces/ ## [0.0.10](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.10) This release has several improvements in different dimensions, addition of new features and bug fixes: ### Improvements in Accuracy: - Part of the RTKLIB core library has been integrated into GNSS-SDR. There is now a single PVT block implementation which makes use of RTKLIB to deliver PVT solutions, including Single and PPP navigation modes. - Fixed CN0 estimation for other correlation times than 1 ms. - Improved computation of tracking parameters and GNSS observables. - Other minor bug fixes. ### Improvements in Availability: - Internal Finite State Machines rewritten for improved continuity in delivering position fixes. This fixes a bug that was stalling the receiver after about six hours of continuous operation. - Redesign of the time counter for enhanced continuity. - Improved flow graph in multi-system configurations: the receiver does not get stalled anymore if no signal is found from the first system. - Improved acquisition and tracking sensitivity. - Added mechanisms for Assisted GNSS, thus shortening the Time-To-First-Fix. Provision of data via XML files or via SUPL v1.0. Documented at https://gnss-sdr.org/docs/sp-blocks/global-parameters/ - Other minor bug fixes. ### Improvements in Efficiency: - Added the possibility of non-blocking acquisition, which works well when using real-time data from an RF front-end. - Improved flow graph in multi-band configurations: satellites acquired in one band are immediately searched in others. - Complex local codes have been replaced by real codes, alleviating the computational burden. - New volk_gnsssdr kernels: volk_gnsssdr_16i_xn_resampler_16i_xn.h, volk_gnsssdr_16ic_16i_rotator_dot_prod_16ic_xn.h, volk_gnsssdr_32f_xn_resampler_32f_xn.h, volk_gnsssdr_32fc_32f_rotator_dot_prod_32fc_xn.h - Some AVX2 implementations added to the volk_gnsssdr library. - Improvement in C++ usage: Use of const container calls when result is immediately converted to a const iterator. Using these members removes an implicit conversion from iterator to const_iterator. - Output printers can be shut down, with some savings in memory and storage requirements. - A number of code optimizations here and there. ### Improvements in Flexibility: - A number of new parameters have been exposed to the configuration system. - Possibility to choose Pilot or Data component for tracking of GPS L5 and Galileo E5a signals. - Enabled extended coherent integration times for signal tracking. - Configurable coherent and/or non-coherent signal acquisition. - Some configuration parameters can now be overridden by commandline flags for easier use in scripts. ### Improvements in Interoperability: - Added the GPS L5 receiver chain. - Added the GLONASS L1 SP receiver chain. - Added the GLONASS L2 SP receiver chain. - Improvements in the Galileo E5a and GPS L2C receiver chains. - Updated list of available GNSS satellites. - Added five more signal sources: "Fmcomms2_Signal_Source" (requires gr-iio), "Plutosdr_Signal Source" (requires gr-iio), "Spir_GSS6450_File_Signal_Source", "Labsat_Signal_Source" and "Custom_UDP_Signal_Source" (requires libpcap). Documented in https://gnss-sdr.org/docs/sp-blocks/signal-source/ - Improved support for BladeRF, HackRF and RTL-SDR front-ends. - Added tools for the interaction with front-ends based on the AD9361 chipset. - Intermediate results are now saved in MAT-file format (.mat), readable from Matlab/Octave and from Python via h5py. - Added the GPX output format. - Improvements in the generation of KML files. - Improvements in the NMEA output. The receiver can produce GPGGA, GPRMC, GPGSA, GPGSV, GAGSA and GAGSV sentences. - Improvements in the RTCM server stability. - Improvements in the correctness of generated RINEX files. - The receiver can read and make use of Galileo almanac XML files published by the European GNSS Service Centre at https://www.gsc-europa.eu/system-status/almanac-data - Own-defined XML schemas for navigation data published at https://github.com/gnss-sdr/gnss-sdr/tree/next/docs/xml-schemas - Added program rinex2assist to convert RINEX navigation files into XML files usable for Assisted GNSS. Only available building from source. See https://github.com/gnss-sdr/gnss-sdr/tree/next/src/utils/rinex2assist ### Improvements in Maintainability: - Setup of a Continuous Integration system that checks building and runs QA code in a wide range of GNU/Linux distributions (Arch Linux, CentOS, Debian, Fedora, OpenSUSE, Ubuntu) and releases. See https://gitlab.com/gnss-sdr/gnss-sdr - Creation of multi-system processing blocks, drastically reducing code duplication and maintainability time. - Automated code formatting with clang-format. This tool is widely available and easy to integrate into many code editors, and it also can be used from the command line. It cuts time spent on adhering to the project's code formatting style. - Improvement in C++ usage: C-style casts have been replaced by C++ casts. C-style casts are difficult to search for. C++ casts provide compile time checking ability and express programmers' intent better, so they are safer and clearer. - Improvement in C++ usage: The override special identifier is now used when overriding a virtual function. This helps the compiler to check for type changes in the base class, making the detection of errors easier. - Improvement in C++ usage: A number of unused includes have been removed. Order of includes set to: local (in-source) headers, then library headers, then system headers. This helps to detect missing includes. - Improvement in C++ usage: Enhanced const correctness. Misuses of those variables are detected by the compiler. - Improved code with clang-tidy and generation of a compile_commands.json file containing the exact compiler calls for all translation units of the project in machine-readable form if clang-tidy is detected. - Applied some style rules to CMake scripts. - Minimal versions of dependencies identified and detected. ### Improvements in Portability: - Several CMake scripts improvements, more verbose outputs in case of errors. Building configuration has been documented in https://gnss-sdr.org/docs/tutorials/configuration-options-building-time/ - Improved SDK for cross-compilation in embedded devices. Documented in https://gnss-sdr.org/docs/tutorials/cross-compiling/ - Improved control over minimum required versions for core dependencies. - The software builds with C++11, C++14 and C++17 standards. - The software can now be built using GCC >= 4.7.2 or LLVM/Clang >= 3.4.0 compilers on GNU/Linux, and with Clang/AppleClang on MacOS. - The Ninja build system can be used in replacement of make. - The volk_gnsssdr library can be built using Python 2.7+ or Python 3.6+. - The volk_gnsssdr library is now ready for AArch64 NEON instructions. - Improved detection of required and optional dependencies in many GNU/Linux distributions and processor architectures. - Improvement in C++ usage: The library has been replaced by the more modern and portable (except for the interaction with RTKLIB). - Improvement in C++ usage: The library has been replaced by the more modern and portable for file handling. - Improvement in C++ usage: C++ libraries preferred over C libraries (e.g., instead of , instead of ). - Fix compatibility with Boost 1.67 (closes Debian bug #911882 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=911882) - Fixes required by Debian packaging. - Fixes required by Macports packaging. - A downside in portability: BLAS and LAPACK libraries are now required even in ARM devices. - A downside in portability: the matio library >= 1.5.3 is a new required dependency. If not found, it is downloaded and built automatically at building time, but this requires libtool, automake and hdf5 already installed in the system. - A downside in portability: the PugiXML library is a new required dependency. If not found, it is downloaded and built automatically at building time. ### Improvements in Reliability: - Introduced 3 new Input Filter implementations for pulsed and narrowband interference mitigation: `Pulse_Blanking_Filter`, `Notch_Filter` and `Notch_Filter_Lite`. Documented in https://gnss-sdr.org/docs/sp-blocks/input-filter/ - Improved flow graph stability. - Introduction of high-integrity C++ practices into the source code and included in the coding style guide. See https://gnss-sdr.org/coding-style/ - Fixed a number of defects detected by Coverity Scan. - Improvement of QA code and addition of a number of new tests. Documented at https://gnss-sdr.org/docs/tutorials/testing-software-receiver-2/ - Improvement in C++ usage: rand() function replaced by library. - Improvement in C++ usage: strlen and strncpy have been replaced by safer C++ counterparts. - Improvement in C++ usage: Some destructors have been fixed, avoiding segmentation faults when exiting the program. - Website switched from http to https. Links in the source tree switched when available. ### Improvements in Reproducibility: - Setup of a Continuous Reproducibility system at GitLab for the automatic reproduction of experiments. The concept was introduced in https://ieeexplore.ieee.org/document/8331069/ Example added in the src/utils/reproducibility/ieee-access18/ folder. - Fixes of Lintian warnings related to build reproducibility. ### Improvements in Scalability: - Improvements in multi-system, multi-band receiver configurations. The receiver now accepts any number of channels and systems in the three available bands. - All possible combinations of signals and integration times are now accepted by the Observables block. ### Improvements in Testability: - Several Unit Tests added. Documentation of testing concepts and available tests at https://gnss-sdr.org/docs/tutorials/testing-software-receiver/ - New extra unit test AcquisitionPerformanceTest checks the performance of Acquisition blocks. - New extra unit test TrackingPullInTest checks acquisition to tracking transition. - New extra unit test HybridObservablesTest checks the generation of observables. - Improved system testing: position_test accepts a wide list of parameters and can be used with external files. - Receiver channels can now be fixed to a given satellite. - Testing integrated in a Continuous Reproducibility system (see above). - Improved CTest support in volk_gnsssdr. ### Improvements in Usability: - All Observables block implementations have been merged into a single implementation for all kinds of GNSS signals, making it easier to configure. - All PVT block implementations have been merged into a single implementation for all kinds of GNSS signals, making it easier to configure. - Misleading parameter name GNSS-SDR.internal_fs_hz has been replaced by GNSS-SDR.internal_fs_sps. The old parameter name is still read. If found, a warning is provided to the user. The old name will be removed in future releases. - Updated and improved online documentation of processing blocks at https://gnss-sdr.org/docs/sp-blocks/ - Improved documentation of required dependency packages in several GNU/Linux distributions. - Dump and output files can now be stored anywhere. - Parameter names with the same role have been harmonized within different block implementations. - Added a changelog, a code of conduct, a contributing guide and a pull-request template in the source tree. - Added colors to the commandline user interface. - Updated manfiles. - Updated examples of configuration files under the conf/ folder. See the definitions of concepts and metrics at https://gnss-sdr.org/design-forces/ ## [0.0.9](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.9) DOI: https://doi.org/10.5281/zenodo.291371 This release has several improvements, addition of new features and bug fixes in many dimensions: ### Improvements in Accuracy: - Major rewriting in the generation of pseudoranges. - Fixed bug in Galileo E5a/I codes. - Fixed bug in Galileo E1 correlator spacing. - Fixed bug that was causing errors in receivers above the troposphere. - Fixed 16-bit complex resampler. - Improved time tracking algorithm. - Added Bancroft's algorithm implementation for PVT initialization. ### Improvements in Availability: - Improved numerical stability of the PVT solution. The infamous bug that was causing apparently random error peaks has finally been fixed. ### Improvements in Efficiency: - VOLK_GNSSSDR: Added NEON,AVX and unaligned protokernels for volk_gnsssdr_32f_index_max_32 kernel. - VOLK_GNSSSDR: Added volk_gnsssdr-config-info to the list of generated executables. ### Improvements in Flexibility: - Added maximum number of dwells in the Tong algorithm. ### Improvements in Interoperability: - Added six new Galileo satellites: FM7, FM10, FM11, FM12, FM13, FM14. - The Hybrid_Observables and Hybrid_PVT implementations can now handle more types of GNSS signals. - The RINEX printer can now print L2C and E5a observables and navigation files, including multiband configurations. - Added RTCM 3.2 output to more receiver configurations. ### Improvements in Maintainability: - The VOLK_GNSSSDR library can now be built with Python 3. Switched dependencies for VOLK_GNSSDR: from (old, python2.7-only) python-cheetah templates to Python3 friendly python-mako and python-six. So, Python-cheetah dependency has been dropped, and python-mako and python-six have been added. - If suitable versions of gflags, glog, armadillo or googletest are not found in the system, they will be downloaded and built at compile time (versions 2.2.0, 0.3.4, 7.600.2 and 1.8.0, respectively). - Fixed more than 30 defects detected by Coverity Scan. - Added CMake Python finder and module checker. - Deleted files related to CPack. - Fixes, updates and improvements in the documentation. - Improvements in CMake scripts: General code cleaning and addition of comments. Improved user information in case of failure. Improved detection of dependencies in more processor architectures (e.g. aarch64). ### Improvements in Marketability: - Reduced time from a commit to deployment (see virtualization mechanisms in Portability). ### Improvements in Portability: - Now GNSS-SDR can be run in virtual environments through snap packages (see https://github.com/carlesfernandez/snapcraft-sandbox) and docker images (see https://github.com/carlesfernandez/docker-gnsssdr). - Now GNSS-SDR is adapted to cross-compiling environments for embedded devices (see https://github.com/carlesfernandez/oe-gnss-sdr-manifest). - BLAS and LAPACK libraries are no longer mandatory on ARM devices. ### Improvements in Scalability: - Fixed bug in acquisition with rata rates higher than 16 Msps in 4ms code periods. ### Improvements in Testability: - Major QA source code refactoring: they has been split into src/tests/unit-tests and src/tests/system-tests folders. They are optionally built with the ENABLE_UNIT_TESTING=ON (unit testing QA code), ENABLE_UNIT_TESTING_EXTRA=ON (unit tests that require extra files downloaded at configure time), ENABLE_SYSTEM_TESTING=ON (system tests, such as measurement of Time-To-First-Fix) and ENABLE_SYSTEM_TESTING_EXTRA=ON (extra system test requiring external tools, automatically downloaded and built at building time) configuration flags. The EXTRA options also download and build a custom software-defined signal generator and version 2.9 of GPSTk, if not already found on the system. Download and local link of version 2.9 can be forced by ENABLE_OWN_GPSTK=ON building configuration flag. Only ENABLE_UNIT_TESTING is set to ON by default. - Unit tests added: CPU_multicorrelator_test and GPU_multicorrelator_test measure computer performance in multicorrelator setups. - Unit tests added: GpsL1CADllPllTracking and GpsL1CATelemetryDecoderTest. - System test added: ttff_gps_l1 performs a set of cold / assisted runs of the software receiver and computes statistics about the obtained Time To First Fix. - System test added: obs_gps_l1_system_test uses an external software-defined signal generator to produce raw digital GNSS signal from a RINEX navigation file and a position (static or dynamic), processes it with GNSS-SDR, and then compares the RINEX observation file produced by the software receiver to that produced by the signal generator. - Software Development Kit provided for embedded devices (see https://gnss-sdr.org/docs/tutorials/cross-compiling/). ### Improvements in Usability: - Now the block factory automatically detects Channel input data type, so it is no longer required to specify Channel.input_type in the configuration. An error raises if Acquisition and Tracking Blocks are not configured with the same input data type. - Block names changed from L2_M to L2C. - Documentation available at https://gnss-sdr.org/docs/ - Improved tools for compilation, execution and testing in embedded devices. See the definitions of concepts and metrics at https://gnss-sdr.org/design-forces/ ## [0.0.8](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.8) DOI: https://doi.org/10.5281/zenodo.57022 This is a maintenance and bug fix release with no relevant new features with respect to v0.0.7. The main changes are: - Fixed a bug that broke building when using latest VOLK release - Updated PYBOMBS instructions - Added Tests for FFT length - Added Tests for CUDA-based tracking - Added Tests for SIMD-based tracking - Improved CUDA-based correlation. - Updated documentation - Fixed building in mips and powerpc architectures. - gr-gn3s and gr-dbfcttc moved to its own repository. - Improved package reproducibility - VOLK_GNSSSDR: Fixed a bug in AVX2 puppet - VOLK_GNSSSDR: can now be built using the C98 standard - VOLK_GNSSSDR: Fixed a bug that broke building when linking to Boost in some configurations. - VOLK_GNSSSDR: Added an option to trigger profiling at building time. - VOLK_GNSSSDR: Fix the CMake-based check for posix_memalign. ## [0.0.7](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.7) DOI: https://doi.org/10.5281/zenodo.51521 This release has several improvements, addition of new features and bug fixes: - Improvements in receiver design: Internal block communication has been redesigned to accommodate the addition of new signals, and now upstream and downstream communication within blocks is implemented through the GNU Radio block’s asynchronous message passing system, leading to a more scalable, more robust and cleaner design. - Improvements in receiver design: Correlators have been rewritten to take full advantage of VOLK and VOLK_GNSSSDR, and they are of general use for any tracking block. Their API now admit an arbitrary number of correlators, spaced in an arbitrary manner, in 16ic and 32fc versions. - Improvements in receiver design: Block adapters are now all managed by smart pointers, ensuring better memory management. - Improvements in processing speed: The VOLK_GNSSSDR library has been rewritten, following current VOLK standards and adding a number of new kernels. This approach addresses both efficiency and portability. Now the library provides the key kernels for GNSS signal processing in 16ic and 32fc versions, including SSE2, SSE3, SSE4.1, AVX, AV2 and NEON implementations. Please execute volk_gnsssdr_profile and volk_profile to use the fastest implementation for your host machine. - New source block: Two_Bit_Packed_File_Signal_Source. This block takes 2 bit samples that have been packed into bytes or shorts as input and generates a byte for each sample. - Fixes in SUPL assistance (supl.nokia.com removed). - Improvements in acquisition: Added a non CFAR PCPS acquisition algorithm based on the estimation of the post correlation noise floor. If enabled as an option in the acquisition configuration, it allows setting more stable thresholds in the presence of non-gaussian front-end noise (which is the usual behavior of front-ends.) - Fixes in acquisition: Fixed mismatch between the config files and the acquisition code in the specification of the IF. Fixed a bug in the length of the FFT of local codes. - Improvements in tracking sensitivity: Added configuration option to customize the extension of the GPS L1 CA correlation length after bit synchronization (options are: [1,2,4,5,10,20] ms). Only available in the GPS_L1_CA_DLL_PLL_C_Aid_Tracking implementation. - New tracking block introduced: GPS_L1_CA_DLL_PLL_C_Aid_Tracking is a GPS L1 C/A carrier PLL and code DLL with optional carrier-aid feedback. It is available in both 32 bits gr_complex input samples and in 16 bits short int complex samples. The gr_complex version has also the capability to extend the coherent correlation period from 1ms to 20ms using telemetry symbol synchronization. - Increased resolution in CN0 estimator internal variables. - Fixed a bug in computation of GPS L1 C/A carrier phase observable. - Fixed a bug in the internal state machine that was blocking the receiver after a few hours of usage. Now the receiver can work continually (tested for more than one week, no known limit). - New tracking block introduced: GPS_L1_CA_DLL_PLL_Tracking_GPU is a GPS L1 C/A carrier PLL and code DLL that uses the CUDA-compatible GPU to compute carrier wipe off and correlation operations, alleviating the CPU load. - Obsolete/buggy blocks removed: GPS_L1_CA_DLL_FLL_PLL_Tracking, GPS_L1_CA_DLL_PLL_Optim_Tracking. - Added a RTCM printer and TCP server in PVT blocks (still experimental). The receiver is now able to stream data in real time, serving RTCM 3.2 messages to multiple clients. For instance, it can act as a Ntrip Source feeding a Ntrip Server, or to be used as data input in RTKLIB, obtaining Precise Point Positioning fixes in real-time. The TCP port, Station ID, and rate of MT1019/MT1045 and MSM can be configured. GPS_L1_CA_PVT serves MT1019 (GPS Ephemeris) and MSM7 (MT1077, full GPS pseudoranges, phase ranges, phase range rates and CNR - high resolution) messages, while GALILEO_E1_PVT serves MT1045 (Galileo ephemeris) and MSM7 (MT1097, full Galileo pseudoranges, phase ranges, phase range rates and CNR - high resolution). - Added a GeoJSON printer. Basic (least-squares) position fixes can be now also stored in this format, in addition to KML. - Obsolete block removed: output filter. - QA code migrated to the new asynchronous message passing system. - Improvements in documentation: update of README.md file, addition of documentation for the VOLK_GNSSSDR library, updated links to new ICDs. - Improvements in documentation: Satellite identification updated to current constellation status. - Updated and cleaner console output. Now Galileo satellites have the ‘E’ identifier in their PRN number. - Several improvements in CMake scripts allow to build GNSS-SDR in Linux Debian (Jessie, Stretch and Sid), Ubuntu (from 12.04 to 16.04), including amd64, i386, armhf and arm64 architectures, and possibly in other GNU/Linux distributions, as well as in Mac OS X 10.9 to 10.11. It also works well with CMake 3.5 (some problems solved with VOLK_GNSSSDR as a sub-project). - The software can link either against OpenSSL or against GnuTLS with openssl extensions, whatever it is available. This allows buildings in distributions such as Fedora or ArchLinux, while being compatible with binary distribution through Debian packages. - Fixed a number of defects detected by Coverity Scan. - Some fixes required by Debian licensing and packaging system. - Added a CGRAN (http://www.cgran.org/) manifest - Lots of code cleaning and fixes of typos and small bugs. ## [0.0.6](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.6) This release has several improvements and bug fixes: - Added initial support to multi-band, multi-source configurations (multiple signal sources and signal conditioners). - Updated configuration files to new notation. Old and new configuration notations still compatible. - Added skeleton for mixed (multi-frequency and multi-system) observables block. - Faster local carrier update (25% of improvement). - Added initial support to GPS L2C real time tracking and decoding of CNAV message with NSL STEREO v2, Fraunhofer’s Flexiband, and USRPx front-ends (the latter requiring external clock). - Added initial support to select the frontend clock reference source in UHD signal source (i.e. internal or external clock reference). - Added 2 bits complex file source for GNSS-SDR GSoC 2015 signal sampler designed by Ajith Peter. - Added a new rtl_tcp signal source, remote access to RTL2832U-based dongles via TCP. - Always build front-end-cal, a calibration tool for some DVB-T receivers based on the Realtek's RTL2832U chipset. - Fixed bug in UTC time computation for GPS signals. - Updated satellite identification for GPS and Galileo. - Defined ‘cbyte’ as a new input data type (std::complex) - Adding a new data_type_adapter, from interleaved short to std::complex - Adding a filter for complex short streams. - Adding a fir_filter for std::complex (aka cbyte). It converts the data type to floats, filters, and converts back to cbyte. - Added a resampler for cbytes and cshorts. - First working version of a GPS tracking block implementation using CUDA with multi-GPU device support. - Updating RINEX obs header when leap second is available. - Updating RINEX nav file when IONO and UTC data are available. - Include Signal Strength Indicator in RINEX observable files. - Tests fixed. - Fixed more than 200 code defects detected by Coverity Scan. - Updated documentation. - Updated documentation and CMake scripts for the GN3S v2 driver (Linux-only) - Armadillo version automatically downloaded and built if it is not present in the system is now 5.400.3. - Updated old links from googlecode to new links at GitHub for Google Test, gflags, glog and gperftools. - gfortran is no longer a required package, but it is used if available. - Added an option to remove logging. - Enabled cross-compilation for ARM devices. - Lots of code cleaning. ## [0.0.5](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.5) This release has several improvements and bug fixes: - Now GNSS-SDR can be installed on the system with the usual ‘cmake ../ && make && sudo make install’. - Added volk_gnsssdr library, a volk-like library implementing some specific kernels and ensuring portable executables. It comes with a ‘volk_gnsssdr_profile’ executable, in the fashion of volk_profile. Volk and volk_gnsssdr are compatible and can be mixed together. This is expected to enable faster execution of the software receiver in upcoming versions. - The former ‘rtlsdr_signal_source’ has been replaced by a more general ‘osmosdr_signal_source’ compatible with all those front-ends accessible by the OsmoSDR driver (bladeRF, hackRF, etc.) in addition to RTL-based dongles. - Added manpages when binaries gnss-sdr, volk_gnsssdr_profile and front-end-cal are installed. - Now GNSS-SDR can be built on i386, amd64, armhf, armel and arm64 architectures. - Now GNSS-SDR builds on Ubuntu 14.04 and 14.10, Debian jessie/sid and Mac OS X 10.9 and 10.10. - Improved detection of dependencies, specially when installed as .deb packages. - Added a ‘check' target with some minimal tests. - Added support for interleaved I/Q byte-size sample files. - Minor bug fixes, updated documentation and code cleaning. ## [0.0.4](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.4) This release has several improvements and bug fixes: - Added hybrid processing GPS L1 C/A and Galileo E1B, providing position fixes make use of observables for both constellations. - Added implementations of the QuickSync algorithm for GPS L1 C/A and Galileo E1 acquisition. - Added processing blocks for Galileo E5a: Acquisition, Tracking, Telemetry_Decoder (experimental) - New configuration files allow to configure GPS and Galileo channels in the same receiver. - Added tropospheric corrections to GPS and Galileo PVT solution. - Improved precision obtained by changing some variables from float to double. - New building options: ENABLE_GN3S, ENABLE_RTLSDR and ENABLE_ARRAY and ENABLE_OPENCL. - Improved documentation on how to enable optional drivers. - Fixed bug in memory alignment that caused problems with high data rates. - Added ENABLE_GENERIC_ARCH, an option to build the binary without detecting the SIMD instruction set present in the compiling machine, so it can be executed in other machines without those specific sets. - Added ENABLE_GPERFTOOLS, which links the executable to tcmalloc and profiler if Gperftools is available on the system. - Added carrier phase, Doppler shift and signal strength observables to the RINEX files. Static PPP solutions are available for GPS with RTKLIB via RINEX files. - The executable now produces RINEX files version 3.02 of Galileo Observables, Navigation data, and mixed (GPS/Galileo) observables and nav data. RINEX 3.02 is the default version of RINEX files. - Armadillo version updated to 4.400.2 - Armadillo now uses OpenBLAS instead of BLAS if the former is available on the system. - Some raw pointers have been changed to smart pointers. - Minor bug fixes and code cleaning. ## [0.0.3](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.3) This release has several improvements and bug fixes, completing the transition from Subversion to Git. The main changes are: - Created some missing directories lost in the SVN to Git transition. - New C++11-ized block factory, flow graph and tests, resulting in better memory management and fewer segmentation faults. Several raw pointers converted to smart pointers. - Reorganization of assistance data input and output. - Fixed memory leak when talking to SUPL servers. - Improved retrieval of assistance data. - Fixing an error in a constant value related to Galileo. - Inform users if the temporal folder is not /tmp. - Fixes and additions to the documentation. - README in markdown language so it looks better in Git repositories. - Fixed a bug that prevented the update of all shared map structures (ephemeris, iono parameters, etc…). - The configuration script now throws error if GCC is older than 4.7 or Boost is older than 1.45 - Improved detection / downloading & building if missing of Gflags and Glog. - Improved detection / downloading & building if missing of Armadillo and related dependencies. - Fixes many warnings that appeared when using CMake 3.0. - Improved detection of GTEST_DIR variable. - Include header files in libraries so IDEs such as Xcode can display them. Enjoy it! docs/doxygen/000077500000000000000000000000001352176506000134705ustar00rootroot00000000000000docs/doxygen/Doxyfile.in000066400000000000000000002271761352176506000156220ustar00rootroot00000000000000# Doxyfile 1.8.10 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "GNSS-SDR" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "An Open Source GNSS Software Defined Receiver" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = @top_srcdir@/docs/doxygen/images/gnss-sdr_logo_round.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = @top_builddir@/docs # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = YES # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitiHTalizer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. #SHOW_DIRECTORIES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @top_srcdir@/src @top_srcdir@/docs/doxygen/other # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See https://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.h \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = @top_srcdir@/docs/html \ @top_srcdir@/docs/latex \ @top_srcdir@/cmake \ @top_srcdir@/data \ @top_srcdir@/src/core/libs/supl \ @top_srcdir@/src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr \ # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = @top_srcdir@/docs/doxygen/images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see https://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see https://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. # HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.gnss-sdr.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.gnss-sdr.CTTC # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = CTTC # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.gnss-sdr.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [0,1..20]) # that doxygen will group on one line in the generated HTML documentation. # Note that a value of 0 will completely suppress the enum values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. # USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see https://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = @GNSSSDR_USE_MATHJAX@ # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # https://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = https://cdnjs.com/libraries/mathjax/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. # The default file is: searchdata.xml. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of # to a relative location where the documentation can be found. The format is: # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = @GENERATE_PDF_DOCUMENTATION@ # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = amsmath,amssymb,amsfonts # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last # chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what # special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created # by doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = @HAVE_DOT@ # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, svg, gif or svg. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = @DOXYGEN_DOT_PATH@ # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES docs/doxygen/images/000077500000000000000000000000001352176506000147355ustar00rootroot00000000000000docs/doxygen/images/ClassHierarchy.png000066400000000000000000003534701352176506000203630ustar00rootroot00000000000000PNG  IHDR dj AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYsgR9iTXtXML:com.adobe.xmp 150 150 5 1 2 H @IDATxE'^ʥJ*I$@(1E `Pl||*6@E}`A@@)K@RH~wio>rw2Ww3;3p@ @ @ T+ "@ @ C @ @ @ PaRaPGF@ @ @  H@ @ @ 00#@ @ @ $@ @ @ TTԑQ @ @ } @ @ * `@* (@ @ C /N*@VZM@ PRLPc+&P,@ @ (`@' f$$"Tgg0<(u;.o$NP줿?Q/I2/fJd8Ʒt4?ԷsP&]@ P6bۿ;V&L uԱw%%&* GJh{ F63&p4x]瑈n4|N[ Qj`삨,x@>w^ dĈ6@ `@@Kڵk'#G|#H@zujK)VbW8Cޙթ][t#ǥA:]y=h!~IP#ߑ.(NP??lqn,r##$OxޟWϤ_z+*Lzԭ xW<͸VO5jH~iydРAҼy3ճɣV@ P sNٷo|N;1"HUEv#{~7sD5 '};mFՓ|eނI/G *KJ:4ↇRp7WA}e׮]ƀԠGU@ 6@UsEPڽ{}*%Wll-K ed6ҵucPNf.=j`v3g\X+vZ-ȄdO^{~u*|ӻ\>4WK~RtF&,,glf-֖_[.iu$%[7+:4-(ӓ-\f.E??ݯwxuԛ\mwٻr-ehU֖2muΫJZ*ZE:alcT;֞1g~yszڶ[zm$F#y8H4H~8pW1HgB5*@H@h4'ZQ®[`%fszU*1zFЦ\svwi߫}4cIVmi\RrdN6ѷ4wcu 6U0r`{ˣwf1sԯWTn-}5:V.-]fLKzA!';sKfF]ij] 6C μU}ez֪ SiI&񲺽l,ݸKՕ[~:V& RC 2-yGLScˆfYe Ll%qR,,5%nwJfzrJҹu٥e{o&iٴImxȺ{%Sݧ+oiZޝUmbN~[UBC%&'mS4{2ۤ0Cwz gk@ @H@f>dW HO7\#`UFCSU0ޕ_NhHw͐K T0Bo*HONZ&٪j'47Ϊ5C'Y.Ӕ ߸msb;9wh>HDf,,oY'3Vn5v-;i=gV˄jOfԗo''g`\{YUqj>vV(`sfw\o2L5I}b p6cu]cZy */^iR$oj?&eɘYk#}$˕xxJc:h $/`YMX. iP<ya*SSGV,b 4խC+F@ H! 5@RUpp#%`qwWf54VIɺ>k #(~kLȘ[հ Vȅj#1|PSqKI}OlX.j3e6cXv)#q>%u4-ӭثd٤EjC_uzWP&ᢡXn'~d麝SZfd&4U)YԺ*J>EN0{rSJ|9J|1L7e^p-Zku a< L`\ baap \\{ޠjU/N[mҩ۶ùf!ҷ]c>RSyPk|2s3LŹ{=qP7%[ջTd5ƸtW){4-c@4 @ ʁ@0 nP:4N٪ v exVf_-fTt|`ז2]Y--AzãWHVA)']6vتLoM۠+BxխmU&/oҰ15Pw9P ul@cEH 24>]*IQI n-/W9†!CkyL,Ļ,EjXwj >!JN\ @| T}*eZTiLfe`a* ֺCxuJpHz .mUhܐTb@ @0 i_QTA<*l_<}*yP wonARƩCŤ{X:W&jx~2)s%j?BJr45\٧RFXU>:mЈGԞBsf#Y*D62fr髥2(3cNتavٹ}CeNVfxM_tStVդהwV~٣Zk9A& *endw#*31OM8A:/߾WլJ_U֩ٚtRI:*VQ;L5>ߢ` MaB` {N-VN*״W\569'Wi7af9g 鑷3KVS%5J> @ @0 nX8+V@xѢHǗUwzEUP3'SNa6EUZ趹I /)ոu-ܥzE&u /VޙR7U}fvN>-~s@25.e`_:6H20;I՚Z7D)5~WCF=9g@[P5,'On/mgVlv"ʙhTK+eұy9CeP%gtnbe&=,3vNצuB;KjSK.Yj[򊖵;lnsɐ m#nf2'#eo#"/%t.DfLooOu@w u:@ Pyup!4`[o !ٮΔCmj ۉ p*`0c6ɌJ5<23 غ8V5mWi6(%[~05(x{SLEwd>q.n4<#Pzm[XtJ7i>j !)FZ1A ]7JPҥw% s0% O#솅Qcqd>H/F8`nS6҃r6 #Fe12j@F "79!'e…&)"ZxUQLčPrYaҦwRIA)?U':qH'1f{8X9.BϏ򤧛Itt=0 L@ dR^J(j@D ;mUƍeݺu@<ٰalicrHH3G b` -M76 +@ P^ 4H~̠t@ ,&j[1`@ 1l @H@6vuAmx}Lڈ%@ 8Fs֭[9n tF@ E "v0 |󫿏k  _I{}Aǫܥɗv@e@ b5p@ TFKW~ǐ"ǯu+L^] @G FL@ @3NY;S1\NNݹ$77מ=k @ Pj[1@ @ PbΝo˚5kl ڵk_~2gѣ_^{=:ter)մbf @ qR<*g>ٳe̙2d6mmVqg?+K,1cҧOcHx%{)SHΝ%333^Pיdz+i>@! HkӨQ 0۷o^ve>2p@{Gl@lbI >\7nl~%e|g=%'3) @ Pf>lKm6A ;:g!3ȨQ5k`1l0@ XxTp.pH KK2S}vKI&R~}cn?ϣ} @D ٮQ@ A4jիWK-cǎҿ-+VDx7uTc `7yd1b4hvJ !,$av-ZdLMҼyskرu 'Hvdf{cKg<@ To j@)@C390xS.]*؁\tEҥK3HG-ꫯ6`Btiŝ_ l2ygO|#&y!O>ii^s5V^z~G夓Nʗ$"p@ `@G @#S6bƌպukxwyVHA6ol $aL¤I )£>իW/9E>}}r-fBDyn3wޓYg%ƍ;C>Ʌ^hd @ Pq0)N߿_&N('0 0V]wuѣG/~ >&H>Ν+_|K_nݺRvz哟IRH_IS0x'mIӟdiD+~_X@ P7qT0#0n U`,vb_|Qع \rl4Jx!8$imݺU^)a{_k- x Cɷ-9󍁹̏Q ñ֭*F]@ ,b?ĚQ@ F?gNZ*U~řZ2( d, QM68-%##Ø>@[ICvj&0@~ș$~:{sY.@# Hqr52dI'} E!KӦM-Luϟz)@ u={?00'{Zk *Dk[O\|> [ R$.-* y׬Y3ٽ{w;!wKAw@ `@]}e} >Q'{vAf=1*oL6؋@1``' 6q׿w}WN=Tԧ>%&Lʼn}k4`Ƨojmx`dD*\jFifXY0??CݕIG-A 岇@ P K/5dr0 fرCG^uӳ/&$BqGC 9f̟?ߌQB cs$8TrrrTPv!H3N:: LJ>!rJiٲ`;ؖ4=;.!Ŏ (a =ğ@ j٧ZW|*&ʠ+y9crWA >=~\@ ()[>H?`0(:tg1)L?o q! &C:Aھ/|`5jٛdeeÔM/'/HE`ZH"@  )v.(=YWE$@ G؅ _xꧫ7!8@60*pyncꥨP^xoc:dg]Ҙ9sI@.;j`Gr 7cG Q@  @ @W@ T%G/C IFAXK: c 3vlvښ;vone4QSDU@ @0 )@ P81_&+;ka;n`Ď;>@ jԜ@ Pf8 :<[#F@ Pf>R#L 8QXbɰ)= @ `@*E @ 3qq4?* UWثⰎ@ HG$,q9@"b/a1@ @ >"e2J: @ 8R Йŷd,wub+@ PHPc  `@٢wUV *\L/@B )(qĬ,ٻwo3*@uC "Z^zv ' }2M@ j"/cm=p@e~cL2*t$y뭷kԓ} U `@`jިQ#G>"uVINDgdtyğ@ jp ?_ads91LСCe3q!F5@ r&]MtݻwMΝ;mLNzLLL99!~)9J]  0Hb^(PI:!D{U 5kdܹf7c&My6"_zgRuǟxí[tb'y6*@0 Gi,&<&N&D&R? | ɞ={dƍұcGx$E /F/jdED rA;% rɴ% .-1l} 8S~uȐҥKڵ,[-[XکS'iӦ0-ZVZK b.ƅ7[IT5j@-4RFNT`,p=]䡇Uo}[, ÉlcP L#%0۷oLK!ڡ@%(6mU~q좪w c"S21;l3TY6ôtW)37_,m۶0{ &@ !)F1c DO܌3'?1v4 z֧yF(^veNW7BrLAV5k&_qU ^zʅ^hcg U*~8unw@ !.As2!:h"wc+ur 0&%KXLJ\W^xA^|EG $L"֭7xyYRKa/ĸ2n8ѣ󱤪γ۴:?ۯ|+f?_,2WM `@ْ qڴiocPaJK?^{5[ F_t'1K>#=\93Mmr.q!QY^+}nM6lPi 2l|av`~~3cp@ `@ѶI=ѣG}`*[lNMW:(;v\~Pϗ~)H-I,A_0a~69LZ `($yvbW=veBI[_Uq,Vdׁ{?|A~`^џ@ P=ve fE&?O_ZX%%#ނ W+qXW#F0̋3ɓ3ײE/>j{a`X-y,RTck`@N8cnI"[UdB`@8u2{o=;cR[>j@F BڟI Zͅin#Hy͛7d`4ISѼoa,&~8v%4hIxvpCm<nݺGg?nF9ScBq#T@Ϟ=]qbx u}~;:ujRJT(J @" H!`20?;o(39J:Nee KBpgyF.";ČIVv(A8Al/O ޮj#!zsb'?)cnjyɸ2eL'8/_b{fg(>T8eU9믿nQ{O~_Y}/6ԍ 'pǪB'@DNA(j LɏA!8@0Ct%$9Az*=-~c`cXb 3qDgIJw$gϞ-9}1wwy4bɇMr0)Ix򗿴q u:F?a;m;|  ;gq-0`w]2 oec+@Ƅdƒq?&Gt?q(j0*uY6V]ƀV: p>x K #y%ȑ#VݑxZEb#~Tpz5g$a5$D"O>|߰t=yXX]=|ABa6w\c<X@ )+fcTetq]F%؅Џ\/?,aj9 xΰfeoel(S `@h(qHEXy`6V|Qς`ðf|$'FX/'@q?nKHܞBp3,=l;OƂZG A6 3mCHJt9zP&dVXag!Xx10%0'%qE?'c jXlfsA8WQʨtY?.sG-@ TMjDVl;' ػ-yYAZ`3fIJ pO> [p}1F漇H9 ˆ#,D ¬CxM idy睶+?NH^#j۶mIȦ_7r$z;wi0u}~} 2' 0b^]6AK),x@ P9 H1%9aÙ Pwg*'] /{y^T.P_+̱ќyp_80JT۶mL2l0^ S;[ԲώP= Fp…  86*uG+;bs=gjvaFEMQ@ )fJv_c"1)rZ4pCh*W^pq-8ZXwbSБ(cquי4Ԭ&oڴi7L8X{W=6esO|T0sًC[yW(@ hRh^y&8'8U[D'32pG?g<ߤ8_};v{o8y$^jp` o8>++v5>ovN[Ds* B]Fajjws^ȍ7h6wַ\zLp- x_ cC I[Jpl'9ٳE&+EFj HMš2"W+Cc` YAR$ m0$"@xs¸k2f5p@ PmĎ4~z'JYu2K0*H@YHĕ*Tlwn6l'CV0h~ [n3q;V #$!)}*=/̯54D%_!*.dE] TDga~ܱ~06) .1߮;+_|fk^!d'v ˷7u p`h/| NYvZdU+Ӓ1>O<L3JKi[N+n&qt•ڀ9fpxl*} `\mx>~0?C]x1"Ty!}% \^E~Yhwy6Y'Ǧ 5/ҏ~||_}׶ 4'M$rNVG #!/ε+O H15@U,!XsK2 T΀C!؁<©Ċ`|m,ZqWأa+\ %CEvκ۾}{""tȫl8tP p"p8 ˉ0t=[_2 Ipx7ME0<>0.1էvU&SN5TAV˖-_]6nX :=m;̙3mn*qB0XaHRtn͛']Aݹ?ǵlHD8eSVRt)N%M'Ǘk׮xD֑1V0NkѢpW7nEHܪ 4mp}" ͥVWm Q,9FWP6@y|ld3ϔ|~q_t{p`]IEug~Qw" 2N0xҼwP+#NDZD:kOsg`qKw]U;.G"a[n ;0UիWwř<;vw[Az{6,i׮4nX֮]k*|{6q,q⋗|X}^8|pi޼mO'[je[t#ko@1޿ڿݡsЙ b5#X Ž|"H2 we@rb!l6mT6WT ɮ.{̚55kXa1Q@H؄&Æ SO=UI~̨G~r7ۆHƌcF6rqر @_YH} HMY @ԎD\!hTL V.`@*|ԩDZ2JRrr;y4Sg̘c,pT ||'8l]m608F`@2׼?3MAX 4Fn0vg>;K/Mǒ  %\"7p 0a<#c] w!?|SO uz2zhQ?CE4 VMSAë1rI3 a0TP;?PtLG NJ`lo[.b#xQ>uV+ҾB ;?aL V0۶m|#~կ~U^xSo*ܗ%D$G}YyYtQLkŊv4e#{vy!T:BRDHg LED r@rH:,t *'mҷsΊɼKآO?$aA j"w+&_q& |$#C#d0cC󸧉3 q͵|@VC].bm;S8e W9$]LۺرcG 9 zdl Av H!k{ʕ+󷭤-$+Md!!\֕x7RHB`BX`:P" >WßxmT`&\tO?я䢋.K6mdfֹk*# 馛o|r>vZR! H!Gon j!Qu9! H9[d!|*eäϷ}L% f j~駟n眠s&$-!R?l;_/y #yX67Fma 8?F!u?i*\/;Zw FW1JUP@JŰƈXTP@EyaRHsQ[:4Ah>pc Ha^)JŐ}ȑ#mL|r2^s5FԠsLP0pU͝룲*ywW.]M0l [a` pHQxf:9j?`LtstI']@>Fuj6(oX)at Q+Vq* J*GHμS曈8~$*ׯ7uƦX )}{8C͕w8G|[2t0PCEFve廀 W4=_r~'fH?o'q}(ğx ~,g=z9<Ƶh saQtRv0r Eyg. hcm~1D2DE |"/,j\RI|;ǘ;</\Aۃկ~eCfG-ma{Ԡe<8U e]fKCedo˖u":v-j0goZG'GwI~KO"ğBp`BVeIΞ=;,"@B`B-B+U3=-ory Jx{$'[;A޽{w;}sBH0UշԴ 'v+x[pYQ]I$*NEw~VsA~U@IDAT]),yUd9=~OpPV$ ]TAkY@G,̈LEp*(Ȧ,r~8*/BIQe>c W ] ~g[|'')#cE5!Nc@ (|C0"e0 im4N$qxh Cj|Ip` 3'Ƿd#wı [E7M[]P0aRڛq8q!s_'? 発qdb}zj^`ppx8KBdFǥOx~J۶Z)j%ҶCaO8zw>6{gO.>.'&yOx9 9 {X{?=a>= ~}ȗڵkeJ#?85Xcng|]Pp#{Ogff7~1C`s`@b$6$ ,%9#rb2fEo4#u{8cX>1Q rB>s? fe2[Dzb""]|+lFOIU*#t8CH::x]maK mPTu줌0ǧԡiPrM,Ѹg飪Y؄4l.Q&SefJm Z[*J ƘWEZIٌpu&6" T SOekT?QFҰaC#Ɲ~ ‰W0Ǣ E@*ӟԌc\.xfAG)6hN2lɾO{&x뭷vHz0R =:w,ЇIrGah2!aL_y iLS@K;9^q9 WCC1 D(T .{XǷd:L8ώoIIRTDPٰagEY/0+ChD]`| T(c'*TƎV;dMdJBvJD&Ȋ)U+4Sc*XizWVtyߚ2OMd lI֭R) %j2+Ř`ڋ"k|TP-ҏ{G*g3O`x5}^xԨ/_.O=`T8\lٲE^{5SB +g 5.(*@u )CĠ#@}) d؍BNs0=xNvYgYX+ϛo8l1vX뮻l"d؈epfv\❒g"Ps5| %I+ +V܇X-ȕV3ѥÓZŖf^z/~ Gė,*QmaBpO]ՔålWUND @ U*WA]+R CFse<,h:O_4PFiF)+\%(J5?Ocnً199Wu0$]t1Bk_#[LRF-tj0@9 F3@#Er1/0FH'p;[[6_M> Q3#=ҡ>^?iS^)R .pt z9XlƀeuVhb!흳"r* &qp&&#[6bJƯpxcoZԲ83h v*]E74pǷ0tsuDJ%谗AM/险6kSW `|+t?/O^6n<;33AscnРA/PoO:6LjIuCJB91aPtưF ##Lz=)j8#Į`[0r7?@f@͚5VC_DdT(8pF{T``sɟh+AjLA@\ve~b0+[*Qy^"qq+~a S Gv &.% 0:&?"! =zHPEqɆ{xI:09\5j+2ys ̯:d}F> $*dYa]Uڎv6➁Wڧva`L [ ڹV(7aYѢp*_I۠+[-p|H>{9;_(kI]au>e'*"<ن~WwnMeio߲n'O^nw__#߻?sMwdU{<]O{A11?!֫⦆H!a`.v&~izL+N''t1HF.?>+R1 աcb@`Ύv ma}1:mIeo6H@0~T7mXǭd?A;s0E-&qF w^Jz7K~t<w#.=@O:WM}A z ǽiwV<%Xd/gv$H6|Wt9R(.~(FTItM,ʈe;v_+adZes Qj3MY _a L;$bw0}˕}?mv"-)*k`pZD󶣟v3ÕtGa1e<`6ǒc~AƱqKO|c2QtmUQ}|x,gaL/|<9%f@&0DG4.v v ϥ/|HDO=#RRD Je㘰 12Ø&4e0 3U߿ē UyN=8!4oHz[PWXyR%˗WNj';Oz#ǒvdh6 ׊xlkwLs,82{C;&lJc]vk;.0y2]*a8NYSz^\;/beY-  A#\y#Pjq &032#:B6{PxWZ0KRY j\]C,cρBgL SP/bKĒ8MJSIMZh ?&\k O_!}NDZǟI<ُ JX138a 7`{C? jX'3A$ Xwt8 xY[A;909F\,h(,Z{_RVT WM9qd%S|eBFȩ۬_NS?;(ޟ0r%q1?SAaK̀P?YcbgbrfRfHF@`ޱ'wtXMvv-e@=}Tݔ$kn9oP{iޤ~Nɕ.%RYqe{OVGWrsu9x*=.J,O,ɜ+"6?*i\y>{rsҍdd6ҧSztܓ'1\݁=S֖]9{)YҴa]NRO%b0N2 ZVn#WEկ+iId,֣֧M~MeNI#,-9X=Foq33 SmmMj&+,OO/̵y^i=}aVM-/=mQ9+l7Rۯ3E;;Kf -/y8޼K2b|o ;IY0pA2J$ǺLv+)k5mPWk[wf I:sJudv:&\p; y:ﯖM:N􁔺c-x,o|~33Rߔi^}OT,9yl/Noyi_[~`|Ri.#^:S3G|ې#cnvKg #@g؟I ]m?-j>m7FOk"u h@o5c2MZ%i)R⸁ز)ʪ*+壐߼See%b/"ϑ8;#6^6^z/~Xse=2]cPuVq}ժ '^-ɖ3խ{hE-I<$a= S*č{cv|G8N\ K}šIz '3m^fWP<#/#B8+f!ԓ퍪 %üO/!C}5=U3\vjJE*=wT/=-ϯ+qƱ)(\qߑGTxEkE[^-{?eԹۤ{E| !/墾|_|'?u4-vrmwPVpm9ƠyPɟޗ{ߑvuvhIA率n D=/a@_΢ay`ʥfay]=j4o`Ȥ k#7)}JUTZM[HyQޤzU?ܹUjM2ews%å]_K̀0`_w l6+GtU=G2vL^V3ՕL]_alޣX"'uo!mtGdmJvɦ溺Zߴ#WzwNZCUY˷H3%R_nSwp bm2ov#tn&[7q=pT*_2|[v]ҳ,޸[6jWB69Yْ @MR +Knٺ*zNU_:[nZ.u\ub;Ò~ 0n@c|?z/ D-h$(,{m"6zݩmem-Q)r͗oq2{UjTޭ1omm;QӣLHɏھ.V);vc||jH[ޭ✍2X jdT]~=SڷwվZ*U,/kniXTG6ȑE@xC%`T5S 1[&۾{Ő:>-ݰKkKzBBJc**ۤc/eݬE}CV3oOC_m9\/iLp-<;s[e*-!%r+w+.GDNzԩ0 z<ƎӦɕ:Ks>#}|aw+'.\USS[x}ګ/~Ez51㉟IS`}UۧPr| R]B/?dӥQߐ~Fڪ'`ԺT'bNMzUec?$c8E\n li!v^Oiu NFWw'/lj7/|A}o1{RYnIѩkMU­ҴNUӿDާؽDJ\5B}6}~5q"li˶:"`fិ]po(;-' ]+A0k}~=01KUc -\!3ǭi;%[Qsq謕18 c3o0h(&e)]yL7oiT b6rJyN۠ΣSjؕ}=6daB~Mh/`?1}BtW2|OC?W&Qlg*CY/+^mIYm3~tfmwz b'yw5#龪Su }s:`WB۲ڎ뙺xW _Go`**|:7=+傉zhjiPp)(Wuv^gfSKZuyyiUQ&wu_jD*_Sɶ=uԅ'굁HކHߙ2y/ys稴ʒ?.yk͘A޻Rޱs,\N_ץK7oNgg˴)St6Wd@Ar)ʄlZnnҢ8[o)ZgsUYPѸݴRzjcY{wOjaDJ,)e3mS@$8&R&ql=!Jߣ3{gJ]j2iL\MWrt|o+]]l!:D:"e+QuzrU[t5BO3/=mH0D(꠪1\7MLviX &:&'̄˧1w֮Pj#rqՇƩl׶]h a0x)ajgh.8M_&c@æUD ?eղ^㑧i%OR+b8u2e 'TvUiը~ ͖xBxnܶ[\]nIZ& 0c5T'j?g3|*~?,$PŪZEwj*W;-5ZsI+`i8EVB/__&S>،`6if}İ]2^ҋTVUX$&~4fe U7jp9k@[(j?[l" ^h`}`l٠*u>P;u7+ XxL @LCީ~s3\&:W(юjqWH}B{uR),%ki#"ӱ6jthr\6I.$JTxdԓAm*筗Gt^aIYKLaLRDkJ"W*֟voܘ%S~uf|.&~,yef(մfl-keRZF *SYsTV뗿DN7aUO>v0B U0X3qA siXWhyLB$ /$<]{I86 xAyђ*Se4dlÒzuutqJTOU:uv)}MjeTZj/Ude:sE߯.H(OBj;].zRjOZ=Gqs=(PZVI*ei[nq:l)XTY!tD'S&NÐz j `ue.X(=PQ-֗:%au\zkZ>ʪVQRfd q^WɸJ U5u%&MJ,VDgxvJUJlmT η1C&)谱*̵%~C+gusMX:|\z[onm(4:mXt5 .R=eЙqFI{KԱ)vl\HݼsX%# R S=up2>(n> erq2eҞϟ;~]86S Ba[|Q~rzOaSͼZPf/- ᆰF Yգ\iE iec+ڞ1*$`aT >8 {esB-UYh~"=bp?Dfh׋qkBe/eDYM ->-Ƨ{8p!,#Vf.01 L, pi%L;-r!4aDqK` 8mLkM`⺿4gUВ_&KU_9 !hǿPxxZwŨ{ KAw~0tɝwi1$ 4fW1\a19_&r~c3-)uoON*&)CiCóh<-ڳ1 uwu!pz'>ySu +dkC0,P(h/Upu^( ti>uQ.R !1e !ig41dG18dg,sB',@,\i &qt"\cb_%)|Ph?}5o ^zCL[̂j7\;ߎkSWgqi0 .x :O)Jd@&hBfxa,*<,W_YX~־Og YW1WjԓctVA&FS2DՑXdu[) 5LѐJˮ@ H9F0@@dZ|6gB2Y/ aG%k!ݻ%GXfuк޾mm0h{1ҠHn;E^r -Jkf^@#-f94PuE7= Zj'XpCƍwXS kwV#YYX4d#11ch -o#kj=@3.ńY Y՗\He5g~:|k_ zKxg;zT1Ǐ O=~J]h>rbޝ Ew~2BE&! Na(sh"b<+e?+ƄTtv8Yr7mе1CBQZI/{C>9d}% [cn:9β< 5eZVA :Ĥ~3b~4kw{b\[^Q=JÔ]C\`$f2r (dPZLZE shGh 1`R Wiu ҋg)⬑=5bk-.c" ֎b`^k!+Sw6I( L D4bq)S>0lQo4U5)#zj9^#KԾ{eUŕk_Z`j0(Ѻoj@{oP,! յO)03._Xy+' T?vt1ʰhϢх.`؆-hq)B9kkJRg Cff?sƦ k[L=;_D/9qD/s0Xn=$ps@ꗎ[ʀ cWb>ĴYPêrjKOJV+Q=TY2]&@L>ۍUGU.UbW$zUs$MU_, lB! i|bynԎە;?,k% S?`ʪycd)!K ; G%KQgBG \Gtn/Ч^̓/*ga4"awhOeqbDݖXmc '3 YYؠQСJUP//^+%whߣZjgy8QUÖjZ.*@(닡bpu4A&n>$_Eo`YWj5VP?ş1 M/E*b F~,>݃GCƀj&+;t>^ /!p*YbAI)i5Fb[iXWj0'ޛ9,`% 0``( 3cDNdl;]iziL x~3,1A°& ?K"бG,ҭ g(x%yLLFrm,Mh.3_4ɞ{&+`w #cuZ&IaJNYhbʽ}7KD~Ɣu3wD@.~gЇ;ЗbX,^1[v7ƍ' N:Q/k ׵2m@DI0T%?5m >ל!b ;0Wh3)w7j]3q@_:*c܃% :Y*a=`}Ol Mz+GJk,4܏kU+'Ҹ L8kQgTYX agXX\Ҧ=_ܽ*EkF} /d-)\wnXNHs@@ǂ-2VߘY _KbG-0ڥۘ(0p,bvhP2RRl_{C8}iq+FzXmt, 0;(O2x (lݺ%අ@~]8}OXebd,WP (_xN1 '~%J!ѡ MvkZ9?|@P=+{زuKiؾEhAdža0svO7mOdd$5O\[n 0P10<gi4l|Ŀۛ5{ c樥Kz[hOIKڃOܖ6mx]}*vi bDccLn i? _{d~^nz5^Aܭ99fe6{θ?nww`d?%2'nN;w>[8Y-(F%<Р7f|?(XL3 :J. eh:% $a,M#L,8ޓ'ZK yE#1|dqm[{M UC,$KPu X|晬սYFɯiZ<ljsp}2:F={s$vye9*ӟ5qo2t|,Qv?py9 yVYYLx^nF?׸Od&U ec`|B@Դ{Wmt|m֚jyB UMz5)a,FPĘ;h<ݵkV%.W;vld tjWXAX|Uq#{Aǖ-[jZ#}tFڹs=-$T 9 f'"-dw[)VJŲ`itZv#7\Av2&ЎVϊ.%@DB+Ѽssh+Aok(hp%ruZqsa4C:*ϵ^V^"X|{1%p2[T^'4̩–iEOQ,GggIVU j_vXFs MO1ޮe! ̖,̵p@ŋ 6ZAWLsiF*BB xUu|ӏX“i_b;.̜ٛp Џagidd&eFZrzM+ R.:q9#vYRL&K00.7p=TKL|o%nL?JT,}~fcuŪM;f[Jzߨ,UjcrU 'hU0NqGf TՍVJ'%kg9$h]6^x4e%wUG}ͽ0P@-0n_Z{p7m*Ruڦl5h`t,]؄]t %HVX! +/%zc% wl)hvJaIh.Ⱦkm6 ,Re6{bK3o|+UV&ٻbO"/^d"Smۦ!vT}ꀮ[]&۷T] $ .8׮ʻ #a>9{A?z@%Aܱ5pzH鋵[v_c04re1[I92Ϫ0zNZ>Yg+1vfGi/*KOUqMl=};~p Or*Dq{._'GgW󷵟h. SiW޹̔\8vOÊ@J@\_mEx{ڦvL(zӎ=ڮ7˲.ش1 >Wi+^>rR3E hS6|NW ^㖤t3zՓ4cm%|qAGkߒ}k'X&>OR橻Ok2eUwJˌۙ?+ (`şSV"лk{(,F+2:hv@\L> {lÒp}#L9>YE:~k@ ;v1aΝ:¸h`R Gz-/yW+vR N߾%NysB(ҒB[ d%Ha:kvZ •ڰ X㜥'*-Zk Vl 'Kx 0Y91n>Z'6#\ih-w6rTSzs-bu#eDMpm)U%6E["٤o6b}Æ fCɞ%?+\-Y ܳo^l ֨ :վݵ-%k&;ИUXwj ,RD$%U7V`Ye_݆%ّ}sfxR=&!j+PrXrX IlILc_6T[)Yy\,5wVm ˵Gl5Ųn(/hzri'[T/w.fx>$ щUGЗ6Be,sZ&#ٮex羽Z6SooҫۅK؄i,WV|/xc:ёU&M0hƘIJٿ%y9/cat6kEK(VbA+pAy[ʕ js5tVVϣJ^ 3, :߱;;B,5R{۰ 4bCAZm4Ut}XRǒlEX.K$`,QᧀjZQS;g > $᤭,0R@hQK$Lt"4h4,GcgW oh9aśm|&UAݯa\KzMj=46ojҞ'.>4ަ)c>B"52?G) u3Uo3aWhM;mc <Ĭ4uO@OF [RmҘl8W5JN]IQeψ^ƶ 姗WZb37voK(<^<8ZyC}׿;G=ZjnU紕?6|F62G ;$is X Y{1g>pZk^k7;^Z-+o z'o wAjm NQܦ1m2hJ;_\vq{ڰwX%aW$f\_@ ǡ o3 v=Om6E^b ŴٹRGzm\H|J+?kB`GljCmHPd/ǴSѽ[[%k%G>,MZyLWhA/hN.6М12;B~M6h `@nM%m~^?h߮{hi /\u6ݧPM'Wֆ ڝjmu7M.ZrbE 0N":[뱛9K:&i6<֦\Gxv-N3'F ,@LO&I>"&  "ߟ>X*{^5ա&b/8wx|<igs%Suj(iJ$1Kud}v|>Wˤ}WyoDS ֊Xyq(!^m( ӤA4A()DL5b\a(ڰ+cubD ؉M%7 ?mmW;:[Lon0&f >w~^C m_8Udv~iڡHL6̧sh) ;i 3cԟ٤o6 o(`>hlX 6döI u%~vv#VG cĔwx(aOR, =~D.B7יś<ZEv=O}AUG0:٫Mh^}67LK@fGz2!vU47|xBVmCyaj+dPJv$(ܝ;x%^U -X4r")KyUs: Ao.^MƧӴ  WO x:㔒T_T,`͡vX ؽ.pppڈm~#рFeaV0ˁ!A >:0֚WMωyݨ dmТ*$ty0sWT.UL/-h#hTvzw0s8i<&Er^|g=Tuܩ`4q.h|t *P[;gc":`' 0JL t V+94Dc9T:4[{"ߕr-uܱ,mbVsۖa\ak/i*) M̸t Gm'pD>Bj; !~у!q 5azA4nm#۪STNо'9hwn;&O!GPa Mx]+f %8mݗnҬ_i \FŎX 4AͭM҆Y,|#lH6kkkn"g;23VӶKpRi\~Rգ]`wt\IORɲUJp"[}U_`>oTJ43͡E1,! Z?cZ:zO9jG4lED3_VJ~F_Ae Y-|+b5Ps0=G6I*):snD)SOiw$_ rFр<4{xDɀp@ ԷƑ]ܢ&;nb,KhtLL>:<#a_(+>+6'+QP!\PK+  1: c"mtB0Jip@EJ?v&^e4~dn=Ƚ81Li/xtW1 ԣwvVқ:8O0(AA0?G3Y״a3zr@ODZ<35 r#pCR;=:CQt4]r / sPu15_g,L?݊Kc11w Hnռ9=Ғ- o-2x;=x!Wr|<c6ڜP+ xz'D4ŊcC0E1^IՋP&+J=]@Qi<8d+'PAgO (`1ვ?G&䣃oV pji^irsʛA!,s@2NP&`%545cmA H,;@ɗ54`mearY-͹@HޔkZ'2ՋvǡU]+|F] @G6H7HnzH`yזnkMu<#wZ^" )L,$Zq:20Xn}Oˏ`_]vjCѷ%7/8BsB[`(W 07ɵܤ}Ekc! Z` #cJyQܭph[ Տ6;=Θgl 'ԏnP,0 Rg.1+ }M -P;1`'7MfwfuS@ t_>hK.gVϞ<о?2w rĢ<@nU毷Xg`Y.d{l_8ǚ˗Q^>Hz[% mx5'fx5:%JnJWxCKc-g?\ `RAs*c\XW[y.;2ůj7מQQs<4v4f"%5< H -McU߶r*~"\E+.DM{GXwaH{;7? Db"A~x66StX:o55ML'ʤ{*0ݵEhk| 쭉dXN;1wq?M~0+fWWw(,2fuīj]W6Qt%Ւ)v.-%[p,b٥#ÓyFV$4x> S[=F_]7O]RًdO,"ڴ7ݤUpz[O*'ܭ~4$01\%@qaww&ɋ2t WnW?iHdřZ-mj !\8}&PU'/`cwr(KKI;Q`#) e٦o)?icKaW?_F*û?k1+Nf !ޫ8]b9@!C%{Xcox 9abn}JQꨠN85XCrdNm2v\ux(`1M;?搓Kb=D.L//C0`4\p`x\jGq,XW&*Vq{#%+jK8">O2jdaɒ%_ GnGט>,Ao% m)}[c{κ1 G2d:_og]t7V-kYB9Sǝ&gjnbE 4feRfEMjYWOLNY,VZsr:k/5D2ň0i@e=Y{hf侃踞0YP _f e6A+ hjL &A|n/p߯mq*'=hM] cjvBkܫs\ ^wVJLju,bybYeVAx5jNhe/GsǖB(K3/})~, {s{V'P{v&d!JzO׏MJړgIo' g ܢE f."ȃGʝ#*ثĽM+iaW03 OVL:G@ⶥx+Ƥ3*/C?@˭^@qu&1FA(ވłZYz(m@[~8M%Lwc;D7T%~:ũqKZklpp[H֓Ʃm]|Dm}zLԮuN&8A s̉l`]# "rqjW8v ĬOW GQ@cS;]E 9@Lr;U^S93h7ڦRpe͛7^ ߃Knܸ1,]4L<9L6x r  k}ڇhO,ScI;RVu̝@:o^,nK R&RLfvFkL0910hC[_ yC >s fh@{Z& (^}q>ϑhSpu%ܟ:E)r#x6]r( " 2 ZaFh\ 8%&98.9o&;Kr\5y5qk1&zɲˈˆ;aÆ y$ݣ>je#`t4|\ ]yA\徽#2ֻ5R%e+_sc#i| I=(hJ:.~:RZ;0<@Xba/ GS ~~TIǤd&\GU셤L+ĠI~É'hmg |8 d4H'"G<=[鑞WkesA/$&+2_K+ͮgPGė^z)s=6AݻW}ns8apvXNx$|铞`9ktՕ&|#\hQXfMf~vUKzԭ[7R'>^468{0o޼~7 'N X  p0@;6MT1^xD ե=u@(БSOXGd]y'(32T^'df4铴ɃM?ѡ}@[LvOy=/_lbe|s$$CӴ1S隮 ݕ8|cDH'w&M 'M%^˦),S{_ԧ?e}U]e2y衇… .z{NV%}]AMg[g+yymik|<#OР3uM5I㘖˭`{@Qg4rxV肴4meec7s΃>JJJ޷o_['} Sv.;"i3t1hظ`aW'Sk2a>ܹsgl~@7yA\@~/{GWZZjEhY_ifi T74 mw11.jZ|QZt5]Wejz<~m۶O|"|]vm0/᯦u4~ݐs痷z+|+_1nI}p?¼ ze+@+ <@T*eG|aBgY'`/uАڀ<O9MUxq\sMy;4=KQU2mɾTj؝m8TU 񽧴ǽگ1=THhs.:z ΢A+sڜ"r5׮]k+0Faر5 }px ,_<w/0O.cKN~:e*7Gԓ~yW G ІqMo߾a„ }k׿pꩧҼGMj?>JO~bQF5q񧎷[;'uN'a0JO/Z&MիWzK.52?l-cƌ s /U?& 8嘯F4~ ͎o/iuX;q+=ں_ݬ |R~``[<?y8_G8'\y}|ҼuS6:{gq>GɅQZdn+SaX*%Y~a`U;B.m_[M nOiqk9VVc?\CkHi)ժVM!ݦݷr,cMB=cǵZ, "džZyyGE5t>4>s4CO>cc{2Xz3peZ07!\|šy2urKxy 0hSEkG܇VN#FS6ooox})<8c~iY+-7ƌcI{}Wo O$+ZG}W]weZÇ`SaM |{&U]σCɕww[W^y%q^}vvDLޘ@dr)749ϟ8( wwɏ7W޵y/.?'4O?=|aU`u6"4bzСV qyٖ|6yъ6vgA6멥Yk#:0yXVhJwv&4 gggRZ6 e8M~ -ʵVf_T O&I]BvP/GKʝ+҃(0BA 6>㏇N:"y.໤wr7S{vpwr{1!tei ^:m f^tW8/F}뭷땻EOK.)n:;3ykp'B9B~;Ә-@)41sOq}:O??ȵO9(an3຀սk^x tL>hau4vqzNmS|ߏ^{\^@~_=-,(̙3Ǵu , ]8Ϻԗ庮M~-6y* K]„!a߻m3Nvީ\}x䍵aav5|:`RIxePgjs[z'Y7zjȓ)(e \vB/DuWք5Sav@}͎r.!yj0cl{rkkVm sGqYB@g<5 si{o+aEyuuGoIqӐmS8/c!m骫 ?Ϭ=@spx`o;Ook% x u:[@/rrH @UWݞ0jp& &Lc.bC&,)U0Rٍ "i"ISu=d9GI`ԺEX+ ez -ihO /\|QlO:,Qx]v؅ W-:Ovv>g9akIik6i2W6Y&=d߾bz+ ʸ`.&PB[ǥ`Ÿ8@kӦM&v@ѿ=mw-&앻_lEg0nupE;ǷTk.SmTgYhrh̗LbGNX'`'=_+b1h0X3Av 490? W]~ 2!>4hGq@τ e):n~o&k_~/[#4uaBdǻX|6m` O{/Lx@B|#a8ڿ͏2?[xxVcU\y4g4b[jU\)+qOG10ô@0 898ÄڥƤ@tR40LLď\p>& $Y_}ն rZ|[$li4ȋ4C& aǯL<0ss+{A &1y"S>u]PH[Bp@k_t&|,hxZ?icV~_h,KH^XpYA4_{!e i0|'30t!X׸| 8! &$%~(c͚5BV&G;>(3wQ]9Q5-h0^Ъ37 1}`Q%|Ơ^-88>ڇ}:%d#|@/Ŏ(>ZwV"AL$KY7GУqa kligY\:UCc>iZgecn# .i#fO>t KRz>>.ye} Z0?sˠS-ǵ A A׵3<9gݨ0 ʀqG3- !LNhɐXa`lhHY5O0Zh2|a-!ea=FCVyƌ%I';({3I4L t|3&v/#„-D 0`ǟ Nh1"03iyF/k,,W/}KVtf:d^,Ahik(by7D[p% tqG< hqiL|0կ~4 2,  COfCx dQB &B 'mz0{[Y1 w|M~LXuY{lP"w*>Z*]u KI -[*{\tHp{Kk%Fuz[W3rЃT6_Lhrm۟cY`>@@ fܠ/y_e~=T2ccϯ kOe''EgMj<\3^vO_bNqv:sLSw0Fh\𶜭vKi1KO]p |;oSx#:0ȧ%-sc oHLD?3T!:wjw;+ R}Rp40T5C`'{\R&8W3²˭b3\0d*LhOCK͇U>fȌ~X{'@IDAT4&wwFtd018-H@>*8%W /&i8"x;[&=:S `(gŚBnmm .nQhV7B揁o֬YF $ (`֮]pJ}>1KCo(*q/!2pI.Eô 4pB{ !$#ݵMkFä6&:~F)Ӣl3gG=/ 8yeM&mdy;=pyVҁ+h6OVw^UOhXy*ƌCL$8!U.4 c}:~ׄy_᛼jLdBOd\,w>{,ȥXAFXȮgyvu04BJ0<3ϼRkgc(-QR3ϳn59RM#>>'Mu^}ϝUI`^cK.2#r͹7&<3ŮB 2L)2/Q_!ĉCˤƝ9[M- |M:>WyL?.5::0t%+nWt|%-i[ >Lyp]68!wLXFhWf=CMRj1ӷM mB@ȥ |qoCV< ٸ$u~~q܂9B0pJ''0Z0k! TxzPJ{g>/,eACCKt> E`|15 ?U-fmϪ#6_ɩ&}{0g{|{Jb/S+H0a"/sṗW ={Fy&f,ao~eJQy2Nm fk7\i׸\1.y5^>K/@B؞{}5 JbPSHٴ}N]\\"pC_]t Wow^|1|WJxJG׼}!}CCx:)0QѨq3Bɮק9NC&[&u{9SM>Lm.A`m\'RO: #LL)D\'(s_*"\hm7ˍ"LЖO~2Bp]ol@݋mcip>2hrXBߡ`mC,(%Bp1`7 fFUQܫ}O*0۳YsGґ''pL f \b ),RX2ø7 p 3B=̗O?#{]sl⇨3Sjƀ5t}x^\gghX #Ds4SP\ո@ Ɣ-E0?h CS  BChZQs&]_%##aA;i{>!FqLh҉ݡIGsMVA{x̉ i .hzpD fx?L_sqkX B<$hzWLVvú3E-z\K.}]%:yW'#G'(6H{sM}NC^a-GZwHPg _o{1 n"81ce6!l#35+Xsq/~aVgn׆{b3\ox@sDs71&B90\0hvg`..f+E/Ђ  @0 Ӓk~mL =n F5Y  :.Ev$- X TU~̈́:uC]*8a&/ЀV~Eo3 =c8}Y}AÆ[Da;#L'}c EBKB;r˯/-x|N۹?s2i$?t~C j0% 0 #ミiPMxG;RJt(I3B*"S8(7J}/vQ)`1~ƹ_Ra0ФB X-6xyeF \2)0W ?hX:`g0Q'K0Jh |c 8b&dD"H]m -y@7w DR9~k_5 ra`pk~X?z(aRV6VB"0o4r#45X?X0 G0߰,2&"#L{*n3Z0`bas+R+}瘏~x9(り5^!xGY\;y>Phn*0p1.ȾIǀhc zDZ*i3q`fg:&wI>9={фn .Lsh&"Tha`/ L]ac{>YPޡaC2K`*1o8p3bp9- <0u"qb†&}֬Y6 B u_.8cXc@یW1'*MOC0Ma˭E,'?<! ɓ G\>jw~" }U- ^XڐzfQ׼ތ;hq+#_YT8r`H_*ܿq7/_\!( (f4;\tE|b]Q\1Qp1"l B_$@ߢ,VD·0p1@1HuߤHtYF2%5 3>X$`J XG+ Iޙo Dy|sA+Y@XLb0bMI4!Xd4zqbhNT"0R\YѤILe_s6i\hSG7.w2|Vbs# 3yJXYn]'cAƙcs@ 3 \ eL -nj?{+1{:s ,急%ʊ+‹_ I?[@~`.c #  T·M_e/ =Z)sNc ?"->( ,0DaLjĈ҂-5F -I^q}l:8^9B+Rpdߏ{]gyYLR0)xVA Bh@bpXc_{f>~ha23QhN?,M`θkWnɄDЀB/zG,/IQn4yX/'&Fѽ{q]0JH?,/K'0Rvco9^c?Ns [L+s WaMA"DVo!>X]Gܸy ̻ S]>I{\=O˄ 6n"0&f$jܸS!u<܆:R5^/Y;ԿHz=k+;M5紀B_M@O.@|^S<?f??׎\e֦~q/_|ne*#?9&hU?9>]W!O/2Y =, 8V:ɨm^oČƊm8 X$@0A3( (4׉;# aK "dc=!/," a%7,1(wcV0,kmpQ@|~}Wuyz^{e,iLO?+0P@(qS'%0E.f>:wc8)O|cP;=Mq^~~(a/%?0<=Rpyy7݇U.{j'uɷiOޯ=hcxɮO.|U6ݺ^7PSN> BXl繿ik:IX$bqX8r@ІcMڰrBq=}t484 +AJ6 "(kMߓg൱9\yե?}Gj~r ͏57{,0:LBM^]*:Nrսy\^KP\nh;DuͣEҏwLp]%,3 ]ӟj[nhuW~Ϸ{}8ylzYNfpI#G峙_Ks=Qb` H>L0Bx x<6E 9DpqmpWY" a:P8vzS{b▇'!3iF rẶ>4=?6#4>b WkXfu@ ` w%Y(%fq١pS/L&8C[s)*++ 7lU'VGՕTg*Wk'"`f͚Uɝ|a$ q0Ԧ\=!zu]GZy.9[`q Nvv\y9?uA 6c\sa=  _i1er$ 7+,(4ą\.d2ƮZXD-#1@5X)9߃$7x [Zs2lmTvvF)@ Tkӿϥ$@21f&"h=s&vR|+; oΈ+ܯ;prs; NX6C@N !!{51-vbNu͛d8lvwMY{8ınq6Ŧ Bw=ң$ܫ{u=C߽@G`mm(|.!P'7;˂[&+)~ XȲ0e`l.;^f3,}/@am];]DW[[&NHe6w;.vV゗N;DH79Б3UWn6|ժU)fl fJ48EW^ip|qIF+XC=sUZGy0F<8@0Myj;Ъŝh'N# & L:,a@q,M/<̻w‡]BV]- 1wM'ڏ#[o5ȡx \7.@?a(,` 7t'M"L\E o^[_6<@0Xg'BYRco6TžGPv*@D 7){v0ý„c-2.ɝD&tEd9>bX|Ke{\-C\Ă?SA:AQijoT sN"4 -蜫1a=M7HY C 9fA dLi +,8ڛ+4r7+8{;=>.;ĀD,F{w{1F,v9cޤULRnO%~6q8#^8I_ ! X ƀ   |衇dɒ%Dnr.L؟A= ¹"Z4:'8937kNjSsp`Am# ȉ1@!`ތPÂ{̜93 jc] U ,az_˜Ŵᕡ椭 #Hja3LPԱNuRO}*|o. ]? .}9x_}.!m-Ԯ i¨{pG `΀ćSrbE ÌJ LN&`›r[Xڀ,#]Súu?<{S5͉JX;@KR3 ANA?a8AU+WS%\"nf卞_Gxg@ѩ8iCC~??6N5VB#~E2o}+n"ʸ5|2>c!ڎhLkj8ʤ#5?;#Xǵ+\ŭv}DsK%<pҊ@t`-*'Z/^8]s0^pJȇީwU#,9G ޞ& 5agA>,sCK4QtZI2σ#8 MG|/8" _'A݄v>ue_JnG 8[l#зK/45pߣgPz'#L!sѾ g'{,_qt%%%F_r0FE; 0>qڵkslԜLy8@"pa#[ H o#؂si,9" avZW"0d; qE4™s3nw+ cNO+>qGu0v98@n!xC<8N42 I . tSu4&C~ >L*Fu!c>hs='<3lAϟPK &1)p;؂975|j?Et6{#8]7]րnV3 zwr"y8N޲e6.`:X!›7t̚5+eP(D%}WgYe25~8 _ }9,8#Ăsp$>,#  #!]~1,#b}NaT؃`O@%lv%΃#8#QR磎r"΀pTžaPgƜeuq#893񾲞sj#Nl:r6ry;G]k|9NH;΀4A v)Gpx7^< H:EG mQL.iGp҇K@xtj@Z]&xM"&LpGɅmt8U@*XMx#8@ͮxu$^<#V`@|L@~՜#8@Wí3 1$L! Hƀ4#8#΀[6:Ii* zpGp҅s/5CtJ@p b;!DєGpxp#3 _@5)Gpp&$>LKg@ F&LpG =8/΀ċSsҊ3  x|<pG6ɶ7q@p6Gpt!\! H|X:%G -DT}lU;GpSO΀{^0|$^M&WMwGp g@u{g΀4΀4ßGp҃Ͽ H|X:%G #0 3 MX#8# 8 oDp#:rGS7>\Kd4̤ H~8#΀[6:|&:rGpg@ y\g@O#8A7tJ@F8zhFɅJPrƇ3 a ` HFr^#8#+΀ sҏ@3Q{nx#8#8/ɛDJ@x Y>y#8E7a H|X:%G #OMX#8@z0PO΀{^0΀4<4aw#8@ H)o#Dę΀pG S΀ćSr2@QQ3 y%#5;ѹ'΀q2K@8#8޹_Ezr<"x#8@z P?΀;8}}.#8@n Hn'o#Ј`5B!΀4aw#8A?1u#!P8#gBExtj@p&a@\/ sGH>Ƈ3 a *XM0 V~8#r H:/NH;I@bpGpg@ ynLH|9pG m 3 y½3M$ I&~8#//Ps$~L#dc@2TW8#81 H : G ; ĝ7rG p#x߽3 #G0 G XsGpCT|Sg@)9A !`AA=z4|2W8#8FNC!`"|gB q$ ΀~uGH6~u$޸7p+lhhp HsHpG FL+FyO 83 MoI Gpt!|o|:N>6A H~8#΀[6:rGWO9?NH.n-xzsLpG ~o<ć3 a!R&Qr<;Gpxhn>Ss$߾='v6 'GpH\:Np2@ 3g^#8y`OپnU;GphTtRt$o_w<o <=4şGpF~=8Gx\ǎD9$8##&7>PKd P3)ތ}"Gp@;/NH;|4<#8@8/΀ċSsҊ5\ #GFCXB8 WS'{cc]굓dslxWԙe.g@2t(QMƝ֋:@!o6<$6[M :^]+^<#|3ф91ޏ8߻woӫW@gO_D~g@F$^<#v25ঽ#1T`{L'd|߿_yټysXtWXX(j.),-EEСCL߅9sٳ`t ΀ YL ihvHB>/Nv7>9eR__;X=yDa .B)cV[dfTгgUY}ʃ>(˖-/~R^^tύs}:u:G"]Wj1cvm2l0ihhZ'6ơcǎh1=.ƣҐwE2*((ɓ'sٷo_`@8>LN:۞ƷmrN@w`N㒍q<#E"$EEECGXxfsر>rT3BQDA2ۍη>"C(r{!LEاOe莆c&D=mޣEnOqL ,!>_Llh ~G H^`PxbLȀo.WAƗ['tTMUB]*JFbc\U^޺_>h25=|H>h[)DۧUpUH!W/jCĭq% d޽RWW|WL/΀ċSs2K!Yp0c 8C >EvM%}z;OԬVo'ZU%WM#Kum$v8,%a_{AjoQ-l&ը;t1qbphT; vXHqQa`4(:]Ԥ_Q[G IHj2%$0!xcU7玒3&G^gު c2m*ҏe {FIJi^3F 7/Wʄ# N_SݍYFNTV K"wmͿS$us '`={' մqHB88 oحQ0H+t ?#͌p&8 U)Q;H eZğo4 7'&oxWG1+iG{ n^JБHjU OTL [ +t%֪dVV̑wv/(PlzɪWd(+ J5RH#`QTi:jUJx%W*W?E=kzXdo3 -Xt9/t3l k\;2&|̋ ,)KH-}c^iꩅWw6:iU<U+[(#vwP^ҝmL2X.%'ԨS ix] ^TXtר䭪טƗɔA/kkڴa KݧWc|m01> R"h>`Q(P g!g*@xpbgn8sh5d!2WǨb7Uȑ*IUQ"1r@ s&le`zi%CT"7ga*e& 6}3oxƶ:^b _:t?||nF9A༘#0+6mfnOtrJE@U+X^z !艿-+a≡w>)5@&XDV8=9{ѕ*5Q+^"Ak2')!tЌ92:9f#@IDATtЏfXh TGnT Gpܡ2G':WFT.gzgYV]%u ՛#ҌzUm@Zw*xiCnUÝ3q9eҔ!'Cү#^i)ʸFdf"Sh5uϪJRʃ+uy@SX uTdv+s\g@ө9iG ]``bn}ǒ9L֝=&Gҟ\[%EsX$ r /KDwQ:Ge}vew#ԏntE(V I~0E f&hLJIԇGp#$HTY:qa#20ܱd(y ͂Aݓf;U%2l"8%=ARbՑWSoWQ=դ#Sebj0o-kO"TຳLUSkD: ~$uQjBjux{} i0ZmtESvmGCrOGbQ.Ezyز뀼eT7ʄO;B3ǔJ[SwG;;&]Z/u5zo ]xϠz*V""Hmd3,®7h~bUi%/J:hȐ ֿxI0"!&$"8 l6Ut@?y6cTu6h p ׿Q#,.Y$?x9_p]Kϧkӻ|B :?NH64.s@._''J*Nx̲iu=_]aun{)O(1B`]| 9}C6dTWO0rބ]\LXLėdt&0HO:QWW-ʸptEULHOԇLM*FIOc&ҩzV?[]k)XH鴕ݻJ.kDj{yHM^^)+Wem]>녭Fz%ajۍ--1>YNGy 73TNP!SJ@55n>-[nĕEҍF[m$O\cKﻥ8 'ʓtg׮]_JJJ䢋.ӧ˰a»}ጜ`zNuJ`pfΘt00Uj=?׸e/Ҋ:Ϛ^1$)x#}̝w=prk)H ,Sp 3EYԪN6Z(ma^UL(&}Aa=lS (Y8"2IFÇ{J, lі&:[JƓV8miƤrNԇtO4IY-[&O?{2w\9d„ 11i7i~>G;"M)vf NlJ-YvheJR9qÂU=7eVwes)Cl-e GvICO!3TzݣjXHg^S惕a䔡m+keFMsO?n z.9$؁jupnȜʬ VD  ߅sw_WDvN OXڳ3y%&ηU3vuLHGB4<  ii"& }@8 Izm1rT g !o4 N!w!<'zu]R\'+3r%ԩSCuɣ>Y۷O.\( [nx@N̙# , {ʟgZTT$2c 9C}{eҿ'ʢEzK,5kȀByv/B8p`PCxꩧW_ :i )++ m|套^>}4#"V'xB1pqFyGBfΓ)StNyv3 85{,ēN34`Ig},z=Z:f/ڵkA򥥥+kwȑ#V\>|P^:4uNy姞z3&WVVҥK1gϖLJ;w/=HLy賈uЧcx{ m{SN ;v}乼\fΜJg˖-c~4+V KhB ]ll:Ce.sJ O}HŭnW\<\T˅g[jMf=kD.V!Ϯ{io#[5A;f`70xk)#`dx2.@Jf+34uHܽ|g\whܽJ.TEݲVW*+OJ28}TQz$0P!ԅ/[xtEaWl¼ uv]rҍ@t!K9v1Es[#0%滦>?,y JkC DI l!)H]R3I:z`Ģ,YY< y ,XQ#Ea͕,I'thP7 B>K'v1gO L|NΔ!h%J&ʲh'\P4,P Z= cꄹ уbnm.۷tOMt`i]0f6?ԉ6lQ hwx7{3$ :=ŷ1 gtixe.˗/I">\JD`~B+a7uN000ЃCW^] Dy/iqwG?r뭷|g}f#,_ד/6HBJ&z}zګkt xdraPY4s?,H-ڢNbL# Ce28SX,^{he'O3;v6塾="5QKUT֠–8\EzaJu*sJHPa]ܵ` VI ꧣ@uV a샾^&ӵ3 }zo9 lӼœI IA4 H$Y-K">Drxbב3 QB¬*mmB=4ԭghiaBС;cq{UWu]bcOK!C 7RreŭYh_|ᓚtI |RϚ5KX$Sj-YƏ/v[NG>Ӊx-C5BkZ~|Z `/|:h< tM%j f۶m) C=~Qz麷7gkTEK[eO\"Sbb_~JKxPCV[ g;uڽ х>jQ؍@c20!DA`a WA2\RaInh7ÞrR豣GPiHV.7Tׇ6?Vu}̱ΟmSWیɧ~΀ Y DduӉ.m5K& =aRȖ3GMe"W4T[&CrMUCdj$ZQjSxZe!9C ,$`0ȃVW3o7kYrUsb&oKgzMZ,oSک"U^*,\cfSonqScT1mpȶ}k G,$BIByC'zZa5V@Zx!0Mc$MU`8Hqzk$8OznSݞW[sW[FY*dѾRӡnҸ .9G m bǩ7cǧI2TM&Qݬ_ۼW6 ^0dq2'0v*Mߣ_ S虣Kezb$Nܣ GWUph!z{u]f!0UCWըMXgRU}ԴΙjQƥU皺g x|v[_}8;dcZJOTqg.'>[7]NKX˚]n_@8%7Ax+[;c-d q2'6y#nzcECtJcCti zeN`G[-/n}D0,RCH-IR{(yqrݩY]QY]:gxPma'nJɝ˳u}Z!}sM `XD/Yxp%z5w|\!_H$y=8@0~xd6OƂ橉'$!sʝޔ߿M?尞A6~qFr8JTƿ0MN}wi,#[{GG(ri#6WoV;ՃKJo(c j>Xua28AUAz2Y0 u?@m4lB|gFؔl8'64M{S˚{JGTwB8MxYm\+ѳF)}M]lljm}miB'ރ#d~l6BS͏Ď??^$~ɉA@! e6Pf҄@P04"a !u铵4mIK>$ز0cGCs5ު; TbMmcjkutm/ZvG]NIgA֮QL.U hW ge0//0RPI~5Y܂) 7xEm*g ,PaU 8kȒ =xQ+fdfW}}E^ij=Z0BObFzaתw1**oZeNS*rQ mKk*E}LmR ڽT闫z+i!T^)~>*RN޿u5SUFgcsUWTY=ɃsRf dq lY31_Rip /;&%2\vKfg@b1I]8iB-3U2OАgn.^sbI;t1a*Kj5JrE;aGOIviuoFc'V=CΠ)yǫr6%%vui } n }ݻ#<"\sMȄp2?[?YуOճ&*Ye1rP|lSuo~yuLм '{Tm'lQU 2wo_.O* ӯ_ ^5PhC\=\*<*[478=g!⼐^TC[˯4 C]Q{zҀ/T*C[j- '$u6TݪvvAaW+򜛴O=C9iJϚ2Hva?{zTh=+UOoO\0N庩Je\6\*Tr+H&``3 Dg@)9iGA¸+cm6y7-S[RC4>zn;D'8u;**6e=yymtRhFS[l|׫'-[v[4;@f :M%N&e} ?bڨҠvZ\.u.`yD|]< Y5~eTWIiQO9e9*~|tBd0lM]nY+_]|Ri g-T J.Tfu2K7@UF =9r$HRSSAJ^pB $‰M]%U:Y[nHƪ0HUFG:~U_ Cʬ`ˁReU=j.ԉgCmk{m<8.ިz3^wL.ӼE!8 V{~ESzx,R> U1B>$CQѶ|s>R0QG*m*^lR < ]p$+^7h,0#@5}zf؁#\#`8G7\% 癢J`U$$T*PIS2u>ch2 #s-~*Ac@($:PT@i TIYc_Ve2.54H4&~>Bh\UAid+N<M]8!0=B0r8%j}IφZ2sF%pRw {#V'3 'B,C ];LpX'#:t$3آ]V#3g ϵRQQTƆ.UUU~z9SdȐ!f͚A}~c3L,N^pN zxj͡ƃIa_9AH7*u Dթ#7^dҠT"٘7v՝jPnaGکJLߡgUpi"t. ^"baU+z47jh/Fd}.o)f–1vUڪ*RՌ!Zra%zGaPB%G½F~2]mE@,PB2qzݯlYj1M+c*5ҦE xyF;,/W[j20 'j8^g-]=e`8LjrսjJ-QpYdr5W']]QZSԎc .7jς7C Sa +>{<~ja.+SG4fAol.ڛeOld*4g?sH${7@@t&u::J%W\ԗPCM638C^|Ő~9ȬYdٲeA d3<q߿?0Aykq'k{󝈎;TW^l!ڣ@J o)T[Ȃmg/T$ Tzc/$ccA:cGc ww`ߏ UX1o:K@: tY\ S%moiv|@Ig}V`6f̘!ƍ *Xe1)aO޽{T&M$=L:UCvRSnsQC ʡCȑ#eڴi.'fQp @ .ҥKo?T8GH%t5Ϳ?0s r\2&pFkecUũM,`z$ )Kfy!0\'P؞$($ S,6* V.LԕhV V'&/a}f$O`3 O3ڞUށn>ф>ص:]v" H wڴ ')F,9ncQra\5*LHD0@߭Fgyf yeΝ2}Ջ6&gR>=j[0;<@`\&O,gϖCs='vP /X,X.6Qݸ1qz@wFz20 0][Mӧ]3vdnt&Bb8ѼԦ禔lroذKjʻY΀t؄鞚؛4!W0a|~T$0#_r F ʒ^]]-;vh\0@ O{lJ~˺u뮓s|#y뭷yp6^?ypڏxxAz~ #7Ͱ1dߗLLכeg@2tL|0H &X=ܠի哟dJqxDcL:ݰbw9|vF8yGy$X uYr 7ȸqB3#@@+"0 0ViLn0& m!y饗Bwyg0׻Rwg}vP #_| _y~g1@> {_c~/Ё:2V_kEɵ7{2`B`QN]\$8``eCLPҗ$??iP/)x;>)S¹>׿ w-o{ۂ ե^lHn*v,ZH6o,7t|'|'XdI؍A[_$$믿^Ə¬|CG00!(ϤWL_'oI!M6>x:&Yo4o(k-ئ۷M ܂-E1iuXh)?TM-D`Æ@ JKݖf30 t۔hxp/XT `nF*JlpgBKY8SC^q`(:CmņI r6>E:K@:v2M"]9Z2qpJ,1&L6QÐZL-\ '$(m!v ?{u]A2ؠr-ַ%7‰Qꫯ H[L BGh7w3 T.y&N~=PP /9;S~[>SAy1bY|qqꩧ4?,8@g64O=Z(XQD o~mFmbLCK[#Or6*lQ6,"Px͎!   ~_). L}uWGǖ48` ~T$/rYjUXd#(** .,[ΫЅ+,mÄ̙3'8@b 9㍥۸@{`sp!S:?O 0`ƘǘHz0Zu8P %X`D0l3' g@gr&&t0ZwzPnW-vxŹ s27 vPB=#Hб6lXpLxB Lg}{_eRvBO;PxFbypx@ʁ'kHPV, f"?`XSE9k~E*$HY6nԝ`}JV)RqWʯ~0f`ӁDϐ!C/{Hbk&j6x , tND 40f"`#@Ntbڛ".D :DօX$.??ɮ]s{0LJIaQ",N`~Xaǒ]J&ԋ* h,Z[D#H`6`Lɢ(dK 8`N 2^GGՊ 2.p62R7L {FR>H^l 80h 3ƍ6koc4Cۡc4 03O9"R'R  6xR;pvl@IO>z/,:,.q>_U y,4Ѕ.]lbnm6U ;KSTjS,v 1p6~)^$N&w n -@pN$H|ɠ"?l*`B; ~B`F)X $ł= @!@1l8F@3I.$3` ~ԃʕ+3k֬F8VIA*F vh~u7NYY611FO@5,ґ`F=aKn~ bb"tPQcB*-  E胇!osoPX.g@:r |,L+ ^‚=%,>0.eX=5mdBgwFo$&LLxa"g ,lr;"}*x.l <exU AaϤEӉO}&0e3)(tQQf6} ea[ đny,+Gh`lDB=x ~Z|߷C<8NL2YO٩N^G~ eR}'k_ZT,W1ܛ1 Las  3acămD g8KңyBqB~CE}j-[[RҎe45I'`upO5o,?cϖ'Jhq[gcϤ/\٘G$Qƅ|ڇaܾܞ-i Os䒅͋IWwAe]EcΎ$vЫf'ѓ656կʘ1cHR -0aÍSl&T!8E\ؑ`Lj684@9կ# =EU7IGHov8X4O4&5&ڃyRSZOcD س[~=1`!Gd&I&#gA%L,N,F/^ܸNxBU#:Zх逹oԩsCL ]|{y~嗃'|ʂo7 ]7[f퉶16-O4>{h6%+ve q9V# H:EG m0@H 83:Z9Khbce?xu ABr=4z6& 'Zp:ڇ[K8>jG8y #Qmk#`M͝cKF3imۉ2O*w6{>G WhZBOwXyNG`哎Aƍ}cu)t9]01bDųG8y`4n Mh$7|\20 FO>)0  sbGPk3 -ⱎ@V"` ֨s .0>mڴ@8;&3M'i*w@~#`(\paHΓ-[4JE nwGhYw! HN#`fܻ0eg1gipɉN#~{95o#Q8o3ewv@IDATw]C`ŕJthGK9ߑ,3 Y"@{H &xi:r3}!'g,G@ z~S`.mL2~uڋ/ H<8:G #0dJ6Q9`B]l4#8@5E1Z|j>vZB ι%3;}vr}&WߠpG W77 H|X:%G c 1"GpG@U 8΀ĉr2̇3 ګpGpc#΀rGpG_|tJ@% i+pGp4" HuҎ@]t!tGp S<#8Լ#Ѕ Gp;N>f jpGpbFur@p Hv#8@s\9}rzyG p HU:#8@,8 N|dkpG@xsj#  ^#v{?!-G(΀dn6F'QҎ#6`v%;D#>ǻ c%.G.G"<ؚ#Gpp+o6ڵ@VݨFfj_4ƾ;% ש;iA '̴tى:#Јc BKʳ>+sΕɓ'믿.'N]vIϞ=o߾E&L slٲEy2e{ү_tfژ@*!I47iEJ)qg@nu_|{8'ˎ;SOڵkeСgϞ@&f塇*4hPȿa5kU[c4ړ#LzZE"E2|f3AI媢Ĝj3 9@8#O$l}}}r5J {%wu\tErYgÇe2sL_~Yv)s̑#G4t6gAs܂%P_Ld"uD E&/vH2#M0Utpm{p$\[SS fi N>}wA IKiiixYl&uan\S?)!"<* Qfc=z6TG?]kgO$ǵ5ɘ$f_{ժm3 y3 y뎀#wHuu e]*++cLCmm߿_żBɊ+dٲeA /ۑжEIQQQz>ztүLdQ `5*ʌ 7Br{ڟezJqqq _ܭQYY8$GHx8p` `SLU)_"vmٷ[z,:GңlҘ@' cPD*鱯JJWOP&-0"/|΀t7qGp5\TƎ+<U,6o,SN jW[n >}z@ jL;/\.Ҡ u!i7) . mE"g6]RtѮ5KDj Ʉ3E&)#C./[֙DG*R%$lRjLtO~$fRcwnY|uN==LSkذ᪶vD{TT#u*ڡH,4n*Ǻ_1eI*YپAz<=9V 96f("voSD>_*-!;_'@ 9w=b: دl]hsmݶ1G!` Hwp ]?3r-/˰0_dIp˕|֭o=xƂQxᇃ/TzַmW#7I$˲& L w!$ɦf7MdS7lzJHJ轃۸.w[r"7;O#]?$WYVcw=u>sL9wˇ>!c$pC|;rˇ?a{}YoƐ裏eIg_+ 3Q^WvՅ2%yBTթ_|[>aȇnU˲Kd~(իw_YdFql׶VUY٪Ҧ *o+?+.d"SdxD*KW>K*_*qTrT*QYI? Ko?/o?/yTȸebٛAE*(E~|T>g!!43)XHZKE ; FL*$H4P6lل@M7dR-2}r1җdLGvvI,JM _rueom F<{܌xcB>c660._LU/fB޵u<'N jPձf=$24!'Fr,}gLTi0eC'|Hx֬^#99Bf!1cٯJ+[3T.IPڟW6Rg,}2x&OJ̿KJeUڱXZ韕^}iVN[_W/%U3JU^W2Pi)]"Tv̒)WjBx'.[RXFM@ @ Ѐ`  }l/Xc l2c,P"`(N۷B/~ԕG25KՌ)Vm*xž_XBMnUIå^jvUvlKS׺ۭMKꪫ&r)w1 dv>Õh,)]"9}DUe}|C7a(:u4 /eN;]^x9"C U[`V~%QM*{7_SUR9j7%uU..;/uޞhHPRxFmqEI3}=F@ 427ol{^cBӃ` }pm ^0`'s;s̑K."Mu]gPkpC\좊E^KOWF4/[TuOŶL~ȕL/SoRvjG1FZyys)s>;Yrg)V=)jպ5Te<)Ndr 7A7/\Ճ Q]ԺjEG* ѻQdCUfDT,Mgy-x+_Q5mE9@wR{ HEiwI׾ufK~"2|eD >@U4i)Rt3Bl2Co<\{ВL0Q:E߿`sg]TkHօzִiӟt^l׶mLJR+/0@ؐ<@鳇w}hE>Uaګdu'SFdU/Y ɏ2Wgdw"T*JdtSխVmSWÉvOͫ`@@ -v|_JeU-/v?&y9sX}ԣp]A7jV3=jW';B -_*HZ뮻?ov$(Ƙ=@AF 6Yt9rχEVi]|\URVꢝƊ>G*aP"''SaTHnbB1^ ®4%L mJIu,63T3LEmD6Zqz !K>@v!+UURM)+O:fdv=4ĬI4han Fh FCbmW0 QXS,1~zy\Y|peHkUFn^7*N) Hکd SYJ+:wݚӳgTcj_AqHJji̕1 Fzn=B{€]ռ;r9r̢D p䄿  s#T#qjP, vsU0/gM@qﰅ7saV8io~Ay oӧuNZC6*amHEOZJ&*N՚.S p< ҪNJQի-1o}je0,h09CMuR) yPQ'hT 'y6mB#rϫ2-[_0Gw^떫qںoRKVkmr020,C;&71@ @`Q"d> I/T,rsi4/>Ҕ3&=ggjK#`%nd7!j!mB^tty!gU&#GH/OB],#P{` Eyl;}$!upڬ⺾ D\-!^&|g}@tg=xɴ޾QkqM`@ [ >!.@ .}|LOԎP{Bu=nYB՗%%yQӑ,d}2_ǥ?{>iɼs#9c`@[ @ @Q"B3" C C2x\L@0 _<@A`R@sFq95EӘtry:}sl.醫;:Z @ DMn\JaVٚ#Mr&ϯǻn% HKxf@ F@ c[p8_Y6b3AJm, t~. !x0[k,؜s09:4RZH_I/4,o~Sz A8EO=جI.1tZ~̘1C $}㷑6dzciHmrE@ @ 8.cgCps;gYbE^):&S9Se„ ҵkWcB',Ni ʱ` H{AQ pHu@sD欬,1bݹsg7fa<j}F!}WӦM%K؉Ia$peˬMae^ߴ6d}ӓ&M3nH!X#G~E@ 89q;//~ՠX\'0G~܉+˖-[})`!\ wՊ!?^Ə WֵGەJ4׹aͺqGʄr ?((C@sDsN皛k.xc?k`@~ڵmk;$XmZO{K_kaE2n #Jw_93jI;ʛH&~ڵRSܸqc5x`@K2}1vZ *ұלDZ5@sEYi*c\sywyyWqB*ҘCy~)߹;%".Iz _y)ٶG;̶),dǞJ)ٴK vжL<ڱCG# )b!}{n 7Lt(;q7  a޽ժWIhLjՋo-m:`_Sy@ *Cp`ν̑"۩ܤk/7lͳ=yrV=6ؗ` Tr@W52n5UZqذژ̤/JCH@鋍nHvRǺ; 2QMjTM&aaުc/Z]E!3TsJM4~x.[L٦9ZϪ;-mTҽtrlNy FjzNozh#΅kʤ%_qlPo#f[Eso%Z~C6ڽ}_-C]u:jx @ .`"|Rѥ2*u(Z#OTZ!4Tx:u`Ym4TczZsCY[Wj>r&uf8wUZ=UiUő پ Ss?j^~iA;]`4`@ Ҏ/5v;V ,0Ȥ|ԿHԞVo1Ggy~^,֝\f,O],U2GuEGjի˷mW6O^\%_`jBMr׌u?'ӼlZ,u-g:Q݂bzw1qSӋûK@ CE>kFwWOUs'2U'sBRj\QW\6aNo9;SGBm5X|JC>>Yʻ^ 2_7v+u@I-0@ 4Rc?dK̇yzh6wztR+jSSDHo`u5մ`@ = NHBdRUrNU)njJLlnj\aQ6i0`5(WG@Li.INO;eLSmey)T[t|@ ' ڪAE;ϓz>P:UWU1Ur^&Uӝ0ST1-FCOu")De\v򘕨iQy*I^i-n Q<{>{aH,P{ jĆCtlEQn710}'ʓo=3Jf:dTL*d.z_Ok]~lPiFa&6fBkҀzz⹳F8OO*u7XIFlt.lsT~*svJsު~9ҿ KJ%O>Tx>{d9;OհtjT_5{ہ+PG*6?{̐j,w\-8M=Q?m$9ך'=2K"NcFԂOy 1wtS6Y(^CHsmM7'u4t}d:¨$FJd^[Nz:=xkKd ʤd:iu%ʕ+SNҽ{ڦ 臨53xf,ڨʙEztp]V#˘νL:@Ox|MT:<\N6kqitsy6S6S j֤WXu%2ev&_T6'Ç)]z$TL߽hz}F^/ZJ1 N`Vi \y ƧNƏTUtdbG(=d`^E!: µe2+'T s肚RMXcƚ6gM̑%'J6thvk9w^Gm~Dm}!LKSg^69^_j?Pmq(w:tPGB#yCmqz-YvtQv%2|pώeݺuҮ];;41++K}6/^X֬YcA?!C|VXa&-...]XzIIZ\}}ϗnݺUqF }{n0r<锣}=ztO_y7v@JϤS?}Rk\W/ޠ ;t`鼟-[TN;!P|@\׮]s}$|M;>NCfgg[:}tSy:A#xy0~~<{:􂏧,x~۞3߳c@:<:t3u$i#@Y!$2\==q8T;+S?[nEN;4yG?GL"'t邷7F"4mGeY57ne!\EU3 dgl:^AZ *X˾F;.OVoh<5w¡%2^%*hTjo&d+ 7!âgzv'K@X4OWwFo(`=w͒W2LulPTwTjxςao!@rf`K<ίnY\'e"a EUh#3vO}Mi Mf<=ߒ%KdٲeEKرYlٳgu:sߜk?[eeeAnlg6ƍ:1LdmxfAo̙39 ѣG#s޼yKNN&zXcH, ?a7R5\S̀S4_uUD{カPfʗ]vGaOY_|pڴirԩSꫯ:Y8},4zyy'ȘSN9E&x0>-[0lpYfwav۶ma ~Fu|wI:LCիW[Ygedž%#\r%f?O 3i7p-7Iy0C=ÇzH?~aDep<àz뭆}#FM7$'Y̙#, X;qfFr}.6K.~~@駟.'N#6+K[IwOW3ys1f#gya /`O]h;e-}WO~081z7ްxV;gH9 31-\C`HXh; ɘF;{;W~C+4{L/'Ohۦ?Q_H,uY˂|gU S0u:X@xn'gKʈPY9LOe,=1%(h#]eUzt֤@(|Y{#hSsT]迸L֨Dbg|]VI #WNcjOveVo5OO_"gqgdj}$S;uSL C LllqfU 땥&]Y*+S_m*-9]]SI?CINYլP}&/'Y~ uC6Lx]js 鼫!PFi[t袃ݬ_nVy`>CN>@ĘS TmBRY Zu!X6,iTJX<yH㢢"ԖN6YI`’L]2yxH_`xꩧ֖l '=Ptv&O]wT{ ҤO~u:"h~%XBUa+;DjVWS[#L>uZ| $GzcM>pGdRu5=hh@*KO^jûd.ڗ(S 2'B+y8zt89+_t.So2 ԫdΕldh:zj;+SBԸQ3' \Vg Kl=uhJzr ^'d}<'feW' FHO[ݳ.{&ВO$s]H}W&˧Q_Cu4$w:56UۯthHLJ Ok˓tj 2G^[;4p~ @IDAT YUCC2l0P4Zo8~T "TIs"= U^YmeJEN߽O[byC;uz՚<8_eQ+ \e,֖ujG2@ 7 UoR$T-nAat1:VǓf9RܒtDf/o/fI@:F v곗Odor4Wh8tefwT A/ueK`0AJ^`qxߩnt0hV$&x0j:Иwٯ~m\4"_wkt'G|a&ƙF;NNߩb[De?:1[)R=l*i>aDW}1D W>8HEF*Y:wZsQ@麑ݪ'Im!#jP҄o:xW)ID3< /Xεz sXl9\7Ii\M_#-E…zsͣU{.u"M3=y`^׎]SRWwhn:ݪWW@R6}n2OIƪnt9@2N+$Uu|.rv;i_*逷M|hfe\ SFyt\RZ=omZ>˷oqzS؜7@  >~J2x4WAI/)PUWDM]KSӘr讪(W7pN 2H?ó6Xh\XU7ytBoa:87Xx(6/SCxCya2JL6"gL:r)XjvJ?e4<`BZb㥚1=di,-am`>kV9YdU셕\H92^j?K_:upڽY…;Rng(PK;ۿK@AvݨB/.j~k ݖʈ]DtB[|@ pMϩEg6͐L<.ng,ݪgRxcz4Ѽp$.C23* Y'"e.t$[?9anWQqEnff,AIDFU31E?{\Ѩ:}H@ .N-kWjzcyc{)#bX-W,7F2,ԑ\=)Q U5WCK<4;TѝLOa3\u|) RmxR}I8.U|rbpV  sɅ2LVHXOgv<<uU=nTSNgQV؂0qxGNːkh:]*!uRPIj<1'S[5xW:^4ӊSq9_]BH y鬒['toIq=PQ9ca]~EW @ PfqWWJj\z>PҾbgz1g_^*S"-zѼnbW[^g 5oUi&yҸGPӀ@řQ=$pq'=[ǃNury'WJhlJ>暗,#` p# ^>_|<,[G<虭.U'yrДG:60B=<=y8!@ 8]suzj \Xc越0 8Jl GT 4xMPKvF{Fxxz4Zʸ@j%iZ9E~SmO?{y|~ƀ}kTj{M^HDRD*J+m*&~Mn4!F*F0t}Ie׮]ՃAX#?rhm!50}qV+e}:r=yu=~Ble9@b3WUqեp($##nI@,|Nޓ9*-x$.ޤh_7ʬYOo[ρ@  \tSTz*H.vPI|^q3?oVs2F]c"wJY`Ysj#!@BՇ_3?Jr'^,Wm.VB?vcL~]rn$͛i[%)+c0AbR?vڧt퇙K63O;r oYb=۩E7==CXE:*c񚖁&%g2G^r 36wV<ȳHNҺxC+մbu5 H#yAF#™Eryy̞=[JKuٽڨYh촐/ExjN;Wʔ [MHLБ2ek6KS'SfH11RoNg҈#IOOϟ[{]jڷhJփϵ]]vRVV& .4@ @20H1p?o9C.2B~S<}ySϲ:K& \gn0r*:8y?=_ dÃ{սVMrÇ٘.{9\xHwt:6Dm++ d22F](Uʄ2O.*6\0=?LTqkj9TA];@Dڗo,X'_U ͊GgWYo_]#7)v2;%e3e3BX2dsJ o4!F*FΝ;P>ʰapׇKxbw:H3Ai' պudm۶j&D@ 4t͈aDk;hl-xxd};O/WU]jס;8\<:Ci'_Z&/yV[0 @([~sP^RV!҃wOQ'0֌p*yLyf|S7v~]̞cƊ2y; +Ki)z˴qj;gg#Լgh7pbJKQF\UiƳ Z^O?o*Zl+F˹{eҋTջk%K%Bxyוb@R877"%hHX۷llQ߱cMECҜΖ޽5^so@ r@R{ZK.Q)"䥽.m/O ႾJP"RSToJRrF K&\` N@zMUrMԟ8 fշ맫VhvPyVPi鬶zh뛣EsF> -+OE5 dL *uRW0 ='۴_~@d-إ@ˍ~6IUTi֭o d/={Hqq=\SCvhsjxf pcr32gрMdHPQgUfg[)QU*YԣƤkrU}nm,ǶFOoJWUimV'mhz1Vy!`&Sl<*3C٪JEA`F` JH窝gO-#C/rE1ԯv/ ΠBKuzQlT"]sdH*i/!H81ॉE1 ~~E܉Z$;͓|ɓ'KEE٪ 2Dϟ/{6%KԩSk׮zG_RuۨCa"@ -9K}ߢҊVU *M۩jO viV{GXcGl)}2sUJz*TT#u8IsJ&fRmߥ=dҊm!:({u^j$1l^ZQ=kW'4m/IZإj]˺^m(ݹO^ҳ s;eQ;d{lPzSd׎7hCU\(k\߱h\#i@XLHo Grx7夓Nի~\x&AApiq7{ <(Cp-y=|";!@ G@XSr5P8տug4a.3Gp@u] qBJ74eY>[Ezv9I*8WA]-[v{y^qA),^\?$'%6h|5F*Cβ+pu}mg꒩^S+FE;Jrїuz}#vJ`^zNRrT%{Gv3Mqݐ[_L*EAjt|5@f`D52BNm*[^S!)/¼ϵwqrE@K,`N:D?cB`<-Zd$2}tYt||Н1u<8M.)k]etZ9@q !JX)]9/܈-][ N›zw,YN:߯leH/w8_ 7QU{+>0v *bu9Uu*B׷{Vu}n;&EO*{5ZQz<5*uȖwKg; ssr230U1@z[`O~ };ufƆ͑}riO[N ^mޢv c$:yO_y?;b?:tNJ+dӦMciӦo߾k̙ '鯍dhFPjW^fwX& @ h>tW猀g $AQxJR),?=|\qդK&sڢxؑxHod%㚞79eޯAzf@+yOҟ^'; EEE#bЇ@- u+aDpm;bc̙#%%%2zh@\x%$cfg5F2Ef_c׾5wkm>ҧO9s&OK;@ &>70G?2 ;QɹX{x&MCեZZubM&U4S`Yj?qёՅw*C #4=P 9rz8ݍIw\Nxtz8hU-ȁVHMTbUg(K9}[UY/sWF-[駞;飼 䨡@"a:rss%7ocHv-1!x+ $'=Rnݺd} ]h /@Fe!^zI&Nhk׮7x&33Թl2g>cLS/Y^yӟdL<` E@L^|Iq -N|GjOV^^.?O-抴;S#3K.*}.Ș 낗oZ@ 4/s= hrv _r%vc0|pxڍ7hi|B;ɀ; nv؍> o5ʌБz\ve 0]p2w\c F?|6l---/}KrM7SOqHI`rSZjI}CĨGO}JoȘ1cL*DG+B@ '5EH03_.E{ħMd}'IW]ݦ7p)5GZ2qC&=vY >XT ]D@9 yf2\HJ0h H=TW\iG0nG*4w @^7gq!t+/Os$sYl~=z 0 N#4YtxEd E$l\y`/̡8=A:u;c k\7q/bʐoЀl H)]K?HJ ]믷 8Pbk CC^}U;{i`Ǣ"7W6}gLUW]egvCh=;2Nސ4$(3PL/hѺ·-aq4B?;?Ms1z0c΀D00 =cL[o%9\I:xE5f8stl=C!tǽϥu!{a`pK;H8u0-`! ٺuű?a~뺬X:߁@  _3G8$0HFC`^$9HXSk֬y7/G/-}IJY>?Qϯ\Y& &<[nE]>gc-UdP)ynm ;I!}M[<`10زSŁBxɢ~D}{m{FDsAd~$B @BzjSk- 8Cal+؂a {.n|Ć`A=00H)m܆D~@ubXWP+/iL{s̿0Rh a>L?C?ssL nQ?<*ɴC BZdzsM AO]~1 ^H/ wx0 q6oخ?HZh=yO bvpH^arhIS]ҒN_@ MB2c>gM5j ,yM5*T0D99xX$?L rr*LS'H*H.SKfZna7 }e?y u&8,IW\abo3O# =hBꃄO~MQ'  g8dM:y5$ Fߚ>(A4ĺȚ8LLwW!vYM%TSO.F\?*m )bw vT`ELK,1cuhd@ д`g^`AYv|BW}6z$wsgB=/`*GA`T&1G||eBy 6n3 QO9 l2VgPсޓF[i… Mjs&m0e=93q N0 > j\_c,·]_Wfkҡca B$ t. 8~@I e 7)UR Fxbg VeK @G(-F,'XX" ;d:0y`L`~xqI / @T'fNseL|%ۅr@!aA*[E}5絇 H yͦ x8vq@p݄iuw,ZH^z%cP`r#ct&t5@y ܑܔW9,ɴey&y׹ $Oy<>'ҟ=%]kV4-@ Pq x.NvӉC' =OzP+$$āK% ^xfx m^ +5@ !0d#ג0 %kC 9C|rOOK\2y?{N,6!CB԰cけ!E:k:}@ tHcOګ-1bxh x!6|J78@ @C  Ѷ "<>06U|'!ʸk@p|@ `J 44(ooD&;AxJHL` W,_ag8捈 %@(k5ai//H?6P9W98|jۈP?l`' ێW@ kc2?4F`@ >prF#,X /z[QQyԢvǾ~[@ g2\zgp!0O?mg_pp{gdɓ'[^ĵ  Hy4@ҿ1+X uH?`8UB(@ ppN$eJٵkg?˄ b>8Aﯱ4 HcyAG#b8++څA5B!3-X ^5@ ;e˖UϣNGAAk6{W+€0fו-`@Z{^ւ/|_R=D9„ӒQE 't6`$L{ꕌVZU+1"ZWN"OH>}r?Qc ' gB\m>z}ז@0 -]GO@,N$@ i0o&&954[cemD@ @ @ᰎ@ @ @0 -'@ @  @0 u@ @ xi? @ @ h8i8@ @ @# H @ @ @! Ha-@ @ -`@ZO @ @ `@h)@ @ h@ @ @ ppXGK@ @ 4K*++OzrܼhӼ @ @ 8LdddX{"Oq\i_-`@ZkN@ @ p|pړZ2.y_W~/@0 F@ @ 8_^^yiݺzꩲj*ʒmJIIGfϞ-6m38C233^{Zz1]R/jY^l@ @ Ojyyy'nu.=px~x?V%6 @ @ 8${-[ĉGk.{hWlYl [~i۷1'>}L4$#7xC&O\]3 zHFd4-syy̛7OfΜ)+WVZñfy뭷dR\\lѣG3D-ߍ3D:!!21g%tU$M^O2~%^8g<+0!UuAZ`@ ɨ'@ /N:U^~e޽nUPP fTdڴisN Q@:F y|Qɽk%~:/K\q] '9վZ/+YHvHLe8Z"^;L[o(OFt%YOd|J( IJQ-I櫾O#Y:ORXFM@ @ Ro1ێY`I4`4ڵk''t}` :u;*4_hBEQUhYQQQRlrqFƑAQeGh7hQFD-$$yoNfBԐPo99{ &),,4"tL֮]k6l)~gHNke oacƌX&M\BkQqo_þ6{l]v.ΆOohV~,=ӬhG;#̽VڴimH̩;/^5ndEE=}eț+t4Ypj8pu"^g[ lE"cYf=- DHeK'{V/(knwo>ʝi 5Q@"PHݽ{ iӦys]wu^wԩѳgO۹s1|jܸq~o^zQqΤ~ >^u{G:h;wm߾E>kveyC=zySO=ewu~Kynvh!B`܏a\rP||6ٶw^?<6p`9zm͘/1򛿛dyyV1br۴q 2ޝ?_xo&/Z5hm;w ,;-ǣ1 +U[azh BTB'-l~zM֦en ~NAvP?K Lu%" " " u@soݥx'NC߾}}QNwcԨQ.J@g-bzr",}.xWyü~m{'a3ga]~.&.].ay7| ]0LF Q@BcfJ\ہ=f;e66۵ +4 WYl;|6?n!v9OjosC1ҠA5HH:dQL$3 Ip[%EG-zc}jiiٷ ;k`YY䯜դ Xܦ%ɬ)F-gafF[bvD@D@D@D@ |ѽ`ëƍoѢ/)mذa~ bK2oEwuGa]o; \hx}.!p?i$0yg ፡`W_}5\{XSB `ի>5b)Vv~k~euk.rHvFm1 ¢r3)~3ɦ^zx⟇;4gC:eim;ZZ9myyE3FC% :=/nєKqWfsYbq\ke.2w|m4yEO76ljoχ˃D|>P 7 D@D@D@D.⟍aW`c7]IQ`pB:Xģ1Lp+f1׃euly" atTG vt>B誫3g+t1ap??>+rA2;#FY%Kx,5mGEVw<((%]6>. *-.i~;f~$c珵8ƍ2"۶1{X7P@$ℝŋp1ꥹ/wxiry}[ķ#Jo?^5Ҋ@eXmE?qAss X0ލxubvZ<05mn ?z exffA~E;[Ibջ@5c=aSfͲxn&[="cs ႛℍ!۰v/ҙͪ[5USI~3ޘ3Ӂ[.D8XB,r87 b)$sê<۽;9!Upu2p4֭]ՃDĹ'/Q4r|q:~Eδx}0٢ۍߘP'蠈A5MY "+]R|76G†]!9IM$U" " " "p* (5k.tӄ U^z%w23B<8\C(d h_ׄI .DK.Ę:f]qpzKsr$* &6VbE@FQ@1һ_< }̙{\x7GyN3[<n~ȽO"$l/ܗp0Ԍ+-=bssO~6(N" (rv.9l &1`; [%/C4"'Xjw΄=ť> M5)㗃(K^W t&y&" " " " O =xwlȑ\0|OۢEOF"qg0\1q'=9!LZg: w8 Y<Ϭwrlşķp=HA NRr[ lAP./¬tСP)Xռy{_'5|/>֡:2lC X|>KQm;7=Jz}k6KuF#}63(=p +>TlŇmpxh)@p OGf83í7g<~,;ǶiӪ`@FVQY 5񡽖،7ɵè IDAT#,~e ~s`ⅴ܏/xӗXǢ[2s,hO~9a@D@D@D@.½w>ynDP" ;vʕ+= SfEgn7ftKB?&'ەW^iSL??o EO:%tGNw7VzlĉgXh89~5;bXmF0;vHD;S1*7]vp;%C d |^4iŹto;,-;gt=g[4zEG=ҳ,Ay%*xd3PkEaIO uMc.HJ}6J⣆H Uw" " " "PpwsC1߃ 2; Vڳg믽t.ʠAj]0G/>E%vYJ ]`]~g׹a!߹X"b SADɂ8*.癭<NcZh̡tgzQn.BHEU.]3yc2m0➼肜 !c%tR{]*VC; ;# Q`2$2"v{vN!մx/(8;"$Ѿdt?Ňj" " " "  8CݹHfçxps|QP(qN1qe1VbR; (2z!>}Wbs9XϧYx g îB3ಛsy{ټ'> Ϲ++|07,)AU,̋It?(($vfhٲˆñb$l1QA1#!5 Hfȯ9tF( SQW WaLkbdRqB3C!&pMFyy.9lr@j:D ~rWj•bV?bM6GOE EEj Q`ߥK:tG[Wcԩ0 -ڿ}Տ0'p4ۛmƬaf UJi#d;Y|b-Ym?+qO!a ~.s Q V:]~ҤhA_@qe &/*Yaa_ܟ$JKG X\^\ e(!G$9AT)GD@D@D@ B V/10;z ^6w?w,<`-V^ ,駟LlIaX^\!gŇ߁7si2=N(@XZ/90d) %y)¼x-^%ռyw1:װQP%Jw2 )Zi[cnR̕%ǁhf;HKD}1țȉӽ" " " "  8x΅k;|O " d  X |?9*ashũ*V~^pBR.5 (ϸVV;W{5bXzmM,RՇNB"=UTp 5;Gh?c>)*s B8ST_koꮔW{@ǸQptJD@D@D@2.¾\H8xa cL? Er?VӃ rS|݉~Cb|)! ;^ }" " " "p =8 ?s O]S;MǸ옧$@G'E@D@D@Dv*w U‹=cMPxر-:T$@CM)J pO{VZvStښ_ w_f3h_i *Ht@D@D@D@& 6l;m"Tn{L87 CtX-cw 5#@a999=s09WIohZ8 pQv{kH)NMb7nlY؁9! 4yiuOGcs&86v6 T I"S$ϻ討ӖHSD@D@D@D@DH;OD@D@D@D@D@DD@D@D@D@D@jHփD@D@D@D@D@$@7 " " " " "Pk$@j $" " " " " Z# Rk    ZCHo@D@D@D@D@DHj=HD@D@D@D@DLIsIENDB`docs/doxygen/images/GeneralBlockDiagram.png000066400000000000000000002675361352176506000213030ustar00rootroot00000000000000PNG  IHDR ?tEXtSoftwareAdobe ImageReadyqe<$iTXtXML:com.adobe.xmp 佮kIDATx`U5Æ{]V[v{*Ȗ%("{_@Dd)S)((Zoo-`y>엓uMNNj, BP(JY@P( BBP( X BP(T`Q( BPP( B*hT:/^ϧ_hR( !7nlggGM`SPDEE| B^dffYfҤI4+:U(JƢAP(E˖-ssskÝ*4''8]Ulh44+( RjN͗CCC<OV54( BbLvBP( Q(W^-,,l֬kbbbtttB`Q( ByQy Fs> ((_~Q˗>@ @O>6mX,D~rvv&!066Fyyy999P@&Mܹ.!ԩtȑHWWWKKK趻wiECVVR `Zzj*o)`omm_T8o}J} 0`". cr_ Bܻwɓ H-R1ުHDIJ".Pwww^!G vD")**e˖QQQ}ٶm[ӦMq֭եK)S!QOq v̹s0^PK\Ǎ7F̞=Cٳ ,C 7}Y~Pa#/^2IˢT*WZբE k_ܰaƍ1AUF }'Zf\J e&_q\zeؙ ;ԯ_忭ӏzP$}OOO;;i2C/.DB`D*Y΅d'PN)## _p _=B$cF>;;:JJfJ NVVV= J02a„e˖Alٲ%KԩSO>m``6mJOOZbCvذa:Ν4i _׮]a۷o~~W_}5z~ ~I5ҧaÆL+WtvvF=A% vH(HP#+ x{{ *WWW2'c<gΜ>|8lؠkDHLϞ=]fccCBw'פBˏFr?]ɛ5}||;Y|Knݺ6n8 aC}wn޼& `Ig9r$hƌO(oO0bK8;v4xJJ C\~m۶Lv47nxB...HիWLСCPuϞ=~,H(3! vѻwo8½lْUN122 , Rc&jyъ2Y)o "[IAh&F̕ D Qrp5`M+w*O999bP( :tї999yyyUz,ұcDzƍGabbbccC?L>ʙ6mڵkOod2Y׮]??.W\ N:AӐ"X\7nhԨR, YJլY͛7mDz+V aP]«~a^##P$ / 8unP`NiŠP(;jZP^վiҤI֭!DF0gΜ;88̟?ʔ))))>|xll, P9K, aAFFYJAAA~~> ?C.]ڴiӶm9ZjyPZAݺu344|npKNNRGff&v޼yK./6mFƍ7i$}޽BOCh@hŠP(X-Zr׀r咞qƙ3gOx<^!BCCg͚'ŋ7otqqolllo b?' t (f://N:3 ¦MB11qذaA,݇ٳgPxP98+2™Y(Fb7k 2?88ݻ7DD`1b n!;; цء5YnFP( lR|Scضm֭[װŠDETllltt4z'N\pt=zʕ+QQQFFFdӧѝ;{lXXX\\f5PՃ)pBP(5 t:ݻ&T-:w/4:x<ItBP(Jm9[%/a{ŨaWzTBP(J%CBP( X BP(T`Q( BPEP( BBP( @iP( RPToj7BP(*E~~!]]]+=|BP(J-ܼI&J2,, 6T`Q( B*h4tjϟ?x۷otڵkѢEٳgϟyfZ{˗CG-Xp|̙3fXlٲd͛]4'&& i.]84hl,X믰iԨ*1A #ج_Yz5lz`A59 XIc͜96#F`l MS:0&O>O?͗_~۷6xфH AgϞYr%c?)│~g… a3`Efؠe! 31c?LW8;;=A&le_~ ZY۶maafժUAn067o$E|#6IRd[ٌ3`3r2ENQHgxgl DQȐl`7c-Sdq L! QPXA16xaSk 6* lPP* 6 *m"# e ,wal(~=_dx0a :uq&6xIgu6h4AQ?~ؠ!b}װAcؠ ~4k ;Xc;6ha =uiф2E+W0 2cL1/м)2t |e #b?q6}t|g/}BBBOTTԍRtYcΜ9g^xݻ+G0|L6V :ׯ#?a8|y`"2d#`~`5l0,O<0  r$f͚_7:p W^3cǎ<xP1}+W _2Gy="##!6ҥ &LV^`*R5 J F$͞=r:???'c``0eʔ9s搩e(OOOimmVJ%l-`!FR`"2 333KXXd%''#@RUA]@P(ҠAh^I_B@0g yqoC^3JI&'+KƪWBx!]oKBP(]v2%k,k׮o0Bd$%xݺuQT'>11Q.XLӧOLJD, _†\*JUVcǎ&Mx{{M6-Ye˖=@̞=;RoxfOccc!Μ93xn֭۶mAAHEEE}6lXpݻHe˖N: 4>_g;cƌ9t"_L4ʕ+ 4ꊉY~:=$$BCCIjiR(J:`nnN ^ӦMFT`U] sNF`TqAJs3DXt)8PbbbnݠzIseddA?zaÆCP`.]ZnիWZEV+ ϟGPgyyyڵ/Ļh"8wyuؘYZ() R@#qۘ{̙`333rtO LMM7ol``ШQ#Ǐ:۷o.,---66V"υSN z- =*2`/]3 77Gy=ztڴi7i{@=HOOqFf>È.3yd???xA}>}I]Dtdh5B'O?~≮BcA"(Z}7oW) Ru@s qW)W`-^8%%W^ CCCO0 ,''믿F/~s| KvLL .'ݲe Yt3gY ҝE}nݺQUJÆ # ! ()TH(:u@AA,,ӧOrȑ#g͚NfA򬌌 rѣ~z?eH7yIP(*ŭ[^uCj)s/J\WdO>k'O1c|!bG5fdhhX\\L>Wqqq=z ],:*$aϕNef͚>f)BTMaڵ[l3lfxaׯ QFWSQ%qQHҍRkolhԨzqәSXXȊ?xn萖fFzBS h6lVwqq*5J7^AG^ tZ3WyyyO>}vӦM===uJFluu҃34m=eJ[+(G8F,HVGBwcUVǚÇ 19)sccEΘc,066yW.ErԨv޶~Oӏ[)Z hlZ7 e,}DT3^iKTBܸA'fJT 7ԩS> Vo۶m:uhVP^1@n׮˽ךR[?4)1_u@bWO:y￧nr>--lPqYZ֠A ׭9ը@  KAKǂ ǓHDvvcRڻW͡R{جWҁeKS߄ wfffZ[[ A}iK_+v)CL!+xz X'ر ,ʛժ L+pvq|4.33ʕhCAZZL@0*'vTGDN.WM}ѢC_HJ eEo9ضm5>q)hg)? 4y66&Ӧv;nGmy(Uiaq,kX C͒JJ6@Ѫ|![dUZ>d#- -1,Fᠸ؀ d8R*HիW}||,,,j,7qqqPW"b NR* % ,ӡVwMNN?FF&-u+>-1LNJ۷/u ?|%%&|:r ZP)~s<'7oFeK}O" y \g ZaÙ@g@pvguǏEìϗ{}#rsb71hF:[xѤM4m!Y,OOW}wzJ YH,_7`p$ =BRQ֯rU]]Y-ZGZh[]#:txٱS@z,B$OѣF = DPE_nq!sKP{[7PԚ[H`~)4kh ;t_hhw8255h Hlv^ԽI4P%u?Y'EVZB݆mܱvnY^7.4mJ",>Fhw=z rm#^?Z,-v }%7{5##Wz٠kDorW&[%˟>}DMBy󌖞.qAݩS0AXM+V ӭR98XΟ?JMz:ܚ KX~ Hg{bQ5E/:,JKBq\p.e͚2o44 j_Ci~l݊FV\Qky|\V\\Wn&sk3KjJZ>l+1ȋP(I2''?-,Ȗ UᔞW-n;v hiiIBP>V_I$ܼNq8lf#$DT?b SK;`?cή~gmyxj`淯RvO{{ku:Jd棠Tj^ЮU O98_{wah~~jc{yWp"##\\\*}w*.m۶ݼysiVPمw 4~Cܼy399988BcְtIA},r̪,ES@ˊǡ>>>IIIXkժX[[uʢѰ},V3Rw [2{ՙ:9ď3fx.TMZ#*Ot>;1`qtoje"UJ1KU*dbPZ ?B _?ڨe¼lk{7VjGL^]򀩴F-@+ݞh )-[˕ Ο?-gg瀀|R*EEEj",++ <ljll ǩ0T* . %Ert4|r~ԩ/D"XsΑZ)8a?##hT<:))-'N]gyqq qcX C#uvӧ&&nnɓqXpFXITJe)vzz_4gM_B+6HAiii `cbb? ې!Cvu+V gϞݢEC2UGpplmmaL\E߾W,T/ E}ti>NE8 ;v\>|濧(t˒5r;O+YӧO[@/xY<>[z VVVtҥK,X4sL}|󭬬,YW_M6-== CHzp4 ,S(Ϟ=+..V҂B T*Z%7AP|J;OF mvuJ5Q(i9l73@b ]jUkݦ.Z ]ݹKvb>&yʵkN|5cp5jmVfQ%CnPSuEԈn߾4iC244ĉXB ?EEE$4@tYH֭[K$"!z͡C}zꅎ,TR ]ŜLT@>y-##VP@Ac!kkkK$jM4cd:Jq5w}ƌtV!llɢ촴bUϞ+W yPz@?ё#GlUTTܩS{@aU*?jNr*J䚕RMsJbeaYR#)?WTnFI$W Yf3glݺʕ+={⢯]BBY^^^PBG?S:uD"Qvvvctrrr+''2Ĥr3@~ƍQYBk׮a844NfʋZ9@ԡOKe29'M LLM%|>'#-͸d̸;~II9|W(9o E%cBYFh(=D"h&&v>v-B^rLBԨOZdĠdžReh4ӧOwޛeaaꊥ[%Ju6lؼyo\. Rhllܿt탺r U~~>ڢM?~hoeccfkkkii UehhX[xĈbxҥ/Yz5778p` GAE -/:(1c:UŠѨb{&fNկڻwC;{>Uɂ0$RyF2کS8*U*_>xM~gݩS7m>;lغ{`(Q0~¯EY#G_ K7)]OT剨իWŽnРA۶mJZ6lXɗН`յkQFA-^յC>>>gW^HHB{W-֭[g䰵ڪ>;;{]t% >NeXhTbd*/ *]533ҳ)9#l_˜]G5%DbwYb,INN%BAʳ2J! >)e2Kgr" .kՃ166vpphӦM=988tÆ {&{ΦZ>)* 3(H]'t9[_ZϞgf=oCQ}0/0PT閟j+K7o6[y/&iq]R(afxHT~zq㆑\.ҥ!6oܴiӧOO>"## >裾}V{͛aÆy>>>5 ͺPUKMPfa:3sgz_dϘK< SAWǽ^ e@ ^+:F ,~wd@ZӬӛ2wsY&!8qD`` -uBZ3r4Yf̘QXX8l0|Qu6gΜ76lÇڵ+l`ʼndḝ;w죣Ǎ uҤIO< ԆۤG(e&c50pku2]3K2[UiƟ ;Rʬ!Bvq1}Wp+GwAwmũV5 :>}:!!-Ȭ!"t/!4hK+++SSSKKK#""RRRFAҹvZxrPQyyyPcaaaBP$A~~~ZZTڋSvr'f䄣iH!߯9sWRj?dp󸸌;#'ݼ)] yiә%K[(_;LV~?oΫ&ڳբJ7G޽ڙ޿8|x5%]Tgcs4PȧBBSs m -Sf7jU7zWP3}Qtttzz:Q$2 =8b^#bI>d٤"A쎎 8333FNkʕ]jׇάLJ k5G`"; ntSv y+4ؚ1{ƍ'ck;v^oo6j5DչÇj:4굘;]=_}j$+Ooر)7w#յKnzfvbfooڳ{%Ksv10@3F, ʴV|{tH$BrPt!X\K3r00xy7x tX[[36r\P=WS򝧖yLqx{h ڨ"4vDAWcҤIGvGɅpwB!p$ὠҍq@Vŋ4+j1lF-uGFFF̜۷Bed(jwPJ2D<|_ǁe/]wĝHל)njm퍛N \4h~?`۞SUҥ˶mP4+j1Z L Y Ԡ51&LOKw}aȌ?rvﹶlY?La=kfY3 E\- \μ{Zkd$ n]K7pKm4%p蹨{ 8}PQd*yqnW/nbbV>~߿֭[!C`4hP'RNJ?{R |×.F^8?U* BGZ,W4WD?J;~lڶG?w#"jMT޽G?c& vv&w┾O \Vh%jQFW^}a]]1 VɓxR5I-[IeFv1)Zp%G:df~6@X&ӮWFi]: J>[R8 LY,M_leebf&iPqlvV~께lt;a%% _JUnAζtR O|o*K#.dzݔ -$$$0PEL3g>'ֽ+P>>I…Kw"Gpޣnz0ERr֬쭵}DŽGݵkerz'#W5UWγClZ.?},sm VEsfR 9OIߢGQ*I$י̩:uw柪#sIP+JCPj5ӟǧP3hO< O+ʩ@277IV#77NR-BLrA&"#V84QF "tq={YAP(U-y6sxbnpB07ɫ;t<*j}>rdx̽#[ vTwk]ssN:j&yyS(qqO2y|nf>sDZsye2TuԴレsket=G1nJ0Ѽv/pzvA@{c}6i҄V?RiȤ|,{vlaswo Q իZXמk>vo kyy[k>w<8q<**'vQZg 5Xܤի _99YSETd4b6RޢxVmNxgS[6VWX1?G؅й&,6$@"8)9/cw&]j٩S=Ϛp7صj˖>7k敗'i_t74q.bѨqih$ BQǟۦoJRt- [ _i4}hc+ycu̬kogaɿahSvX,!!}Ԕ9J\ދj, O?3XƝhMff&|M)o8H ={ ,J GS ͊Ztӏyd1V=zӥk.]SU㑽/|_ݗCZ6{{۟81?uR_S5?jwĈF77OUI7uΝBoȽviiiy…0DbY?1"s,{'kLh**///''J%;.#*.zھ};XGc?g]gоF¿m48doV9_ Ѯ+Hxkkkȝ۴vvv:Ϟ=h4bXT r<[03_D L!dZ  B!!Ja)all U󎏁Ҷm=6z4 ($t]>znoCz4"ry~~~VVF{TM䙿d Z  B*kxQUu? B2E7(V]FRkȎ G"2ʉY-E?a;Q ?ѥ]6.Q( uDn pppx?VN1 5`$Zj ,)>m-Y-}BP(w/j8ϯꤧqX=fEJJQ[lo7RRRLLLC By &׷janIbáZ6p6ȴX {#WX)nmP*YYY\m'~A",l5kl`Ԗ6jy5ظZSӾ*o۶~۫W/ZM)JMqV⬳M=MV^$VgBpĬinIq)NmiҪG]k)!||3[°ή6fLEo aÞ322oSH$_r 3l ` ڙ=:d|?Hz^|jLnaU[:G4+KڛʪUP(J6sz\ShT.J+ʉOݼ@~g&,h5af4{*1/=g@+Ke,>KwfA>[sܧ9)I {wl|,OwN5ok`xmɺk&Dr¹ޚ`-J_~9~_~gnذa}љ}K ۷###-,,|}}=u}}$аݻ[TTԴiS\bmm^*IKHUӬKBcXaajdfs"#U3Cp6sswv'B{UJtҙ'OsSl ͋U[ɏ:7Rӂ_M~[)1[#5o(;Lh͜o72wcnMF8}r\󩧷F7rjYYҼNY8nrS +R|LeMSJnų? j l<?gVÞ80rW <^DDDrrL&#{Y!X:ܚ4i"H^RSS꫉'><%%\~cN8w^ww$QFܹxYp!4ٲe^$+/y (Qonrz_ P`#?lP+jXS~}R( pyGQ|ܥ! BQ@Q(vņ TD޻PB $Bޯߖ!BBM`s|gg- mYjJN58"K宖(^º?daa% y{[y(aE/aжƒe/Q|Rg|}*+T P{%-Y}:ؠ'FԺOw2FW[R :ζXFO"2,m2^zQTH*@0CؼysRR;_Źaaa|ȁ ſkcǎ;v M@=Z+t&G>AyYIQͷ] $Hm/w\*;|DtdŹ$CS4  UZk|w`Uo}z5#0tmχ7h%>~yT=V%O047()TOD!iI0MXaSb[jw4P v\;*,K(F_N2q'I]tT jko߾ً-p9rd͚5!jJHHx'N8w^Hd:{ĉ5 ^+2e{;v{@a՝ "i]vڴi&M`Aݮ{O+-zo5Q˔8}2qBɠ'*<:?gwI󍱓CS,/>SXUl#Anހ_vs49!? {21XOVUJZn;pr%~B!f߃:f@H*Iaւ7pqyrS48>2jEySNu?G+9e2B@ئMƍ7rH׻[Vv:qP[Q!]u+{! An6A|6Q(tȻ6P4N*Jc_xzlTfHc+֟9 LE |b‡ur5|5vEp`|W  خ ;JR$fΜ߲wҥ/SQQrss_}U>gyÇC@"DQu+J6(dNnJO?MLL*_A jBSxyQc2]W7SF OrXW-O,gddH$x-dW//u<2==i7No`9/H}C7{Bpz#8ES`a E1M7m'PWG_`0 ~=B*H AVJЅF Z7 7n-O R.Z Q~vRV7gDٲ,8I'ok(-:tr^RuO6:)Z'\ duXNa%DbArʥ*9k[EP5\?P_ TCbBSK"SF""FG)Yxq;CXͱ^|W5N,&*,9c03&fSȕV}擓gs MM]Ea2ƿ㋀O],/Q`=~pI9X6v3q0cӝ̸SsxO kL溤>|  s .ݾ)ΎYPk"CX j?(q~rN计p`A3[e2^WHMۜL\ݕ48}/=#́ t|NRWUeQ%roĈN$"iMCeq>Z&8> keיj6+7~fg"|觟~JIIȟ AmzL օKH֙IǾVydCvT,WAÏ`ax{[Lf7y|&MrÞ"BLL9~a+,917 mb7wIVjR pC!.a"؉aX$)pj_91_p'FkLL0!<*}h p^otS:qE %ڪ6݅);ZЧD,Akvʁknt#$JM&lvUᡊgz$y`%){u}$.H1뎟9x Gˤ5NBK!v{b .e|hZ\6V%޾]י\.iA9|"b[ilHg ۅ RP]M6رc===K5>{ M]dbPvF /5wBW\ٿY{{{w-''wabH8!MSH]2~5&I';'XܷoHtrfḧ́ؔOF%WyIfK֘wP8[j?yJOù{Olv^/UwgoViHyp4MqE9m렴vٴzwp%EbT}b'I|%K΂ Ծnl; k4ǯzTzEU111ϟ/**2qZbp:\V_sRt:dEur9$`0xj׀ETI:IiR)SkngJNɸ3y_Ԧz"۲Նq} !>ӭv )lFLE':YvYQe[ńdBG.~́߷`sڶ[9l.$Hv.gPp5Ǿ}JP藦|-gc;S{wnw{XO_o'A3LI9YgxD)>[%$V)'|Uu=$B ®#H A]\-v,k NTpj0?p Ԋ8Ap5u` exz'۝F7y$ UϤA4MYPߨpNNI`/=I*thxr8XYhjtO(Nvc8234̯Gt޾AQL !q}p=[9cA ԆhK0.Pdu&qP6I csX>8ľLJiwځ49m7-0s@nIUQ'M?8 $L50ssÆ &H AVhm:Q6"خ;jKoag͂ $#o!Kf6U Ynu֫0?t@&oK/)+UsJkAt(܁6o\[[Aj0! ڭBCC5ZQQ"%NZk;^6Ԗ"pV݉"0$)8pіh'4Pn>!Bv,a9Ɛ~"O;YԱԸTNx/^'$aKXq*w6>'1=KaTzחnX-O^ sn;%_ 1q>Wrܒob)Iz }ׯQ{꺚r3'x`k*!<.)\ֹ8WYqºQh{UV42v+~bo/:qw=.…-A{4- ðq wP,A$Å:K#jo܍X.=]uJG"}bh2%G8Wp@:{6ؽ7/_;|`2\AޗoPr/+.IŐڊbTY[Uo׺g\{SaށsLˮG CwI}fScp~35ԬYI~htyy%HC;dJXXw}?ܻwo!+0Et=lI \J۔.SpSd"Imo ́ڳnn_䑄>ae|jقvG"vX*gg  a$bHdܺe0>1}XzQ qX,!DbBPj ! r2OK%R J^]CyB*U_Pǒk-BTp H%K1Bȇ;Ʈ/(Ia-欧(`%h'")!wW{'ʂ/i ں;WL'e)HkX[(-;VY^lw,zB%mjR(u"\8ۑ%RHkƋߔo'vxr6X@S4i4,!eb$흻 ks ԁDqi~wF,nB>܁"$jYd: 8msh(PT3yxd2D\4{a-^)0E>qCo "kþҤYoYwx&v[zqX̸Xljb|s6+ɭr:lQÀB;n[cltٶm͏|i1h̴gֹP) P!p /&t GE8Bˉ-wj2qDޙ߻0T.j[|tA}L 0Ɏ.9"TWfu1r(w7aK}4{˅s'uF!f,vUkəGкykϾ4M1cO`X,6Hg~JΞ,u3&>{a!B"Ѻ&>:{ϖԑi=؎UϠi yqy E1[f NRNWڄݪixV{iJ*7xBOx cj؋GHM~?OW4-nhz-KB ԁDstcK,n7.;v&..N ?ѣGNbz kt䠯׏_:F̳ZLӖ^-B 7>v0`~kh 2`fӌ3~DrUނX zzuF 8iϞ=И[RWWE~$I\81+յ߀bXR)JX`XJJѣG_ZZ{{/7u@a KЍn8gkjcAǿEp5k 0hhh(++;jsJ@HA'xlcd a?ş.ɼFD"%Ց BCC!9RPPPYYe-- ם^{ n"컃EJ ƭ]l9*˷o)")Ge=#~J5msٙ&?XطonnֈBC}}}.=}xKmegg_p$IF`)lV6'-vf3999v!!!aaa`]Nj:--ѣPL+**XR#vr A׹g+8+iS"%{A†pOlٺ5LT"mFb4Ԏӳ]os"_ϸ9}'Hpme)_ X5{&Fn >wxrAuXv oYgjjX>`h<|dJA_50A(Ɖb$K4 Nl4+Xb;P&)ZvB U4.BkOij)K+b0JNP7#SP%[n"&$O ?TVջQ(Y7I+;q E5opQ f~>ONNJݤjvU*Ah4h߯ZR҂O .D{(̩6ݛXWە@zǍ'w\(U.@G*R\$6rQS5G- Ȱ/ pcT;АWWW=ƚTZ[[SPPЀF٣{:nL&Hd[_ɿ'ބ6w g,~.O.d21'H o2r\?|Z, {S Z9\YY)5|oooϦ +@~uB1PM0V]#(q>Aqu7SPuX} ^uϨk _ztjj#N8z/Hi'=z322^;18}(18NSɉwĀ5ӥ#qrĺ!yyћ÷`Gm+IZbnUne4333r*))IHHAS^]f%e:w 56o}i /oW91tt}֭[#""Շ2gq?#3_Pn5 s*^'QkVsyDVѺS,j?wm;8%H*Ʌ#\)`5=ZR9< ٥n˛b3u \D"?Yvsϗ|`8,~V t v}SiiiRRhXWp8㎕lY}'ET$*kwCECs<*g?7mf: @Ν;gϞe˖+W?ٴiX,7o^~~sfd2_N3z[mڈPʌF9{y쉉[Vs!-#MXT wJ5%)\2ii6ZcXYYǚ58cuyx ,R@ebM'WR'7`ybuοo@RzE$A/iѪү"U?\S]s27w=t[߉ûV.d;/8 %Rղq7>:[*ի=߿kO|sdMUo=wll/Ξ>vdggˊ_濒9hҶwRݲ/5M {>PV ^~A\h֊Ky4;RnCpъrB%- .>H4l.:8<+[1m3:uuMmc4m4ek%1 ůo ʡD-!TUP_Ug?ˎ&26 (V9sи L{PsEsCG5N؟6PL*aR4TdJVo EB5zvJ);(W)m6KR `q|&`*L)/VB"PBz$2R&STWgoku`a4S)w|8i0P /;dY>- =X/nZEDö|ol?8!8;2#a˾mm`ɹoW/?3&&ՌP)҉\qdн;?p"Ϯ]۷oϞ=te….UA/l][֐(hRr@;v!A; U^o1yI{iii3f޽fjf薑 s]zQ^tƎz_u޽{kRRR\'quA49m) X,mSLywv;DΣĉ4hwN8(J,h!1EEEO=Z޹s'zWy`yzzJC^^޶mf?gΜ&$$y.B` FdLZk[IXA5Dƅ$jy|j^cF#oQk}2z—GW8)~1.d7gHS'~`TZ]kl`l=6nj|2V&g/i_|~s>e]<ydrɇL;ԵڥnqS_>s*f5KeoWmϕ}}A"?FuFo KqQن߿KpyL{p[!c)?OaM>i i@i(REckzxÇp~=%z)< d Ko@ddlP$#SC";o~?E0\.i}0šSN@y~$! u""" GW^"@={ӎVo߾Yp_fΜ)H,XB_!Dl9vҚ߹/KR t C=z411] e .SԹk]z+pQK*/\vK -䥀-$j,NFv&5tt4=)QBńBNӹGΤw_u߮?'=yO woxlzl/g?Rzb I ٽB1\ҥ@kMSb1n-+.T}wn54:qo1frN9s𡽛ja~5Oe 7sǧ*ç NHjaS*O<}B$/DBz綾}:MN`f6W6_sfktؙi&?p'^zG9[7"%oذK/c2ŏrJD!!!JZ%HX,vt:W8)99iB[ע:Zݝ n- PV޽*O?txxYQtipv:" ijp. EgFBP6{?SHsE E ֚KKi~F4\G ӄڃ>cEIJfxie^Ϝ,zOe*cѩZ7cHD!٭qC#Pˌ̠1b({Bmg$29Sڭn}ϾnC7x|icsݿ Ҙw݃rBP;hIH  >CI! ?F7x\2AAᬇU0`͚5lCaͥNzWFiA,),\qn ANE+= uD"ILd=\pR\n[Oa]ˈEd((wd2B@#bm`'tWIpex¥Dzc_s64X˼k3Oj狕Aj[cOzB1!Wh:E?Hhׯ D>:Z؉ q0w9KЭ׉3zo6@nUL&juZ QەP~¨4ZL pl=2z{NAᑍ_)58"RmCgkryji6I(_(i20*$DM6Q :h4ǎ*jذaΝJJJƎ#Ą5եKZq/jWl]¼\ٌbу{W/]ofI:c9PWeyɅB\:vi1Ο-+>&""t½i Mi"#&=٧PP^|ɘFYis?p6ԛΝsׇ@׹TϮ#*KIIO?2?8c)7pH]wҫBx߃).'o? _! 6أ P<ҍ#X_ յ g[q0!Xw݌(FxǞ+G.Π)v>Xj mDq4?JuUV'N8M}2Ѝ ڽ{ 7iҤjfgg?^g }odɒv2'(V$wp8Ś/}n, wmXU*͎u?-톺WfwJ$g+3_x P:ٵqE6Dr߃s,?v8,̯9viS +$:>6a"IB.FFR^Wlf֪C9{t1ڐ)SE%ګNobc>6Ⱦkп9of?nBٵao? Ad0-\7b)jjva,QZn{O"(! rrr;PJHԎ&QF#44:vZ V.ʔ).Y`u lܸ1++wq _ZZ!QH5 3u3#lV?F~O;>9OW(b$Nx&>HeXڦ|C|b;za=%>_cJ!iI`wfΝUUɍFl+bƻgn[U4mܦ\[V~g1>[Ilp-bĽO6/,(+>'OM"FTCe߬u^]u-…ܤ1&7 [ X(VaZycw!D"AoҮjz}ee%PV\cdSfffnذ vkM`uЛ(7|sw&0 j?^G\ b_|/KxPo5t_GupQ}-hր1ӗ-xB-NH8m*$ a}|]BT0J_֐BwC8Uro_\*Ĺ/l7h:4T+"~H蚮}9oBȠa#:U ]߽ILHI!!I+ q 'K6a<~ 43MW0Aw NBsr򊈈@ю-PՀ,mdqS]t痕5~A={KOO/++뮻C͝;7%%ۻa ܽ{v <66ڍs*EeŕsJJW[Y\v0)=]shHLT=xڪ9;Hҙwx'8!Oʽ Nf6O=K͢S3`kYy^#{Y<</ a[tdmYę7~!QN$tkؾ yM%k}B\^ .0`@G\MQTjj~NĿ8o]"-t ECG}dj!O./={v9s,p.RRRdž HIp>qٳgO6k֬~/~a77v<Xmhgw]]] ua ֓ ) {l`.7?t:iuvG<8?{>' j$t{{P9,$BSv$26M,jя${_ \ m"."s 3hgLrK5uL摜"R\=ctեBрQv$"%ρ|TAʋ£S̖:fT 5}Ϗ0`F;b?wPQlCk EY*QqºFe1"JKH8'(\ ܋`%VE+ Nt2JLL k׮3Z{84ݺ H$'V}ǟB__ߗ^zb󛭡~Xʊ; UUUս{w3~ꬬ,(?4|Ν;H5Ǧ 8E`077փҳm6(^1NBE2ၡOo,s"$2fF_P$<+&S5kNF"uIh$,!T M<.֝:E *&PvYZM{*DO2naK$rRVRgV,Sye얡bGڸ#dT{jXI'I3yb)`sӭϘ.݇BNXMl]p##sнvHZ`&\z%5jfK%+ӥrj7hPWaݵqYQӺtϰYN%SR);QJ$KYK҅MՊ'PqK6-6-&4&;6?~pǬW%8~h3lJ^C\ӓ[YznׯG%anu8N-&g_y¼H?򾜬ŅGwn_ì ޿-|3l[cGM)9o57T7:<鱷C~n_>s]WMX ˭Q_KT(MsWdAn `+K BvF~a:@/rСmS8CĻ孬x_\8Bӌa_XRbbw}6S|;ko=^VESMSN'N\5WB ATmDo]Mb:TzLw_DvV4r:@rQJ`y“sdc=}N& bTNQN2F9hk+K!ȕB%r<K:ڗ&ΟOȘ_=v`[hTgZ8`+XҪ=N{q*3gwSVa"L uB t=CRQQj07EgϞ&˃X6mݽO!~c^$1A^)5Y՚ PhFa4Ap:t(777(({$=GNIIRȸ~*7;)mpH. >eZ{[g$K_=*!]Dx#f|+|fŵiu_ESQA'""EzN$!HH!\&YВ$x|_!Դ],^z͸A"225/E m@Kf%]4E>V@T)JҔ9Yx $͆ڡ@7?T&v J(I8m%H3FƐāFp}D`>|+&H&0 [<ƙcۉE9|KpVťf&G+_9+Qp(Rj5%QnۗI؟YnWaE(FgϞX'1 •+WܔJ< ;e˗/^>}{Q%>&S pή&.=I%-1 Ȩ ;Xx+BLF~ Dm2Kt'DZ;@'.Y`>>.M%44瓒~A5tvcǎ$$$ aÆzd+^I];}H< ֣ ƹ}oo^(^UhrǴ*) &kri΂Q@n uժU>h㞒faX(iZb#|vPu`=8ъf_8h)iUN$LkD4>m,cy yTTTk^h4Ԙ*PN &1n! qZJt4+0J\~׶{8KCy{{YHd0𜥽 `_("^K jJ&k; K2u&Flٲq93ϴε CffKмBR@888d2Y eY JKKAW;***:r$k~{Y]mj\EF+I3g>eə{-H雐ٳ3т@/'8.A@YYhjk֯_j5 )'%1CU@* 4KӁ6زeADX`HϧCRv׷<3f̘4iR݋_wKx5'/ΧӐ(]g/Dcs;(PU酕LJkjwL>| Nz\panݺUHa|=ev3b DަM[sgXUUU'NqppHHH`S¿⋞={bufNZy϶ +I[b"PВlTސQJ)U,(r.]:xF 4aRŖg^ﺎ+q@D+**z}Oj+//㠱@]T~'TXؒ;u ݁*=AS]`ضm[bbb{1˴i֯_j8swwwss++++(( ls6d20) mGMTrd2F8OUf呶+x+ZV4Z\Rf2#BYDVK$Z%۬] ж=.ѺN LplHΞX`5]>Tcƌip-R r 郶zP2 `E,nncn |u- 0QgOPKY0uZ9m xV[YyyBH2h4_wD7 o+wut t]pKaQU@_|8puvs~e庾}#F *fǛ ioxm#{ ۾5pPx^k0=o޽.^"2eʔ7ϟo<>}mFj*af̘gϞÇu ŬY;|СC7o޼uVF3{lhh9sf <;;;cqqqNNNW^ xxxKw˗/?uꔟn4uW_eddB &?‰yp‚]?~租~ZRR5 n'22WUUw}焄'n aÆ^ztE]z~]Vb Gex M򀆟N+_XpMWmTlEqRTrZ!QK(YTMGV4yk]$MEH_5?dMFK*EGJ-R'+%j?ow5O>WZZz#PNu%$$CBoI%/ί; 5{_GyE li)cm莬(^Ɖms'qxZg.I}f7N*3㈲·?}2c}cŰ&eɜ=>zҢ܀Π|++KOZx%G|6439w {ə,|3-"E24ˮD:FfRˉ5R7@^`:|M$gfIU)Cl[}dĠA {*355u޼ysNܓ'Onxh XnW*9Ho1Up Y2& fqzpuum=Xҥ윌B]EEhH_WF-c2QmGYET+SXofʽO$Sd2hN;k,&tՠ\~<=Dt t n'NG LK֠.V; |FEդ,aj @iiGjj#v ;)q AcNe͜Rq =ج *Ģw.gԼoL Ns̹K(94L;=IϟjP :eeee4Mlll@@3g@EرRNOOҥ ȵ|4}=z8::B G֭닄Y`_ݽup:1;\6֙%ĐCIR.S*d*'•[8s酂C.dfKʴ#GYLPW$AN#hQ7vGu[`xj1ABLHhtwѨBIK|+%ڹã&}vFvCЊ?~rFQ焨BYKm@EqgDvx/Ҏ4U`Q!,$l˥Y&+|e$J؎-&+( VY%AÐm7ڷN7h8BKFQQAK}N,\wR(Fu[:l6wOL\!9cEKAB8i!JR&<$n˚*cpT7HR^wDc Aa򶟾M"x֢rp*/-*zqy5UZ4bWs379.BpE\* wz}/u]tr렔:u7tŌin3M.,f=9x48Rk6 xyJ oXQX~9wbZLTw-fw,,cyq¯E#Aj?TX.4h WfŁZfCpp 9===,,O"ΝԩXCZ{իW/^l߿HvJIIɖ-[vӷo_;,iՇ~ޮ]6zhUh|W3gDAofĉBcǎ ^[hNr Tx&2E\(¶OH:CVmS6B3mX7V_˫)t_NL>jXd-us(E<[{I}>^vLbSo&vQpbھ ^8|R 뾟?}g.+x#=ς,_1joW;{*ZFBԀN9'^aѓtq7}pZ*F=Ti[`ڀw'!%Uҟ::$ȭg{]IkaGk4N&#Ygasux\s3ો_*JJ 0loou[^Jy+΂ ȡxL(T̀DL(W.tlU9" U[0LF_Vm"ZsH;4UQɚ-V ㄳ@#KZhVSeW*W" Xw ?uOQs{tidLr"Ķ6@6!)@Ġ4(TVV!M@lyyy:99UWWzWTF\pabbBj_|9 A'.]Ck6^vmdd$0d",xiRDLx A.QiLD)OԮo[^aDih #Mjs`1:#WSm0L:H S,˵FcNNsL?Ĵ?e[%KA5HHmM;Ը9ndvtwS{hl'NJf͞G'OFHeg|qH8yh냏H3!IBLF>(`B}=r04%X9[[2y'׫3]x`aW%VӼg F)'t4,y]O,.Z] cL bnYs>PLV7/U$<((Zf&ɺF" 8X=0l:% r*$,wBVpYRX,И$\=h|v M.\^DXoThC%( Ѷr$6 * Jnc6mB=]O&M OMKKvZAA>}#ԑQQQ/8o<#<;9rW^ z7f?߳gϷz"77OMMꮮ?k0a\499^E}vSUU q~'ǏaÆP0gΜ+W@ Qߐ 8'|hW^y.0uTP :| vW޻wz탆;L\\'USSs0DP{ ; [WX.@k{ӧt:H|\dB~AB=CN` B ˛ y-V3Ќ~R"JFdrF)J*y5G( N_p#g~@)(bY4rX0}cFNLUFNq&BRI;aꠔڢ_~ ,3f Cj+2kIJҶNM ƑZo( */n;tոyz{wLëI+o(!]#+ˮ]+ ͊{ֹz\8ynP(,)XAWد8 )%!W/PBFC5U\t-W2~{CByag*YEJAZhބVBZMh R0xABk#{賷 D!.lēTFڟj4.gPKR0 \-pi^0'()IIl&ђc&嬡nZ'En P&JǺ83okZ)B VZ5uet]nJAzqOy*֚(պNdȀǏ(ywlPBkׂ6A,[ ~:w!7Aր5kl۶ $HPNÆ ۻw/1c/77{/y_}P~w}ԯoƬYN:6W1o(hѢE|}ꩧ&Nx}|{po% dQgչsgH& $2\ xA?ӐeLocbbΟ?g -BKUW;N%-BnP 3C) VD㠜{1XZP  !KhJbMIFsA"3Va2.'(u9ek š9.D2hhB@K*$hAF G7e%[+c0 ӪvVSd]MaxdDԊ*Õ.ۘ+k5!ñ;*8"a:OT 1G=啜SyN..?~FIJv%R_}04 x+S<9@P 157R++z)XeHIB W]Ѐh \k7_ :RmHQ8(\ z]? (b=<3dpPRE;u[Ύei{7TuS]QqffAFvjϝJ說ȽtX!*\ טq1>=z'N ~U2;[_Y&vyX噳.j]&kpo?PՁ$rvv^p!hD={ڴ| BPlN,PyO> u6^ll,(?Ν;fffB4@Y(&Aށ n 7|n &LЄX 護Y 4+]믿?c0p@*\W_G@|rџ9+xQR ~|||@ >t ]zAh#FǃCu\OVe۶m\VR?믿B͛w!Y:w HNN=zxgڦH-[̞=QgLBD4d |Gctmh(NXz  %%uf[D(7=)4aGڨTj=|bX =nCrٻT#3E_ҏ0c&>&gIstv & C"e Oti;~֛cM®kiQt42M;vҥKӦծZ믡F{ˀO^jĚq=3g`00P?nnn?#<: LTTpnݺ'8s /Z :ĉ z @|)))O7|);.XŹ@ :\ }Ijժ~СCP\õ@Yh2ѣ^,hn\r%$TN#lϞ=;j(LPcHlΝg6m&W/nQYojrV}vDb0Jcվ h{o7VBr^mVXcn o*ںN(WBh-"Lj Go%-Vгe%x$ϳP+TrbpٻuUsP?YNz0}*е<@aa!fJ]hnܹmf`0mZi_])Z_ga"8-7d-#XogTZ5mY?qjCS5pTh&Ѡp > qԧRMJ;:8rT|r H'^\˲H 5c{%*>5N9hIv=6įPGP V֬YCC=IH>} "A~W=III%PPȿ6P% [rr:fΝ;Ӓ%K Ȓx \5+k׮!mJ%) pAϚ5k˖-bA=C7o =f\a՞xᇧM&MMHRf0MOˌ3Zn',kI:f_g9[B{Nۿ3<5_z%MkpG@@hlӟcلqa*4(4լ%IqAh: ҁpTҥ (uPC As jڵeee޶mСCDb44h 6 ap8 4-VA_YlxE'Ke+O]gw@bTR˟XZ|gߏM(|tf}@r'^v9 SNp!6; w˪_II EoY.grFױ4j l9r$UVp@PWcǎkצu8vXd }5X%FaBێ dZA6IY05YXV@`I'I_rb+,l2 L&˪so[%ݔ)Sp2L¶lS ^)-_DkC"c[g 4 /.]:lذ^{/\~O.M ^xb'Ns/^駟㏫V駟\\\=:}tSNdɒVT*5…Ҋ N'H $./4H"PB|>CH~ ;۬?g}Ȑ2|YVC ;pww B;"#gh=@ +6~abMRsޤ%UB3[mdA;0:)3{@J(RJWoOTLp/;V^slK|X-8a3A?)o*]_dgɬ#.^:eQ"z;r~T&'`06s=RiƍAN!o1j(M'S{ZZ2V9bĈTwuȑYf 0`ʔ)xNzK43 Z611 6@cUبBA+F 2@\ C+܅!BN~z;IX!QNg̚?/!U&]ɹ?<8Z7$NyBޙ RWG!UR$k'GX^Uzθ8|+V sgDRQ\@di y0^L,n:HȬj\Ms{jO ͪy}||@B-Yʕ+;v_`rJWWkq\MM Riٳg̘yfal=i%+ёR.,pCU~˻giy1/i}rFtV~vW5 eiV^PZQʔw+v?z[u"5eU~}lޟXa/ya.SWmY"Q%\R]b1p0LI #lG"ȸ? 1UkGX-fhF&+mO CVY&R_Snw{FA>wJ:wܜ9sא???0666!!a޽Pa Hi.]]O{xxQP|rrMRSSpڦ-՚=ڥ1}g} ޟ{umS޻xxH1Nn\8c̿2d iYzaC)djL(s F=R~#[ hûNSo;~0糧ҳ):c]]&$ ˖-(NE MvHvj"7ۈpީcdCJJh#>҆k t ~Fрa 6.]?zyyρޛH֟V 6Aj'=߳ө ipX9˰ Ryj1MfcWHؔpDzFoX+a$O|^HcR؁S?_@94~*V9$d9V*vSXD8'>Hlsϊ#ab_Vg%4U111eee 2T*=>79MZ5cJ8rW˾<dz f*+M7A HjBS4qa;a:ClpҸwLIPSQS*cdJ~p:Y9!(1I<ϢysB|<)p-!MJ,g&>7A}SsO+--m ۘIZI!!X`/-8^3(ٶ]~prPRRgLӻwy]Ӿ}Ο?OT-wa9%*:bBb:oY,&9#K堟lrVMx t&3os榿9s 4 `cQhm6/(̮ICًo`0LK9q hvEҪRZ̬ 2ܭuꊳrՆ*MZ-0`0L<4<بj8E,[IqyZx>&8Q&SLobW^]IIJ^Gٝwn卓0 s#Guޡ²}h60k,k-F9 Y7)=aL"lSl\`0-EQ7nIq/ ,JB1.u,g. _&IBi\ȤT"ax ~blp Ԯ79r??db0FPff-`Z2N,Bg6[Pb2 \nRL.S9y^;匄A \{"ܼy>Q sz$ͅ  )<ϛ7GnRNQaujkcSLp5kU=e4fb}pch;`2wKXk֬_`0X4[V斑qYe07[GX%x+?0iY8h;qltP(gOyAY~XXއ`nb ߿N L!I_ I>V 4>7ި`0DMɲ,NL(jMxZV; %Mh_\`qkb`!|b9\a0 D=P\\l0,l.//2 @f!*b/D+ HRJj}uttG`GdRZ\;3'@AW 9(FP^ IV+4pC Ӱ0 TZZUSScA4(!BNNN VtB I+@{Ok(@jEEEH)r \)]GOz,x޹Kl"$h!]_[+3B -QWyl|z `0;v숍uwwok>}ĉx<<I-%%2 (o*J*IVkAꪄq)W b0 CQ޽{A4A44 x匌 J%Ib@Q#U3L&U|urrmwG`ՃH֙O,|촳 @kmLն 'a^(H.j;(ۦHДF ҌZZ l#V;b0 sҴju`` R<׮]#,>P?#ɅKll+8!'Ztr N~EEEGXR%PgCၓ`04  >Dw}h I.d D" CWhul7 s=ꫯƏ`0NiiF͊i%ILz݊z7\uh[dmN?`0 !ѣuV3Zrm+ X; zwW{Cm$qVe۶m* CG/g sw2L%n6e Nvkt!7´1"hfBv۶m2 , lxy=ZX(Iqkb2t4V*իi'pۙӧOgdd899Em4ȼ;j00 #nM.hM8|vXmjLj1  2S TX㪢bϞ= oxTIIl!JVAPHW\\ hi']m۶t`0Lہ8PQ6 I.d"`0ddd;wD 0׭[jA_v:u˗e2b3g.^6jA`=f͚6mZΝ~hRC7pʕK,+J===CCCcbbŋO.ٳg寽Z׮]Ϟ= *ӱ\(99K+,:GXXN 4;Q>C6J`!hG rT(Pmd2 5a iv򜝝Jej@H}ƪRPZ ҇($#mVvX,,k"5Aj$cÇ[`:6 D׮];x*&\.;;;++ NW* r9)=U%mzppE8VT...񎎎X`a~W<`0-MӭPtݻwWWWڐd컣@56@QW~}iMGX`G'`Z?Ѩ0ɽ"++kFkBr }Blquuurr*..޿?X `Zp!N:۷Ĝ BAځnsRH$Z5S ˗[s!`0Mޠx jOfaqn;N۸%Zi>:૱N_R\.mWF3f N 4; MkѠis@ZQը  TSS`тAN)1}] 2 B ^^^p ke ^X`] CN`0 FdȑsMNNnkHH$Fj^67hADsO[Q%Ν; SL:tѣG_?cpYdgp9shYxܸq)z{뭷eբW_]._QQQgy[/ eg,{Bo`G⌲Ԉη2\,,׻kpd> 7o2h GYVAEYf,[v,C6EoY72xŷL2õx,:D;2 R^"Yf?e[v,iHM5-ngxa]AL)!!!n^egN3dVe) .A%E꿼|ܸq `0LۢUe׬Y#ˣBCC;ؚp 4'Nc{mQ7+mva`0 3 R)h3g8;;GFF?hJR af>% 6dXd2qHi׶i , `0 UWz~B=aF TRRf޽{߾} @fٳx-ɓ'7mTUUiذa}ݾ} M\?~<\eܸq#*-j , `0-{SUF#HRVh΃5ϟ?gM4sΙ?(|Z=m4P]wuu߿ٳ 8 X `B1vZlYQQ(*0q-jCHQuȑJ|u!K d}饗^y=z̛7a,0 cPgzӞf3>}0ԩSeYԁV\ صkfee'''44V`` hli`0;a#G6y R iJ ?ß|Iv BO<-[ 䓌Gfee988xWdBO?ŋV , `0,˺k?~s= 4, XTTTTTK+VHII!DT*;?gFڵ[d $-- 8%dիWݻ$#@oMzKg&NH`=,****'!TO?vmƍ6m7n\uu?K,Չ'U*Hd``@wejj ۲eB!@fUP ڈOO5kvX,ꫯ+z=XTTTTTOB&&&s `PJJJzzzZZ3K; 9sfƌbPTΝS*#FXp^HH=|pTTThhhqq1LJ"66Օ* XTTTTTϔ***덍]\\KKKkk넄&c=ztȑyT]]Bx:񆆆nnnxkeffH-^w~G> , -t2CD{=8O6vIIfVQtrr !X/_>|ybPICCsLjJ%5gt"G{$x;UUUxxYbV*rEWBv#X Bq",****ahh|7xn0#G16g`8]vc4iҐ!C`\Y*wwwَ;nfffkT >jjjJJJ233Ux(FFF\WbHa,B2_twvvy:(`QQQQQ=5iȄw=::ʕ+@%]7W)<<<**ҥKo1cbfO2ۻw.((=ztϞ=a\u@xZy{{?5k EL2{ U]] pBv͍3+X䴴4b aD;vLjhh4hK`ѣdH7׮]裏~Ϟ=ё\>~xݼysdd$P_u:ʢz›»4J%|rlmmq\TTo߾9s;B^36ʾFKXZ1 ,3uw=R7TTTTOEfޱc/x555k׮e lgΜ9w7 m׮~:uTttO?f2 |xxx׮]Okȫ|MX\ >Txmyyyqq1M& $kp.RtiCjT2b*b1)pLЊA"]ћ&StGjuI oD p''㡢z6lj[nV(__7nTWW"=,T*urrz rС/ȑ# ;v,JAA*vWW׹s9r˖-[ׯ_ppp VxxD&Mw BLiddD[̊)-D"h䭅=,|H/B :L!;LCf2}D2Ko{1L$;cRcֶ/JpG& 꿋T&L3f ²errrtkx KNN=_WWjժ۷o}vڶmۢX-µ}/GEE^~UVmm-#???++ KJJ`Bba`,--A...`J[[[33 [dGH oVHcii)Na+B|>vut{XC<1DtE8=A(? c,jBBrj|<7KfQ07"E!@p[(>>>v UқgϞo_SSsU|8ϭqBaDLL 9Pw޽/',f&ԽL?hР;w26sNJJ 7 1XYY!Z,-H8}ƒQ'$$Q ahHr!dIcW0C:tܹoI XܤiTa)DDcȌӭS+\}?8ltwwGZfZÞm=O>JȻ! v=;;;55<44Q$entwT*TE\=YroFzIee}|]kJsʤ/<,`&spgq9Ls_P¸gffp&77f4mƀޠ]rrrVVZuMNNNh2v2LM~YYYttӧd/Ep)Ԋpgee%8 i1X^^^dd$Bo߾_~0Fm7}BQ*fP)W]4yPΟ?F Ƹk[%{ rpp;9sSTxqUcyK"G&N;c ^W \si<ϢK[@ڲeKCCȑ#@%OOOѪiСC>LwHNHHٳgH$B.]r wܹ~GX,p9mߥKpP "=.x6$ OKpݻoܸAR`%%%iiiEEEH\>aCi=¸Bt5 (,zD*5[>UK91MUۚ3P`XjJWO_3ǧ@zܹPْIL `c$eܸq… ~)XQϙ3cǎ,PbCk([d2;;^{d˞ի۷o)&&&~~~} 65;r̙ -ZA###4@[JJʭ[N<9}t{4`ц077|mK:OvUŤ_yԩ>|{I~ %2Q\)Sjjj@Z p|ȑd0HqƣG5ՠAv[QQk.aYY1|𐐐Æ]6??.]J7 k5rHhf]~>JKK0aÓM{O`|}Q,){QEzwt/bigYugjEu_Kͨ5dF#IZ]Q-5h<7T #_Fbcc׭[x`>ƍ7iҤv/UUUIIIx򀀀ŋ7+F?|+%%%-ӧc[v&pkAFF0$k.^~($H+G ,}ʐ_58-,,ϟ$WA,ŕg%,d~l#Y +?%2#_.2Kt{&ڮ_7rZR}J_gtX& 53uhgQi/cm3мj\igggE`GZݽSN~~~V hA%, ãڵkי3g$ B!6GGGO?Գgϧix{CFsIezBPsls.jjJJ,CZ`@X^QT`g#rdRUX.Fg$G>h0 Qhe DOc`1sz}Ȑ!_իW}||N81vXX۷o_$_"ҥK۶m0`sXў+ {9gg+Ȕ.y`U1lm!C;8wX@Κ5RGFB=v:Q`ۡ7p}``q)UU@, ^OHF!?&^̩+*a}]"2jiUՒ~vIY̾rs` ~ݙ*¹&\!ܔd$yz5E2^y- \<] 9U&akOVn bVu`AIIL&#c:_Iꄀ0doYY2 \QQK iao38pk׮cƌWvyykkwywlOuk׮iq>¥n{/R:V.Wbokyb~k~K<%SPWVRoU+*OK% ĦTrfJG6Kƍ0x̬gX\9s$LlFF cǎ z D;|p pKF`O>=~{jƌÆ &߀Y`52cx߾} hv?id.+DNztRSSA'N@̤$ȸghh(n3ـ<%rɓ'O1băl Θ;wիq#@aPP/D@"Ν;HCؾ}{@H$۠%˗'M8Cbcc͛3L}.(`,++ 6g B욓Ee92hũYF%&v 7ˇ>'N\/w4G`Gk3>DǣgOL)@WP.&!A<º;9b\nLj%nPXloÿWdt&墅ۍyMKs~aq^3&x8ew/4ؿߚ6qkX_ٓk&ϏSoagLj۝%RA:B3 s j`5ځE0O6rŊ0L0ÛoYTTD :XֵkWggg^%K ÄSN3^`8HқZp'==וD"^QF鴄5p/2--lHj0%O?=w~BQ1###eeenZf to&Sv:ܻw/Ȉ ߟRd"#+Ǝ%^Qfrd ȑ#I8ׁ0#sk XWdGdz,f֝*@s=rDeKQa,,l=Bv*M6Rb Uh́c9Y9 v9JR8MVܨp$2|7 6vՋ--)֖9uS_߳Yhb7g'LًEۙbs +>:#7OljobR][G|hoT?%SJ;3.u%Ɏ?3ݬu.XW\,YZցPYljbK}7"Kg_i$-,,gV>}v}i-[tee#&L߸q EnbB!͚ @U$u3R Vg̮Ɍ 2 fFH/V'+>O*R21`yrF.~e0QұpL x^]~zs86a`I6ւKE'!/%jw&yX _R2j5VPru7C"w 9~:T+(i`qp3yws-DNoP̝-4TV,/O2pWy#--G~A'srM*W|=1V.6Hc8  q񥄡L;u*.mI(E[:}2e F"a#ZhmYur؆\LG ЧA,G"__k]HQZPZ&1D¥2>w@1r^CqS _͒+ 9rBB_ocsyM6kpdLV_bC&,0ʑʨVBcd6i@nLMMeiH|}}/_̜jc0cƌAx_8n:ݻ… F(x`ooQ#|@@ptuY2ή嬆9rdFFn}ԩ~=7/XK={@@e 0D넸vuOYs+"dffXC^^ww?qܜx+Bx0Ed9?ҬؑYx$ĭx DcI@[mfiiI|+>Op* CC XWjNfC'KȲ2TfeUyy9*̨u.VTT4 pڵHcDzt]8q?ǭaN:k֬F ]t 6I۷o$Z 6$''^ׯ_bQO>h YLL  ѣi l ^޷o,_ bΝo%믿ٳ'$$nܸMF;t?lh#Gvuƌ6mBA`Rya4M@6Ach1rnj3d377711q͚5ߣ QWWyŋ^o&bXhDuܹ&"C-4 ^o _KvdziTjC|K?U`&ϟ 0|v R#]G90b6 ؈vB!W*U{~hk\#j;u( yS7^GXzi ]:Z׉?ol@sLU$fd]mrI'c!OPK%JfM7SkI>>?H˄eʔ=7l#xbi~Y|Mŗܮ9鷪bʤRpBvZKr Uju^Q˺x{D^)3)cvA322*C.$ Vd؁!CE+=aa>Ǐ|̝;e8H0jQQQ#x*{͑/f XHb֭˖-t[UUU k׮o2e @Dw_@#uݻ}}}̙XAIME:a{d4 lf5jU'O><8h)##wp-hp9J3Z$`)\hh(yBz<<_n70aeee0H#}X0Rdq߾}:UNH?$'''#sss?'ȫ|+u{ WWWW[YY=-1`!QP6qᳳFZA9(UӧOH()hʕG<#w/p1rdd+jkkW^߉'NNNcq rk K8rw^0J{>>>--Q 全ELQQQ}}=eiaa8%)eJսfΘYkQ"fSCN^zm/ ЁlW_w^v2n#h&h5${ /NN&gv ?'do#UvqVi$#\y% 1UQ-7oTIJАWMQ9 e[,AMHskvF?Qlq"#FL&WZ H} 0Al<W G%Loll !;ɩgi&Ml[Y]+Z&Wh:UпT:?kʳcwˁi47jŲ$"|l(ԟC (pQ;"TQQ@5aȬ,T3233;F6ۙ9snI||'Ν;-*`h`nZw5tPǏ~,__ߖ  o$OFuvFa]bVـz`n퀅3 b`!Pa5 }io`{3Yyuf@TPy Ĉ݄u-j\B7;O͋Jj\ZH}(K.gMtCt;z}4b'&WK5lvU,f5_dC^`.>:b0WcksAAQ) xjy3˖-c&멼zΝ;]חjjjTXH;YF<rd@TuWF\bvu4 dZtj/w(>=;q.a0oQ1eF|EJ!;nW@VD_0Ĕ3}Wė W \|2bHA)뿽͎6~H)+ <ofc9{8 >u!_h/\Uq*|Mti>`O_(<xN ɖeRCNĕDzgh_Q7.`rIͪeo#_h^3 YN8c\[V7n8ҥK  >8pUPb1>N00 mYZZK| Ɏ"<<<6y|@ϾO57ӯ+.o\ߚ:EZVmXQ\4/2߼38/_t,(nכ!A֚QZ^e,-Z"So?nkmx1d!-e"}yOMlDZ3'zP!]/6ԅ=28N;cx:G;`*nT7wu6Q[ ԅ6k=: 4ܽMFtR{9th(;Zz#_A}:emlyOCvË uڮG5?| :Xsܜ'qiPW O Нr{v7?} riC;VTJ'q0_0G`q'q0=Ԅߧ-!uuJ**J&ΩM pI}hNR'{Tk&u e7G{in! HKK# w3xzSL#'RR4;Nn޼O? B4N c i&믿, t- kG";=]jZ=ѯ"׮]HJo<{l_>-n___2FT#\x1Xbĉ+i8h#̠ARJ/ul@`???<.7E</SN 7Xy}rqH"Uj{5K}{Xe>u9D$aSҵ׃iT+Y B7V]/ P]?+ ".|} *gf׸# nU]Տ7* kx8[\"perًǺRqQ1ũ텁-)B 8ֻefffڝݩ~k?ZM~> ? -Jrd,i4,]WP{.x vK]RhwL)x"osTj_3LNvܺzRyc#1xH?/H7Mw.W$V`2v/Jz,!M7$ٲ]#Vy0evqد-|=Stba\rC]eB6r]c+UTKSn׌SxwUz=:^`S+Խu^NN䱞 )<پ)v62W܌/\.;d 6.,=m#ɦ=JTM:u*?snn.qy-Pu۷oܸqcN8^#e\-` l lmm`޽{8O&4t+T2{9UG$&㭴n}'ez$t ȳX)nÐqrC:`b zK}eא:OQE =vϬoP[;gϑb͊\ P^ʺzy.vF?6lSer\i.FMn:ޟ>UPWTjpli*s~ܠF6lʙNuu mO竺Z.^K>i[#9,ݜ**2t8˕VۇE\T,t2'px/S_,VxL!Iub{{BJTdEnBEu_0aןYZZ6O 댌7|y܏Jdierrrm M$^NV1U*D 8Cm`瓹1&&&-tuIDd[kF)))5$$$::8oggI&DonnH.WYfЅ+b|KFts>|SyZ V-djm&)dJ;[77q uuTȆ!8rI㹑W 9 zvFU#sj'q9(6E>SנddovxꋞBCnXNq]E|@Ǟ<Ѐh/G 77̙:HœbakvqawhյǘB QogٍHF-JUDii9E>J-CER; \\5g*JWTT;9/^$+f&?|0|AG.PlK\S¦0 ť{@f͚l2\l G#رc={}dq£ۙwxp^Y[G E(F((Ubxz:{l׮]tϣ.Lɔ=B{vQT=Df` r_R,Vb/qmL9;x-Q't7(9@r-B:ZGY!x7h8qTJ5R s%Ġq>g>>[h]X`R͞}\drLn-(dDgr:UOdKFɺ\Qiƌs XU…  W^푞!Inn./66|˖-@###ranL0LUUULVVV Bة|D~Ν+W_~ʔ)H)駟p;dHΝ[iC0P2T]] Xdi}b:WG7[RRBEy;,|}}}$+]rߵ̞wY hz2FeP,}7j sn2 V)d#NTTTTCڝGc***@dΝ;&:tx!CzvDv_81U)**j׮@ ԕD#s>$Ā&l֬Y-(--E`<1"o-P] ;D/E8ƄJ6m3ӰQ0:u4iREEEE x$5Q#'666 kkkB!wyy7`55x^z)I+b\vM 'IGǏ#Nݚ!#F(͛7geemv$*b悂͛sRRRp n-tOYHE"b:nܸaÆ311)..f~E9# 2R҉6aVQ***V~TAL۷/MPՃcZ&\ʜ =.++nݺa?ab@Ex{{DׯWUU1sp(;;;666""bȐ!<;~xѩS۷ص &=V|8"LyyyIIIOڼy3y}||Z猍9;;(5㣊iܹ,#ZO|{p g>Qܷ{(iPQ=U{.ݮR5+G/YG{Y ɇ. lq9և*)Zql=+ROx%[Oɢz211[UUU555ͺ5///G ` |ʘ=>ԣR{q;w?~|Μ9O,((믑;vTWWO6 ```еkW\W\ k:ٳ7o&C… d'Ujj*K O`;2dhnn.ޮP(DY':z7m# ƒh2>V\-=}leEC n-)U4F&ݔWnrO6qT*6__tJ<J{ʲQmbbbr9zɉ&JHH@{̋Ge.\ &M]m`r-xzӧOG>ݻB3{斞~ҥ7opek׮Ν#BAl˖-Cn0A$'oݺk.poȑO>c,/HϞ=#%%%(RuuudLDh͂ѣj$y|xZD"ӊ8q>ZO^s7ۙj64|~{;[/Q?ťq ~z/%b=ق]4}DYTOUBnܸԺ F'N0-N:0tmuu={ :qwwzL 0`cNJ~իW3Q0"/ٗ_~lܸׯ_A0ࡊǏʀϟuT*";wn^ \gdd{eY,]bi7'YBmK$(AC"~^ `䀈9Vꈄ!ȄB!j4w2>d: "L3;o% B҆M ?UŕrimTmիÇIvډ'`/@W?3Trښipjj7qqffݻ\t_!ɁSӧOǓO>ݹsI&>$m6 B&''ر##GWu~~X,&L sƌJK3 XMt5C$t۽t25) ns/{ s@cQ ܹLwNmY|4CBBE6-33s炼Ad |PUUU$mӗ#L^LB?0NwL&C+z?~|JJKKK׭[7۷/,$t1SE`5 s۲s-/iسgI&w7hg_d}1TPhb=T۷/**z(;A O>K.ϋB!fF@鬣FD;N ŋ4c<_Gejj믣'$$^zʕMw122l@1FbO>M|ڵ{رTPׯW*>>>ϧs)`QQ]r9Ղ`rssI-{ XTD + 0]R34뤔011M:|%C梴03Ѭ"ww˗{iii$d.] L0k=<<@H999NdC*/E%K]ME lVVp8 t#9.$X޽{ 3TH$%%fggWVVx|> p,7t4 777˹n~i}}}]fCvqĉAW#Fha)\jժ[n[ ,6l}ȑӧO_f _~Aׯ'z^zb˹ZM}CCCd\ٳgwsS@ L|Ai!}Z$OTT3Aob1q;DKLL*a3[ %p~~~|I ݉@~ضm[AAR\d {i/=XTT%cookII%*B2@3#9hi]Vץ L*իWK; DU_nذw߽<*}׮]W^EAo4)`=zg[˖-[ti#׈T"zkݺutuP555֭[ɎK.#G A^WEGG{999\.̴|F9rjlԩS+ڃESPVV=]*--R(TϧT*U\\/\fڵѣ0C$5m~={vڵ2\Zh.Ύ|rJJ MT"iӦ!#B,****֮ڈFzzz,SSS777___///`АL߿kja[o 7b#Fd XO^tcûws̙’Gkp8{vjjjRRq}ѣǃ{xEEu^z;ƎPSI ݻwfffbbb||[@Z8`udJ㑍L\. p{G[,OEMh>Rz@>l0 XTTD_1cƔ q\SS#r9E_1^(`QQ= c.]t'US3JxuUޝQ6#"*3*:Qe\;8}z\\4zUj{k+.WoO.+en)8!|N ' J5duj>F/DiǐJz%겶[4{70ޠu2^>e :B}No>&YNuS PQs'{֝clVЛ7o !چTUU +ŷPiIS%"9EgqRTNVCO\f)3dsߖZbYS{m111C;w.ƕ^ YI7p\"`wJG˛N؝9ɢǘl+"Ѣ76beA<9Gygiz`ZZ)$IG}=f"/_ h4pB7VMVGJ3 L2 oO7o(i}MCZ;VX[NnL3vP|ɟq{I LSHt2UpZ_|SΧ{+Xj,gfe"VhZ}q 4 w ƢM[Ff$4M}V+bMmSc jyVY/-V┶YlIlyj[=\pQ'Rd`P繻&歍O> evaz6 -B:͂I&)tc"OEJk=/)F͍Lq9 Y} qQH?[P\XCQݳH "sQ[n:`֭ ^{-\YYYps/)ij: &CzuiןIߦJ}tM~x6ǡ&5NL1g'XߖZWV?vsN8]ى"ys$$ne̬MW)I+kWUuwxtG[4jAU:J':1ޢ͈M֡D8,V+-J@`}fD7NU .$ꐛ]cӈY1Z02Zk_eZPV0Hg f>-AZ\N-i-,n=Zw}fQ@1]|ŕ{/wIHŸK J5Otͧ^̅8v?ɶmv? ¨m[Q:ћbͺ|yDBȢ… C Ƴpv]חJI' * ~@`ŠT+ckG$EՅ'Г"&)FXQ (8R[ZedScҶ` ^/&H4 Ы\)k K$]P߆'XI(t#`lFRKJC$ﮈ13Gˆf\D"E1p br1{D\NxڼyKEEo~MŇ|)o{EA:|'M\j ^7~}ғR y*jէ.\Xx1Q\|믿ZY:iӦE?WTzzdzɒ'ώpjy5Dh4 ӷ[n?SffrΝ;{]طw{CW]]/"i3gO/ӕ,jpرW^Ws8Ao;wqcc>˨?ؚ5ra}>0q*Xo"7 ř kzw s\<$q~: Ouz!::ڿa'^w]. 3O/ھ}oξG曯Vpzt:/|+W}mLl U=zL~}j5UPx<_ڵj W_-Zwq[]("8Qd2|Yܹs555ƍ+ȑ#6m„ pҬWq㣴Hvx3g.Z;v9KU$vbJZ=$I4iBrr }Ar 67oZ-8 =cphbOHi&*'N7/q\w *bP7;~N$@=+|wXKk[x ֈ|䈢(j楗^ᄏNXy7^#E2thW>|r,]ŋ7')6gZ 㔀^{ϊ+WYLF;,H#VпY ui>Ufs+O&"uC\e˖͛7oҥ~$oNN޽j?Eu:]?c->d2&nLOO}㍷-zG5 qz5 /,, ίB-mcџ"D` XHJgDA4Ynyk}*1LK6U¼E1AR !&х knM;rbaޠ0ꘘf1oP9.LSmIڠij*˭9h˼aZGMg7e ՞ ͮ,9GVu-CNL ϝ;r8ڂo|ĉ~Vϥ{ϿcO{ia p,.}嗕s*=E>H'++g…+hkq+|e`ɨ~CCC.w}EGGӈrRڇ"~QU[lٽ{7hi|FI/c„ 3f̠rQ^srrϟO?Dm9^/iUV9s&tZZUu ={9‹ 6I&p ~J{J;H^b2Ӊ R!d/++ciD뮻RSömVTTD^6l>|'t'N2e 3fr);ϙ3LN<))}7??&DS믿&+6*/))پ};;jjǏm ֭#Cdw!Ϛ5&ǒ0"Q;dij5J%Тѣd)[ojSwLرc/hj覛nm>}u~f:99~r/X7n4yQ'#ݻwǩi;:E5;vlϞ= QhZ\H4CPNBӧT 4:J4*)_7?nP\s6l,//[hIz, vl)xfzi)K8:Tb?\.#Lv~Թ7x8sSUg?~d>c Zd Y7Ǐ۝5xeƢs 'HOpkwT=ʙxaE5Xy0'a,B GWi1F񕙦C X0A4^kFbYCHTfCHTLSޠ"a$$IT Q*(_1x&l!o`:3MU>D֤`J;$մJh!1w ,bn^zuӟ@ K9CP\}ׄ4t4K->Rִ`-i^(r {w Rb .>Ex9.Ϗ'[a1} wun` V7ݓ'X~喷5sCn^lRrIyTNY޿ޢ}ǤʎCGB`1lφ'CFȌOӫՁY>Nq>y1>)?H7L+k&F'η<"EQ) tF <_9wpۣ;%#C/K`1[\gVxe_y`)΢5jLKO*kwt-#P⺮ܴDZH][JuU[2$qtVV÷p/~Pz@gwʇ6m$e <vV`iK5;Ub ԈVJ~xzԸpIK?rcvJr{im[4RyqzSUQШyAH f)nK7kAU#qz$Wik\*7kDW|8V-3El'omR+ۋ\ ĂOVvfdNMvMG˛eIQ2F3xFweN>ڨIՏȌ6i'p βɞgĤ힆VρU'r"6!yg֫F pc-ߕ5׵yq'k-'谈 *ۨI/W@o3z`1-IENDB`docs/doxygen/images/SignalConditioner.png000066400000000000000000000317241352176506000210650ustar00rootroot00000000000000PNG  IHDR(~EF AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYsgRiTXtXML:com.adobe.xmp 5 2 1 2'XIDATxSKV4M^H"ED & *BSAKQDlH}{eM6ٔ&7-Swn2Μ97CPHHHH  ($@$@$@$ PA@$@$@$v]@$@$@$@TPx *(a$, $@$@$@aG J5 D$@$@$@ @vM $;wŋGRYV  HqRbn(/Q#(PN֯_3/  vxAՓ1cfbeHHH TR5AIUR8 @by!    PA *()"  I1 @by!    PA *()"  I1-\vM~WAɓr!zjP!ŋr%+~$E:~C!/TP«=Xd"pi0`̙SbbbhѢ#GyܹsΫ^Z .,֭s˗g%KJ|o̙3K6mt<r̟?_s&M8Ywt"UT hܹr=-"%JYJÆ e9sTTIG#7~%>v0?:A!SLi޼5JΜ9YqSOi#kZOxwޞ(o߾}u;ϟ_Apz?'\{*9WN]-O H"RV-yWV Ov!qH9xoٲ|7GF/ʕ+eĉˇ~ N㯿rtݬY3A7p@[M~p6n(e˖y?^1aiѢT^ݥ&(ŪUdϞ=RtiظDrЧOySN˭*k֬_ׯ/}"9CfϞ-;vg}Vw;v3f:ψ#4gp0#`w^&SNmrAt|(:ŋv3g_+]&AB?#E z衇(ڵk$6m^{M6lؠbŊmO(h(+W.}={V>cydBQI@}I"F BP=%ӦMKL=05Ö-[ۯ_?ip(%y9rġz52j@硞Jp$?<ʐ!c޼y 8T~؁|O?T_W_MÎٳ;3LJs3;uw}5 8v>F5<)cڨuҮ];QZ'8ÇRJ)R2e`3@ *Qٲe?~x`L`$ /W\q ## l۶M>͡| 9L#!Z% fSor:Ӑ@$ɭDzEjժFdРAz*ԩStLOIn̩[ض :xLE7.͆5ه},YiL-a?EJz"l[.]*P`D)|w Po 5kBLd̘1}Oẗ́&mi믵($heuΝ;w޺M6-WM7ٿAU`>BPbEQh2l0YdQزepsY`{\`Ѷm[--Fq 02GP{"us¶#GP)P`#F #@uCz}i0]i%gy0aPM!h%@%Z[> CH°uڵz +|MiР( )ch :L2:@(AtP^+8n YFa0i \lEthP>\S&/^ &ς h o sbp,4m[(fX3ydR @KR3OCgX.I8{_i?RZ5su e%˹a@h4]hOZiPB=~*𧂩#|)cN_2fѳg#X [ȃ˺Xk!_x4cc `  71&5B|"0RiP)obu3a5kzk0#Y(*VTP壨x('gdFoodP,| lY +FС[?j%IO (Gj;Lo`v1P O-gtv9,F*_Aa5S(#] '862$֞q=6Mt$8-$йsgQ>PRt `сBAyǼgnxp= G1 ' lICQ1eP蘴kͶW^Zڵ^ӴiS#,8S~;Lt=ҳ|r EOKa7) cnHVׇ?\9$dyKRluy\rYS鰡=k/? 蟇v\LZ)k%Oz_*x\&mZ;3,mtRrRf|χ ˹3qNg2ܒ OTP|"JZ׮IduFO-k~ e/e1S黯?+nC\U d3ͤb&Ҩe}%-z)/o"2eA}t_2X)cgnM˙40HfM|B&dȘICRJCad'˗.Ⱥe;+Hw,)xA) @ 8 MNr̫?%݇U6R[6~6_ݒ+_Xҧ(YRb@<7S7ir)e9杞lk:|mmҤI]HY廸Ϝqr-$ A($@$@$*( aܲ˾qp8tN]HFJ9_ 6{g>Cޑ}Y8y}t~Uə_Q9 )Q܉ +/9ùC$@$@?!ΝwL\tQBdԈ y J(0v*E 6{>8\ ۤ UK[?%ڥeP ܅sҥҶHɧ@1SDܐ+X!$@$@$'I )\jE^}>V_(W[-/*.t!cf=ʂsG)t]o.2YqHH&ڻ %yB)^N~`v9vW0b^-E(+M')ې /l:8]K,-ָiJSfy ⅳ3w~ ramQX U&3/Nm>ӿ֯#U뵔j[c+_6Xݹys3N giץ锂e zCyԩS-[6ɘ1cJ]RNSL^OߒG7'k7.tKfEd?U̻mťijVheȐQolOIKNz񃷇9+Q^^g=%3&f {~[Fp' Zmm3&^',];y|tK%u}DmjWV:Ͽ\u.Y2{Ê[cJI揩Նeϔ;i0 ҪNZvn^)/ڦFl=g&Cok9eZ2C]oUM}הOOl9r JAپ}JժUeҪU+ɚ5kSqSˑU-9.sO=yO.4l}8\`RIuHW"pz ~Ssfѣ<ҫW/TRJ-mckSgZ^NS}}s4_.Gu. _6mW\׮CBYtپy~ } %]]ǻ7tVrevcDz_zE/Bv|sy.ڟP%L>\%5++ 9}z&I۩{LSk3ՆX+V4צ((#?l[#(XGy{n^状(MVʊG1;4{ғ5ǜBo}ƶmJ G-ڵlٶm<ҳgO`f͚ U Zˢ/<^H?c{ 6nW)y+BLٹEm:{rUSvd$v{-䍀zuI`$nMVYVƿZC1^9ڐ6rdk`U.eK߸͇~0l{=CIǟrX5eoߚS@/ZI0D/Xesz~0K;Jvyq8WҭQl:ՆSӹrjY+5҇bn4~]*%Vv+5RӾ q-?l~y#g>"{1딩3BnرҩS')TPHiQOWHM|٠{U V mN{}ċ[DMyVaV(ŕ;0T=Kv+&=o&nؼPo$6jC8w1>/O*+[G=u/etYYTpr7ϘW9;_2Y-|/TL*6y75πJq!S<.9*9KMS'*q> !);@ !u(Un[6@v8+UF6ʋU0Raut"P!чVokq(e#Xw<>\{ ŒRJ9gA<"x^`SqeC!PF(iשG[Zk'AA%x1qº~ gI%g\ϙN$ۜC[  JR +@JoK_(FΪ CV>.`6%j$6H1%+8K}2cBsoʱұ 3px1<^$)3 1: @ 7yVRտ@|fpzTfDWf/]Ļ_.O}*SGuёYQkͷž((I}$ ӓ K JHM_/2_dʜE*Vʅ!1 ~Kzxڧ@tuJɩѰrZ^ πPPd$@$@  ߅뢸x'mn;|TB每WNz+O0/K ˣT׮^4?\qHHH.3FIHHRTC x#@'  H5TPR =/L$@$@$odxHHH PAI50 7TPy   T#@%$@$@$@PAFIHHR@z=~d6¡'p!18y_+B򾊐b}ȢRA)]t  1mI- |_TT J Pd^+Ch#@hkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HHhkq֗HH"h$HH@HeСVlHHRzȵ"&' M$@$@Oʉ4% Vʐ =PAG;$@$@$`+TPl՜ ؃{#kA$@$@"@Vʐ =PAG;$@$@$`+TPl՜ ؃{#kA$@$@"@Vʐ =?h:IENDB`docs/doxygen/images/gnss-sdr_logo.png000066400000000000000000000213251352176506000202260ustar00rootroot00000000000000PNG  IHDRN Y@ pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڌP 0  1Q)/7Ke,w܇&r(>|xJKD;* */R+T*s. ˒l8m#維!ܦF;sK6ZL9d9$3liN>"H%10 E8S8D@Q uOr F& `َCD1Z6E10u){EXC{! v-hTSH*4mB.(_}V*s%H>*i7}@D97Un00Wt7DH d2V2N/D] kklAgv~>N5DUN6{}&5EQ 80۟}!4 JaaG7MmR"~_}5;$9ȳ @)elf!Nc: s4X/OSm3k 'Jw[_EAp!ah+8 !,K$kĔVUӑRZyQZoWO0?h‚Z; DtƉ 1n9NJzls=7:7%c0iHy6d2q[e8ձ)}}=6"I56mj "no?e/As ]hXUH1mѝΎGW fM Xw,8цr,#oy$Dr[JbIeo{fIZ몿q$ZxamxD-瘟v;Ih] ̩[_n&J;`k^vu邼afUfOJ ǜ a7V%sy yOE@ZB,RҴbS=/80<$ .ΤTV /"MX4:hmYFQ|*$G"iFr ,㐧cԶ)qR*8F ~RDQD|$IV{ "׮N:wb m.wsqq USޠS~ !G c%M h'߶C1..n N& |?7D'JI.I ?,Na=7aVZo4)F{ۦL&#XP#fC?LpeYOETm}Aڦ9iΆKǶEQ,i`0,cly(h; ]\]7&`̧d!U<onn$'v VI4 [;B*ATdحVh0r:ƞ\TΠ{A=ߚqM$J>*2&I6ώaTF~uxi,W*;Sl:AcR((;1>B"yժS6d$|qD<>(Q0Iߎnn㶚&뺮%6==Mbkپ_Gg^/ /2q\2/@NGO_acGɐ _AJ`jmF$Tt<ltB\d2tP5a_>;mf4ǎ!ض f, nh:RKGc3KBH (OfmuU _m}|P1'|UMkYvm^KɚUև2"M(qdb`_Ë 社*oגvVg9߰-vaT@s1oIjr0t9ül мTk[Q߿]OLgXjC.1E"VRLTZPiVim6z RT8)+Jm6R0=asPGԮ(D9Wof<6$E)Od<~fggŇ1&|f| [5W{* Α_~_[(~+p'C.8.˵;v d2 9F@C xEᇧN[=yIXlWU"~ܛonoo?~8}g_H%ʃPjZZjP`C$ ' *8R]]]jLiV,)%sss/ol%=zeoYȒeE @ģ, A{ H{jW;;ߌX51}m)J"fUb,$I  # wndX{ I-9mN'Qpm@#pL%I'PBDZ MdoBn,<ٳ2xYٌƍ`(Q4VݩT I[⠪wz&+LJWWImdKKP`_o )7F6z®7n ./-Aj'!ѐ(777c{ xos:Jᴚbo $,㮚t胇!&Itux2 HE!]|J}LRŻEFFGMn8 ʋwhv؀b TU7]\\$ρө|>Onihh0(SbżC+{ʋa#Ux BxjJ ()M߿ov͑*Y1who5(FG+降)SO5b/|uJoz𪕄bLd221X${6֒OYuWh.vy?|P!F.3SiZ!qsS54^|*Ny=EUB&5uFf|VGDQj" lb2jĕU&`J=gу(n(^OCTf*r<@5Ȳ\p3Hc{MBQiC`P&q8T* Ԅ$I$IՊ#rgGy${7Y RiՈύaCGaYX\^Z"y-/ze v}5l~},hds9,O hZtK#P6qI l~uPB=pkw @`1Zoz3>j`9{J*>Ы&aғaR" . B},Uۜ[!g2@8oj2. `e4Z{sdO٩gB>RS\s"G|`v 5$kצgfƂA4lnpIs_ oY[_lTc؋>=w+_%4obmDiL\㼻KAy[iÁ04p~ah0.fUU5ﵶemiΔ}񨊢x磏~"w,Y4 N nꬑzk``m}":1 ~KLJ-ӗ/]۟[(``dJW,.//A" BMMk8R%ù [l,#3=3Ctg|y>HeTAp2hooGw}*B+rC(bYLvm4ȏZ[6m^8deG juʕoVWaDbqk$dj\<Datzb a53&+˲AAlRCbWExW-;խ0 cLy%)'4ZrztCv ;:7; PFzU(8g!O%I>{m h$BjQU/QhEѸ8yA"w,meZQ q@*碰<2Ļh~mt=6*)i:`lJ(Oxf@;RR<`P^VhmJ|_)HIeDV]̙37ިzVHI8ƧOu75544l MWon$IENDB`docs/doxygen/images/gnss-sdr_logo_round.png000066400000000000000000000154711352176506000214420ustar00rootroot00000000000000PNG  IHDRHAEN pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FdIDATxڌA PDahΡ"$O``KtVn^ ?fq`QuQ׾ l^Uy4m^)yVf+89pmSҲ,ע>K0Hr"N%ÑÇ}FVFP}6nk PZZ555c``WTDYW[kial&"\$ſnjb!1@E=,W@ [p,X<VH#ݳXCЬ|̵m{k!!M 6ڄ뎯gYXI {[=RU$YG[ykFm`.uQ+4yHTg4/`h:K"6\Wz"$ ?~..*/+CSMCM 뵫W8CRQA~~޽ ɓ޾yWyyytG) S"'=tGFGG_KSF0"!ye[[ې100<LRJKH_HPbbb"#ܷoW,[}9cI135gjd(g83CvV;;ìY%$I{;_~-^dϞ=ׯ][z9888~#߿)G}ꕄ<33ϟ?. `aa1c NN' Μ>,X^^,r1y99}=={TP@ׯ_pΎ& 5PZ@h@HH |||o߼a``066~YXXXYY߼}+Zʼyx))X@IAȽwa&s@rraUŋ"#?|o^ 1?xYZJjϞ=YHgNݻw/rͷ"x; l۶M^NS$3ظqႏ=Zf S;[[iu0۷oK/##4܌^EA@G@WBcǎ]8?5 nflh(&**$ `ceffj)",y&b !l۷oUUTdeKK_z{Ҽs߼~]VZ*++,۱}f+!.P_,"/'ߋ,۳g9s]MaDd0k yl߿&OW47740ڵs'I&IJȞw޽ O 9x ##uII))dN?~8v8ӧRde+`}9YYHC~!-%cb16:–ꂰ=*!.211UOOϨHb-ڳ{cccM`U덍0 m(ݯ{,/|AXP  ,-%%$|m###ÇYXX>~ ;{KJI112b;JAPP߿޽MLH```ŋCBC=+W0LH=]ݎ?|PRBBEYYYQq,;E9w_o-Z$j1C4[ CpMlgg'|@__EY~1JeeTo_^NAlT?+R #m:ZZ˖-de--4ڹS [ⳬ9**$%$LMuEÆL+-QUȭ[mݪԄUHΙ#'+?4q-MMEHlDNV> ffeĔIdedLQ*9y*ʢ""{w(F b""*JJ 'h -Zɓ勡A\||PpnZ6j^`ڃ:x9wZoh"H&lvhhhppp{{{}}F7ߐ BQ$&T*WewefxqjjZfg1CUeeLt40+3GG"`W:;ccb@Dhu2 .ڷrPEtv[B BbG/3áo׼y xw掠% p S$&9?\~UV`irr^+B!a,"4>>>..B(1LGD޽?l9JKKa!3gXUe%yB.\ǃ i;v|RLٞ/Hx\*Ɏ T Cs?ziKs3q Y`<,/]11Aj:;;Zf.qo\.b2m$+7DrRZ-3#t[N$gO`fX e8d2^RՒ-[Hhjj*..zΞ5-v%sfАH=˵:Ep^^^vnsz{`zRBlظӃ:ݨ8~mXb؞=P( FVK{+)yVѶ)mwܹW J}v{+)۷"#er9Fh4:ͦP(TKNShZ Ƿ ͛Aլ?nl gR׮‚p+Yv bIlѣݑhuuȥRkll\ͬ8i|> 1L"z^Eaʊ Ax x֏wUXPdgONMz`ؚaJ \@(l&Nt8sKp+9),DLJzr82LwRb2r? C%%?}-knsK:V[; '$6mЦVom~Ơ$N86mQݻwbMպPׇ"bӁ'O8Nt@-rrrLjmmt\qIsK 0=U١VG(Ȥ˗/76L[gҗ\tI"P IIbCQJKL ơC0(]/p #啼F<$J߹3g4-oI$>_,u}} OHME0_Tt޽ʼhT LRL&6lܸ~z*JRi4Bqfkffe2111/:ajXFFF#{~v _ q  g_{ % kc.ȓhIENDB`docs/doxygen/images/overview.png000066400000000000000000010731371352176506000173250ustar00rootroot00000000000000PNG  IHDR pHYsgR:iCCPPhotoshop ICC profilexڭJP@ EV p'QPL[):$ٚ4T)M'prt7P:8D g:òQaUU' ̲@n&vQ>_vo4a hcqSE?7}(2`RdN'ZV,˒v/Cy4Ju8L^*UW= lӪz2C oF91^[m na:o+e(m ïPo/ cHRMz%RX:oZtIDATxyxUUkҝ =( HD"8↎8:n8GFGDEQQ6YY]-@ $@Ȟ^?:iaTԩsNoyϩ(" 82"|Yg\?bLLQ5y""2h ХU!Nz^0Ayr2""27퓺ȇE(6^u$<0q a""mc {fZ {6nB""eBULUUPR{1ah9 zgg_߲xRRt_LV!%=ygƨүjsL""rS{-ҳGO=M1tCT:Ib7M'ȈFW."bR)**?rE:֭]C-n,] ?m<'ܾ9O ۶-UqkX@,t:-4Z;s0_~z ]#u]k׮XVv<`LjN[1s>%m`?~ڑIӦ-xyv|H% 1Λپu?[lBC68g\ʣFAJ6ұW< O<ǏU6t+ع{7/">wj0lVv-` ?7b22p2}+B9;tӏ?g]4FT3q|>ήvm ngExez <1nDž@n;V _z >옲(**" q%`صk^O߾}#}nݺQa, a:FL׮]21t eQ*sXDPL 7($%%Ѯ];\7 go|6PZlI˖P/[c]AAE׺$jʿ[a~}~'Ld`U{׭dk<W6?;gpR>[ɣogUA Aςw\\: f"oN0Izz:ڵ۵u?yvʽ^F H΁p\׏O?4o~gXfNQ eX3k,<$Ɲ ~}J>][878lޒՌvLw=/^zEEӿLXє#zn`ժU\zUkKu4M+0=&ǧ"SoN|3quj8BN$&&ҹ"[?NRRRNv1Zu,NTEDŽ_5wW X,58QFѸqcrssQU~HiCyIsK/10*D}DU/^:7b&P^-&_WgNTw~"Ry^'Hx"%*By:sn:wN=~4 > m}1(̙Ci޼9ӧOgʔ)0mĈݻ7B(B\ѳ }Ť(wA,7YpWU`(aRaȤ{ /c2]tJcƼ\MAl}a{yGW2}N^<Ǖ:[dY:}^9u vmj M"Vr8My_Pr{-91?eEC({4" i4hzҢ|s8 X6wnc옫0?drr2Ma׶IHi% iҤI8Zk0ï!{˗,[03².gD%hFJJ2"B-q?HrJ^y0[U q?~J]G|l45on՛9oxaXv.UnQpr"0hLK81XL-#S4lD9ZLD9+G1\ue*{n-SyIɞJI4o+w0hF X`k{noMtKM;~TQDg3t%=Ƭg"/]ܑKz4&??:o;֭[Ӽy/zjG86 Z,/ 2T\|5\{ZqӝëVkheUsED^'f DT ] b2]59Rxl((jcyجKg ˫Џ5P F~-/d$$$D4IF 8b/3-sّjhРYn҃;1vщ R OuLJ6ҽV*zv(C+̙sv;h?)_q||XUUҐmN+fy{h WPIQLoWNz0fAVظX.Ғ"<9RYZ/Zӷ|•)xKiה p6vGJ*/QC1Ѫ N1l^}m QZV6-(X,u@(.)"19GL{@Tb8p`FWp,N>Ήb&~w5?e`ќYrrrXbF~ 81N/nq}v^ zs ;{]X,v\o-ɍ"q} jpRl7ShX-9is4mڔmQ/" }\!`GAV|:kN!F.XJltK;Ć@ㆇITUHraҫ|ԴiXOqp]^ ϸww-{✉ǒIZ嬊EXBw-!)Qx<尵Wͧ 12&ӽ{wUUQZ9qpq1P`?ׅ\ŚW޽hmvTƮ{mdϋ:˖PР! ~.]`6A$raSǝLnu ߙs_:`Dll,n`0H||<G||<~ruEll,PRRl6UMhFLL wUJgbbbp\N\\VS,f@kG0y9rTUg2())*pff:Oy"Vfw;J:`0XmޤWag>E H5M#<5Li< 6u_ۙ0a'=zu#* ;Ybss:'d;C~r26{tyo3}},(U 4>裈׮.\X /,ё%ٙ p ӂ PGT)# rIv*V`FrmXU6«۪ 1ya32d222"{tڕTTUegz (l& N`0x~4(fP\ݮѽEQŞy^HimYtڕ8rOHWƣOM"Arqn&ﯕ/=}W7mO' ttp"oYwlïl^&äs(jcٲuM_[VRVjpAuGyy>t5UMӪ&}=0ixL~,r?oʠoF\tƲjժzk$7MF /̕}Rϥ-c}f3tJZU>48X`aS}GrCq^\\e[;>cŎAJbbT XE#hsM1B>BA=L_@ eee5ލ۶mBH:oj vM̚E&D/ڶ;IT!bV-2rȽ笎lo4-zUqnAV+mڴaGB{MQ8^'8t!00ӽ]r_)cJUfa]vIII(RYD IWh[Ԗ}٨UlV3?m?@kF˖-)**ra#BG8bEY f I-ݵWPU=;Q,q~f3P,L&b ==B,KWEB@hAXa9NNbJZ姝[PVdܸ=9{+'yᇙ2e k֬ W0aB›>{1ϟ_gDണhQ(Pdffa$4's$4xƌ\ڹ-j%s~%&wk)i3<ȯY|9_~%7ofʕ^SO=ҥK+*/YMΝlhs6nނU޽a B0X̑B'Np {% qx M~U @UUrrrHII &]d8￟~3gz9_oڵkUp+@JJJ>B3Z9yrҽw=i[iS>y?MН()@csN=]P 1 $7dĈ{!2$^Ef5Gf/_ ]}A~aCNhԅ t qq\q7! `P $w6"t!Ĩ4jM2B on99{OI@)\!E [_UK-&~1bGŐA#Bx<EEbСb/VZXbMӛ5wl8|" FaIHB2S E)mo bu={ڴe=W_9*r(ѩ8d#5CUǻvg֝|"‡I-$#IHKrE~=0""3/{~II:Wkcg/ڸo OUkhס3/>.Y^/7gfe޼1 )|6˜lܸrG~5GSU~NMc=$IhтFaZډZWBZ.㛅[v؇gݺֽ͔/;~9RS[`/)^|~ (7b4SG]hdbe,XT<^/3\]烪_W&9" TH?U0W<~ Gs9yѴJ. KdXk$ق"'pG޽sMnUHopi$4D`ɓh|[A#؂`ʃ y_ُoܯ'ƍ6dhZ>nGlۺ]lھSV !6tXrb"//٫D&f%BDX,B!^wkNSÚknÂO @lؼUTѵS{/}(w*1nTq3}GN={\ '啤o߾7G$B[֠{`+Ͼ5O pG TΝʺ~4LWN^ ?s挞 AN_洓[UDF`~N}=$7kř8|9uoҠ*~%/E]DoY(y FOh܇ኂje֭8]>wEɉ0L:}ϝI)255۷ӻwo=;<}Y!`g;mnX-WdFd<ԚO)IyfQO.ذ_iӦͣcǎ@?d~JwԩS$⨨7;`̘1=Or3bpRܫlؼYc hٝ;5!pb/^̄ . Brqw~] 4J˺/699ݮjz)""&MW3ك$IsBg+ XmTIBE 4UPU}Fm۶K*7pڵץAP5;5ke hyH 둲,3x૎х&bPB2Ui~Ԁ?J:,2;ykVdŀBlL$UN5Az27'tΜ+BewU|^sDex9ֽ gr)%66KtQ5$ (ue\Jjr "[//^LbB)m&+k8zO mtzH"('!Bp9r$#Fwa5f*~nZl8pKoÑÇqWVh4NBT&+Hf-XRR 8 NaTj~4^B, T夠   b՟|~!#b/Z?*Ԥ1`11x~NTUhXLHZ5 a^Y\TTw6q{g۾q;eue˖ѼY3T!8q"[lR ncM(һ]Oe"""][y64|+[jUoI}.]%7q@؆OZ?M533#FԹvǮSF.MN?s1g| QY":gYG 8j #GСC7 W_ NfIF\d?ye z!4MLfؘݙS),*䁡8^QaPP[&KRB/N5q<'(-/jĉޯ_?Crۉ ֙Fy>uo0+gL`/X,5A*KU|y%c2l]=It_߱N^)ދ%2MTMGQDT4 RU,bcD 2f|ޘ>|[.ɍoNM.ņas_Q\oJ+6enn5fL<5v&qN{zRV[4!_p șY1BhӦ5y|^()5V] -ر#bw,弅ɯѢp-q6a\(+zjD ž:VonL^OFK 9y1Hm"c;O,>?7/v Ӧ{وJ}d v 7ehס+ $!IHBѬ ycoxh+p !@2sמv_^osـKև0N - :D; ., lǃ !WѣGdK 1r W9qxU?"h[o@ɩ`кuP=oϋ;KL솚|amWe M `.R͍ETǛ7ovr]5h.^+F hIFYj6`TOO{?P|r~)(* zVCE3y>N{W~%6.L8FyJ^ҿ 99 ݎ $!$bCzCy" /?~<˖-"r." ITd|>py*زw} zu$/}^$E%EПVa# ! f5SyLQ;ve˖ Ru(SSS)v\!*k$6曬ti}'QI>* hx֜r2րYv &'BR}HB#9.{y0/%98*[jP\`0&77M6uˆIs$=]+xȐ!&3ƍpTp2w?m{8FĐМ7@J=_X Dξ~ww=?A _6PYUERR>7\e5%ʪ7> ƫsÁ(7U<Ykk*xPQ}s]M1Dzcٓ'NԊ^+vL8;%{wWX#G蟧LdBQ@$ZzQtTT9*..=\*$!!lu| h@bb"fYk=/>>C=gO{ꯈӟM||<l޼ܽSѺui8*/ùʂ[tڵVxU9|0555?7yv[IzJ{|SLOa o Cƍ'7F6jԨk*jǒxƏf*6&IA)%;k/)'T Up\%q P!4If|ϻWSYEמ_QCRsB.]W Aii)~7;FHdџgR|37H9 ->Mq3$ͦ`jj8)+;֡ᤪ*3wRw'ώ;k]sNϱcǐ$]Ȏ;L*F̗?cՆy/@l&Q+:g iPk`vPb 6D+}{u}_s0%eTT_t3*rJ>_CQ/zI(iʔ)ڵ?ʋb` ?@,&@ hd`Xn` M־X*<d?BdH!rfuezU믿Ama/裏o>}9}4f̟?^` 3n|pCP1_?Z A^őG.m3g+dkSH Tި3fQ?Yf髼[! ]Y._c'h"6^dž g_Ӥi**Z忧M徟oz-YShZ/aw ڷo JBnzuHM2ʵKvWSrpnxu#|N1vX>SK͙cI4dDQ dH^Rt ܌$ZOԡ)%iޕNoMJJ?\4"++릘Klqr|[䮝c/#$h ~UE `scchKK/Yf[b\.O;TopE3{[G4MDMF˦8+1[ KRJ AXprb0@.I>O tDGDl^TAZf#:-~M + j@мE\>?FE/{CDntkȐ!lذcǎ|rƍZ:]-PsRU⣢ƒ N"ZYϞ=zv<_yÞ#hS j|M$HӏR{1Iv>2*L+ s=4om -|zqLr9 |>F]rU-Z̝;۷s1vٳzᩝ>_(Ԗ'~cs8/y^߾LN tݫE+BH0xZIIɵzs|!EYs!2l7Ǭ^:Ow;oBČ !زzĢpU/Ǐ{E! % t& UPX@EQDDQĂ^ z E@Ql w! =dd!R'ٳ3gf9NWMן} 7s->I@;"[q?1oIB!# Ӏ()3 @HZFaPSmL/>!a Vt2~ kfdB_G,bB ?\!L"lLBx,1c3O8Lб? !x{⫢$?Sd9&V~n!SixB,bxxʇbۦds|TCa. .Wp[-W4%i=%Tip5eҨ_|E^yKv@ew"kT7}.Ox?΃tԞ^=ޫt<{qU aUj Axxyi#<8 /BQq.?H`H_lV;Voooub @;CnvN%)E-R tF u)ޤVBa} lRuc- Wޙ3вm<>5!u&--R-fcoFaq -3Q֟YGhPt駟y+4l66lx:U4Ӆ-Ku%ߵÐK]{ɓ'3qDL&IIIİw^ZhA۶m)..& ֭[kR\:!H}P%m۶23a,uMъ )gO-SyLr(Դ& L&t###W_uQ -:4oLyy;e퍈4h٦2`[-!C 0t2ȲLEEȲ̲e9rذaCÔ)SO>GNIB%ʕVA%_n /nDW޵kדVEB8o; fWPֶ'[5n#'7=uVؼ~+ 6IX*Ъ HZDҥRاǎBW`-)ZdԾ۴NhU 6-z)Nq8t0-[-//E}:pnCgBqݟͩ*B[b=#qU*M8vN=4:8zިU^Y@%؅KI6E)?vm;$%O@$)WRf,/\2'g(,7"NETة(h\_ETz!HmB%9kP=Հ sѤ.Jp}^>zmنZ#6P.!ۈܶC+DQt:-`Zj(hD닏ŅH\{dDח\TZAVpXШX,:=NBPPPtԉ;v+/<8+d - 8ö?ѡC5wZfYK~YujZ/#}ZW̓ϽI_{жA(*'*7!!<=bMZ-Woiж5;$u⓹ݿ)Һ֍2?3q έbѫ]'iY@Ph]ٛJ.w]nXG!7scN8!?/bԨQiӦO7\ @JN(bשQKSYH3Qt8vޙ9;o!;#5ض YEi^iP[uB~6Zb;(+6uF5TJJHK%9e%m}TQQwYq^j4TL2o<AAA̙3Ie̙  3gt5k1oeO%&с"Ф L/ӧ$$D_5x/&qX W_eŊӯ_?9$Iܹ YCS%+/ۖWѨIZV!*Pd$ "‰"'v fa;=f$ZEwۭݨT]H{B߳bh֭zQV&~Fl*zhG\qɇ&4xB4i“O>I.]?~{\ճVH-9q7m"\&^Ȳ" iQi8'zo*̘-ȨG/l3:B pO̍(X.G^WM|z^n l^is) {I(Eg1h :DFPTe>x1Lo)t-G`NJCʇNDFb8y2k onv{O8qb)--QFpXѣ4iR-#<33pƌ ϖ}}5yiVX4rSNS]7"o=oW#u\x1'N$""Yy|8H`CPNDDn59sf0g |;p(%Ht06&ٵ+ :w'#ekɕ[RbjRUxDeρ8Eeۺʯ~=|p"I(N'&͛տ  ={jΜ9\$''pcccO7 ,̘ì'Ym|tV2V$Æ #%%222 ձoAynxyyj@_3Sj c0yJ. [s,s0=Kvj&oC뷨y(5#86+Uf3K,uwa222p:n7ҥKbގSX*X҃ vdժUL(~*A'+=d!U xE=kF}U@Ƴ`=\<핫NaP޽;6mgc RqB[_^ԯgbvRFۻ.%..C \/}[~=gy(72Qn)@cP%+hyZ-d˃fwhq:Ѩ~\Jez]?Ad(ddikmOqq){]=&?7<22LWa~ظq#=ج 9g7}oEeeE ЁVF%@~\IIQ2eF+AMC);=j|)5Kx|? vayZ8r.oM@eu.8uB{%MOe]YEzqdÞ?ƭߕ,moyӧDиA kz"lL&O~+V0dq88q0ZlHR8huJiNJv`.0`D())& 7lO& _K;O>^8o$+ )2f~w"ʀǭ=ǷK9<{oc^}g2|'NwOKi,&NUw ஍.cXzsl4n،4j|Ky M?~9޾w,o5lovݫWZs%w9o#$w^EPʊT$,$Y8oݵ'hkΝᗂ47?0Jjv}ݴomO'W h׹ʥT d+9mot l>Ag#뼐4~#S>tl7w]C///˯8.h5~WU{xc'ryb v*LxxTVA+ϮKI(a:9}+%4jՈ x1GK'HɎQsyQ/bZ Ydf&qfͺ)ĄBQiKּwyB"4Q;.bϾ=EնHnՀ~`f}9oQ2\gg!bdDq*ː-[{jmXݚ5k4hjV ^FPTG%b*6piQTɁCvPTOPP8 5 epa;sVyjx u=ҩ'LDD@5}`ѢE >JjI;E~q:~Qҵ<_q:m? #j #}/^oEdti]=4^l̥QL;Ҫ+;@jz~_/66'Ng6؞y.B KK(wwH$lv;ZB%xyѫSid;ʶ_OPRR-(O=o|`0`6d7nXcr… 1bU'/~yGN"07VeѯjD&w(Κ$!>w^9hsn/ 8"됝ql_QxۣH"111XV5jtk@ -8Nrr XNQRZ'Ŋ 8/@#iyp:2{z믻5e j=#o?@VPRyͬ$4L,ewUFTϓyL*L<{)j~_۪zK@H K31'B>ٽ_C{Ac뫑UVq=N#::ւ=jo$oFfWY8qܢT^~TK9C=E,IX_ m&xog1Lz8oXC?O@BHzLN;ƃ]GWqm1؅!³XDPޠՈlK/1{lL&uiժvކ?K6(8A9C^ӱjcV#EAIY.~ޡCNp*J p8}8)p8zm!=8/NÏj+Sg,dɵ2w \6rʼn٘\JJm6l~Q6G^1d 1?ȥ8+_-veo~^]X|M$''ӺukڴiCBB͛7M6jՊcǎu?Z(Bxd]ёөGhs̚<碶̦k & btn9;Z &AQ:W/]'Րj{.GW>a߬:t On]zpZ$hs{g ?`o$K5kBAWݸ922,6]Fj7ybVTWvil+aMxUISNBXP=˻켶lقZSNu~Usl6_S |sW(>x6B͞:pfr_jS7.4mV>}yVeY !n8>ad_W~wA Xa;_vΥw'xw`4Nzzzs.l4k2$ t[ 55K. 2~K.Ɛ!CO.H;Zcs ϐkeO#9prޏ<7-7،6ӥ Ej}(dJ*,$gg"C/㥲Ch]N8="E Ғ"V0 ?O 6 {<]^uz_P\m۸q`̛7}MZZSLaѢE9rzU&{Э[7>|E~4ܢս\:r.u9` ֭c߾}W!//NJTX 늧NY'ԡY{_54oޑ1x?xetD nݺdffvZzNYfffRi8yf͚Epp04nܘ'NdBb)-cGp/KHzZ.-EP_AQ"BtYgX~ +7f{ j[%AAAtfdfd PhZ54~sL 4*IнpNİϲzNzOEF/`oGi6֮]^dqէzӽӇ'x?0 zMXX9999r'N0d$I={`6{*Cii)l޼(233/yhsݚtfo9_1F:[5J#,ȀG?'v'qߐl۳pJɄ_P0YX.aa׿А1E)3q`o 2AaC=´ ڈWk׮TTTpAV^MϞ=ݯѫ[c4iڴ6DDsX?o¾{h?JK@H8M;Y?WQf o*P5m®]0^>f OZ N&2߮oٲ5̼"!m8rx8u46oMfV)όsfB@Jaf73g|r;.]O>"چ8T7d{!11HLLy?Dbb"wl6ǿl2ecILL$)) q(//gܸqt:6l@bb"V#2ƍc1~xILLرc={D***?~<|^Dl6Ǐ'.. &dxlݻDf3ǏÃ_3gȨQsssIMMv}3f o>>>ծϱc03w\^~eT*ǏjVe̘1xx\[ӧoλ#i~zzo<<1ٽ=E|{$cb0LhUiSkq6Ⱥ 7)~<6g?HWӉeY, 8'>=zUB$g Wm 3QL/q&Z ~FaƇ?2`yZVwpG===)//宨o9| xrw\Ⱦ!28FqXpAJBQdGDUu$ J{,) T(p-F^3x[VE/ѩ̢}(h C+0@ |Fyq5q:u]$وiYMI[P቟'I ?ByƎK/;&B 2{/۷owWyRΝ;GBB6llDR%&L@߾}iٲ%oBFk} !( u^ljK @VwMAWQt0J׏Q{YPّhmhCU01[n.xՂ믿FѠ(Jn#n裏W_!Ij-[Mp:8z Z,ے`&?/{e_YRt N V dFhh$ٙ8]eˍAnիc-0ƉWoygN'+V`Μ9aSCX?6bH,&'Z4T8:5)CMTwt2iHNL4.1ccƌ!==Jz=ˡ}:wsw>^ШI@ʙ#ԫ߁];hҤ YYx{kUoʅҥK7tj^K<%'žuBбcGvYmngĉ!w!88Ztw[_T[kQ'RJص8d1UQ"X8'C^ƒѱQ'l !ZPRP]!i)NzO!ij#Gݶ"ŔO>cf-, /M&֭^3ⅧF3!S> @(qgbҫcK#!R \ۼԮy1!"{D;QL[^L~}@B!| "O/Zl)@-bn/d[^9tB!Ξ:.WU?egR'Ioog㣨8ɦW $tA:" ATPP6DҬX"EH!{}lI( Oٝ{Νߞ9˼ҿy}Th}T!a!=|~xQFč[s @\Ӵo @ '.EG}oxEYaYnxs ѵ+ȊLWB|bĒ%KĩS'BxuO?6=F !x %6E&6_%X-8y8vp;B!f}S,7n;tħ_~#o?Jޮ=z.:$ E"̞.z!ڶh"nB/L~@|hJH/X!m[y_~BC!DmE=˯e"Z4o./, v|-!6=z\/2rD"nC%1չWnLQPGS{玾 6o"n_c{p׈})DQs7 ]޺S4[[Do't0)s]&pMZ̓^^NbԈ(J_˿7c.N+ L{l/$$~ܲQ1vqOBEEH|yDe+lڴN:R3&3_-uu%  s4t&v$ *ZQqv뀐!|N&49/=5MZS;!ް~99x4F#jEV$N8Ox!Ozra6ŤY FNN.yyu놈a^̄IMM~lUѸwi5Rl$T~)')LaO ):~&tF#̚Ϯ=htqS7-ZFvidfq;H`PnɓIS̠ӣz"wB7ٗͭ͝G`]J!ͫQq_6vqr>bĥ$ɘM$ř%hnqq' H +5dnVqA>xlhBPqQDv!:s@K+ ;v7DnY5bV"WI>x}vjrM8Nw3ɬ]=n ?K_ZGffaDR}H֌5r?&.$xr IRj&!eAvO>N;C@ <m6ڶm̙3y'}~t:BBBW!!!t:"##eznF/߶#Qo7 ޳iӞe az\ߗT3y++P&f,Co?e8/ nO9~{@aA{Fl N&Mee} 1ؕ BcwRn-*Az.#h8n8>9r6Hf⣏iq[HI7';'ޘ1wΈq]$%%]sxt:rRhycw:@iV~ Bt\~G"##;<3<2q"_KN^pI y'[?L/U$aswνh\a*[2lB6A=֮[>%//YWzEK/dYU[oDU֘ܳ\߽mECVa?ORfE $x\\%Kl~&N4sdY.+~\:w[+,[Ν;ps 7 qjO/OƠqLJo[A-)- ## ͣԨCҸQ}5iEpBmC;7rhI6݊ɤШc7VYCک,9xnP-R) 79+Y$B* :Y#D+ԮA]~AꭤY\է(V0mH~ݾ0ĭo^St.jбvYY2HQ'&?^/<fi 0ʫTNdr:E%vTDfFv$*2"4Myy }L51Dg27 f.nը$I[ٵ7V%nBAA:"b9t'O2{ mF&:xbTe+/!wy;*!95`ֿfy<'F#{c60sm; ⍄x'=jPݨN.CAS'Jf΃jȊ(>ŸuyȒď?,{79>Xo-fhxj# s^K^ԪQ0?р/-`'oQHĝwdӠqszRxj˙,$CWY=3,==Sgb%233ENNHOO"33S抂|QP'e‚l) sZ"22REvVE"%%Q犌Tqĉeƍ/La4h'\ln.5?TWs Q&\hS4L<#ޘqú!owѵso1Dŷ %6P]6%e/V~ީWzs,cr%^ jB8\ ڹc'aM>\!zsY]i}͛$}9e!vO;QPpp;U/.F~DP9=WBQ\Z*z5!i^`):F 5B* ^y~}PXj" 'ɓ$$$xbnK%$pݾ:n7Xֳvl6[寝NY]h_€xwr,oӂ;vi/oQ_{'3/|xrm"i'6mXON]xyd&=_`ҏ5vI#Z_㏎㻕8-X {p/=}oGW3ƿtuPy?J=~[ES :4$CQWU`x˛nٲR;cnJJJJqcM{r 篿ZaǏƝ95Uƌ ;MdZΝ;9ydyn n?kÇٿYk,))5&%%#T9p3;q5uW_vU; $իW@rr2qqq`٨YXbb"D͚59ujբ,j׮d"**wĉǣiIIIgffv;~8FNAN$Mx^a(<n71_S9pb !+N}SF $IϏ JII .]vZkAlh4 55 Eq;}_khG'rYL'OEg4x ["ҏ>cOkʨøcE3woIL@YUa(9B~Cy׷~rW籲c6>{ڶHF!|l'aab7l+/=OxEL>I8u* MuLJr2BSrkgr|Mz~ٙ<>j5ΚC#I[o>Z!kK bѱܓMqèo G[Μ@z!T 3$r5TW߾ 70zA`d-jE X?¢/#3)ST9ݻ={6;w2w\n76l`ʚ5kx뭷B?h"n7˗/?`|g'7|xZ vZN'o67nr1og&==tf͚+={ll6g8֮]˂ Xj~!xb>-Z,ZիWi.ׯv`6oތdΜ9l۶ kƁc=z\fϞɓ'd֬YʬY)ժU`̙HH z:_Z& ݆Ҫz]Z^۝ i>`H>:Wo#,Qɣ-g6=Pl|71M"5WO, xBV_*6HD~9r$͛7nː$ EQtX,rssY|9/֭T6?֑#Yv# %&Ri鹜8v={2 !իW'//={*5jԠQFdggO6mZ~)6SZX&P PSGҾؽyaR4J5rC:I3G6x<6mTh4]w~̩L{nZ\׉瞛ur~m7C/Jӧ3vXzdB$n7Gaڵt֭<#UGPo,FHL0aDՉZh'X")s1%R`ij܂f>IƆ:=zGZ^Y/UM+V5>$ItW-b''OV+Q\;(ƌC/ odhvk'Z7!Xu*h7w}:uTk5 >_E])WZJezo߾}R/q!=Ni.Єle:E^/#!ykMxP5j*P;U`2ZX\v'9B$S=S^`WVfвSc ڷ;һ%`C/O2117ҤIZhAٳg;vnCMjjY($4Ľl}<;$8 :$I5N8]SH 2_Ap˰8= .GR.5f~0LF~2.֯]J0$&Ur*7,k0k~Yl0zh.zG"$iXV_wr7nCϚ5W_}BIUU[H%~X^QXع'e_AMtz<(h2rjh.:EC%: =v{{cAAA,[RtIxF̜b;`(+ӲeeW#m;?n:_,wetޝ9 ?c/,L. t ?dxN?8v8+Mu\sҕ V#7'zUԮGAAS|04mz=>/-f܄/fݔ2%Jj-[r뭕;iFڵkߧ)kZ믿7F}F[d?8uH'p ){j"@\lK?MZ4Gop]#;c"/=DvDEQ~=$IV횸]n#4+1;/+-7o#Ve|a/Z{[WSog&ՂXX֬ xZ^ ~<-xiasgM4~GΚ_{^z3ZD93#d!:Ÿ:/vLSȲdNy!elo^ӅGha-Qa%Up# ÅQM$*Kܼ8c%*xYÉP=DGGsqO=X!)))DDDTy{ΨG}%~KM㸺L>(5Mj3 DR6#ф4YAtH:BCCQ=.T!ԨKRf Ab4M?=n %ynw $ S#G% ^!<!""lw`خ W]v|dnKIVb'u7NB3Y9E@hɢ,P ^ϙ&^pYc;! W)6FP MFxx86nܘ$-[ƀhx2?8 @g*wC`n76sf#")z5iC=bFqw1Όm8{28 ?t1w#! 2<8h`ui!:Աobb:?y[[5\WD)$qR,!_aU $Ish4ҪU+_*v-#W,K G3o6sOU<\tQX\Ù3g2j͔rJ y*xΌmxת\Dؖ&wrmN>Iz.2&>4տY9u*z) nДrRtt4Lh_gLaƍtر A',&tŞYDF@tDF@1&9A0~Zǖ-U[|[ $tyǷaEPh'ף? /Ǿ#tЅ{=Jaa/\{4 E^>*csx vS0V+,/3 {G-okX]{0ij9 )* ЛlpxE -|Kr]wѮ=k~߭~]/!ltH0WFpU뮻 g Fj0ULyh‰hE<)pM9qY/h&gq83ojpǰGyAMK>tz7'0@&Mè)_/6dege'Lfu#t(Ki'b!2,Ի#wq{hӶ+uж]T4t;\~Oʕ+ׯBW.מȣVJSO(-(MׯǏǨ,Z:sT]H} D~I+lC^|sNq8۷/*4m{ Gض mƓYDtt zUGxZjʣ"j7#)d ஧f_=1]kp8ܸ=Fݷf4-[Uzpx9"e^XATQf#?4y{?̫ia:4M(z>X&qq5o2xKl$,Ĉw9HV B(Lը]EQH9A1Qd>?N8qA {7ƣAo8I-q:4U]2 %@D0'+_FCI;1變6oNУ۩]1m۶go~~^qSCh;`b,\{h r4`*+*.A*,uf} $IB% MQa{'';FMgd N{5fҨq'HPm;Wl[8Rػ[m ) %mBq]N>-b'rMZnI!445璖MQ{C6ik5.Eaʜe**"Bjt~WegqmEÖsӡ<2y][i G[U{ o@WUnYl:ر|L: KrmT݅)*w눌$;ePU\Iۥy|a= TX}Qs =oQ؆_VaGX"a(~KӼv*͚wtVMg@/UM㫜ԺoΌ=x Cc=|q5߅~ 4@ 7.Хٶ[ҋryrY\q t' F{ӈV0}tN:J-^~|}cr:es} (&6馛*J*lbƒk\Hr/46!!(Y~Z$AL|kn9VS&F<<Eh nb)&7NgEWx/7fdžXG.h#uG_(֋OY?LA7(Z^юݺ.PTͭW@@/[KA ;(]Fέ:ꨉ`]i3j<l6~n7~ #"h&TtRYxk,z!a :#)CEձ@LU"&MN &to'E لD%oM'{ Lz#9Ely37No@d̊,PU `hsM_29bVFe*WWV ^knVZ)<Ϲ=.[*ʬ[ya ;WX̍eWb8p6à4zMN ,+.֢Bҏ% 7Sk(X=z4M*rzR9 `I^\7ȞstAg_Km ~&~FÅM:wWnWFsr6ւ,'6wvj ciV)f+Y(]^r^MP0d`V423. |<$ !KH@ېdPJzm ^NgE\BGD%4˹2 cŦnFhP 6G1.moSTˁ_ӽgV͵c6r;* Bz3mz<~e1GNm}'}ARNQF2{>]XySkĉW>~V ĆSv~2}3‚bך;8PNT CNU8""'OWh'as f;PNց|%ƯU>]ᶾعj%9c?-Z {Fث5=:Sl83⊉ zg\[9G& $ т@wLoDH`4N%XON~YyɒF|:6;pOo+Ƭ6+m|ّ$d'ƲewFQ=a9Q@ Uf(Vifʧ{S̀ U@tk֬!77B(.,EQ [ΕtՂM&fdef80Cs]И9 8g+EnHҶ34DNy;9:ӺGٝ^Oj-Lè77hP',0a2RNUծC~N.< E'3fzXzjV (᧟pǰ>Pn]L&s{)l,Z1.[J=uۭtmӖ\|7.9pe-t5Ҷ+0+f%Y<:}JplօR?S iO .Sf[ 3+2=cXlj~Z 55A8jQa}ڶfa}qv67o&99*_U< -)&[zdZXa=M_Îy/&B*o]ie7Xфȧs˛&#+p[mY R{$ w*3tZa>0q|czADD/f߾}^,ӴiSnzATTTMJ !xww=hhKXp4> =zù .BDAppijn8NBBB(--LII s%I.3}F|UUiٸ 0`Laa!7]~[n8BBTKJJ vatk?ۿ6w844FaAϿ2VEpp0͚5cÆ 4 חO>}>|8t:, 999UP%p=3t3IKh/[ZY{|tT[Z>.G_*l Cn`X:˟ïceRlb ),& I|[פiGq!qk˅}*ѫ|:$xGT ag_;Ww_y5kvֹ?Sfǎ>Sv~m#0pMBGJ `)˹בqeVޡs1{{VX7m 9o]߃cHьV zԔWX%'B31c\obmB/gǎˍ7zSkZjEn%:}JAyg 4|+f⿅JQ˲.\.yyyVm۶47.X!CR)Hx.‚NWЏg۱uF\@(ei*"~Ϥ#G\ .ASpj>>Œ]p n%+7MFS˅Hn;,Qv?Н;wV)6\M ho8x<(B^^iӦMHHUx`ׇ(ēV_Yu{Tgu-gΩ: x! 銂~W¦ju*i h*8\[,$ȥ:H}MhMsbWݝR)l,oڙf6 [T8Ù4iZbĈztޝ~sfdY7ѣ|_J!ř(‚/`<~G$fl`{M~|W8ַK$'{Ap9tŁ ۷^Qp#TMӄ E`v92T PpHx<. G@q>o MMQUVÎ}v'-B&حEN:u̙33gv^x8NǙY M^ܙ[(K9IhZxd,lV+!Z.=wJKcFUcDW$3+aCp8Aωս{k׮<䓌[3;C-䷝ćc<]) g;S7y۰1GY+WҫW/~Gz-W nݺ̝;[n˗nȑ#,Z肯%#G˧0~^})ꄐ* B^|tCƵ$hlٷUj3 ^QV '?#` .EHv$IO(= c=?̘>?WkoJw>'V'%6B? nGy[o'OV87bdY&5 46QҢyg$_~%;wO?=>Z\\Lll,:t`ذa{QPPÇiР_OAq&L Oۘs FбgM2fqftj9m:̙3H :&?4?>o`a2y|L;Fl\MkO).)'f7BHt8??hJL'J]q6^II'm#LgƍcB,6m3 !駟|kٿ?z!-[d׮]u=;>[zW~u(lshT7"M>ëBk~ SO݇Gr2{ť]v^EҚ5:zӸi B/Bk֬Ye+uL9VZO?ͳ>СC2e ӧOg!߿?2p@ fO!TU0VgOdD07f:Ӵ][nħ/?{I6jӦMcQZZĉ1 _'t:4i:?p&M_RR8q"6I&SO=ٳ 0>))d&NHii)&M"44s2uT4iv'?*'Nb0w\7nۙ3g5j`$$$0|5kƒ%KxgDDD0g cΜ91oIZym\]P _/eyb U!vQnn0CFfX:i7=ǭݺ Ӝ$IShȑ5~39NZt,n4֝ېZoAA5@ 4MuSdx4Y'!i*CxWѹ%"Vy֮NP EQ2h$rz潇MHKMt#ieeqC϶о_5R#&n_:7yx<ϕtT~?5b \^Cl08TAQd_ΈQYj5aߒP#am+~SEh%xXx1>,%%%t!ߨҪe iԊM1@"O F Kg̈́[[/pٶ< F&T<IR@܏S5xpg';;/L=yg(~if[׬]ШagU¬,E.$6#GiUYjAoh:jӦ "MJlJY:,m!Ҧ1;VgԉnGDFB/ɸn4VOO>xCB#0&~ݺrUt9ˑ^Minшnh4}˳}5MCނ>#FrJ<(Os8N, NrY4.<X(+T" ŨGx:X 6͎9 :TI*8`۩^.f &&*j;_A͛7p)SȂ 0Ȳ$Iԯ_͛ӦM ZlI˖-^:v`A:u*{Xe/+re1\:4UƮ AҡxlN>:*2}:Ǐ]v$&&v>[ 򩧞pзo߫edž"4ZPCTtrx4?Uaw(vvqZmK=h6`0 6,@weO!EQ(`[nHĜ9s9r$}q#8 yiNaÆqqZɲd:& ~d椢E "YV31xJ  1`6b /DCR-$*'5WƖyfΜ;t%mWw?SeEǎϙwuJ0=+~pXר^!А YE/cЛe:c_Y=$~ռڼ{%E !&PmڴaҥdFeJJJ]6.q"(NQUQT&qE4<(Pt V4ʼnRTz.O +}:|RRRlL: SCBBe*Sm6T*eצv[E= Iξ@ө "Iw=nJS\=t[8T#/xݤ#5u/)g_G,SZZN#++9x 2` ""*E??? +v]vSQ% ^Nh^/Ȋҳۊ.tf ɨ :!AS?O>ÃZ{%DY}M[, 06mh4:Tf($!35 &R"B5BÂ:hx&H]Bdō& dIaqFjЖ:N# I| n\MxWطo3f[n_DUU w}wU@U/ % _`$X"0$:*ͅ@BeBB5ƂwMbc6| EyԩG/8qW5((ڵkӶm[5jԩSyW1c&5kpmQZw˹l޼EIrr2cƌa…|Hc/̫w`*_[(Y4t/ŧ]SL8R_ ÃNQPNEQvٶm;!ؾ 6lq磪*ڵ]v=vXV_G ? 4Mf͚(rVB9v?`,Y?OXX%)g M/CBg6r $d "Bs~ө9SP VeHEOh.$I9q,pj J$^>#n!%-AfA|l :jt6gʪ+VfѵkW>Sx x<\8^u7$e˖1p@"""4c^Ui`vCp *{q IIyonQ,0e'9{nAӜH葄 In$w ,dWEZR&~gs|q?ytڍ˗r$Ƒ!<-L6ӻ{ǧ>.?[nW^ۂx̙L<Ixꩧxβ!O9rdsԨQ$#|5ɽ+Vo(+eMlf IXxuaj:wٴv z3~АP,G D\&&&ArcJW3 /T8v}&2UU+-vXXYYYgG/3z̾][액_x6#<_}+{]=SK^Wˆ;Бcf^Yu.~3j=Nt*^ԶaēϾ*^}ѾypJ*!~\*BC})^}qnz4QKZfbwŽ: @zs(QӷO}%1pqja1O K#Gy]wxT?lM6Bґ"(b;VlQQ+ bCޥ$$7e۽w?6Y? yx޽wfwΜ9{B8;R(ĥ%ax@i>Cwɲrr~ZOڬERG~{!r2ďS^*/k*f; w>/+CX餞U+,_yu? +:$Z/w 躠KX:vFIiwMG؛j̕qz#IL~qrݷPYyݹq|6]la П]s32WjO$^܃UŴjژs/؟%Mq$'G5۸<,_:ݛJܿr8>-qK^$I&5@$x?è @NF&H?oX#wCu$:͢/'Y{"Uy߭xHĪ+$ק#I2{w♗D$maksa ~fRGy`}~9-Bb)IgT$I܉$I\ү-?.^/䞻A$ L{C$IW>J>&OzP.߈$I<9n|u>h6A.\q%qU!q(5IrЊy/9h'!wtpGJS.BCw?tҿE#Dd[ibl~pV^iPjdsK99'5rZ̢ xؚAU]Pc>#"'7b#*:cᓋ492Tã /{\vUA:ɂ,:4b$`= &u%W=UNiy)f5;"ךd5 Ֆah9h [[txpgcHnjr (.xW]5ݺa6|yhÐ詸>g{aoVeJsaY{+6v 2hxAd {4!9oP& CCgbR̠NBb]x UQ ,<;0‹?zʝNC<Yp{ TIb6v3nNxX9/Yf=ztn `(BBVBz<jbUe016Cg0|̥/$oi^kO$YR/`# wUof> WA6IkN87Km<-k)*6ciO/Hb ]HD,dƫ /lAݦH( C`6[hѢ1wA}pX{EG 2AAyE9yLso_$fTatdb)*j &<4Ð% DqY)Cη^fŝ0G8A>xehCkK5rUUv]Q @JOEQ$fe2׋> p 9g7o̤G˅AQ%vnH>CQLYΠsgr;ڥ #Io{B^ DpP0ފr@A[7RSH+yDFCb.B>8ׇI$7jLaa!b' %_X~MlVZðG6e) ^tݴxu:6ew?22%g0Ț~deeѥKcVZE6m䆧"χFe\.V5:!!!WykАYe(y Dpk?aXÙj@oMJ7Ҳe3Ófժ՘dr^ 6n\Eaa!%%l gk:t>b(.)u8fGPAii>EŔiӲŕ>nJ-pCYr@ǣpS~'HAUT^.ZC]xa+j Xvt@T0I2T $:le/Ϡ],NJpDYG(,ټy1'NN:ٳ*,YVz(|0 \.6zロ8:u*?<;wwߥUV2Yȱ=cznbI..-rFS$5\Ŝ9sݻ'VE%2ԊJ%eح&3CJ5CY$88 b&>!IqUVѺMk*+* FQ PTT%<6H]EZALĸ#66UU}={p7b2CyY9uc+dz9'gW<^4Tń0b7e,& . Q"j +qDб҇5GTePǃdڻ|pϝ\ ^-k׮EAzॗ^[oe}B0x`,YR=111mV$ә:uj+22EQXx1ƍ#77/[`ppn[sޔT vvyOb0 xɧٹc;d̘nzzߕ6oٌݬ`#IЪU+MPP?q8;`|=]W$C,AaA1mwjQ/>>O1iPRRB\l^Ç #88DJ*(On%~rNg^rӧϹp6@A$IT<=f*4ҍQ݂ V`W1Wɵ4HLF?#f""DR1y2QqVҲ|8~K9ݼٟQ{̙4k֌{RPP@YYsG0 491`$t/[4DRSSyt؉R t-$ d!C LэfS3J,&""cqجs$'jŔ?DpMҤi2 |If4,f3N2BB0I23p@6o=~ITx(aJnn>J d>.]B||<UœPV*4x|n rيWi{ bАQgdlRn:23ի׹p1 Uk71p=. &7;ã+s*1\>r>O+?gVHB/̴͒^džl4W/Q2iBI:f>W{04YZFhhJ*++EM04QQM*#^_Xb&,,Rl6;,,w@z:bza 1A I0rKs7{8c~A%i&&xWLvE BUDG4kއ0~0ޝ=kVs:\L#`EP̊ atMB r c=v76Z[jrFIi!& 3"4 ѳO oCv~Oa՚+\Ŷm[hݺ5&s(&E~zT;<+_6mۢsoN9/C}:<^ax}BG1p/ ZE003"!t0á`FVrLλZ=z[7o&'3oSϿ{;۶nf5K_Elܺv%%%4lԐE?矧W^z vӮm;~\-jU++G۞Arڪ3u*Mr3v,fHQמ΍Ȕgjsߢ.\# Nx&4mT󐺿R DEĠ$aV&ӣw_~rywI[ M!+pqאS@:x="5h/IP;nxM\sdBB1&r ߡOߓÇigYV\I^}%  Kntb6SQb޵xŬDBpRIkN/>FVfβƽL+l/w:Qe2Q9D9XihdY>}jY_}zqEC@ptBIHj@1I(S0g0i$RU%0ۿ9[Iqn:1O'.6܀Ѽ஻"!1ANFF6vM %7/nF~‹MMA~1\2Bʜ%8K؃H¿ٹBURRp -A"MlXw>ʶ[>`Lf$@<搞 q[`|(eYA%dEF@x3Z[e.8ظfh9ǽM{_[@a401|u%ÆW 3_]ͷ h)ݬ5#ͬ ecyy˧'/ƻu6***X].*+0  Pդ2c2P՟(N:DGGjA4TՌFVd,p8X͔"!Q~23ұZ~MFU '$+S,k$''rNIeee|DYn-.WU4taT/*au<{R"J dEGX1gg0p_NeeMn\iD7lrLȪ~z25ڵiTv'z#i,\㮽gbܸq̘<{fdV_I8*zCށZc4M^BB0 ]300t!tMn]EMah0BQBԪ"P?]zjp8'B#E fl UU1I_hZ_uwN'SuV!wr0}N}ƒB!n}E!CBq#c+tkBѽׅY@vs-?_uڵkuğ%K0`z^IHH8v귽WJ5E rZjEDDNm۶%}1ɐu u4Ulݜ׮Q 0;[nCvdfT/ݏ,rbl>Oe, J~?d0Ț5W5.SEQ0LzRM&B^/dffRRRBEEE 0[o}T}>>,3f̠Aر#:u"22oXZǴxj|N]:2{RTghynWٟ]Nt VlfHiGY6.^x?e+9TAIB/<')6ϗK }X3QZIˋ:sc61}:w_yqcph&67-.{p d>Ve8SCii|#R [o%   'Z]wS£ ۯVZ !Bvy]Q]oF1obռ# \w׋Ҍ}bG+-[k <+.`DŞ.$ޞ=_|"8pϡoU!%#=$N^si:q7!׻x^׊9}>cd ;`;'Hddd`w]|Dށ\~9 r%#i^$a p8s{ԩSN;^$>CD֭Y~=EEE)Sf6m([t)k֬aԩL֮]ː!CeYYYl۶ (۹s'ف={dݺuy֬YÚ5ke7of͚5+<֭[Gǎeڵ+Y$o^Fb͚5dffx ֬Y?(۳gk֬ᮻ ae)))>lْSXX({Yf [l -[5kkrssYv-}=a, ۶m KYf Yff@Yjj*k֬oi&bbbe{͚5;flL45kְsc4mڴ@YFFk׮eРA֭[$cǎZ֭֭#+++P6yd֬YúuӧO? p~4L^ڵk9|0$Q\\bEFd>ŢW aoD>+yyb:j,NROhx<***())FPP8l6fX 4-Ӊ餪 I "))P|>C}уol&i9F%8O~-7^̷c} W_9YW\1ppJ1!uZj%e4 U`ɒjf0]glV7k][I_AՊ"Il۱8tɇP:5Ɍy4$jk˗/'??ŋ0 |躎,ˁ.VMW f3SYYIbb"?7dŊDqѐGѸ]$¾L=zwdTtkɊ]|tnߒ9>弞Qu F]0qf9WIrr2g޽4mzlH6%EYCEF%.(/&?'}IۗBan>[:mGVѭ;|!C7? t/0ŏ_v)V{h{Í߱f$ Sʔgէg!qһwo3Q$ĺId&$4DQI& ($2|F 9FRz8Dx(|ݢRQYE~~>5‰%KoЬYs3윜x7o~k7f^~Yx ^HaAs2eF9"OlUwŞ-Ojff! yqk4=0<h'%Wn{gmf  :nD& a֋opɄXh o?gOcm#C)TJU3rVzP?Gl"4-UUjYv#*220שS7|{ W.,,\=Bj>l>H&ŌUp0q6Ȗ泏ߓ\=z(E2;ʋB?֫H$4M d8e9VFZwv%!D>Giqgʲ>/qd2h"Nut.{Mtm'lɴ<0Zuy}'zf'ӏɶ5Ҹqcصk-[dB`{CÉr1|"|?B׫+33``? רpBmA|{EtiڔvC.[Ӗ'U0h̓$1y< YAr(*ؼ̼fҰr/lS;vٙ s?nD˗Ҿ\|2^64y֟Usqw2cƌگ͛7ӳg92Ydd?atGjVˇhtGPjx5 DG<3Jj^$'Sǚr<୹G~<[W;Ɍmug###SYYIUU^Mp:Nh- AAAMxx8vh۶m7o&77,ӿpW">luC'}C~;)PqUb3&:#"񜒎. )} u"8mSnzr&>41(q-~x<\.͚4G&tUtnڈ`Jۿ= xsviNvؔZF|\8ii{5f1z[W-}>ߴט2vYЦQl9!!f|w\ztrkm8aZ$o/zNJhh(SN%<j>6ȫ(Vϣg>.Ȥ ,?ϣa b2*+ Koܸq~ڣOӡqXE%3yjVoLEaRLshf3fޛ.ÆCuxb9S}/~ ?Ŝ2rѾsE:]ºM<ޛ:  A$rylݾ,`j> 4 NC~%~onkΆ|>-[vJ@Hę$yp4 M- III : W\qE-h)hժY0絧i^W%}2#.aW#5nݺܹVZ>ǔU5r&$I/P+u/W%s}Fx ұ\܏[r0$ $H3SL4Su'S֩U^~*×_~èQTQZj7c.*ʫ غ0}Dkƒ *n]9&&ٳgΝK~~>۷N:DFFnxFx^*++)((tVZĉvrr2?ѣGs!.]Jyy9cƌ!99 ,x<rssYt)YYY4mڔMCgY|9m۶%8xퟍ&PiUȲC'p.XAlL8m'P&VEUQCL&1TjFe$IF5 TUFL&H UEQdIF5XSRUdc*q9=DHR{|6&GQ[Ioqqgw7|X &MZ2td p/:W#ė@7_?(TP(@7q}QTTAnn!qq޽{2{lRSSuVzرcYf Æ O>r( nֵ< <6mL6?u0bg_شaa$RO*HB ߿ѿXUkdžЄg 's] 3W^9GWIaȊW_Oir5t:ՃÈoGӹ~8.-\=}}ȷo~\xiw<//n|y &K~~>wy'˖-f&''\4M#<c|pٲ {XPPNuZJU?3Yiݺ5;wCq+cӶM+*+oMxiޱ۷o彐B^^1.f;v{<߿?]v%,,={디0sL"m@&0ѼU#ڴlo _~ -bA~h,wV ) ?6@IKJѧ'Y1!yePWOѪM[֭]LpP>aL&ʫ*Pa( OUVՅRY1tz/fc@ @Q IR qXMŋOct:O~5$\˧ Ћ) 2)-}n`3g|ǞbCN'x;JpP@r]T RA"&ր>p!Nl~p8<-LuFIaaa|asDwRmEi!#xʋ'ط?7$$Z[~}ڵkǪUhРA}߭%KXl*+$Cbzov˞]{NHCIG\$ә_N$NVF&eǧ $UD$6[Ea^$IAAu0 tÇ_[]V3nFVu))@tl I(0P$~BTۓ5Ϋg t7j,˼<쬢S,ڙǻoɜO`Oxv]UAXF^zwM35ܜdO7`8fթkL+Gg"8ZK;+|b]%~o" Ґ۟G4h1B h R&&&yVlV0xB11ص5JYz3f{ ˩t 7"5=dǐu@$+~RC@QM" dIA7t+0xF XCH#^vkEƍ(/BF1tIA""#(*O|,zLj=%$&&&,uRrepiЭ˯û7p{l5[^٤(jD+# d21k,7naԩSxvdCFF&iiil687̔)S 6o\$== 6wʍ!A6]),͡{n8p 6 `2bhнM_Vn^n*/ZfȺCI5eTth̎-)@HR`Y$`\3v6GUZ TE$* @ЄNj0t*6ń˥"YQ1>u0+qN(bAH|UHY4҅k,]M.l0SL95!#F8-H~:w|{ѣz-[P^|%~|:NMvNNU֬YCbb"} oaÆtI$&&qF-ZD=ZǸ5nܘkϻ8#%9*i',A*[#Lv1cx;`ڬ~s<6JbCv`-`766nן^U5s>;]S믿N-N0iҤ75dV4-@s0 6nȭJAA^zT(cgG_0bh[@GQ,+UcV!/B7Ѵ} ~7W\BqNUyqiC$/_Nݺuy`ңD*>E[1z VfZם"{%/_Nttt`k='==ןU2aMTUg`ҤW1۬xVJIHKM!%% s#,NU[.>ߩ 5j]v-"##㌵SOBB| >,'yp#[J\\cʗ,E)=p!!XƩ[ `v1a^Y3^X@מ|/z2孯f$YYj)'ʲ̊+`С\s_*--e 0nBlA!΢B2֭[D;d*=ǎᨨ(Z8M4!!!۷v*`ҤWe Ggbƅek]Qxq hӏ;"Vx"|tlJyaDea/HxɟtJ!CPYYyZs:۸\.-[Fߚ,:uSNQ^=f͚_Oy'[F\\qL2#j\uC1h  mdеE>ݨӁ8ر#_)f„ `ҥr7iĞؗ,ӫ`YAn(->6]l>&8(33W_}F66Sl'|p'>]%*wd jFQL!x<0bGXX0^Ã膎QuO?MӦM>inժBbc9 M7ΦÇaML0N(df"==LnOhhhuιO^+.8+4WpENv:aN'..IIIDEEkѽ{^lSx ԝ8a6;Or!/gɳNϡ/5k`6CuH;A}>5W%44H$I"''͛73|pϔW_b"kذ19nFzwq }hP45PlkG!hLxh)s~bNTuY?С}S|i?q-U;ocU~!XM]Eh;b()uqcAth=vMB h0XQdP`EQk !#aw{U#[&i0k>y[#N D6ɘ (>B_JeJ@Q([M(--&7'LdTE 5ԓ2 d0W;wk>O5@T" àYfp $4Y>X‚yi<23݆]Gi~.{^ash իP? ??C箫ҋӲ{ bo?-޿ d콯y6X~Q^^N6m 9 8+gGC(7ҷLp{Z fdb0#!?ǠN="2 Ib\EFH`ّXUZT !=Y`lz dкL7';ZIMv|AaN2ia!nˆMP%ߟ^? E9Da::bT͋?wάz7¶Ѹ^$V/_e~÷}(S+T1zh.wB'y^"ːj v;o8BF^2 C@钠hͫ"K/T+]UUƌCaa!}%nçAvyiG.b1χ|gCXx\O^/w9b1dވ?SPPpYal$ŏi5tlͺ໷1H]ջ{ylЀ6dwވ$ɼ0-22ҹ6:ݵVCvgBQ b;t#`Rؼik7FUvW0YrTNvӸdGӧNg*xA0[}@FD't:7wӠnBNXp!K.(j;pޟg3o܍}a2C/]#0q4C.rFtY穧FVDRn२O'. [7 2ͻ cf3.힌i[cV|&]is\>!7 ^4izv67 YqI/~&ϴ\dn$d=ZԩÕW^ɇ~xkɉ/0A|Y`1[}? C`Y2 nݚ IP^^a(Lby4iVFfE㣴<M+R3!2^QI~joSlذIQ6UQjctAEUjC7\DGQVV!b1W$JJ)]^C٬T<<^ ɯ8h>C&b !STTDW?Hxn$I0$ɄYAI<W2wܿd/&xzeYg pFtmIe`Sb<U^M~~>;wdذaK3?$8ErFԲ2&+E|>CϞ=W^a'۶]3=oΝxS|ӼbzMJX>?oڐ$aԛwYZ7hЀz{vqMK+*Bx! !TYFӵKBB7< 1*V~pF2$ka+/v\`GTTad*/<˵5Q\RR=ZBHʑٮk&tMTY+4$Wa&i00IB7լ(NԱ$`G^EX8tj1b!!!!081DaʙtksZA޽)/Gr']υSIHoKJeBvA'PU3]v3`yV+!ߝFIBBBXre-~?Pi0K;aֿGɨN:̞= .8ⵒ¾}hҤ))dڌXT+.|åc.}@V!2231uvTrF@AAH`ڷo/"*f !%f,B|$s0[G2%5كlr88NQm>'嫯:kB7nGe)Y⤈Ϫm_"}E?Y|2F1|u?~*o=ϱ\ AH&\qPU 6;TUfAiX,- NX=s/ڛ8E3(*-чj匾ʓoLL ^SNv[/!+wuh8w !{Æ OH`3 [C SXbY`- ZS=O“0Oρ9'I2ίIa1~!' -EcLF|I*#I񤦕Ҿ}lVr399?̘fΜIOnW0m4IڬEV KYw Z6yD্<#'a2S $dgk,^AdAYAP#|.(. 6h W|x=偧i6#h?<>2V=ph|nx_})#ʪq̹}"j'{=YFSTwNw#Ebrm:,)t֭Vٺuiq5O޳ 0lذ?d'jf~v tV/=> 1M,LY)+shc&3|/gܸthcyd 7f֊=7c I`[E|<}OLH-f=.dspoRO#…,G2pM&vd95 2},w t I2FIϷUKE,h l6Hh swFm<䈓W5ڷ{.tab9<>G ?(U'o4i҄DvY"i ̒?PB&$$  CȲ7<ş$jl$+,פqDK@H)呪i$TBn{?TīzJAbbsiLΊ[UtosiipYZ9TYbj4 |K,} [ VL7Rj o&F`'1p`ۀA݉O;k߸_ȂW_b]w (q LnCX V}5+]d4nEE]tDEYx0%),=Lsp-MݵG>wB,$O#؎T8^|R9d>z3mڴ=- Oey kիǯzg!ZUHʃ=.΃EHGOYFdH~>EWP-~Tljbɟ::n`13d`$5f>H5MBPYDfƍ-7P<|7FڔR*4l '(C{;{;d^%3*FMC!JGGϔ 22dV+g2D|ڙ+]tl~6+OF}Z8?ȑWwqiXbpd ?t3[2_E(Ip:{s~QxOɲDDh30V}";vm]t-G{H j;vlUUUԭ[VفXnt^]/ Bk O bXoKE֐%!H5 GuV$M3Hs#a`Q iЫ# 0BG,!l%!3't=ٲ=D檻X)\bYq) gj8훘62E/7"BJ4jo!lt/.Eѽ0 eذa'45L>^ 'žӳF _U|%>* <8"Py!^fGן& ^K5TG.}%CVkΧgvX=xB|S^gkB'!1~7P`Jr;).˭Ž*mO;tAj&FNRx~YZZzLDM9p4*M^%%Py]U,x>fo5$4CC&n^/aa躎d0 ***0 dBUMmV,&3sf/ m^&MrBCC0 2:Cm??jC ! 3>$&o EN87ou]_xU-"ٟ3x2id擅lafg`J7xe^z3[V W3%H~G7;9f}0k!>y)Lㆋ'0 l 3}Sv`EV ,wxg6>&=tmw9q5¯A=Ĩt!vFTx"hWonm.& ]kr'n#n&d(A}z{}ʠ Pn]f̘#eͽOZ!8ǁ$GE4D":r"ð98xuESDRF5 aۃAQTdYӐ% ل룪 %nA5d2!K2r=JGz@VMSy\(BgʕI.[IJJ:Lÿo;6lk&W~JWc䴧 :,9? qh 3+RMVt!)-WG|:yGq; cյ( CCC9s&SN%""?4^WSuH2B0iJQrrvF"}F-w0IQM[*(+e|;w1AWVe]͋QVѼ.*Kv%<>5nāL!eo Un}v*$I2)ly5vʕ 6vON _2A()@$IK..|N Ɋ&,K(ŽM wCed$Px!LBD'"!HM$,,Ch8 "Kw,1gCZl35tMtŧy(-/`@eDƱ+m5vHld">KCݐJ󸚭Osg\>JG5d9pxVK:#JQYqO )&+.OYY$I|bzJIyީ-^>7Nwgӌ-g*q&k94Q\==≬GԺQ K0N'!ΐbbb"ӧOKRRRؾ}2x=(m1 <" ~"W5PA6x}>̪F`bF|膁d( :(J{T n004 լb:Ɋe'šz' z̘1/ԵkמW$ITUUyf%<>%^70`:[d&=gUn'Y ѨniCTx]*@Ds`VݹҊ<]޴lx>!堟 I, ^\6GU];wᎸ\y]D=;>ޛEҪABdHr Μ _hjUWB[J[_q?IeOY]eee ϫv\ :mC[j?gn/o4$I|̘1㌚Τ]]N=V5[ֺs@d-J2-<@~q^cѽEG5aDBx}.Rm@qRG봦ab[0Cda5{K}Npm>ϭdB3;?XˉPd2+tNه6&]tK "\n5i.:Śٔ#6jAU̔U̪9?NàOYE(&j0P<1ea-HRtOd}\-ې۩rQ+8vt,ԫWVف 6,zV4Mcٲeǐ?{J:-@1nhdbd{hָ5lβt@|TC:H3748i{OPXrOWڔR6 ʼp?7`]r\:ɿ[ ʔ tCU *uꆪ"q";:䏩! bp.bC0N0nB%-40Aٟ[T9qq`7|p8@FF֫WiӦʾrdY,{˞e2xWN;|E$&&WӽeOl[}2 Rhؑ"(  7+ޤ:i>5"c{<&'⥗^BADDkp8GUU|>J|>;B:uTޥ 64S3cHC$i?N:Y{RSSOɬ!I?C We?mx9%AyEU1cRU`, dYaw/cus(*`bW C@͢Kc%@3h_/ </ށh?LjZ5\EΜ3w3e=;\JYGCtj>r?Ftt4w}7< d Z؟bPbD5 8 Lv 2^ dLb2d bJd( (̇HF~,̝;tƎO<Ç1-q'jrZv~|qrgS pm66lXijsNZhfaÆ̛7£ !n^m&v銦il ғ/Iܪbޚ"r؞rRm؄O'+&R緌Ln޼~rΡI$xU9b C'0DI|dCÓ[(N6ġTUi+i' IN$6HDLx1Ռ`!vzA ;+yoLE6g{=msd\FFD0x@Θ1c(**Be.]Hac\, ΤIr X|-*+b,Ux<nn'%% HF.]ұ[5Ν zvw0_|ݺdKy{%^xEVoX#&O=DpD$n:zxOR:m_RvQ 醎QdБetF ,f OQ3->x(B1GȌBiMk]רWVKeu14t'5k >z|hw^ƒ  H~ jwqDy=h׮#Gl6#}!hݱ; 2x+l߾O>iӦz4c4ڙ35%tgC17QUՆUEHh8&=wL`Ϯ_ջwf,Z=2/ Ped_eTjfP755W_}KqmYv0jơ=x%W=VEnQ{-,0\О7lC%..͛a*++^zK=֮%((Ν;oJJ?[ UVa"&?Tǟa|>w.̛)adg3d<~zvڅ`݁-[uVl6Z}x^6mĎ;Σu֔qFrrrhѢ6m"%%zArr2yyy\|"""8IHH +++WRPP@rr2=z ""V[4iB׮] !%%SRRBV8 b˖-ٳ|^}ՀZk׮UVJ}p 7,+ 3OR𤇘f9!4OCVտzWj={d$''c =jh5QK7aq;9ApyOn oU-fΌIxH y&ke)*;Bz4ӣG>䓿b&MHHH`'vyN㭙Ъ]{o2pD_f~C]dإ"i ի;v?AaG&|t-? rg(JLxR Kd٦9o:U.K_ǚb:(83+Jd],k-/Y~_ '3GeL&A`TJpP]0NxMyi !Q@A}$w}7cǎ/C(Bf/+:u*SNeرe:aΏOb6,`I;dHCضit}ܢ4NUҥ ;gZqF8[ڵkW\q]t h5O?I&q53q3bBܖNkX~G=vӣ%A,pn50NTm_pgLf뾟iq+BVVA朜QFȺ7hЀKc.\Ȗ-[1c~;O?4M7 /pJQfhҤ .'|Çc/j$!=nyԭ?`+Ǥoԉ5ۿbzútx}?1 Z6AA!c`tTŌlOϑFQ~!{@2$^@0t00n JK)r& lBPT0J j@ K=,CxddҤaS4E||TuL!!I! l>}2& .wthհ|rr 8[\a0Qwaر<!:u*|~!4B:bxݱy^eʫ VȆԋoy_Mӈ/?>gWj~0$! $! ఫC󃠪RYᦨ0=_CVC+*06d!Ӣ0  º8-(@A&L O~~΄?{.WC||I7 +(L/F^lEQM:Y#.zIA5)+rsYžԉ0t"Be+hӨ7Ee4K֘~7&66z[Q܅yQmv,NrU[ I#&&Yu XL ܟe+Vy8G gN\CXH(}&2&d {o}.j!p\hz9 uZPZZͪ +χ*@5yN|k4I믿~LJ%33-[F# 04C%i0G"W@ҤeGd*:uI١]Tx u?'oe~ͳ8g3MweO?ߴ`1[-SLaʔ)L>3fPXX<ò ^, ֭W^6:..)S0w\γ> t\ǙmȒ̓|FYկDLEU˯cןPR^K& !@Q@Sd!AݟlOez棏#?ڱ'-NBA&~MCѱhܕnc),#16N;{wCˎD[i§+} WaX,RSSeOǞ^{<c[^!pCVNqW+0vXBI$\~Z׏x̙wMƍygҥK-$)`⡇e˖,Xo +zZDѵ2m|Wc:$qheIM9ah|QAfpm)*02B:T49?~UQs.(,=Tk8'&&&k~ h…f#$(aEf>?CA^!cVMHHFa1Uw܂Sn}oӌ++>l< #3#Ea5 $t`ێWc^B"BC,q Eq<7N5L_ye:k7N$!ӬܶΝK6mhܸ1aPXXȤI駟HLLUVTUUڵkJT}>_x Baa!7|s[o0ի1a@wLcXocu‡s?n~U:THAAn^^oB>tC'&*HpWUBg =)/` W}vFr>nL%YT G݊$&ֿ!`ҥ¿E?|РA"܎S_z\z饁ƍ㪫b„ |lڴ7x-yĈݻ{dɒ9ʲLvN]hɩsI8oIY%4unbXd 瑧QݨrK3GG Bg 1j7л$։!kzH8+J)̚\ŀWyKiެ^#)qm  B6k؃#?gc:E+m:gFe^{yDRZJppp,QpNҰaCzݻiHȦ 6\.63O=|˺u3np+|4]EEҔ_HaA! 3]dʓӤEaÂi{hz3SrbxЌRT%?y}O?f3cƌD>)UUkF]xN]ʺuhժ9୑z.;r~ԍ< { ٓ;pٕPXMv6l fϾ|b{qF_> +a 0jM|\+/ǟVc&;+c1ttԎQ/ogْEDG$0zHR}Cŋ@Q=*RKcΝgY5^rHFii)vS))) PK~$''gϞl6}wԫ$4FMdy.-VmD߾=/h$ s غc Om3\8+ [CIĆקnEp4nDnA!p.>ͬy Ӗ1Ǚ74D,aʳs^GFNbj{]C^8`)I$)p8u4ԄЪ892L>k>KZym:3蚺uR~}mۆ:::Ѐ&xlϟ_K);wYwy*'--ZB!_9ظqx ӱ '`zA6+WMfL6fwqXO^L|A\#.$69g>M{ )sFnaV.=ޱE!8FA~1·-aMvF]y=&Bv##=ꆨ ;ktB8 e-"^J>T"+TXE‭bT- PTy @d8ϴ$G[|'Oδ߽߽]ZE *aѺ ذCi/mڴ!qW.+W`ժU}:[lIO\p3fH0 ME]}f-4|^8t^%K3:u*;v '' ;Td-\Ϟ=;zqmܸ3f8YfzjDwyѣGL&ݻ7<@sݿ\/[ 0fܸq-… y[of޽n:-~i#G׿u>͝;7ap|֭[wݢO3gdڵا={C}4 7 Ț5g$܅t܎i*mT9.8"r%#C^;~9Jb{{%O8ǵM?i8ec;.{눘&3۹TպZYdPt0i!ǣѯo"8~y̠S Nnsk_~Gu+G͎($F=NTu^6\(L$١\,¡cI&c8MVD([Αrȥ(Pd"pY"EvTGqm6sPgٵOgc0t&*.e'1rÍ$ 3t>lzw ۵%G'ddq18$ɤcM Юm[F6ޙ;lYqK+y莩<3)r .:?Ί6f%Nj]z=DqzQو k@t(#a%,O6H$-5th׎J~?km@qQ]&4.8HsN bb`%0| V<"(ūcYh.(> k8fwbp2 Y>T4\ӝd$֛oo`a ~{^8\^m*,o%K󢸠.* |jsa?9g5[FЯ)b'yfuT5tkdEXS1p/~1V20MTUM˲{4-]- Ĉ6F*#jTT&udggi0\@,DMکr9qS1YG#LS\|+'F=ۋ}'ٳ>}zftgF>}if-gw}wg;d> 9\UFgO #ux~L4m](C#EL //n8(:(i "*)!ZA!^Q@TUQ\1qT[ :&b6*?uxíplG%OF^e9v$gnξR$Zu`&rs[B̟7NUc=C9:7o0vp}Av1{ ;\R'{E_@;z5N?ƭʔ)S2;+a۶mf7ÉR}>cǎgŶL\l_5N(;*Y9($M8و((혨J!"MU S0k-Xpu .^DGOAm[SEQU4EvFR .gu]TMmJWpEaÆi ضmiһwmŵEcڵ~JJJػw/ws̡h4J0ޏǸ&qd+ GZFnn.rq`>TEDZPux(X0?W_J_}9LrH=8(iCǶPupy,].YYTGj((hMv؎M^n{.֮[Y?$L<;289lm&Lku֔RQQ >|8ov clFU1o;w$ONNZbРAqFҤ8N폅뺨ȑ#;D"ĉoW\A^NƏp:A\ofܠ}D«DϣFljx-j++UxmzlU! YY1 j|_U@AR׃miex3i*VDTW5PY}"PUq}7C?4O7oK/ħ~aٓ={`&?<o~صkW:7jSR2{޽;avmуO?P(D$aʔ)y$I*۷o7?& O" L2~xv횹4n|QRKnBD*+PIԩ8 y)--Fq D4)My >*DGp, FNXIUAQUtc96*n#q UQj9㒗 ԩ?D~-(9ZN\& ~Xp]=z "/m5i;wdՌ=̙3yꩧ8p 7o& CÎd!cmGII ^z)5{EQUUŬYڵ+6l`v:t.}] J ˲ ƓqI_+?{E=h4Lt' NRY_Ũ%8&x R׀-ItWK341M[  uL!?X P5-芋zH*IDV,3ö`Y)@C]uH%Hx3xcͯӯT;vH^^EEE8pP(uLNj%~z?8p!//|/_c=Q| }xuZaq6m Er~ĉ>ޞV?&fM*ftP|<-aˍX1t˲<ضlqpm-;`7:7|dPl>H&R=uo?ɢ'770 3zhD׻rJ8`˲|ر%|,)eYϏnbg\ ͳ tݠh9P*jn@aI$UKZ3G)_i5(i'>%ǣ4uMOY"`hH"(ƉPxu1@K|و+ߑ! ~ěL&S8Ce߾}DQyzMyy9cҤIswM5 qHUUɓ'Ϧ-憆' \ѐ8m{D^4‡V⵼4**^"ahtjӁKk@Qnz(*mƎ%EgEqSk".ข(*fc' ~h& "躎iWC$Irh:͑әn҆r {/xkelm1;{.{n63{c G 7Ȟc\MUS ض*8 GJѳ 8:`Mn]CU41-ݣi 4R=- 䒈RtzG:w>օ_jjx3>^/4t76[n۶f+xMLUUG}QFH$ڵӧc~@UU(oƍ#M-:(3&HSND"NAakbqji:S58ZJvl Zm.$MMw,UV+<: P| OP(1tUCBU1 PdO,EG8槬;w +9YG5CD"L>^x!-^Cmm|T,s%PPPO0lhvS`Mc߾}{qz)--e߾}0X,F"ѭ[7~iW\#҅<3h EjqMTF`\4c,-UbrtrJ +m1F􀃪x=U');H;?nbF"ҁ}Y=0=4T\J롩M ~#SRRBqq1?.]piO[!6T5Ӻ:JKKٽ{7֭3<˝wޙ'卪0k֬xsC<gҥEEE+ 8͛F4ݯum;0<R8ZF?1of>W|TEm?#xr-F ׋LE4Tn:rhEШBۚHQeH4\q^,DQTFm,si3&ģ$gw/.EUTCsd_<;g&NO۶m9t˖-;RXXH~~>B! @Ӵt8ضMCCTWWs8@YYaPXX8\s5<#L6-3OB5KrףzFA^^3gr}ˑ#Gud24FTU0tqsaeaȐ!̙30dzgsVs3w%sNU7PC _Cٖ(gmu$ %!r(B,U (x5lHbME: +˲\T #nbAWu\CA7tTMDZ]3ݚ#iAƍcio^x!k֬Ix=䷉Zf:I =^eDDŶ(K5.7%\4V(^̷0j ]5Q$,~}x$xs%ah9蚊$ 4MGl7% I*%I4o%U\nQ4Ug#@ @5,˥mo= 64m򩨪m PWWKNطΠ~YhA"mb& fXS{1<_S<38WUU ªU63gÇO? ݻMZ"|8C}}=ݻwO[||m=`*cq fW.LPہ@ `4^- xYl4OmaWuƶDchա(_ix$lS-hVAKC@m1V D'񖖖~z>,V~&>g璡CiW;cŊ~QZJŠ<6,//o޲, à]vQYYɶmcРA,^Lr5fꪫx_Ӂ883VO>̝;Ǔ;vf!7#>MwRwӭן0Mq>B0V"gFIom-))iZ>M4֯_ѣGY|9iңGߏ(DQ:wV9kƺuҥÏ=LͶ 0A?ض-""dq\uӟ;iqg蚇Hj: ""9CDgsg߁R{ٴUoWD=\BDDϗ3gKdmISJ~""rky EDD>4XDDF^sQ /ĭq]3~D@&M![$ ec񗉢']WW]"W0R15rNoSodV 5ɒֈk%Kd/o;R9; 1Xn"H[^zRWS&m^*UQ;f\z0ևDD'fJz0mQu#ՖUZ&^@\0;C~3g~M߻^G)`Y):1]lK|\yF[-={IF:"ToHvv6c%v{^s^l`*+R8Azy.(xqx}Fi0M>jTGZLj壏ƢRu-.%uF{e1{wl`=(ڏ7طK>[KO>Nm4Xĉ7p`,xwBQas 7Wlޱ9wθ .i5yڌt{ 2i;'gb,^= ʫe "[Gyꢥ<|5VGR q[] d Ue(BMQ6oܧ歿}ȩmC2~ь7 {R3"5y'e-,NO?c!F;w3&/˭@>cKw?O ϙ)Jggr~2aMRgk-#]͗wmI FȀ6՗ {RƆt/+OκSxp͢.?'.8%3 ~@umn q%A w/pqBgIg?fwF v}'ٙo:u՝hG;ypGT0 @;OpvsO9J|گqxoOq;D>_{ ˼UC^=q?CjhnmܸW5 O|ݜŗڅ䟏xFZ2 CgѢ<Y[q@Iw:|i)\3H?DBr$I#F3g745v I94{)Rn^2!I/'=v [?2R٫g:?9:7ǏHN],Qd&aaͭ/GF7$IO<,-)3'59#GF=opnVf__7O~\ؾqӏC>?6sP0t`/q&wv3yEv.j TxX0sxw\rٕmw{FWYXy3ckq?|Ka=wʇ~rw{mapccoA(:feKPw̪m)-.kY~ Q [\DoH '|w:+9TTTƶ/\lb:L;g\nQyy\}K8ˉu# EУg7+>BBmv&}: *c ;Bfv.;ugӀq2xqmAcm56r89z1nX_J]:10V.]DEUW]}[VcE~5M}M-Ǐrb} wu*;嚖smǞ/\SBd3H4z~?0o3}dNc? ۸wpƦez*\\yՕ{؋| X;j$':MaĉT{q|S9Gr._{5w?4eesLx N,XĉILHΝy}Tm^.K+IψI?|0eA;i'Gp`J8Ⱦ|!#bjKM pea q9|44PIsډdz}G!cM&C@(ȸGйG9y[Yz C>pܜlr:szQCV '+λ=)oi?r or$ K 'Gٺu3ƎobCQF׃mgp.zwLݘ~{=v;YZVdF F=osvKnt3;;CGGK?Eks4Q%-@_vxюvDr=hG;ю?54p*^ BiM`@ 'G@%SV^76ӣ;֯aHMCa"K ikB9i$˞O״x;kW"L'"/kER_GoBБ%52pjcqz20ѫt!PSJL M5KK(zӲ1BAƎʼn'!~f BNy۶iߒ^95F1СC# w&)2i3L.wVv%`2x@/LDRTTI@p7l%64m\GE%O;\mj$ aXl*]{dp ts/2lEkAs'4 $Y ߍx;v&*!z\V\O?-hPba@N!Ahr>~xΛ>lS瓏#7q\$:6S 躁jbXaAXbi5ןG$峁$)RnaqBz`QvƟ.\DRr!b,8qX,u 82bv>ezbb=j;4uō6;?& (^!3#GgĠ(;YzK? 0~u7;]Mo+z!;#`q 6g^7KflS]Un:-C,RSR t ;Wn$n>v$ 7h-Ud5ME A/i9/U܈ŢA2nu +Iɩآ 70kݺ<%p5qϽ& N$:dX]#vDZֿae1E!l6yh'޿5xvfL5?H}] eIU5BLl:VA$P+V73hP[mDPHlFNiE[^$Qa $$0f IVi2CX L&ʙ@؆jXj0|N=.0$a4vG `HLƁD4B@M]B !lB8t2 "q]eDhXcP5KDϱ~~K,cFu]'>>U\_Lj|,!YVR7'1 ?/hkxovaBO%^YˣK.466a!LS)v{ElN+A)3(ܺÇiZ8`h'~|!GEAZ1ӽp/'>Ijjj> 7_iS)z&!&&Ą/IX,q"Os#Z`Iv"ЩİN(`RP_ʚowdM[uMBZ_~ɘ1cp:/!%%%3q2Q?jXt/GKF0 oRB85>:%ʦl WJtl7 /͘#Ph;L3`&&QVVʆ5spF٘_Ϣ%|)%vygA38AyE).Nf'+UFwpĦМh+iPuNo84k,[ 4#K*>dBȊxYb5.M8r? ~$<> 9(Qh,(0@PWVMYvJMM&)wRJBĴ 8:H\.vcFaȲi(B `֬YM]]~8S1M`0H||<%%%dff YlpI'm6:u>h{:݇t䄤Ҙ杍zp;#W4vè,O;x;yݷ%&6z ^=&nnG54{ߥ;~+7ceARY^j(V̪K))݂Z-T֭DR(g"BΙݑ˄cڃ2DMJJ':&ۍE0By@6cPͮ gŦ HK`M`Q 5x m.fĵ1=ƌæMٳ',kp8"7x\93e?믿͛7gzDke8&M!yI(,,ܣlM]VЄi"*!Ĵ7ԃ{?xu=W_E4$cHLDSbc蜛Mqq Pӄ\*˂dev.TTVLvx,L0(uĐ–a_AtTRLsU5}'::矝EF4KJd%%|WGoc˦s3*EQM2 Cl''Z"أC!t$ڞdڐ, 7}1 1o%AKV㰣ǪEaTR,XU, %-3 Ӄ j$ UUz?S$ѽ{wƌC(0 #<w\„477i}ǏRu5vXth;ކ;7[U-8mG BFP3]K !HNv" ?^o*+ӧ'SXXJaa5u_ښ& JBB:6fa#hp5yE?c[A![nfێrdn6IMMLrZ,={ws$&&ie"[0A,B``P'\q}O~@/HFrbl*DYV 8U$ՠi٪S؟jv{x]f zjm>|8III% B 3gv[_jEQ$ߞ5]7YT\.f4Vn&լ\5˃)dbcctV* dtܛ`KEyvMjDmde$lְ~Fͫ!'-NRb[Z4|MM446١Cx "-=TE _߮oA7Y8NHKϊ($rJ 0M6k;ZMDE7{]T2qa,m,>I5iЊ*H`1.afD2SlY:CǴ9&115kp'#Ifj7|Cuu5>}&Mj.0vGSSw}73gΌhć~8C K/婧T'):DBA(&Bʲkö[ ٳ3´q*467PUUn[^l__|Ŕ'-5+W`;0,p0"#!zG$4ݜx≨֭+M"!>Jc9,\.W3o6NG U$$@}OޔSTDN 43 ڱբT[13o#&PYg>i$ I 7|8lrC"[!0P gõ [nGdgg;ᇼ;I!(J$QO7|ƓO>ISSoso`QNB+.,sL,޾ƶ ?ƻ(o%n У{7cÏ 3+eA (iIRpDNZz'֮YKjJ6 )--OCVd<5e ۸Ssl)YjSE5kQ\THyי4iɩ =eUUU,Xn=:q#p52ig$Gt 5dW^@~XOEcOfUWK/++aMt¡ 6UI ]Xe(u{6t%cl`,X+*~~<#~8m cvAbIrˤ$m$ FJ !!.Dӆ464r )1:cIHH!%9+/CЬ%5- Ih_WpYKCCq 11x<&M:%KYX,*zHg zФՂ膎秼:=t*qi<3<m\ꚍ.p`:f//@(Ԅp .?vG~mz[$:IIfrݭS^Aq=AR#eC(gyXcH aqXBijnp(*5ȲL\\,a p]DG9ihh@C$$<ȊjB:t=Rqw$$n8{rj$G3e#&|5>[ 墺}{2m1ۇml4}ugE81L̬ > *ʫ>v$vЉK;~˜Oee5Y4ĤD*0M'_r;)HLMt1n0dddAm]1N "Q%Ic(0u?f(D M~_|Lb!DlfϖQUZe Mr/`"#&LK6խ/h5Ռr[7ӭkJH|DE㌶tĵ;Blٲ=3[ 6q]/~B}!>M@7EǗ ?Ԁ,٨Jff&bCEמo@whf86.!ښH=l\4,PPYzs%P)(( C %;p:> ݠ<3=YJRZYBi-g+waBCfa*bb}xL(O>o 0*a7?1iQK&NM"J p:fLabJ8m&heS}ɃJmC ɖҥ3h #Gaep{oiI #G k> ,aJ]U,ZPY(G46 RjLiR^VL0EݡpZ5)SRVĐq:(*fSPTR1!VrXݰr7ƲU IL]ܜ4zfO8]Ms)'??o#VKR yIJʋP  |BEꗭST*aUdQCE(EB2$ b )SoQletr8d$.뮹C\bZR}ATN{bW6cZHJJxU=|{D'a[q5XȊa,Y<6?K$ Yr(VFѯޢkHyޚM'LjIFFJBT@J֋ ỲN(+%˿YBkɠyBLk~M%#1ot>#/`CV@V5TƖ[9_ò8${T,KwS\ץ mfx\cySlƎJ  ׿ 4W/ /p~dYfҥC.E[cdv^d!WFo7z45@G2nQVS#hAkJKb ~$ap1G2v(bN8K)ض=GQE%gBn!4Mp8$"qaSqDΞCrL,6MNJS'T4U0O+ửbї>~$ivHOH0r28QN]u@g!ڣ#\L0 ѵC[em;AN2SPEح$IB؄vAb8C6l(<4 =L3@(BIجd0gw'ᰧ?d(?f*ׇC(,wΗouZIKKog hhdΏ+n㉇`ʤ>Xko*|3~ V㌉> qM7??^ںbjkKirbx'ZJMm0HN"tJVIt%bRĄas(@p&xV~&`$ω*AC猓ȉc Bh$ZZVV?p"SX݈$[0BЃ/f )>VǤ>8~8>Y_J}۹4s͵pWp'rYaO~IVV6vV_RѰʶmi>GUY!?ztݤ KcC=Ng4UCU]]1t㓈NCN:nO3M73}ٳw};HMIEd IL"і$SVQ4o2 ߟ2oޏ2r>?F".6`xWY|99M j 'T ˇ"I( SsFR,$%*D;T6SžCis,V ]G/ 5gd3T.̲/wꫯIJN4L[©ٚ$EEUU5i$jzH󑚚J\\ H *: Ws3VБe׍x}^bc|>bcQ5bĦMHOOvu|>?z1idIp 9d!L:y|E_~`hhR2 *M0MŴpdgq`ɐQeUQ5HJEC9gɫ /hb4W^ X,B4d7鐒@q8-m_S/F0 8kpFfpƹ< 84އkڹ?R& νtN<:tWLb+pQ<;m;nƻ7ŕw?$D7l߿Gk39x‰=1}Mm+α3 {ˆ^esugHlmVl,98NxWo%tҕ'|Fp#"`i^/x{j ےZ=Kk%TUEQnw$ǩi$&&F.b`m)"IjwIB@&" ih M!𓐘QKfb>x4UB0/j|ɯ СCۙ7" Qu- ?Dl Y}i9Ux6"\@n'1l8Λq{"qqqcW[A1 ]NۉIO"9>Gn5Ϧzj `'!5 =dPK(.،C K i]O"la|*wUktG_}F( ~kn&?MJh'{hZ*mW{r.63࿏I5|Oӻns>S8ιHRל=^xMby8:U4TseWQ1(ģ_d7`oX (tʌA.e c|KbO: Bh`k'LBzi0A!!000t!LS0[ a0 e0M]ahE?{f1tPюIDjJ"RE#^X*BQ!Ihzx!BEɦ'K Jq9yT!]&VrB:g9K&C aźB1D9yڑB!~qI'Rśo.R: gi%&UPT9B⤓N}N,|E 2pFBs۞_!;>pB*mߐt>I񢡮ZX㻈ӧ") FBtvk'޿?ډ!ޘqWyudQԹBl.D v?~,Y݃.))un!@xX7w,9 KBTԷ-[]ډx[Gxȑ# z!~oHJJ$c9F_߮-/a鷟m㓄h.݇".=Po͊O%n㞽nO˯$*0n:V\[o… x<4Mj-V%jOZs_.(,mֺ`i^~nޚ3:˩issss1c_|1{x!or!,X`0@1b#F ;;XN'azm[+RD pOG/?IٺN?r<5nƈ ,\ 䊋6qr3xI},K~1FSF>KR4U\p} klSRN 2H\}B!?F\xl(uh.z!nAw&Cs9B!N>Df]qC.Bopv)|bs Hdee?IID%#Li|G_Y/=##J;n'.9lƎ[.Y>/{x㹇DO׷xƍk ho]⡇KοyK!pՕ{|w0B"dS" B]" VOBeܷFC]9^zҎv(-->/ga`WT}}*=L:Nt/b9SԚj'=c?>`h 5 ]P(P( ƍ(BXt)Hvv6-bгgOK,AHf˖-9KKKns l e˖( iiiddd9g=p:xؼyssQWWatA@ܺaDEEѭ[7/^itؑH( <Y1 UVՔ 6l6m*ksMdff>.]!D\.\ӎ;hhh4M ի1MtrrrHLLlsMBuV^I6lقisM@NNNkܹs$oWؓ|M+VyyynUWWŋ^zEv!6n),Y"#mskݧ~*kXyyycmVX!bcc\SIII{߾}ŋEeeeyyybժU<'{H[IIXhQW|oǝ9q/ܹs#m˖-yyy;UUUEaÆEÑ8ZŦmꅟV.lsJFB9-԰x'{ɓ'ejkkIK 'nپ};Kll,۶mCUåTU4M1 Ν;-kk Bj~?B6\\\ nh۶mC4)BP])++0 :uH[9%IC{=gAA6}vdY&;;ITVVzinn0 rsÕ!ZjDihfn=ǝ(\> ))}$I"''gЀ5iFzz^Szz:e״'٨Q6,n'j#;U6>IQ%2m/"\"M p,sV:n[b E}%}Lx B}DHd]F$3! ݆sGҳk7LSG@B妆|>dYR^/ibۉ!&&(l6BUU@χ墹ǃ%11$%%qrJ:,f͚3cu .j*\&w$aX]BBFgo!33r:/> ]9CB4L,:"aJ ȄشaЖy[yД&] `Ga)p5> 6{ n@[pġY.eu{WtW]~a(ʿۿqƏ@SS.+EmZZN'ȲL(N7dݺu|>| |5+?Bn,VmYy\]<駝`1z]|萅a"aNDvdɒȻO 5P:荫مowތ1!2o5jɊDDw %! 4IWL U%$%?) -ӆtóm3>2O<@D3)**"***B]ЊnDIqoz%IDQ9$Ia&cǎeذa$''rزe W/DSZPÏ)9i78 oګ.㽏U SraH =dkJK}8a}zsל=30Zܚݎ$I| Z l*nTd>Q7MC5$UyS^wȏ z;6&r$DjbXL ڌ-!   h#8|7\~ I29FBg&FЇSVVΓy>o&$JVV֞fjOfo;<<4 ѯk {} E"֯XŊx:e0rAX$9BmQMwfAP:NLEYą/9ǟڵׯx!1qģ Rĸl)VTȊJ\|6Md =%k$11ҒR;RPPY| ydKJ#(**Su(JIGm}[B;㟊>oy/6=/?ZJeS3Gs [}wryrkk{m^xef)Y|;o%%i9L>HT 2*eEn+9|b6._^1dQ&T7n~8A5X&y7dR#+> &+w2Y*ZlN>xS PLϮD8qvHf2=Gr5qۥ'7\v.}> aǞ̑Ndwsy蜝Ò?2 {8ݻۙ1coW;ڱhwVRyc֛(s} ӎgHC5\/F&E%xn#0zPfp#<_}ڪR~>G|Y^&qX? @hYDDyLpyulR"j/e-E #LX7{$L}c-m;#<#MeUa-=)]r]cj wSOeesDA.T68uuoȡ'P]DM&;g``Z#QZZɲ3QU hcn͖k{k٤[8j쭦w.z][B}5 5sOפzrV}i} ǽdD9/{_m[dcO2sf]kY6Z[rua;cG3x:k1IJNisdlT%[hx* Y$W.}C \vp۩pc2'ր% t4݉QWX*PxQH!6JTQ}>hh&it:R>[*[+[mn`EG?x8t9t c5Cƍ$!)Vq~n˘tjn򑚘@|l6;>wX--#G9?r'piv_s)ics~?~<:usR\\LJJ F"'' ΝKEE999 :,?>tڕ#GCAAK,={2bbbbXf ˖-зo_>`v;+Wdٲe!ׯ b|r/_ :>}Xf +Wn3l0Kss3-bDGG3|pzE}}= ,`1zhtBMM ͣ ƍGRR555mu̘1$%%ŋsΌ5DVXʕ+illdߟx-[ƢEp:ٓ:P(Ě5kXd ǓI(bϘ1c֭Z;vرcINNfNff&cƌ!66e˖zj<Æ gϞ8N-[˱Z 8!CrXr%+V 66>~Q__O^^$%%qӽ{wjkkY`;v --cǒJ]]ͣHY`0zhؼy3 .ݻ3|pYn/0p@ @tt4WfҥB!̐!CuUVftA 4@ %KXn6xJb ƌ&M!S0u$.]ˠlkfuO>M{ytvn[}k{k0a.+"6mbڵ<3m[aM~.!&UUtJ@ter0jX6"є p:V^HJL͸0 $*y z%iXRNDR4G}[ˍ37s0z]ȓ/?̄#Gv,oAA_=GDNRRR$ҨvOCԨUvNv  ٲe +VkÇOYY>5:: 0~DxqE~(=%%?ϙ33gN1457A@I#)[r F7 5k0n 4RHjB2%Cj2RSvFkر}SO?]MATn>{j:ܓ5 f D=x(^ -Qk{DϱSكzÆ lι*^z_nsFsoIsssd1{lM֭[)--v-`0YiVFKaݎelN6}og̙! $I"77 D9Ms dqqqm }=hgDnr۶mdZ] Snxjc۩3N9;aÇwBuஙK'3 \kd$d Bb@F]y97^{[˂K@1zX]:z4<4l+f! =pi,G/7nGok;t(-v%=rv{ɱ'<;-+;_Ρxݏ&N/ʻޮ0x.c|e$&$PY[KqE Ng5<N;mBt8}~?M裏 ٶmn+L /Y)o+vq:b$%صddd?ɓ_]bB+]{ wXSbpeReDvoyy9O>$ӦM>H*:v᧟3n]4N>N?7oNcg`}L` U=@>O*qD7LSjkIJW˪eLVTE9@lU$L /ca䐔DJJ lHd磩jYnw}"ֶ `cǎUFȽzBUH JmP > ٗv(\HJ>re'0ЛN._bdzs-Mյ ;CqCNI^z氓Gc=Q@ ?Nll,)))TTTGwߑT5jSNc(Ԋݻv#y5,0 !ޅfO*K3$ɭȲ"(2~+ݩ0E!L`b&H Ly'U_F0^Z]] I-Eo ߊc=6o޼Ϛb2RPY(* Yٲ-[6l&L>ҹ1s \2c$7Âp76::ٺwE3Rɏ=hڿͽBХK**-_jgƌ|͑hDٴi7p\|Ÿ8M۔?S9昿LW5 4µ0e)I=a•sG¾U3/I "!L0ZM!֨r!0yDnG- ?4?e4$fddSZ8\p8u)>l(#1aFbfx`r8ARF/g2w@uQY͛7#In;2)))\ve$%%s1yȶّ*ĭشi(-h N.!&m,Ȩi"hEEVTT%T TՂbEQTEAQUTEAV4MCE$,IjPW!dLdmG6UF"]n'|}KPcF? iiZtKxH*9E?$\Z Q[鿛'DRR V2IKK5=!vD>#Q>6nȠAڬyjzw !@-u%tv5]jDGǁ"IIn 7*-6\XB!=e ْzN`B70WA03aOMS )\'4I1;Ebl}?L=F wphWk[cˣig7?%3'&o$?>1EO% SqǢEx>|8餧Mtt4`q\,\ݻ+ObblL0 N2zhoCs3#+YVp4KpXmVt6(z،KȊ HHRد4L$Y;-0J H (!Ȧ&+-f"Xd-|#cGgol( R+ZVg.UH#LAbJ,zEqoqN8f5y};tZh>vлS2KVxݳо!Nll,v: pX,"YFA `ǎm=ErM9Ͽx5ń QN6(fLيJhP0iJI B( %LH-hiJrxNCzxQLaU-0ɛM$$ӈ|B':՛qBȃxQ{IT/'Of͔mYQ^?ƫf7$'q&,8̃C0*KMC`phf- Ci2rdwRSc9QN+,V=0ɱSeᒕ2reu<duȩO:tح۹KۦW^y>{Jʾi^/6PHf"2upZR5 :-CltBVh)(h UUmiP4P5 pG5U eKVLD&8$863 @0L S˖2-A33 ?$TBx~jXnzo,ӧӧϘ{L¥ᨣ/g^%%Şpeqד!`M:eJJ\|l32п7| ϼ>Cqئ~z7⍋w :tc-\1csCV"%u;(ۉF6BF P%z85,nYf)Ix=^</E'ih0 $5\D$L `M mLES[@JKid MUe|R4</̝;)Slٲ}jx)$@"Vm{4@xZ*@4B4-Av˟~VMxws%8N[[9#$i;E]&teELt8_8ŁfXER)V/=~mƣi>I+&65he]|< |؟Դ atB$IGTT!"&6ۍ 99_ @}m=dCPHg衄tʋ9&a$!+$C ~o3bB!n~Mu]'..nURSy9i{`RXE~re:^${ü(&>qOo^OW^}Mwv?gPĥ^_IV+)))s9|>KBXT#8QS%PqsѢEym*8 ܿwbXgĉ,Yd:l6;%ѳ :uGϧp~-fevB?& ;<^\FL[L3NcFS[Sƍvay@kV+%e7!)*H*6mڌաa!,2@*_R^Z55B0@7w$LtӠG,AH-U||<}%?R1go~>p7Ia#$)lR,n&SUSOni׃7bqU9;٬l^G~v`(,[(ztj], 3rHlق'’$ Oӈe--Yn#"-֭[ޏzjfggx2d6m"**x dbywy9 >˖3wHuнG~/C.6^G'3-G|B<@o@/6_(7UҽWwK*% CATEAMRӒ )#Yp&C'%# S7fL,dfu4$GJ(W3U0p&30.!CSSSv^]q:En ʻ}B]o]"jauF3j\CѺk9N<^xܖ=pbչ-ȀSZAtH,?ox&Lbe:kֱb"tW -[矿[iӦ!ᬳm93 pWkGnQT\H>=('Wu>;v`lQN`)TrALaHIi)<% !@2I0dEFر1lX#"[gj+z}aÂi""$ٰ~i[+tB7ROdeeWȰiaitMsأpPja%DIY$'N; )/G^v_#^"5瞄S ֧_es^>|n~?;v^be{"fXq1Q$'id,% N9]qׯ%cddaB*c,4[Eg_CR"ERdI鿤 +¡ ab MQ)-,p8IN\WJ`J KJ8X2h 烐|^Ҩ4$d_/}s ۶mxlJDM#m~'4 O] ~ #R%== $aXdJxq0L6m܆Vk?=zxl֭۟r-OgI.?"xBiQ "j5{HaUP1҅[tPw6-" Wma Nt** v%쿫 " U a a&i4㤖l]" gu+(0$\.u1ud2EX:N `@7=\`v{dA;2n:\ w݇w-Xv-_LNNW=6mbSv6b㉏oqԑۍ$^tUUk׮\yׯ;k !Ce˖ZgSVfb{C{v:<>?bC*@UdN #/M7p͡<}^b TLIB<ET$2REV1LMz,gS`0H\t !a4tCL9vMunriӦڿZ;^_Ά Թ )ITTVtv:l=~$Cr 'DO> \q6}}-w!ILlt쎹ضEIRu񛔄6;uuѪk[UUu@[SSCrr2uuu{qgRRRkѯp.gl͍Vҵkg˨ G dp5O'~zT5@Jf.qX۞:򷢦: &7; Փʺ2AArp}_9s"4hn\.?_q&?G AFr4]Hb)+iy3clUA eN9f`?.b1AE-~'moǸqx뭷}dY'ݧ\{c=|=4}zecU ?l .R9;e$vtGyű\s:NVmfBsanYTe2fvf͚ŵ^fk@0K>+/¶zN>X2sӕ>$7-ZVY@rrr=2l<{:$]wxjtjTש_P!5<F͆UP\_FEf`k}>~AC$SRHs, ~Uxgv:t_$ HJJ:mڴzkҵkߵ(qҵkW;8N>dLBqq>Ĝ9y=X"~Z?JT > zw^@k̩r*y/Hz Nl>z8Wc--O6q!їMj9{k=p+cݙ WpݕW1l.|>f>_|.+!dtq䡻gZp!gqFY٧~ʘ1c~SsI4`0x .>u Yl3{wkQh, ] /}}}@!,X۵bcGҲp%4SUFN3,|~2ƎKUU:Fm6|A8383;v쯖1MAMl:,pBjkj88&8鲋*Ԭ09gIJSQRlG,2 H^O<ԯHe):}H,[bY~@>p5 pv}Sy)Bش޼_h+vQ)n<-`ԩl޼;v†j7/IyoaĻ3***89Ù)%76~x ɜtI{S:R8cS }  [P\\UW]O?Mcc#111Ӈ28|@,\uv/ŨJEEmmܹvaiع" $l bbzu0Ov'u7QPEtt*Ӡ8~ J Fk"vd YC|RQ 6匦w4T ʞ[l md$‰5MAUT-#f WY5EaТ}n߾.] vTw&۝ofPf8p] lhn*"lf1tx뭷1c>(B!bccIOO `++`ӦML0}RPP@||Zrزe ]tW^l޼Rv*Oo(|e&'*׾y =s<ﮭGӆ,ӱs#N:DbbbhllCss3$aIMM{L:&|Eѷo_jkkz:AJUr`eӏt9>V6n%uxCParuծ2TRoٌCG!$ٸh4Qf %R^!(w5+Θ{$^!Z*0(/bl۾UQp\JJJ MME0nA5$IX4Do/W~TVq٭p:y4440x`瓔Gu&8k~~>=W_}>o `ذaLvp/cHL7`Ĉ\qxz: ]jW\t V+]t>={ݽ*ڐ?w1M;vv5,;))X}3S}!P/Z__f@p:m*J!hhhav;vDbܸq{@8}…ZMJ&ٟat<~|-MQibEQ4,r4Yob8{56RǤ#xK.eĉ̝;46 +74xx챯2HLLGۋhBi0`>E0CNBP2gP8| ^S3*%֪&p*LE}]4gׁ'%:ሁr[_:̪rLw1tw(赽v]@T nf33c3szyff}vw]]E>-ho͍oGvr_~lܸQx8"'p !X#GOsa„ y~%5IIis:]r)[DeT B 45Ue3mՌ8ʮe,Q =-:FaZ61EcV Qd1LLfL&3%Yj9O盍Ff&5%]+_']hdb фL6#3 YY0m@rJ:nf3&,&3m"I(c6Z,جp.lZ\SUD() 'ټ % A wYvJ6~,o!ciҤ~c{emhxb=}+.|;Ͼ dž[{͆$I~ڷoOuu5ǒa}`Pbjk< 4mtu RDDLf]dKMGa DdEDdžWB>α}7mĉ` 4 fŝCܫ19 cb}];]v;%%%{'$$ %\שs#i)/:ডSokz\lP#ݺW=~|/h*X…ߊu5/248yyy|Gl߾͛73j([Բti EE5G%>2?e< pS]"1_Ϸd!** : %]E^,INRr$o `犟BŖt1p-ti]Ar.x !L~7} g˘sg+}Уglr/=Ž=C t\&ѧX֗o6œӽSzd])h4iFM  6Wt_(ԑE̮Un"s 鵤fr ++_̦hӂuQٸqsAHMoʶׂNF{ڢx?F j6oJ-ۏfE -\y.*jή$HKOcESk;qI)|6k*+*QW\yD[f 3g~ۋ^ث-;;_\~\r%8bt ` A=V$)9,Դ?T!&w6){?Ç3$Sp kħPQ1qu5AtHi=Z .Yl3|Mޯ]v\y啬\P(;ĉ{x믛O:]gnew!_ɓ2~ɓ sn .mrw 2,;߯|rzsxh֮[VƵW4ZvH_ٴa)|?cw1𼛩_O@4%%&fzҴi |t+:oV7$_-b{c=z؋83?.enCZx"-d#鱈n˅i&G O3 &mï+4BAIf~bWGeƖ *rXB!|$韽@W/=n% %,t<dB B!bs.fsY%CNkEŎ0lADI╗t;3g^â)+.d(ȒD\| ѱ(A(jUf7s8XVl|[RTTnGM_L\zJ@c6,b=^5s_#>=Tn˰wyJJٚ۷ ={6`ljHiN]cy`DFNO Tgru%N=ؾ#ļ/'Evn;㷿dՏo|>*++ٲe  >R=҉OndNt} pUxnɘfC)[mg?nbcxyɝ,i>Esil߿SessbPU>}']c /ӣs%MS1 4#(Ql'd/GQQ>D1K5z}&_"%?n7xـ(,ꇔ gOö[O )//E#'R+zBֆښ2dQvEH_jhXOh4:)bA$IDӠ{g%Onn6zZF"dϋ@FfF5hwTWa2ɄAlIKMj ``Ȳ@ЏRhѪhl2b L& PPTĐ#/Χi#=.\~r1j".Wzts@4N$OFeeatpV)7c7GfV~Y}ȶELR5ab7j22v?g̘1@y>Pim =FÎ%\rոpk4t.e>?,pOSRC<$C8*$Νi?9ShMeˮ=t1H^!sSLl>B(&]ΦlL~"]kRW7MgEEoaݘLZ֯^nAHQ { J"H Mfp`2`0Db#%)~Z.<=۶Bcپ!}y}Gaٌ(łb4$Ơ!FEQtb49LQvT-A68  Qa9SW" *qшD@SL4oּ#jhFv\n&S[W 00A ` m7ocƌa֭7x7mڄMf:9ęZh,3h&CRUUEfj_>[A@D&Xcs Id{ɜ9sͥc? IlعK./lHJ@TEjp}x?c *" "" "@QtY I$^^_hT "!fw bUHFZ$3DNXɬY[Buu5_|RWHG `ؙ,ˆqw&P`4D1qƃ]u x=^DQ  !<Gum$C*˪ E>: Ņ躎A )J8,Rό %sLcu\z|י|Tו p88qb}VLF+ͽFMDQҴHR~KJ@ x7 Bp3qDn~diqdxJ~dϾ Xoǎn9r$>a렴OW2syoпWb1?W!==ſ.}n;gBa K`@4r!KFr6o*~w-iIIеS;/_BVfJHc۶b$T#5dG4+O>||`dA4m :$Ev=wkz=[C0]G5S֗(55AA  --Xa' AqbWl>|d?QwR*GȂy"\}mW!͛7ӻW/jġ.6VP>~W G2h";qC v!WO) hۊMh2ys3n\R š ygy7iѢ9 qX,Ѭ]>lX,Q~\T4M w d"ckС#u덱`0xH˸##ijI$#;έBRZCb?l~]6" G=J ++>`kv5H0mێm5 {n(c#8 8[ &dKDYCuu>lu-O?$Gžxdx&66K=hsBH bds9w`zD?( (Du[--[9ٰGLTU uXu؎?\Ĭbf͍.EϷO͞Ԓ:O1fM׈ ŌTgsW9Zc=M'W}>+ܱӮ~`89 R t̶~&S8`qAo 8DG%;*C#'ٟ^CG'oѢ>3@MӎB%-atctq"Nˋ.f5Ă @lL:k_)i\ jBC,27LzfvRZR( 4aN&=lٜuYܹzWFH.~7w.iZ1ĸlDy5U!稑|"ƂN\'b z~9?| 37-j(v+RЅ~s>}p:{qٳa{l?'DNѝ4ח|hID6um""{c+(b4hzx_`O gӶHj* T$U՞&,AcӦ2Z"7\{I$<:O^-I'n'Gֹ֯sg.]z)=֭[7~დkإ,1iٜ3\D3pf+,0,#9~|EriX/z5Y(ٴ?=7o@PoS3ן+cYRRS4i۾-=F]ʖ0W}y)IY;Wt&Lfyg]yg= jʱ@t}Y_,$I>boz~'N0aw N%$$зojLpZHyw59`,~HrZXGf*o5ロbX{Ge˜j/p gM= ɓxgi*Mp̻H_pS8"~\={b27"8*x챯8.q1 0r$M6 8 Lj4Zf N]n b~Dj2 pE[7CUuIIl6CIIQct{91xb WL믿f;)ƂW("11 q!_qLTܾ,Ix|uG Qa~>%d{'9kбeay;[nڌ_s%9 y% /|;:%rwP[ At[a46 غn,/uZa/_ɗ_ǒs.E|Ot;+8t@@a{ӥ[QCGҙσ>Ƚ),,<:V _F'nSo*ɇ_QE)\"uA/%I4 ?{uKr|]D׵>nCk_4 Yҙk~W?۷7Ë=dgp좪j]G5Mo e3qK@W^btR´  ÚMwO{whwVqh}}0f=l[.YM7t4U*p_u1o~R|'c_Ig$-~&NGQu̡ *޲Æ r1evu<^[C06 h9,沎;#SO=uoA6)#I(G&dɀ(I(H(H$ߏ{dYWE4-IQ-bqô"d@@81qa&g~h?x䤙z:2ۉl/_H|;;׳̺e^n}{_|P] ۷leҌg0d:ҙ?Z$}wΝsWuxQ񣚢;{'!--뮻Hz BEDC~ĬTz.]Q@U^LUt]CU²B"a!\VPj >IPްϣ$0<Q`QU9/}DO?찦w,C"rpkfsӔ7)~ԯj(J8uCkV+OkQvJYyГ>WkODQYu$F; (*/=]\eD=]};u8]ڇ :1ԢxYCdžH *1H80ӳƤfߙuY {s`zWD- 9.*J%nLK=osmȎՈLj/$)?)ڵlI{}- d;?ۺ_|+][T_틑[6тyw֭{*s2dȐSkꂼ0 a=" ]M>Ch5Gȯ" re+: R=l]@7 r9䕐Mn=qVVeF,#:0 zXR<@{Fn Q0C8n,܂PMZk@H&.Rx*b`eq4kn‚N~رљ:233 +0%tԋ| x{ڢjLU^"w KDnfW?Xc.Q 9(Bqq1LV| Eb*m\詺,đJ1xxeK,aԨQ{hdST -B zT?M묠E\&MTB>A0 J". dDAã( A#u tш EA4 2` ?(P hJBB [WPc5;wP[ gy!u8+pV?Nz0L~M)`S iD5.Q|% gСo˿vnXr"aIvB!%~!H5MS~T1u'˿ōևs0iLz=*8&!& (bGx-h1NW3E#ʑgѣ;v8j p5t>즸n5ZtM#&6`0X/C D9<`ۭ뎮)ncaLEtl6[|T<i錽dy,  PCٴ₱cRRROzyx 3weҷz3$?~Ӻ6[nB1g_:4w^PWn?TOgH1!޹wqPVw#<bIɠU@<ظp%ZI<ƣ= <QHC G` ɆWCP !pd#&y4"7=>#!Mv,dž?ٮ?Nh/#ֵk}bff͚^А "h&t]ISVRP LQ@`-KHb=wd@eTX_C$AAdK9~u9t!,,,u( )M8ۨ,-gWcyffa-'JNjPuuڃ5 EIb'uX1[UU#:G4ZfS:R<ʚB ~i,iew4]us$:ϼ㇟Ec [C$eW2c_AJ|>J=ܪ?*VRkL5Gs=VXO3i Tu{Dd F!E 0EY(-75NeeTUWc2$ )omy^ 2|3~GM P`0D @UCIPQ wOe~t{x`Vѫ-萰s[g.yZ*4Ei^#m݉ulڰ~̋#ZNw^.os[vA-eeM!_5^=Vh)o::4,vp4+k HorPMMwi63ܽ4 += # ,[轋.ƌsz{DA=\`4BHAq]   &]X:ILLDUAqܘfDY B2QpV{kC CGf3(ܾ zXNCA{.gh@54wqGt?@ 7?{nSÿ+MHUՃ.5 yI罩Lt=) MEtyu>-8QJ|=;+zʚ½$l.?bfQWW[Ovd|-%k?4LG,ӠW"d6yϑ20I H2a1d(H2x5;Qpb6$b4$ ?.¢.F%ޙ{7O~1nܫ(%IDj99S^} c&u%$Ĥx?~R5hi1DDO/D E-{U$P\\#<̙3OnUUM 45C(p3Mܘ9wdPL/,-ԉkh?P d2t;=APV\fb B Ʉ 躀TrP=t: f36lM6G:T5(J=b&QX>Y$o-珺 US ל""ULó&‹/7EMS1Fw:X(QS]Eivz厧각SMnEHZ8RsH; @H jJH pդgغuKQ^ʱ]]CJ"O~l=z{vk.S ,ADُ&&*x l8J O54A#)TbcƎ[Xd6MPu&N$kQlC6(j!!1]ye[a! ^$j"~Q>*oiѴ~qƱbŊF 6+SuJuDZBSmD&y"=xAp8Kf)yۉ0n j]6,ǤVt cDz8k8o@Ќ6:M 3!ł^@e$0N<8 s:i$(ɈE#}(ٜ;DHBcZi]SBT9@rrJ d Hd6;:rt I}e!":7Ӯ'6Ύ h(' L*(e;X6EQzUeŇca R( 9>ɣn#16R\2u?VݨuU֓ iTngDKHo¢՟jFv9)?\df"بk2u GDMr0NKeɈ`AQ^u-Wtb+ߏjS66â}<~{屏7GyB NRTb (+q$! PSS,TQ./%1$sL)+sRVU?2cA ʢ B8!:NX&#-($`pYg#77ehTKӴ$ƦTe3iIjD TЪIW_E\t-4rBZgȢkxJ"9 a.Ah;'{Zbl&DIq(uDdo >BTCv抭k \ɓ'<ކm0E,f3!M!֤4\p="FyأMXͦ!I2`04}xJ=b ˽ 4TA BN0"h5 0E**aI@+Ç~8 ڽ{w^}U*++&qzpPg 1QIƼX'dI&=9v[[ Fd~!!;RX$Ib7)([G.mڃԄf8(JఁX n\jEjTЦi(PVa21xC(Nv8nAۏږ]KJjFPClpr5Ȯ=Y,} V[na'7Fbh߶'Tȴ0>:$z4$#,*&Y "FT&4U DI@DQD U"o#4$ADUUׂ [fffiӦ}Ndq\ sȴ<35ɭ1&lBJ[QU[H4LzR lX^?tk;^'h-?-DYf$2: X`ԹMdo )~u;oY!3mfF6d:B4F\ye.ɺOe2=j( s[Sc C@בݕͯ{֥K}<k֬9gpu/ &c Laa!۷?a/"`:W9?.{m$Ʀ ;!~Rj|3TbMx(mqM,/sz 2y./{ego|B( I"*P5t>sWFN<#{8jF3}DoX?NnC],'t2YJ?x.:.i(JW4iN6dA ([|3;P\}auP(D||L{NP㨏GG%H.d#IsWnByM6 /<@ )R2:E@*Ee沵`9?s1>A]ϥ|߅}1F:wNUGy%ు&, q;9*A^GEa˻Ġk:΀)s2JyafmXxUP) h9V AS VWn#2 9wN_t1APP|C߻|3_f _|^;o< tJ,X^zΗBaaaK5MbԤ!ЪIZ5$GQYSȲ p԰hę3lq6-ճIMgH@Kf,wo~.5,."DSP4eAi:ޠoiw6ieD3VGD:6|ClKsW@=٪M|kQFD,4"BtM_UWNg۶m<U:<G0 `6eI4LXVEQWWWNIe˖v9s0qZ\3)TU&"'b坹u:w%ࢱ"Kn_ȶm\8>MI0tQ3:SKjhj}k; J67Id*2 P*6ꡃ@M EnK`tߩS_JVJ,&;| Q2/T^yfX#fda"(PVI67@ <`Î_z8_KJJfѸAd0̳)ͯl6~N&$dC1ͬLf.Gx:9mo&M>|8/:u"&&~ TQ%2; /rJ]MM VbZ"Zhq͙1c)gԋRRj Qc K#1OC'PZxcf)h?n|m pYly_eH9kȢAҿ$#})d5|vA H8K,Bm P@ neXoH FI2ޒS}9=&މ95m_0O)nO] ]FX ~1ϿpV" 1b?>TVVRUU㉄F#6D̤w 0 bn; No}I715 M!W8i4&}bZRRRN!j#iZ.epyΕ5E$Ĥ3u1|#޾7|~# @%LM~iPPS{,]:W}pv9l$!&= -"D+&5ِW+A 7:p2e5 Kf/)l+uWһiڛ{_ǀ.gj]uW9wt=#zCH٭tm`LE1&mTוh5w4)\[kYf{XШ h\Kl]ޒ@~Nmf0BLL ~;*Wm۱hz&L¬;oy(֭[5|{yXa94hOU yg> Bn.UHNcnZ:t?ĉٶmEEE|!TUmtnFq^]ԋ`%dz)x ;<.u3޳"Zn+FZ֭e뎈3rJyy(.]1k. r\ȑ#O jyku<3k%Ow\&m~C@'HFJ4Ԡ:%%%l޾Ι"k"L&&&7xCbrΜ0Mme&gŐBBHVVVVؠTR⛐߄dPh0KT8 2|a=ku翊{fU6\(FǖHOjW'rNC{KDFSp+[tλr FQ @έީCZv{(AWt:᫯bȐ!dffYeY}9 X`IIIŴ 233څ 2zSkTia κZvg5QW\A&l۶ W>f<3KςjN3`s2qF y̦(j]DYb,b1)V/uwWYS{[q\w+x玶$_Y2PPAp%Rt]iӲ L<|:x5Uv}k%;;;w믿i&|oVӴH:Y6mС7pC$a9kv|㵠?DSTXDl\͚5»%S/+t6o?MM׿ .  ]~=?0SNm4*?Es?SJBa4izv"x1 ˘PyNV5<b2X,{=͛SUUk_yNE |Hup Dڲo7ܫjP=6_atڕ4֯_Ϻu(--e`rȰsN.]ideeѯ_?bccYj׬ƣ3lvldDsYr+V@tBΝ4UVj* C<WfڵXVzIeɒ%86-a9ҪU+voe י6ѯ_?>|8>( .$##Lqq1/Mҷo_غu++VCmm-SN#!!Ν;cXv-˗/' ѥKt(Y+ ݺus֬Yúuz~Ki&ܹ3۷ptRoN||<&##zˉeԨQN?t꫿w]G !"={kq(UANtщ$7ĉxG(p1׹< 0!$UsxWѣX |E.yQPP_0e]<3ⶽ8c5ܻ44M=m|˳n)R,;jA4M %k 8}5xguؾ}? 6O2eƎ}Wߟd֬Y=Q){=6kB Y͒i]w] /ɧgo%o`xQHh\˗/+ ))f ?s{Ju6^lsn?Ա=m@I#Yɭ^76s4( wf$(P(k ;,-%)mp{ks%%%)SDBAs^@!S1#@ E^J֕l,53%! FUd&Rt<֧> ZSSڵk1|&6m9S1^0{$f$Naf}uTM!.: Y6XgHnM0䣴j;Y)mNxӷؑ6ͺsfCݠID\\~wg;ﳪ4ZfuݹҿYɱX6|~!tWqƠq4>BhWl*ՒU|srw%!fX1ۭF:wG/~Cw~%ųm9r$7on4 h DVEQD% uvj5ku,hv٦i/(ѩfكaՖHOn(9Uk9GFclW)nNN)))HĸqxT4i(4i҄⚳R0 6w9}ؒ눵1gok?$Qpk<Q8ᆪM6dee}Ν\wu̝;6mP]]wK/Ć x#y8KJiKvcp0HMMM$P ٟʍf}7wԚ}=q7([<ykfIY |72ҧu%'{2jԨeu]Q?>SLrqӪU+JJJxq:GQ#15kpW3l0nF~+rDWi\~1s!?u,:^X}ɭR<.A@ =% xPԓ;S{i={>GZlu]ܹsؼy3]tL:ztƏo<~Ax1c ,g%??ȴiӐeEQ x^GMkoҨɒ -.{QUwtd`sR%|#,1wI,Z]'̯4 hkT|`B :&v ! B:$*^A$F'Xp_|QUU@5SOMtD=AT{L,)'Dfddqhn͑~ ϏlߴiSxB;tP:f'##.z hncrK&yëT`9 loIn&򣪡{65,tm3E ֎uTRV(>O)ݳ3  :+:VR0^DŘcLQ5rG[@5194c6|N7:[5]4R&k0 \g?`W&Д ޏ, -[<_>4t Q5 MP5 I( Bj**4j $&TT`7, s>D{Kb2AG5t]ECEt4UI{lڰvutD px ӧÙ2ZC}:k$x{12&M#pu1x`~g6mJ:u*r ;wfÆ $&&+SO=W\n_~_= F#CeǎtA"d>Ѿ}{n^xf̘q^bCܗ=&\UWD\t*w0+F|Ā c;tFR^\ºp/ "Ox9[gxϋՄxwmv xwwM ^_#u@Lb7 2H[|Tj0@ j%`D$N`6*sp:)("%5Y@T5]SgO$&Z#Kr+B@t\n0<6Ђ( zu> _l̚5+УGFdbҤI)jwݛ?/)S0|pv؁iHn;w|| s=3hupy qv["FlA+hG=N(D@?5)ۑDY2ϠizwDema3dJI}DUQ+0Ot]ET@b1SV]I,K2( d(Ƽj1$PAG]@Uh!zVCUjդeҺu[B !% ( `3$ FT.:fI1DLo] b1ù6(*Hr3I1D4 QlmSbȵaPk`ukPBHHH8 bH4nݺuu֬YtMuwUWQXXxq""_&W>4q`6YW F1"j4nc b0ɘ-2$ J,bY  &F_ۂ .jZ婁XLvq;o)þt1(tt44ALj(I9q,m! ,&$tA󓚙I^a%Qr%(#55kcDz$&!*}@zk.!(::7Fn/ iDA@UTđIIITo;vફg` \p/VI/y屗HLy&ڵnʶMk1d&ՇE)Vȶ??~g2~U$duw_ȋ5bIJJBe.}]&N@SQеkW#\PDZZ]vwqG`ر ɓ={vH,|C{\?ަEf4KEfr+nyhr;D^zւ% QGgslNMhssB !M 5NGfR;Xu$dX?K?wRZc'56^%ol6 i$ W2ׇbQ_~} #,u;ݏ[cH,o]nSrdc"En$hg]99~$oit jȪ&k *(ߓsN>0Y?3gF*9:3pԆ6fZIJQ'|nl}Q;P'Y=UUi۶->T诇XʚBmFBLɭHKlUW^%Gsbv~k=SŸ XVnxps]d(saւ` ~j)Y2HKlƎ њu*NޖƵ^{ x#SE_[Kf ȲL 6&D1<4łd_ʩ%<|\y,:g5uZSWS͠LK@WMgMU`4Rt , WJ錬C?.K7~?~;`3.ᜫ X5U4I0pom.O>xG㙧tu߽Oࣇ/W^agW73'Yz?}MӸ袋=z4&Mr1}C3 RSS},^JJJ0L|+bbَ3jمfa3]Ɍ{r \qw?^~wF۞d3O= YifvMjgR'gha)ZK۴FSO=ERR Foa <,[ 믿Y` _au]ǜ9s"1_|.]Db |Ԟ){8ħHIb1p\J ]=&j1cnMnlZ֌>(N;%8kzgp?ܑϩS#^QDEDA@ER\vхw`n+& ItDn3͸~z?{r_ysb 5Ca@XxQi!gż `>ZbLyt_HmI@ L&GPl̊afTUUqG3Έ xl2?|]eoӡKo5d%@Ѯ"]r+&ws׬x)+D6TTv8Dj?#GO#11;ӽ7_OL91󮛙tHjдc,U>/68 Ewm%ʟOlȅtnAHHH8F%uUdǶM詩XV/^L~3gNDٷAIO?+}jQX5k~)3f3dܸq|<{\ve,Y 6Db2Ki4Jp4$A;G` Ƨ>f1]@SL4; KhUٌoK] o{>梸JHz`ӷ;y;yخ=+FSWYw"||'4cǶPV[CYx:fN~37|>dY^ફ m#Gcߟ.Rv@:``ۧw/^]HjJ(۶ V* x,-;s-wFyΡGBrleLWAU5UDƐGu){$$))ɛ_e$$ƠC^xCfTY$Z 5V6O9aA@et]raX7WF~mt]gĈ'59d .^ 7' I&M}{!kV/#oGuf9m4<|9 lCqlٲ-;1дpREl<ߗng'-5jm.zpp(J6<~7=Ob¬24|G5?[oGAA| /Na]x>id`4p=XVRi֔ԴD^{MFNQA [ϿaǎmCA5\^m7lQ!nyȲL(B$EA vw 2jf7V, &sM աjALF3ϿzN"3}!<1w˖-Ĝzhʹ> N>|I=ݼy;RU^NQ4rE17LQQT8\:W^/߾Eͼ'&SYUI|>+]ByY5!d jjjPۢ?zW۾ @ll4fP0̸^q.eLt 7o8]ĿAVRNt3f sNv} xP#vNbB,_> s/YY(FM]5wdmd&":UE% 1|\zm6\ǫ}cd :a[𞲓X`Æ ;]v"ZmR(RSjn\N/:.0A9'/PU^΂ZR#u[پm'Ғ'Yj%ӯ80j๘_v?zuT]`0ILhWY_V0kLt<//](-j91 1?V:dIڞyDQDQ-[Fqq_:Vii =SX~=f:ᆨ#tte^o-#0tpG"Ӧesbbl霑$3/qٶm3eЁȂHzf g}5j֭㏍&.1`@aӆ]nTEE2I,u1o>#~N=J6~[38q:>!tŊ\y啼TUUBʣh[osNv19Ƿ~.5XοVFJOD;_rorJ #GFED2S\+*]k"7 I2NK/¢k).*t ,q6*”PQuZPHAQǬZi뎴mNꫮgX;g=Hˮ]ILIıu#0gH 5k`45o<3LU-  0hJhnö= g a}tΆs4=t =D2%眍'))MFRj[yC`@$Zly>偞ecӟp:TTTsV̸jzNׯ#3#vU5誂Mlz;&5k$OS*x;nδeP392=^!2xxyشi / uM2m4222K$ӹsgbbb"$z+6*{HUYY?3 .DUU^uxq8vNbbb>}:$ѧOFIZZӧOGENJvv6`|{ ={6[oQXXȻ˶mug}]?AYf\|dffFiرtm{1͔/:/O,ZUUy7(//'(n, \s ЩS'&NHFFӦMC$`̛7E(E{YyGekAhժSL!++/I4iڵl6GT>^A`׮]hƫJii)_~%+WDUU^y\.sn4#:h  DRR3f@Z~}OQQ:(ʕ+l4++ 5vMTYfB`>=,j']؞OVIL=*k)A QXmՅoi,_}7C~ݧRNo Z%۵EURX`ѢIxqTU{͆ PNB :fIII 2$rNIڵ+cǎ%,3j(7o/2ᤤ$~gE!77\-[@HipPV=ztDpO zmeee|'J="7( ;v/$ l8g3f^4hРO> uYgС*xA`„ TTTп͛(t v܉;ȲL&M>|^mݭ[7ƍ9$I7iQ0,]Q!''%KPWW,nӦ^m-2C cBB@ n|W(B.]ҥ yyyڵ UU#A&M"U gϞ{ݓ(?iksYp!.oI%s=14O-voJݎ=ڈ(b4hժ%O=4# fvȥYN]zlQ|5g!iqvnKq83?z/U:TUAWD5HVvvځ(I}EP YY9dNdF "l/K׮̞W1LdYr-WMMMhm[z5;w>g}K/4}mOexb2bΜ4~G>$Уg~'QvD 2SVZHNX2q9٭!-HN.5]FbQ nFjr&! M@C(D],DETtI$ʞ|u>$D,$ICS,f :'`BՇE:8jrEU*+x\.>N/յ8j8JKw T#ifJJq:PT/ǧ\TJRHQ] 8,$r(xJU lBT\C3t*JPN8LR8"|GK5o-G)0i}imBF>2V⯨Ɗ`3H$=wNueѧ>h_Cxz&Y?#ۥђ%KַԩS1c[>\q">?tt1>=~tNb*~Rn SC xQ'q|-H@0Lt EI ydvGUWUQ\4ExM jfMqȩuf[b|tճAQä)zz:eQl@ZTf$}[dGYI]vtŠ;G^HIq kocn-3 LOCK;&<-%U9NJcNٿ>g<TUCogfG"¨Qؼy3gq'tR>)TW_LJs^dٚ ylcrȡ⼵unZ /|*[g&|h^chzo`&a35LCoj蚂40u1 CP]UQu")( Hy=8L*(ꨪ븮G ০f>wDW :h׽d~%4n^ԣ&iCcG⍿i*Kkބ&oHw[#N:i'N㈺}aeE|@ֲ~5_?ޛ‚ x8蠃rY٨s|><cÆ yK3mqQxT CC6:\6mμm8~ W^svHEcXzO\s%C"j sq< ]Uqms&E;k[(J&PT\I ,;?czMƧ{h~DwjcVX u_e¡i }OIJy'ʟѼq gs p~ @n*N[MIcJ}EG{ gj* 4c'ȂXOGydgÔ)Ssەtgm3}t:::?ޗ5 Aq [ \MuCHţh86%b(W(. PRQV꣬0@aGeHҒBtT ) j]llsq<qp;Cv5Q ZMKF"2b1T (ilu x$o}=qغv *a[;t+7æp'ټCK1quhlldy7>o_]ܱg2e׼ϲdTij1;N0"I&DIx9H_ֹ-%+(#<[D6H"\A׍^zA ^>LP`h!{ 0x8&@&@LpϵOAU\KpUSPltՔgdnTC>xw%e챻RiR)4M0 ,\[Wo4@ hh) m$R/Bkc7-iAz\)Sm**qi%[aUIVZ:_\IKhχ'dMD)OJ"m @F+I:Q5?xABG=xL(뺹ZX~=s̡nL䢋."PTT6lذ̤VrD+d3e͚5,]jkkihh੧E_"K (J^F2+vyEkq+P8>!%%W՟g" nrl߾ $2.LlUWR6j*C40`'[\xtE;6EEQq)/נ8[g,=x/_Ζ-[y7B֊3f ,O7 cȐ!L6 4shIԽuVx fΜIKK HҜro G nN+E,0IhQQfi~TH x6I:ޞ{IGj'OFujkksK,aq,91 Atp8m;#kf䂂jkkOΥ^;6²,ƍG*"oh{$S&ގ.n* bӕjDRtvj/dHAcKYz2aB0opF}&%RX*a;.>Ӥ lQěsϑyGmr衇R__OGG>]4~zFioo笳΢aÆxbsfΜɠAq'W,/)|AU!'b&4JobZh_%R5]2D4H۠hMM:q5fڿj8iQԌl fcu<=s+C<Ii_E̗ Oi&~_x5,i:,\_'? lܸ1+)--a >\d* <ܟ5UF5[ь*IV:.]ahZE u=B"- hh،k+Q{0L?.86* @d* : ┏?A "$io0AI-4~c_.hV%L7=bA&0`@.DIWOꭻ|:Yɝ/XC2̹k*4nF.$Iisx1Fq1#A̎v0PUB]o{XÇN'q , (m+h70C!zmo\G! Q 1IuP4p4o s/_C ffQW_{g;uys(455qyՕu۶sd]\d;t]WVV?)hmo.(F 15 EhFyeq`N=IOW .d>Oniy oK﮸e{%%AaGNc­,y2JPtwF ##yB(ʲ!9*I<%etT;+].tJI N"n|hI:?PEc†af***:u*?0r /fΜ9K|SOe1믿BTU݉|UUرcu˗gV]uq)<4#ήnB :)? 󇅛pQ ,z`;gC tל최!CYYNǯoSIu۸4)C\P4q=Up2~Fa-^idx)y7=aE~a֬Yա*~!+r,_> seѿʕ+4`qii!.bOTA4n@GO% -zڢO@53 1EP2ۛ w*UP kW.f(tG(X]c04TM# T@<GUT4U\M(J\7-n@Q TMPsIӳX7=SrȒD":::xd͌7Nxoߎi>L$H7n+V![VVFss3|N>S{.Q4[Moږ :_#OVN݈a,xk!ayCUgR?.eb6~l'( ڐ#@cs#ᰟG}?+.bB K7=;|B!˗/m۶DBe`t:ԩSYz5 .c& Xf v~{&?pꩧoPP8침b6tt ߇ 2;dTܻyX̽T j~_Ȥh2D- [0}**)BE%X L:((8ߟ2.F 5GJQLb9IEyc'A+뷛JPU0(,,`0ײq*B20 G:t:믿Nee%MMM%|h(-5).b8_I4qaEp<dC3(4n`j"8na@æZFV0tؼe m{l&|5<մzsfWkǁT%mQ҃ %($Dr<Դ"=^Nj&Ɓ@rC7p<"!<0|XοX0>Dsg:y++-}:̝;>" .쓝H7[Y"k666??<lذ4줺g}[o+Cq]0mK._~c9&we1B:GP=nh~SEQ 4lN)(LNet(:Aӏ/ac+5P_Yg-YBN)*HD"ii0ПB"UT ,Of8Hiå*{X~=#GdgȐ!TWW{窽~ڔmD"-[X`˖-cҥTTTPUU_LaaaerT*[n!d;<ǭG,3'y6mDWWׯ'L2x\E6bW\qSNn EqƎ{w'D)IGNGViH8hJ%QGSu|Z >=")^A$TWJdܡzH>^8Ũ1#QPq3 fb}زt AU&t]'͆5Q?%TUV,JӴdBM7Z(QGiii!N2Iuvv/=#F`ќ}ٔ3f +Vƫ*MdժUlݺNJ44͜ne,;C*Yn]n`|,X(,,<ϟ=ôiӈFA(xK/[<-VXA]]]},Ɵ'F{k+'[>T8QRr`N/ QPA (\ņz)D".*D/AL‚&4J)) +*Ak$Ulް=%6xxxn& Ct<㿪'tR2+=+Odo? 쓲(gJd{4tK n@Ɩ L0̪5WPŔ|,_;6Go,$8pزY|m&%4G)) m⣥+ 4D $z#t <µ ,7SSؾ*(\(I z5:,\1CnBMN)tD9X|Ӊ7ֳ|ǔXO8Ʀ:S&}K1{)' =8㿂4 [o;O>d߇0(6mbӦMI*eee\p}4זvq UD:gq.wժUL6-'e3n瞎CS\D\ U3-&zxh{7P0ŒuZ8i%JSQ˖u=(ΆkPu#:ebh(A 9nFUAB8?pQS=x~-[AQ(.."ͯ W]{%?xTNv#Fd ᆛo␃`\w(),bҔqs| w$KߛϙV:;@~pq$N>\9T<ʱ}ٲe'?94g}M<`0˒;%h4JQQFoNOsȳgdF;1bDf;cY1g "+gI#{IM{{;uCزm;V2NUUwqGEqxꩧ8c)**Jmm-6lA&Uc0S oQVVĉwvQG+S4~s5N<-*=]QT=CA?|H^2v&P4dW( m=@w;ںAx%$hGMzX(bA@t)*OT zyKsأ'~u]Uzb %z:X] XPY^mAe}D=?>>?m_ط3NW4OK~䤀E7 &jEOs*++Yr%'NIJ> >'23Qр CoeApm˹ ptFcGVg8_h{yoV7|nV\r-59<蠃{sɄ;yK7>~*PQ\ncb;T O|?I---L4)i3l0C=D gy'|ŋsw3o<~4> cԨQX"+gyi#Gg?&Lh4⃖eѯ6*J(,*'"0?i)B8e˖qUWr|/؍yoq|ժUxǀ6lmmm=￟OZ^x.(vG@fRe5eee seYR):;;ikkx6}s>\ Ys_p%_ƀ}rE#m8⺮xgKRb۶HaY綳k<>^ҽ- W~uE.l)/+-r@~SO?]TǗI͐:X?ʔ #ȅW"K@&L;>B /a#/Ff&vʉ}?/{c?JPMO=BJ*]rgȱG" !aHgړ3O;Q5GěGyԣޙw&>~T[ZwpP/|M)dҕ2a>',s W_)r=Nm졻ٯ ?2KN2)ygK2t#DD$H9g~W;=9WY<+n@3WnrăAth~5tةlDqYLNʡ4l;'d&8nڀ˺%( g>Fg[3| Uj|/o]Uxs?</Y$Wi߿gtʏqpڀ$A8#>uu {oΑ_ kʶt"#.;Ld؈1^]Xk/9[n/5ᓦb)?H]iu7Z U~ Nߞ.)L?G΋gӶڮ@h2ʼn6 u""RQZ$.g+#kgB'&%AiILL!^}O~sU #{Iyy0b !s27e`?6b?Kmm uݢiLqR[@:[[;o\2N"!rɹ:f(ЃE3rk|s7e#䉿?=yOwx:hsWn-7]/M_g3xw~#Nˎm|ǝ~ɨ!%R3PvQȃ@V,#nj|rmʰo,;ܝyF ۭ+'_a.uoȌs r?xg\KT:7g?!=Ds ]O;[,* 7\}z՗],<{(_| ュU;}>{^zI`tlضa9/,-}5k55*O"pX9i""0}d\0,?񕢩=|\u]%ѸIN><WK(c7g?&>m?4eА%ֺ!cZ~OUusȷO8BByrQwÆ =1[.<7#?K νn͒pX<ב|[mUA19{?͝wڱˠ%KEټ^.N9g^_&p#rDjk3dr-fITF|2z3 ȖGȃ3'<3""2'@2kFFܷV o11=?/{l_/(kW,xҖ7EĖw=( D0.‘L6;nED!<ϯǗyQ}Iޭb[. nWS$@ JB4W{ormvk;' !r۽ٙgS~˰471.)T uD;wLJNr"H$jy{[ Cgd`̻V_,vۿV=ܮEEuÖpM H ~d:pŰ,rymBTO2$e^}8㢫XzmZvl^s=˲eYfJCS @oƝw_G3Yx&/~sy{xdF>yfy#>WVwl,YW _`}ajV\ƫƾ# ^U>||Er'ذ-ۯ{VmY=^~C}QU{n;Ss"ljg[sC>]٥'aԣĕW^!uBS e.qW j_+9Y<(UغZ,^'J *jb[/ @\u㟺%+|¸U θuU{Ľ#{iqqG.FMX@Xbg_ďj*qrfjċn~:[bЄŗ_|.{̫o!cwqϹ@w~/ #RU:`B+}.ֹs/+/Q>0a !<βwhnOvGn75ijLx@M>e]J κ6wÖx_r ĐAC jcbUŽ2kT;qUO X.2yKEu7!H)&mbGqϴB1__??W~y*.2LqS]] {EQz[dA#ŕ#Z2]rDjf(o{!A vسks5E!Q< 2G0`&^ RMAN ~UF[+Oj6EaN66_B; `R忿 g,GIH4 1=Ss։SYU\oga#oҳg֭^e˙2u2.&Ά=1տjL/ɷ^}Y 3O>UW\EwN9V [4ǔS7 /3O9~P>+]UdM?P =D7gq-n~NvG7>(-)UXd)SN7#XS@U,3?a%d^|Ĝ[mBmmY颕 u_Ա]sۢL=MNJ^A u]Sy]{{q}/gzٯ'=zeƌd`GS2 3fP=vm| =l%]>“Oaq$3c g{Bsqԉp&nr o $@ $@ o"o $@ $@jH[̄T "n-!Ih4}YD;BtiX HoGHq)%(In"0F@! ԓnKjC;~kd9dз}гg w.$ ыxE4G9dnxe2bF$Ŵ:BTUMN!I(m[%2+m`HM]$!P- wqJN<;,uǝ@t5XvTh>VJVm,a%1]tIEO an`'NewICb*$ r5ӧW{kxtֻ<F,C.$C&HrQdh,H8vlv^o+@޽qE444nz\.'Ç ZDSV>U|7sW| S K ~|,\DVaGUN #I:VL]BӁ㢹G,&ڬ( UQZTUFUUz}ES(#Xp=of{os-7_<Ow":뀃Ч'KSسN0:Io?{]~p9w/Ǡ(RBIȲ 8ؿi-8>G@0_|96%LLl]{Yq{5d${ $_ q+ KKYe >/")+ݻwaZfo4¬X~t5M,*4|v Sa0M(!ɢ,i 5|iL4eϞJ4JZj:a+IOFv;*‘66m^Je&dY"bV\yHLFYh C7q:]mNnFcc<ȊIRpT0qaHZb,_?4M!-#֖VbQ;iiՇ.bXlBbQjkf"$?DFTH(H@Sf0 @0*a"#cu1^8YKeTWa$kd5Bnmh>l(+VJܼx:`Kˈvf\)Pbo:0]74SPBd=6M%b@2:#ҡ?^'j$ 'ՓKbDu47pӮIy\]TVK2E7Fq[Crrv5HN, ik b 4rr߿?ҧ8Y4s_ex|WdggR\RDYyO;Cf*FxD#qk02 x>**2hP22:_=LaEJBͨZ-躊'9I"-z_#m(vn7 At#aF  (Dkk3%q՗ڇOQ; kY&<4x#ҳ37 582z` l*I u(#R"ģ:iA= $4*b%dCBuX2j bm/Du $O;?v!lD}UȲNsK .L)sn(nQvBuHM6mm$+=ZM^\7C D-geD_H(F}ڵ"NwbӜm.\LIvXd1 <_QT+ᨛuZfFo.}Ķ 4.wX=U dSk'emX, IIC}=Ng2vDX~4=! aDVH&/V l"㶴[& Q[LL71u jX% Ţ)lV0:Pm?G1)JAFv}RmsvQIdt:4F B P(aD:B#nGӴM{ej$9^$ SjČG}6Dk$4 dfy©r;w) '+HAxM4e+yx'((FM $ @E4m.޽͔"L$.rkF1sSi#X6dY1B*iąf`2PdP,\n$%=,vH4IIM!b 䤡e}شi#C")Q#L>j Eع]Q5'˗f2~( 0n=rA2IOK#œBjj*B!%'7}k4 =lv+8INLX,p ۃ@ 1$~?XYbL @@"Zc&,PcINkGRn6uulCQ d<@0VGFz&?7SrrxznJʷouIo`F{tBtϟ1 ÈVEQp:RUUEFFhK?ah'ћ(OgVE\IcVyMs|0 5?%fȫEx \*n>'%^YF\qŕ[>}Я_ʰ;lf{%GdYGL S_Qw47tldقM<)NS<0o$9F$$+;C4 ,S\kYMUd;6JFdRQda0@h=A(N7hQlIOK؀$%%Ir@-G)4z3h!| t=L,ڊdWMT0iգ pڵ0e$iL _"}*ϻuj bѐ%֦* ](S`&ua?`*NbzzLCSlX-6)NdTh#OC&Sklx}]鑖U3X&/*Uvn#g{nhk \[uuu p aÆo%dРA8N}Q8, i~+⣃nGUUZ[[IJJqݸ\@og{9̃MtDx .a\v,Ҟ\COnլ\>~,sߚ*DsаÜ@+fq#mL44֯_K>gD5clن@ac:$%{hm]HLPHMԺi*[lW&.5נGde呓2X@CC=i_906C]}#V-n5US[MvN6H`[珠GT'GQtjkhӷEIUŊ)SD8]EPR\1@׃TUCe JHNa{.23SY4@uhLe|=/Vވ3AuU K̳g`hH _>o+Æ0A2M0Lb40PTh$fw`Q(m0vaȠ۰X(oʰc{(ATn!3EłUvowF (:6! ~M$ArYC9*a$@TnA:_\s5y|]nwmmm<p 4PYYu]#ʹK +O裏n[oQFqi]wŹKFFF9Y8'̽p݄B!l6gK.a޽<ȲOmm-.}555p tTgzhMhXS tpE,))Hko :obe"B:|2-$'%޸P59rvgyLNUf׮}|9w>)ad$ hmΝ8|T,D"Qt#l~֭F4"6j@`rS=~uu XQT łȄBa4B[(DM P_- F4 TetkgdqUW0w]&27^}#9˵ x)L}} 8lB}]Əeƌ47Pc⤾{0⠁(,ĉihhaMۇݥ!R sQddK&z/Vo-T[FEš8Z@ 3;ق?݊V'䍐>6Et]'// 2n8.2}N"( @>oO>SYF;[o'Yf1~xjjj+9蠃838sضm&Mbʔ)]vMN4ep8B,X~ /}̴i8_Orr2s\r ,˝oh$Sr ގ+%q)Wym>.%P'/v<3NLffr$݁"9كghYeFZٲuT40|^ E5<W#Lo(bD"A<Q[SCSS#6͂D5\Vǝn! cX# )1 " G8㬳 Zؿw^r! MȪҲF Ie/XVLvڋfQp8촴xQU+Y.6ZZ] A^^>MMMۻ Łj%31Q|NCc-H}qY'K $k@+Z9$O(2H*)P$ Iu_0$!qo.p**+طg'V`+}1[[Y`u UBuQbtS2zTi*NqNJio0S0d|7e Bl޼SO=~O<rw._s5\#[':ӧO#O>L>c=޽{w~#//X,֩4MBP$I

# DNN+pXo^~m^{5>aN†7_$T;:)83G55#f1, hcz0HA^N.FdֵlZ -bmANSXՐY33&NM1 d͊b]"1*#P2.o3D#&ɂdQY躌:ڽ:B>N8kK.3n8nFNWgQg/9\NRSܨͪQ\ܓ'9s2xPY\.g&PO$%3# Dc,[hD'`6oEREjk###:LzF&I$8vXz H$JYRp%9:|0nh$i RRpXb,2hjlW>xz|L!>|mO>RU]ad-.9C6L!pxRihhČJL$aՊT˰ð<( C:VZcoi#௣Eٻ@^-gƃKܻO9$ˆԅlYE wBT iU#ፅdS)s DQ4ն2{_RSGd}(-Y3IE'nǣX(نæപHH!L@([ bN~:lԟv1DA4jG[Ylf겴駟t:cҤI1nnj]r%13dba޼y 0'|/lhhh<@s@ ]:X,̜9~O>3f 1M?~;4͊FaI!g4h(L[h~_ EU$݉j}{2`v;d禰f ƏZFyظiI^^!{*wT6d2҉S[[ ⱧѳW `trx<466NJBd?h_Z[f,VK/` bb\.t]GQUl6+"E"457v# H$ aIncx}Eݻ 4˶Y~={vaQy4T-MؕPh\t4FۈaD=;Qf,l3i@yBt'yRVvIM2 *aQAdEŒȱxh+I14 l&NJ"@vN b{o2̏JzBf.ŅT Avp2t! Ea61B)lذܬl INubZQUL''#?@ښd$A]]~l' 0(cHImMRXj[䜳Ϥwa3g¥_b :QexHMMg(쁢(Ģ1Z9qx[WC,tbZPUUchzf 5X ->nUiўtF۫HS!ʎώ21=z a#S D|ېU%5idžysM*ǐ% E6`bL<6f`*V, dE4!-]"K\ֶNnwJIdLTґPф05iRw|ZlvSѬv0bEuY,?׋ddd_S'%bjkf-a'm&*vbuL2_իW3j(>?^m[r܉'i QYYICCcǏ#aמlX)S&؄aaЄaLII,;w"- C$NJt?`4t5R@0KP@;XߜF,Gik3هu;g}'~+u}mT?x+g D="LŶ&jGEkߧIv%2e&ϊʊDU>2 B44>AFz 6FLa2w i鹌^LIq>׳lJXj1aoգ, ‚ˑASc= p:454`123i Y"H476ҳg>U =~zMHr8$ɤ5DWqepZ<"k- Bry yn{x{Yc׎ BTURc(B(1\=6f8F Yf2 s3Qijj(*:@`|!Q2r "D&cFcH~]Zl\vW_~elPG3)0;)5իصl ɱCRP,4lJ,p INC`JXl26J'VjvО@MFxR41h8J4.x୻ʥ/I$E䀄BpرDv 2(**f}_b!95l6tҷNrYWFAHx N7G$+;{5]&TX{G/b(BRd}[-6ҌX`[&ύN0B%D8A7bX- i`* 8?MX-2Rm_0F #=*S G"dee (Pq:X-vm yy[H,L=A2Pd MSi6qgƍqŋ1pzg_TIg c Uk'5> OYq9Hbtpp% ]a6v$lf->K`KUfzLf`wp8ݙ8!4M ~o mIvVb8ӧ ZL^N:%E,^籣bP#ɒKh61ppoM/@ԅ( nkafQY{{<9K)1zt Y C[ 7ղ9EӊâbT)[! jmSpYcm:$å"lYd)dLYAQd0"(wzXh$Չe2rR[ 7ݏaBA3$4S4F$ܜDOI)B&:DIkmHr<,K 5H#Av<{u^G{r܉,3?+K@qiIg!y}$P&حL9Hd)a$3|$sSӌj2Hʿ_7b(F Ѓh! Dl"Yܯ7;L 翽ye:p2 HNvXzR $T$G&ώ@80 X,H$YB``w9QTHuzt0b@RShi2ibz=j",&PY:z Mdfe jE  ,+!dŊMӷo-Z«iXϿ\_sPI(w>wsq+,dbQ$+(Fʠ"A?S'QCmg^5Yw(*̦gQvJNVN7N`يZj\J1;cѨCV(%ʲy5`b(f=tnL jG)e7I2&#ǔޠIXmZ%VHW( ]7:55XjH$)EAA 5}AٽgH8bh&ңGvj iB8nE%(((`bGHۂ,˄C4Cm6Uf޽ ??ƆNN/paJTYD kѻor 3w<JvOtDdSg{sֹOjh{iDYq YI7H-'9p8LkYh pڷ:C&rN\'@Ӆӕn-nwlA:I$bFZ|~Xx%6lBVTO̹3e]$4$ŋ;vl{2 UU!XTLقFܩ,>!h d%jD3I$R6`H(Ղap9MvvMeZ¡޻_n84-82S0c8[5N=fA]?ul,1?;aW`b'}x8#hjlQTX*٩GSc5z=.W'gR*| +N^N69iP;#?1 &АP0N^͹4 J?4X%;\Jˆ&iC3 j"_[+4L=E1eX,JUU-X}Z,~.'&d` i VVA'ښZNv;*(-CC]#5u |~n^iݱg2{zJ HvP=ڇgo=I!:j N3۟'9w._û@#ֿ+}r-1(MSqwq2t4SeaWy7n_ɒu{#78{ZygUeݖ]\s4>_c Ow9YgXO7v>&N_8Gnv~x8(pj #eI{}[SC^(vW5p#&OCoGdVN9Vr#l3ZiR<e%ƕLV̪ݭvBmxM~zT#w ?zΙ(8B{ [I$c9I Ihݘ,˝BNsEQ:8N #nۤ( X EQX,b$aǎ!HII!##T.WY6P_@Aӧeee 4 }q)+Ḡ#-@f)!&H:U#ݕȡ)+f 43j0]$[!*Tl۰k=NgoC[~\yk|[+`gx˄Z]8Wf}EXzgiO( s:II,r+ǯ=Υ5L;[t͵07`HI7gq+C=,L].6~{'ᬳb)3+J{ "FOߛYzidWrg?\Ic[~+ G^KNa~\|ީTVs WuŗME?]{ I8(9/eٟ39 tH̚@-Erj][ZZzqd2VShkʋO桧b<#ln& oP_Հ7ou+O>{yoeW[H1>oGOge~z7?̃ZVabFFxM|u"v>,MUs @l.;%oc8Oj?$mrfZ U$5vMR~p`f;1Gў"N$RvGے$u(Jg201MPd*,+] AZDA@dIFIgpx N%_x͆3IA$Lӈ[kuƝJTEAuh+H͎ b ѣGc&˖-KHk0i6:dY"!YH@4#1Q͢`]U1LTIBY|wPDK1ӆaF!dbu47 -4KGpL`!dfx~ Ixr$ $_:Pa (}ܳˉn%++N91L$YJS6N%)NFyon=׿sm͛9#w=؁9LVBKx2v$!|u渉&DݞQc,`O9Pr3ҫyG6<Yo{;zxzo\LaRN<{0 9g\.=!O=|7G6WOE6ְd *vWWȨcNjOWaM-={g⡇Um3wcM`A𽷰S|X\_PWWϩn_,XL0D%VӃIP_F8xiĢa,\hzxozƍ?3‡|JmQÇo_Hi~_-]JvA!e]Ӂ:>'IXaŽ>u v꽻5v<{lc隍dQ^ړײljvVV1a!2~lkl|֬[OZn!#wV[Bz!TLŞ9T7+.dkE%}mƙ O"_]usIdBpЀ !Z]~j] ً È;#'4&X U:fEG>-NG{JL_ 7,Yرcq:& "@D1$$YF:Yں"==~Pث9SQǯɐP,2aj\qd^v_FHU\dVK'?T)M%?5sarQcxi*p|AV}ᓎ8/,CKy-<~q;`^,bgB~{+weILÑԾ}]>y'j6z)ҳ2Y3bJ}&o$`C")ѢDÃДSIc*?uG}/~5XǼۛЫ|m}Ε|'Po52hlt\T[?j2sk/:<8V._ƌ7N1t#1˦¸ƛ 9xt#EShC1dن?;;úu먮fO>+dǎ0a.&LL $Tg}NZv.1_&[8S9帣[y[1GMg_e%}˙0r\oۙvQw3sBAÆKڼ4xQXYz'OWa 5c455g |>ڈD"B!bUbXsUo~?Riݱ( ,w~)~Ϸ߄,deeqG{ey܀saёݻjjjbݺutF=(**#c?p?.֭[7W_}Ν;ٿ>}0dzAvv6yyy˕XjO׃Â_ӷ_9^5ǟR1c9qjPQQA jtΒ_x/\Lna '~k#-=kא]P¥N_gΝʂs2P"yyz٧$Gykva6Jdy5QKJ}֮[ՕFpi'S[g!#'փj犫=AjzM5hl 25o> f-YEGcǎ ذr}5 9SO9]e%b.lw?N>,J2*gSGw wu7MfGEGǣ,Z| -NƱGN~6d.9Tމ/Π_v}[QXɧMyt~~^!6m?>cʕ.eϧQ^^NVVŤcZl?[{ 6߶lw8{a?}ρ}sߏW_&RbncZq\p8ʢ񘒒BRR"봴yfڵkٻw/߿n 5)vIUN;wd]Qρ2շsX,h;>)xߟ9[{8aL/>{n|+V7۳t&$I$]ϴL CbƌO~{L=(Mׇ{-[p10zhF 7 7<\O  0asNqd LbcXIgv4%D:?P@ @8Lkk+{졪 ׋瑒Bvv6YYY1qDƏOqqtr:b,N-ӑEO;KKXn=#N: իWX= mUlٺOFӏ.owzֆMq "K2v .#6Gφ8^ګ.Wp;`^ ]VNZJ%\sof)k[)ΉۖDA~9QLVR -O??{5^ze''4 :4O<^z)VbȐ!@ $@'~?-b֬YZ(L::??u]&.NKRsKS8hb$2?O~^~e***xBp\8 $@ ]wŽKAA- qadD=L.0tYUPo710 $0U$n M!-x[]vYrgRNAAAA2III]2 {:3ygw++t;OӥLnnnϟ߭K.[{lr}Q:|:>uvkcVnҥ]| 0[]?xrv[rÇVnϞ=]ܱcG2SLV7ܭܳ>Odžuis…:yyu+7k֬z 9<+9eʔn֬Yӥneޭx[{[t+ݢƍ9Cuv+/w+WZZO_|d%\ҭO?mlu)x=O+VV)ҭ38[_HOOqΜ9X< .[3gv+cZzww6O>.e yq01D@ vKB$ 7W7.y•>m ?W_|OxfňaSKUo@BÛB=D";ϴihjj[~=O8FfǎTWWwԳgOrss )7mD(̐*XVbNNbQWjj*lrm6꺔ݻ7YYYuFظqc6V+eee8NjjjRWzz:}% !tuV+))!//`0YMD"mv::ضm[(--% "Iv͛7ܥ\^! iHM6:۴lɡߏ$Il6lҥM>}F(lsƍ٦墼3i墲.mѫW6- 7n@YYnH$jyi<ѣGw M(++i]̤O>AN'۶mxd)//ks Iuu5;wRWvv6yfZZZ\}J(b ظqcIh4y޽{OII @3zƍ;,//pFlxN'eee]۩fqq1rC$6mL|}g]p:l߾(**MM6rnĶmۺrnuΉw 0شip;w:(6m"~񘕕E>}:By655D4RL۝??Q#bDpqO?^=0,شq-,UXL]M$*&YitgZn>>EzZc{ }& u&jkkD"۷Fb@Fp'9&Ύ;^4M ̲-#Mf#55dTUjZ 33f8cx9#ػwo9]xhhh`Q0O@;NKs`i6B(!T\M DtAzj* iHIMf㦶z@ $ Hȑ#q_;YF 4LPUdIDV`"BB2H`"~pǻRHRwDD #!/d8LWVx@oIu#@ mLD$LCGHUrI'p0ӉDvnqZ4q$!IdCeXJ\Hy|}쑀NLב Bt̀ شi.۶m#PPP@VVdggArr2nMp8l.1\&s`o.#AfPZa N Fjkk;WVV"2 bʔ)=SU__ۓJ-q0r-wӪfۏpQPZ¾m$=;NO?EҞr5PsyWпfT }y ɖm˟oaK_ ݚ͟x5m膙 $@?s!u@(LѬ_+k֬HMI!#5 g9ٹ-֐']"x%I$(HIB YIKE50P4 ՂNaڝ芆aQBٱuXwz(͂"[% EtD41I&וA6{oq%sUWs 7OTl6G/?'x"999(¾}0  HOO'==dRSSIJJjv~:k(ڍPH @ut]VFD"ax^illd߾}455ufdda}YRSSYp!pMY_ÄO#sݏz/2{$9v*waj<r'WWCMan mbf <xE^zA.[Ojj* | Lއ*%M W^<#}lذd!f0cقȠ_M :)M!%}xji[Ec]=>o+5u4Ԍ aD4iG~N.I$ {ҳ42RӰ-?̓q+KX2SijLR7BD1 Ţ*[o/ƭCia602HA0f>)XvTقJ`)v EaEl6+N*3ڝ'it=ӌ!'r9r$\X0>ݻw  B`ߡPp8L$<曙$7Svd˱X,l6v;. pt:ȠKH$B  FTVV2g6oa}<=| θeqٲn3}?|ezVnΔ#gAsםSŸIq"L@s=>r0nǘ|$;.poC<*g>Pb &@ ޗj>h2DjHLmC#q2Є,,$U[׳eFpG{D4Ղ(HIN´kM# -,[2:jٻ{7@@os rӻgoIIM&33̴t33HO$;;T\NiR!IR;!nrka& 2yx >!" lasQeD`_^!,ѻ"44yPp$7fȏJTFE#bQdEA,ʉKQ|Ckq3(H /iry1ydzE8U,Ux<\x@ܫ~ҤITWW'n~ $@? H}0v *M 8nΜ yKc0|d2R;4Ϻ/row~ 1#B5ڝSdRH縫.$ɞFm(EAd后jь"YE  ZǐZ6SW{`I52i\z;|"L8Jzb"q/t6Yǟ|FawtPsһ zg9x%}+/ysdke-x2r9|$M~Q i$*+;+9=zsGϲuw yyihKggNn $@ $]X,?8H'S26 *6a릍' e29'ÙqZv&X5hALfmIG6n<5 x8ؑΩW_+=ʍo7\͹\ΰr/LMeȱ2HqusM İVhal祡qrF z %Rʞ_iSJa܎XrZYlڠ3l8 8l0 Ni[˛5^UEp>ek;w9ϦrB9|Zjٵm5\9q|GXUo3O?zwgr sIO>9k94nN;vϤみ?3fղżӼև(z*+m6.]ꍛj&> N ={b]Ki>H$B,4 YAKK B~^;89 ?3=LC uqv]X`aqwww @ h2=]Qݝ%oHOOuWխ{}yub`)δi纶ۧi&pH$aOl1Ms,V ``0(`0} R۷?FMӈ4tl{N=1>ީ۟S(pۻwt]cX}뺽ЧuujN{nmc89q{ڥ}v4m '{mmm;mX,aԵKm3.{fR.;{ƶ̴v?Km ;|fRS=3;j;:3ۻN;Xvo/S$v(Jd{Q\\̲e8ymw^yu7-\vk,xVl给_qx_ƛq_N  0*,KEvxA00C 'I#̄(]l䎳gq^sؐ'ٷ˽kOeBz)YOH-@@lÿ"GG}pv)Jozձ6ZUE_ϫ=[6iC3@/Cv}{-`bH]v琶3'x,&(<#hjd 5ד X[Z0-Qh^y* 9QUEQQd ]hndx=cQb(l78 IģX dAS5u4Z.%dI"9ȣе8rQd)Lk[B[>~?^x!CmCQx<\wu3ߏڕ)ǟ|HNO->%ٖ>޶ۧݾ\*~{3u#}c{ۧ?}m61n R۷gvOS6윶wvv]f?纪vѽ6H=,,^owec>->qLήts)Smvܶ](۽3qg;mS /_{ic%4-HtV^ͭwȑ#3gNZ=9e%\2z,ձ&nQ淟auBi~tR!F׼RB,ig9w]Nv_/S'vyL9X8O$ֈ:L2sxOx{=rfr{oQg0-ĸiKXhr7rn&P\2'$ C`u*5x {sP1^$Dd\BELw#hyΞ_o ya$b:!<!Z1'CT9 컷ǏwsOGCTOSإ;AA4%,z|K:|('z=4-T,ZFee%{.'NN`}ISVh߁9TUrQYY;C~83]W`WXAAAas p"h UUi:9x]NZdPevͲ?p[$$% SCc}=RRT@Ֆ͘B^N6@_V&xhh'#CIvtFgt'R 85h?U 񞒯lsoN8궻i8zEqqNsۗ\ve4Ws٧3xLu&v8]v9zZq+nJ 2D C3']1 K,<*&G'`Ei bqLSGV @<dtӴ'd,)H!jXH$JȢ(H亊Ә?IǫҚPFt]O)xҾMrz#''vJ޽ikk6UUihhr3;VVV(ӧdee1pp 5^~ t+rK9K﴿-'ađ,Hc}*`f B,,Ĉ!Ō'F"!pp0@,O[H8D5FCoAc1B umt:.]0rc¤p<}|/8n\?i E ;e-70W^~\~! x77Rq)WYddxEvFgtƯݺu;7^Nؑc[T~RFH)cT477sgpB^xKE `Сyۋl̙?Oii)\r yyy;󑕕әz\y|W|2ɩJYYY.(֢*h㯨੧'VO>;=g_N?;S7e@[;Ͼ'Eϑ#))/Ǔʨn'eG1p-   DBb0`-#GHc$1bFB S+ "b% q9frerpNY>>nNՁ$3;|NTXdzq3?uiO/rN=ߧLɓ۷/MMMNO>aDz,x&NK'dYf͚53iG#a{V)FTp8]8@~^.-ש!H2@@bn{^ظ%[3PhhZ6-$+$b1:ngtFgĖ*b48M$D"OvvvDު}q[_7noEoF8,JˉB.akF~~~Bj#b.x$>7_5 ".Չtxdfdqq:|. (\n.*EA~>n ,0 IRe4mxEI"H Ӵקwzs<\{}RF{7g!J%5,ى'Amu-= <|4-VLQC9eyQ 3A؄$++<_N{7̳'WYM&f~Wߟo}Yb耞~ۿ9yk9y/cO BkkS`٪h°#k螟]wEqoKF DSk’.v=4oүwwb?nEOI:u^~yToǟtGvMs?nϾGzytA.fI#keS}waKWн|gϞ=:t(;x`TLD7 =%+2j,[Jt%C1ww4 *Ņy|̙̚5LCZêU )**icCׂllYXX.~i~G j+BRa/{a0t+<|2$KcY䅻鼣8P[r¾CfϹ³OW9xh瞦!o>q'cF |spocK] yxŷyLJ 3?A ڭzD).⓷^/IK7{L̄IqS]~:7^s/"~LOOWӻ(1Ɛe0[sNd d8ef?P[.qzGFFwO?`РAD"NgMop\ѵ3:3~WѥK|I_KV$I`Q___ƍҥ ---BBA '+scOC+x㕧y[bHI69]ݣ+ꫯ(//gĉ;,H$hllɀwGE*mYVzv}>aKMRpA?'CELDQ.&MȿnYc&X[^8ͧpyyLBy$q$C&/lًPc.Of3QԅO=7nt(mi9']u/7s0݋8qsx8l/ U"ƨ(Dö@KK3o(,Z=zryOx72nxѻ;O]mUU[*m,OɘSh=4yE>LJҺpzfpоuwgxyYNq}9%e<[R@8`,`͗=fo%:[~,9} Fe>\tEp J;zh,Y¼yhkka)5v?v$ KfFdm/VVnF׍?3:.|^oZ5 ͥP(!ByGi%|3#kso߰)1Xr1cMd>5 ۍ鱕[ozykl;;oEؿ%8i 0^|UQRRt0g<zQn>T,, aAW#TD [usa5g2y$N9v K- aE UPB΢?Ilذ=z駟{EkUUU_~TV\IAA?x՛3ϼXs>Q:,okya]~QPPkF8SOݡ쮊RnVrss9SvLf>/H /'Pd Ӵ3 ᵳMVBam8wq^x!ֲy뭷f„ ;IZff&~:wyS*@T39i !y E_0<֮^%i@Iyi1bž %Q@2~ @%uEv)-wC{` K^Ɍ3kȲřg&\@, (יm@V^f;ējtdpb{wf3jD;팎rPꫯ{)++plj躞f HI0ж8mS(J ^SBE Bdee2vXDQL[~i?41) ﷶ8TQ#3j`)n'3!n?ݹkEQ~be֙;i Rq W^g_~ˠ(r+p\x8z#Xe՜8ÙIgQ2r,^0 Ys6ڗ{4zk15\Oz7 ܰ7L{g޿$ǦM8KYr%pD"A0Lh$Vlvy<ΐ#KL<EQ޽;gqF:^7w7Qq͛=z4gϦtۿ/iv05; MN!roE=Kbl^em/VH[EHmÚ l{R&I?}Nt:ii-VTTZB!;zw/Lpt:;tԏKx;'}>yᮻ>汧`ӊe ҍ?~n͞Bo?'_q4ƿe-*{(eղⓙzn#M =2+ qS[<&DzxeC'wצ;ޙMo#Uжi&Ea#77 _ (**pT]qݸ\tVղf(BӴK$Iqӧl޼X,ݬlVV~!999L2ea8rss;/.Lm*C4:_̝N,EI$+$+ R'w[ƥ0e6;xJ`,QLo l `vහnbYI2֊A,LL#Ul˲]:U_#Hhq\7OA7Ug^zqmFӶƿ44#Fp8:T)w;˻r뭷tgtRahA" G&}F|6|{nM3ƠŬOc:Xjޞ ny442c3_T‘L$X+A^%2W{ˡ eωcY㎻ޞwW$"z!"4_,**?{cSOemiV@:˻m@󑑑A,cΜ9\s-+<+V۽Cۯ_?~&LҥK8p`E ygbUU1-UVR "`u)ޞW#"&`Px^4D7t!IdRKH'OADH&Br6vDMfiLTnЍt4Yl>&01-3QS~mӶ%K2mM>Y,3Y-B^JOzB!ȲrϕY(***m+2'0 Gs z1cCΪw]'mA0̩>IS&z6lƍF_ѩ+Fyyyy;1rHwa/ʣ(Nn];X+yA;o&24G! D-+%0M0`XMcc3malذK0e%-a-,$+6kskf]@gl+B=$&[;;IN9$DA@%D+ )i{Q AQx<Ջn`4g&3ĭwۇ=h:S?zs=L6mʧ~J=޽{'n``}tV;qATɗr 'p#gw=|*_7#3imP49c滙WbĐꚂܳ?`2@W^M]` N vfL"'ǥ3bB>~ Þ{Iaa!(iZ9-墶Ͱ$IiW6EQe9Rn:>sYj 6 EQ~ =ʸqz;-Z B2m4 |ݳ)A{)JɌ^ UQ񷴱pr/YFkx@WQR@VApJVJ-VΫ$v4z1WJ[;Sm&ngS βv7 SmiV7 dfz:b7FnFq-B4@4L$KDlqv$,,4ðsR%30#eG5 ;g^/ /'s' MD7~QGȻ/f͢eu١֋YQd3pIc7y8 'M˾[oBU|r bEf-{‘ɉ]:MHtƟ*be׿6 iiioFZZZhmmM RiiꦲYYY瓓C^^]veڴii@[SS]wݵDP(b;5 jjjgϞ\{1'k"0uƟ& kV qX"+]*eQYMTնq8]N2Hi A3 ]:aD4 :\￟}ݗ]Eb"wU$)#UUh;5u\|G*S} \s8\^N؋698[L\~ޘ@aQYYCbbެ 1z[('7\-3dgfS[]=7ܙ撓nI2Ù@ǒL|mVm_{&i[Y ZYd(uY$" , @YTq( B::>D3gҷo_ng"]KyǞ ^NެM6uo[59.8j*+Ü$8ǽ2(@a/gA>snx}Mm䄃o˹rOy{8l-+7)U 2r13C;["e ɶԙ(JIΒ )|XҥKilldӦM8EQ?>999 JYd3o %rC 4raϣ0aTIw?c69Egؚi7=YW>WeԀ>du+qEOfd5W&BMy%H={hŎv}aѢEuٴi%%%Ջ#G@SSgϦX,ҥK1blٲm@E 5[rRheɢ?`Z?>6LC  ӷoO+khpaiN"(PMEyi>˗oAYK"lе[˗/`q@(D$Mtͮ 4^[G I1-#c:* &t9%I"@%0 4DDH`U,Y`QDP$na!"jnCu^ۻVmH2E*-< fk'HrnD!9Nr-2I]fh ĤF$8eyI~"z Mp+괶b. ,[Syg^>Lz';G??xe9X$a>.|K} CϹϭO{W⿱coWS]%_> [?Xː=#Q2=3ۍ\qO{^Ai H|JLJt>[;S6l-]IVȑ'%SN8ӎ'i)_o8$ _7d Z?Yr8lڴ UU)))I/o=z[n`ٲei9źu8dJ*4MZ[[YjUZΐTUUyӧO.E[[mjE-$Y2JL8eʉ,Xfh8E On $!"zN1˫a26 J Ee_1v0.^I(`'?rײ% Q2Pڥcxp,Scd2ϒ6`$ II$3a 0 QE HYil{_[B7qVbф(`hQ#ΔRڥ K qt݃#Nϗ;5 ;Y4*#i,ڳl2gƭ7aYB` ` ,) bQIcԈ񨯯gƌ_CRRR(GAAA.Ti%F Db/"e54q?:yW$"!ZAeAvAMx8먩ZF>=.-:^{ttܵwP3Iqt(ic;':3:ch>)Xp7ob8*m~ Gx]@ѪfNNO<ҕ.Ć$٫¿ݶoܶ醉n6نiNQ(w}@P[Ȑރp46PW_.X`H;hKI5krK|bkH4̧yњ`"J2ѥKynF/0MTUDl,erk[jsXu4&H"fr-&"aȶk6B-ӰUܪ¬g܌2dϤ><{S8I eԆiaZ޿`n^+MV*kzT!H~R=+AdPW׊UL$"ʞ[:SNࡇ%);rH]`& C07FO4*F_kXRD<$#R݃ƣXHbf~1]74ӁGwFgt ,ˢlF)IiRUu'b12}>,"Aq D"]0(((F222~ UUñS兵kײ{sG2lذ?uK.MGM;}yo{Ȩ@qa!e]Jlټ&2 FdDBFt$Tk#me}+odz ZBK{a($"i 3a`ȶ㙡$)anbbR֖L Ad00t ED0QlG3 ð l_NJva"('tzML LEq\1dxE\NB NHi.vdzYh)kVpڀ]3<>WIHd=)YfYn"%e39i!N,ˍE"tu֬AD"( )I˅e 5 2i\W"I"Z"Awuuk?FOzz݋>BxmkdS\@m]"%s{NEg6QSS΁Ѐcر <$ɖFq4M0}jyMeEr7ndΜ9,YX,ȑ#ywe!Q^^NAA & B)#BZ4q*֓Vϗϼy~X$i*$jRe-ۃӦ?Bu[Š=P [7#nDK$阢"XWݮNq-k5X{w_.D,KA4$D~~;;_ČD +*;27Dѫ'}#H |Ɔ Ӵ%;L~<p0E[?ײl`!b0L[Vl Q/`I $؅evVΦ"˘I5H~f^))+GvK5JW dl_Xá L,B-dx,2221 6A4DQrb&a tF(鈢HMM -~\x4LɀhP($]FJ^[&arp8~$I/TooǗa=aXfnߏL_i^HDKK $qRXX|׼K,YM6QYYᠤq\i]X4 ôH]]XRH~8p {999|޶j=---L2EQvMt:illdݺuqFAwW%RLST༓gYj1%c'GliS( ,c$(p5ەPɇk3Èı2L4k!Zׯ$axPg ꤮&viaH ӕM!-[=sn=1ߕcN) R7nGeM=uT_͆KL1i T-,tdKX"+Y  KܗN[I&! jC-XŠek,?0 <>/I!f 8 0MO?I.H & C󦄡Y5%uFD*+i%T%Zc:NQA$,hhXg]lڴ MvI7ҰxbJJJp8 @qq1s'N䨣q-rssOYp!W\qw*..ԋ~"#^֭\vv i<3x`JJJ6lp`FiߙRLHȲ@QDQ$S]]˩fҥqN?ZMN&vU,..fp \}Ji~x.,\̽ϼGo4dH,@P$LH#Z I T-YPT#véh45 P͕l|MNvϟŖ-0EKfjL)+* P,šu0(aFqivHV5!=fs "$Dl I+?owAu.f%tpFN1 A5 MQD˴7(*iNEIm.ӁqE'{a-Lk:OƑeQi %7@eӑVzeٖ$C@$e)9lmHv$]\Dd6:٪Xpd!H U dIL[F=,X^J[[\pt0M\NVxeyyy_?nӉCOF4 _ua]= W_}E}}=̝;,~?ݺu\n7^geR^@&jkkY~=K,!9s8蠃l%Kvѣk<ED?KB?hTodt ^KiN "vKXN$ 7i [q@ WE>H `"Y環 yg2q4a™Xaho$QJLä$BIa-liw|dL]Z@Ro0tn̝E![%EJԤwxq'ݐj IP& `sh%IJk꺞;l aRcv1Q%:Wz4(v4%AJ4m0n2Uf%&[[-HkyHbYi nI1I#DCJyѶFf>3[t8S^wu]U0u-JKKAgЁ|I[mTU$-^Ohqhm6U\{h'2dB h;KCrEGKUeiBk*8b8\3:7˲t֍?x%7ͩg틅瞝I}m3L""^g@EvF1t"aA`L&&?A3 ,^:cvš{[_f"9 F{R'41- YRɣ{awFPEaTbA̝{%X2W fJ;4Ȳ BH;udJc6{{?̳4'ۛ!}hF~E>MCᮻO>޵{b N=T*++6m'x" y)y ./ÆdڴXzGii&FE/x+箻xksp?OG/f#UJ!}iw\ŵFNnzztI8b/(ҍVrɇuGS +ygױ6rNWZQ6IcpiGf>q4 Juuv릅NYYY-KELL'tR[HmK;o]}#BILQ\˸o`:s@0nd r*ǟq>u"-M mOe@ @VVcX( H͛7&7kY:"-m nm6LiI.TZ8"!xZjfHRFTC)*̡"3 Ґ$x#Ey2<$>Y2d qnlXشO$T4Yq3Vt9/>]ѵk7m5uM*}3ȡ^\Mfqvg 45I˝ugc},V^MMM ."MMM2,շQ^^|g[oe͚5{(-- /3<_|awq@mm-a$2Ceza?V% ˹lشy61pWdy;Pf<_Kk݉iq(}27Vw]I1S{ON@ cV<2&uD$batMg|tagOL·`pٿΥn"C+7/>%w#n |dYK.Q\\̔)Sׯ&Lrmx@ a806Pֻ; Ͼ@)\MsillDУ||YdNB׉ L 5˳M? V!Wr1kwd Evу^AK:M-PuDaWd S* fbȐ! 2dtX{w 0f|I}*7k7 mA:vXcEwyɓw}$_Z|= 92I:rd9Zst|pw4=*ygߓY jpŏS 7x'=\Wkrڅ2|Tяyl\2Z8}}jjYu肃k,OB lGt LYr%p̋/k`,3~iTUVo#aPYY󓊔ÇGQ|TWWsp'מXtk3@F U@`ݖ:ЭʑGdcU#/5PK$c'3iϞ<_V,+84K &d")dtf)+n>;8?pZ[[2+,%qGlY 5&}_Nq =9}9껱ޤP1(=,9q},z Xv)7H>LrK| Q:) YGEE.4M'//綵:̟? 8mmUQe-,[1ct--,?.ADbwki,onJO=\`* $uzۻ?48S V.az+r oa:cFac O>!/ /8n]Tnq=wL&t&tr) zU}q 9_W+ dȶ*F7BaQ.NM mjλe,>kWiDќw)TWm$;+ƍm x5JX:{,cY%%ar~SUV2o#33aÆpunժU\x.=e=τMEgU!;{ك7ƛ XL٫;d٢_'S-Ǔ% HrB-ʠwA)K7C)%d!Yn QBĴXEô@n[Ai-BL _Ť), + |SaPSٳijlFVv 70gH]:*k& m$Hr:҉gupl!)g]:*FJaA>Xm+x ۀNCSaڒa;eOˣ>,\'DKv" ǰ~\q+, )[Ubg+2e"h|>sΥK.L8q7miiɀvfa+ uII”43 ̜~`":e)rt+CY;ݻl 'E"*J 5nF[F`ZO8@/&W0דM)^]wj)OfFޜ,rKPR`F"HDkچV..ȝE0JvV&bȒaXXMJ;aGdbSRm%QQ #IN.;MۺeX6,KKiک}!yNEi"MDuecYQ$H cB2ogs***Xf DbIDuu5dddꫯf̙x^:(6n\b8z-ƍǰa8餓ҙବ,-Z+%\_J! @.8%CwG>p;p2~x~3f~ Ś u$ z]OhxUu(( DPU뮻.YfΝ˺uhjj9EAUUTUow2y0 D"A"@44MCUU ѣzb&hHvvNWdY&LssN{.=w( 82YqfE]8Ex]N,'^$!BXO >:?`SPVP׳7zrJq:TIɔP$gJQ \,LT,AS_1 ݯ1a!=zfE]B3`b&bv9%4ɢO>TtGYYl,KIn-U~д>_^ZPҥːe"v_D""+I3--tC%% SD46ښFVDQ4m;4I"`aN/ng&eچ,Iբ( x<Ct.'Y>MtZNcS q:EU$ID [oa=ôiزe/iѣGSPXWJsc3ʯQfnhẁHĉD"qg*477#NWn2DQ⏄-,Fy e (//GUUVvDtl>88HƎD @E233%L[}?0.p'kW-{7ワ u|Ilڴ)=!>|8Ç&uM%?Z 7W<FVQ躎 77!qi&MXU`+3iH%H#˹d8+et'?+y\&KUn%$ְt۴J$DA"Yʜ-fVAN=i]bVܼr$Q5i&ev3{O G擛 Va2)KP]%߼AOsqccM1 K}C+[6# *"2`"}n5fͻ +*Xnx3Ȳ,1Q?d CgXI0p``Fq8 ,$;;DB#S\\`¡0~|>yx[RV̠6,_zŋt9ck$!$05dYATU!ХK4M0 $rq:DrrsH$䒛K,%??QH$4r,x<ת2>,ˬ['2gA浪,DvicߊHݍiJʩ~'l#I]*KN%ˣгP6ng}yG<+`ԩуMΧuiPmY:cc8ݾ4=y~X(l>g$3|00x1} Nxw8Sꪫˍ X4Փ,$՝)R~%_y (5I*G5W< 1#='n:. p*O\v#slq(͑g@cO#Ödկ Tu1~x";{a7P\\Lvv6R(wƯ>_&w}xnlʊ|"&LW_MeeegA#33375jZap8~C]9]LRQB79C(FaQNzW5_kC(5ݺ=e+fXDdA^/ , PRRB,C$ )++:YQm'v5{'erO[ ㎣)**baO_ڝ\7Ue_Λ7r&OCۯ_?~&LҥK8p.>vln( 7N^@}=Z8LZ_3wl\G,* +N )N;Řs!%ngLKl˦mZ` 2Dj%JC{q>;؇C_r=Y/>3i>HN|Z%z'P0YtRl)f8ql_"a7nFMrK U'7;" ؼAYEJ&@+y]4@Ex|3G {xaT˩3rHϟ=ȑ#9tҭ[vyvZkX>1Y}-EYJ(>Iw[;#⮃0lM[6r[әkt0^Ai؛Ԅ111hHOmO^/]1~II Çg\|Ō5 ɨQ۷/MMmHyy6+iv0yIi_< wLG5]ymAUel=O=\ [('hrس) nOÁ)~7|Cmm-reeeb1bRUdkL;bͰY 1!ހ>-B;wZC:U<im3%ȪP(+ k֬q 0X,Bvv| C'ټoblSd3Y44rUgy1c:S޻>ԩٲ~J㑅$D"qv=FuukA/6Ӎ+3yyydffRRRB=͒n-9}$ħmϋ>/?Af 9d: ߇ٟȉgyr}9G_/t= o=A>1b贿󇩋dq'qSgvJ ?zAiX!=O-2riX3xO>-[qX̧!S}$z9ڊ`$R 履~Jff&ÇaZvv6s᫯/xn.cƌB|)'7٥<\VWf:BA;#"en8vNL`[9ӫOW;p7Uߖ^Da,COx3\B X&5hmQEdd A~qx(bX7q=x<E([w~R#|f~2ŋrigڊ >`_I1Lp$B<C[I$4P\R|2 +4 > J%l/h uUu.c7Fc>STڋp h&eLs#r:Dѡ`&tZꖅ:l$ݠKy9?wqE%Dr [)Eُ$dff UAy{(B8J0:˖aͪy"$t=SWbIАh2Ae̓O>sƌL:SO=}ݗn[ZZʭJnn.zEQDef͚aߟ[n'2O,"qe3O_t M8}ɬw_bPlA_?4Mz?ڗgny_&0?>z6`YL>zE~A>W6T6H$g >QFRWWGs  RE{g҇|>x7&.̖_/?KÖyYoesYj)+C[ڇzR_qsMw1l3(j̛3W૏A=8eQ{(w`;M0=IGaGq ,`Ŋ7 6x5jPk .(bv恣?G$xљ/d˖VMۍk9aRlܹ <̹l/:9̧((Hi82,_y^{^4 B\Σ%I?rBc Kv"GmAB! (;~vʐ!C9rsZ2Ћ\`rC))؍=QQ3Luc#Etrf:&&"Ɍv*J~F#;DatN>$ re(L=\ rYb>Dv @a[oi-(H@F ¬p3ztVeIȒT 4hmAT"8,Lh%66Zn/vSY+V0qވCULܼ,dִu{|~Z[2Ez ; E$KBD~Z5 \./^_Ǟә Xr9(v/*B[l% ˰LMVěh=zSTTNC8|d/r N'+ ̄ ?MӐZEUUgsgu!xmmxL7l !a," rL2Ν آ;4~v&DD3fLzkÆ ,\w=9:Z.={HhZ7> {o@ HEEuuu)$k׮￧_~qDz gagO$k<8n|(s7s7aD$m#F7e-r<74?@.x~V!g>{r2f\!L;^\B=&ճGL80Jbҹ5W- (+J_+0уjcܸq\.^:\K@M$7eȐrF"36[L)n]1,EvY۟8aܙ93g<9YffL)ۛio4|tN ,'jwв0a=5x AU=!p:%g@6R ` "ͧЧ_iʮB4N'UUU<<58@UEkm6c)t ~W.i$-#u0 ZM2Vדj! $IimmF"gР!l۶#ǔb6 Mf0zRYtҼ-J2!gt]ƴ҄"ADBqHd$%C* d]C$$FTU&b;T:Tb;8"(,!Lu6pHCuӸy7Cbn.x<}K.*_e۩l2f<tw +,NFA0̠Qem3q: 6mJv+6Hg۰ |EEOAG.pOD2L;ޑGG4& O$p8XA;qg٤Յ Q^>JT`æUC=LNrB!9eeϗ_Bux<躎(IxhcO_:!czx-˃?97\|%p;\w떄8r<^tg׼[zę2K萠j$'lIR}]LddY4h'N$=&Ľ7~~iM[rL+0R]8 <0eǞäдq!^ o<8:A;쁳lPmWcܤC?~寿/o/̞=6wnFV1ԯpߞ^K(HCClCH$pkܱ kIUynVmy lx2D(LSSLɓ'SXXHgg'D"\HDL8$Z,B7'$&(SEj $H_ޠ~`$n5P?)\.GVPitx! zy7̚5k;v,wqկXb'NܯP9t9L=dyw+ްÒ 1Lj0L˰py@,t2mXޗi"I"<T2멪,D457+~$dŮ* YoNu;1 Ņ+SB(@躆(tuɦjU ptځV"),w >y,{ ha֬yQiO~tvL"'URR~s24h M.ri(UU0 H$'EIi͈dq<$.JF\p$N# zz 09A7-DBN v ai *vxV;̙Gp N8dlVCU*+HD#D`SSF$Y"QA |X,ƪUX~=}nk&Lc=!CsbsSkNeNʼnhp/,Id3)a)bӅgp.b0WF'вp8ѵ LߞKb hl,d2qSO=E0os%`;kJH{Q]0nW9T/X&DQZPD↿$ zH<'\adi\?l<I"۟C$Θ٧UKS4-yAveȋ.U :BȲee1 ck1vX,o_|N2HV t-d=x>p-3'o>O6!IvU&FEI4hlYɷW~ LӠ4?bfN"w$勍HE,4 >Q4P$QW*6fsKR%PPidm zw8{5j---8aPYYI[[_\zIp+]KHDzRPVX6%Θ{5]&V~$ #2mq]?#OH$0 N;-6qD"a VM`xw5Na5zi\K? D7}Ww.F̌I YGW((yiSN9L&رcF(;DHi88iRV4+dwEL6swR__z}:ih ώ "X&rNݘWnTt= {@@QS2i$^&MD]]~?,,G:;;Q"TXAd( EQI%"RIJk$t'O躎$q|E"%pN\.)݅ *1MHQqܤRiŁ( hH\nL@7"n,!'b*]q! ) \NT2K'DA˨#ET<5R¶5=yṭlM2E M1[+2F`&A͸g+%fP- I:21zvivb%WqEIDP+7{U}w}f}~Η H-/M_ka477S^QdO>_iΓ_$IbڵvIdN{E !]t`J=!G{ M,Ñ(N-DW9W)ك>d-byIC'}x^e`fx42Sv-ʒa<DڹnÁˏgE @{{;yyi2tr*t* hIڷA&۶n垾뉦 nj[GJ۱clD>}:~ >iGmm-ӧO?  FظCAr睿fѨ͐|kp:m{yN=$ 㡪~2qDۂ,{+%Es {E=@&SRZ?7;XL#D&N~;_+mzeyj%.I61-,D'BIhDr;hmipeudQObÖMlYhҲmWR/fH]!>V C[}oKf@|qg`Y7o[ @ʜ9s۫t2(Z>040R@3fbY@vw| `/ X< ibh@,kYEUxt܊ 0ifUquD?r`>OkX]|hko'Lȉn@5Mg؈>%r@X,ƤIx|҄(:H~T$0Mⱇoyfۤ~T}~Dg=!H;Ȥj>qNJ(*,`[6V, x(((WAs#^0eޱsyorǝ`uuiipRRX]wᧀO`iGfE_b(M*k9IPRXK[$]SH4э,)^VγˮMqO@kNr9U~d-Vx[ŷ[60(`WrMdNzwν^7#y3+ㅷJ0-9x2`p-kI8#Hv]>riD;i:7T3x.j8`y}'ﺺ:?p뭷fƎ_-1hHtsZv1y+/+5b:e ss-X#Ht, z""Ʉ(s;g~6,4 F-]X9C A0`I;b!`bYVװy}75@.evb$LQTr+2)`BLfl O,@GM6q=gȑ#wl,iL0"2l14k3_T۲,Q_E&B^8HxJQg[,2lH! 9Dd2tdwu/] SX6YhQq(ÍDOmWLf(?A@O2NPM+Rbh$Au  hڲQu3fTB Eeغnu*F9) (i;̉3'ڶ`sM߈"Ξzڎ!wL+Q[ygJQ4OjvƒH,)pG_ʤ (!ݰY_q2M UЍ,if? `sCiX\QE,(p=hhh;G!P0@^&a֬YTVVҡcRVXK,ÊQ8(yZKRZXK*@DY@vrWgHRƍ(o?;`3#ڶ?s,UR`ӿW n%PF~ïIup͙x?C]4/kqҙ͗ͦ%O#7_=oB4yE˲(.ٷ*ȳ):GfJߺl"xXv-tƍ9Xz5e1a„0n.Gr~v|`_ܯ؟xmq( .IRp:rsr+8]J?kqIT$&\.~w +u}ٳUR\.ǚG]62ZDrO#,NSzZ:7*O΄/{P: p _@`ۺ?yز~o=lڰ@а~%CJ$i?k>HWFE"Xt47\mVD )+YB#nO/G`^6,rNtB<$l'AvI$2oA[Fcʤ| 82h$(ʸG~o W>EIg^Nwi`!8r+jDtvexw;HiO}̟?˲hllR&2˖-ʽNkd:[er mۯէ44RQ2mmkT>l?~0?Cn^}%W7_R#S^>+e1i8ϾVthR1Ο-F?&Kc{x?fC$:",||?-w"_8jB<.#awѼ-AIlʱYeu~rTނa嗗~}w_ߋ\|D | 2Qz]&H/@lB. ӏ/$cv$E%2)8htɦ4aQ Yز;L_b2lwl8k?@U`jYI7Rl^ie'ᄏK/D&d9$RQX 8Ky9 dD"̜9dzt7Kss3o67nNܴ;銮 7`ܹ :t/t9氋x߱y%2Z꠺ޱd#kl+$3>OM@>ҽnWSəg`޼Qxo=|0}Ml,ho'DӮ`y;s32^߼]# ])%Lu,uSN{Ǘ7oxW-f^yWϬs9xFJ)X Tݿ;'/x(߽c8̙uFOqoSrYh dݼRL˜@2aoA3wl"<FM;CL:{Q"i/93?(ga-hŰIqY֯X-c0EN&CP(εߙi]~%>⤇>|9͟j^T*Ld2$ ~EݣH¿E;m5Ըߎ7nDbەqFn֯_?x?W5nHzQSvgǷl^v$_b;`xNrNPǭP<LDQ"/ے.X /l6ˍ7Ȍ3Ep8L&ؠ4 FË o%*茖dܰ,]W>Ul)@Pr>.sii$IaL?R:Rcrӽgah]ɷ~v@Y%@g}ж?75 0-XKb&-~Y8pe铸- ~go{ }.$ҁ`0(n6A)}7YOu_OFv|*jwPZyw|ٻQW s=k6lCNƒWѸb"pu!`V "Hs vM ɼV5PS? 9hjS/~_+|(Im#:_܊);EEQ(,,ܥJ(g.Ook{#?t'nvL](䡜(F94 ^<bl3g;+84sk[ykOv<w4}N !N}cc?{?BzV?[7NtiRT'SI~PU纓& ɘ˲fL2! 1qQ$31s$m?@{S~߼u *A:8{@]G:I&=tB"b2b񣵡7 ֳ)~ Tmv޳emt^y7bLi0[7b!yFs`q} .u,™'gAP,}e¿u CӼy%_9ǜ}8߽u됤, ܽw/ ?EiirwpYgpm1bl삩)%ˈ>)ٳ_x)--etwwt5kpꩧ8x{TՑ;Z2|A@l+aE,`a )r?kk(!۰Ae9YcBl%  @ײx " p%,IaZ}9g&IW@x?oHt+Ȳ €tٴᤓN`-XfrJ`O?KL4|eâUOsq?$I|Fҩ\AeD4}p@@m6FA8=dҩ=j[6BudO_V:|d\,3<]kٴq=5ջ$II;PŘ%V혲cوn6oތi\]nۇL@|IQc<+:`" Ѣ_ yeu]7p:`o(ltELް!4-,LFIy6 ,KBŒ̭ЏcW_>^鉴1v,ቫ9qRALG8H:C۸s;ÜrxKX3QW+BJXl9b$& e?YM8s磧r$?XK,`?>r I Exe7u eҤƌσEh+,A>py;mŏ,ydo{9hjJsy-B,R[Ǎ?cj>1z~1#/KUȥ?ۇv9"lF ǯ~S>ՖEiEEEx^4mǪ^ࡇ|$yiʘ ZH\Q*Ȃ(l"BѵQ Je^+),,r`nl99anɒ_$@ʱde6C+hyPl7$X=A´TYW1m!DQBQ%zDDAB׍>T6Km 0=Q袢 n;p Cm;y-O/,&&R^P].dy )Z B$c `!#b>U Hc""B@vݤ+C)2)$B!&z԰EEqdY0Ұxbjkk'i 'CxKǿ9hd }ʙϮP#O\͗5Hgl8ѣ LSWI2p$>\좫}tHq1wr?jCjo^ý@&Mg|1);A_™JIsDڡ9tO?cP|N; +ǭ; 2#gDW蓕"d2\Nfy2'ymz|"6&ҩ'HB]Uf̘.p8̒%K $0}H NL KQ4t8U:4|p= <\ -ZLÆswK7\tb#Jw6 %Mgmwh*8h']S4jc""YF.{5A^y{wra/ɐQ OE\d) _"eTo~ Sp:N|M:(Fo2uC|?u| t9tJ kq[1L](UO~[Zmg[9xX#@`_ DUDvns u`i:F(~'L,F j 4cXHz"R*H$5jdC5poo!@qZ`#6P,42: =#P%DMT"c[ R>Ux-N#Ht X2ec@qGe,엊 I$Q0,cl襪 m툢HYy)٬̦OE=LӠifZI]4qN$ ,KQDDDaBDU%u,B3 $ IuDl-j3-΋e#]_ͷzs\-9lt$mm^vV~ ,3G ncU,`Y^,4CM7r)sZӲH5ʺ:nz֠q3 WjkC48~LQDzgq1AgqwyW7oއ<`݌(t6XP)'E1q2kx]+&{*\ d~wu?5ӎH .v:1PH`"#T3.e˖vlURR’%K8(hmڴi퓢]y2i̩sòLrD]$O%+-eY|¿pS5r噿e[|q8ܬYs]K:{B;μ;x*5fQ#Ct55~-k $ hZU-ꡡu". %qja[)IJLbThD2LZKRIS|A`| Gs'sͿW>no_p%_{#(߻Ÿ@Xwx4.Aw9[9u$CQVVƙgIWW.yA1! Qԑd A0$ѲF664HHl&$30~YJOVV-uNr;< ym!d,>'$QoۻOQʱ9)}> B 2m} vA("" MV.ݢl!MHѰmxC N4?5x`jkky?HaQU f@BUVL5LġjKDe@D (N .@L[3d``QUR[+c2%Q:%D& ǻ 6k gwZ.mo-v \O?t:::غu+Ѽ-"0k,TUπfls^8T:v"@sF.<ںp]Gs1g#iތ$~=mtFJ&V3jpi?@n yw2f+eJTQg?.IՇBAjkoބ d[2M]Qֺ ÇCuM{`_ng6ڭ $T&at |rJP=AXVBg t]eţsݽ~/3+ppJ=.:<OZÇ S1f~4{w\444`YNmG':(֭[ѣD@DH` " HҚ:TC%PZ$^Ւby/aB bN%!EA@m=ZL//W={Mu@ N@4MD. 9A1DPDӉfhVe0<)A\ΰ(J-kj ;#Z&eas "LH89o!-"Q}2 #I6O: &#piL'Ü \E߸{J$՗[xlY7L6aryn ̑;MprK'nJby~VeBQwM|RR_Z|JM vxq AWg۶mHGwv CD2$lه9cS{V3gQ:>Ui(~zӬoX 8 Oֽ (;liR!I]=ʪ`M Or?AVj8HpO{1tw(e%~YiCړ($RaL"|kkYE*b!67/c̐yL1pT&'3Z* d-&WY\715ȊIθ~.~:M]`r?@a 3 - ?NI7!}g2*++)**ڭ년kXe;- TA@0,#lk؆'e@l.Qqچt Jp8eZ(B&E@ UI$i!l`i-@&K#DT*E:L&De^^],_I"cy-7pS3o6: "/tX2sd>s%P\ Exȿ"DK; ke@X4$xb|S]]kG{ $ ^Ii^e*ez0s 8tdBs7]pHr<^>Db!QC\J!Ճ]:--YLA`L/|[{,>&9d>Dؠ[ñr*wܜu46|0AP RWWǐ!C˻}W2ڃ[|;Y濼Ụ(Jt3z$CeB*+ϲq~S7bz!-s:֟ϗeY<@wOZ@)O"a>vl%W|Ģ=7 'zw|cETk˲ Y9g}5h;-E2#RgќLY@:fҞ۽B*F:8TQKGt](Ɯp8PDv=I&1m4z Z`aXʈN A7xEI$z2b0i!IveQ#nȊd4M#ÁDɀz74qmvZKC0MÖ,,]GM0,ErWl@%>7DU9gn@?m*nO!nR7E3s{30%4-'&|,-Ț dr("J:d{ɓ'sWyܫd|;{s+$3P^bPz(BQ$>JW<Ts}~_9[5<.XL8+QV֫m3Wp꡾e(Q!3_+VY2_V3`ٖ{ڧ-/ mDew- DLIeW:OQKPVWW5]O&;;;3f wz&h h:KCIJ ,DT*:$d:B$Dq8z]($ڞ=l߃ D<1tQIeۍ[ $Iq 4*i?9W31+,Nx$D<@EgW Yd40 K7 '܉vpTٌm&Ț$a:"v(Sm@,+Hv*i;}\.^1j qUh_]|'(līSO,@:0O)L΍ C) Oy{YmjGqU#'C o$)#-XO %,]}:g_(HcGS5!$#I38N')``jToۍhfiފ],f(,}*;+E~LID,@e.S]Ym#6+L I1 l6I6Z5PdU:d =YRIEC>te){Ed2DQU"0Dσ(˹b=#+JΦB7>{bՉhۀ9k0Uqmr}7;wg&)4My^~T_dĈ1#L>b8U7J:ٺ΍t90=:\U'Mqbk,Z4Xp2f2ݑR؇+C(TWWsI'S(;?:e+r7 ~,Z+egsG8֙>J~ط90EJw{D&``jYIw΢?ʋ8v%D?HDQQ|[Caxںry_~9mmm|46ĒT2%(XKtԱ} )GJGl EEEwq{$EFo[~_b &N8?ۥZ2#I:lJjeuDn"X2`YČN~%e#'ƚek&uV Ljl⨣E` Z.)ǥrbCvNв&dH5jKQTlFCtL&㥩`(ȐA&t,.p"[81]TWaL>-&i}7>FAUҕ0jHB0YM.me AA E@7AW1)0̙C&aĈ 2r)e/5 h4J2oWlƌȲ3ыRji:2qCgR-к>s1i0v cCU\$QR~gEQ"JǨ*9󮥼h0m[yHgʙ4FJ"! '"oYVQ %5d$x^i&t$Q"mٕ=,\C&q̴ 5W8Sq!%{(}>L%މ~!5khZL\]>xꦺl$ɏx>Ҵvsڼ:֯__ 1?jA[Y21\ W!k+YW֣D :ZZZeñ,.k;fx >?!QQzp4۹FR$錄ZXʲ`Zp$%# l{+ɸ1 e)dYکID6E,Щ=n$x j;y&dŁ Xh;Y֐/ \n7h ,]CL9UUX$81-EQzlIEQ(+/E;4hOS4@24>^8K'݅Du(XH((hF8Ő<![ill_?p^xۅ%; Q(\$ K<%ڎ`jOt:͠A(++ۭCfWWGofԨQ ;ECR0$;-@4D2`8= KY KTIdŚ]kHȂL\,%ԦQdJ$ caqĜrr^}KBNƀdz8DtIⰹs9t<+dkG" ]iilA$24e5CQXOl۴RI$L4hM @Z[6sG;4D0IcIDIFT[ײPn=ĂH2 *F*4-,C4{wop=|&lu]pAp8+5#G) u@*$U'Phz ҙKFdST ehx*Kut&N24u>N\i`#28 %Ț y x!dhx2D"݁ J$ލ8)K({ol$%ь >WkcXFA0rkpRΓDJǜo!'4$ZHD_ARgѴ4Le4_9/bJ "::J1CfRcP(ܹsQUp8 6ӧ  ٬NwO rp:$ M7vAELZ-%JDIIJLL 4"h:FE(J*(vB6xPYUҰPŶH^JS0 QddhnbYvD Yp8ܫk6kZ喍z(,BDudQBE[= /Q, i(Z\ %+dŞ1[i8{Os=褧verJihh砷7w&MgZ0qD׬vST${3-hڌeZxKriTIMHJGH iY-4#A1Lh;Ń)/dqźX-տaX|Ehc̐T CZt6NV qt(J, X_9TYRɰ4uN?dd*,d kÔ,?Z{- F SZ?Qsˆ46j;^#d q)CcXdR[ӏ"{+XrT$"O 6|.JDj=fRVV7~nLr8CL+` LĴ .dY&K >24 4EI%ô,5''fSZ64,N Lt ,˒0,^eLl`Ac#M[&HV/3#h[` :3o8i;YXHKIAEd)8r4jd s^@_9(..tT]׉D"_zCǃ(^!MlYZ䯤_Ʉsl6Lpn?(wns#D7=6ҙnj2"R }帜^gmQ~{bb m-Mm] ŀEkW,+HK&0TQ䯠A~RLj,)X2;Ԍ*;?)a+'Ct7YN#$h[ ~u޺R: \Fv o?= rqE"$I;ԈU[˲(-wgttL9]XSq` GGp *'*IlQGw"hꀔ # `nV\fذa9r$w}wޘiժU?~ ~xmW 'xNfO?M}}=ӧOf.Mz(#_~O1~O10*Vف%%LG艶}VL˰[lN_3> ) WrD[[ӳ zV#)p(.7YV[dYAe(T+@m(JSZX2$L c8U7=ѶU`:ZN+9 RC&쑔(ԇuնyvT$:$`ʘ;ED$g.Ӱu Y8dG"1qr&+H 8DB. cA$OT1nҼB{֕\t#OAĔ]Nza-)W1z435[0\> GpI :ĆA&21T)@qEQYYɓw x?贶? qƭʉ'̴i\uUw}?~L&ìY!,"$))ف%UqT=Ȓnh$3Q(mcDZlmQÇ,$PMi)#=%av1q(N2X[[VF[p+x7.B^WuQ[1@ &i 7=nfpcD݈޻)TM $ 0iѤwbg{ |"|-C 1bpgFGKAnp^S)sT,%Ջ3-qfk'xh 8ʋOytK: c]Fyh괤$Jc]Hy2.,4o`m#X% JX)۩~b6mӀ#fSnH;E9!TT%M*U.lBZ'7ҁShI]@f|JC 2)vI&I D>w!E\Q3S~`Ֆ]*.,Qƒ,IA%SG$3qL1@Ԓ$b& H eb8 0 KRA1>Zuc^LD-`d( fj9"wtvg}6MM}a$I FYYn Zv;kb18T8 ,ˢ7y}z{֖,Yرc3fv@{ >ز0~jv*.jF8Dف";$%W;`/FH:' #I C. J%7BIi$=_HWGpWJ3|d*(Tp]d2,u/p⬯Un1CA2o4ݜe\gREʜ"i1C-QW"NsWq"*TnWLPY0с3cmCbNch—BYX1~F(?WG"ӇzcYe+ Xee _:J6]O5VD=cI=g@KVLڶ钊/)6u$Y8PQ-@Kμ4L‚ѽb W!@5`׎``d/sj)C@\3̙3曹k˹KYz~i>sG( Hd9Nشid?|;]ށq3<`ԨQףi~uZ[x1%%%Lw#OD*B*0W-7q/iS$YZoH72],|hIKīFTѶD("%u,8iWb3˴:%YxƤ.) ~tjqΎf\檠);NsK2EjaAVPExDHJ#Չ^;מԆ:,.׾FCC]\1&t$n_i"ݩ4TVVsq7|fHb~bҥ93׮˗s5F׿ΤI4)..p 3 N_̚A3yfӿ8DYRp:= FId ׳z2 տKcFJ1zFaIfbd44ظ}oxp֮zJ6DN4 zmx*z˛?2q~62Z4gq$;Ϫ,N#{=뢶ƸORS~ ²WvӒ0YP`G 5Ie&-h*Td ,MKT45ց.$IB*y-a;`VRR ~$Yfa>~c2ٽ۳M2a$.Rb,餲MBWWxh4JWWaSit]8NÞP2eeex<eeeTWWSYYIqq1 F&ɹxXd cPel6;0 ށ8c~/ %\=X>0  ;ITH3s9a O[VlYEEM7) 1q\& mSD(+U=Ex݅N<ͬC΢QNX$ئ$Y}워v-f1\#ͤұNoxYw3GF7{m&}l]oXHtwKh.tat E\tGZylKDT:F2aЙ$ґ/<> :7?} g] N%ƭ?+¥w^'K| 713}ޒ!|z&NfD磳,lee%tP(]n*BUU?|Ν,(,y?t]G|zCW$dY!9͒ڪ*aEEE[;(1$ FEIInUX`<UUU\ Pi!,{A@$5l^ +qygT.rIx 1אΆQpV."\(Fb % Na}cEu:cԩDс>$85exhXOe06n[B(چ$)'WlX*'# bYՒ<-δ9ce-|93uޑs^9 &>/; i8d;Y]Cwrb/Y)..fڴituuqg3j(Nѣ)**>^5M}4-J0,h UUD I$iLgI&b$S).UuPeRIeQXX}CUUN;4T^>|8:<?Y0Mֶv$A'\sґǑIdxq#O1J:{1gyYmfr6XwmWŒg.t/6B2m?)T KF~ EKO ĻT^+,X-SLwC"wcZ-tn""n+Cu*Nف%3B B4h":˹$غfU?λviǞ:20c~ʗq8!d !u?_݊(|>2 H?π0o<|>~94Ӄ sr?Y;DBAףٴ|a#QߕbpP]dqon`Es~p=u~p_ԳL&СCd2|Аy̙gj-|W9xꩧ 5Mp] H.(ģ1|iKO<~[ċ=R2:wVʱShJH;t{y RR0vL-UqڽUu1v x7q@',`E&b*8R\PMO^.b#8zڅty腛9^;֮-Hg "THcOg4fcdw\]>8Ͼu/k֮bp8**G1e1hѴ el^&> l' 3waV`5bn\|x^m^zτ# %;:bug[az9/Wr BwƼ1oQFx^~avF'¤#N嶟R40tֹq:PN7fcS7@ [5?`h0CJ\D}&ɐf@Gnx|L8x|n*#F3?9r 5x B7MH,exŜ> 6,g\|ɥd|Xh,C*_1eh? n:AןX9d-['S[QӫȶaX( ˾b>h".L&pSTT40<2Z⡬_DCJ ɒӀ8wְ;m(4x%%LӠ+ԈeY:cQ](هͳoK*3SBwI<_ ESz±JPhPdk)W1nFޞڲ,d8KO(Ȍ鳸ԟt"ҙD>:G̸,h$H GF"]x `QY<\PO'X Z$+INܕÙ:5®7vI:S1 GU GH$3 mC:D#a:,ZtBN5zS)zpBjjj5kXlg6%.gs x $ ް~c&;_ f|8~[)(.L=^u6>_}daI2k6mb䔑;Pf2c, T)SꫯpB`p%0 @Hg GUp;6pظSxqX3Bz{oɁ(JhZ%/ lC/ח?Ʒp?CH=;,qeNKIsn6Jx=m@y`^Z ?=:N{j±.Bd~&aMy71n~EuB_9=zFN f1|L"WimjfɫoQ1| =_8,N:pȔ$l6KCC@AOOD®0 'صg[ Ģ; J B^RXXeY4662u)DQ$Jdv:7x`~anvn7Æ 'd&;LQfQ[̙5ۮ!a $P+WPWWGqQE <4tCQS]ͿyB9xVZ̋RQ]cOP-D,Lu@@!jkkٲe },w]%Ib͚5SVV6 M6<I.s#3'4MMK1̽[v0q:<@2W~n:<#| 7r7q_'Jǹg1MhAZFm쿛lk[ͰdIʋo~ϑS@8IG϶}p/]?i:Tb׊ [֮ؠ읗_P(+Pd9`6z'J"x7oN8袋innr!tH$Wex<\.N' lMHӤR)dt:1 #OH$hnnγX)Svb1b@cW^cc+/gĉHAAg}6]Țի8TUղ~ }jj-͍XL&kY{ \˨C6bn43oI„)*&O*J<%l۷@ -Bf _O?~:8illWUU{=MƠA@HfbVq SǞ@(ƥT&ݳT˲p:@<FZ# @Wm|?=JOoɳ̣G4޵נ?HYRT1v$31 Is ^W!?ڋlk_ndeYl-7ϻGfCk/nٴ˂(IEsK ȵG}tETU9U%_LLV4mkޞŘ8q",j墲׻ⶁyFn# vZcK5U4nko;W\ez: swyq_"o17De.X4j;u]nU]Pw7cLW ?bw{Y,l޼#G4 ~L6ɰC)mK;d&޺7:{@et#K,DU$=|kf */ث$ruKy[3s)WHE>Ÿ.˦Dݔ !9T=6*3f2>p.3g4u/lURRO<%'yG}}:}M$NN=u"&NK/YA B7 ,Ӣ7L,Q% oA*~ 7"hQ#r~Im`$IynI8skyy٧׾ӎ===ttt|r,bdY6l@}}=TEl2bٲe,ZT*E}}=7oF4/^eY,_zzzxxwI$466vZt]gѢEڵkimm%{xb8ͬ^0Xhn:mF"o{DhmmeʕɢEflܸq6-_Xr%eh"2 7ofd2-ZeYXvBK.`ɒ%$Iٸq#lEa&+WH$?%KhnnfݺuhWx<ŋm ô۴xbt]gÆ l۶d2{4[lɷw˗/k}d2,\0_v`ɒ%b]D:wi4'26o޼]Eqz{C}7l%f׽o%Kaƍ444NuŊtttzʝS9r 1|2zl޶56`Y+Wh4ʒ%K=ªU0 #׬YCSSviҥaYbvmڴiNS0suV6mD&aMl.l!{KWw-[+uswX>\xyZ[[F{N֬Yt]gڵ455H$mzFziׯa6uwwʕ+ŋ֭[I,\wޙؒL&ٶm[| whjjʷiUvK,ɷiժUۍ6lT*~wulٲ%zϱTjs~_b͚5{M|NC,+ PY0m)ljֱr{olaIt p`i (<#}|k~Db(*_SLRXXHww7@t:]נ`0HAALf6co_<oTTTDOO>/ vw_{{drkJ,+_{w:ȲL,,˼ABCjG\8knBqX=b iݽr$PRRBOOUuQVmxo_t M|;xÑgvv<HUco `vqb*.쨪u}N3ߗz穷>(Ǒ)'P]2T:OCwIGaDa=jo~@0OzF1sCo}MqE_>;#A_17sLJx`|)S`{=TUEogcD}1z>gmRn{^7?qnÇswszjƍx#F`<3hb5M $ D:̛{ 09Cl ?p޵֎$5(~q2KW-勹׿* 9t}( (2(?C e;8uvT,"UUU|8N|ɼvek /'nd9x%8T7z\00>ʈiԔoǺ8U/c ;?SкƎ5w ҇ڲo]~HDQQGqz(> 7oDڕf)//gҤI̛7-[}^Y:|An&֯_ϨQ~"O W@-r1gNyGxg6d0u*&Gy҆㧺v0E2=nj^} "r'}ULv½0{pā(Z y6q تGLI#bxݿpk9lI4wn`ˆ{+R &/'JM}xEQs=ϸ+{keRdx^xKX>vWU\ljZJ4WaIeESUp8O479v 0\qPRR2pQ>G +2MӰeLLOEއl!VJ~v9vdA4lM&8ف":JvԶm OFsк[QQ6HE,>w-_pw*Æ 7ߤ!k֬tJ~!vjJՈblݺL̐o3f |oӦM`׮]\|XE<gD"f̘mXhapĄ aѢEZPB{W?aW8G(soaHF(A~v[Wuӯy1 P:RgÎ p #IgY_<;98H۪2iv)cd ab0E_OmVt/k_i|7+9lǘS5{` ̓ P)570iԱ+-U5D^"a 0csiCۛ D@ ѣ4i')›AuQH)Yr%Ce׮]N 77;v0`^~e ZZZq]wQVVF"@4gzjnV^MMM  7+gk!EQhnn{aĉnn7}a}裏f̙TTTvQ^zq90|TUMk{H濿 dqKxSۼ?˚.CMe%)/HQnvԮmO9P$ 8rI$9ҡ`Ͽ]s1z!he'ygp1^ޏbY~798ēEI9'rϗđ_}&IIpߕRgK_!U"CT:DlP&ܳE@HJmv,)ƈDb=H} }ӫ'oV:*gܹsy^{ H\.^/{/\.-[g̘1lٲ^{r6lʕ+4h.˗~9uv9 `#[oljڵ+Wrmu8ݻ9sxBлwo}]ENj$8s1㭅ÒOP^TwBvKs{ d_ڶj;v ku2i8e/7t(+3so+ Ӯ xAB!&>ū_'!Gf~ӧ/#??M6e:GoeK!jK IlJ:w,P!%TR$VJ@L]&pi@7Lr!04[njV0  6*8ݔrlp@$tD|5m"H!LUQpnds=Y] B}DDZlҒ4I&?dHٽ3<^[1jq {^z뮻X,FQQ̘13fPUU?UUR9c=ֽsd磏>"+++-(((Ca}IItcG}4>~%]tyי5kVzB&OɓyYr%'p;v`Μ9̚5+_)^eKG W(o/z_SLKC~ǎ㏙:u*z(ov:nOh6ma8hI\uϾ2t8Rn 7gTE{h51e)lܹGZD! ٸ+n8!n@y棫m;A^gY!#.̶m[U *PZ8#&_̮JHn}.<^yE_cwƯQ?NlݺSrgd7][0r-%#̌CM(#vQ$l*,F+U(FDbq"c:م;pعx1"ݿɤ*QA]$a:*.(*I4b &0-?㣏>f a6JK<#IŘm.2퉮)8cH&.~ UUqCEMӪI}~Q54[)M3!2t?@k`uE4i&1?Oyw(((㬳Jos%+tTWJIUU7p?O_{<J\=usف"۫Qus 7|ûA(ϛFߊ>T)&'%> }Cik[؎Eq^?׬䑗w,ƴhc^~#f3i1;9m/1t7en{kRL)լm t4uGknEftZ+2c@$H"H"0S/@V*RE":#joP<WCW%q3Ni-%i%@q0eJ7h~/Ix׬"t]kj] m&Yz7ec[q'm&RL~0"ѷo|$YLaaaz"韤- &:QmxL{裏FJݻ9r$;vtMMMlڴ ˲2eJ; E(bŊtb'9dOqUV}F$JD#H$XlY9sp-#'XL~v9 WƝK;JgS߼Gn\CЛg7OPZ0ݍym$Q"t" |>|KBLd"_ ږ*(=Eތ98QW)DOwdDZb#adETM"@@(˲kcuE%K+M(E2i☠(q-W}*u;qQ #lTaG6/Z?BOK*24imn0/A}Z_SREU(tt1ClBUtFM{kࠪ3i]עN[[PAQPP@<Z MI}(ȃfAgP&h@^E%E +֒#& !I[/@b0u+wbQ tصm3\DG)*-EՏhn0VRʣV^^~]ٰa;v_~i7~x*"Gy$-&S. _}t{{;xO~~~??. /<-Xx1r r1w\NAg4in|0Mv{~k^;8;w.~BJnFJɬSӋ:Vm?{gJҊc1"o.cO#`%,D p$À@m#6 ym es)*W5x\%KɽbL;tFA^WòR(ѨID*)[/xZ;;(!fǜz-\x{Sۇʾ2z! juw &kQPPoɯk %tL~h8FKSx:R D,)e TE\  '$xMQF~w189#8_sC?|lvܧEe%7u!}ՂKS8_#<Nʰa8묳8ꨣ(--ꫯ iL:]BtRZ[[ B̟?9sr`M7|={v^x< |'}L4_~0:t(^z)>(sƍlaر`?k[:j1p Ouk3I$!hu.RBIAI^xdy'>-1Ǝ*d6;-l^QO1q}v=[iL=?౗wr]S_9xֻEQJQeotM{y(6|M[ChB" ӏcdS׾n[(͇CQn>!DzmxECgUdpDZE2$7o0žz)M?juAA+D!O0<:.p5 tu!t:m2@J=c;~YAliHé9q$nl]v `4o&ZHC}L9p]t*m]uK̙>a `&>0֖v*47aqq(BTWfXj-۶"͗^c)%7|3h*3//Eyy9?8>*nEN:;;5j*Eb 8918N8z-vjCYYmt9E(7oCSuzo.*HM]c5=0N*F3MU]{ >ʱTkbŒzV||_Ia,.YϪqB׷M KF<>UnB],|guk[),EuB-Lp*ډiul۽[2qLp:mutD3 =֯_QG5\CQQQC2P@sl&f+1H+#2zA\v%tulݺ"w P['fjJ6Hbw;UM% yiG߆~Xm۫ٺuk7p9GٿEESs;6m{x<\4 ۲ڵ ]0dH,0܂Rٯv2+ dٽߚ˲e˨i$3 fgzimm㠩0~_-ZA&֌夢R6(L_MQR GAqE*n7 t;vPWݼ?0 ?h4ٽغe3>A^q:YΠ lZv;Zxc`y`0inEQD[30һXqhq`AL?rj8\tqLg ]aKs xN9m8;H4n!^'t ҃O>9moo緿-/"y饗xW(--MGD~RNYY ,_~[K/;vpaO2qD^}Uo?.]ʅ^Hnn.#Fwߥ"׿2uTrrr|<B!Ν9xw)//gܸq{ϫhmme߻kH"QT@W+T+0yv!3NǖE`6 Ǒ]FG['V#Ètĩ B弟ǣ_4HG8mK~:$$'Gtڛ x=~5Gp( @W4Jw-Ǒ֎$>N,:Cx3!b(`ۨX i&t#i矽Ï{WE.<"IҎh vPT BvO]NS|ف\v52WU0( ĀᓸS9Y<~֮M7]E±'^@uU Bg}ƎJW%m4C(ORmf%lY&p%3`Ӗm,_3iv#G3]: V;܌.@B49+8Mmlذw\ZyMiKm:ZhHgB˗u'"=zlD[꿈7DX8*ڸ[lӡvGzژ.B*4uﻫKm¥!D*QAcc#gO?㥗^R)N8.oΓO>_w=ٯ;w=DxOGÇ/h4ʼy8q"y ''kϢ:sB b$ދx< :Z{=T_EC Zlk[H %xtJJ $PsZ6W 2gpwR\\ f Q0$ Et;(,Ȣ''ŀ޶,C!h@M52Ҭ4-f;8BM[k;O?6/--.by^duDAB!ɂ#A5v3J hjm1ntW\y6Vg ,t[m 6M흼+pY;"9tf6Q]OB=SWLH"G< ^.nU nĎ$P"H dSod2U.[LA)(qP2ymZVVEEEO-oRJ4M۫&}OdĶ.EFRD/[o>saeN?tn77x#va_?N; Mm_YgEee%uuu|m9G}GyG}^z Ƿe9.7Q)RnW2^~x27Cx -( )HpE;w1|ZC\^˯c=Ӧm ݅(8J2Z*ꥀtT,jga[1dzDO]RRƥjض+UQR V~K.>u%KVҳ94iթewM+7Wb]fÆ7??xOavpˢcF$,HPMSC(Ruat͠p0]~a׃>Ȕ)S]娜rIlA[<Xr,cЈ}RnC= j*s>#}Y(`SN9e7I!zʾ {㩬dɒ%Os7#3gɬY(..歷ޢN:;wҷo߽1.:^.ۮ+(z4։km8.9}&93e'|h<^'@Ҵ.Us,l޲ma(͵~ W0=/e;,ᥩCdĊ8w$5x..Z>Rw17\y!.ƺp{]$۫1" *]%D]RP$]d4<~r3:{!77:N<OJ?cDzx⿻-g}볞vivi_!>(~;'x"BN=T~R^^?׏C= &PXXȊ+իWO?4_~9xgyc=+ߐA|*i֖!klҲm}o9F$N< {:{5U5{-,|+/p]wk`츺MMCA}}5y~֮}~Bk{+huREqy, qP*99tĻpF7Cző\tl.X±8;14ılb1 x--9t5kdy2BLTʉ'C/D̢ ?{Jx饷pv!%I7a hBcb |*C_Iq7oK,!ܜIb7cOow7}m'?RuӧOgӦMTTT0k,<@L^زe sca;|G]6M.ٮ s==܃bѢE?1Z;"&D)MRI$6ֶ^_MKK-#GӚد $ w_K>/HH(Ʃ2T ` H6Rͼr5P< XMgg*AFS5lJ4-ˠ+Jec:9c돼|\3a$̞ hlT]bVTc9T'fEH$cn7IEEI;tWSch EB˸_0z>L _3Ymݺd2IusL73|>3^{5bdeeqꩧRTTDNN< J3]PGmh 2vtt o~ zs5BIZ ؎ץQ^^yD"惷^a[U G/~ep|p~qgܺ ӱPPA,`mc;l?^s~wǶ'mr F-7_T@8aQɔMŲ, E2dAy᧩GQGl޼_..t9P+17T$H\tl,'C2dTUjOy%54SI{s9^do^{4/;<@*nƌo— _pgnL&|ߟ@ tJ ׇ"^$6RX8x..R>v^CmM#gy Ww~p~$u;k8̟w@ uE HSK#/^ 9Wlؾ}LV^&@n ;H$x^ lܴ .:0Vbx6XcgPG"DQU|>?mbu|4pd"TC"IHEY:||"1lG5Cw^|vzAAƩ^7o02!H+<OFϛA?2f^yF *{ygN Hl]qpA01|@~{3a FRIޚ>9rvVoO>GkO‹ ϣX2x|,YΚe 3i WbQwPS@,76A. à,?~X,~Xn N:e&7^{'?>~1MsG^< /O <Eupd*ZM`鯯ڽQ#+39xOd4tÇfբeыCeF6lwv 8*.̙UUzқAsX墰08Y8;nwd2$8D(6u 1Vfsr) ?> *SV1یK3ian9oJgW9K2Pv܁XXN6瞟Ù/ㆰ{w+;7rʙgr¿H&hmE* b1-ݧa#rhoNX=|G 0?a,A5W1bHTx82ucm3ivDijҩ<V\α'D}c#yylܴOmVQ>[[ocgA]C(nQI>eTXt=A4e5K<ŋWk4cdg{;p6d(B8b&!B49`!Wm7^Wmz*3 <@v--U0t]QvlJKKN %N2Fu)("u7\R+ ϋ'f?`̙3{={v:2o۶1H{1MH$Bcc#msg?^Ș}bPR| K#fͿq/^r.MP˶Yf5ջp{ODشa=@{7_C[}O>z3h"-]#>mwaCSPGCc-r8H)UtL;U bm\zѵ@6n, 9XdͲJ: H\X7]OqQ)}&6ow|D3~s>ӊӴu=W~Fxlsyg[iۈ!f6JV233ig֬Y ~wΝ\vep ?п~_qwgQTTDWW׏J~~>C aĉl޼{Ѷp8̮]2 %:;;)//3ϤYVVƐ!CgƌlذGu68fEhi~}|g SĺWRT\D;nx"A$CѠp 塽 LJۧqǀc̄vtF ݷe%ٹSWۀ؎dw$nt,_ .+u=UB5n>zG#fp?j6 ҏKeWN EG # j>Fˇ q&//}۟.Eu`Ё䕔Ps_guHdT{.Ed>}"4d=W=۶0-@{{;DUUq]KJI)=Ze{ Ba4MK[T奢{˲P%AJ$oPe]]]DѽU8Ckkks۶qdgguX,FGGG:a6ٸA<OYύ~}ٶMkkk=˲|D_wH&{/ם`0pP(SKK mϓ"''gc&Iy1PU5/)%---b17cömx<w<~1ϻD"zD"%4srr2:;;bw\1gEQ:;;\uߏF466+9Cnn.o;ƾ<y @[xط E!??smo{<h&=YEkkke w4vXƎ˸qkx~\uUi!A8FDJ(Xvrǣ:PIܖň#Z彙jw˟+1|~V;(RP!'\2LӦMc׮]455 z?J_dڴitttXfW4ZS__O4Ŷmꪫx+o MӾ1a 2H$BVV| x^TTTj*:::p"?x~馛hnnra&}1mۆ#3<_6x sMaҤI]v}QN94g_9f]]`&qvm{mFE!Я_$L4N K.Jy^vM8NE]ēO>v\r w}?}8y׿5W_}5h5g`Æ Gqq17ofڴi|0˖-k￟>\.D!d^n馯oFzeeeùk׮o;~Aּ+f.c֭Bo=,ʾ\0j(.\H[[ < <;Gy$hhhs磪k矦&eQPPo~ozIKJJ0Ms~ Iv~;{")ewYR6 T=Y)˯ګ2w #|lvֳ|Z,[_rUGa WNebnNG1LBuLJ~͹g>L:g}5WZW_"3{x+G[𥼼ɓ'g9l; FXJoL0 ww 6miL1\/G_m++ۛf ]|m** R:{E6R' :hH )SB!TG"( H@JmݪB>G #@I(zO:Ӛ/#m E*J~AJ)RAU!%R[H=LB#%8(w򄾝brP6nՅ[Q5MWqu5XC7@Htp..'3y88|l)ƌb|lڸ`ˏo׬a,nw r#1i~4?:K;q9 I^tŒT.cqLl۲Ǒ(@S5eL i8I|ۚ߰jn:S(1nE! MXb=q{\@QFVe" %%k۷R]NdS̄y2 ~׶ F{Cd( X~u5T`q,[͛gxW-' !WycǎcTUB4TEt!em4uv{8a?(VA1+mYTTr0+o`Mx=^&MH0Ųm4yUӔFC{ֶ(ضDQF 8"I X˅4q'Q"n`$ 2*c"Ub(IQAWUv"$B8ܼ P<~ЧO)Y>T>6mp`*bEy ??MsKX*mmǡ!,_Ӧᄐ$QMCErVeK ǜ kҋ83Dv쬦m'`6%ģIJ8؎^E8cXI+|2Ѕ㓕;X{( KÁ&2O.>] E3ۧf} ]}A ]8([ϯUg3f Es[+L@m}3Gs1Tz33OdoŞ{tGct$ׯGu^eW-n,H$BQq)C-5ttx0\tK8MWP1c! rAICC%]eYn5a -DeQܯ.Cۦ)<1X(KgUx.Ng.-_9OG[; >#gcXr%۶me(U3zHLb{rQayL;`*~ |>CGg@A,X)%3rYR9dп\n\n i$!|>BK3PHd""4?yLY947t+o0]M &g&sԬ< YYHgvje<譸tk.?w^{/+PW'f4 8H ݥD$m4M%WT҇$qv4Eٸ ;, ᥩ%^ s<44ݻ]m01^ 8456\̘y$@_q%l'w,ĥGVLKs&M;dfA=IN=$Y5kV !4-/L ~p{d0YYA <6R`:µ*$IlqmxPP²-b(7HT߇iH3JTXD SH0/.7V֯f‚d'?0y#a{EV@Ӌ`PӻMnA@: Cئ%- $h Oˑ'x4 ܊ yFG iEj^L)7v}&;˃GLz 9yVI [vؘOǓ@J dtԨEi ǥҞ%C|.h .^3eJJsվx8 8)-L3厠m#1 3qN><,[!ͥ @knF˸1cif1oBjˋ 1mh$F8_߾4}>;@8cRQƈٴsJJrWsGqQQ،RGJJ*JIg ZI05;2bbU[)𻨯o%j}`ٰ-;[B0 :碋.bKU-?5xFH,N ]\LNʀ_L|iY'3keA_ݓڶ8{avI$6! G0$++~E{Cz4|r`,TE̊lJFUwΚI{S+Wlr >XB4jut+"An~ ӷ9A‘.:Ed =o6xDN ,YO?%\֭b@_cxwd\tŸ hmmGJJLE|ȴzfO8vLvlBc} ͝aj >O[*f ~_[ṣ\eD$+ˮv֛)KxGOeغx!En.ɶ-5$wDL 2kѓUo&@۶y{Yz5`OQQc6---l޼V֭[ŋ`ڴi~{eYiBc%Aw@D1r1툢ZK~$i*K_>444s0Cз"+itgS4I{h ;bk BgAD! H Jǥ7bd' "bPR !"$h@>>JI8!: p!Sm)9(@HLe9oS7nz4-tkAQ$R8D*R^U]tl!%$5%CU6LD:IR-7^ٝr"IZ!4UKu:H$BrHf>p*$;??>$M@iy>m-!&/mX|~=?AqStsנW_V6a-(bٴ9G:C9P;nMq!DT$&_H4I&_kSAd vɓYv-/X|W{zrB^kkkyO~¬YxGO' ~(hkmjVzϜVN9$֬_ź-[=(vlƭۙu vn/ȈÙ1s M\+9>s:?ZƏ˩']G9n$n頛&F1L).l/YaN"GEKH҃P%'[RכHTUrFU%HSI;E{V* \3EH&@4YV#%6)BA)I*\# e7JAtnH?|W|+[v bh*xLEQSjǤtpm'hinb˺ kXv&|a[]l~R>X#'WXݕ}ͧ0ODwoGzri(9ϠjFCFi^>[͑gST[fz o ر{3lN8B]mT؎aj9b &LbՌ-Р=&\* 6vIBI,LFȋ:h?±.ꫫib9Y9'~ch((|8e:.A8҉PuB P$T4T]'a&q.Tm[)H B*8ݶcBIr EG"KY&&m'}HeXʽ|IeӼGDHC*,M-)`;+"`.ْ XĒq!P5{4QIIY{u1Fcwϼ,W\vkcd ΦOxoe5ee%(]*-D-/n3Ve:t&յDcŚm4uAaéީPGL?͍-<ܫ̘2ISPd`^um 2;wҕ,_ʩ=UdgQ\ЏF'_'bڐRF(+TvHE{T#?۠}躻:;1Z:)aD:Z(.,_ɇ~ 3Bj*@@OԶ&s/ks{Hq2R\uUL>}*d}(M !^â?r VT]A]8#BEEE`:G.!l0U  2})[R)&RHj-eT4@HҬP$']QIT#-u=4T&|F#/yaObO{w7d"dG,#KYghͧǫɓ'SRRG}~IlѨŽz$uLiXJ' vp8 :]A8h.rV%D҇ဲ׾!TDwUA: H%_$*z׎$MȾ׃eGPl8- [(tVʔTtUʔA.rH t<EFXh d"pZ 33w@jp'oI0w~+@ϟ@(jDJgmwe*̭4o^ˎ*.UU\t8aYKdyGx%f ut" ۶16 Bt$8c;` 9@jA& & x5 PPkgRx~ꛛq4|vnOPB,icYqb;q $Sp*$P\}п_ڻXb^dڂ?>B ]VCӧϷ~L>گ-o!dEFT'2jѡljl̮J~~_r-쵏22t7d>pn (݀ |Uy!if@NX*v#t[ڣ_KY{~OxQ={+O.pv=tkBTe4PqkI! {='hG:H.{q,*HLU]s [Q5ASD8qikkgwMrNw/Wt47E v2Hܞ&iV77<On›Ad?>'NqlڴW_}zZ oYgk›p͝ug\8uWu+Q%B!ܓRd pI4W|!U(# #x2d5%%؎:_Zv'*ɺb`d|y)RTcێ]dLSk#:`xD;!1QQqSZn+Pf  bF4T$v Me LL R*T g~<;ÃRTXHBqI]]x}^|>e񤢺voܸGڼ\{}PT?ω?XAMo@\c ]X U UX8( uTSl@&Iw(Q7HPP$R:r$~P{+>&sc6ف8Rz1AJUqR%M6H Jƺ@J4UWtr 7~7[U9JKPTPW*|*MdqD;c8ES5vSe#2^xoLgd5B4i^ۮ^l铮~tuuQUUÿ;?=h)}޽{ssڵkLeYi?^UUפWYT,YH$°aH$8aq'jO212څ $R(B,EEL CE$ z d ګ;0jc6%:%㴀'Ėq<b$w$hD H-UJ$HOiH"6-'Mk1}v)QCUv"|F6Nc…#%vwW44M'm@(l,,@ah&VT#45zLlhϛMcc=fg@xT8BStu"  eZcB{{;& N \l;ՋJ^}U^}U.bB̛7H$#LzhS\ 7`+*FGtDGc%:ڌn 0L#$$,R `iwa: L?s<4X8b"@ڤD E*l+Nj 8z6nـpR LG:l۶׍*|8I w'Ij.a'"4v)6,Bz}pGoU ZqErQݵkr .$1avIWW{*ٶ]ױ, 4m-..0 !///N$,Y?tt;KVVr\s a&t?Z;۳吏zQF OII uuux^8G}4_x7hD"JKK{/,nwf`f)`{w6(7ɐNKG@G8Ȥ $l`pM4wmCt#B-T"IpsJEpg#$0$$ڮ !*46c^[ e0mӶpn<4Ww1pBh.VQ{0L+@:"m&KGT$AiBXĶUtZXJWCi|6g [hظ`vòL`x=H>I.*n`GCM).ű-܎]seodM D:zH$ҳ!=gΝ~ƍGeeezx]Ӌ'xaÆ(D{{{Zv%S ^d=لI,cN[[B۷/999޽m۶awuW]u};`̙{-VXի1M}2m4 UJ' nwo HG-¶m\.d2S oD7j8DVףZ(t6,A͛ٴ CZah^l$X߇ b8e^ӌC<GJ_GEZDpcET(@Mdӫ۱1 }f˖͘F0-"voĚ1UmaV:)6Cx3 )LGŞȩ8L6-4yYt)L/H1-N[\"x/VpS]¤OP)?;Cx3 /7/!rӧORiӦvZ {{,%B&$$Bq O.v5Ќ$U8X TPuPc.3HI^, hCba.% D1!dAm7ޯ޽;!=. h4J[[/'|2ռL8]Yh~?mCֳo˕TrBWWW: ^/={~Cvዄ7ۍ8bm2 8RN1#iHT]! 64$!(JdC6JK{#iδEKBj6`geӱrvU, ۩ nE0qS[U"{jxrMwR}^(QZ<^A>vc&d|*X )%>x<XHG;2UD*fHU^z  Hia&H  DGö[T! 2|#{JZZZ,+]@Ǐ{̘1~3ϰeعs'>#LOr̎;(++KG{HiOoOT=qJWW 5'F7o7 mH&O3lg( 04&ٔS ;jb8^D4Nn n܅XMĊQ>Yeҥe5BҴ9_R!JAa>]wMRʔn E %:82v+)T%nCLU5Lխְ<&m.E86e`SxrIb ] `t]mj,54g(6ض? 2 )z /)34--Y, ,68tvvrGPWWGMM |͌5rG3tP^/X~=Gq ,`hFnnn:m{&A4Ų,n7>CGGc]?O\.\.3 (TTz|xɄ(>:"t(1J1cq:jP1gf|r+=΄%|sU4tEP<ի&q *JFsc n{-ґȞH!~pax44j u-l\t] 8!(8؎cṳ Rb])-Qv`NR -6iË)U';BA=HiߛW[9 2$SUU"xGKK ;w$L)**C[[_I$L0u֑G,㥗^"//aÆ1b;8rrrرc~:wh_|;wW^aL&inn1eLdƍ㡦fD"[,Qz45A,Elm8F6PBAQ).mK u֩[GwRe|-!8HGEJFr9x Rv~hE @H2u* XNBV-YEKUw*mk,DZ ]Y.FPZ_ow ˖[AUPSl"D볉妶0?ƍ$;+5 ](jJRǑ{=lH,p 21GG8P] G---TWW~#<իW86l`˖-Wk￟QF1tPF]wE8N'=:^q4-]M4$p44h{. ,:P(]aMUU ph4m(o;_5jTfX BlCD: bi!QeHik]sYSpj$EG:pgP.R\ғֳi*`tbx|cwUTgrFV aQehTkQnjպZ VU+ZpAP("A&d]g;{MX};';3̙3w<9H^ OBCӴ5FPI >?c&\H$;9T1~1 IK`0#;6蠵Mӈb5?0?cremҁC T) @ȒCo-+!ATE4m,SGǔ%l TH"Slt$,2 Nf$PL o B@@LM(=KHHę* X}zѻO/ DU,,;Z鬊yuUQ%3Ad#l-j4̈"c&ю,z~9';zD~e}35x]p!Y簾&ݛ{鰴wUZBLjs(B<Ge&MDuu5̱r999hacV$ZZZعs'~8O< "L[69. H0w`{ ZO"}z;͟غJaDʰMHe[5ݝSdǬx/(i$v<(* -IJOb.TG9No7aG5\h'ze[4G[=m475OsS 5>ll`cU$<L&M($Ja[vF7۲-$q"L&4գ b6a@UI M~p"E^؏ш'!Ր,I! Nvvx… .Ӑ:֦îx<kz43fZAUՌޞxfL0X,F$!c7oFQZZZﺶk=Ui_.I# rzv3Ѧhx5#jю9yt hH ֑$Bm(@R%tGQE"J%b,)#F02^41,8@2P$3/C0vT!>jfO2JYY1%_@"jw1986&JxBeYEU<gΨ5'mbK&CJfMѰm=[16XflJKuk Ya.y*ϘNͶ_Xn--##\ׅ .=^] G43}Kttt2خpd2Skv\@W%D{ nK0gq'O\dY&Hd9 f6mĖ-[صkѫW7d2?`}gp@d$̽UT[Jv~994ڋ'%+'DS}@@޽Dr CرBTK]sQ<;Q\\HMIa)a^1MIinnxHS AV0L4G "c[R>MAæCoCIYijZhKQZH4Kg}N<BɄdP:7YE\ׅ .Ec74.2/^x^;8nFY>L$c9getDb!'nEɬ%8*vގm… H`m 7C=c=9>fq۶;v,˗/dr D*bǎ477g]w=`}=iCO>xh4R)۩'P(D]]æMfڴi@7˲2鐽^/555s9;\wu,Z'Gep~ۅOL%ll,naib[nJ0c$R 4 ik@(AJM(d*XI KObYqV;q#FNҒGlKNf݊$0 &J1$%I~~%GM-6}^QΠKL=K2ڎm|bmtꞡ"aszlg-hL (݇@ˏ ݶƽ?0s0t3ۚz:U)A uG +n#U&0 @(5x]p]~Z}YN:$ NG}}?9wygƸH<'H$2T׳gJKKQ={dp 3իW3a@FgŊ+ >pR=Bn&\wux3*[/<>laFҗ5sDC AߗMݧ4mA x! {x{oϧ}Wl%'lJ |߇mm*Ü|B=MjWY3y,X(̌7FLx5O9J7ݻٸq#7nd˖-@>EQDQ$IFIRI]=$GWOʁ.JZ=rLc&I0Md2-P_~ 0!C0tPp/@zI#F0vX K'[e]B}06mGf޼yqjjj3f ׯ3`f7헍-_8tJٍsl6@zL¤Iضm[RQ__Ϻu2hD^^ڵVnff͚O>F:&MD]]@FA"mwO!dS$D$Usbq$H%u^ Ja&$׃x$y}UdLHD"D |^ĢIrs;OOPVRmGTD,-$KDٱ}P,,r͡a#S\*3rH***=z4'OF$ Ju=r/@ IFikkcժU|lݺ."&Oh.g4@ ̦aUU3ִ!yf֮]ˬYXj3g^N`Ŋ̚5u֑E0pw HdƠtp\Wet[\\eYݻw 1IH&dggSUU3h4H)ݻٶmcƌa…vam,{eܸqݒA ~?P0 _7Ml]1NƵa1M챉'訋 I AŧQ4=2*Ht(^F?G,TUhޔϫdLˤ8A#9E:omo~0hoĶlGAJ(¶*wfOs!aweT,I$z1tS#;tz~-GGز1 @0IL&Y[ȨIaq%ܵƦF<RٽE4]]O`Iϵk~֮]˚5k9r$:;vՋ<((( ضs˲2߁xtH}ax2REir$&iii۷ge~kp-0 <$ѫW/ؾ};%%%,m ߢ":,N>d=z4 $e<כw ȬD5Hk (R)mۖ9ǁƧ!Bnn.e:::hhh SQQό3fz$%KpgdNJ*bԩrWx7(((ѹZ–XJT1MLTEQaZx<:F*$ P1 ( :а Ild:$n%5}o`O"XF m" $" Ѱw i& JeضH{H&SD1ma&q4yuc++2!H` 3 N\rz@7 <U(*M!H\\BUo#qIlX1}F|>Yt OB$FKN8hGM6Mvɺu' rCRTU dXAG,ittD)-+CUdRudD#( "Kd={ STΝ;d君4 iJ$H&cNiq1~7I4aΓO=E<wo  pyqpB8∌6mdWjL`~3Z[[Yp!>Gʢ6,ˢ!&L`̘1f2S]]֭[%avri|*IRFQBQ|>_f_He0ҧO***fbQ_K}c=㣺cQ g]JzοudI"Z-fH`!?l^|ig]˴J<}ɮ|0eTɩVow|7ʦ7bԑwݻ}g,+u8aL`#Q>__x^["/. tM+qUW1g6oLEEE7oWmZb+mX~<vm7 {N^^3fOe ,_ֿ$`z-,Yrz]w=蘺I}2vPBÎJh6 e,DFEd& ma"P; $zP$Dl Y!*]"H=c-Q<!kO~<Ɔ'5VijdqWހW0-0( ݃Q; ]Ǜae45'\1U;g7B&O, H+dn6FE߰˯Oe֬Y,~f9y-i!}喹94_x>w0Ͼ5Wqs_ lg3''_޷nF$'jVVVh"&M-E]۵LWa?~ 7o/E q/()v bTnٹMa/}rX|7ݱqǝ SiW ο_?]⊫">mW\we۶m=zT?seed7pׁ ;Zx(GGd|0) T  xT<@ˑu&}B I !l Ia[6?gaszԂFӼc1RzD"2l%L$EжK$;w7cjl"(d3 z' ‘4U4BYA͡0(--wضrby%:"ͻQ\T[Ȳ +9J4 ^x,NII1i؈i؝YdiJd"V(+~Wܟ?^x;vCmI1tݙ-0u[Dt΀:GyBy' \qv[BڋΌtR #JG7owu:u,ǟA?ό3xg82UUU~lr@CC#gL?hMBض'KHtQ6Dzbjڎ&GQ$pxMNm# D#~;X"T%I"a g@犎,J(iXx4dĶl$U`[Q>;oҜ/@<86l䣎gY dG%!;BrU7_ŀ>e3HĢ2.\"lU?A]'im2$ ; 2YW4Mb7oCwk À V*VXgI=H$|͛jWVVRQQ|<x4Au s .-U/\}mNP=R;wv;%M lOYi1mqLãJ#7-5x*2P{!?~6x`8~H$ 鯋tks2vX^{z|ZqJ .un|9Z[[Ջnm--2٣;'! alYgOg!`$t |VoQQd. zn|fϞ2t y .sgYaܸq4662tP:,VX)S(++cTWWxbE2~:֭cѢEL>G}SN9!?N…cjOm6n݊wޔPPP@QQdeec/LB4Ǭk캌N6k0mq8Xvٻw/L~~>TVVb.\#Y ΦiivCu:3-DQu˺*:Xh_[xp7M43jiX]B}]p_zٹs'F,/$W@A*iCk̮A%^p8LNNPR)((؍Rv?MIs:Cz"~/o: dN41 G24M~1*MQ<O&[WJiϾtbǃ먪,XM؅o LH. BdZc/{gJwJ2!gcݜp'3Od=y7߯<,WɃ7tTWVT?zJ+O(7 $ {X/[O>Q3.d ߖrڔ) x%:&JyӓI$Uã:cDGJJˉXдe3~K88n/uN;[v%:BD .\p V1W@"//OtZL⦻o"ѳ`1㴩]qXb')~Qybg}xɅ{E(70wEQg6q @L;TQxoF3gP#qkE*>+Zvg|tDYߛ 1"qM.y+aC3)nj~ !ќ2  o%~l):Rs~)ٗ]!w4F8̳Q ^z-On'.~ xiT\9s-y=gq NHTv'+'K8m~|:Sw @{[nǟtؼ>p… .xmu.ӏ>Ƙ{uTrsyw (@kS M>;ҿ68;!'o`F{04vۣ#?YvVl=|ZZZ;0vJԟ!#Pz0lGUWy)_vgp'U'M˯`ocۭ;rawG5Hn3k Gc n)/so.Qrf~Cqi.{a C\w͜rTf(l3q!/~)ؼ2~pOݠ5?bPЕ8q…`H!}~%1zvE<诼l@'YË/U0 wrJJz9-g{Dr/M M;F^S}F^~M3q#l۲+W2`(V\I~Q9}KYr%\PN1w?HL't$.g3ٹ>ݗKz m~#QS6$'Ve),f)0{\U_y㏝ifʕ +1MI`xW¼j5!M3d鲥TWW瑐Cy7ϭx6|̑dʕz!ظa-oZ ᬋY~QXr%= aPRο|^zl-LuNɷFix?d\޲$g>_'@z¶X%N9tջb纷 9[]mB7!LK{]~A4 ܚio)d".Pkgz8m A$ {"-mYե?څanK,~ֶH$(_s:9B,]ub3)jd K4vH>v؉' !SNw.xK{$tIǢbfk]t… R:ņE֚]#HDX DD2Oo 9nso0,B;xwyyj$I"P,y" l[=+Uaomz~̻L$Iꦻ(-,bٲe`=ECm'^)G;ebRzt*z!I{s=y,| /Z̓} _DV<\_2nhO$IIޥ`;A{&Yg$IbwyV|%fΜy? h[_zh{;˖-cwkO>p~3=zW0 I>`mSAQUtcʂH3 ⺛`~#̻*~+>^_$I Ü9s8֏jk… .%9jeCYYdee!<Ϸf$*.Rz eX_":m6k6\8 8)$Pz9|Z4;̜9nz FyA945qMX4?͏y*&m^\</ߋ%c: 2h+iwzZ~ '78 Jˣ-D r{%/ b$NOGsvy[;74:툶[6afJB^ysHi*sLEE$a7Xذ~l6~&6]kE/-UbD"eiE;Hbqלd(Zk~wBb'zذM>IDbnǞ~%sm[1@jB[;vQ "a ܽNt/~s}}2P EIϞ1>.waXJqqEqE]Sv[!DWu#QyA?{w޷}y- w,BH̆_bs}+ď?x#7_G=4[u;﹝7!!Zd7W܇r3?~+,yLB9/m߯s=RS5=rzk᲋/w/׽_LCgk1?>~6oɯ 6UmZֽYY^6ጝL0yv̹<`.vT*{>u4~!xK7|k>p'NC{S6G=3.[M(<Z̕?}6茡5[ Zwce@漳{~4}<$h=>Y)>xqϣRۤ>w2n Gh7 pÏJQi9p_Q6#aét(=L-(\*++ . # /*! \mainpage \image html gnss-sdr_logo.png \image latex gnss-sdr_logo.png "GNSS-SDR logo" width=2.5cm Welcome to GNSS-SDR! GNSS-SDR is an open-source GNSS software receiver freely available to the research community. This project provides a common framework for GNSS signal processing which can operate in a variety of computer platforms. This tool is intended to foster collaboration, increase awareness, and reduce development costs in the field of GNSS receiver design and customized use of GNSS signals. For details about GNSS-SDR and using it, please see the main project page or browse the code at the Sourceforge project page. You could be also interested in subscribing to the mailing list. \section toc Contents \li \ref overview \li \ref build \li \ref using_gnss-sdr \li \ref control_plane \li \ref signal_processing \li \ref license \li \ref publications \li \ref now_what More details on GNSS-SDR signal processing blocks: \li \ref signal_source \li \ref signal_conditioner \li \ref channel

  • \ref acquisition
  • \ref tracking
  • \ref decoding
\li \ref observables \li \ref pvt \section overview Overview GNSS-SDR provides an interface to different suitable RF front-ends and implements all the receiver chain up to the navigation solution. Its design allows any kind of customization, including interchangeability of signal sources, signal processing algorithms, interoperability with other systems, output formats, and offers interfaces to all the intermediate signals, parameters and variables. The goal is to write efficient and truly reusable code, easy to read and maintain, with fewer bugs, and producing highly optimized executables in a variety of hardware platforms and operating systems. In that sense, the challenge consists of defining a gentle balance within level of abstraction and performance. GNSS-SDR runs in a personal computer and provides interfaces through USB and Ethernet buses to a variety of either commercially available or custom-made RF front-ends, adapting the processing algorithms to different sampling frequencies, intermediate frequencies and sample resolutions. This makes possible rapid prototyping of specific receivers intended, for instance, to geodetic applications, observation of the ionospheric impact on navigation signals, GNSS reflectometry, signal quality monitoring, or carrier-phase based navigation techniques. \image html overview.png \image latex overview.png "Overview" width=12cm As signal inputs, it accepts: \li Raw data file captured with a data grabber (digitized at some intermediate frequency or directly at baseband). \li Any suitable RF configuration that can be driven by the Universal Software Radio Peripheral Hardware Driver (UHD). This includes all current and future Ettus Research products. The USRP1 + DBSRX 2.2 daughterboard is an example of working configuration for GPS L1 C/A and Galileo E1B and E1C signals. \li The GN3S v2 USB dongle (GN3S v3 might work with small modifications). \li Experimentally, with some USB DVB-T dongles based on the Realtek RTL2832U chipset. \li For mathematical representations of the targeted signals, check out the \ref the_signal_model page. As outputs, it provides: \li Dump of intermediate signals (configurable by the user) \li The processing is logged at a system temporary folder (usually, /tmp) \li Observables in form of RINEX file (experimental) \li Navigation message data in form of RINEX file \li Position, Velocity and Time solution in KML format and NMEA \section build Building GNSS-SDR In principle, GNSS-SDR can be built in any Unix-like system. In practice, it depends on being able to install all the required dependencies. See the building guide page for details about the project's dependencies and build process. Mainly, it consists on installing GNU Radio plus some few more libraries: \li Gflags, a library that implements commandline flags processing, \li Glog, a library that implements application-level logging, \li Armadillo, a C++ linear algebra library, \li Googletest, Google's framework for writing C++ tests (requires definition of the GTEST_DIR variable), and, optionally, \li Gperftools, which provides fast, multi-threaded malloc() and performance analysis tools. After all dependencies are installed, clone the GNSS-SDR repository: \verbatim $ git clone https://github.com/gnss-sdr/gnss-sdr \endverbatim This will create a folder named gnss-sdr with the following structure: \verbatim |-gnss-sdr |---build <- where gnss-sdr is built |---cmake <- CMake-related files |---conf <- Configuration files. Each file represents one receiver. |---data <- Populate this folder with your captured data. |---docs <- Contains documentation-related files |---install <- Executables |---src <- Source code folder |-----algorithms |-------PVT |-------acquisition |-------channel |-------conditioner |-------data_type_adapter |-------input_filter |-------libs |-------observables |-------resampler |-------signal_source |-------telemetry_decoder |-------tracking |-----core |-------interfaces |-------libs |-------receiver |-------system_parameters |-----main |-----tests |-----utils <- some utilities (e.g. Matlab scripts) \endverbatim You are now ready to build GNSS-SDR by using CMake as building tool: \verbatim $ cd gnss-sdr/build $ cmake ../ $ make \endverbatim If everything goes well, three new executables will be created at gnss-sdr/install, namely gnss-sdr, volk_gnsssdr_profile and run_tests. You can run them from that folder, but if you prefer to install gnss-sdr on your system and have it available anywhere else, do: \verbatim $ sudo make install \endverbatim This will make a copy of the conf/ folder into /usr/local/share/gnss-sdr/conf for your reference. We suggest to create a working directory at your preferred location and store your own configuration and data files there. You can create the documentation by doing: \verbatim $ make doc \endverbatim from the gnss-sdr/build folder. In both cases, Doxygen will generate HTML documentation that can be retrieved pointing your browser of preference to gnss-sdr/docs/html/index.html. There are two more extra targets available. From the gnss-sdr/build folder: \verbatim $ make doc-clean \endverbatim will remove the content of previously-generated documentation and, if a LaTeX installation is detected in your system, \verbatim $ make pdfmanual \endverbatim will create a PDF manual at gnss-sdr/docs/GNSS-SDR_manual.pdf. Please note that the PDF generation requires some fonts to be installed on the host system. In Ubuntu 12.10, those fonts do not come by default. You can install them by doing: \verbatim $ sudo apt-get install texlive-fonts-recommended \endverbatim and then run cmake ../ and make pdfmanual again. \subsection debug_and_release Debug and Release builds By default, CMake will build the Release version, meaning that the compiler will generate a faster, optimized executable. This is the recommended build type when using a RF front-end and you need to attain real time. If working with a file (and thus without real-time constraints), you may want to obtain more information about the internals of the receiver, as well as more fine-grained logging. This can be done by building the Debug version, by doing: \verbatim $ cd gnss-sdr/build $ cmake -DCMAKE_BUILD_TYPE=Debug ../ $ make $ sudo make install \endverbatim \subsection updating_gnss-sdr Updating GNSS-SDR If you checked out GNSS-SDR some days ago, it is possible that some developer had updated files at the Git repository. You can update your local copy by doing: \verbatim $ git checkout next $ git pull origin next \endverbatim Before rebuiling the source code, it is safe (and recommended) to remove the remainders of old builds: \verbatim $ cd gnss-sdr/build $ sudo make uninstall $ rm -rf * \endverbatim You can also check The Git Book for more information about Git usage. \section using_gnss-sdr Using GNSS-SDR With GNSS-SDR, you can define you own receiver, work with captured raw data or from a RF front-end, dump into files intermediate signals, or tune every single algorithm used in the \ref signal_processing. All the configuration is done in a single file. Those configuration files reside at the gnss-sdr/conf folder. By default, the executable gnss-sdr will read the configuration available at gnss-sdr/conf/gnss-sdr.conf. You can edit that file to fit your needs, or even better, define a new my_receiver.conf file with your own configuration. This new receiver can be done by invoking gnss-sdr with the --config_file flag pointing to your configuration file: \verbatim $ gnss-sdr --config_file=../conf/my_receiver.conf \endverbatim You can see a guide of available implementations at gnss-sdr/conf/master.conf. That folder contains other working examples as well. If you have a working configuration and want to share it will others, please email it to the GNSS-SDR developers mailing list and we will be happy to upload it to the server. You can use a single configuration file for processing different data files, specifying the file to be processed with the --signal_source flag: \verbatim $ gnss-sdr --config_file=../conf/my_receiver.conf --signal_source=../data/my_captured_data.dat \endverbatim This will override the SignalSource.filename specified in the configuration file. You can get a complete list of available commandline flags by doing: \verbatim $ gnss-sdr --help \endverbatim \section control_plane Control plane GNSS-SDR's main method initializes the logging library, processes the command line flags, if any, provided by the user and instantiates a ControlThread object. Its constructor reads the configuration file, creates a control queue and creates a flowgraph according to the configuration. Then, the program's main method calls the run() method of the instantiated object, an action that connects the flowgraph and starts running it. After that, and until a stop message is received, it reads control messages sent by the receiver's modules through a safe-thread queue and processes them. Finally, when a stop message is received, the main method executes the destructor of the ControlThread object, which deallocates memory, does other cleanup and exits the program. The GNSSFlowgraph class is responsible for preparing the graph of blocks according to the configuration, running it, modifying it during run-time and stopping it. Blocks are identified by its role. This class knows which roles it has to instantiate and how to connect them. It relies on the configuration to get the correct instances of the roles it needs and then it applies the connections between GNU Radio blocks to make the graph ready to be started. The complexity related to managing the blocks and the data stream is handled by GNU Radio's gr::top_block class. GNSSFlowgraph wraps the gr::top_block instance so we can take advantage of the \ref gnss_block_factory, the configuration system and the processing blocks. This class is also responsible for applying changes to the configuration of the flowgraph during run-time, dynamically reconfiguring channels: it selects the strategy for selecting satellites. This can range from a sequential search over all the satellites' ID to smarter approaches that determine what are the satellites most likely in-view based on rough estimations of the receiver position in order to avoid searching satellites in the other side of the Earth. The Control Plane is in charge of creating a flowgraph according to the configuration and then managing the modules. Configuration allows users to define in an easy way their own custom receiver by specifying the flowgraph (type of signal source, number of channels, algorithms to be used for each channel and each module, strategies for satellite selection, type of output format, etc.). Since it is difficult to foresee what future module implementations will be needed in terms of configuration, we used a very simple approach that can be extended without a major impact in the code. This can be achieved by simply mapping the names of the variables in the modules with the names of the parameters in the configuration. \subsection configuration Configuration Properties are passed around within the program using the ConfigurationInterface class. There are two implementations of this interface: FileConfiguration and InMemoryConfiguration. FileConfiguration reads the properties (pairs of property name and value) from a file and stores them internally. InMemoryConfiguration does not read from a file; it remains empty after instantiation and property values and names are set using the set property method. FileConfiguration is intended to be used in the actual GNSS-SDR application whereas InMemoryConfiguration is intended to be used in tests to avoid file-dependency in the file system. Classes that need to read configuration parameters will receive instances of ConfigurationInterface from where they will fetch the values. For instance, parameters related to SignalSource should look like this: \verbatim SignalSource.parameter1=value1 SignalSource.parameter2=value2 \endverbatim The name of these parameters can be anything but one reserved word: implementation. This parameter indicates in its value the name of the class that has to be instantiated by the factory for that role. For instance, if our signal source is providing data already at baseband and thus we want to use the implementation Pass_Through for module SignalConditioner, the corresponding line in the configuration file would be \verbatim SignalConditioner.implementation=Pass_Through \endverbatim Since the configuration is just a set of property names and values without any meaning or syntax, the system is very versatile and easily extendable. Adding new properties to the system only implies modifications in the classes that will make use of these properties. In addition, the configuration files are not checked against any strict syntax so it is always in a correct status (as long as it contains pairs of property names and values in INI format). \subsection gnss_block_factory GNSS block factory Hence, the application defines a simple accessor class to fetch the configuration pairs of values and passes them to a factory class called GNSSBlockFactory. This factory decides, according to the configuration, which class needs to be instantiated and which parameters should be passed to the constructor. Hence, the factory encapsulates the complexity of blocks' instantiation. With that approach, adding a new block that requires new parameters will be as simple as adding the block class and modifying the factory to be able to instantiate it. This loose coupling between the blocks' implementations and the syntax of the configuration enables extending the application capacities in a high degree. It also allows to produce fully customized receivers, for instance a testbed for acquisition algorithms, and to place observers at any point of the receiver chain. \section signal_processing Signal Processing plane GNU Radio's class gr::basic_block is the abstract base class for all signal processing blocks, a bare abstraction of an entity that has a name and a set of inputs and outputs. It is never instantiated directly; rather, this is the abstract parent class of both gr::hier_block2, which is a recursive container that adds or removes processing or hierarchical blocks to the internal graph, and gr::block, which is the abstract base class for all the processing blocks. \image html ClassHierarchy.png \image latex ClassHierarchy.png "Class hierarchy of signal processing blocks" width=12cm A signal processing flow is constructed by creating a tree of hierarchical blocks, which at any level may also contain terminal nodes that actually implement signal processing functions. Class gr::top_block is the top-level hierarchical block representing a flowgraph. It defines GNU Radio runtime functions used during the execution of the program: run(), start(), stop(), wait(), etc. A a subclass called GNSSBlockInterface is the common interface for all the GNSS-SDR modules. It defines pure virtual methods, that are required to be implemented by a derived class. Subclassing GNSSBlockInterface, we defined interfaces for the GNSS receiver blocks depicted in the figure above. This hierarchy provides the definition of different algorithms and different implementations, which will be instantiated according to the configuration. This strategy allows multiple implementations sharing a common interface, achieving the objective of decoupling interfaces from implementations: it defines a family of algorithms, encapsulates each one, and makes them interchangeable. Hence, we let the algorithm vary independently from the program that uses it. \subsection signal_source Signal Source The input of a software receiver are the raw bits that come out from the front-end's analog-to-digital converter (ADC). Those bits can be read from a file stored in the hard disk or directly in real-time from a hardware device through USB or Ethernet buses. The Signal Source module is in charge of implementing the hardware driver, that is, the portion of the code that communicates with the RF front-end and receives the samples coming from the ADC. This communication is usually performed through USB or Ethernet buses. Since real-time processing requires a highly optimized implementation of the whole receiver, this module also allows to read samples from a file stored in a hard disk, and thus processing without time constraints. Relevant parameters of those samples are the intermediate frequency (or baseband I&Q components), the sampling rate and number of bits per sample, that must be specified by the user in the configuration file. This module also performs bit-depth adaptation, since most of the existing RF front-ends provide samples quantized with 2 or 3 bits, while operations inside the processor are performed on 32- or 64-bit words, depending on its architecture. Although there are implementations of the most intensive computational processes (mainly correlation) that take advantage of specific data types and architectures for the sake of efficiency, the approach is processor-specific and hardly portable. We suggest to keep signal samples in standard data types and letting the compiler select the best library version (implemented using SIMD or any other processor-specific technology) of the required routines for a given processor. Example: FileSignalSource The user can configure the receiver for reading from a file, setting in the configuration file the data file location, sample format, and the sampling frequency and intermediate frequency at what the signal was originally captured. \verbatim ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=File_Signal_Source SignalSource.filename=/home/user/gnss-sdr/data/my_capture.dat SignalSource.item_type=gr_complex SignalSource.sampling_frequency=4000000 ; Sampling frequency in [Hz] SignalSource.freq=1575420000 ; RF front-end center frequency in [Hz] \endverbatim Example: UhdSignalSource The user may prefer to use a UHD-compatible RF front-end and try real-time processing. For instance, for a USRP1 + DBSRX daughterboard, use: \verbatim ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=UHD_Signal_Source SignalSource.item_type=gr_complex SignalSource.sampling_frequency=4000000 ; Sampling frequency in [Hz] SignalSource.freq=1575420000 ; RF front-end center frequency in [Hz] SignalSource.gain=60 ; Front-end gain in dB SignalSource.subdevice=B:0 ; UHD subdevice specification (for USRP1 use A:0 or B:0) \endverbatim Other examples are available at gnss-sdr/conf. \subsection signal_conditioner Signal Conditioner The signal conditioner is in charge of resampling the signal and delivering a reference sample rate to the downstream processing blocks, acting as a facade between the signal source and the synchronization channels, providing a simplified interface to the input signal. In case of multiband front-ends, this module would be in charge of providing a separated data stream for each band. \subsection channel Channel A channel encapsulates all signal processing devoted to a single satellite. Thus, it is a large composite object which encapsulates the \ref acquisition, \ref tracking and \ref decoding modules. As a composite object, it can be treated as a single entity, meaning that it can be easily replicated. Since the number of channels is selectable by the user in the configuration file, this approach helps improving the scalability and maintainability of the receiver. This module is also in charge of managing the interplay between acquisition and tracking. Acquisition can be initialized in several ways, depending on the prior information available (called cold start when the receiver has no information about its position nor the satellites almanac; warm start when a rough location and the approximate time of day are available, and the receiver has a recently recorded almanac broadcast; or hot start when the receiver was tracking a satellite and the signal line of sight broke for a short period of time, but the ephemeris and almanac data is still valid, or this information is provided by other means), and an acquisition process can finish deciding that the satellite is not present, that longer integration is needed in order to confirm the presence of the satellite, or declaring the satellite present. In the latter case, acquisition process should stop and trigger the tracking module with coarse estimations of the synchronization parameters. The abstract class ChannelInterface represents an interface to a channel GNSS block. Check Channel for an actual implementation. \subsubsection acquisition Acquisition The first task of a GNSS receiver is to detect the presence or absence of in-view satellites. This is done by the acquisition system process, which also provides a coarse estimation of two signal parameters: the frequency shift with respect to the nominal IF frequency, and a delay term which allows the receiver to create a local code aligned with the incoming code. AcquisitionInterface is the common interface for all the acquisition algorithms and their corresponding implementations. Algorithms' interface, that may vary depending on the use of information external to the receiver, such as in Assisted GNSS, is defined in classes referred to as adapters. These adapters wrap the GNU Radio blocks interface into a compatible interface expected by AcquisitionInterface. This allows the use of existing GNU Radio blocks derived from gr::block, and ensures that newly developed implementations will also be reusable in other GNU Radio-based applications. Moreover, it adds still another layer of abstraction, since each given acquisition algorithm can have different implementations (for instance using different numerical libraries). In such a way, implementations can be continuously improved without having any impact neither on the algorithm interface nor the general acquisition interface. Check GpsL1CaPcpsAcquisition and GalileoE1PcpsAmbiguousAcquisition for examples of adapters from a Parallel Code Phase Search (PCPS) acquisition block, and pcps_acquisition_cc for an example of a block implementation. The source code of all the available acquisition algorithms is located at: \verbatim |-gnss-sdr |---src |-----algorithms |-------acquisition |---------adapters <- Adapters of the processing blocks to an AcquisitionInterface |---------gnuradio_blocks <- Signal processing blocks implementation \endverbatim The user can select a given implementation for the algorithm to be used in each receiver channel, as well as their parameters, in the configuration file: \verbatim ;######### ACQUISITION GLOBAL CONFIG ############ ;#dump: Enable or disable the acquisition internal data file logging [true] or [false] Acquisition.dump=false ;#filename: Log path and filename Acquisition.dump_filename=./acq_dump.dat ;#item_type: Type and resolution for each of the signal samples. Use only gr_complex in this version. Acquisition.item_type=gr_complex ;#if: Signal intermediate frequency in [Hz] Acquisition.if=0 ;#sampled_ms: Signal block duration for the acquisition signal detection [ms] Acquisition.sampled_ms=1 ;#implementation: Acquisition algorithm selection for this channel: Acquisition.implementation=GPS_L1_CA_PCPS_Acquisition ;#threshold: Acquisition threshold Acquisition.threshold=0.005 ;#pfa: Acquisition false alarm probability. This option overrides the threshold option. ;Only use with implementations: [GPS_L1_CA_PCPS_Acquisition] or [Galileo_E1_PCPS_Ambiguous_Acquisition] Acquisition.pfa=0.0001 ;#doppler_max: Maximum expected Doppler shift [Hz] Acquisition.doppler_max=10000 ;#doppler_max: Doppler step in the grid search [Hz] Acquisition.doppler_step=500 ;######### ACQUISITION CHANNELS CONFIG ###### ;#The following options are specific to each channel and overwrite the generic options ;######### ACQUISITION CH 0 CONFIG ############ ;Acquisition0.implementation=GPS_L1_CA_PCPS_Acquisition ;Acquisition0.threshold=0.005 ;Acquisition0.pfa=0.001 ;Acquisition0.doppler_max=10000 ;Acquisition0.doppler_step=250 ;#repeat_satellite: Use only jointly with the satellite PRN ID option. The default value is false ;Acquisition0.repeat_satellite = false ;######### ACQUISITION CH 1 CONFIG ############ ;Acquisition1.implementation=GPS_L1_CA_PCPS_Acquisition ;Acquisition1.threshold=0.005 ;Acquisition1.pfa=0.001 ;Acquisition1.doppler_max=10000 ;Acquisition1.doppler_step=250 ;Acquisition1.repeat_satellite = false \endverbatim \subsubsection tracking Tracking When a satellite is declared present, the parameters estimated by the acquisition module are then fed to the receiver tracking module, which represents the second stage of the signal processing unit, aiming to perform a local search for accurate estimates of code delay and carrier phase, and following their eventual variations. Again, a class hierarchy consisting of a TrackingInterface class and subclasses implementing algorithms provides a way of testing different approaches, with full access to their parameters. Check GpsL1CaDllPllTracking or GalileoE1DllPllVemlTracking for examples of adapters, and Gps_L1_Ca_Dll_Pll_Tracking_cc for an example of a signal processing block implementation. There are also available some useful classes and functions for signal tracking; take a look at Correlator, lock_detectors.h, tracking_discriminators.h or tracking_2nd_DLL_filter.h. The source code of all the available tracking algorithms is located at: \verbatim |-gnss-sdr |---src |-----algorithms |-------tracking |---------adapters <- Adapters of the processing blocks to a TrackingInterface |---------gnuradio_blocks <- Signal processing blocks implementation |---------libs <- libraries of tracking objects (e.g. correlators, discriminators, and so on) \endverbatim The user can select a given implementation for the algorithm to be used in all the tracking blocks, as well as its parameters, in the configuration file: \verbatim ;######### TRACKING GLOBAL CONFIG ############ ;#implementation: Selected tracking algorithm Tracking.implementation=GPS_L1_CA_DLL_PLL_Tracking ;#item_type: Type and resolution for each of the signal samples. Use only [gr_complex] in this version. Tracking.item_type=gr_complex ;#sampling_frequency: Signal Intermediate Frequency in [Hz] Tracking.if=0 ;#dump: Enable or disable the Tracking internal binary data file logging [true] or [false] Tracking.dump=false ;#dump_filename: Log path and filename. Notice that the tracking channel will add "x.dat" where x is the channel number. Tracking.dump_filename=./tracking_ch_ ;#pll_bw_hz: PLL loop filter bandwidth [Hz] Tracking.pll_bw_hz=50.0; ;#dll_bw_hz: DLL loop filter bandwidth [Hz] Tracking.dll_bw_hz=2.0; ;#fll_bw_hz: FLL loop filter bandwidth [Hz] Tracking.fll_bw_hz=10.0; ;#order: PLL/DLL loop filter order [2] or [3] Tracking.order=3; ;#early_late_space_chips: correlator early-late space [chips]. Use [0.5] Tracking.early_late_space_chips=0.5; \endverbatim \subsubsection decoding Decoding of the navigation message Most of GNSS signal links are modulated by a navigation message containing the time the message was transmitted, orbital parameters of satellites (also known as ephemeris) and an almanac (information about the general system health, rough orbits of all satellites in the network as well as data related to error correction). Navigation data bits are structured in words, pages, subframes, frames and superframes. Sometimes, bits corresponding to a single parameter are spread over different words, and values extracted from different frames are required for proper decoding. Some words are for synchronization purposes, others for error control an others contain actual information. There are also error control mechanisms, from parity checks to forward error correction (FEC) encoding and interleaving, depending on the system. The common interface is TelemetryDecoderInterface. Check GpsL1CaTelemetryDecoder for an example of the GPS L1 NAV message decoding adapter, and gps_l1_ca_telemetry_decoder_cc for an actual implementation of a signal processing block. Configuration example: \verbatim ;######### TELEMETRY DECODER CONFIG ############ TelemetryDecoder.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder.dump=false \endverbatim See the \ref reference_docs for more information about the signal format. \subsection observables Observables GNSS systems provide different kinds of observations. The most commonly used are the code observations, also called pseudoranges. The pseudo comes from the fact that on the receiver side the clock error is unknown and thus the measurement is not a pure range observation. High accuracy applications also use the carrier phase observations, which are based on measuring the difference between the carrier phase transmitted by the GNSS satellites and the phase of the carrier generated in the receiver. Both observables are computed from the outputs of the tracking module and the decoding of the navigation message. This module collects all the data provided by every tracked channel, aligns all received data into a coherent set, and computes the observables. The common interface is ObservablesInterface. Configuration example: \verbatim ;######### OBSERVABLES CONFIG ############ ;#implementation: Use [GPS_L1_CA_Observables] for GPS L1 C/A. Observables.implementation=GPS_L1_CA_Observables ;#dump: Enable or disable the Observables internal binary data file logging [true] or [false] Observables.dump=false ;#dump_filename: Log path and filename. Observables.dump_filename=./observables.dat \endverbatim \subsection pvt Computation of Position, Velocity and Time Although data processing for obtaining high-accuracy PVT solutions is out of the scope of GNSS-SDR, we provide a module that can compute a simple least square solution and leaves room for more sophisticated positioning methods. The integration with libraries and software tools that are able to deal with multi-constellation data such as GPSTk or gLAB appears as a viable solution for high performance, completely customizable GNSS receivers. The common interface is PvtInterface. For instance, in order to use the implementation GpsL1CaPvt, add to the configuration file: \verbatim ;######### PVT CONFIG ############ PVT.implementation=GPS_L1_CA_PVT ;#nmea_dump_filename: NMEA log path and filename PVT.nmea_dump_filename=./gnss_sdr_pvt.nmea; ;#flag_nmea_tty_port: Enable or disable the NMEA log to a serial TTY port (Can be used with real hardware or virtual one) PVT.flag_nmea_tty_port=true; ;#nmea_dump_devname: serial device descriptor for NMEA logging PVT.nmea_dump_devname=/dev/pts/4 ;#dump: Enable or disable the PVT internal binary data file logging [true] or [false] PVT.dump=false \endverbatim This implementation allows tuning of the following parameters: \verbatim PVT.averaging_depth=10 ; Number of PVT observations in the moving average algorithm PVT.flag_averaging=true ; Enables the PVT averaging between output intervals (arithmetic mean) [true] or [false] PVT.output_rate_ms=100 ; Period in [ms] between two PVT outputs PVT.display_rate_ms=500 ; Position console print (std::out) interval [ms]. PVT.dump=false ; Enable or disable the PVT internal binary data file logging [true] or [false] PVT.dump_filename=./PVT ; Log path and filename without extension. \endverbatim \section license About the software license GNSS-SDR is released under the General Public License (GPL) v3, thus securing practical usability, inspection, and continuous improvement by the research community, allowing the discussion based on tangible code and the analysis of results obtained with real signals. The GPL implies that: \li Copies may be distributed free of charge or for money, but the source code has to be shipped or provided free of charge (or at cost price) on demand. The receiver of the source code has the same rights meaning he can share copies free of charge or resell. \li The licensed material may be analyzed or modified. \li Modified material may be distributed under the same licensing terms but do not have to be distributed. That means that modifications only have to be made available to the public if distribution happens. So it is perfectly fine to take the GNSS-SDR source code, modify it heavily and use it in a not distributed application / library. This is how companies like Google can run their own patched versions of Linux for example. But what this also means is that non-GPL code cannot use GPL code. This means that you cannot modify / use GNSS-SDR, blend it with non-GPL code, and make money with the resulting software. You cannot distribute the resulting software under a non-disclosure agreement or contract. Distributors under the GPL also grant a license for any of their patents practiced by the software, to practice those patents in GPL software. You can sell a device that runs with GNSS-SDR, but if you distribute the code, it has to remain under GPL. \section publications Publications and Credits If you use GNSS-SDR to produce a research paper or Thesis, we would appreciate if you reference any of these articles to credit the GNSS-SDR project: \li \anchor Navitec2012 C. Fernández-Prades, J. Arribas, L. Esteve, D. Pubill, P. Closas, An Open Source Galileo E1 Software Receiver, in Proc. of the 6th ESA Workshop on Satellite Navigation Technologies (NAVITEC 2012), ESTEC, Noordwijk, The Netherlands, Dec. 2012. \li J. Arribas, GNSS Array-based Acquisition: Theory and Implementation, PhD Thesis, Universitat Politècnica de Catalunya, Barcelona, Spain, June 2012. \li C. Fernández-Prades, J. Arribas, P. Closas, C. Avilés, and L. Esteve, GNSS-SDR: an open source tool for researchers and developers, in Proc. of the ION GNSS 2011 Conference, Portland, Oregon, Sept. 19-23, 2011. \li C. Fernández-Prades, C. Avilés, L. Esteve, J. Arribas, and P. Closas, Design patterns for GNSS software receivers, in Proc. of the 5th ESA Workshop on Satellite Navigation Technologies (NAVITEC'2010), ESTEC, Noordwijk, The Netherlands, Dec. 2010. DOI:10.1109/NAVITEC.2010.5707981 For LaTeX users, these are the BibTeX cites for your convenience: \verbatim @INPROCEEDINGS{GNSS-SDR12 author = {C.~{Fern\'{a}ndez--Prades} and J.~Arribas and L.~Esteve and D.~Pubill and P.~Closas}, title = {An Open Source {G}alileo {E1} Software Receiver}, booktitle = {Proc. of the 6th ESA Workshop on Satellite Navigation Technologies (NAVITEC'2012)}, year = {2012}, address = {ESTEC, Noordwijk, The Netherlands}, month = {Dec.} } \endverbatim \verbatim @PHDTHESIS{Arribas12, author = {J.~Arribas}, title = {{GNSS} Array-based Acquisition: Theory and Implementation}, school = {Universitat Polit\`{e}cnica de Catalunya}, year = {2012}, address = {Barcelona, Spain}, month = {June} } \endverbatim \verbatim @INPROCEEDINGS{GNSS-SDR11, AUTHOR = {C.~{Fern\'{a}ndez--Prades} and J.~Arribas and P.~Closas and C.~Avil\'{e}s and L.~Esteve}, TITLE = {{GNSS-SDR}: An Open Source Tool For Researchers and Developers}, BOOKTITLE = {Proc. of the ION GNSS 2011 Conference}, YEAR = {2011}, address = {Portland, Oregon}, month = {Sept.} } \endverbatim \verbatim @INPROCEEDINGS{GNSS-SDR10, AUTHOR = {C.~{Fern\'{a}ndez--Prades} and C.~Avil\'{e}s and L.~Esteve and J.~Arribas and P.~Closas}, TITLE = {Design patterns for {GNSS} software receivers}, BOOKTITLE = {Proc. of the 5th ESA Workshop on Satellite Navigation Technologies (NAVITEC'2010)}, YEAR = {2010}, address = {ESTEC, Noordwijk, The Netherlands}, month = {Dec.}, note = {DOI:10.1109/NAVITEC.2010.5707981} } \endverbatim \section now_what Ok, now what? In order to start using GNSS-SDR, you may want to populate gnss-sdr/data folder (or anywhere else on your system) with raw data files. By "raw data" we mean the output of a Radio Frequency front-end's Analog-to_Digital converter. GNSS-SDR needs signal samples already in baseband or in passband, at a suitable intemediate frequency (on the order of MHz). Prepare your configuration file, and then you are ready for going to the gnss-sdr/install folder, running ./gnss-sdr, and see how the file is processed. Please ask the Developer Team for a signal sample if you need one, and they will do their best ;-) Another interesting option is working in real-time with a RF front-end. We provide drivers for UHD-compatible hardware (see \ref signal_source), for the GN3S v2 USB dongle and for some DVB-T USB dongles. Start with a low number of channels and then increase it in order to test how many channels your processor can handle in real-time. You can find more information at the GNSS-SDR Documentation page or directly asking to the GNSS-SDR Developers mailing list. You are also very welcome to contribute to the project, there are many ways to participate in GNSS-SDR. If you need some special feature not yet implemented, the Developer Team would love to be hired for developing it. Please do not hesitate to contact them. Enjoy GNSS-SDR! The Developer Team. */ docs/doxygen/other/reference_docs.dox000066400000000000000000000360111352176506000202740ustar00rootroot00000000000000# Copyright (C) 2012-2013 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # /*! \page reference_docs Reference Documents \section icd Interface Control Documents \subsection gps GPS All the current GPS Interface Control Documents can be downloaded from GPS.gov, the official U.S. Government webpage for GPS. \li GPS L1 and L2C: Global Positioning System Directorate, Interface Specification IS-GPS-200 Revision H. September, 2013. \li GPS L1C (available with first Block III launch): Global Positioning System Directorate, Interface Specification IS-GPS-800 Revision D. September, 2013. \li GPS L5 (first Block IIF satellite launched on May, 2010): Global Positioning System Directorate, Interface Specification IS-GPS-705 Revision D. September, 2013. \subsection glonass GLONASS Official GLONASS webpage: Information-analytical centre official website. \li Standard Accuracy (ST) signals at L1 and L2: Russian Institute of Space Device Engineering, Global Navigation Satellite System GLONASS. Interface Control Document. Navigational radiosignal in bands L1, L2. Edition 5.1, Moscow, Russia, 2008 \subsection galileo Galileo Check the Galileo website of the European Commission and the Galileo website of the European Space Agency. There is a website with Galileo constellation status information from the International GNSS Service. \li Galileo E5, E6, and E1: European GNSS (Galileo) Open Service. Signal In Space Interface Control Document. Ref: OS SIS ICD, Issue 1.2, European Commission, Nov. 2015. \li European GNSS (Galileo) Open Service Signal-In-Space Operational Status Definition, European Commission, Sept. 2015. The European Commission is granting free access to the technical information on the future Galileo open service signal, i.e. the specifications manufacturers and developers need to process data received from satellites. This document informs receiver manufacturers, application developers and service providers on how to use the future Galileo system and what they can expect in terms of performance. \subsection beidou BeiDou Official webpage at beidou.gov.cn \li BeiDou Navigation Satellite System Signal In Space Interface Control Document. Open Service Signal (Version 2.0). China Satellite Navigation Office, December 2013. \li BeiDou Navigation Satellite System Open Service Performance Standard. (Version 1.0). China Satellite Navigation Office, December 2013. \subsection sbas Satellite Based Augmentation Systems (SBAS) \li Minimum Operational Performance Standards for Global Positioning System/Wide Area Augmentation System Airborne Equipment, DO-229D, RTCA, Washington, DC, Dec. 13, 2006. The 'RTCA MOPS DO229D - appendix A' is the reference standard for WAAS/EGNOS application development. RTCA is an advisory committee of the US federal government, and issues standards for civil airborne equipment, among other duties. One such standard is MOPS 229D (Minimum Operational Performance Standards for Global Positioning System/Wide Area Augmentation System Airborne Equipment version D), which describes the implementation of satellite-based augmentation services (SBAS) for receivers designed for civil aviation use. An annex to DO229D contains the specifications for the SBAS signal and message. The RTCA provides regular updates to these standards. MOPS 229D is available for a fee from the RTCA website. \li Global Positioning System Wide Area Augmentation System (WAAS) Performance Standard, 1st Edition, Department of Transportation and Federal Aviation Administration, Oct. 31, 2008. This document defines the levels of performance the U.S. Government makes available to users of the GPS SPS augmented by the Wide Area Augmentation System. \li EGNOS Open Service (OS) Service Definition Document. Ref: EGN-SDD OS, Revision 2.2, European GNSS Agency (GSA), Feb. 12, 2015. This is a complementary document to the RTCA DO229D, mentioned above. It describes the scope of services provided by the EGNOS Open Service to be used by end-users or Application Specific Service Providers. It details the general conditions relating to the use of the EGNOS service, a technical description of the Signal-in-Space (SIS), the reference receiver, environmental conditions, the service performance achieved and aspects relating to service provision. \li EGNOS Safety of Life Service Definition Document. Ref: EGN-SDD SoL, Revision 3.0, European GNSS Agency (GSA), Sep. 22, 2015. The EGNOS Safety of Life (SoL) Service is provided openly and is freely accessible without any direct charge and is tailored to safety-critical transport applications in various domains, in particular for aviation applications. The service is thus compliant with the aviation APV-I (Approach with Vertical Guidance) requirements, as defined by ICAO in Annex 10, but may support also applications in other SoL domains. \li EGNOS Data Access Service (EDAS) Service Definition Document, Rf: EGN-SDD EDAS, V2.1, European GNSS Agency (GSA), Dec. 19, 2014. More information about EGNOS can be found through the EGNOS Portal. \section standards Other Standards \subsection rinex RINEX The final output of a navigation receiver is usually its position, speed or other related physical quantities. However, the calculation of those quantities are based on a series of measurements from one or more satellite constellations. Although receivers calculate positions in real time, in many cases it is interesting to store intermediate measures for later post-processing. RINEX is the standard format that allows the management and disposal of the measures generated by a receiver, as well as their off-line processing by a multitude of applications. \li The most common version at present is RINEX: The Receiver Independent Exchange Format Version 2.11, which enables storage of measurements from pseudorange, carrier-phase and Doppler systems for GPS or GLONASS, along with data from EGNOS and WAAS satellite based augmentation systems (SBAS). \li The most recent version is RINEX: The Receiver Independent Exchange Format Version 3.01 published in June, 2009. It includes Galileo and improves the handling of multi-constellation data files. \li There is also available the RINEX Extensions to Handle Clock Information, published in September, 2010. \subsection nmea NMEA The National Marine Electronics Association released the NMEA 0183 Interface Standard, which defines electrical signal requirements, data transmission protocol and time, and specific sentence formats for a 4800-baud serial data bus. The standard is available for purchase. \subsection kml KML KML is an XML language focused on geographic visualization, including annotation of maps and images. Geographic visualization includes not only the presentation of graphical data on the globe, but also the control of the user's navigation in the sense of where to go and where to look. Google submitted KML (formerly Keyhole Markup Language) to the Open Geospatial Consortium (OGC) to be evolved within the OGC consensus process with the following goal: KML Version 2.2 has been adopted as an OGC implementation standard. \li Open Geospatial Consortium, Inc., OGC KML Version 2.2.0, April 2008. \subsection cxx C++ Standards In 1998, the C++ standards committee (the ISO/IEC JTC1/SC22/WG21 working group) standardized C++ and published the international standard ISO/IEC 14882:1998 (informally known as C++98). A technical corrigendum was approved in 2003, and the standard was published again as the ISO/IEC 14882:2003. Published ISO and IEC standards can be purchased from a member body of ISO or IEC. Free copies of the C++ standard Committee Drafts were made public before the official standard was released. In 2005, a technical report, called the Library Technical Report 1 (often known as TR1 for short), was released. While not an official part of the standard, it specified a number of extensions to the standard library, which were expected to be included in the next version of C++. The linked document is officially a draft, but that is due only to procedural issues; the content did not change. Scott Meyers provides more TR1 information. Some ISO/IEC standards are publicly available, for instance: ISO/IEC TR 18015:2006 Technical Report on C++ Performance. The standard for the next version of the language (previously known as C++0x) was finally published with the name of C++11 in September, 2011, as the ISO/IEC 14882:2011 Standard. GCC, the GNU Compiler Collection, provides partial C++11 support. Bjarne Stroustrup maintains a C++11 FAQ. \li The most recent public draft of the Standard for Programming Language C++ was published in Feb. 2011. \subsection protocols Positioning protocols in wireless communication networks Cellular industry location standards first appeared in the late 1990s, with the 3rd generation partnership project (3GPP) radio resource location services protocol (RRLP) technical specification 44.031 positioning protocol for GSM networks. Today, RRLP is the de facto standardized protocol to carry GNSS assistance data to GNSS-enabled mobile devices, and the term "3GPP specification" now covers all GSM (including GPRS and EDGE), W-CDMA and LTE (including LTE-A) specifications. Precisely, the label "LTE-A" is applied to networks compliant with LTE Release 10 and beyond, which fulfill the requirements issued by the International Telecommunication Union Radiocommunication Sector (ITU-R) in the global standard for international mobile telecommunications (IMT Advanced, also referred to as 4G) access technologies. Control plane protocols: \li Radio Resource LCS Protocol (RRLP): 3GPP Technical Specification 44.031. \li LTE Positioning Protocol (LPP): 3GPP Technical Specification 36.355. User plane protocols: \li Open Mobile Alliance (OMA), Secure User Plane Location Architecture Version 1 (SUPL 1.0), June 2007. \li Open Mobile Alliance (OMA), Secure User Plane Location Architecture Version 2 (SUPL 2.0), April 2012. LTE Release 9 introduced extension hooks in LPP messages, so that the bodies external to 3GPP could extend the LPP feature set. OMA LPP extensions (LPPe), supported in SUPL 3.0, build on top of the 3GPP LPP reusing its procedures and data types. Check the OMA Location Working Group (WG) webpage for updated information about LPP Extensions (LPPe) Specification. \li The OMA Mobile Location Protocol (MLP) V3.1 is an application-level protocol for getting the position of mobile stations (mobile phones, wireless personal digital assistants, etc.) independent of underlying network technology. The MLP serves as the interface between a Location Server and a Location Services (LCS) Client. This specification defines the core set of operations that a Location Server should be able to perform. */ docs/doxygen/other/signal_model.dox000066400000000000000000000623301352176506000177660ustar00rootroot00000000000000# Copyright (C) 2012-2013 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # /*! \page the_signal_model Signal model \section gnss_signal_model GNSS signal model This page describes signals transmitted by GNSS space vehicles. Signal models are mathematical representations of the electromagnetic waves that are exciting the receiver's antenna. The current induced by those waves is then amplified, filtered and downconverted to a suitable frequency (can be at some intermediate frequency or directly to baseband), and then converted to 0s and 1s by the Analog-to-Digital Converter (ADC). That is the job of the Radio Frequency front-end, which at its output delivers a stream of digital samples. Those samples constitute the input of a software receiver, so for GNSS-SDR the signal models described below can be seen as the rules of the game. GNSS' space vehicles are modern versions of lighthouses, but with better visibility. Each satellite is a reference point, and if we know our distance to several reference points, we can compute our location, just as mariners do when they see a couple of lighthouses. For each in-view satellite \f$i\f$ of system \f$s\f$, we can write: \f{equation}{\label{eq:pseudorange} \rho_i = \sqrt{ \left(x^{\text{Tx}}_i - x \right)^2 + \left(y^{\text{Tx}}_i - y \right)^2 + \left(z^{\text{Tx}}_i - z \right)^2}+c\Delta t^{(s)}+\sigma_{e}, \f} where \f$\left(x^{\text{Tx}}_i, y^{\text{Tx}}_i, z^{\text{Tx}}_i\right)\f$ is the satellite's position (known from the navigation message), \f$(x,y,z)\f$ the receiver's position, and \f$\sigma_e\f$ gathers other sources of error. Since the receiver needs to estimate its own 3D position (three spatial unknowns) and its clock deviation with respect to the satellites' time basis, at least \f$3+N_s\f$ satellites must be seen by the receiver at the same time, where \f$N_s\f$ is the number of different navigation systems available (in-view) at a given time. Each received satellite signal, once synchronized and demodulated at the receiver, defines one equation such as the one defined above, forming a set of nonlinear equations that can be solved algebraically by means of the Bancroft algorithm or numerically, resorting to multidimensional Newton-Raphson and weighted least square methods. When a priori information is added we resort to Bayesian estimation, a problem that can be solved recursively by a Kalman filter or any of its variants. The problem can be further expanded by adding other unknowns (for instance, parameters of ionospheric and tropospheric models), sources of information from other systems, mapping information, and even motion models of the receiver. In the design of multi-constellation GNSS receivers, the vector of unknowns can also include the receiver clock offset with respect to each system in order to take advantage of a higher number of in-view satellites and using them jointly in the navigation solution, therefore increasing accuracy. The analytic representation of a signal received from a GNSS satellite can be generically expressed as \f{equation}{\label{eq:analytic} r(t)=\alpha(t) s_{T} \left(t-\tau(t)\right)e^{-j2 \pi f_d(t) }e^{j 2 \pi f_c t}+n(t)~, \f} where \f$\alpha(t)\f$ is the amplitude, \f$s_{T}(t)\f$ is the complex baseband transmitted signal, \f$\tau(t)\f$ is the time-varying delay, \f$f_d(t)=f_c \tau(t)\f$ is the Doppler shift, \f$f_c\f$ is the carrier frequency, and \f$n(t)\f$ is a noise term. These signals arrive to the Earth's surface at extremely low power (e.g. \f$-158.5\f$ dBW for GPS L1 C/A-code, \f$-157\f$ dBW for Galileo E1), well below the noise floor. In order to estimate its distances to satellites, the receiver must correlate time-aligned replicas of the corresponding pseudorandom code with the incoming signal, in a process called despreading that provides processing gain only to the signal of interest. After a coarse and fine estimation stages of the synchronization parameters (usually known as acquisition and tracking, respectively), signal processing output is in form of observables: i) the pseudorange (code) measurement, equivalent to the difference of the time of reception (expressed in the time frame of the receiver) and the time of transmission (expressed in the time frame of the satellite) of a distinct satellite signal; and optionally ii) the carrier-phase measurement, actually being a measurement on the beat frequency between the received carrier of the satellite signal and a receiver-generated reference frequency. Carrier phase measurements are ambiguous, in the sense that the integer number of carrier wavelengths between satellite and the receiver's antenna is unknown. Techniques such as Least-square AMBiguity Decorrelation Approach (LAMBDA) or Multi Carrier Ambiguity Resolution (MCAR) can be applied to resolve such ambiguity and provide an accurate estimation of the distance between the satellite and the receiver. Then, depending on the required accuracy, the navigation solution can range from pseudorange-only, computationally low demanding, and limited accuracy least squares methods to sophisticated combinations of code and phase observables at different frequencies for high demanding applications such as surveying, geodesy, and geophysics. Next sections provide brief descriptions of the space segment of different GNSSs and their broadcast signal structures accessible by civilians. \subsection gps_signal Global Positioning System (GPS) signal in space The Global Positioning System (GPS) is a space-based radio-navigation system owned by the United States Government (USG) and operated by the United States Air Force (USAF). GPS provides positioning and timing services to military and civilian users on a continuous, worldwide basis. Two GPS services are provided: the Precise Positioning Service (PPS), available primarily to the military of the United States and its allies, and the Standard Positioning Service (SPS) open to civilian users. \li GPS L1. Defined at Interface Specification IS-GPS-200 Revision F, this band is centered at \f$f_{\text{GPS L1}}=1575.42\f$ MHz. The complex baseband transmitted signal can be written as \f{equation}{ s^{\text{(GPS L1)}}_{T}(t)=e_{L1I}(t) + j e_{L1Q}(t)~, \f} with \f{align}{ e_{L1I}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{NAV}}\Big[ [l]_{204600}\Big] \oplus C_{\text{P(Y)}}\Big[ |l|_{L_{\text{P(Y)}}} \Big] p(t - lT_{c,\text{P(Y)}})~,\label{eq:L1CAI}\\ e_{L1Q}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{NAV}}\Big[ [l]_{20460} \Big] \oplus C_{\text{C/A}} \Big[ |l|_{1023} \Big] p(t - lT_{c,\text{C/A}})~,\label{eq:L1CA} \f} where \f$\oplus\f$ is the exclusive-or operation (modulo-2 addition), \f$|l|_{L}\f$ means \f$l\f$ modulo \f$L\f$, \f$[l]_{L}\f$ means the integer part of \f$\frac{l}{L}\f$, \f$D_{\text{NAV}}\f$ is the GPS navigation message bit sequence, transmitted at \f$50\f$ bps, \f$T_{c,\text{P(Y)}}=\frac{1}{10.23}\f$ \f$\mu\f$s, \f$T_{c,\text{C/A}}=\frac{1}{1.023}\f$ \f$\mu\f$s, \f$L_{\text{P(Y)}}=6.1871 \cdot 10^{12}\f$, and \f$p(t)\f$ is a rectangular pulse of a chip-period duration centered at \f$t=0\f$ and filtered at the transmitter. According to the chip rate, the binary phase-shift keying modulations in the equations above are denoted as BPSK(10) and BPSK(1), respectively. The precision P codes (named Y codes whenever the anti-spoofing mode is activated, encrypting the code and thus denying non-U.S. military users) are sequences of \f$7\f$ days in length. Regarding the modernization plans for GPS, it is worthwhile to mention that there is a new civilian-use signal planned, called L1C and defined at Interface Specification IS-GPS-800 Revision B, to be broadcast on the same L1 frequency that currently contains the C/A signal. The L1C will be available with first Block III launch, currently scheduled for 2013. The implementation will provide C/A code to ensure backward compatibility. \li GPS L2C. Defined at Interface Specification IS-GPS-200 Revision F, is only available on Block IIR-M and subsequent satellite blocks. Centered at \f$f_{\text{GPS L2}}=1227.60\f$ MHz, the signal structure is the same than in (\ref{eq:GPSL1}), with the precision code in the In-phase component, just as in (\ref{eq:L1CAI}) but with an optional presence of the navigation message \f$D_{\text{NAV}}\f$. For the Quadrature-phase component, three options are defined: \f{align}{ e_{L2CQ}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{CNAV}} \Big[ [l]_{10230} \Big] \oplus \left( C_{\text{CL}} \Big[ |l|_{L_{\text{CL}}} \Big] p_{\text{\tiny{1/2}}} \left( t - lT_{c,L2C} \right) + \right.\\ {} &+ \left. C_{\text{CM}} \Big[ |l|_{L_{\text{CM}}} \Big] p_{\text{\tiny{1/2}}}\left(t - \left(l+\frac{3}{4}\right)T_{c,L2C}\right) \right),\\ e_{L2CQ}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{NAV}} \Big[ [l]_{20460} \Big] \oplus C_{\text{C/A}} \Big[ |l|_{1023} \Big] p \left(t - lT_{c,\text{C/A}}\right) \text{, or}\\ e_{L2CQ}(t)=& \sum_{l=-\infty}^{\infty}C_{\text{C/A}} \Big[ |l|_{1023} \Big] p(t - lT_{c,\text{C/A}})~, \f} where \f$T_{c,L2C}=\frac{1}{511.5}\f$ ms and \f$p_{\text{\tiny{1/2}}}(t)\f$ is a rectangular pulse of half chip-period duration, thus time-multiplexing both codes. The civilian long code \f$C_{\text{CL}}\f$ is \f$L_{\text{CL}}=767250\f$ chips long, repeating every \f$1.5\f$ s, while the civilian moderate code \f$C_{\text{CM}}\f$ is \f$L_{\text{CL}}=10230\f$ chips long and its repeats every \f$20\f$ ms. The CNAV data is an upgraded version of the original NAV navigation message, containing higher precision representation and nominally more accurate data than the NAV data. It is transmitted at \f$25\f$ bps with forward error correction (FEC) encoding, resulting in \f$50\f$ sps. \li GPS L5. The GPS L5 link, defined at Interface Specification IS-GPS-705 Revision B, is only available in Block IIF (first satellite launched on May, 2010) and subsequent satellite blocks. Centered at \f$f_{\text{GPS L5}}=1176.45\f$ MHz, this signal in space can be written as: \f{equation}{ s^{\text{(GPS L5)}}_{T}(t)=e_{L5I}(t) +j e_{L5Q}(t)~, \f} \f{align}{ e_{L5I}(t) =& \sum_{m=-\infty}^{+\infty} C_{nh_{10}} \Big[ |m|_{10}\Big] \oplus \ D_{\text{CNAV}}\Big[ [m]_{10}\Big] \oplus \\ \nonumber {} & \oplus \sum_{l=1}^{102300} C_{L5I}\Big[|l|_{10230}\Big] p(t - m T_{c,nh} - lT_{c,L5}) ~, \\ \nonumber e_{L5Q}(t) =& \sum_{m=-\infty}^{+\infty} C_{nh_{20}} \Big[ |m|_{20}\Big] \oplus \sum_{l=1}^{102300}C_{L5Q}\Big[|l|_{10230}\Big] \cdot \\ {} & \cdot p(t - m T_{c,nh} - lT_{c,L5})~, \f} where \f$T_{c,nh}=1\f$ ms and \f$T_{c,L5}=\frac{1}{10.23}\f$ \f$\mu\f$s, thus defining a BPSK(10) modulation. Both L5I and L5Q contain synchronization sequences. \end{itemize} \subsection glonass_signal GLONASS signal in space The nominal baseline constellation of the Russian Federation's Global Navigation Satellite System (GLONASS) comprises \f$24\f$ GLONASS-M satellites that are uniformly deployed in three roughly circular orbital planes at an inclination of \f$64.8^o\f$ to the equator. The altitude of the orbit is \f$19,100\f$ km. The orbit period of each satellite is \f$11\f$ hours, \f$15\f$ minutes, and \f$45\f$ seconds. The orbital planes are separated by \f$120^o\f$ right ascension of the ascending node. Eight satellites are equally spaced in each plane with \f$45^o\f$ argument of latitude. Moreover, the orbital planes have an argument of latitude displacement of \f$15^o\f$ relative to each other. GLONASS civil signal-in-space is defined at Interface Control Document. Navigational radiosignal in bands L1, L2. Edition 5.1. This system makes use of a frequency-division multiple access (FDMA) signal structure, transmitting in two bands: \f$f^{(k)}_{GLO L1}=1602+k \cdot 0.5625\f$ MHz and \f$f^{(k)}_{GLO L2}=1246+k \cdot 0.4375\f$ MHz, where \f$k\in \left\{ -7,-6,\cdots,5,6\right\}\f$ is the channel number. Satellites in opposite points of an orbit plane transmit signals on equal frequencies, as these satellites will never be in view simultaneously by a ground-based user. \li GLONASS L1. Two kind of signals are transmitted: a standard precision (SP) and an obfuscated high precision (HP) signal. The complex baseband transmitted signal can be written as \f{equation}{ s^{\text{(GLO L1)}}_{T}(t)=e_{L1I}(t) + j e_{L1Q}(t)~, \f} with BPSK(5) and BPSK(0.5) modulations: \f{align}{ e_{L1I}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{GNAV}}\Big[ [l]_{102200}\Big] \oplus C_{\text{HP}} \Big[ |l|_{L_{\text{HP}}} \Big] p(t - lT_{c,\text{HP}})~,\\ e_{L1Q}(t) =& \sum_{l=-\infty}^{\infty} D_{\text{GNAV}}\Big[ [l]_{10220} \Big] \oplus C_{\text{SP}} \Big[ |l|_{511} \Big] p(t - lT_{c,\text{SP}})~, \f} where \f$T_{c,\text{HP}}=\frac{1}{5.11}\f$ \f$\mu\f$s, \f$T_{c,\text{SP}}=\frac{1}{0.511}\f$ \f$\mu\f$s, and \f$L_{\text{HP}}=3.3554\cdot 10^7\f$. The navigation message \f$D_{\text{GNAV}}\f$ is transmitted at \f$50\f$ bps. Details of its content and structure, as well as the generation of the \f$C_{\text{SP}}\f$ code, can be found at the ICD. The usage of the HP signal should be agreed with the Russian Federation Defense Ministry, and no more details have been disclosed. \li GLONASS L2. Beginning with the second generation of satellites, called GLONASS-M and first launched in 2001, a second civil signal is available using the same SP code than the one in the L1 band. The use of FDMA techniques, in which the same code is used to broadcast navigation signals on different frequencies, and the placement of civil GLONASS transmissions on frequencies close to \f$1600\f$ MHz, well above the GPS L1 band, have complicated the design of combined GLONASS/GPS receivers, particularly low-cost equipment for mass-market applications. Future plans of modernization are intended to increase compatibility and interoperability with other GNSS, and include the addition of a code-division multiple access (CDMA) structure, and possibly binary offset carrier (BOC) modulation, beginning with the third civil signal in the L3 band (\f$1197.648 - 1212.255\f$ MHz). Russia is implementing the new signals on the next-generation GLONASS-K satellites, with a first prototype successfully launched into orbit on February 26, 2011. \subsection galileo_signal Galileo signal in space The nominal Galileo constellation comprises a total of \f$27\f$ operational satellites (plus \f$3\f$ active spares), that are evenly distributed among three orbital planes inclined at \f$56^o\f$ relative to the equator. There are nine operational satellites per orbital plane, occupying evenly distributed orbital slots. Three additional spare satellites (one per orbital plane) complement the nominal constellation configuration. The Galileo satellites are placed in quasi-circular Earth orbits with a nominal semi-major axis of about \f$30,000\f$ km and an approximate revolution period of \f$14\f$ hours. The Control segment full infrastructure will be composed of \f$30-40\f$ sensor stations, \f$3\f$ control centers, \f$9\f$ Mission Uplink stations, and \f$5\f$ TT\&C stations. Galileo's Open Service is defined at Signal In Space Interface Control Document. Ref: OS SIS ICD, Issue 1.1, where the following signal structures are specified: \li Galileo E1. This band, centered at \f$f_{\text{Gal E1}}=1575.420\f$ MHz and with a reference bandwidth of \f$24.5520\f$ MHz, uses the so-called composite binary offset carrier CBOC(6,1,\f$\frac{1}{11}\f$) modulation, defined in baseband as: \f{align}{ s^{\text{(Gal E1)}}_{T}(t)=&\frac{1}{\sqrt{2}} \Big( e_{E1B}(t)\left( \alpha sc_A(t)+ \beta sc_B(t) \right)+ \\ {}& - e_{E1C}(t) \left( \alpha sc_A(t)- \beta sc_B(t) \right) \Big)~,\label{eq:CBOC} \f} where the subcarriers \f$sc(t)\f$ are defined as \f{align}{ sc_A(t)=& \; \text{sign}\Big(\sin(2\pi f_{s,E1A}t) \Big)~,\\ sc_B(t)=& \; \text{sign} \Big( \sin( 2 \pi f_{s, E1B}t) \Big)~, \f} and \f$f_{s,E1A}=1.023\f$ MHz, \f$f_{s, E1B}=6.138\f$ MHz are the subcarrier rates, \f$\alpha=\sqrt{\frac{10}{11}}\f$, and \f$\beta=\sqrt{\frac{1}{11}}\f$. Channel B contains the I/NAV type of navigation message, \f$D_{\text{I/NAV}}\f$, intended for Safety-of-Life (SoL) services: \f{align}{ e_{E1B}(t) &= \sum_{l=-\infty}^{+\infty} D_{\text{I/NAV}} \Big[ [l]_{4092}\Big] \oplus C_{E1B}\Big[|l|_{4092}\Big] p(t - lT_{c,E1B}). \f} In case of channel \f$C\f$, it is a pilot (dataless) channel with a secondary code, forming a tiered code: \f{align}{ \nonumber e_{E1C}(t)&= \sum_{m=-\infty}^{+\infty}C_{E1Cs}\Big[|m|_{25}\Big] \oplus \sum_{l=1}^{4092}C_{E1Cp}\Big[ l \Big] \cdot \\ {}& \; \; \cdot p(t-mT_{c,E1Cs}-lT_{c,E1Cp})~,\label{eq:E1C} \f} with \f$T_{c,E1B}=T_{c,E1Cp}=\frac{1}{1.023}\f$ \f$\mu\f$s and \f$T_{c,E1Cs}=4\f$ ms. The \f$C_{E1B}\f$ and \f$C_{E1Cp}\f$ primary codes are pseudorandom memory code sequences defined at Annex C.7 and C.8 of OS SIS ICD. The binary sequence of the secondary code \f$C_{E1Cs}\f$ is 0011100000001010110110010. This band also contains another component, Galileo E1A, intended for the Public Regulated Service (PRS). It uses a BOC(15,2.5) modulation with cosine-shaped subcarrier \f$f_{s,E1A}=15.345\f$ MHz and \f$T_{c, E1A}=\frac{1}{2.5575}\f$ \f$\mu\f$s. The PRS spreading codes and the structure of the navigation message have not been made public. \li Galileo E6. Intended for the Commercial Service and centered at \f$f_{\text{Gal E6}}=1278.750\f$ MHz, this band provides pilot and data components \f{equation}{ s_{T}^{\text{(Gal E6)}}(t) = \frac{1}{\sqrt{2}}\left(e_{E6B}(t)-e_{E6C}(t)\right){~}, \f} \f{align}{ \nonumber e_{E6B}(t) =& \sum_{m=-\infty}^{+\infty} D_{\text{C/NAV}} \Big[ [l]_{5115}\Big] \oplus C_{E6B}\Big[|l|_{L_{E6B}}\Big] \cdot \\ {}& \cdot p(t - lT_{c,E6}),\\ \nonumber e_{E6C}(t) =& \sum_{m=-\infty}^{+\infty}C_{E6Cs}\Big[|m|_{100}\Big] \oplus \sum_{l=1}^{L_{E6C}}C_{E6Cp}\Big[ l \Big] \cdot \\ {}& \cdot p(t-mT_{c,E6s} -lT_{c,E6p}), \f} where \f$D_{\text{C/NAV}}\f$ is the C/NAV navigation data stream, which is modulated with the encrypted ranging code \f$C_{E6B}\f$ with chip period \f$T_{c,E6}=\frac{1}{5.115}\f$ \f$\mu\f$s, thus being a BPSK(5) modulation. Codes \f$C_{E6B}\f$ and primary codes \f$C_{E6Cs}\f$ and their respective lengths, \f$L_{E6B}\f$ and \f$L_{E6C}\f$, have not been published. The secondary codes for the pilot component, \f$C_{E6Cs}\f$, are available at the OS SIS ICD. The receiver reference bandwidth for this signal is \f$40.920\f$ MHz. This band also contains another component, Galileo E6A, intended for PRS. \li Galileo E5. Centered at \f$f_{\text{Gal E5}}=1191.795\f$ MHz and with a total bandwidth of \f$51.150\f$ MHz, its signal structure deserves some analysis. The AltBOC modulation can be generically expressed as \f{equation}{\label{AltBOC} s^{\text{AltBOC}}(t)=x_1(t)v^{*}(t)+x_2(t)v(t)~, \f} where \f$v(t)=\frac{1}{\sqrt{2}}\left( \text{sign}\left( \cos (2 \pi f_s t)\right)+j \text{sign}\left( \sin (2 \pi f_s t)\right)\right)\f$ is the single side-band subcarrier, \f$f_s\f$ is the subcarrier frequency, \f$(\cdot)^{*}\f$ stands for the conjugate operation, and \f$x_1(t)\f$ and \f$x_2(t)\f$ are QPSK signals. The resulting waveform does not exhibit constant envelope. In case of Galileo, the need for high efficiency of the satellites' onboard High Power Amplifier (HPA) has pushed a modification on the signal in order to make it envelope-constant and thus use the HPA at saturation. This can be done by adding some inter-modulation products to the expression above, coming up with the following definition: \f{align}{ \nonumber s^{\text{(Gal E5)}}_{T}(t)= & e_{E5a}(t) ssc_s^{*}(t)+ e_{E5b}(t) ssc_s(t) + \\ {} & +\bar{e}_{E5a}(t)ssc_p^{*}(t)+\bar{e}_{E5b}(t)ssc_p(t)~,\label{GalE5} \f} where the single and product side-band signal subcarriers are \f{align}{ ssc_s(t)=& sc_s(t) +jsc_s\left(t-\frac{T_s}{4}\right) ~,\label{sscs}\\ ssc_p(t)=& sc_p(t) +jsc_p\left(t-\frac{T_s}{4}\right) ~,\label{sscp} \f} and \f{align}{ e_{E5a}(t)=&e_{E5aI}(t)+je_{E5aQ}(t),\label{E5a}\\ e_{E5b}(t)=&e_{E5bI}(t)+je_{E5bQ}(t),\\ \bar{e}_{E5a}(t)&=\bar{e}_{E5aI}(t)+j\bar{e}_{E5aQ}(t),\\ \bar{e}_{E5b}(t)&=\bar{e}_{E5bI}(t)+j\bar{e}_{E5bQ}(t),\\ \bar{e}_{E5aI}(t)=& e_{E5aQ}(t)e_{E5bI}(t)e_{E5bQ}(t),\\ \bar{e}_{E5aQ}(t)=& e_{E5aI}(t)e_{E5bI}(t)e_{E5bQ}(t),\\ \bar{e}_{E5bI}(t)=& e_{E5bQ}(t)e_{E5aI}(t)e_{E5aQ}(t),\\ \bar{e}_{E5bQ}(t)=&e_{E5bI}(t)e_{E5aI}(t)e_{E5aQ}(t). \f} The signal components are defined as \f{align}{ e_{E5aI}(t)=& \sum_{m=-\infty}^{+\infty}C_{E5aIs}\Big[|m|_{20}\Big] \oplus \sum_{l=1}^{10230}C_{E5aIp}\Big[ l \Big] \oplus \\ {}& \oplus D_{\text{F/NAV}} \Big[ [l]_{204600}\Big] p(t-mT_{c,E5s}-lT_{c,E5p}),\\ e_{E5aQ}(t)=& \sum_{m=-\infty}^{+\infty}C_{E5aQs}\Big[|m|_{100}\Big] \oplus \sum_{l=1}^{10230}C_{E5aQp}\Big[ l \Big] \cdot \\ {}& \cdot p(t-mT_{c,E5s}-lT_{c,E5p}),\\ e_{E5bI}(t)=& \sum_{m=-\infty}^{+\infty}C_{E5bIs}\Big[|m|_{4}\Big] \oplus \sum_{l=1}^{10230}C_{E5aIp}\Big[ l \Big] \oplus \\ {}& \oplus D_{\text{I/NAV}} \Big[ [l]_{40920}\Big] p(t-mT_{c,E5s}-lT_{c,E5p}),\\ e_{E5bQ}(t)=& \sum_{m=-\infty}^{+\infty}C_{E5bQs}\Big[|m|_{100}\Big] \oplus \sum_{l=1}^{10230}C_{E5bQp}\Big[ l \Big] \cdot \\ {}& \cdot p(t-mT_{c,E5s}-lT_{c,E5p}), \f} where \f$T_{c,E5s}=1 \f$ ms and \f$T_{c,E5p}=\frac{1}{10.23}\f$ \f$\mu\f$s. Channel A contains the F/NAV type of navigation message, \f$D_{\text{F/NAV}}\f$, intended for the Open Service. The I/NAV message structures for the E5bI and E1B signals use the same page layout. Only page sequencing is different, with page swapping between both components in order to allow a fast reception of data by a dual frequency receiver. The single subcarrier \f$sc_s(t)\f$ and the product subcarrier \f$sc_p(t)\f$ are defined as: \f{align}{ sc_s(t)=& \frac{\sqrt{2}}{4}\text{sign} \left( \cos \left( 2 \pi f_s t - \frac{\pi}{4}\right) \right)+\\ \nonumber {}&+ \frac{1}{2}\text{sign} \Big( \cos \left( 2 \pi f_s t \right) \Big)+\\ {} &+\frac{\sqrt{2}}{4}\text{sign} \left( \cos \left( 2 \pi f_s t + \frac{\pi}{4}\right) \right)~, \f} \f{align}{ sc_p(t)=& -\frac{\sqrt{2}}{4}\text{sign} \left( \cos \left( 2 \pi f_s t - \frac{\pi}{4}\right) \right)+\\ \nonumber {}&+ \frac{1}{2}\text{sign} \Big( \cos \left( 2 \pi f_s t \right) \Big)+\\ {} &-\frac{\sqrt{2}}{4}\text{sign} \left( \cos \left( 2 \pi f_s t + \frac{\pi}{4}\right) \right)~, \f} with a subcarrier frequency of \f$f_s=15.345\f$ MHz, thus defining an AltBOC(15,10) modulation. The QPSK(10) signal \f$e_{E5a}(t)\f$ defined above is shifted to \f$f_{\text{Gal E5a}}\doteq f_{\text{Gal E5}}-f_s=1176.450\f$ MHz, while \f$e_{E5b}(t)\f$ is shifted to \f$f_{\text{Gal E5b}}\doteq f_{\text{Gal E5}}+f_s=1207.140\f$ MHz. Thus, we can bandpass filter around \f$f_{\text{Gal E5a}}\f$ and get a good approximation of a QPSK(10) signal, with very low energy components of \f$e_{E5b}(t)\f$, \f$ \bar{e}_{E5a}(t)\f$, and \f$ \bar{e}_{E5b}(t)\f$: \f{equation}{ s_{T}^{\text{(Gal E5a)}}(t) \simeq e_{E5aI}(t)+je_{E5aQ}(t). \f} The same applies to \f$e_{E5b}(t)\f$, allowing an independent reception of two QPSK(10) signals and thus requiring considerably less bandwidth than the processing of the whole E5 band. \subsection reference_paper Reference This text is an except of the following paper: \li C. Fernández-Prades, L. Lo Presti, E. Falleti, Satellite Radiolocalization From GPS to GNSS and Beyond: Novel Technologies and Applications for Civil Mass-Market. Proceedings of the IEEE. Vol 99, No. 11, pp. 1882-1904. November, 2011. DOI: 10.1109/JPROC.2011.2158032 */ docs/manpage/000077500000000000000000000000001352176506000134235ustar00rootroot00000000000000docs/manpage/front-end-cal-manpage000066400000000000000000000060251352176506000174100ustar00rootroot00000000000000.\" Manpage for front\-end\-cal. .\" Contact javier.arribas@cttc.es to correct errors or typos. .TH front\-end\-cal 1 "15 Dec 2014" "0.0.1" "front\-end\-cal man page" .SH NAME \fBfront\-end\-cal\fR \- RF front\-end center frequency and sampling rate calibration tool. .SH SYNOPSIS \fBfront\-end\-cal \-config_file=\fR\fI\fR [OPTION]... .SH DESCRIPTION \fBfront\-end\-cal\fR is a calibration tool for some DVB\-T receivers based on the Taiwan's Realtek RTL2832U chipset, sold in form of USB dongles that allow users to watch over\-the\-air DVB\-T European broadcast television on their personal computers, to be used as GNSS front\-ends. \.TP Normally, those devices send partially\-decoded MPEG transport frames over the USB, but exploiting an undocumented mode of operation of the demodulator chip, the user is able to obtain raw I&Q samples, stream them through USB to a personal computer and then use \fBgnss\-sdr\fR, turning the DVB\-T receiver into a GNSS receiver and delivering position in real\-time. \.TP The crystal oscillator that ships with the RTL2832U family devices exhibits limited accuracy to be used as a GNSS receiver front\-end without previous calibration. \fBfront\-end\-cal\fR implements the algorithm proposed in [1], and it requires Internet access since it retrieves Assisted GPS data from SUPL servers. \.TP \fBfront\-end\-cal\fR is able to work with raw data files or, if there is computational power enough, in real time with suitable radio frequency front\-ends. The whole receiver is defined in a single configuration file, and therefore users can define theirs. .SH OPTIONS \fBfront\-end\-cal\fR takes the following options: .TP \fB\-config_file=\fR\fI\fR Set the configuration file. .TP \fB\-signal_source=\fR\fI\fR If defined, path to the file containing the signal samples (overrides the data file specified in the configuration file). .TP \fB\-log_dir=\fR\fI\fR If defined, overrides the default directory where logs are saved. .TP \fB\-version\fR Print program version and exit. .TP \fB\-help\fR Print all the available commandline flags and exit. .SH SEE ALSO .BR gnss\-sdr (1), volk_gnsssdr_profile (1) \.TP Example of configuration file available at: ${prefix}/share/gnss\-sdr/conf/front\-end\-cal.conf, where ${prefix}$ uses to be /usr or /usr/local. This will be the configuration file used by default if the \fB\-config_file\fR option is not set. \.TP [1] C. Fernandez\-Prades, J. Arribas, P. Closas, \fITurning a Television into a GNSS Receiver\fR, in Proceedings of ION GNSS+, 15\-16 September 2013, Nashville, Tennessee (USA). A draft copy is freely available at http://www.cttc.es/publication/turning\-a\-television\-into\-a\-gnss\-receiver/ \.TP Check https://gnss\-sdr.org for more information. .SH BUGS No known bugs. .SH AUTHOR Javier Arribas (javier.arribas@cttc.es) \.TP This software has been developed at CTTC (Centre Tecnologic de Telecomunicacions de Catalunya, http://www.cttc.es) with contributions from around the world. docs/manpage/gnss-sdr-manpage000066400000000000000000000072771352176506000165310ustar00rootroot00000000000000.\" Manpage for gnss\-sdr. .\" Contact carles.fernandez@cttc.es to correct errors or typos. .TH gnss\-sdr 1 "29 Jul 2019" "0.0.11" "gnss\-sdr man page" .SH NAME \fBgnss\-sdr\fR \- GNSS Software Defined Receiver. .SH SYNOPSIS \fBgnss\-sdr \-c=\fR\fI\fR [OPTION]... .SH DESCRIPTION \fBgnss\-sdr\fR is a Global Navigation Satellite Systems Software Defined Receiver written in C++. It implements all the signal processing chain, taking as input raw samples coming from the output of an Analog\-to\-Digital Converter, and processing them up to the computation of the Position\-Velocity\-Time solution, including the generation of code and phase measurements. \.TP \fBgnss\-sdr\fR is able to work with raw data files or, if there is computational power enough, in real time with suitable radio frequency front\-ends. The whole receiver is defined in a single configuration file, and therefore users can define theirs. \.TP There is some flexibility in how flags may be specified. The following examples are equivalent: \.RS 8 \.TP gnss\-sdr \-c=/home/user/rx.conf \.TP gnss\-sdr \-\-c=/home/user/rx.conf \.TP gnss\-sdr \-c /home/user/rx.conf \.TP gnss\-sdr \-\-c /home/user/rx.conf \.RE .SH OPTIONS \fBgnss\-sdr\fR takes the following options: .TP \fB\-c=\fR\fI\fR or \fB\-config_file=\fR\fI\fR Set the configuration file. This flag is mandatory. .TP \fB\-s=\fR\fI\fR or \fB\-signal_source=\fR\fI\fR If defined, path to the file containing the signal samples (overrides the data file specified in the configuration file). .TP \fB\-log_dir=\fR\fI\fR If defined, overrides the default directory where logs are saved. .TP \fB\-doppler_max=\fR\fI\fR If defined, maximum Doppler value in the search grid, in Hz (overrides the configuration file). .TP \fB\-doppler_step=\fR\fI\fR If defined, sets the frequency step in the search grid, in Hz (overrides the configuration file). .TP \fB\-cn0_samples=\fR\fI\fR Number of correlators outputs (one per integration time) used for CN0 estimation. It defaults to 20 outputs. .TP \fB\-cn0_min=\fR\fI\fR Minimum valid CN0 (in dB-Hz). It defaults to 25 dB-Hz. If set, it overrides the configuration file. .TP \fB\-max_lock_fail=\fR\fI\fR Maximum number of lock failures before dropping a satellite. It defaults to 50 failures. If set, it overrides the configuration file. .TP \fB\-carrier_lock_th=\fR\fI\fR Carrier lock error threshold (in rad). It defaults to 0.85 rad (48.7 degrees). If set, it overrides the configuration file. .TP \fB\-dll_bw_hz=\fR\fI\fR If defined, bandwidth of the DLL low pass filter, in Hz (overrides the configuration file). .TP \fB\-pll_bw_hz=\fR\fI\fR If defined, bandwidth of the PLL low pass filter, in Hz (overrides the configuration file). .TP \fB\-RINEX_version=\fI\fR If defined, specifies the RINEX version (2.11 or 3.02). Default: "3.02". Overrides the configuration file. .TP \fB\-version\fR Print program version and exit. .TP \fB\-help\fR Print all the available commandline flags and exit. .SH SEE ALSO .BR volk_gnsssdr_profile (1) \.TP Examples of configuration files available at: ${prefix}/share/gnss\-sdr/conf, where ${prefix} uses to be /usr or /usr/local. \.TP Check https://gnss\-sdr.org for more information. .SH BUGS Please report bugs at https://github.com/gnss-sdr/gnss-sdr/issues .SH AUTHOR Carles Fernandez\-Prades (carles.fernandez@cttc.es) \.TP This software package has been developed at CTTC (Centre Tecnologic de Telecomunicacions de Catalunya, http://www.cttc.es) with contributions from around the world. docs/protobuf/000077500000000000000000000000001352176506000136535ustar00rootroot00000000000000docs/protobuf/README.md000066400000000000000000000021661352176506000151370ustar00rootroot00000000000000# Custom structured data format definitions Files in this folder describe structured data formats that are generated by GNSS-SDR. They use [Protocol Buffers](https://developers.google.com/protocol-buffers/)' [proto3](https://developers.google.com/protocol-buffers/docs/proto3) syntax. From those files, the protocol buffer compiler creates classes that implement automatic encoding and parsing of the protocol buffer data with an efficient binary format. The generated classes provide getters and setters for the fields that make up a protocol buffer and take care of the details of reading and writing it as a unit. Importantly, the protocol buffer format supports the idea of extending the format over time in such a way that the code can still read data encoded with the old format. Just grab these files if you are developing a client application for GNSS-SDR. You are free to use C++, Java, Python, C#, Dart, Go or Ruby, among other languages. A tutorial to create a simple application using Protocol Buffers and a `.proto` file in C++ is available at https://gnss-sdr.org/docs/tutorials/monitoring-software-receiver-internal-status/ docs/protobuf/gnss_synchro.proto000066400000000000000000000045011352176506000174570ustar00rootroot00000000000000syntax = "proto3"; package gnss_sdr; /* GnssSynchro represents the processing measurements at a given time taken by a given processing channel */ message GnssSynchro { string system = 1; // GNSS constellation: "G" for GPS, "R" for Glonass, "S" for SBAS, "E" for Galileo and "C" for Beidou. string signal = 2; // GNSS signal: "1C" for GPS L1 C/A, "1B" for Galileo E1b/c, "1G" for Glonass L1 C/A, "2S" for GPS L2 L2C(M), "2G" for Glonass L2 C/A, "L5" for GPS L5 and "5X" for Galileo E5a uint32 prn = 3; // PRN number int32 channel_id = 4; // Channel number double acq_delay_samples = 5; // Coarse code delay estimation, in samples double acq_doppler_hz = 6; // Coarse Doppler estimation in each channel, in Hz uint64 acq_samplestamp_samples = 7; // Number of samples at signal SampleStamp uint32 acq_doppler_step = 8; // Step of the frequency bin in the search grid, in Hz bool flag_valid_acquisition = 9; // Acquisition status int64 fs = 10; // Sampling frequency, in samples per second double prompt_i = 11; // In-phase (real) component of the prompt correlator output double prompt_q = 12; // Quadrature (imaginary) component of the prompt correlator output double cn0_db_hz = 13; // Carrier-to-Noise density ratio, in dB-Hz double carrier_doppler_hz = 14; // Doppler estimation, in [Hz]. double carrier_phase_rads = 15; // Carrier phase estimation, in rad double code_phase_samples = 16; // Code phase in samples uint64 tracking_sample_counter = 17; // Sample counter indicating the number of processed samples bool flag_valid_symbol_output = 18; // Indicates the validity of signal tracking int32 correlation_length_ms = 19; // Time duration of coherent correlation integration, in ms bool flag_valid_word = 20; // Indicates the validity of the decoded navigation message word uint32 tow_at_current_symbol_ms = 21; // Time of week of the current symbol, in ms double pseudorange_m = 22; // Pseudorange computation, in m double rx_time = 23; // Receiving time after the start of the week, in s bool flag_valid_pseudorange = 24; // Pseudorange computation status double interp_tow_ms = 25; // Interpolated time of week, in ms } /* Observables represents a collection of GnssSynchro annotations */ message Observables { repeated GnssSynchro observable = 1; } docs/protobuf/monitor_pvt.proto000066400000000000000000000035371352176506000173300ustar00rootroot00000000000000syntax = "proto3"; package gnss_sdr; /* MonitorPvt represents a search query, with pagination options to * indicate which results to include in the response. */ message MonitorPvt { uint32 tow_at_current_symbol_ms = 1; // Time of week of the current symbol, in ms uint32 week = 2; // PVT GPS week double rx_time = 3; // PVT GPS time double user_clk_offset = 4; // User clock offset, in s double pos_x = 5; // Position X component in ECEF, expressed in m double pos_y = 6; // Position Y component in ECEF, expressed in m double pos_z = 7; // Position Z component in ECEF, expressed in m double vel_x = 8; // Velocity X component in ECEF, in m/s double vel_y = 9; // Velocity Y component in ECEF, in m/s double vel_z = 10; // Velocity Z component in ECEF, in m/s double cov_xx = 11; // Position variance in the Y component, in m2 double cov_yy = 12; // Position variance in the Y component, in m2 double cov_zz = 13; // Position variance in the Z component, in m2 double cov_xy = 14; // Position XY covariance, in m2 double cov_yz = 15; // Position YZ covariance, in m2 double cov_zx = 16; // Position ZX covariance, in m2 double latitude = 17; // Latitude, in deg. Positive: North double longitude = 18; // Longitude, in deg. Positive: East double height = 19; // Height, in m uint32 valid_sats = 20; // Number of valid satellites uint32 solution_status = 21; // RTKLIB solution status uint32 solution_type = 22; // RTKLIB solution type (0: xyz-ecef, 1: enu-baseline) float ar_ratio_factor = 23; // Ambiguity resolution ratio factor for validation float ar_ratio_threshold = 24; // Ambiguity resolution ratio threshold for validation double gdop = 25; // Geometric Dilution of Precision double pdop = 26; // Position (3D) Dilution of Precision double hdop = 27; // Horizontal Dilution of Precision double vdop = 28; // Vertical Dilution of Precision } docs/xml-schemas/000077500000000000000000000000001352176506000142345ustar00rootroot00000000000000docs/xml-schemas/README.md000066400000000000000000000033451352176506000155200ustar00rootroot00000000000000# XML Schemas for Assisted GNSS-SDR GNSS-SDR can read assistance data from [Extensible Markup Language (XML)](https://www.w3.org/XML/) files for faster [Time-To-First-Fix](https://gnss-sdr.org/design-forces/availability/#time-to-first-fix-ttff), and can store navigation data decoded from GNSS signals in the same format. This folder provides XML Schemas which describe those XML files structure. [XSD (XML Schema Definition)](https://www.w3.org/XML/Schema) is a World Wide Web Consortium (W3C) recommendation that specifies how to formally describe the elements in an XML document. GPS L1 C/A ---------- - [ephemeris_map.xsd](./ephemeris_map.xsd) - GPS NAV message ephemeris parameters. - [iono_model.xsd](./iono_model.xsd) - GPS NAV message ionospheric model parameters. - [utc_model.xsd](./utc_model.xsd) - GPS NAV message UTC model parameters. - [gps_almanac_map.xsd](./gps_almanac_map.xsd) - GPS NAV message almanac. GPS L2C and L5 -------------- - [cnav_ephemeris_map.xsd](./cnav_ephemeris_map.xsd) - GPS CNAV message ephemeris parameters. Galileo ------- - [gal_ephemeris_map.xsd](./gal_ephemeris_map.xsd) - Galileo ephemeris parameters. - [gal_iono_model.xsd](./gal_iono_model.xsd) - Galileo ionospheric model parameters. - [gal_utc_model.xsd](./gal_utc_model.xsd) - Galileo UTC model parameters. - [gal_almanac_map.xsd](./gal_almanac_map.xsd) - Galileo almanac. ------- Please check https://gnss-sdr.org/docs/sp-blocks/global-parameters/ for more information about the usage of XML files in GNSS-SDR. You could find useful the utility program [rinex2assist](https://github.com/gnss-sdr/gnss-sdr/tree/next/src/utils/rinex2assist) for the generation of compatible XML files from recent, publicly available RINEX navigation data files. docs/xml-schemas/cnav_ephemeris_map.xsd000066400000000000000000000101671352176506000206060ustar00rootroot00000000000000 docs/xml-schemas/ephemeris_map.xsd000066400000000000000000000110701352176506000175710ustar00rootroot00000000000000 docs/xml-schemas/gal_almanac_map.xsd000066400000000000000000000057621352176506000200420ustar00rootroot00000000000000 docs/xml-schemas/gal_ephemeris_map.xsd000066400000000000000000000107071352176506000204220ustar00rootroot00000000000000 docs/xml-schemas/gal_iono_model.xsd000066400000000000000000000025141352176506000177250ustar00rootroot00000000000000 docs/xml-schemas/gal_utc_model.xsd000066400000000000000000000023761352176506000175620ustar00rootroot00000000000000 docs/xml-schemas/gps_almanac_map.xsd000066400000000000000000000055551352176506000200700ustar00rootroot00000000000000 docs/xml-schemas/iono_model.xsd000066400000000000000000000023051352176506000171000ustar00rootroot00000000000000 docs/xml-schemas/utc_model.xsd000066400000000000000000000023621352176506000167320ustar00rootroot00000000000000 install/000077500000000000000000000000001352176506000125315ustar00rootroot00000000000000install/.gitignore000066400000000000000000000001061352176506000145160ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignoresrc/000077500000000000000000000000001352176506000116525ustar00rootroot00000000000000src/CMakeLists.txt000066400000000000000000000016271352176506000144200ustar00rootroot00000000000000# Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(algorithms) add_subdirectory(core) add_subdirectory(main) if(ENABLE_UNIT_TESTING OR ENABLE_SYSTEM_TESTING) add_subdirectory(tests) endif() add_subdirectory(utils) src/algorithms/000077500000000000000000000000001352176506000140235ustar00rootroot00000000000000src/algorithms/CMakeLists.txt000066400000000000000000000021411352176506000165610ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(acquisition) add_subdirectory(channel) add_subdirectory(libs) add_subdirectory(conditioner) add_subdirectory(data_type_adapter) add_subdirectory(observables) add_subdirectory(telemetry_decoder) add_subdirectory(resampler) add_subdirectory(signal_generator) add_subdirectory(signal_source) add_subdirectory(input_filter) add_subdirectory(tracking) add_subdirectory(PVT) src/algorithms/PVT/000077500000000000000000000000001352176506000144745ustar00rootroot00000000000000src/algorithms/PVT/CMakeLists.txt000066400000000000000000000014631352176506000172400ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) add_subdirectory(gnuradio_blocks) add_subdirectory(libs) src/algorithms/PVT/adapters/000077500000000000000000000000001352176506000162775ustar00rootroot00000000000000src/algorithms/PVT/adapters/CMakeLists.txt000066400000000000000000000032321352176506000210370ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(PVT_ADAPTER_SOURCES rtklib_pvt.cc ) set(PVT_ADAPTER_HEADERS rtklib_pvt.h ) source_group(Headers FILES ${PVT_ADAPTER_HEADERS}) add_library(pvt_adapters ${PVT_ADAPTER_SOURCES} ${PVT_ADAPTER_HEADERS}) target_link_libraries(pvt_adapters PUBLIC pvt_gr_blocks algorithms_libs_rtklib core_system_parameters PRIVATE gnss_sdr_flags pvt_libs ) target_include_directories(pvt_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/core/interfaces ) if(Boost_VERSION_STRING VERSION_LESS 1.58.0) target_compile_definitions(pvt_adapters PRIVATE -DOLD_BOOST=1) endif() if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(pvt_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET pvt_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/PVT/adapters/rtklib_pvt.cc000066400000000000000000001503051352176506000207720ustar00rootroot00000000000000/*! * \file rtklib_pvt.cc * \brief Interface of a Position Velocity and Time computation block * \author Javier Arribas, 2017. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rtklib_pvt.h" #include "MATH_CONSTANTS.h" // for D2R #include "configuration_interface.h" // for ConfigurationInterface #include "galileo_almanac.h" // for Galileo_Almanac #include "galileo_ephemeris.h" // for Galileo_Ephemeris #include "gnss_sdr_flags.h" // for FLAGS_RINEX_version #include "gps_almanac.h" // for Gps_Almanac #include "gps_ephemeris.h" // for Gps_Ephemeris #include "pvt_conf.h" // for Pvt_Conf #include "rtklib_rtkpos.h" // for rtkfree, rtkinit #include // for LOG #include // for operator<< #if OLD_BOOST #include namespace bc = boost::math; #else #include namespace bc = boost::integer; #endif Rtklib_Pvt::Rtklib_Pvt(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { Pvt_Conf pvt_output_parameters = Pvt_Conf(); // dump parameters std::string default_dump_filename = "./pvt.dat"; std::string default_nmea_dump_filename = "./nmea_pvt.nmea"; std::string default_nmea_dump_devname = "/dev/tty1"; std::string default_rtcm_dump_devname = "/dev/pts/1"; DLOG(INFO) << "role " << role; pvt_output_parameters.dump = configuration->property(role + ".dump", false); pvt_output_parameters.dump_filename = configuration->property(role + ".dump_filename", default_dump_filename); pvt_output_parameters.dump_mat = configuration->property(role + ".dump_mat", true); // output rate pvt_output_parameters.output_rate_ms = bc::lcm(20, configuration->property(role + ".output_rate_ms", 500)); // display rate pvt_output_parameters.display_rate_ms = bc::lcm(pvt_output_parameters.output_rate_ms, configuration->property(role + ".display_rate_ms", 500)); // NMEA Printer settings pvt_output_parameters.flag_nmea_tty_port = configuration->property(role + ".flag_nmea_tty_port", false); pvt_output_parameters.nmea_dump_filename = configuration->property(role + ".nmea_dump_filename", default_nmea_dump_filename); std::string nmea_dump_devname = configuration->property(role + ".nmea_dump_devname", default_nmea_dump_devname); // RINEX version pvt_output_parameters.rinex_version = configuration->property(role + ".rinex_version", 3); if (FLAGS_RINEX_version == "3.01") { pvt_output_parameters.rinex_version = 3; } else if (FLAGS_RINEX_version == "3.02") { pvt_output_parameters.rinex_version = 3; } else if (FLAGS_RINEX_version == "3") { pvt_output_parameters.rinex_version = 3; } else if (FLAGS_RINEX_version == "2.11") { pvt_output_parameters.rinex_version = 2; } else if (FLAGS_RINEX_version == "2.10") { pvt_output_parameters.rinex_version = 2; } else if (FLAGS_RINEX_version == "2") { pvt_output_parameters.rinex_version = 2; } pvt_output_parameters.rinexobs_rate_ms = bc::lcm(configuration->property(role + ".rinexobs_rate_ms", 1000), pvt_output_parameters.output_rate_ms); // RTCM Printer settings pvt_output_parameters.flag_rtcm_tty_port = configuration->property(role + ".flag_rtcm_tty_port", false); pvt_output_parameters.rtcm_dump_devname = configuration->property(role + ".rtcm_dump_devname", default_rtcm_dump_devname); pvt_output_parameters.flag_rtcm_server = configuration->property(role + ".flag_rtcm_server", false); pvt_output_parameters.rtcm_tcp_port = configuration->property(role + ".rtcm_tcp_port", 2101); pvt_output_parameters.rtcm_station_id = configuration->property(role + ".rtcm_station_id", 1234); // RTCM message rates: least common multiple with output_rate_ms int rtcm_MT1019_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1019_rate_ms", 5000), pvt_output_parameters.output_rate_ms); int rtcm_MT1020_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1020_rate_ms", 5000), pvt_output_parameters.output_rate_ms); int rtcm_MT1045_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1045_rate_ms", 5000), pvt_output_parameters.output_rate_ms); int rtcm_MSM_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MSM_rate_ms", 1000), pvt_output_parameters.output_rate_ms); int rtcm_MT1077_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1077_rate_ms", rtcm_MSM_rate_ms), pvt_output_parameters.output_rate_ms); int rtcm_MT1087_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1087_rate_ms", rtcm_MSM_rate_ms), pvt_output_parameters.output_rate_ms); int rtcm_MT1097_rate_ms = bc::lcm(configuration->property(role + ".rtcm_MT1097_rate_ms", rtcm_MSM_rate_ms), pvt_output_parameters.output_rate_ms); pvt_output_parameters.rtcm_msg_rate_ms[1019] = rtcm_MT1019_rate_ms; pvt_output_parameters.rtcm_msg_rate_ms[1020] = rtcm_MT1020_rate_ms; pvt_output_parameters.rtcm_msg_rate_ms[1045] = rtcm_MT1045_rate_ms; for (int k = 1071; k < 1078; k++) // All GPS MSM { pvt_output_parameters.rtcm_msg_rate_ms[k] = rtcm_MT1077_rate_ms; } for (int k = 1081; k < 1088; k++) // All GLONASS MSM { pvt_output_parameters.rtcm_msg_rate_ms[k] = rtcm_MT1087_rate_ms; } for (int k = 1091; k < 1098; k++) // All Galileo MSM { pvt_output_parameters.rtcm_msg_rate_ms[k] = rtcm_MT1097_rate_ms; } pvt_output_parameters.kml_rate_ms = bc::lcm(configuration->property(role + ".kml_rate_ms", pvt_output_parameters.kml_rate_ms), pvt_output_parameters.output_rate_ms); pvt_output_parameters.gpx_rate_ms = bc::lcm(configuration->property(role + ".gpx_rate_ms", pvt_output_parameters.gpx_rate_ms), pvt_output_parameters.output_rate_ms); pvt_output_parameters.geojson_rate_ms = bc::lcm(configuration->property(role + ".geojson_rate_ms", pvt_output_parameters.geojson_rate_ms), pvt_output_parameters.output_rate_ms); pvt_output_parameters.nmea_rate_ms = bc::lcm(configuration->property(role + ".nmea_rate_ms", pvt_output_parameters.nmea_rate_ms), pvt_output_parameters.output_rate_ms); // Infer the type of receiver /* * TYPE | RECEIVER * 0 | Unknown * 1 | GPS L1 C/A * 2 | GPS L2C * 3 | GPS L5 * 4 | Galileo E1B * 5 | Galileo E5a * 6 | Galileo E5b * 7 | GPS L1 C/A + GPS L2C * 8 | GPS L1 C/A + GPS L5 * 9 | GPS L1 C/A + Galileo E1B * 10 | GPS L1 C/A + Galileo E5a * 11 | GPS L1 C/A + Galileo E5b * 12 | Galileo E1B + GPS L2C * 13 | Galileo E5a + GPS L5 * 14 | Galileo E1B + Galileo E5a * 15 | Galileo E1B + Galileo E5b * 16 | GPS L2C + GPS L5 * 17 | GPS L2C + Galileo E5a * 20 | GPS L5 + Galileo E5b * 21 | GPS L1 C/A + Galileo E1B + GPS L2C * 22 | GPS L1 C/A + Galileo E1B + GPS L5 * 23 | GLONASS L1 C/A * 24 | GLONASS L2 C/A * 25 | GLONASS L1 C/A + GLONASS L2 C/A * 26 | GPS L1 C/A + GLONASS L1 C/A * 27 | Galileo E1B + GLONASS L1 C/A * 28 | GPS L2C + GLONASS L1 C/A * 29 | GPS L1 C/A + GLONASS L2 C/A * 30 | Galileo E1B + GLONASS L2 C/A * 31 | GPS L2C + GLONASS L2 C/A * 32 | GPS L1 C/A + Galileo E1B + GPS L5 + Galileo E5a * * * Skipped previous values to avoid overlapping * 500 | BeiDou B1I * 501 | BeiDou B1I + GPS L1 C/A * 502 | BeiDou B1I + Galileo E1B * 503 | BeiDou B1I + GLONASS L1 C/A * 504 | BeiDou B1I + GPS L1 C/A + Galileo E1B * 505 | BeiDou B1I + GPS L1 C/A + GLONASS L1 C/A + Galileo E1B * 506 | BeiDou B1I + Beidou B3I * Skipped previous values to avoid overlapping * 600 | BeiDou B3I * 601 | BeiDou B3I + GPS L2C * 602 | BeiDou B3I + GLONASS L2 C/A * 603 | BeiDou B3I + GPS L2C + GLONASS L2 C/A * 604 | BeiDou B3I + GPS L1 C/A * 605 | BeiDou B3I + Galileo E1B * 606 | BeiDou B3I + GLONASS L1 C/A * 607 | BeiDou B3I + GPS L1 C/A + Galileo E1B * 608 | BeiDou B3I + GPS L1 C/A + Galileo E1B + BeiDou B1I * 609 | BeiDou B3I + GPS L1 C/A + Galileo E1B + GLONASS L1 C/A * 610 | BeiDou B3I + GPS L1 C/A + Galileo E1B + GLONASS L1 C/A + BeiDou B1I */ int gps_1C_count = configuration->property("Channels_1C.count", 0); int gps_2S_count = configuration->property("Channels_2S.count", 0); int gps_L5_count = configuration->property("Channels_L5.count", 0); int gal_1B_count = configuration->property("Channels_1B.count", 0); int gal_E5a_count = configuration->property("Channels_5X.count", 0); int gal_E5b_count = configuration->property("Channels_7X.count", 0); int glo_1G_count = configuration->property("Channels_1G.count", 0); int glo_2G_count = configuration->property("Channels_2G.count", 0); int bds_B1_count = configuration->property("Channels_B1.count", 0); int bds_B3_count = configuration->property("Channels_B3.count", 0); if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 1; // L1 } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 2; // GPS L2C } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count != 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 3; // L5 } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 4; // E1 } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 5; // E5a } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count != 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 6; } if ((gps_1C_count != 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 7; // GPS L1 C/A + GPS L2C } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count != 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 8; // L1+L5 } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 9; // L1+E1 } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 10; // GPS L1 C/A + Galileo E5a } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count != 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 11; } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 12; // Galileo E1B + GPS L2C } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count != 0) && (gal_1B_count == 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 13; // L5+E5a } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 14; // Galileo E1B + Galileo E5a } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count != 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 15; } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count != 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 16; // GPS L2C + GPS L5 } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 17; // GPS L2C + Galileo E5a } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count != 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 18; } //if( (gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0)) pvt_output_parameters.type_of_receiver = 19; //if( (gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0)) pvt_output_parameters.type_of_receiver = 20; if ((gps_1C_count != 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 21; // GPS L1 C/A + Galileo E1B + GPS L2C } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count != 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 22; // GPS L1 C/A + Galileo E1B + GPS L5 } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 23; // GLONASS L1 C/A } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 24; // GLONASS L2 C/A } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 25; // GLONASS L1 C/A + GLONASS L2 C/A } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 26; // GPS L1 C/A + GLONASS L1 C/A } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 27; // Galileo E1B + GLONASS L1 C/A } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 28; // GPS L2C + GLONASS L1 C/A } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 29; // GPS L1 C/A + GLONASS L2 C/A } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 30; // Galileo E1B + GLONASS L2 C/A } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 31; // GPS L2C + GLONASS L2 C/A } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count != 0) && (gal_1B_count != 0) && (gal_E5a_count != 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 32; // L1+E1+L5+E5a } // BeiDou B1I Receiver if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 500; // Beidou B1I } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 501; // Beidou B1I + GPS L1 C/A } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 502; // Beidou B1I + Galileo E1B } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 503; // Beidou B1I + GLONASS L1 C/A } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 504; // Beidou B1I + GPS L1 C/A + Galileo E1B } if ((gps_1C_count != 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count == 0)) { pvt_output_parameters.type_of_receiver = 505; // Beidou B1I + GPS L1 C/A + GLONASS L1 C/A + Galileo E1B } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count != 0) && (bds_B3_count != 0)) { pvt_output_parameters.type_of_receiver = 506; // Beidou B1I + Beidou B3I } // BeiDou B3I Receiver if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count != 0)) { pvt_output_parameters.type_of_receiver = 600; // Beidou B3I } if ((gps_1C_count != 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count == 0) && (bds_B1_count == 0) && (bds_B3_count != 0)) { pvt_output_parameters.type_of_receiver = 601; // Beidou B3I + GPS L2C } if ((gps_1C_count == 0) && (gps_2S_count == 0) && (gps_L5_count == 0) && (gal_1B_count != 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count == 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count != 0)) { pvt_output_parameters.type_of_receiver = 602; // Beidou B3I + GLONASS L2 C/A } if ((gps_1C_count == 0) && (gps_2S_count != 0) && (gps_L5_count == 0) && (gal_1B_count == 0) && (gal_E5a_count == 0) && (gal_E5b_count == 0) && (glo_1G_count != 0) && (glo_2G_count != 0) && (bds_B1_count == 0) && (bds_B3_count != 0)) { pvt_output_parameters.type_of_receiver = 603; // Beidou B3I + GPS L2C + GLONASS L2 C/A } // RTKLIB PVT solver options // Settings 1 int positioning_mode = -1; std::string default_pos_mode("Single"); std::string positioning_mode_str = configuration->property(role + ".positioning_mode", default_pos_mode); // (PMODE_XXX) see src/algorithms/libs/rtklib/rtklib.h if (positioning_mode_str == "Single") { positioning_mode = PMODE_SINGLE; } if (positioning_mode_str == "Static") { positioning_mode = PMODE_STATIC; } if (positioning_mode_str == "Kinematic") { positioning_mode = PMODE_KINEMA; } if (positioning_mode_str == "PPP_Static") { positioning_mode = PMODE_PPP_STATIC; } if (positioning_mode_str == "PPP_Kinematic") { positioning_mode = PMODE_PPP_KINEMA; } if (positioning_mode == -1) { // warn user and set the default std::cout << "WARNING: Bad specification of positioning mode." << std::endl; std::cout << "positioning_mode possible values: Single / Static / Kinematic / PPP_Static / PPP_Kinematic" << std::endl; std::cout << "positioning_mode specified value: " << positioning_mode_str << std::endl; std::cout << "Setting positioning_mode to Single" << std::endl; positioning_mode = PMODE_SINGLE; } int num_bands = 0; if ((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0) || (bds_B1_count > 0)) { num_bands = 1; } if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0) || (bds_B1_count > 0)) && ((gps_2S_count > 0) || (glo_2G_count > 0) || (bds_B3_count > 0))) { num_bands = 2; } if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0) || (bds_B1_count > 0)) && ((gal_E5a_count > 0) || (gal_E5b_count > 0) || (gps_L5_count > 0))) { num_bands = 2; } if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0) || (bds_B1_count > 0)) && ((gps_2S_count > 0) || (glo_2G_count > 0) || (bds_B3_count > 0)) && ((gal_E5a_count > 0) || (gal_E5b_count > 0) || (gps_L5_count > 0))) { num_bands = 3; } int number_of_frequencies = configuration->property(role + ".num_bands", num_bands); /* (1:L1, 2:L1+L2, 3:L1+L2+L5) */ if ((number_of_frequencies < 1) || (number_of_frequencies > 3)) { // warn user and set the default number_of_frequencies = num_bands; } double elevation_mask = configuration->property(role + ".elevation_mask", 15.0); if ((elevation_mask < 0.0) || (elevation_mask > 90.0)) { // warn user and set the default LOG(WARNING) << "Erroneous Elevation Mask. Setting to default value of 15.0 degrees"; elevation_mask = 15.0; } int dynamics_model = configuration->property(role + ".dynamics_model", 0); /* dynamics model (0:none, 1:velocity, 2:accel) */ if ((dynamics_model < 0) || (dynamics_model > 2)) { // warn user and set the default LOG(WARNING) << "Erroneous Dynamics Model configuration. Setting to default value of (0:none)"; dynamics_model = 0; } std::string default_iono_model("OFF"); std::string iono_model_str = configuration->property(role + ".iono_model", default_iono_model); /* (IONOOPT_XXX) see src/algorithms/libs/rtklib/rtklib.h */ int iono_model = -1; if (iono_model_str == "OFF") { iono_model = IONOOPT_OFF; } if (iono_model_str == "Broadcast") { iono_model = IONOOPT_BRDC; } if (iono_model_str == "SBAS") { iono_model = IONOOPT_SBAS; } if (iono_model_str == "Iono-Free-LC") { iono_model = IONOOPT_IFLC; } if (iono_model_str == "Estimate_STEC") { iono_model = IONOOPT_EST; } if (iono_model_str == "IONEX") { iono_model = IONOOPT_TEC; } if (iono_model == -1) { // warn user and set the default std::cout << "WARNING: Bad specification of ionospheric model." << std::endl; std::cout << "iono_model possible values: OFF / Broadcast / SBAS / Iono-Free-LC / Estimate_STEC / IONEX" << std::endl; std::cout << "iono_model specified value: " << iono_model_str << std::endl; std::cout << "Setting iono_model to OFF" << std::endl; iono_model = IONOOPT_OFF; /* 0: ionosphere option: correction off */ } std::string default_trop_model("OFF"); int trop_model = -1; std::string trop_model_str = configuration->property(role + ".trop_model", default_trop_model); /* (TROPOPT_XXX) see src/algorithms/libs/rtklib/rtklib.h */ if (trop_model_str == "OFF") { trop_model = TROPOPT_OFF; } if (trop_model_str == "Saastamoinen") { trop_model = TROPOPT_SAAS; } if (trop_model_str == "SBAS") { trop_model = TROPOPT_SBAS; } if (trop_model_str == "Estimate_ZTD") { trop_model = TROPOPT_EST; } if (trop_model_str == "Estimate_ZTD_Grad") { trop_model = TROPOPT_ESTG; } if (trop_model == -1) { // warn user and set the default std::cout << "WARNING: Bad specification of tropospheric model." << std::endl; std::cout << "trop_model possible values: OFF / Saastamoinen / SBAS / Estimate_ZTD / Estimate_ZTD_Grad" << std::endl; std::cout << "trop_model specified value: " << trop_model_str << std::endl; std::cout << "Setting trop_model to OFF" << std::endl; trop_model = TROPOPT_OFF; } /* RTKLIB positioning options */ int sat_PCV = 0; /* Set whether the satellite antenna PCV (phase center variation) model is used or not. This feature requires a Satellite Antenna PCV File. */ int rec_PCV = 0; /* Set whether the receiver antenna PCV (phase center variation) model is used or not. This feature requires a Receiver Antenna PCV File. */ /* Set whether the phase windup correction for PPP modes is applied or not. Only applicable to PPP‐* modes.*/ int phwindup = configuration->property(role + ".phwindup", 0); /* Set whether the GPS Block IIA satellites in eclipse are excluded or not. The eclipsing Block IIA satellites often degrade the PPP solutions due to unpredicted behavior of yaw‐attitude. Only applicable to PPP‐* modes.*/ int reject_GPS_IIA = configuration->property(role + ".reject_GPS_IIA", 0); /* Set whether RAIM (receiver autonomous integrity monitoring) FDE (fault detection and exclusion) feature is enabled or not. In case of RAIM FDE enabled, a satellite is excluded if SSE (sum of squared errors) of residuals is over a threshold. The excluded satellite is selected to indicate the minimum SSE. */ int raim_fde = configuration->property(role + ".raim_fde", 0); int earth_tide = configuration->property(role + ".earth_tide", 0); int nsys = 0; if ((gps_1C_count > 0) || (gps_2S_count > 0) || (gps_L5_count > 0)) { nsys += SYS_GPS; } if ((gal_1B_count > 0) || (gal_E5a_count > 0) || (gal_E5b_count > 0)) { nsys += SYS_GAL; } if ((glo_1G_count > 0) || (glo_2G_count > 0)) { nsys += SYS_GLO; } if ((bds_B1_count > 0) || (bds_B3_count > 0)) { nsys += SYS_BDS; } int navigation_system = configuration->property(role + ".navigation_system", nsys); /* (SYS_XXX) see src/algorithms/libs/rtklib/rtklib.h */ if ((navigation_system < 1) || (navigation_system > 255)) /* GPS: 1 SBAS: 2 GPS+SBAS: 3 Galileo: 8 Galileo+GPS: 9 GPS+SBAS+Galileo: 11 All: 255 */ { // warn user and set the default LOG(WARNING) << "Erroneous Navigation System. Setting to default value of (0:none)"; navigation_system = nsys; } // Settings 2 std::string default_gps_ar("Continuous"); std::string integer_ambiguity_resolution_gps_str = configuration->property(role + ".AR_GPS", default_gps_ar); /* Integer Ambiguity Resolution mode for GPS (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */ int integer_ambiguity_resolution_gps = -1; if (integer_ambiguity_resolution_gps_str == "OFF") { integer_ambiguity_resolution_gps = ARMODE_OFF; } if (integer_ambiguity_resolution_gps_str == "Continuous") { integer_ambiguity_resolution_gps = ARMODE_CONT; } if (integer_ambiguity_resolution_gps_str == "Instantaneous") { integer_ambiguity_resolution_gps = ARMODE_INST; } if (integer_ambiguity_resolution_gps_str == "Fix-and-Hold") { integer_ambiguity_resolution_gps = ARMODE_FIXHOLD; } if (integer_ambiguity_resolution_gps_str == "PPP-AR") { integer_ambiguity_resolution_gps = ARMODE_PPPAR; } if (integer_ambiguity_resolution_gps == -1) { // warn user and set the default std::cout << "WARNING: Bad specification of GPS ambiguity resolution method." << std::endl; std::cout << "AR_GPS possible values: OFF / Continuous / Instantaneous / Fix-and-Hold / PPP-AR" << std::endl; std::cout << "AR_GPS specified value: " << integer_ambiguity_resolution_gps_str << std::endl; std::cout << "Setting AR_GPS to OFF" << std::endl; integer_ambiguity_resolution_gps = ARMODE_OFF; } int integer_ambiguity_resolution_glo = configuration->property(role + ".AR_GLO", 1); /* Integer Ambiguity Resolution mode for GLONASS (0:off,1:on,2:auto cal,3:ext cal) */ if ((integer_ambiguity_resolution_glo < 0) || (integer_ambiguity_resolution_glo > 3)) { // warn user and set the default LOG(WARNING) << "Erroneous Integer Ambiguity Resolution for GLONASS . Setting to default value of (1:on)"; integer_ambiguity_resolution_glo = 1; } int integer_ambiguity_resolution_bds = configuration->property(role + ".AR_DBS", 1); /* Integer Ambiguity Resolution mode for BEIDOU (0:off,1:on) */ if ((integer_ambiguity_resolution_bds < 0) || (integer_ambiguity_resolution_bds > 1)) { // warn user and set the default LOG(WARNING) << "Erroneous Integer Ambiguity Resolution for BEIDOU . Setting to default value of (1:on)"; integer_ambiguity_resolution_bds = 1; } double min_ratio_to_fix_ambiguity = configuration->property(role + ".min_ratio_to_fix_ambiguity", 3.0); /* Set the integer ambiguity validation threshold for ratio‐test, which uses the ratio of squared residuals of the best integer vector to the second‐best vector. */ int min_lock_to_fix_ambiguity = configuration->property(role + ".min_lock_to_fix_ambiguity", 0); /* Set the minimum lock count to fix integer ambiguity.FLAGS_RINEX_version. If the lock count is less than the value, the ambiguity is excluded from the fixed integer vector. */ double min_elevation_to_fix_ambiguity = configuration->property(role + ".min_elevation_to_fix_ambiguity", 0.0); /* Set the minimum elevation (deg) to fix integer ambiguity. If the elevation of the satellite is less than the value, the ambiguity is excluded from the fixed integer vector. */ int outage_reset_ambiguity = configuration->property(role + ".outage_reset_ambiguity", 5); /* Set the outage count to reset ambiguity. If the data outage count is over the value, the estimated ambiguity is reset to the initial value. */ double slip_threshold = configuration->property(role + ".slip_threshold", 0.05); /* set the cycle‐slip threshold (m) of geometry‐free LC carrier‐phase difference between epochs */ double threshold_reject_gdop = configuration->property(role + ".threshold_reject_gdop", 30.0); /* reject threshold of GDOP. If the GDOP is over the value, the observable is excluded for the estimation process as an outlier. */ double threshold_reject_innovation = configuration->property(role + ".threshold_reject_innovation", 30.0); /* reject threshold of innovation (m). If the innovation is over the value, the observable is excluded for the estimation process as an outlier. */ int number_filter_iter = configuration->property(role + ".number_filter_iter", 1); /* Set the number of iteration in the measurement update of the estimation filter. If the baseline length is very short like 1 m, the iteration may be effective to handle the nonlinearity of measurement equation. */ // Statistics double bias_0 = configuration->property(role + ".bias_0", 30.0); double iono_0 = configuration->property(role + ".iono_0", 0.03); double trop_0 = configuration->property(role + ".trop_0", 0.3); double sigma_bias = configuration->property(role + ".sigma_bias", 1e-4); /* Set the process noise standard deviation of carrier‐phase bias (ambiguity) (cycle/sqrt(s)) */ double sigma_iono = configuration->property(role + ".sigma_iono", 1e-3); /* Set the process noise standard deviation of vertical ionospheric delay per 10 km baseline (m/sqrt(s)). */ double sigma_trop = configuration->property(role + ".sigma_trop", 1e-4); /* Set the process noise standard deviation of zenith tropospheric delay (m/sqrt(s)). */ double sigma_acch = configuration->property(role + ".sigma_acch", 1e-1); /* Set the process noise standard deviation of the receiver acceleration as the horizontal component. (m/s2/sqrt(s)). If Receiver Dynamics is set to OFF, they are not used. */ double sigma_accv = configuration->property(role + ".sigma_accv", 1e-2); /* Set the process noise standard deviation of the receiver acceleration as the vertical component. (m/s2/sqrt(s)). If Receiver Dynamics is set to OFF, they are not used. */ double sigma_pos = configuration->property(role + ".sigma_pos", 0.0); double code_phase_error_ratio_l1 = configuration->property(role + ".code_phase_error_ratio_l1", 100.0); double code_phase_error_ratio_l2 = configuration->property(role + ".code_phase_error_ratio_l2", 100.0); double code_phase_error_ratio_l5 = configuration->property(role + ".code_phase_error_ratio_l5", 100.0); double carrier_phase_error_factor_a = configuration->property(role + ".carrier_phase_error_factor_a", 0.003); double carrier_phase_error_factor_b = configuration->property(role + ".carrier_phase_error_factor_b", 0.003); snrmask_t snrmask = {{}, {{}, {}}}; prcopt_t rtklib_configuration_options = { positioning_mode, /* positioning mode (PMODE_XXX) see src/algorithms/libs/rtklib/rtklib.h */ 0, /* solution type (0:forward,1:backward,2:combined) */ number_of_frequencies, /* number of frequencies (1:L1, 2:L1+L2, 3:L1+L2+L5)*/ navigation_system, /* navigation system */ elevation_mask * D2R, /* elevation mask angle (degrees) */ snrmask, /* snrmask_t snrmask SNR mask */ 0, /* satellite ephemeris/clock (EPHOPT_XXX) */ integer_ambiguity_resolution_gps, /* AR mode (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */ integer_ambiguity_resolution_glo, /* GLONASS AR mode (0:off,1:on,2:auto cal,3:ext cal) */ integer_ambiguity_resolution_bds, /* BeiDou AR mode (0:off,1:on) */ outage_reset_ambiguity, /* obs outage count to reset bias */ min_lock_to_fix_ambiguity, /* min lock count to fix ambiguity */ 10, /* min fix count to hold ambiguity */ 1, /* max iteration to resolve ambiguity */ iono_model, /* ionosphere option (IONOOPT_XXX) */ trop_model, /* troposphere option (TROPOPT_XXX) */ dynamics_model, /* dynamics model (0:none, 1:velocity, 2:accel) */ earth_tide, /* earth tide correction (0:off,1:solid,2:solid+otl+pole) */ number_filter_iter, /* number of filter iteration */ 0, /* code smoothing window size (0:none) */ 0, /* interpolate reference obs (for post mission) */ 0, /* sbssat_t sbssat SBAS correction options */ 0, /* sbsion_t sbsion[MAXBAND+1] SBAS satellite selection (0:all) */ 0, /* rover position for fixed mode */ 0, /* base position for relative mode */ /* 0:pos in prcopt, 1:average of single pos, */ /* 2:read from file, 3:rinex header, 4:rtcm pos */ {code_phase_error_ratio_l1, code_phase_error_ratio_l2, code_phase_error_ratio_l5}, /* eratio[NFREQ] code/phase error ratio */ {100.0, carrier_phase_error_factor_a, carrier_phase_error_factor_b, 0.0, 1.0}, /* err[5]: measurement error factor [0]:reserved, [1-3]:error factor a/b/c of phase (m) , [4]:doppler frequency (hz) */ {bias_0, iono_0, trop_0}, /* std[3]: initial-state std [0]bias,[1]iono [2]trop*/ {sigma_bias, sigma_iono, sigma_trop, sigma_acch, sigma_accv, sigma_pos}, /* prn[6] process-noise std */ 5e-12, /* sclkstab: satellite clock stability (sec/sec) */ {min_ratio_to_fix_ambiguity, 0.9999, 0.25, 0.1, 0.05, 0.0, 0.0, 0.0}, /* thresar[8]: AR validation threshold */ min_elevation_to_fix_ambiguity, /* elevation mask of AR for rising satellite (deg) */ 0.0, /* elevation mask to hold ambiguity (deg) */ slip_threshold, /* slip threshold of geometry-free phase (m) */ 30.0, /* max difference of time (sec) */ threshold_reject_innovation, /* reject threshold of innovation (m) */ threshold_reject_gdop, /* reject threshold of gdop */ {}, /* double baseline[2] baseline length constraint {const,sigma} (m) */ {}, /* double ru[3] rover position for fixed mode {x,y,z} (ecef) (m) */ {}, /* double rb[3] base position for relative mode {x,y,z} (ecef) (m) */ {"", ""}, /* char anttype[2][MAXANT] antenna types {rover,base} */ {{}, {}}, /* double antdel[2][3] antenna delta {{rov_e,rov_n,rov_u},{ref_e,ref_n,ref_u}} */ {}, /* pcv_t pcvr[2] receiver antenna parameters {rov,base} */ {}, /* unsigned char exsats[MAXSAT] excluded satellites (1:excluded, 2:included) */ 0, /* max averaging epoches */ 0, /* initialize by restart */ 1, /* output single by dgps/float/fix/ppp outage */ {"", ""}, /* char rnxopt[2][256] rinex options {rover,base} */ {sat_PCV, rec_PCV, phwindup, reject_GPS_IIA, raim_fde}, /* posopt[6] positioning options [0]: satellite and receiver antenna PCV model; [1]: interpolate antenna parameters; [2]: apply phase wind-up correction for PPP modes; [3]: exclude measurements of GPS Block IIA satellites satellite [4]: RAIM FDE (fault detection and exclusion) [5]: handle day-boundary clock jump */ 0, /* solution sync mode (0:off,1:on) */ {{}, {}}, /* odisp[2][6*11] ocean tide loading parameters {rov,base} */ {{}, {{}, {}}, {{}, {}}, {}, {}}, /* exterr_t exterr extended receiver error model */ 0, /* disable L2-AR */ {} /* char pppopt[256] ppp option "-GAP_RESION=" default gap to reset iono parameters (ep) */ }; rtkinit(&rtk, &rtklib_configuration_options); // Outputs bool default_output_enabled = configuration->property(role + ".output_enabled", true); pvt_output_parameters.output_enabled = default_output_enabled; pvt_output_parameters.rinex_output_enabled = configuration->property(role + ".rinex_output_enabled", default_output_enabled); pvt_output_parameters.gpx_output_enabled = configuration->property(role + ".gpx_output_enabled", default_output_enabled); pvt_output_parameters.geojson_output_enabled = configuration->property(role + ".geojson_output_enabled", default_output_enabled); pvt_output_parameters.kml_output_enabled = configuration->property(role + ".kml_output_enabled", default_output_enabled); pvt_output_parameters.xml_output_enabled = configuration->property(role + ".xml_output_enabled", default_output_enabled); pvt_output_parameters.nmea_output_file_enabled = configuration->property(role + ".nmea_output_file_enabled", default_output_enabled); pvt_output_parameters.rtcm_output_file_enabled = configuration->property(role + ".rtcm_output_file_enabled", false); std::string default_output_path = configuration->property(role + ".output_path", std::string(".")); pvt_output_parameters.output_path = default_output_path; pvt_output_parameters.rinex_output_path = configuration->property(role + ".rinex_output_path", default_output_path); pvt_output_parameters.gpx_output_path = configuration->property(role + ".gpx_output_path", default_output_path); pvt_output_parameters.geojson_output_path = configuration->property(role + ".geojson_output_path", default_output_path); pvt_output_parameters.kml_output_path = configuration->property(role + ".kml_output_path", default_output_path); pvt_output_parameters.xml_output_path = configuration->property(role + ".xml_output_path", default_output_path); pvt_output_parameters.nmea_output_file_path = configuration->property(role + ".nmea_output_file_path", default_output_path); pvt_output_parameters.rtcm_output_file_path = configuration->property(role + ".rtcm_output_file_path", default_output_path); // Read PVT MONITOR Configuration pvt_output_parameters.monitor_enabled = configuration->property(role + ".enable_monitor", false); pvt_output_parameters.udp_addresses = configuration->property(role + ".monitor_client_addresses", std::string("127.0.0.1")); pvt_output_parameters.udp_port = configuration->property(role + ".monitor_udp_port", 1234); pvt_output_parameters.protobuf_enabled = configuration->property(role + ".enable_protobuf", true); if (configuration->property("Monitor.enable_protobuf", false) == true) { pvt_output_parameters.protobuf_enabled = true; } // Show time in local zone pvt_output_parameters.show_local_time_zone = configuration->property(role + ".show_local_time_zone", false); // make PVT object pvt_ = rtklib_make_pvt_gs(in_streams_, pvt_output_parameters, rtk); DLOG(INFO) << "pvt(" << pvt_->unique_id() << ")"; if (out_streams_ > 0) { LOG(ERROR) << "The PVT block does not have an output stream"; } } Rtklib_Pvt::~Rtklib_Pvt() { rtkfree(&rtk); } bool Rtklib_Pvt::get_latest_PVT(double* longitude_deg, double* latitude_deg, double* height_m, double* ground_speed_kmh, double* course_over_ground_deg, time_t* UTC_time) { return pvt_->get_latest_PVT(longitude_deg, latitude_deg, height_m, ground_speed_kmh, course_over_ground_deg, UTC_time); } void Rtklib_Pvt::clear_ephemeris() { pvt_->clear_ephemeris(); } std::map Rtklib_Pvt::get_gps_ephemeris() const { return pvt_->get_gps_ephemeris_map(); } std::map Rtklib_Pvt::get_galileo_ephemeris() const { return pvt_->get_galileo_ephemeris_map(); } std::map Rtklib_Pvt::get_gps_almanac() const { return pvt_->get_gps_almanac_map(); } std::map Rtklib_Pvt::get_galileo_almanac() const { return pvt_->get_galileo_almanac_map(); } void Rtklib_Pvt::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect internally DLOG(INFO) << "nothing to connect internally"; } void Rtklib_Pvt::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr Rtklib_Pvt::get_left_block() { return pvt_; } gr::basic_block_sptr Rtklib_Pvt::get_right_block() { return nullptr; // this is a sink, nothing downstream } src/algorithms/PVT/adapters/rtklib_pvt.h000066400000000000000000000066641352176506000206440ustar00rootroot00000000000000/*! * \file rtklib_pvt.h * \brief Interface of a Position Velocity and Time computation block * \author Javier Arribas, 2017. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RTKLIB_PVT_H_ #define GNSS_SDR_RTKLIB_PVT_H_ #include "pvt_interface.h" // for PvtInterface #include "rtklib.h" // for rtk_t #include "rtklib_pvt_gs.h" // for rtklib_pvt_gs_sptr #include // for gr_complex #include // for basic_block_sptr, top_block_sptr #include // for size_t #include // for time_t #include // for map #include // for string class ConfigurationInterface; class Galileo_Almanac; class Galileo_Ephemeris; class Gps_Almanac; class Gps_Ephemeris; /*! * \brief This class implements a PvtInterface for the RTKLIB PVT block */ class Rtklib_Pvt : public PvtInterface { public: Rtklib_Pvt(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); virtual ~Rtklib_Pvt(); inline std::string role() override { return role_; } //! Returns "RTKLIB_PVT" inline std::string implementation() override { return "RTKLIB_PVT"; } void clear_ephemeris() override; std::map get_gps_ephemeris() const override; std::map get_galileo_ephemeris() const override; std::map get_gps_almanac() const override; std::map get_galileo_almanac() const override; void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; inline void reset() override { return; } //! All blocks must have an item_size() function implementation. Returns sizeof(gr_complex) inline size_t item_size() override { return sizeof(gr_complex); } bool get_latest_PVT(double* longitude_deg, double* latitude_deg, double* height_m, double* ground_speed_kmh, double* course_over_ground_deg, time_t* UTC_time) override; private: rtklib_pvt_gs_sptr pvt_; rtk_t rtk{}; std::string role_; unsigned int in_streams_; unsigned int out_streams_; }; #endif src/algorithms/PVT/gnuradio_blocks/000077500000000000000000000000001352176506000176415ustar00rootroot00000000000000src/algorithms/PVT/gnuradio_blocks/CMakeLists.txt000066400000000000000000000046151352176506000224070ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(PVT_GR_BLOCKS_SOURCES rtklib_pvt_gs.cc ) set(PVT_GR_BLOCKS_HEADERS rtklib_pvt_gs.h ) source_group(Headers FILES ${PVT_GR_BLOCKS_HEADERS}) add_library(pvt_gr_blocks ${PVT_GR_BLOCKS_SOURCES} ${PVT_GR_BLOCKS_HEADERS}) if(${FILESYSTEM_FOUND}) target_compile_definitions(pvt_gr_blocks PRIVATE -DHAS_STD_FILESYSTEM=1) if(${find_experimental}) target_compile_definitions(pvt_gr_blocks PRIVATE -DHAS_STD_FILESYSTEM_EXPERIMENTAL=1) endif() target_link_libraries(pvt_gr_blocks PRIVATE std::filesystem) else() target_link_libraries(pvt_gr_blocks PRIVATE Boost::filesystem Boost::system) endif() target_link_libraries(pvt_gr_blocks PUBLIC algorithms_libs_rtklib core_system_parameters Boost::date_time Gnuradio::pmt Gnuradio::runtime PRIVATE pvt_libs algorithms_libs Gflags::gflags Glog::glog Boost::serialization ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(pvt_gr_blocks PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() if(Boost_VERSION_STRING VERSION_LESS 1.58.0) target_compile_definitions(pvt_gr_blocks PRIVATE -DOLD_BOOST=1) endif() # Check if we have std::put_time (Workaround for gcc < 5.0) include(CheckCXXSourceCompiles) check_cxx_source_compiles(" #include int main() { std::put_time(nullptr, \"\"); }" has_put_time ) if(${has_put_time}) target_compile_definitions(pvt_gr_blocks PRIVATE -DHAS_PUT_TIME=1) endif() set_property(TARGET pvt_gr_blocks APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc000066400000000000000000012146001352176506000230250ustar00rootroot00000000000000/*! * \file rtklib_pvt_gs.cc * \brief Interface of a Position Velocity and Time computation block * \author Javier Arribas, 2017. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rtklib_pvt_gs.h" #include "MATH_CONSTANTS.h" #include "beidou_dnav_almanac.h" #include "beidou_dnav_ephemeris.h" #include "beidou_dnav_iono.h" #include "beidou_dnav_utc_model.h" #include "display.h" #include "galileo_almanac.h" #include "galileo_almanac_helper.h" #include "galileo_ephemeris.h" #include "galileo_iono.h" #include "galileo_utc_model.h" #include "geojson_printer.h" #include "glonass_gnav_almanac.h" #include "glonass_gnav_ephemeris.h" #include "glonass_gnav_utc_model.h" #include "gnss_frequencies.h" #include "gnss_sdr_create_directory.h" #include "gps_almanac.h" #include "gps_cnav_ephemeris.h" #include "gps_cnav_iono.h" #include "gps_cnav_utc_model.h" #include "gps_ephemeris.h" #include "gps_iono.h" #include "gps_utc_model.h" #include "gpx_printer.h" #include "kml_printer.h" #include "monitor_pvt.h" #include "monitor_pvt_udp_sink.h" #include "nmea_printer.h" #include "pvt_conf.h" #include "rinex_printer.h" #include "rtcm_printer.h" #include "rtklib_solver.h" #include // for any_cast, any #include // for xml_iarchive #include // for xml_oarchive #include // for bind_t, bind #include #include #include #include // for nvp, make_nvp #include // for LOG #include // for io_signature #include // for mp #include // for sort, unique #include // for exception #include // for ofstream #include // for put_time, setprecision #include // for operator<< #include // for locale #include // for ostringstream #include // for length_error #include // for IPC_CREAT #include // for msgctl #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif #if OLD_BOOST #include namespace bc = boost::math; #else #include namespace bc = boost::integer; #endif rtklib_pvt_gs_sptr rtklib_make_pvt_gs(uint32_t nchannels, const Pvt_Conf& conf_, const rtk_t& rtk) { return rtklib_pvt_gs_sptr(new rtklib_pvt_gs(nchannels, conf_, rtk)); } rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels, const Pvt_Conf& conf_, const rtk_t& rtk) : gr::sync_block("rtklib_pvt_gs", gr::io_signature::make(nchannels, nchannels, sizeof(Gnss_Synchro)), gr::io_signature::make(0, 0, 0)) { // Send feedback message to observables block with the receiver clock offset this->message_port_register_out(pmt::mp("pvt_to_observables")); // Send PVT status to gnss_flowgraph this->message_port_register_out(pmt::mp("status")); mapStringValues_["1C"] = evGPS_1C; mapStringValues_["2S"] = evGPS_2S; mapStringValues_["L5"] = evGPS_L5; mapStringValues_["1B"] = evGAL_1B; mapStringValues_["5X"] = evGAL_5X; mapStringValues_["1G"] = evGLO_1G; mapStringValues_["2G"] = evGLO_2G; mapStringValues_["B1"] = evBDS_B1; mapStringValues_["B2"] = evBDS_B2; mapStringValues_["B3"] = evBDS_B3; max_obs_block_rx_clock_offset_ms = conf_.max_obs_block_rx_clock_offset_ms; d_output_rate_ms = conf_.output_rate_ms; d_display_rate_ms = conf_.display_rate_ms; d_report_rate_ms = 1000; // report every second PVT to gnss_synchro d_dump = conf_.dump; d_dump_mat = conf_.dump_mat and d_dump; d_dump_filename = conf_.dump_filename; std::string dump_ls_pvt_filename = conf_.dump_filename; if (d_dump) { std::string dump_path; // Get path if (d_dump_filename.find_last_of('/') != std::string::npos) { std::string dump_filename_ = d_dump_filename.substr(d_dump_filename.find_last_of('/') + 1); dump_path = d_dump_filename.substr(0, d_dump_filename.find_last_of('/')); d_dump_filename = dump_filename_; } else { dump_path = std::string("."); } if (d_dump_filename.empty()) { d_dump_filename = "pvt"; } // remove extension if any if (d_dump_filename.substr(1).find_last_of('.') != std::string::npos) { d_dump_filename = d_dump_filename.substr(0, d_dump_filename.find_last_of('.')); } dump_ls_pvt_filename = dump_path + fs::path::preferred_separator + d_dump_filename; dump_ls_pvt_filename.append(".dat"); // create directory if (!gnss_sdr_create_directory(dump_path)) { std::cerr << "GNSS-SDR cannot create dump file for the PVT block. Wrong permissions?" << std::endl; d_dump = false; } } d_nchannels = nchannels; type_of_rx = conf_.type_of_receiver; // GPS Ephemeris data message port in this->message_port_register_in(pmt::mp("telemetry")); this->set_msg_handler(pmt::mp("telemetry"), boost::bind(&rtklib_pvt_gs::msg_handler_telemetry, this, _1)); // initialize kml_printer std::string kml_dump_filename; kml_dump_filename = d_dump_filename; d_kml_output_enabled = conf_.kml_output_enabled; d_kml_rate_ms = conf_.kml_rate_ms; if (d_kml_rate_ms == 0) { d_kml_output_enabled = false; } if (d_kml_output_enabled) { d_kml_dump = std::make_shared(conf_.kml_output_path); d_kml_dump->set_headers(kml_dump_filename); } else { d_kml_dump = nullptr; } // initialize gpx_printer std::string gpx_dump_filename; gpx_dump_filename = d_dump_filename; d_gpx_output_enabled = conf_.gpx_output_enabled; d_gpx_rate_ms = conf_.gpx_rate_ms; if (d_gpx_rate_ms == 0) { d_gpx_output_enabled = false; } if (d_gpx_output_enabled) { d_gpx_dump = std::make_shared(conf_.gpx_output_path); d_gpx_dump->set_headers(gpx_dump_filename); } else { d_gpx_dump = nullptr; } // initialize geojson_printer std::string geojson_dump_filename; geojson_dump_filename = d_dump_filename; d_geojson_output_enabled = conf_.geojson_output_enabled; d_geojson_rate_ms = conf_.geojson_rate_ms; if (d_geojson_rate_ms == 0) { d_geojson_output_enabled = false; } if (d_geojson_output_enabled) { d_geojson_printer = std::make_shared(conf_.geojson_output_path); d_geojson_printer->set_headers(geojson_dump_filename); } else { d_geojson_printer = nullptr; } // initialize nmea_printer d_nmea_output_file_enabled = (conf_.nmea_output_file_enabled or conf_.flag_nmea_tty_port); d_nmea_rate_ms = conf_.nmea_rate_ms; if (d_nmea_rate_ms == 0) { d_nmea_output_file_enabled = false; } if (d_nmea_output_file_enabled) { d_nmea_printer = std::make_shared(conf_.nmea_dump_filename, conf_.nmea_output_file_enabled, conf_.flag_nmea_tty_port, conf_.nmea_dump_devname, conf_.nmea_output_file_path); } else { d_nmea_printer = nullptr; } // initialize rtcm_printer std::string rtcm_dump_filename; rtcm_dump_filename = d_dump_filename; if (conf_.flag_rtcm_server or conf_.flag_rtcm_tty_port or conf_.rtcm_output_file_enabled) { d_rtcm_printer = std::make_shared(rtcm_dump_filename, conf_.rtcm_output_file_enabled, conf_.flag_rtcm_server, conf_.flag_rtcm_tty_port, conf_.rtcm_tcp_port, conf_.rtcm_station_id, conf_.rtcm_dump_devname, true, conf_.rtcm_output_file_path); std::map rtcm_msg_rate_ms = conf_.rtcm_msg_rate_ms; if (rtcm_msg_rate_ms.find(1019) != rtcm_msg_rate_ms.end()) { d_rtcm_MT1019_rate_ms = rtcm_msg_rate_ms[1019]; } else { d_rtcm_MT1019_rate_ms = bc::lcm(5000, d_output_rate_ms); // default value if not set } if (rtcm_msg_rate_ms.find(1020) != rtcm_msg_rate_ms.end()) { d_rtcm_MT1020_rate_ms = rtcm_msg_rate_ms[1020]; } else { d_rtcm_MT1020_rate_ms = bc::lcm(5000, d_output_rate_ms); // default value if not set } if (rtcm_msg_rate_ms.find(1045) != rtcm_msg_rate_ms.end()) { d_rtcm_MT1045_rate_ms = rtcm_msg_rate_ms[1045]; } else { d_rtcm_MT1045_rate_ms = bc::lcm(5000, d_output_rate_ms); // default value if not set } if (rtcm_msg_rate_ms.find(1077) != rtcm_msg_rate_ms.end()) // whatever between 1071 and 1077 { d_rtcm_MT1077_rate_ms = rtcm_msg_rate_ms[1077]; } else { d_rtcm_MT1077_rate_ms = bc::lcm(1000, d_output_rate_ms); // default value if not set } if (rtcm_msg_rate_ms.find(1087) != rtcm_msg_rate_ms.end()) // whatever between 1081 and 1087 { d_rtcm_MT1087_rate_ms = rtcm_msg_rate_ms[1087]; } else { d_rtcm_MT1087_rate_ms = bc::lcm(1000, d_output_rate_ms); // default value if not set } if (rtcm_msg_rate_ms.find(1097) != rtcm_msg_rate_ms.end()) // whatever between 1091 and 1097 { d_rtcm_MT1097_rate_ms = rtcm_msg_rate_ms[1097]; d_rtcm_MSM_rate_ms = rtcm_msg_rate_ms[1097]; } else { d_rtcm_MT1097_rate_ms = bc::lcm(1000, d_output_rate_ms); // default value if not set d_rtcm_MSM_rate_ms = bc::lcm(1000, d_output_rate_ms); // default value if not set } b_rtcm_writing_started = false; b_rtcm_enabled = true; } else { d_rtcm_MT1019_rate_ms = 0; d_rtcm_MT1045_rate_ms = 0; d_rtcm_MT1020_rate_ms = 0; d_rtcm_MT1077_rate_ms = 0; d_rtcm_MT1087_rate_ms = 0; d_rtcm_MT1097_rate_ms = 0; d_rtcm_MSM_rate_ms = 0; b_rtcm_enabled = false; b_rtcm_writing_started = false; d_rtcm_printer = nullptr; } // initialize RINEX printer b_rinex_header_written = false; b_rinex_header_updated = false; b_rinex_output_enabled = conf_.rinex_output_enabled; d_rinex_version = conf_.rinex_version; if (b_rinex_output_enabled) { rp = std::make_shared(d_rinex_version, conf_.rinex_output_path); } else { rp = nullptr; } d_rinexobs_rate_ms = conf_.rinexobs_rate_ms; // XML printer d_xml_storage = conf_.xml_output_enabled; if (d_xml_storage) { xml_base_path = conf_.xml_output_path; fs::path full_path(fs::current_path()); const fs::path p(xml_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(xml_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; xml_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { xml_base_path = p.string(); } if (xml_base_path != ".") { std::cout << "XML files will be stored at " << xml_base_path << std::endl; } xml_base_path = xml_base_path + fs::path::preferred_separator; } d_rx_time = 0.0; d_last_status_print_seg = 0; // PVT MONITOR flag_monitor_pvt_enabled = conf_.monitor_enabled; if (flag_monitor_pvt_enabled) { std::string address_string = conf_.udp_addresses; std::vector udp_addr_vec = split_string(address_string, '_'); std::sort(udp_addr_vec.begin(), udp_addr_vec.end()); udp_addr_vec.erase(std::unique(udp_addr_vec.begin(), udp_addr_vec.end()), udp_addr_vec.end()); udp_sink_ptr = std::unique_ptr(new Monitor_Pvt_Udp_Sink(udp_addr_vec, conf_.udp_port, conf_.protobuf_enabled)); } else { udp_sink_ptr = nullptr; } // Create Sys V message queue first_fix = true; sysv_msg_key = 1101; int msgflg = IPC_CREAT | 0666; if ((sysv_msqid = msgget(sysv_msg_key, msgflg)) == -1) { std::cout << "GNSS-SDR can not create message queues!" << std::endl; throw std::exception(); } // Display time in local time zone d_show_local_time_zone = conf_.show_local_time_zone; std::ostringstream os; #ifdef HAS_PUT_TIME time_t when = std::time(nullptr); auto const tm = *std::localtime(&when); os << std::put_time(&tm, "%z"); #endif std::string utc_diff_str = os.str(); // in ISO 8601 format: "+HHMM" or "-HHMM" if (utc_diff_str.empty()) { utc_diff_str = "+0000"; } int h = std::stoi(utc_diff_str.substr(0, 3), nullptr, 10); int m = std::stoi(utc_diff_str[0] + utc_diff_str.substr(3), nullptr, 10); d_utc_diff_time = boost::posix_time::hours(h) + boost::posix_time::minutes(m); std::ostringstream os2; #ifdef HAS_PUT_TIME os2 << std::put_time(&tm, "%Z"); #endif std::string time_zone_abrv = os2.str(); if (time_zone_abrv.empty()) { if (utc_diff_str == "+0000") { d_local_time_str = " UTC"; } else { d_local_time_str = " (UTC " + utc_diff_str.substr(0, 3) + ":" + utc_diff_str.substr(3, 2) + ")"; } } else { d_local_time_str = std::string(" ") + time_zone_abrv + " (UTC " + utc_diff_str.substr(0, 3) + ":" + utc_diff_str.substr(3, 2) + ")"; } // user PVT solver d_user_pvt_solver = std::make_shared(static_cast(nchannels), dump_ls_pvt_filename, d_dump, d_dump_mat, rtk); d_user_pvt_solver->set_averaging_depth(1); // internal PVT solver, mainly used to estimate the receiver clock rtk_t internal_rtk = rtk; internal_rtk.opt.mode = PMODE_SINGLE; // use single positioning mode in internal PVT solver d_internal_pvt_solver = std::make_shared(static_cast(nchannels), dump_ls_pvt_filename, false, false, internal_rtk); d_internal_pvt_solver->set_averaging_depth(1); d_waiting_obs_block_rx_clock_offset_correction_msg = false; start = std::chrono::system_clock::now(); } rtklib_pvt_gs::~rtklib_pvt_gs() { msgctl(sysv_msqid, IPC_RMID, nullptr); try { if (d_xml_storage) { // save GPS L2CM ephemeris to XML file std::string file_name = xml_base_path + "gps_cnav_ephemeris.xml"; if (d_internal_pvt_solver->gps_cnav_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_cnav_ephemeris_map", d_internal_pvt_solver->gps_cnav_ephemeris_map); LOG(INFO) << "Saved GPS L2CM or L5 Ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS L2CM or L5 Ephemeris, map is empty"; } // save GPS L1 CA ephemeris to XML file file_name = xml_base_path + "gps_ephemeris.xml"; if (d_internal_pvt_solver->gps_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_ephemeris_map", d_internal_pvt_solver->gps_ephemeris_map); LOG(INFO) << "Saved GPS L1 CA Ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS L1 CA Ephemeris, map is empty"; } // save Galileo E1 ephemeris to XML file file_name = xml_base_path + "gal_ephemeris.xml"; if (d_internal_pvt_solver->galileo_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gal_ephemeris_map", d_internal_pvt_solver->galileo_ephemeris_map); LOG(INFO) << "Saved Galileo E1 Ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save Galileo E1 Ephemeris, map is empty"; } // save GLONASS GNAV ephemeris to XML file file_name = xml_base_path + "eph_GLONASS_GNAV.xml"; if (d_internal_pvt_solver->glonass_gnav_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gnav_ephemeris_map", d_internal_pvt_solver->glonass_gnav_ephemeris_map); LOG(INFO) << "Saved GLONASS GNAV Ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GLONASS GNAV Ephemeris, map is empty"; } // Save GPS UTC model parameters file_name = xml_base_path + "gps_utc_model.xml"; if (d_internal_pvt_solver->gps_utc_model.valid) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_utc_model", d_internal_pvt_solver->gps_utc_model); LOG(INFO) << "Saved GPS UTC model parameters"; } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS UTC model parameters, not valid data"; } // Save Galileo UTC model parameters file_name = xml_base_path + "gal_utc_model.xml"; if (d_internal_pvt_solver->galileo_utc_model.Delta_tLS_6 != 0.0) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gal_utc_model", d_internal_pvt_solver->galileo_utc_model); LOG(INFO) << "Saved Galileo UTC model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save Galileo UTC model parameters, not valid data"; } // Save GPS iono parameters file_name = xml_base_path + "gps_iono.xml"; if (d_internal_pvt_solver->gps_iono.valid == true) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_iono_model", d_internal_pvt_solver->gps_iono); LOG(INFO) << "Saved GPS ionospheric model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS ionospheric model parameters, not valid data"; } // Save GPS CNAV iono parameters file_name = xml_base_path + "gps_cnav_iono.xml"; if (d_internal_pvt_solver->gps_cnav_iono.valid == true) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_cnav_iono_model", d_internal_pvt_solver->gps_cnav_iono); LOG(INFO) << "Saved GPS CNAV ionospheric model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS CNAV ionospheric model parameters, not valid data"; } // Save Galileo iono parameters file_name = xml_base_path + "gal_iono.xml"; if (d_internal_pvt_solver->galileo_iono.ai0_5 != 0.0) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gal_iono_model", d_internal_pvt_solver->galileo_iono); LOG(INFO) << "Saved Galileo ionospheric model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save Galileo ionospheric model parameters, not valid data"; } // save GPS almanac to XML file file_name = xml_base_path + "gps_almanac.xml"; if (d_internal_pvt_solver->gps_almanac_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gps_almanac_map", d_internal_pvt_solver->gps_almanac_map); LOG(INFO) << "Saved GPS almanac map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS almanac, map is empty"; } // Save Galileo almanac file_name = xml_base_path + "gal_almanac.xml"; if (d_internal_pvt_solver->galileo_almanac_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gal_almanac_map", d_internal_pvt_solver->galileo_almanac_map); LOG(INFO) << "Saved Galileo almanac data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save Galileo almanac, not valid data"; } // Save GPS CNAV UTC model parameters file_name = xml_base_path + "gps_cnav_utc_model.xml"; if (d_internal_pvt_solver->gps_cnav_utc_model.valid) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_cnav_utc_model", d_internal_pvt_solver->gps_cnav_utc_model); LOG(INFO) << "Saved GPS CNAV UTC model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GPS CNAV UTC model parameters, not valid data"; } // save GLONASS GNAV ephemeris to XML file file_name = xml_base_path + "glo_gnav_ephemeris.xml"; if (d_internal_pvt_solver->glonass_gnav_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gnav_ephemeris_map", d_internal_pvt_solver->glonass_gnav_ephemeris_map); LOG(INFO) << "Saved GLONASS GNAV ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GLONASS GNAV ephemeris, map is empty"; } // save GLONASS UTC model parameters to XML file file_name = xml_base_path + "glo_utc_model.xml"; if (d_internal_pvt_solver->glonass_gnav_utc_model.valid) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gnav_utc_model", d_internal_pvt_solver->glonass_gnav_utc_model); LOG(INFO) << "Saved GLONASS UTC model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save GLONASS GNAV ephemeris, not valid data"; } // save BeiDou DNAV ephemeris to XML file file_name = xml_base_path + "bds_dnav_ephemeris.xml"; if (d_internal_pvt_solver->beidou_dnav_ephemeris_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_bds_dnav_ephemeris_map", d_internal_pvt_solver->beidou_dnav_ephemeris_map); LOG(INFO) << "Saved BeiDou DNAV Ephemeris map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save BeiDou DNAV Ephemeris, map is empty"; } // Save BeiDou DNAV iono parameters file_name = xml_base_path + "bds_dnav_iono.xml"; if (d_internal_pvt_solver->beidou_dnav_iono.valid) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_bds_dnav_iono_model", d_internal_pvt_solver->beidou_dnav_iono); LOG(INFO) << "Saved BeiDou DNAV ionospheric model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save BeiDou DNAV ionospheric model parameters, not valid data"; } // save BeiDou DNAV almanac to XML file file_name = xml_base_path + "bds_dnav_almanac.xml"; if (d_internal_pvt_solver->beidou_dnav_almanac_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_bds_dnav_almanac_map", d_internal_pvt_solver->beidou_dnav_almanac_map); LOG(INFO) << "Saved BeiDou DNAV almanac map data"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save BeiDou DNAV almanac, map is empty"; } // Save BeiDou UTC model parameters file_name = xml_base_path + "bds_dnav_utc_model.xml"; if (d_internal_pvt_solver->beidou_dnav_utc_model.valid) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_bds_dnav_utc_model", d_internal_pvt_solver->beidou_dnav_utc_model); LOG(INFO) << "Saved BeiDou DNAV UTC model parameters"; } catch (const boost::archive::archive_exception& e) { LOG(WARNING) << e.what(); } catch (const std::ofstream::failure& e) { LOG(WARNING) << "Problem opening output XML file"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); } } else { LOG(INFO) << "Failed to save BeiDou DNAV UTC model parameters, not valid data"; } } } catch (std::length_error& e) { LOG(WARNING) << e.what(); } } void rtklib_pvt_gs::msg_handler_telemetry(const pmt::pmt_t& msg) { try { // ************* GPS telemetry ***************** if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS EPHEMERIS ### std::shared_ptr gps_eph; gps_eph = boost::any_cast>(pmt::any_ref(msg)); DLOG(INFO) << "Ephemeris record has arrived from SAT ID " << gps_eph->i_satellite_PRN << " (Block " << gps_eph->satelliteBlock[gps_eph->i_satellite_PRN] << ")" << "inserted with Toe=" << gps_eph->d_Toe << " and GPS Week=" << gps_eph->i_GPS_week; // update/insert new ephemeris record to the global ephemeris map if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { bool new_annotation = false; if (d_internal_pvt_solver->gps_ephemeris_map.find(gps_eph->i_satellite_PRN) == d_internal_pvt_solver->gps_ephemeris_map.cend()) { new_annotation = true; } else { if (d_internal_pvt_solver->gps_ephemeris_map[gps_eph->i_satellite_PRN].d_Toe != gps_eph->d_Toe) { new_annotation = true; } } if (new_annotation == true) { // New record! std::map new_eph; std::map new_gal_eph; std::map new_glo_eph; new_eph[gps_eph->i_satellite_PRN] = *gps_eph; switch (type_of_rx) { case 1: // GPS L1 C/A only rp->log_rinex_nav(rp->navFile, new_eph); break; case 8: // L1+L5 rp->log_rinex_nav(rp->navFile, new_eph); break; case 11: // GPS L1 C/A + Galileo E5b rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; case 26: // GPS L1 C/A + GLONASS L1 C/A if (d_rinex_version == 3) { rp->log_rinex_nav(rp->navMixFile, new_eph, new_glo_eph); } if (d_rinex_version == 2) { rp->log_rinex_nav(rp->navFile, new_glo_eph); } break; case 29: // GPS L1 C/A + GLONASS L2 C/A if (d_rinex_version == 3) { rp->log_rinex_nav(rp->navMixFile, new_eph, new_glo_eph); } if (d_rinex_version == 2) { rp->log_rinex_nav(rp->navFile, new_eph); } break; case 32: // L1+E1+L5+E5a rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; case 33: // L1+E1+E5a rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; default: break; } } } d_internal_pvt_solver->gps_ephemeris_map[gps_eph->i_satellite_PRN] = *gps_eph; d_user_pvt_solver->gps_ephemeris_map[gps_eph->i_satellite_PRN] = *gps_eph; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS IONO ### std::shared_ptr gps_iono; gps_iono = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->gps_iono = *gps_iono; d_user_pvt_solver->gps_iono = *gps_iono; DLOG(INFO) << "New IONO record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS UTC MODEL ### std::shared_ptr gps_utc_model; gps_utc_model = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->gps_utc_model = *gps_utc_model; d_user_pvt_solver->gps_utc_model = *gps_utc_model; DLOG(INFO) << "New UTC record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS CNAV message ### std::shared_ptr gps_cnav_ephemeris; gps_cnav_ephemeris = boost::any_cast>(pmt::any_ref(msg)); // update/insert new ephemeris record to the global ephemeris map if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { bool new_annotation = false; if (d_internal_pvt_solver->gps_cnav_ephemeris_map.find(gps_cnav_ephemeris->i_satellite_PRN) == d_internal_pvt_solver->gps_cnav_ephemeris_map.cend()) { new_annotation = true; } else { if (d_internal_pvt_solver->gps_cnav_ephemeris_map[gps_cnav_ephemeris->i_satellite_PRN].d_Toe1 != gps_cnav_ephemeris->d_Toe1) { new_annotation = true; } } if (new_annotation == true) { // New record! std::map new_gal_eph; std::map new_cnav_eph; std::map new_glo_eph; new_cnav_eph[gps_cnav_ephemeris->i_satellite_PRN] = *gps_cnav_ephemeris; switch (type_of_rx) { case 2: // GPS L2C only rp->log_rinex_nav(rp->navFile, new_cnav_eph); break; case 3: // GPS L5 only rp->log_rinex_nav(rp->navFile, new_cnav_eph); break; case 7: // GPS L1 C/A + GPS L2C rp->log_rinex_nav(rp->navFile, new_cnav_eph); break; case 13: // L5+E5a rp->log_rinex_nav(rp->navFile, new_cnav_eph, new_gal_eph); break; case 28: // GPS L2C + GLONASS L1 C/A rp->log_rinex_nav(rp->navMixFile, new_cnav_eph, new_glo_eph); break; case 31: // GPS L2C + GLONASS L2 C/A rp->log_rinex_nav(rp->navMixFile, new_cnav_eph, new_glo_eph); break; default: break; } } } d_internal_pvt_solver->gps_cnav_ephemeris_map[gps_cnav_ephemeris->i_satellite_PRN] = *gps_cnav_ephemeris; d_user_pvt_solver->gps_cnav_ephemeris_map[gps_cnav_ephemeris->i_satellite_PRN] = *gps_cnav_ephemeris; DLOG(INFO) << "New GPS CNAV ephemeris record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS CNAV IONO ### std::shared_ptr gps_cnav_iono; gps_cnav_iono = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->gps_cnav_iono = *gps_cnav_iono; d_user_pvt_solver->gps_cnav_iono = *gps_cnav_iono; DLOG(INFO) << "New CNAV IONO record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS CNAV UTC MODEL ### std::shared_ptr gps_cnav_utc_model; gps_cnav_utc_model = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->gps_cnav_utc_model = *gps_cnav_utc_model; d_user_pvt_solver->gps_cnav_utc_model = *gps_cnav_utc_model; DLOG(INFO) << "New CNAV UTC record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GPS ALMANAC ### std::shared_ptr gps_almanac; gps_almanac = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->gps_almanac_map[gps_almanac->i_satellite_PRN] = *gps_almanac; d_user_pvt_solver->gps_almanac_map[gps_almanac->i_satellite_PRN] = *gps_almanac; DLOG(INFO) << "New GPS almanac record has arrived "; } // **************** Galileo telemetry ******************** else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Galileo EPHEMERIS ### std::shared_ptr galileo_eph; galileo_eph = boost::any_cast>(pmt::any_ref(msg)); // insert new ephemeris record DLOG(INFO) << "Galileo New Ephemeris record inserted in global map with TOW =" << galileo_eph->TOW_5 << ", GALILEO Week Number =" << galileo_eph->WN_5 << " and Ephemeris IOD = " << galileo_eph->IOD_ephemeris; // update/insert new ephemeris record to the global ephemeris map if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { bool new_annotation = false; if (d_internal_pvt_solver->galileo_ephemeris_map.find(galileo_eph->i_satellite_PRN) == d_internal_pvt_solver->galileo_ephemeris_map.cend()) { new_annotation = true; } else { if (d_internal_pvt_solver->galileo_ephemeris_map[galileo_eph->i_satellite_PRN].t0e_1 != galileo_eph->t0e_1) { new_annotation = true; } } if (new_annotation == true) { // New record! std::map new_gal_eph; std::map new_cnav_eph; std::map new_eph; std::map new_glo_eph; new_gal_eph[galileo_eph->i_satellite_PRN] = *galileo_eph; switch (type_of_rx) { case 6: // Galileo E5b only rp->log_rinex_nav(rp->navGalFile, new_gal_eph); break; case 11: // GPS L1 C/A + Galileo E5b rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; case 13: // L5+E5a rp->log_rinex_nav(rp->navFile, new_cnav_eph, new_gal_eph); break; case 15: // Galileo E1B + Galileo E5b rp->log_rinex_nav(rp->navGalFile, new_gal_eph); break; case 27: // Galileo E1B + GLONASS L1 C/A rp->log_rinex_nav(rp->navMixFile, new_gal_eph, new_glo_eph); break; case 30: // Galileo E1B + GLONASS L2 C/A rp->log_rinex_nav(rp->navMixFile, new_gal_eph, new_glo_eph); break; case 32: // L1+E1+L5+E5a rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; case 33: // L1+E1+E5a rp->log_rinex_nav(rp->navMixFile, new_eph, new_gal_eph); break; default: break; } } } d_internal_pvt_solver->galileo_ephemeris_map[galileo_eph->i_satellite_PRN] = *galileo_eph; d_user_pvt_solver->galileo_ephemeris_map[galileo_eph->i_satellite_PRN] = *galileo_eph; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Galileo IONO ### std::shared_ptr galileo_iono; galileo_iono = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->galileo_iono = *galileo_iono; d_user_pvt_solver->galileo_iono = *galileo_iono; DLOG(INFO) << "New IONO record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Galileo UTC MODEL ### std::shared_ptr galileo_utc_model; galileo_utc_model = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->galileo_utc_model = *galileo_utc_model; d_user_pvt_solver->galileo_utc_model = *galileo_utc_model; DLOG(INFO) << "New UTC record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Galileo Almanac ### std::shared_ptr galileo_almanac_helper; galileo_almanac_helper = boost::any_cast>(pmt::any_ref(msg)); Galileo_Almanac sv1 = galileo_almanac_helper->get_almanac(1); Galileo_Almanac sv2 = galileo_almanac_helper->get_almanac(2); Galileo_Almanac sv3 = galileo_almanac_helper->get_almanac(3); if (sv1.i_satellite_PRN != 0) { d_internal_pvt_solver->galileo_almanac_map[sv1.i_satellite_PRN] = sv1; d_user_pvt_solver->galileo_almanac_map[sv1.i_satellite_PRN] = sv1; } if (sv2.i_satellite_PRN != 0) { d_internal_pvt_solver->galileo_almanac_map[sv2.i_satellite_PRN] = sv2; d_user_pvt_solver->galileo_almanac_map[sv2.i_satellite_PRN] = sv2; } if (sv3.i_satellite_PRN != 0) { d_internal_pvt_solver->galileo_almanac_map[sv3.i_satellite_PRN] = sv3; d_user_pvt_solver->galileo_almanac_map[sv3.i_satellite_PRN] = sv3; } DLOG(INFO) << "New Galileo Almanac data have arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Galileo Almanac ### std::shared_ptr galileo_alm; galileo_alm = boost::any_cast>(pmt::any_ref(msg)); // update/insert new almanac record to the global almanac map d_internal_pvt_solver->galileo_almanac_map[galileo_alm->i_satellite_PRN] = *galileo_alm; d_user_pvt_solver->galileo_almanac_map[galileo_alm->i_satellite_PRN] = *galileo_alm; } // **************** GLONASS GNAV Telemetry ************************** else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GLONASS GNAV EPHEMERIS ### std::shared_ptr glonass_gnav_eph; glonass_gnav_eph = boost::any_cast>(pmt::any_ref(msg)); // TODO Add GLONASS with gps week number and tow, // insert new ephemeris record DLOG(INFO) << "GLONASS GNAV New Ephemeris record inserted in global map with TOW =" << glonass_gnav_eph->d_TOW << ", Week Number =" << glonass_gnav_eph->d_WN << " and Ephemeris IOD in UTC = " << glonass_gnav_eph->compute_GLONASS_time(glonass_gnav_eph->d_t_b) << " from SV = " << glonass_gnav_eph->i_satellite_slot_number; // update/insert new ephemeris record to the global ephemeris map if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { bool new_annotation = false; if (d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(glonass_gnav_eph->i_satellite_PRN) == d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend()) { new_annotation = true; } else { if (d_internal_pvt_solver->glonass_gnav_ephemeris_map[glonass_gnav_eph->i_satellite_PRN].d_t_b != glonass_gnav_eph->d_t_b) { new_annotation = true; } } if (new_annotation == true) { // New record! std::map new_gal_eph; std::map new_cnav_eph; std::map new_eph; std::map new_glo_eph; new_glo_eph[glonass_gnav_eph->i_satellite_PRN] = *glonass_gnav_eph; switch (type_of_rx) { case 25: // GLONASS L1 C/A + GLONASS L2 C/A rp->log_rinex_nav(rp->navGloFile, new_glo_eph); break; case 26: // GPS L1 C/A + GLONASS L1 C/A if (d_rinex_version == 3) { rp->log_rinex_nav(rp->navMixFile, new_eph, new_glo_eph); } if (d_rinex_version == 2) { rp->log_rinex_nav(rp->navGloFile, new_glo_eph); } break; case 27: // Galileo E1B + GLONASS L1 C/A rp->log_rinex_nav(rp->navMixFile, new_gal_eph, new_glo_eph); break; case 28: // GPS L2C + GLONASS L1 C/A rp->log_rinex_nav(rp->navMixFile, new_cnav_eph, new_glo_eph); break; case 29: // GPS L1 C/A + GLONASS L2 C/A if (d_rinex_version == 3) { rp->log_rinex_nav(rp->navMixFile, new_eph, new_glo_eph); } if (d_rinex_version == 2) { rp->log_rinex_nav(rp->navGloFile, new_glo_eph); } break; case 30: // Galileo E1B + GLONASS L2 C/A rp->log_rinex_nav(rp->navMixFile, new_gal_eph, new_glo_eph); break; case 31: // GPS L2C + GLONASS L2 C/A rp->log_rinex_nav(rp->navMixFile, new_cnav_eph, new_glo_eph); break; default: break; } } } d_internal_pvt_solver->glonass_gnav_ephemeris_map[glonass_gnav_eph->i_satellite_PRN] = *glonass_gnav_eph; d_user_pvt_solver->glonass_gnav_ephemeris_map[glonass_gnav_eph->i_satellite_PRN] = *glonass_gnav_eph; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GLONASS GNAV UTC MODEL ### std::shared_ptr glonass_gnav_utc_model; glonass_gnav_utc_model = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->glonass_gnav_utc_model = *glonass_gnav_utc_model; d_user_pvt_solver->glonass_gnav_utc_model = *glonass_gnav_utc_model; DLOG(INFO) << "New GLONASS GNAV UTC record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### GLONASS GNAV Almanac ### std::shared_ptr glonass_gnav_almanac; glonass_gnav_almanac = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->glonass_gnav_almanac = *glonass_gnav_almanac; d_user_pvt_solver->glonass_gnav_almanac = *glonass_gnav_almanac; DLOG(INFO) << "New GLONASS GNAV Almanac has arrived " << ", GLONASS GNAV Slot Number =" << glonass_gnav_almanac->d_n_A; } // ************* BeiDou telemetry ***************** else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### Beidou EPHEMERIS ### std::shared_ptr bds_dnav_eph; bds_dnav_eph = boost::any_cast>(pmt::any_ref(msg)); DLOG(INFO) << "Ephemeris record has arrived from SAT ID " << bds_dnav_eph->i_satellite_PRN << " (Block " << bds_dnav_eph->satelliteBlock[bds_dnav_eph->i_satellite_PRN] << ")" << "inserted with Toe=" << bds_dnav_eph->d_Toe << " and BDS Week=" << bds_dnav_eph->i_BEIDOU_week; // update/insert new ephemeris record to the global ephemeris map if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { bool new_annotation = false; if (d_internal_pvt_solver->beidou_dnav_ephemeris_map.find(bds_dnav_eph->i_satellite_PRN) == d_internal_pvt_solver->beidou_dnav_ephemeris_map.cend()) { new_annotation = true; } else { if (d_internal_pvt_solver->beidou_dnav_ephemeris_map[bds_dnav_eph->i_satellite_PRN].d_Toc != bds_dnav_eph->d_Toc) { new_annotation = true; } } if (new_annotation == true) { // New record! std::map new_bds_eph; new_bds_eph[bds_dnav_eph->i_satellite_PRN] = *bds_dnav_eph; switch (type_of_rx) { case 500: // BDS B1I only rp->log_rinex_nav(rp->navFile, new_bds_eph); break; default: break; } } } d_internal_pvt_solver->beidou_dnav_ephemeris_map[bds_dnav_eph->i_satellite_PRN] = *bds_dnav_eph; d_user_pvt_solver->beidou_dnav_ephemeris_map[bds_dnav_eph->i_satellite_PRN] = *bds_dnav_eph; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### BeiDou IONO ### std::shared_ptr bds_dnav_iono; bds_dnav_iono = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->beidou_dnav_iono = *bds_dnav_iono; d_user_pvt_solver->beidou_dnav_iono = *bds_dnav_iono; DLOG(INFO) << "New BeiDou DNAV IONO record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### BeiDou UTC MODEL ### std::shared_ptr bds_dnav_utc_model; bds_dnav_utc_model = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->beidou_dnav_utc_model = *bds_dnav_utc_model; d_user_pvt_solver->beidou_dnav_utc_model = *bds_dnav_utc_model; DLOG(INFO) << "New BeiDou DNAV UTC record has arrived "; } else if (pmt::any_ref(msg).type() == typeid(std::shared_ptr)) { // ### BeiDou ALMANAC ### std::shared_ptr bds_dnav_almanac; bds_dnav_almanac = boost::any_cast>(pmt::any_ref(msg)); d_internal_pvt_solver->beidou_dnav_almanac_map[bds_dnav_almanac->i_satellite_PRN] = *bds_dnav_almanac; d_user_pvt_solver->beidou_dnav_almanac_map[bds_dnav_almanac->i_satellite_PRN] = *bds_dnav_almanac; DLOG(INFO) << "New BeiDou DNAV almanac record has arrived "; } else { LOG(WARNING) << "msg_handler_telemetry unknown object type!"; } } catch (boost::bad_any_cast& e) { LOG(WARNING) << "msg_handler_telemetry Bad any cast!"; } } std::map rtklib_pvt_gs::get_gps_ephemeris_map() const { return d_internal_pvt_solver->gps_ephemeris_map; } std::map rtklib_pvt_gs::get_gps_almanac_map() const { return d_internal_pvt_solver->gps_almanac_map; } std::map rtklib_pvt_gs::get_galileo_ephemeris_map() const { return d_internal_pvt_solver->galileo_ephemeris_map; } std::map rtklib_pvt_gs::get_galileo_almanac_map() const { return d_internal_pvt_solver->galileo_almanac_map; } std::map rtklib_pvt_gs::get_beidou_dnav_ephemeris_map() const { return d_internal_pvt_solver->beidou_dnav_ephemeris_map; } std::map rtklib_pvt_gs::get_beidou_dnav_almanac_map() const { return d_internal_pvt_solver->beidou_dnav_almanac_map; } void rtklib_pvt_gs::clear_ephemeris() { d_internal_pvt_solver->gps_ephemeris_map.clear(); d_internal_pvt_solver->gps_almanac_map.clear(); d_internal_pvt_solver->galileo_ephemeris_map.clear(); d_internal_pvt_solver->galileo_almanac_map.clear(); d_internal_pvt_solver->beidou_dnav_ephemeris_map.clear(); d_internal_pvt_solver->beidou_dnav_almanac_map.clear(); d_user_pvt_solver->gps_ephemeris_map.clear(); d_user_pvt_solver->gps_almanac_map.clear(); d_user_pvt_solver->galileo_ephemeris_map.clear(); d_user_pvt_solver->galileo_almanac_map.clear(); d_user_pvt_solver->beidou_dnav_ephemeris_map.clear(); d_user_pvt_solver->beidou_dnav_almanac_map.clear(); } bool rtklib_pvt_gs::send_sys_v_ttff_msg(ttff_msgbuf ttff) { // Fill Sys V message structures int msgsend_size; ttff_msgbuf msg; msg.ttff = ttff.ttff; msgsend_size = sizeof(msg.ttff); msg.mtype = 1; // default message ID // SEND SOLUTION OVER A MESSAGE QUEUE // non-blocking Sys V message send msgsnd(sysv_msqid, &msg, msgsend_size, IPC_NOWAIT); return true; } bool rtklib_pvt_gs::save_gnss_synchro_map_xml(const std::string& file_name) { if (gnss_observables_map.empty() == false) { std::ofstream ofs; try { ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("GNSS-SDR_gnss_synchro_map", gnss_observables_map); LOG(INFO) << "Saved gnss_sychro map data"; } catch (const std::exception& e) { LOG(WARNING) << e.what(); return false; } return true; } LOG(WARNING) << "Failed to save gnss_synchro, map is empty"; return false; } bool rtklib_pvt_gs::load_gnss_synchro_map_xml(const std::string& file_name) { // load from xml (boost serialize) std::ifstream ifs; try { ifs.open(file_name.c_str(), std::ifstream::binary | std::ifstream::in); boost::archive::xml_iarchive xml(ifs); gnss_observables_map.clear(); xml >> boost::serialization::make_nvp("GNSS-SDR_gnss_synchro_map", gnss_observables_map); // std::cout << "Loaded gnss_synchro map data with " << gnss_synchro_map.size() << " pseudoranges" << std::endl; } catch (const std::exception& e) { std::cout << e.what() << "File: " << file_name; return false; } return true; } std::vector rtklib_pvt_gs::split_string(const std::string& s, char delim) const { std::vector v; std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { *(std::back_inserter(v)++) = item; } return v; } bool rtklib_pvt_gs::get_latest_PVT(double* longitude_deg, double* latitude_deg, double* height_m, double* ground_speed_kmh, double* course_over_ground_deg, time_t* UTC_time) const { if (d_user_pvt_solver->is_valid_position()) { *latitude_deg = d_user_pvt_solver->get_latitude(); *longitude_deg = d_user_pvt_solver->get_longitude(); *height_m = d_user_pvt_solver->get_height(); *ground_speed_kmh = d_user_pvt_solver->get_speed_over_ground() * 3600.0 / 1000.0; *course_over_ground_deg = d_user_pvt_solver->get_course_over_ground(); *UTC_time = convert_to_time_t(d_user_pvt_solver->get_position_UTC_time()); return true; } return false; } void rtklib_pvt_gs::apply_rx_clock_offset(std::map& observables_map, double rx_clock_offset_s) { // apply corrections according to Rinex 3.04, Table 1: Observation Corrections for Receiver Clock Offset std::map::iterator observables_iter; for (observables_iter = observables_map.begin(); observables_iter != observables_map.end(); observables_iter++) { // all observables in the map are valid observables_iter->second.RX_time -= rx_clock_offset_s; observables_iter->second.Pseudorange_m -= rx_clock_offset_s * SPEED_OF_LIGHT; switch (mapStringValues_[observables_iter->second.Signal]) { case evGPS_1C: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ1; break; case evGPS_L5: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ5; break; case evSBAS_1C: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ1; break; case evGAL_1B: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ1; break; case evGAL_5X: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ5; break; case evGPS_2S: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ2; break; case evBDS_B3: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ3_BDS; break; case evGLO_1G: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ1_GLO; break; case evGLO_2G: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ2_GLO; break; case evBDS_B1: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ1_BDS; break; case evBDS_B2: observables_iter->second.Carrier_phase_rads -= rx_clock_offset_s * FREQ2_BDS; break; default: break; } } } std::map rtklib_pvt_gs::interpolate_observables(std::map& observables_map_t0, std::map& observables_map_t1, double rx_time_s) { std::map interp_observables_map; // Linear interpolation: y(t) = y(t0) + (y(t1) - y(t0)) * (t - t0) / (t1 - t0) // check TOW rollover double time_factor; if ((observables_map_t1.cbegin()->second.RX_time - observables_map_t0.cbegin()->second.RX_time) > 0) { time_factor = (rx_time_s - observables_map_t0.cbegin()->second.RX_time) / (observables_map_t1.cbegin()->second.RX_time - observables_map_t0.cbegin()->second.RX_time); } else { // TOW rollover situation time_factor = (604800000.0 + rx_time_s - observables_map_t0.cbegin()->second.RX_time) / (604800000.0 + observables_map_t1.cbegin()->second.RX_time - observables_map_t0.cbegin()->second.RX_time); } std::map::const_iterator observables_iter; for (observables_iter = observables_map_t0.cbegin(); observables_iter != observables_map_t0.cend(); observables_iter++) { // 1. Check if the observable exist in t0 and t1 // the map key is the channel ID (see work()) try { if (observables_map_t1.at(observables_iter->first).PRN == observables_iter->second.PRN) { interp_observables_map.insert(std::pair(observables_iter->first, observables_iter->second)); interp_observables_map.at(observables_iter->first).RX_time = rx_time_s; // interpolation point interp_observables_map.at(observables_iter->first).Pseudorange_m += (observables_map_t1.at(observables_iter->first).Pseudorange_m - observables_iter->second.Pseudorange_m) * time_factor; interp_observables_map.at(observables_iter->first).Carrier_phase_rads += (observables_map_t1.at(observables_iter->first).Carrier_phase_rads - observables_iter->second.Carrier_phase_rads) * time_factor; interp_observables_map.at(observables_iter->first).Carrier_Doppler_hz += (observables_map_t1.at(observables_iter->first).Carrier_Doppler_hz - observables_iter->second.Carrier_Doppler_hz) * time_factor; } } catch (const std::out_of_range& oor) { // observable does not exist in t1 } } return interp_observables_map; } int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items __attribute__((unused))) { for (int32_t epoch = 0; epoch < noutput_items; epoch++) { bool flag_display_pvt = false; bool flag_compute_pvt_output = false; bool flag_write_RTCM_1019_output = false; bool flag_write_RTCM_1020_output = false; bool flag_write_RTCM_1045_output = false; bool flag_write_RTCM_MSM_output = false; bool flag_write_RINEX_obs_output = false; gnss_observables_map.clear(); const auto** in = reinterpret_cast(&input_items[0]); // Get the input buffer pointer // ############ 1. READ PSEUDORANGES #### for (uint32_t i = 0; i < d_nchannels; i++) { if (in[i][epoch].Flag_valid_pseudorange) { std::map::const_iterator tmp_eph_iter_gps = d_internal_pvt_solver->gps_ephemeris_map.find(in[i][epoch].PRN); std::map::const_iterator tmp_eph_iter_gal = d_internal_pvt_solver->galileo_ephemeris_map.find(in[i][epoch].PRN); std::map::const_iterator tmp_eph_iter_cnav = d_internal_pvt_solver->gps_cnav_ephemeris_map.find(in[i][epoch].PRN); std::map::const_iterator tmp_eph_iter_glo_gnav = d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(in[i][epoch].PRN); std::map::const_iterator tmp_eph_iter_bds_dnav = d_internal_pvt_solver->beidou_dnav_ephemeris_map.find(in[i][epoch].PRN); bool store_valid_observable = false; if (tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend()) { uint32_t prn_aux = tmp_eph_iter_gps->second.i_satellite_PRN; if ((prn_aux == in[i][epoch].PRN) and (std::string(in[i][epoch].Signal) == "1C")) { store_valid_observable = true; } } if (tmp_eph_iter_gal != d_internal_pvt_solver->galileo_ephemeris_map.cend()) { uint32_t prn_aux = tmp_eph_iter_gal->second.i_satellite_PRN; if ((prn_aux == in[i][epoch].PRN) and ((std::string(in[i][epoch].Signal) == "1B") or (std::string(in[i][epoch].Signal) == "5X"))) { store_valid_observable = true; } } if (tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend()) { uint32_t prn_aux = tmp_eph_iter_cnav->second.i_satellite_PRN; if ((prn_aux == in[i][epoch].PRN) and ((std::string(in[i][epoch].Signal) == "2S") or (std::string(in[i][epoch].Signal) == "L5"))) { store_valid_observable = true; } } if (tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend()) { uint32_t prn_aux = tmp_eph_iter_glo_gnav->second.i_satellite_PRN; if ((prn_aux == in[i][epoch].PRN) and ((std::string(in[i][epoch].Signal) == "1G") or (std::string(in[i][epoch].Signal) == "2G"))) { store_valid_observable = true; } } if (tmp_eph_iter_bds_dnav != d_internal_pvt_solver->beidou_dnav_ephemeris_map.cend()) { uint32_t prn_aux = tmp_eph_iter_bds_dnav->second.i_satellite_PRN; if ((prn_aux == in[i][epoch].PRN) and ((std::string(in[i][epoch].Signal) == "B1") or (std::string(in[i][epoch].Signal) == "B3"))) { store_valid_observable = true; } } if (store_valid_observable) { // store valid observables in a map. gnss_observables_map.insert(std::pair(i, in[i][epoch])); } if (b_rtcm_enabled) { try { if (d_internal_pvt_solver->gps_ephemeris_map.empty() == false) { if (tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time } } if (d_internal_pvt_solver->galileo_ephemeris_map.empty() == false) { if (tmp_eph_iter_gal != d_internal_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->lock_time(d_internal_pvt_solver->galileo_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time } } if (d_internal_pvt_solver->gps_cnav_ephemeris_map.empty() == false) { if (tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend()) { d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_cnav_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time } } if (d_internal_pvt_solver->glonass_gnav_ephemeris_map.empty() == false) { if (tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->lock_time(d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time } } } catch (const boost::exception& ex) { std::cout << "RTCM boost exception: " << boost::diagnostic_information(ex) << std::endl; LOG(ERROR) << "RTCM boost exception: " << boost::diagnostic_information(ex); } catch (const std::exception& ex) { std::cout << "RTCM std exception: " << ex.what() << std::endl; LOG(ERROR) << "RTCM std exception: " << ex.what(); } } } } // ############ 2 COMPUTE THE PVT ################################ if (gnss_observables_map.empty() == false) { // LOG(INFO) << "diff raw obs time: " << gnss_observables_map.cbegin()->second.RX_time * 1000.0 - old_time_debug; // old_time_debug = gnss_observables_map.cbegin()->second.RX_time * 1000.0; uint32_t current_RX_time_ms = 0; // #### solve PVT and store the corrected observable set if (d_internal_pvt_solver->get_PVT(gnss_observables_map, false)) { double Rx_clock_offset_s = d_internal_pvt_solver->get_time_offset_s(); if (fabs(Rx_clock_offset_s) * 1000.0 > max_obs_block_rx_clock_offset_ms) { if (!d_waiting_obs_block_rx_clock_offset_correction_msg) { this->message_port_pub(pmt::mp("pvt_to_observables"), pmt::make_any(Rx_clock_offset_s)); d_waiting_obs_block_rx_clock_offset_correction_msg = true; LOG(INFO) << "Sent clock offset correction to observables: " << Rx_clock_offset_s << "[s]"; } } else { d_waiting_obs_block_rx_clock_offset_correction_msg = false; gnss_observables_map_t0 = gnss_observables_map_t1; apply_rx_clock_offset(gnss_observables_map, Rx_clock_offset_s); gnss_observables_map_t1 = gnss_observables_map; // ### select the rx_time and interpolate observables at that time if (!gnss_observables_map_t0.empty()) { uint32_t t0_int_ms = static_cast(gnss_observables_map_t0.cbegin()->second.RX_time * 1000.0); uint32_t adjust_next_20ms = 20 - t0_int_ms % 20; current_RX_time_ms = t0_int_ms + adjust_next_20ms; if (current_RX_time_ms % d_output_rate_ms == 0) { d_rx_time = static_cast(current_RX_time_ms) / 1000.0; // std::cout << " obs time t0: " << gnss_observables_map_t0.cbegin()->second.RX_time // << " t1: " << gnss_observables_map_t1.cbegin()->second.RX_time // << " interp time: " << d_rx_time << std::endl; gnss_observables_map = interpolate_observables(gnss_observables_map_t0, gnss_observables_map_t1, d_rx_time); flag_compute_pvt_output = true; // d_rx_time = current_RX_time; // std::cout.precision(17); // std::cout << "current_RX_time: " << current_RX_time << " map time: " << gnss_observables_map.begin()->second.RX_time << std::endl; } } } } // debug code // else // { // LOG(INFO) << "Internal PVT solver error"; // } // compute on the fly PVT solution if (flag_compute_pvt_output == true) { if (d_user_pvt_solver->get_PVT(gnss_observables_map, false)) { double Rx_clock_offset_s = d_user_pvt_solver->get_time_offset_s(); if (fabs(Rx_clock_offset_s) > 0.000001) // 1us !! { LOG(INFO) << "Warning: Rx clock offset at interpolated RX time: " << Rx_clock_offset_s * 1000.0 << "[ms]" << " at RX time: " << static_cast(d_rx_time * 1000.0) << " [ms]"; } else { DLOG(INFO) << "Rx clock offset at interpolated RX time: " << Rx_clock_offset_s * 1000.0 << "[s]" << " at RX time: " << static_cast(d_rx_time * 1000.0) << " [ms]"; // Optional debug code: export observables snapshot for rtklib unit testing // std::cout << "step 1: save gnss_synchro map" << std::endl; // save_gnss_synchro_map_xml("./gnss_synchro_map.xml"); // getchar(); // stop the execution // end debug if (d_display_rate_ms != 0) { if (current_RX_time_ms % d_display_rate_ms == 0) { flag_display_pvt = true; } } if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { if (current_RX_time_ms % d_rtcm_MT1019_rate_ms == 0) { flag_write_RTCM_1019_output = true; } } if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { if (current_RX_time_ms % d_rtcm_MT1020_rate_ms == 0) { flag_write_RTCM_1020_output = true; } } if (d_rtcm_MT1045_rate_ms != 0) { if (current_RX_time_ms % d_rtcm_MT1045_rate_ms == 0) { flag_write_RTCM_1045_output = true; } } // TODO: RTCM 1077, 1087 and 1097 are not used, so, disable the output rates // if (current_RX_time_ms % d_rtcm_MT1077_rate_ms==0 and d_rtcm_MT1077_rate_ms != 0) // { // last_RTCM_1077_output_time = current_RX_time; // } // if (current_RX_time_ms % d_rtcm_MT1087_rate_ms==0 and d_rtcm_MT1087_rate_ms != 0) // { // last_RTCM_1087_output_time = current_RX_time; // } // if (current_RX_time_ms % d_rtcm_MT1097_rate_ms==0 and d_rtcm_MT1097_rate_ms != 0) // { // last_RTCM_1097_output_time = current_RX_time; // } if (d_rtcm_MSM_rate_ms != 0) { if (current_RX_time_ms % d_rtcm_MSM_rate_ms == 0) { flag_write_RTCM_MSM_output = true; } } if (d_rinexobs_rate_ms != 0) { if (current_RX_time_ms % static_cast(d_rinexobs_rate_ms) == 0) { flag_write_RINEX_obs_output = true; } } if (first_fix == true) { if (d_show_local_time_zone) { boost::posix_time::ptime time_first_solution = d_user_pvt_solver->get_position_UTC_time() + d_utc_diff_time; std::cout << "First position fix at " << time_first_solution << d_local_time_str; } else { std::cout << "First position fix at " << d_user_pvt_solver->get_position_UTC_time() << " UTC"; } std::cout << " is Lat = " << d_user_pvt_solver->get_latitude() << " [deg], Long = " << d_user_pvt_solver->get_longitude() << " [deg], Height= " << d_user_pvt_solver->get_height() << " [m]" << std::endl; ttff_msgbuf ttff; ttff.mtype = 1; end = std::chrono::system_clock::now(); std::chrono::duration elapsed_seconds = end - start; ttff.ttff = elapsed_seconds.count(); send_sys_v_ttff_msg(ttff); first_fix = false; } if (d_kml_output_enabled) { if (current_RX_time_ms % d_kml_rate_ms == 0) { d_kml_dump->print_position(d_user_pvt_solver, false); } } if (d_gpx_output_enabled) { if (current_RX_time_ms % d_gpx_rate_ms == 0) { d_gpx_dump->print_position(d_user_pvt_solver, false); } } if (d_geojson_output_enabled) { if (current_RX_time_ms % d_geojson_rate_ms == 0) { d_geojson_printer->print_position(d_user_pvt_solver, false); } } if (d_nmea_output_file_enabled) { if (current_RX_time_ms % d_nmea_rate_ms == 0) { d_nmea_printer->Print_Nmea_Line(d_user_pvt_solver, false); } } /* * TYPE | RECEIVER * 0 | Unknown * 1 | GPS L1 C/A * 2 | GPS L2C * 3 | GPS L5 * 4 | Galileo E1B * 5 | Galileo E5a * 6 | Galileo E5b * 7 | GPS L1 C/A + GPS L2C * 8 | GPS L1 C/A + GPS L5 * 9 | GPS L1 C/A + Galileo E1B * 10 | GPS L1 C/A + Galileo E5a * 11 | GPS L1 C/A + Galileo E5b * 12 | Galileo E1B + GPS L2C * 13 | Galileo E5a + GPS L5 * 14 | Galileo E1B + Galileo E5a * 15 | Galileo E1B + Galileo E5b * 16 | GPS L2C + GPS L5 * 17 | GPS L2C + Galileo E5a * 18 | GPS L2C + Galileo E5b * 21 | GPS L1 C/A + Galileo E1B + GPS L2C * 22 | GPS L1 C/A + Galileo E1B + GPS L5 * 23 | GLONASS L1 C/A * 24 | GLONASS L2 C/A * 25 | GLONASS L1 C/A + GLONASS L2 C/A * 26 | GPS L1 C/A + GLONASS L1 C/A * 27 | Galileo E1B + GLONASS L1 C/A * 28 | GPS L2C + GLONASS L1 C/A * 29 | GPS L1 C/A + GLONASS L2 C/A * 30 | Galileo E1B + GLONASS L2 C/A * 31 | GPS L2C + GLONASS L2 C/A * 32 | GPS L1 C/A + Galileo E1B + GPS L5 + Galileo E5a * 50 | Beidou B1I * 51 | Beidou B1I + GPS L1 C/A * 52 | Beidou B1I + Galileo E1B * 53 | Beidou B1I + GLONASS L1 C/A * 54 | Beidou B1I + GPS L1 C/A + Galileo E1B * 55 | Beidou B1I + GPS L1 C/A + GLONASS L1 C/A + Galileo E1B * 56 | Beidou B1I + Beidou B3I * 60 | Beidou B3I * 61 | Beidou B3I + GPS L2C * 62 | Beidou B3I + GLONASS L2 C/A * 63 | Beidou B3I + GPS L2C + GLONASS L2 C/A */ // ####################### RINEX FILES ################# if (b_rinex_output_enabled) { std::map::const_iterator galileo_ephemeris_iter; std::map::const_iterator gps_ephemeris_iter; std::map::const_iterator gps_cnav_ephemeris_iter; std::map::const_iterator glonass_gnav_ephemeris_iter; std::map::const_iterator beidou_dnav_ephemeris_iter; if (!b_rinex_header_written) // & we have utc data in nav message! { galileo_ephemeris_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); gps_ephemeris_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); gps_cnav_ephemeris_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); glonass_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); beidou_dnav_ephemeris_iter = d_user_pvt_solver->beidou_dnav_ephemeris_map.cbegin(); switch (type_of_rx) { case 1: // GPS L1 C/A only if (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, d_rx_time); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 2: // GPS L2C only if (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) { std::string signal("2S"); rp->rinex_obs_header(rp->obsFile, gps_cnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_cnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 3: // GPS L5 only if (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) { std::string signal("L5"); rp->rinex_obs_header(rp->obsFile, gps_cnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_cnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 4: // Galileo E1B only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time); rp->rinex_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 5: // Galileo E5a only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { std::string signal("5X"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 6: // Galileo E5b only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { std::string signal("7X"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navGalFile, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 7: // GPS L1 C/A + GPS L2C if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { std::string signal("1C 2S"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_cnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 8: // GPS L1 + GPS L5 if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { std::string signal("1C L5"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 9: // GPS L1 C/A + Galileo E1B if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { std::string gal_signal("1B"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 10: // GPS L1 C/A + Galileo E5a if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { std::string gal_signal("5X"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 11: // GPS L1 C/A + Galileo E5b if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { std::string gal_signal("7X"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_ephemeris_map, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 13: // L5+E5a if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { std::string gal_signal("5X"); std::string gps_signal("L5"); rp->rinex_obs_header(rp->obsFile, gps_cnav_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gps_signal, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_cnav_ephemeris_map, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 14: // Galileo E1B + Galileo E5a if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string gal_signal("1B 5X"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 15: // Galileo E1B + Galileo E5b if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string gal_signal("1B 7X"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navGalFile, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 23: // GLONASS L1 C/A only if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { std::string signal("1G"); rp->rinex_obs_header(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, glonass_gnav_ephemeris_iter->second); b_rinex_header_written = true; // do not write header anymore } break; case 24: // GLONASS L2 C/A only if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { std::string signal("2G"); rp->rinex_obs_header(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, glonass_gnav_ephemeris_iter->second); b_rinex_header_written = true; // do not write header anymore } break; case 25: // GLONASS L1 C/A + GLONASS L2 C/A if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { std::string signal("1G 2G"); rp->rinex_obs_header(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, signal); rp->rinex_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, glonass_gnav_ephemeris_iter->second); rp->log_rinex_nav(rp->navGloFile, d_user_pvt_solver->glonass_gnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 26: // GPS L1 C/A + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { std::string glo_signal("1G"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal); if (d_rinex_version == 3) { rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); } if (d_rinex_version == 2) { rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second); rp->rinex_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, glonass_gnav_ephemeris_iter->second); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_ephemeris_map); rp->log_rinex_nav(rp->navGloFile, d_user_pvt_solver->glonass_gnav_ephemeris_map); } b_rinex_header_written = true; // do not write header anymore } break; case 27: // Galileo E1B + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string glo_signal("1G"); std::string gal_signal("1B"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->galileo_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 28: // GPS L2C + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { std::string glo_signal("1G"); rp->rinex_obs_header(rp->obsFile, gps_cnav_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_cnav_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 29: // GPS L1 C/A + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { std::string glo_signal("2G"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal); if (d_rinex_version == 3) { rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); } if (d_rinex_version == 2) { rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second); rp->rinex_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, glonass_gnav_ephemeris_iter->second); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->gps_ephemeris_map); rp->log_rinex_nav(rp->navGloFile, d_user_pvt_solver->glonass_gnav_ephemeris_map); } b_rinex_header_written = true; // do not write header anymore } break; case 30: // Galileo E1B + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string glo_signal("2G"); std::string gal_signal("1B"); rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->galileo_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 31: // GPS L2C + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { std::string glo_signal("2G"); rp->rinex_obs_header(rp->obsFile, gps_cnav_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, glo_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_cnav_ephemeris_map, d_user_pvt_solver->glonass_gnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 32: // L1+E1+L5+E5a if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string gal_signal("1B 5X"); std::string gps_signal("1C L5"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gps_signal, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_ephemeris_map, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 33: // L1+E1+E5a if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { std::string gal_signal("1B 5X"); rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gal_signal); rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->log_rinex_nav(rp->navMixFile, d_user_pvt_solver->gps_ephemeris_map, d_user_pvt_solver->galileo_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 500: // BDS B1I only if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B1"); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->beidou_dnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 501: // BeiDou B1I + GPS L1 C/A if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend())) { std::string bds_signal("B1"); // rp->rinex_obs_header(rp->obsFile, gps_ephemeris_iter->second, beidou_dnav_ephemeris_iter->second, d_rx_time, bds_signal); // rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 502: // BeiDou B1I + Galileo E1B if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend())) { std::string bds_signal("B1"); std::string gal_signal("1B"); // rp->rinex_obs_header(rp->obsFile, galileo_ephemeris_iter->second, beidou_dnav_ephemeris_iter->second, d_rx_time, gal_signal, bds_signal); // rp->rinex_nav_header(rp->navMixFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 503: // BeiDou B1I + GLONASS L1 C/A if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { // rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B1"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); // rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->beidou_dnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 504: // BeiDou B1I + GPS L1 C/A + Galileo E1B if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { // rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B1"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); // rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->beidou_dnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 505: // BeiDou B1I + GPS L1 C/A + GLONASS L1 C/A + Galileo E1B if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { // rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B1"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); // rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->beidou_dnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 506: // BeiDou B1I + Beidou B3I if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { // rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B1"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); // rp->log_rinex_nav(rp->navFile, d_user_pvt_solver->beidou_dnav_ephemeris_map); b_rinex_header_written = true; // do not write header anymore } break; case 600: // BDS B3I only if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B3"); rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 601: // BeiDou B3I + GPS L2C if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B3"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 602: // BeiDou B3I + GLONASS L2 C/A if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B3"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; case 603: // BeiDou B3I + GPS L2C + GLONASS L2 C/A if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->rinex_obs_header(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, "B3"); // rp->rinex_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_iono, d_user_pvt_solver->beidou_dnav_utc_model); b_rinex_header_written = true; // do not write header anymore } break; default: break; } } if (b_rinex_header_written) // The header is already written, we can now log the navigation message data { galileo_ephemeris_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); gps_ephemeris_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); gps_cnav_ephemeris_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); glonass_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); beidou_dnav_ephemeris_iter = d_user_pvt_solver->beidou_dnav_ephemeris_map.cbegin(); // Log observables into the RINEX file if (flag_write_RINEX_obs_output) { switch (type_of_rx) { case 1: // GPS L1 C/A only if (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_utc_model, d_user_pvt_solver->gps_iono, gps_ephemeris_iter->second); b_rinex_header_updated = true; } } break; case 2: // GPS L2C only if (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, gps_cnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->gps_cnav_iono); b_rinex_header_updated = true; } break; case 3: // GPS L5 if (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, gps_cnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->gps_cnav_iono); b_rinex_header_updated = true; } break; case 4: // Galileo E1B only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map, "1B"); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } break; case 5: // Galileo E5a only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map, "5X"); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } break; case 6: // Galileo E5b only if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map, "7X"); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } break; case 7: // GPS L1 C/A + GPS L2C if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_utc_model, d_user_pvt_solver->gps_iono, gps_ephemeris_iter->second); b_rinex_header_updated = true; } } break; case 8: // L1+L5 if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and ((d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0) or (d_user_pvt_solver->gps_utc_model.d_A0 != 0))) { if (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->gps_cnav_iono); } else { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->gps_utc_model, d_user_pvt_solver->gps_iono, gps_ephemeris_iter->second); } b_rinex_header_updated = true; } } break; case 9: // GPS L1 C/A + Galileo E1B if ((galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } } break; case 13: // L5+E5a if ((gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_cnav_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0) and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; // do not write header anymore } break; case 14: // Galileo E1B + Galileo E5a if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map, "1B 5X"); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } break; case 15: // Galileo E1B + Galileo E5b if (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map, "1B 7X"); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_nav_header(rp->navGalFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; } break; case 23: // GLONASS L1 C/A only if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, "1C"); } if (!b_rinex_header_updated and (d_user_pvt_solver->glonass_gnav_utc_model.d_tau_c != 0)) { rp->update_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->glonass_gnav_utc_model); b_rinex_header_updated = true; } break; case 24: // GLONASS L2 C/A only if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, "2C"); } if (!b_rinex_header_updated and (d_user_pvt_solver->glonass_gnav_utc_model.d_tau_c != 0)) { rp->update_nav_header(rp->navGloFile, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->glonass_gnav_utc_model); b_rinex_header_updated = true; } break; case 25: // GLONASS L1 C/A + GLONASS L2 C/A if (glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, "1C 2C"); } if (!b_rinex_header_updated and (d_user_pvt_solver->glonass_gnav_utc_model.d_tau_c != 0)) { rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); rp->update_obs_header(rp->obsFile, d_user_pvt_solver->glonass_gnav_utc_model); b_rinex_header_updated = true; } break; case 26: // GPS L1 C/A + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } } break; case 27: // Galileo E1B + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } break; case 28: // GPS L2C + GLONASS L1 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_cnav_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } break; case 29: // GPS L1 C/A + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } } break; case 30: // Galileo E1B + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, galileo_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->galileo_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } break; case 31: // GPS L2C + GLONASS L2 C/A if ((glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_cnav_ephemeris_iter->second, glonass_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map); } if (!b_rinex_header_updated and (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->glonass_gnav_utc_model, d_user_pvt_solver->glonass_gnav_almanac); b_rinex_header_updated = true; // do not write header anymore } break; case 32: // L1+E1+L5+E5a if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_ephemeris_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, gps_cnav_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and ((d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0) or (d_user_pvt_solver->gps_utc_model.d_A0 != 0)) and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { if (d_user_pvt_solver->gps_cnav_utc_model.d_A0 != 0) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_cnav_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_cnav_utc_model, d_user_pvt_solver->gps_cnav_iono, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); } else { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); } b_rinex_header_updated = true; // do not write header anymore } } break; case 33: // L1+E1+E5a if ((gps_ephemeris_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (galileo_ephemeris_iter != d_user_pvt_solver->galileo_ephemeris_map.cend())) { rp->log_rinex_obs(rp->obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, d_rx_time, gnss_observables_map); if (!b_rinex_header_updated and (d_user_pvt_solver->gps_utc_model.d_A0 != 0) and (d_user_pvt_solver->galileo_utc_model.A0_6 != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->gps_utc_model); rp->update_nav_header(rp->navMixFile, d_user_pvt_solver->gps_iono, d_user_pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_user_pvt_solver->galileo_iono, d_user_pvt_solver->galileo_utc_model); b_rinex_header_updated = true; // do not write header anymore } } break; case 500: // BDS B1I only if (beidou_dnav_ephemeris_iter != d_user_pvt_solver->beidou_dnav_ephemeris_map.cend()) { rp->log_rinex_obs(rp->obsFile, beidou_dnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, "B1"); } if (!b_rinex_header_updated and (d_user_pvt_solver->beidou_dnav_utc_model.d_A0_UTC != 0)) { rp->update_obs_header(rp->obsFile, d_user_pvt_solver->beidou_dnav_utc_model); rp->update_nav_header(rp->navFile, d_user_pvt_solver->beidou_dnav_utc_model, d_user_pvt_solver->beidou_dnav_iono); b_rinex_header_updated = true; } break; default: break; } } } } // ####################### RTCM MESSAGES ################# try { if (b_rtcm_writing_started and b_rtcm_enabled) { switch (type_of_rx) { case 1: // GPS L1 C/A if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 4: case 5: case 6: if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 7: // GPS L1 C/A + GPS L2C if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); if ((gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, gps_cnav_eph_iter->second, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 8: // L1+L5 if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); if ((gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, gps_cnav_eph_iter->second, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 9: // GPS L1 C/A + Galileo E1B if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); int gps_channel = 0; int gal_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (gal_channel == 0) { if (system == "E") { gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 13: // L5+E5a if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output and d_rtcm_MSM_rate_ms != 0) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); int gal_channel = 0; int gps_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend()) { gps_channel = 1; } } } if (gal_channel == 0) { if (system == "E") { gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend() and (d_rtcm_MT1097_rate_ms != 0)) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend() and (d_rtcm_MT1077_rate_ms != 0)) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, gps_cnav_eph_iter->second, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 14: case 15: if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 23: case 24: case 25: if (flag_write_RTCM_1020_output == true) { for (auto glonass_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_ephemeris_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (flag_write_RTCM_MSM_output == true) { auto glo_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); if (glo_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glo_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 26: // GPS L1 C/A + GLONASS L1 C/A if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_1020_output == true) { for (auto glonass_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_ephemeris_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_ephemeris_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); int gps_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 27: // GLONASS L1 C/A + Galileo E1B if (flag_write_RTCM_1020_output == true) { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); int gal_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { // This is a channel with valid GPS signal gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 29: // GPS L1 C/A + GLONASS L2 C/A if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_1020_output == true) { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); int gps_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 30: // GLONASS L2 C/A + Galileo E1B if (flag_write_RTCM_1020_output == true) { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); int gal_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { // This is a channel with valid GPS signal gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; case 32: // L1+E1+L5+E5a if (flag_write_RTCM_1019_output == true) { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (flag_write_RTCM_1045_output == true) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (flag_write_RTCM_MSM_output == true) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); int gal_channel = 0; int gps_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { // This is a channel with valid GPS signal gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } if (gps_channel == 0) { if (system == "G") { gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } break; default: break; } } if (!b_rtcm_writing_started and b_rtcm_enabled) // the first time { switch (type_of_rx) { case 1: // GPS L1 C/A if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) // allows deactivating messages by setting rate = 0 { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 4: case 5: case 6: if (d_rtcm_MT1045_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 7: // GPS L1 C/A + GPS L2C if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); if ((gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, gps_cnav_eph_iter->second, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 8: // L1+L5 if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto gps_cnav_eph_iter = d_user_pvt_solver->gps_cnav_ephemeris_map.cbegin(); if ((gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) and (gps_cnav_eph_iter != d_user_pvt_solver->gps_cnav_ephemeris_map.cend())) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, gps_cnav_eph_iter->second, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 9: // GPS L1 C/A + Galileo E1B if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MT1045_rate_ms != 0) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); int gps_channel = 0; int gal_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (gal_channel == 0) { if (system == "E") { gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 13: // L5+E5a if (d_rtcm_MT1045_rate_ms != 0) { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); int gal_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend() and (d_rtcm_MT1097_rate_ms != 0)) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 14: case 15: if (d_rtcm_MT1045_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 23: case 24: case 25: if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (d_rtcm_MSM_rate_ms != 0) { auto glo_gnav_ephemeris_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); if (glo_gnav_ephemeris_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glo_gnav_ephemeris_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 26: // GPS L1 C/A + GLONASS L1 C/A if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (d_rtcm_MSM_rate_ms != 0) { std::map::const_iterator gnss_observables_iter; auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); int gps_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 27: // GLONASS L1 C/A + Galileo E1B if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (d_rtcm_MT1045_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { int gal_channel = 0; int glo_channel = 0; std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { // This is a channel with valid GPS signal gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 29: // GPS L1 C/A + GLONASS L2 C/A if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (d_rtcm_MSM_rate_ms != 0) { auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); std::map::const_iterator gnss_observables_iter; int gps_channel = 0; int glo_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 30: // GLONASS L2 C/A + Galileo E1B if (d_rtcm_MT1020_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend(); glonass_gnav_eph_iter++) { d_rtcm_printer->Print_Rtcm_MT1020(glonass_gnav_eph_iter->second, d_user_pvt_solver->glonass_gnav_utc_model); } } if (d_rtcm_MT1045_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { int gal_channel = 0; int glo_channel = 0; std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.cbegin(); for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gal_channel == 0) { if (system == "E") { // This is a channel with valid GPS signal gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } if (glo_channel == 0) { if (system == "R") { glonass_gnav_eph_iter = d_user_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { glo_channel = 1; } } } } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (glonass_gnav_eph_iter != d_user_pvt_solver->glonass_gnav_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, {}, glonass_gnav_eph_iter->second, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; case 32: // L1+E1+L5+E5a if (d_rtcm_MT1019_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gps_eph_iter : d_user_pvt_solver->gps_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1019(gps_eph_iter.second); } } if (d_rtcm_MT1045_rate_ms != 0) // allows deactivating messages by setting rate = 0 { for (const auto& gal_eph_iter : d_user_pvt_solver->galileo_ephemeris_map) { d_rtcm_printer->Print_Rtcm_MT1045(gal_eph_iter.second); } } if (d_rtcm_MSM_rate_ms != 0) { std::map::const_iterator gnss_observables_iter; auto gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.cbegin(); auto gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.cbegin(); int gps_channel = 0; int gal_channel = 0; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); gnss_observables_iter++) { std::string system(&gnss_observables_iter->second.System, 1); if (gps_channel == 0) { if (system == "G") { // This is a channel with valid GPS signal gps_eph_iter = d_user_pvt_solver->gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { gps_channel = 1; } } } if (gal_channel == 0) { if (system == "E") { gal_eph_iter = d_user_pvt_solver->galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { gal_channel = 1; } } } } if (gps_eph_iter != d_user_pvt_solver->gps_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, gps_eph_iter->second, {}, {}, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } if (gal_eph_iter != d_user_pvt_solver->galileo_ephemeris_map.cend()) { d_rtcm_printer->Print_Rtcm_MSM(7, {}, {}, gal_eph_iter->second, {}, d_rx_time, gnss_observables_map, 0, 0, 0, false, false); } } b_rtcm_writing_started = true; break; default: break; } } } catch (const boost::exception& ex) { std::cout << "RTCM boost exception: " << boost::diagnostic_information(ex) << std::endl; LOG(ERROR) << "RTCM boost exception: " << boost::diagnostic_information(ex); } catch (const std::exception& ex) { std::cout << "RTCM std exception: " << ex.what() << std::endl; LOG(ERROR) << "RTCM std exception: " << ex.what(); } } } } // DEBUG MESSAGE: Display position in console output if (d_user_pvt_solver->is_valid_position() and flag_display_pvt) { boost::posix_time::ptime time_solution; std::string UTC_solution_str; if (d_show_local_time_zone) { time_solution = d_user_pvt_solver->get_position_UTC_time() + d_utc_diff_time; UTC_solution_str = d_local_time_str; } else { time_solution = d_user_pvt_solver->get_position_UTC_time(); UTC_solution_str = " UTC"; } std::streamsize ss = std::cout.precision(); // save current precision std::cout.setf(std::ios::fixed, std::ios::floatfield); auto facet = new boost::posix_time::time_facet("%Y-%b-%d %H:%M:%S.%f %z"); std::cout.imbue(std::locale(std::cout.getloc(), facet)); std::cout << TEXT_BOLD_GREEN << "Position at " << time_solution << UTC_solution_str << " using " << d_user_pvt_solver->get_num_valid_observations() << std::fixed << std::setprecision(9) << " observations is Lat = " << d_user_pvt_solver->get_latitude() << " [deg], Long = " << d_user_pvt_solver->get_longitude() << std::fixed << std::setprecision(3) << " [deg], Height = " << d_user_pvt_solver->get_height() << " [m]" << TEXT_RESET << std::endl; std::cout << std::setprecision(ss); DLOG(INFO) << "RX clock offset: " << d_user_pvt_solver->get_time_offset_s() << "[s]"; // boost::posix_time::ptime p_time; // gtime_t rtklib_utc_time = gpst2time(adjgpsweek(d_user_pvt_solver->gps_ephemeris_map.cbegin()->second.i_GPS_week), d_rx_time); // p_time = boost::posix_time::from_time_t(rtklib_utc_time.time); // p_time += boost::posix_time::microseconds(round(rtklib_utc_time.sec * 1e6)); // std::cout << TEXT_MAGENTA << "Observable RX time (GPST) " << boost::posix_time::to_simple_string(p_time) << TEXT_RESET << std::endl; DLOG(INFO) << "Position at " << boost::posix_time::to_simple_string(d_user_pvt_solver->get_position_UTC_time()) << " UTC using " << d_user_pvt_solver->get_num_valid_observations() << " observations is Lat = " << d_user_pvt_solver->get_latitude() << " [deg], Long = " << d_user_pvt_solver->get_longitude() << " [deg], Height = " << d_user_pvt_solver->get_height() << " [m]"; /* std::cout << "Dilution of Precision at " << boost::posix_time::to_simple_string(d_user_pvt_solver->get_position_UTC_time()) << " UTC using "<< d_user_pvt_solver->get_num_valid_observations() <<" observations is HDOP = " << d_user_pvt_solver->get_hdop() << " VDOP = " << d_user_pvt_solver->get_vdop() << " GDOP = " << d_user_pvt_solver->get_gdop() << std::endl; */ } // PVT MONITOR if (d_user_pvt_solver->is_valid_position()) { std::shared_ptr monitor_pvt = std::make_shared(d_user_pvt_solver->get_monitor_pvt()); // publish new position to the gnss_flowgraph channel status monitor if (current_RX_time_ms % d_report_rate_ms == 0) { this->message_port_pub(pmt::mp("status"), pmt::make_any(monitor_pvt)); } if (flag_monitor_pvt_enabled) { udp_sink_ptr->write_monitor_pvt(monitor_pvt); } } } } return noutput_items; } src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h000066400000000000000000000202121352176506000226600ustar00rootroot00000000000000/*! * \file rtklib_pvt_gs.h * \brief Interface of a Position Velocity and Time computation block * \author Javier Arribas, 2017. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RTKLIB_PVT_GS_H #define GNSS_SDR_RTKLIB_PVT_GS_H #include "gnss_synchro.h" #include "rtklib.h" #include #include #include // for boost::shared_ptr #include // for sync_block #include // for gr_vector_const_void_star #include // for pmt_t #include // for system_clock #include // for int32_t #include // for time_t #include // for map #include // for shared_ptr, unique_ptr #include // for string #include // for key_t #include // for pair #include // for vector class Beidou_Dnav_Almanac; class Beidou_Dnav_Ephemeris; class Galileo_Almanac; class Galileo_Ephemeris; class GeoJSON_Printer; class Gps_Almanac; class Gps_Ephemeris; class Gpx_Printer; class Kml_Printer; class Monitor_Pvt_Udp_Sink; class Nmea_Printer; class Pvt_Conf; class Rinex_Printer; class Rtcm_Printer; class Rtklib_Solver; class rtklib_pvt_gs; using rtklib_pvt_gs_sptr = boost::shared_ptr; rtklib_pvt_gs_sptr rtklib_make_pvt_gs(uint32_t nchannels, const Pvt_Conf& conf_, const rtk_t& rtk); /*! * \brief This class implements a block that computes the PVT solution using the RTKLIB integrated library */ class rtklib_pvt_gs : public gr::sync_block { public: ~rtklib_pvt_gs(); //!< Default destructor /*! * \brief Get latest set of GPS ephemeris from PVT block */ std::map get_gps_ephemeris_map() const; /*! * \brief Get latest set of GPS almanac from PVT block */ std::map get_gps_almanac_map() const; /*! * \brief Get latest set of Galileo ephemeris from PVT block */ std::map get_galileo_ephemeris_map() const; /*! * \brief Get latest set of Galileo almanac from PVT block */ std::map get_galileo_almanac_map() const; /*! * \brief Get latest set of BeiDou DNAV ephemeris from PVT block */ std::map get_beidou_dnav_ephemeris_map() const; /*! * \brief Get latest set of BeiDou DNAV almanac from PVT block */ std::map get_beidou_dnav_almanac_map() const; /*! * \brief Clear all ephemeris information and the almanacs for GPS and Galileo */ void clear_ephemeris(); /*! * \brief Get the latest Position WGS84 [deg], Ground Velocity, Course over Ground, and UTC Time, if available */ bool get_latest_PVT(double* longitude_deg, double* latitude_deg, double* height_m, double* ground_speed_kmh, double* course_over_ground_deg, time_t* UTC_time) const; int work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); //!< PVT Signal Processing private: friend rtklib_pvt_gs_sptr rtklib_make_pvt_gs(uint32_t nchannels, const Pvt_Conf& conf_, const rtk_t& rtk); rtklib_pvt_gs(uint32_t nchannels, const Pvt_Conf& conf_, const rtk_t& rtk); void msg_handler_telemetry(const pmt::pmt_t& msg); enum StringValue { evGPS_1C, evGPS_2S, evGPS_L5, evSBAS_1C, evGAL_1B, evGAL_5X, evGLO_1G, evGLO_2G, evBDS_B1, evBDS_B2, evBDS_B3 }; std::map mapStringValues_; void apply_rx_clock_offset(std::map& observables_map, double rx_clock_offset_s); std::map interpolate_observables(std::map& observables_map_t0, std::map& observables_map_t1, double rx_time_s); bool d_dump; bool d_dump_mat; bool b_rinex_output_enabled; bool b_rinex_header_written; bool b_rinex_header_updated; double d_rinex_version; int32_t d_rinexobs_rate_ms; bool b_rtcm_writing_started; bool b_rtcm_enabled; int32_t d_rtcm_MT1045_rate_ms; // Galileo Broadcast Ephemeris int32_t d_rtcm_MT1019_rate_ms; // GPS Broadcast Ephemeris (orbits) int32_t d_rtcm_MT1020_rate_ms; // GLONASS Broadcast Ephemeris (orbits) int32_t d_rtcm_MT1077_rate_ms; // The type 7 Multiple Signal Message format for the USA’s GPS system, popular int32_t d_rtcm_MT1087_rate_ms; // GLONASS MSM7. The type 7 Multiple Signal Message format for the Russian GLONASS system int32_t d_rtcm_MT1097_rate_ms; // Galileo MSM7. The type 7 Multiple Signal Message format for Europe’s Galileo system int32_t d_rtcm_MSM_rate_ms; int32_t d_kml_rate_ms; int32_t d_gpx_rate_ms; int32_t d_geojson_rate_ms; int32_t d_nmea_rate_ms; int32_t d_last_status_print_seg; // for status printer uint32_t d_nchannels; std::string d_dump_filename; int32_t d_output_rate_ms; int32_t d_display_rate_ms; int32_t d_report_rate_ms; std::shared_ptr rp; std::shared_ptr d_kml_dump; std::shared_ptr d_gpx_dump; std::shared_ptr d_nmea_printer; std::shared_ptr d_geojson_printer; std::shared_ptr d_rtcm_printer; double d_rx_time; bool d_geojson_output_enabled; bool d_gpx_output_enabled; bool d_kml_output_enabled; bool d_nmea_output_file_enabled; std::shared_ptr d_internal_pvt_solver; std::shared_ptr d_user_pvt_solver; int32_t max_obs_block_rx_clock_offset_ms; bool d_waiting_obs_block_rx_clock_offset_correction_msg; std::map gnss_observables_map; std::map gnss_observables_map_t0; std::map gnss_observables_map_t1; uint32_t type_of_rx; bool first_fix; key_t sysv_msg_key; int sysv_msqid; typedef struct { long mtype; // NOLINT(google-runtime-int) required by SysV queue messaging double ttff; } ttff_msgbuf; bool send_sys_v_ttff_msg(ttff_msgbuf ttff); std::chrono::time_point start, end; bool save_gnss_synchro_map_xml(const std::string& file_name); // debug helper function bool load_gnss_synchro_map_xml(const std::string& file_name); // debug helper function bool d_xml_storage; std::string xml_base_path; inline std::time_t convert_to_time_t(const boost::posix_time::ptime pt) const { return (pt - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds(); } bool flag_monitor_pvt_enabled; std::unique_ptr udp_sink_ptr; std::vector split_string(const std::string& s, char delim) const; bool d_show_local_time_zone; std::string d_local_time_str; boost::posix_time::time_duration d_utc_diff_time; }; #endif src/algorithms/PVT/libs/000077500000000000000000000000001352176506000154255ustar00rootroot00000000000000src/algorithms/PVT/libs/CMakeLists.txt000066400000000000000000000074331352176506000201740ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/docs/protobuf/monitor_pvt.proto) set(PVT_LIB_SOURCES pvt_solution.cc ls_pvt.cc hybrid_ls_pvt.cc kml_printer.cc gpx_printer.cc rinex_printer.cc nmea_printer.cc rtcm_printer.cc rtcm.cc geojson_printer.cc rtklib_solver.cc pvt_conf.cc monitor_pvt_udp_sink.cc ${PROTO_SRCS} ) set(PVT_LIB_HEADERS pvt_solution.h ls_pvt.h hybrid_ls_pvt.h kml_printer.h gpx_printer.h rinex_printer.h nmea_printer.h rtcm_printer.h rtcm.h geojson_printer.h rtklib_solver.h pvt_conf.h monitor_pvt_udp_sink.h monitor_pvt.h serdes_monitor_pvt.h ${PROTO_HDRS} ) list(SORT PVT_LIB_HEADERS) list(SORT PVT_LIB_SOURCES) source_group(Headers FILES ${PVT_LIB_HEADERS}) add_library(pvt_libs ${PVT_LIB_SOURCES} ${PVT_LIB_HEADERS}) if(${FILESYSTEM_FOUND}) target_compile_definitions(pvt_libs PRIVATE -DHAS_STD_FILESYSTEM=1) if(${find_experimental}) target_compile_definitions(pvt_libs PRIVATE -DHAS_STD_FILESYSTEM_EXPERIMENTAL=1) endif() target_link_libraries(pvt_libs PRIVATE std::filesystem) else() target_link_libraries(pvt_libs PRIVATE Boost::filesystem Boost::system) endif() target_link_libraries(pvt_libs PUBLIC Armadillo::armadillo Boost::date_time protobuf::libprotobuf algorithms_libs_rtklib core_system_parameters PRIVATE algorithms_libs Gflags::gflags Glog::glog Matio::matio ) get_filename_component(PROTO_INCLUDE_HEADERS ${PROTO_HDRS} DIRECTORY) target_include_directories(pvt_libs PUBLIC ${CMAKE_SOURCE_DIR}/src/core/receiver ${PROTO_INCLUDE_HEADERS} ) target_compile_definitions(pvt_libs PRIVATE -DGNSS_SDR_VERSION="${VERSION}") if(CMAKE_BUILD_TYPE MATCHES Rel) target_compile_definitions(pvt_libs PUBLIC -DARMA_NO_BOUND_CHECKING=1 ) endif() if(Boost_VERSION_STRING VERSION_GREATER 1.65.99) target_compile_definitions(pvt_libs PUBLIC -DBOOST_GREATER_1_65 ) endif() # Fix for Boost Asio < 1.70 if(OS_IS_MACOSX) if((CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND (Boost_VERSION_STRING VERSION_LESS 1.70.0)) if(${has_string_view}) target_compile_definitions(pvt_libs PUBLIC -DBOOST_ASIO_HAS_STD_STRING_VIEW=1 ) else() target_compile_definitions(pvt_libs PUBLIC -DBOOST_ASIO_HAS_STD_STRING_VIEW=0 ) endif() endif() endif() if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(pvt_libs PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET pvt_libs APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ $ $ $ ) src/algorithms/PVT/libs/geojson_printer.cc000066400000000000000000000175541352176506000211570ustar00rootroot00000000000000/*! * \file geojson_printer.cc * \brief Implementation of a class that prints PVT solutions in GeoJSON format * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "geojson_printer.h" #include "pvt_solution.h" #include #include #include // for tm #include // for exception #include // for operator<< #include // for cout, cerr #include // for stringstream #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif GeoJSON_Printer::GeoJSON_Printer(const std::string& base_path) { first_pos = true; geojson_base_path = base_path; fs::path full_path(fs::current_path()); const fs::path p(geojson_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(geojson_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; geojson_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { geojson_base_path = p.string(); } if (geojson_base_path != ".") { std::cout << "GeoJSON files will be stored at " << geojson_base_path << std::endl; } geojson_base_path = geojson_base_path + fs::path::preferred_separator; } GeoJSON_Printer::~GeoJSON_Printer() { try { GeoJSON_Printer::close_file(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } } bool GeoJSON_Printer::set_headers(const std::string& filename, bool time_tag_name) { boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm timeinfo = boost::posix_time::to_tm(pt); if (time_tag_name) { std::stringstream strm0; const int year = timeinfo.tm_year - 100; strm0 << year; const int month = timeinfo.tm_mon + 1; if (month < 10) { strm0 << "0"; } strm0 << month; const int day = timeinfo.tm_mday; if (day < 10) { strm0 << "0"; } strm0 << day << "_"; const int hour = timeinfo.tm_hour; if (hour < 10) { strm0 << "0"; } strm0 << hour; const int min = timeinfo.tm_min; if (min < 10) { strm0 << "0"; } strm0 << min; const int sec = timeinfo.tm_sec; if (sec < 10) { strm0 << "0"; } strm0 << sec; filename_ = filename + "_" + strm0.str() + ".geojson"; } else { filename_ = filename + ".geojson"; } filename_ = geojson_base_path + filename_; geojson_file.open(filename_.c_str()); first_pos = true; if (geojson_file.is_open()) { DLOG(INFO) << "GeoJSON printer writing on " << filename.c_str(); // Set iostream numeric format and precision geojson_file.setf(geojson_file.std::ofstream::fixed, geojson_file.std::ofstream::floatfield); geojson_file << std::setprecision(14); // Writing the header geojson_file << "{" << std::endl; geojson_file << R"( "type": "Feature",)" << std::endl; geojson_file << " \"properties\": {" << std::endl; geojson_file << R"( "name": "Locations generated by GNSS-SDR" )" << std::endl; geojson_file << " }," << std::endl; geojson_file << " \"geometry\": {" << std::endl; geojson_file << R"( "type": "MultiPoint",)" << std::endl; geojson_file << " \"coordinates\": [" << std::endl; return true; } std::cout << "File " << filename_ << " cannot be saved. Wrong permissions?" << std::endl; return false; } bool GeoJSON_Printer::print_position(const std::shared_ptr& position, bool print_average_values) { double latitude; double longitude; double height; const std::shared_ptr& position_ = position; if (print_average_values == false) { latitude = position_->get_latitude(); longitude = position_->get_longitude(); height = position_->get_height(); } else { latitude = position_->get_avg_latitude(); longitude = position_->get_avg_longitude(); height = position_->get_avg_height(); } if (geojson_file.is_open()) { if (first_pos == true) { geojson_file << " [" << longitude << ", " << latitude << ", " << height << "]"; first_pos = false; } else { geojson_file << "," << std::endl; geojson_file << " [" << longitude << ", " << latitude << ", " << height << "]"; } return true; } return false; } bool GeoJSON_Printer::close_file() { if (geojson_file.is_open()) { geojson_file << std::endl; geojson_file << " ]" << std::endl; geojson_file << " }" << std::endl; geojson_file << "}" << std::endl; geojson_file.close(); // if nothing is written, erase the file if (first_pos == true) { errorlib::error_code ec; if (!fs::remove(fs::path(filename_), ec)) { LOG(INFO) << "Error deleting temporary file"; } } return true; } return false; } src/algorithms/PVT/libs/geojson_printer.h000066400000000000000000000035441352176506000210130ustar00rootroot00000000000000/*! * \file geojson_printer.h * \brief Interface of a class that prints PVT solutions in GeoJSON format * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GEOJSON_PRINTER_H_ #define GNSS_SDR_GEOJSON_PRINTER_H_ #include #include #include class Pvt_Solution; /*! * \brief Prints PVT solutions in GeoJSON format file * * See http://geojson.org/geojson-spec.html */ class GeoJSON_Printer { public: GeoJSON_Printer(const std::string& base_path = "."); ~GeoJSON_Printer(); bool set_headers(const std::string& filename, bool time_tag_name = true); bool print_position(const std::shared_ptr& position, bool print_average_values); bool close_file(); private: std::ofstream geojson_file; bool first_pos; std::string filename_; std::string geojson_base_path; }; #endif src/algorithms/PVT/libs/gpx_printer.cc000066400000000000000000000222471352176506000203040ustar00rootroot00000000000000/*! * \file gpx_printer.cc * \brief Implementation of a class that prints PVT information to a gpx file * \author Álvaro Cebrián Juan, 2018. acebrianjuan(at)gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gpx_printer.h" #include "rtklib_solver.h" #include #include #include // for tm #include // for exception #include // for operator<< #include // for cout, cerr #include // for stringstream #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Gpx_Printer::Gpx_Printer(const std::string& base_path) { positions_printed = false; indent = " "; gpx_base_path = base_path; fs::path full_path(fs::current_path()); const fs::path p(gpx_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(gpx_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; gpx_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { gpx_base_path = p.string(); } if (gpx_base_path != ".") { std::cout << "GPX files will be stored at " << gpx_base_path << std::endl; } gpx_base_path = gpx_base_path + fs::path::preferred_separator; } bool Gpx_Printer::set_headers(const std::string& filename, bool time_tag_name) { boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm timeinfo = boost::posix_time::to_tm(pt); if (time_tag_name) { std::stringstream strm0; const int year = timeinfo.tm_year - 100; strm0 << year; const int month = timeinfo.tm_mon + 1; if (month < 10) { strm0 << "0"; } strm0 << month; const int day = timeinfo.tm_mday; if (day < 10) { strm0 << "0"; } strm0 << day << "_"; const int hour = timeinfo.tm_hour; if (hour < 10) { strm0 << "0"; } strm0 << hour; const int min = timeinfo.tm_min; if (min < 10) { strm0 << "0"; } strm0 << min; const int sec = timeinfo.tm_sec; if (sec < 10) { strm0 << "0"; } strm0 << sec; gpx_filename = filename + "_" + strm0.str() + ".gpx"; } else { gpx_filename = filename + ".gpx"; } gpx_filename = gpx_base_path + gpx_filename; gpx_file.open(gpx_filename.c_str()); if (gpx_file.is_open()) { DLOG(INFO) << "GPX printer writing on " << filename.c_str(); // Set iostream numeric format and precision gpx_file.setf(gpx_file.std::ofstream::fixed, gpx_file.std::ofstream::floatfield); gpx_file << std::setprecision(14); gpx_file << R"()" << std::endl << R"(" << std::endl << indent << "" << std::endl << indent << indent << "Position fixes computed by GNSS-SDR v" << GNSS_SDR_VERSION << "" << std::endl << indent << indent << "GNSS-SDR position log generated at " << pt << " (local time)" << std::endl << indent << indent << "" << std::endl; return true; } std::cout << "File " << gpx_filename << " cannot be saved. Wrong permissions?" << std::endl; return false; } bool Gpx_Printer::print_position(const std::shared_ptr& position, bool print_average_values) { double latitude; double longitude; double height; positions_printed = true; const std::shared_ptr& position_ = position; double speed_over_ground = position_->get_speed_over_ground(); // expressed in m/s double course_over_ground = position_->get_course_over_ground(); // expressed in deg double hdop = position_->get_hdop(); double vdop = position_->get_vdop(); double pdop = position_->get_pdop(); std::string utc_time = to_iso_extended_string(position_->get_position_UTC_time()); if (utc_time.length() < 23) { utc_time += "."; } utc_time.resize(23, '0'); // time up to ms utc_time.append("Z"); // UTC time zone if (print_average_values == false) { latitude = position_->get_latitude(); longitude = position_->get_longitude(); height = position_->get_height(); } else { latitude = position_->get_avg_latitude(); longitude = position_->get_avg_longitude(); height = position_->get_avg_height(); } if (gpx_file.is_open()) { gpx_file << indent << indent << indent << "" << height << "" << "" << "" << hdop << "" << vdop << "" << pdop << "" << "" << "" << speed_over_ground << "" << "" << course_over_ground << "" << "" << std::endl; return true; } return false; } bool Gpx_Printer::close_file() { if (gpx_file.is_open()) { gpx_file << indent << indent << "" << std::endl << indent << "" << std::endl << ""; gpx_file.close(); return true; } return false; } Gpx_Printer::~Gpx_Printer() { try { close_file(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } if (!positions_printed) { errorlib::error_code ec; if (!fs::remove(fs::path(gpx_filename), ec)) { LOG(INFO) << "Error deleting temporary GPX file"; } } } src/algorithms/PVT/libs/gpx_printer.h000066400000000000000000000035441352176506000201450ustar00rootroot00000000000000/*! * \file gpx_printer.h * \brief Interface of a class that prints PVT information to a gpx file * \author Álvaro Cebrián Juan, 2018. acebrianjuan(at)gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPX_PRINTER_H_ #define GNSS_SDR_GPX_PRINTER_H_ #include #include #include class Rtklib_Solver; /*! * \brief Prints PVT information to GPX format file * * See http://www.topografix.com/gpx.asp */ class Gpx_Printer { public: Gpx_Printer(const std::string& base_path = "."); ~Gpx_Printer(); bool set_headers(const std::string& filename, bool time_tag_name = true); bool print_position(const std::shared_ptr& position, bool print_average_values); bool close_file(); private: std::ofstream gpx_file; bool positions_printed; std::string gpx_filename; std::string indent; std::string gpx_base_path; }; #endif src/algorithms/PVT/libs/hybrid_ls_pvt.cc000066400000000000000000000554701352176506000206170ustar00rootroot00000000000000/*! * \file hybrid_ls_pvt.cc * \brief Implementation of a Least Squares Position, Velocity, and Time * (PVT) solver, based on K.Borre's Matlab receiver. * \author Javier Arribas, 2011. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "hybrid_ls_pvt.h" #include "GPS_L1_CA.h" #include "GPS_L2C.h" #include "Galileo_E1.h" #include #include #include Hybrid_Ls_Pvt::Hybrid_Ls_Pvt(int nchannels, std::string dump_filename, bool flag_dump_to_file) : Ls_Pvt() { // init empty ephemeris for all the available GNSS channels d_nchannels = nchannels; d_dump_filename = std::move(dump_filename); d_flag_dump_enabled = flag_dump_to_file; d_galileo_current_time = 0; this->set_averaging_flag(false); // ############# ENABLE DATA FILE LOG ################# if (d_flag_dump_enabled == true) { if (d_dump_file.is_open() == false) { try { d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); d_dump_file.open(d_dump_filename.c_str(), std::ios::out | std::ios::binary); LOG(INFO) << "PVT lib dump enabled Log file: " << d_dump_filename.c_str(); } catch (const std::ifstream::failure& e) { LOG(WARNING) << "Exception opening PVT lib dump file " << e.what(); } } } } Hybrid_Ls_Pvt::~Hybrid_Ls_Pvt() { if (d_dump_file.is_open() == true) { try { d_dump_file.close(); } catch (const std::exception& ex) { LOG(WARNING) << "Exception in destructor closing the dump file " << ex.what(); } } } bool Hybrid_Ls_Pvt::get_PVT(std::map gnss_observables_map, double hybrid_current_time, bool flag_averaging) { std::map::iterator gnss_observables_iter; std::map::iterator galileo_ephemeris_iter; std::map::iterator gps_ephemeris_iter; std::map::iterator gps_cnav_ephemeris_iter; arma::vec W; // channels weight vector arma::vec obs; // pseudoranges observation vector arma::mat satpos; // satellite positions matrix int Galileo_week_number = 0; int GPS_week = 0; double utc = 0.0; double GST = 0.0; double secondsperweek = 604800.0; //double utc_tx_corrected = 0.0; //utc computed at tx_time_corrected, added for Galileo constellation (in GPS utc is directly computed at TX_time_corrected_s) double TX_time_corrected_s = 0.0; double SV_clock_bias_s = 0.0; this->set_averaging_flag(flag_averaging); // ******************************************************************************** // ****** PREPARE THE LEAST SQUARES DATA (SV POSITIONS MATRIX AND OBS VECTORS) **** // ******************************************************************************** int valid_obs = 0; //valid observations counter for (gnss_observables_iter = gnss_observables_map.begin(); gnss_observables_iter != gnss_observables_map.end(); gnss_observables_iter++) { switch (gnss_observables_iter->second.System) { case 'E': { // 1 Gal - find the ephemeris for the current GALILEO SV observation. The SV PRN ID is the map key galileo_ephemeris_iter = galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (galileo_ephemeris_iter != galileo_ephemeris_map.end()) { /*! * \todo Place here the satellite CN0 (power level, or weight factor) */ W.resize(valid_obs + 1, 1); W(valid_obs) = 1; // COMMON RX TIME PVT ALGORITHM double Rx_time = hybrid_current_time; double Tx_time = Rx_time - gnss_observables_iter->second.Pseudorange_m / GALILEO_C_M_S; // 2- compute the clock drift using the clock model (broadcast) for this SV SV_clock_bias_s = galileo_ephemeris_iter->second.sv_clock_drift(Tx_time); // 3- compute the current ECEF position for this SV using corrected TX time TX_time_corrected_s = Tx_time - SV_clock_bias_s; galileo_ephemeris_iter->second.satellitePosition(TX_time_corrected_s); //store satellite positions in a matrix satpos.resize(3, valid_obs + 1); satpos(0, valid_obs) = galileo_ephemeris_iter->second.d_satpos_X; satpos(1, valid_obs) = galileo_ephemeris_iter->second.d_satpos_Y; satpos(2, valid_obs) = galileo_ephemeris_iter->second.d_satpos_Z; // 4- fill the observations vector with the corrected observables obs.resize(valid_obs + 1, 1); obs(valid_obs) = gnss_observables_iter->second.Pseudorange_m + SV_clock_bias_s * GALILEO_C_M_S - this->get_time_offset_s() * GALILEO_C_M_S; Galileo_week_number = galileo_ephemeris_iter->second.WN_5; //for GST GST = galileo_ephemeris_iter->second.Galileo_System_Time(Galileo_week_number, hybrid_current_time); // SV ECEF DEBUG OUTPUT DLOG(INFO) << "ECEF satellite SV ID=" << galileo_ephemeris_iter->second.i_satellite_PRN << " X=" << galileo_ephemeris_iter->second.d_satpos_X << " [m] Y=" << galileo_ephemeris_iter->second.d_satpos_Y << " [m] Z=" << galileo_ephemeris_iter->second.d_satpos_Z << " [m] PR_obs=" << obs(valid_obs) << " [m]"; valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } break; } case 'G': { // 1 GPS - find the ephemeris for the current GPS SV observation. The SV PRN ID is the map key std::string sig_(gnss_observables_iter->second.Signal); if (sig_ == "1C") { gps_ephemeris_iter = gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_ephemeris_iter != gps_ephemeris_map.end()) { /*! * \todo Place here the satellite CN0 (power level, or weight factor) */ W.resize(valid_obs + 1, 1); W(valid_obs) = 1; // COMMON RX TIME PVT ALGORITHM MODIFICATION (Like RINEX files) // first estimate of transmit time double Rx_time = hybrid_current_time; double Tx_time = Rx_time - gnss_observables_iter->second.Pseudorange_m / GPS_C_M_S; // 2- compute the clock drift using the clock model (broadcast) for this SV, not including relativistic effect SV_clock_bias_s = gps_ephemeris_iter->second.sv_clock_drift(Tx_time); //- gps_ephemeris_iter->second.d_TGD; // 3- compute the current ECEF position for this SV using corrected TX time and obtain clock bias including relativistic effect TX_time_corrected_s = Tx_time - SV_clock_bias_s; double dtr = gps_ephemeris_iter->second.satellitePosition(TX_time_corrected_s); //store satellite positions in a matrix satpos.resize(3, valid_obs + 1); satpos(0, valid_obs) = gps_ephemeris_iter->second.d_satpos_X; satpos(1, valid_obs) = gps_ephemeris_iter->second.d_satpos_Y; satpos(2, valid_obs) = gps_ephemeris_iter->second.d_satpos_Z; // 4- fill the observations vector with the corrected pseudoranges // compute code bias: TGD for single frequency // See IS-GPS-200E section 20.3.3.3.3.2 double sqrt_Gamma = GPS_L1_FREQ_HZ / GPS_L2_FREQ_HZ; double Gamma = sqrt_Gamma * sqrt_Gamma; double P1_P2 = (1.0 - Gamma) * (gps_ephemeris_iter->second.d_TGD * GPS_C_M_S); double Code_bias_m = P1_P2 / (1.0 - Gamma); obs.resize(valid_obs + 1, 1); obs(valid_obs) = gnss_observables_iter->second.Pseudorange_m + dtr * GPS_C_M_S - Code_bias_m - this->get_time_offset_s() * GPS_C_M_S; // SV ECEF DEBUG OUTPUT LOG(INFO) << "(new)ECEF GPS L1 CA satellite SV ID=" << gps_ephemeris_iter->second.i_satellite_PRN << " TX Time corrected=" << TX_time_corrected_s << " X=" << gps_ephemeris_iter->second.d_satpos_X << " [m] Y=" << gps_ephemeris_iter->second.d_satpos_Y << " [m] Z=" << gps_ephemeris_iter->second.d_satpos_Z << " [m] PR_obs=" << obs(valid_obs) << " [m]"; valid_obs++; // compute the UTC time for this SV (just to print the associated UTC timestamp) GPS_week = gps_ephemeris_iter->second.i_GPS_week; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->first; } } if (sig_ == "2S") { gps_cnav_ephemeris_iter = gps_cnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_cnav_ephemeris_iter != gps_cnav_ephemeris_map.end()) { /*! * \todo Place here the satellite CN0 (power level, or weight factor) */ W.resize(valid_obs + 1, 1); W(valid_obs) = 1; // COMMON RX TIME PVT ALGORITHM MODIFICATION (Like RINEX files) // first estimate of transmit time double Rx_time = hybrid_current_time; double Tx_time = Rx_time - gnss_observables_iter->second.Pseudorange_m / GPS_C_M_S; // 2- compute the clock drift using the clock model (broadcast) for this SV SV_clock_bias_s = gps_cnav_ephemeris_iter->second.sv_clock_drift(Tx_time); // 3- compute the current ECEF position for this SV using corrected TX time TX_time_corrected_s = Tx_time - SV_clock_bias_s; //std::cout<<"TX time["<second.i_satellite_PRN<<"]="<second.satellitePosition(TX_time_corrected_s); //store satellite positions in a matrix satpos.resize(3, valid_obs + 1); satpos(0, valid_obs) = gps_cnav_ephemeris_iter->second.d_satpos_X; satpos(1, valid_obs) = gps_cnav_ephemeris_iter->second.d_satpos_Y; satpos(2, valid_obs) = gps_cnav_ephemeris_iter->second.d_satpos_Z; // 4- fill the observations vector with the corrected observables obs.resize(valid_obs + 1, 1); obs(valid_obs) = gnss_observables_iter->second.Pseudorange_m + dtr * GPS_C_M_S + SV_clock_bias_s * GPS_C_M_S; GPS_week = gps_cnav_ephemeris_iter->second.i_GPS_week; GPS_week = GPS_week % 1024; //Necessary due to the increase of WN bits in CNAV message (10 in GPS NAV and 13 in CNAV) // SV ECEF DEBUG OUTPUT LOG(INFO) << "(new)ECEF GPS L2M satellite SV ID=" << gps_cnav_ephemeris_iter->second.i_satellite_PRN << " TX Time corrected=" << TX_time_corrected_s << " X=" << gps_cnav_ephemeris_iter->second.d_satpos_X << " [m] Y=" << gps_cnav_ephemeris_iter->second.d_satpos_Y << " [m] Z=" << gps_cnav_ephemeris_iter->second.d_satpos_Z << " [m] PR_obs=" << obs(valid_obs) << " [m]"; valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } break; } default: DLOG(INFO) << "Hybrid observables: Unknown GNSS"; break; } } // ******************************************************************************** // ****** SOLVE LEAST SQUARES****************************************************** // ******************************************************************************** this->set_num_valid_observations(valid_obs); LOG(INFO) << "HYBRID PVT: valid observations=" << valid_obs; if (valid_obs >= 4) { arma::vec rx_position_and_time; DLOG(INFO) << "satpos=" << satpos; DLOG(INFO) << "obs=" << obs; DLOG(INFO) << "W=" << W; try { // check if this is the initial position computation if (this->get_time_offset_s() == 0) { // execute Bancroft's algorithm to estimate initial receiver position and time DLOG(INFO) << " Executing Bancroft algorithm..."; rx_position_and_time = bancroftPos(satpos.t(), obs); this->set_rx_pos(rx_position_and_time.rows(0, 2)); // save ECEF position for the next iteration this->set_time_offset_s(rx_position_and_time(3) / GPS_C_M_S); // save time for the next iteration [meters]->[seconds] } // Execute WLS using previous position as the initialization point rx_position_and_time = leastSquarePos(satpos, obs, W); this->set_rx_pos(rx_position_and_time.rows(0, 2)); // save ECEF position for the next iteration this->set_time_offset_s(this->get_time_offset_s() + rx_position_and_time(3) / GPS_C_M_S); // accumulate the rx time error for the next iteration [meters]->[seconds] DLOG(INFO) << "Hybrid Position at TOW=" << hybrid_current_time << " in ECEF (X,Y,Z,t[meters]) = " << rx_position_and_time; DLOG(INFO) << "Accumulated rx clock error=" << this->get_time_offset_s() << " clock error for this iteration=" << rx_position_and_time(3) / GPS_C_M_S << " [s]"; // Compute GST and Gregorian time if (GST != 0.0) { utc = galileo_utc_model.GST_to_UTC_time(GST, Galileo_week_number); } else { utc = gps_utc_model.utc_time(TX_time_corrected_s, GPS_week) + secondsperweek * static_cast(GPS_week); } // get time string Gregorian calendar boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast(utc * 1000.0)); // NOLINT(google-runtime-int) // 22 August 1999 00:00 last Galileo start GST epoch (ICD sec 5.1.2) boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); this->set_position_UTC_time(p_time); cart2geo(static_cast(rx_position_and_time(0)), static_cast(rx_position_and_time(1)), static_cast(rx_position_and_time(2)), 4); DLOG(INFO) << "Hybrid Position at " << boost::posix_time::to_simple_string(p_time) << " is Lat = " << this->get_latitude() << " [deg], Long = " << this->get_longitude() << " [deg], Height= " << this->get_height() << " [m]" << " RX time offset= " << this->get_time_offset_s() << " [s]"; // ######## LOG FILE ######### if (d_flag_dump_enabled == true) { // MULTIPLEXED FILE RECORDING - Record results to file try { double tmp_double; // PVT GPS time tmp_double = hybrid_current_time; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // ECEF User Position East [m] tmp_double = rx_position_and_time(0); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // ECEF User Position North [m] tmp_double = rx_position_and_time(1); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // ECEF User Position Up [m] tmp_double = rx_position_and_time(2); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // User clock offset [s] tmp_double = rx_position_and_time(3); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Latitude [deg] tmp_double = this->get_latitude(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Longitude [deg] tmp_double = this->get_longitude(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Height [m] tmp_double = this->get_height(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); } catch (const std::ifstream::failure& e) { LOG(WARNING) << "Exception writing PVT LS dump file " << e.what(); } } // MOVING AVERAGE PVT this->perform_pos_averaging(); } catch (const std::exception& e) { this->set_time_offset_s(0.0); //reset rx time estimation LOG(WARNING) << "Problem with the solver, invalid solution!" << e.what(); this->set_valid_position(false); } } else { this->set_valid_position(false); } return this->is_valid_position(); } src/algorithms/PVT/libs/hybrid_ls_pvt.h000066400000000000000000000051001352176506000204420ustar00rootroot00000000000000/*! * \file hybrid_ls_pvt.h * \brief Interface of a Least Squares Position, Velocity, and Time (PVT) * solver, based on K.Borre's Matlab receiver. * \author Javier Arribas, 2011. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_HYBRID_LS_PVT_H_ #define GNSS_SDR_HYBRID_LS_PVT_H_ #include "galileo_almanac.h" #include "galileo_navigation_message.h" #include "gnss_synchro.h" #include "gps_cnav_navigation_message.h" #include "gps_navigation_message.h" #include "ls_pvt.h" #include "rtklib_rtkcmn.h" #include #include #include /*! * \brief This class implements a simple PVT Least Squares solution */ class Hybrid_Ls_Pvt : public Ls_Pvt { public: Hybrid_Ls_Pvt(int nchannels, std::string dump_filename, bool flag_dump_to_file); ~Hybrid_Ls_Pvt(); bool get_PVT(std::map gnss_observables_map, double hybrid_current_time, bool flag_averaging); std::map galileo_ephemeris_map; //!< Map storing new Galileo_Ephemeris std::map gps_ephemeris_map; //!< Map storing new GPS_Ephemeris std::map gps_cnav_ephemeris_map; Galileo_Utc_Model galileo_utc_model; Galileo_Iono galileo_iono; Galileo_Almanac galileo_almanac; Gps_Utc_Model gps_utc_model; Gps_Iono gps_iono; Gps_CNAV_Iono gps_cnav_iono; Gps_CNAV_Utc_Model gps_cnav_utc_model; private: bool d_flag_dump_enabled; std::string d_dump_filename; std::ofstream d_dump_file; int d_nchannels; // Number of available channels for positioning double d_galileo_current_time; }; #endif src/algorithms/PVT/libs/kml_printer.cc000066400000000000000000000422751352176506000202740ustar00rootroot00000000000000/*! * \file kml_printer.cc * \brief Implementation of a class that prints PVT information to a kml file * \author Javier Arribas, 2011. jarribas(at)cttc.es * Álvaro Cebrián Juan, 2018. acebrianjuan(at)gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "kml_printer.h" #include "rtklib_solver.h" #include #include #include // for mkstemp #include // for tm #include // for exception #include // for cout, cerr #include #include #include // for S_IXUSR | S_IRWXG | S_IRWXO #include // for mode_t #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Kml_Printer::Kml_Printer(const std::string& base_path) { positions_printed = false; indent = " "; kml_base_path = base_path; fs::path full_path(fs::current_path()); const fs::path p(kml_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(kml_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; kml_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { kml_base_path = p.string(); } if (kml_base_path != ".") { std::cout << "KML files will be stored at " << kml_base_path << std::endl; } kml_base_path = kml_base_path + fs::path::preferred_separator; char tmp_filename_[] = "/tmp/file.XXXXXX"; mode_t mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); int fd = mkstemp(tmp_filename_); if (fd == -1) { std::cerr << "Error in KML printer: failed to create temporary file" << std::endl; } else { close(fd); } umask(mask); fs::path tmp_filename = fs::path(tmp_filename_); tmp_file_str = tmp_filename.string(); point_id = 0; } bool Kml_Printer::set_headers(const std::string& filename, bool time_tag_name) { boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm timeinfo = boost::posix_time::to_tm(pt); if (time_tag_name) { std::stringstream strm0; const int year = timeinfo.tm_year - 100; strm0 << year; const int month = timeinfo.tm_mon + 1; if (month < 10) { strm0 << "0"; } strm0 << month; const int day = timeinfo.tm_mday; if (day < 10) { strm0 << "0"; } strm0 << day << "_"; const int hour = timeinfo.tm_hour; if (hour < 10) { strm0 << "0"; } strm0 << hour; const int min = timeinfo.tm_min; if (min < 10) { strm0 << "0"; } strm0 << min; const int sec = timeinfo.tm_sec; if (sec < 10) { strm0 << "0"; } strm0 << sec; kml_filename = filename + "_" + strm0.str() + ".kml"; } else { kml_filename = filename + ".kml"; } kml_filename = kml_base_path + kml_filename; kml_file.open(kml_filename.c_str()); tmp_file.open(tmp_file_str.c_str()); if (kml_file.is_open() && tmp_file.is_open()) { DLOG(INFO) << "KML printer writing on " << filename.c_str(); // Set iostream numeric format and precision kml_file.setf(kml_file.std::ofstream::fixed, kml_file.std::ofstream::floatfield); kml_file << std::setprecision(14); tmp_file.setf(tmp_file.std::ofstream::fixed, tmp_file.std::ofstream::floatfield); tmp_file << std::setprecision(14); kml_file << R"()" << std::endl << R"()" << std::endl << indent << "" << std::endl << indent << indent << "GNSS Track" << std::endl << indent << indent << "" << std::endl << indent << indent << indent << indent << "GNSS-SDR Receiver position log file created at " << pt << "" << std::endl << indent << indent << indent << indent << "https://gnss-sdr.org/" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << "]]>" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "normal" << std::endl << indent << indent << indent << indent << "#track_n" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "highlight" << std::endl << indent << indent << indent << indent << "#track_h" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << indent << indent << "Points" << std::endl; return true; } std::cout << "File " << kml_filename << " cannot be saved. Wrong permissions?" << std::endl; return false; } bool Kml_Printer::print_position(const std::shared_ptr& position, bool print_average_values) { double latitude; double longitude; double height; positions_printed = true; const std::shared_ptr& position_ = position; double speed_over_ground = position_->get_speed_over_ground(); // expressed in m/s double course_over_ground = position_->get_course_over_ground(); // expressed in deg double hdop = position_->get_hdop(); double vdop = position_->get_vdop(); double pdop = position_->get_pdop(); std::string utc_time = to_iso_extended_string(position_->get_position_UTC_time()); if (utc_time.length() < 23) { utc_time += "."; } utc_time.resize(23, '0'); // time up to ms utc_time.append("Z"); // UTC time zone if (print_average_values == false) { latitude = position_->get_latitude(); longitude = position_->get_longitude(); height = position_->get_height(); } else { latitude = position_->get_avg_latitude(); longitude = position_->get_avg_longitude(); height = position_->get_avg_height(); } if (kml_file.is_open() && tmp_file.is_open()) { point_id++; kml_file << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "" << point_id << "" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << indent << indent << "Time:" << utc_time << "" << std::endl << indent << indent << indent << indent << indent << indent << "Longitude:" << longitude << "deg" << std::endl << indent << indent << indent << indent << indent << indent << "Latitude:" << latitude << "deg" << std::endl << indent << indent << indent << indent << indent << indent << "Altitude:" << height << "m" << std::endl << indent << indent << indent << indent << indent << indent << "Speed:" << speed_over_ground << "m/s" << std::endl << indent << indent << indent << indent << indent << indent << "Course:" << course_over_ground << "deg" << std::endl << indent << indent << indent << indent << indent << indent << "HDOP:" << hdop << "" << std::endl << indent << indent << indent << indent << indent << indent << "VDOP:" << vdop << "" << std::endl << indent << indent << indent << indent << indent << indent << "PDOP:" << pdop << "" << std::endl << indent << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "]]>" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << indent << "" << utc_time << "" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "#track" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << indent << "absolute" << std::endl << indent << indent << indent << indent << indent << "" << longitude << "," << latitude << "," << height << "" << std::endl << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << "" << std::endl; tmp_file << indent << indent << indent << indent << indent << longitude << "," << latitude << "," << height << std::endl; return true; } return false; } bool Kml_Printer::close_file() { if (kml_file.is_open() && tmp_file.is_open()) { tmp_file.close(); kml_file << indent << indent << "" << indent << indent << "" << std::endl << indent << indent << indent << "Path" << std::endl << indent << indent << indent << "#yellowLineGreenPoly" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << indent << indent << "0" << std::endl << indent << indent << indent << indent << "1" << std::endl << indent << indent << indent << indent << "absolute" << std::endl << indent << indent << indent << indent << "" << std::endl; // Copy the contents of tmp_file into kml_file std::ifstream src(tmp_file_str, std::ios::binary); kml_file << src.rdbuf(); kml_file << indent << indent << indent << indent << "" << std::endl << indent << indent << indent << "" << std::endl << indent << indent << "" << std::endl << indent << "" << std::endl << ""; kml_file.close(); return true; } return false; } Kml_Printer::~Kml_Printer() { try { close_file(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } if (!positions_printed) { errorlib::error_code ec; if (!fs::remove(fs::path(kml_filename), ec)) { LOG(INFO) << "Error deleting temporary KML file"; } } } src/algorithms/PVT/libs/kml_printer.h000066400000000000000000000040731352176506000201300ustar00rootroot00000000000000/*! * \file kml_printer.h * \brief Interface of a class that prints PVT information to a kml file * \author Javier Arribas, 2011. jarribas(at)cttc.es * Álvaro Cebrián Juan, 2018. acebrianjuan(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_KML_PRINTER_H_ #define GNSS_SDR_KML_PRINTER_H_ #include // for ofstream #include // for shared_ptr class Rtklib_Solver; /*! * \brief Prints PVT information to OGC KML format file (can be viewed with Google Earth) * * See http://www.opengeospatial.org/standards/kml */ class Kml_Printer { public: Kml_Printer(const std::string& base_path = std::string(".")); ~Kml_Printer(); bool set_headers(const std::string& filename, bool time_tag_name = true); bool print_position(const std::shared_ptr& position, bool print_average_values); bool close_file(); private: std::ofstream kml_file; std::ofstream tmp_file; bool positions_printed; std::string kml_filename; std::string kml_base_path; std::string tmp_file_str; unsigned int point_id; std::string indent; }; #endif src/algorithms/PVT/libs/ls_pvt.cc000066400000000000000000000272111352176506000172460ustar00rootroot00000000000000/*! * \file ls_pvt.cc * \brief Implementation of a base class for Least Squares PVT solutions * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ls_pvt.h" #include "GPS_L1_CA.h" #include "geofunctions.h" #include #include Ls_Pvt::Ls_Pvt() : Pvt_Solution() { } arma::vec Ls_Pvt::bancroftPos(const arma::mat& satpos, const arma::vec& obs) { // BANCROFT Calculation of preliminary coordinates for a GPS receiver based on pseudoranges // to 4 or more satellites. The ECEF coordinates are stored in satpos. // The observed pseudoranges are stored in obs // Reference: Bancroft, S. (1985) An Algebraic Solution of the GPS Equations, // IEEE Trans. Aerosp. and Elec. Systems, AES-21, Issue 1, pp. 56--59 // Based on code by: // Kai Borre 04-30-95, improved by C.C. Goad 11-24-96 // // Test values to use in debugging // B_pass =[ -11716227.778 -10118754.628 21741083.973 22163882.029; // -12082643.974 -20428242.179 11741374.154 21492579.823; // 14373286.650 -10448439.349 19596404.858 21492492.771; // 10278432.244 -21116508.618 -12689101.970 25284588.982 ]; // Solution: 595025.053 -4856501.221 4078329.981 // // Test values to use in debugging // B_pass = [14177509.188 -18814750.650 12243944.449 21119263.116; // 15097198.146 -4636098.555 21326705.426 22527063.486; // 23460341.997 -9433577.991 8174873.599 23674159.579; // -8206498.071 -18217989.839 17605227.065 20951643.862; // 1399135.830 -17563786.820 19705534.862 20155386.649; // 6995655.459 -23537808.269 -9927906.485 24222112.972 ]; // Solution: 596902.683 -4847843.316 4088216.740 arma::vec pos = arma::zeros(4, 1); arma::mat B_pass = arma::zeros(obs.size(), 4); B_pass.submat(0, 0, obs.size() - 1, 2) = satpos; B_pass.col(3) = obs; arma::mat B; arma::mat BBB; double traveltime = 0; for (int iter = 0; iter < 2; iter++) { B = B_pass; int m = arma::size(B, 0); for (int i = 0; i < m; i++) { int x = B(i, 0); int y = B(i, 1); if (iter == 0) { traveltime = 0.072; } else { int z = B(i, 2); double rho = (x - pos(0)) * (x - pos(0)) + (y - pos(1)) * (y - pos(1)) + (z - pos(2)) * (z - pos(2)); traveltime = sqrt(rho) / GPS_C_M_S; } double angle = traveltime * 7.292115147e-5; double cosa = cos(angle); double sina = sin(angle); B(i, 0) = cosa * x + sina * y; B(i, 1) = -sina * x + cosa * y; } // % i-loop if (m > 3) { BBB = arma::inv(B.t() * B) * B.t(); } else { BBB = arma::inv(B); } arma::vec e = arma::ones(m, 1); arma::vec alpha = arma::zeros(m, 1); for (int i = 0; i < m; i++) { alpha(i) = lorentz(B.row(i).t(), B.row(i).t()) / 2.0; } arma::mat BBBe = BBB * e; arma::mat BBBalpha = BBB * alpha; double a = lorentz(BBBe, BBBe); double b = lorentz(BBBe, BBBalpha) - 1; double c = lorentz(BBBalpha, BBBalpha); double root = sqrt(b * b - a * c); arma::vec r = {(-b - root) / a, (-b + root) / a}; arma::mat possible_pos = arma::zeros(4, 2); for (int i = 0; i < 2; i++) { possible_pos.col(i) = r(i) * BBBe + BBBalpha; possible_pos(3, i) = -possible_pos(3, i); } arma::vec abs_omc = arma::zeros(2, 1); for (int j = 0; j < m; j++) { for (int i = 0; i < 2; i++) { double c_dt = possible_pos(3, i); double calc = arma::norm(satpos.row(i).t() - possible_pos.col(i).rows(0, 2)) + c_dt; double omc = obs(j) - calc; abs_omc(i) = std::abs(omc); } } // % j-loop // discrimination between roots if (abs_omc(0) > abs_omc(1)) { pos = possible_pos.col(1); } else { pos = possible_pos.col(0); } } // % iter loop return pos; } double Ls_Pvt::lorentz(const arma::vec& x, const arma::vec& y) { // LORENTZ Calculates the Lorentz inner product of the two // 4 by 1 vectors x and y // Based on code by: // Kai Borre 04-22-95 // // M = diag([1 1 1 -1]); // p = x'*M*y; return (x(0) * y(0) + x(1) * y(1) + x(2) * y(2) - x(3) * y(3)); } arma::vec Ls_Pvt::leastSquarePos(const arma::mat& satpos, const arma::vec& obs, const arma::vec& w_vec) { /* Computes the Least Squares Solution. * Inputs: * satpos - Satellites positions in ECEF system: [X; Y; Z;] * obs - Observations - the pseudorange measurements to each satellite * w - weight vector * * Returns: * pos - receiver position and receiver clock error * (in ECEF system: [X, Y, Z, dt]) */ //=== Initialization ======================================================= int nmbOfIterations = 10; // TODO: include in config int nmbOfSatellites; nmbOfSatellites = satpos.n_cols; // Armadillo arma::mat w = arma::zeros(nmbOfSatellites, nmbOfSatellites); w.diag() = w_vec; //diagonal weight matrix arma::vec rx_pos = this->get_rx_pos(); arma::vec pos = {rx_pos(0), rx_pos(1), rx_pos(2), 0}; // time error in METERS (time x speed) arma::mat A; arma::mat omc; A = arma::zeros(nmbOfSatellites, 4); omc = arma::zeros(nmbOfSatellites, 1); arma::mat X = satpos; arma::vec Rot_X; double rho2; double traveltime; double trop = 0.0; double dlambda; double dphi; double h; arma::vec x; //=== Iteratively find receiver position =================================== for (int iter = 0; iter < nmbOfIterations; iter++) { for (int i = 0; i < nmbOfSatellites; i++) { if (iter == 0) { //--- Initialize variables at the first iteration -------------- Rot_X = X.col(i); //Armadillo trop = 0.0; } else { //--- Update equations ----------------------------------------- rho2 = (X(0, i) - pos(0)) * (X(0, i) - pos(0)) + (X(1, i) - pos(1)) * (X(1, i) - pos(1)) + (X(2, i) - pos(2)) * (X(2, i) - pos(2)); traveltime = sqrt(rho2) / GPS_C_M_S; //--- Correct satellite position (do to earth rotation) -------- Rot_X = Ls_Pvt::rotateSatellite(traveltime, X.col(i)); //armadillo //--- Find DOA and range of satellites double* azim = nullptr; double* elev = nullptr; double* dist = nullptr; topocent(azim, elev, dist, pos.subvec(0, 2), Rot_X - pos.subvec(0, 2)); if (traveltime < 0.1 && nmbOfSatellites > 3) { //--- Find receiver's height togeod(&dphi, &dlambda, &h, 6378137.0, 298.257223563, pos(0), pos(1), pos(2)); // Add troposphere correction if the receiver is below the troposphere if (h > 15000) { //receiver is above the troposphere trop = 0.0; } else { //--- Find delay due to troposphere (in meters) Ls_Pvt::tropo(&trop, sin(*elev * GPS_PI / 180.0), h / 1000.0, 1013.0, 293.0, 50.0, 0.0, 0.0, 0.0); if (trop > 5.0) { trop = 0.0; //check for erratic values } } } } //--- Apply the corrections ---------------------------------------- omc(i) = (obs(i) - norm(Rot_X - pos.subvec(0, 2), 2) - pos(3) - trop); // Armadillo //--- Construct the A matrix --------------------------------------- //Armadillo A(i, 0) = (-(Rot_X(0) - pos(0))) / obs(i); A(i, 1) = (-(Rot_X(1) - pos(1))) / obs(i); A(i, 2) = (-(Rot_X(2) - pos(2))) / obs(i); A(i, 3) = 1.0; } //--- Find position update --------------------------------------------- x = arma::solve(w * A, w * omc); // Armadillo //--- Apply position update -------------------------------------------- pos = pos + x; if (arma::norm(x, 2) < 1e-4) { break; // exit the loop because we assume that the LS algorithm has converged (err < 0.1 cm) } } // check the consistency of the PVT solution if (((fabs(pos(3)) * 1000.0) / GPS_C_M_S) > GPS_STARTOFFSET_MS * 2) { LOG(WARNING) << "Receiver time offset out of range! Estimated RX Time error [s]:" << pos(3) / GPS_C_M_S; throw std::runtime_error("Receiver time offset out of range!"); } return pos; } src/algorithms/PVT/libs/ls_pvt.h000066400000000000000000000035601352176506000171110ustar00rootroot00000000000000/*! * \file ls_pvt.h * \brief Interface of a base class for Least Squares PVT solutions * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_LS_PVT_H_ #define GNSS_SDR_LS_PVT_H_ #include "pvt_solution.h" /*! * \brief Base class for the Least Squares PVT solution * */ class Ls_Pvt : public Pvt_Solution { public: Ls_Pvt(); /*! * \brief Computes the initial position solution based on the Bancroft algorithm */ arma::vec bancroftPos(const arma::mat& satpos, const arma::vec& obs); /*! * \brief Computes the Weighted Least Squares position solution */ arma::vec leastSquarePos(const arma::mat& satpos, const arma::vec& obs, const arma::vec& w_vec); private: /*! * \brief Computes the Lorentz inner product between two vectors */ double lorentz(const arma::vec& x, const arma::vec& y); }; #endif src/algorithms/PVT/libs/monitor_pvt.h000066400000000000000000000101261352176506000201560ustar00rootroot00000000000000/*! * \file monitor_pvt.h * \brief Interface of the Monitor_Pvt class * \author * Álvaro Cebrián Juan, 2019. acebrianjuan(at)gmail.com * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_MONITOR_PVT_H_ #define GNSS_SDR_MONITOR_PVT_H_ #include #include /*! * \brief This class contains parameters and outputs of the PVT block */ class Monitor_Pvt { public: // TOW uint32_t TOW_at_current_symbol_ms; // WEEK uint32_t week; // PVT GPS time double RX_time; // User clock offset [s] double user_clk_offset; // ECEF POS X,Y,X [m] + ECEF VEL X,Y,X [m/s] (6 x double) double pos_x; double pos_y; double pos_z; double vel_x; double vel_y; double vel_z; // position variance/covariance (m^2) {c_xx,c_yy,c_zz,c_xy,c_yz,c_zx} (6 x double) double cov_xx; double cov_yy; double cov_zz; double cov_xy; double cov_yz; double cov_zx; // GEO user position Latitude [deg] double latitude; // GEO user position Longitude [deg] double longitude; // GEO user position Height [m] double height; // NUMBER OF VALID SATS uint8_t valid_sats; // RTKLIB solution status uint8_t solution_status; // RTKLIB solution type (0:xyz-ecef,1:enu-baseline) uint8_t solution_type; // AR ratio factor for validation float AR_ratio_factor; // AR ratio threshold for validation float AR_ratio_threshold; // GDOP / PDOP/ HDOP/ VDOP double gdop; double pdop; double hdop; double vdop; /*! * \brief This member function serializes and restores * Monitor_Pvt objects from a byte stream. */ template void serialize(Archive& ar, const unsigned int version) { if (version) { }; ar& BOOST_SERIALIZATION_NVP(TOW_at_current_symbol_ms); ar& BOOST_SERIALIZATION_NVP(week); ar& BOOST_SERIALIZATION_NVP(RX_time); ar& BOOST_SERIALIZATION_NVP(user_clk_offset); ar& BOOST_SERIALIZATION_NVP(pos_x); ar& BOOST_SERIALIZATION_NVP(pos_y); ar& BOOST_SERIALIZATION_NVP(pos_z); ar& BOOST_SERIALIZATION_NVP(vel_x); ar& BOOST_SERIALIZATION_NVP(vel_y); ar& BOOST_SERIALIZATION_NVP(vel_z); ar& BOOST_SERIALIZATION_NVP(cov_xx); ar& BOOST_SERIALIZATION_NVP(cov_yy); ar& BOOST_SERIALIZATION_NVP(cov_zz); ar& BOOST_SERIALIZATION_NVP(cov_xy); ar& BOOST_SERIALIZATION_NVP(cov_yz); ar& BOOST_SERIALIZATION_NVP(cov_zx); ar& BOOST_SERIALIZATION_NVP(latitude); ar& BOOST_SERIALIZATION_NVP(longitude); ar& BOOST_SERIALIZATION_NVP(height); ar& BOOST_SERIALIZATION_NVP(valid_sats); ar& BOOST_SERIALIZATION_NVP(solution_status); ar& BOOST_SERIALIZATION_NVP(solution_type); ar& BOOST_SERIALIZATION_NVP(AR_ratio_factor); ar& BOOST_SERIALIZATION_NVP(AR_ratio_threshold); ar& BOOST_SERIALIZATION_NVP(gdop); ar& BOOST_SERIALIZATION_NVP(pdop); ar& BOOST_SERIALIZATION_NVP(hdop); ar& BOOST_SERIALIZATION_NVP(vdop); } }; #endif /* GNSS_SDR_MONITOR_PVT_H_ */ src/algorithms/PVT/libs/monitor_pvt_udp_sink.cc000066400000000000000000000053031352176506000222110ustar00rootroot00000000000000/*! * \file monitor_pvt_udp_sink.cc * \brief Implementation of a class that sends serialized Monitor_Pvt * objects over udp to one or multiple endpoints * \author Álvaro Cebrián Juan, 2019. acebrianjuan(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "monitor_pvt_udp_sink.h" #include #include #include Monitor_Pvt_Udp_Sink::Monitor_Pvt_Udp_Sink(const std::vector& addresses, const uint16_t& port, bool protobuf_enabled) : socket{io_context} { for (const auto& address : addresses) { boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(address, error), port); endpoints.push_back(endpoint); } use_protobuf = protobuf_enabled; if (use_protobuf) { serdes = Serdes_Monitor_Pvt(); } } bool Monitor_Pvt_Udp_Sink::write_monitor_pvt(const std::shared_ptr& monitor_pvt) { std::string outbound_data; if (use_protobuf == false) { std::ostringstream archive_stream; boost::archive::binary_oarchive oa{archive_stream}; oa << *monitor_pvt.get(); outbound_data = archive_stream.str(); } else { outbound_data = serdes.createProtobuffer(monitor_pvt); } for (const auto& endpoint : endpoints) { socket.open(endpoint.protocol(), error); socket.connect(endpoint, error); try { socket.send(boost::asio::buffer(outbound_data)); } catch (boost::system::system_error const& e) { return false; } } return true; } src/algorithms/PVT/libs/monitor_pvt_udp_sink.h000066400000000000000000000037571352176506000220660ustar00rootroot00000000000000/*! * \file monitor_pvt_udp_sink.h * \brief Interface of a class that sends serialized Monitor_Pvt objects * over udp to one or multiple endpoints * \author Álvaro Cebrián Juan, 2019. acebrianjuan(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_MONITOR_PVT_UDP_SINK_H_ #define GNSS_SDR_MONITOR_PVT_UDP_SINK_H_ #include "monitor_pvt.h" #include "serdes_monitor_pvt.h" #include #if BOOST_GREATER_1_65 using b_io_context = boost::asio::io_context; #else using b_io_context = boost::asio::io_service; #endif class Monitor_Pvt_Udp_Sink { public: Monitor_Pvt_Udp_Sink(const std::vector& addresses, const uint16_t& port, bool protobuf_enabled); bool write_monitor_pvt(const std::shared_ptr& monitor_pvt); private: b_io_context io_context; boost::asio::ip::udp::socket socket; boost::system::error_code error; std::vector endpoints; Serdes_Monitor_Pvt serdes; bool use_protobuf; }; #endif /* GNSS_SDR_MONITOR_PVT_UDP_SINK_H_ */ src/algorithms/PVT/libs/nmea_printer.cc000066400000000000000000000337731352176506000204340ustar00rootroot00000000000000/*! * \file nmea_printer.cc * \brief Implementation of a NMEA 2.1 printer for GNSS-SDR * This class provides a implementation of a subset of the NMEA-0183 standard for interfacing * marine electronic devices as defined by the National Marine Electronics Association (NMEA). * See http://www.nmea.org/ for the NMEA 183 standard * * \author Javier Arribas, 2012. jarribas(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "nmea_printer.h" #include "rtklib_solution.h" #include "rtklib_solver.h" #include #include #include #include #include #include // for cout, cerr #include #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Nmea_Printer::Nmea_Printer(const std::string& filename, bool flag_nmea_output_file, bool flag_nmea_tty_port, std::string nmea_dump_devname, const std::string& base_path) { nmea_base_path = base_path; d_flag_nmea_output_file = flag_nmea_output_file; if (d_flag_nmea_output_file == true) { fs::path full_path(fs::current_path()); const fs::path p(nmea_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(nmea_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; nmea_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { nmea_base_path = p.string(); } if ((nmea_base_path != ".") and (d_flag_nmea_output_file == true)) { std::cout << "NMEA files will be stored at " << nmea_base_path << std::endl; } nmea_base_path = nmea_base_path + fs::path::preferred_separator; nmea_filename = nmea_base_path + filename; nmea_file_descriptor.open(nmea_filename.c_str(), std::ios::out); if (nmea_file_descriptor.is_open()) { DLOG(INFO) << "NMEA printer writing on " << nmea_filename.c_str(); } else { std::cout << "File " << nmea_filename << " cannot be saved. Wrong permissions?" << std::endl; } } nmea_devname = std::move(nmea_dump_devname); if (flag_nmea_tty_port == true) { nmea_dev_descriptor = init_serial(nmea_devname); if (nmea_dev_descriptor != -1) { DLOG(INFO) << "NMEA printer writing on " << nmea_devname.c_str(); } } else { nmea_dev_descriptor = -1; } print_avg_pos = false; } Nmea_Printer::~Nmea_Printer() { auto pos = nmea_file_descriptor.tellp(); try { if (nmea_file_descriptor.is_open()) { nmea_file_descriptor.close(); } } catch (const std::ofstream::failure& e) { std::cerr << "Problem closing NMEA dump file: " << nmea_filename << '\n'; } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } if (pos == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(nmea_filename), ec)) { std::cerr << "Problem removing NMEA temporary file: " << nmea_filename << '\n'; } } try { close_serial(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } } int Nmea_Printer::init_serial(const std::string& serial_device) { /*! * Opens the serial device and sets the default baud rate for a NMEA transmission (9600,8,N,1) */ int fd = 0; // clang-format off struct termios options{}; // clang-format on int64_t BAUD; int64_t DATABITS; int64_t STOPBITS; int64_t PARITYON; int64_t PARITY; fd = open(serial_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC); if (fd == -1) { return fd; // failed to open TTY port } if (fcntl(fd, F_SETFL, 0) == -1) { LOG(INFO) << "Error enabling direct I/O"; // clear all flags on descriptor, enable direct I/O } tcgetattr(fd, &options); // read serial port options BAUD = B9600; // BAUD = B38400; DATABITS = CS8; STOPBITS = 0; PARITYON = 0; PARITY = 0; options.c_cflag = BAUD | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD; // enable receiver, set 8 bit data, ignore control lines // options.c_cflag |= (CLOCAL | CREAD | CS8); options.c_iflag = IGNPAR; // set the new port options tcsetattr(fd, TCSANOW, &options); return fd; } void Nmea_Printer::close_serial() { if (nmea_dev_descriptor != -1) { close(nmea_dev_descriptor); } } bool Nmea_Printer::Print_Nmea_Line(const std::shared_ptr& pvt_data, bool print_average_values) { std::string GPRMC; std::string GPGGA; std::string GPGSA; std::string GPGSV; // set the new PVT data d_PVT_data = pvt_data; print_avg_pos = print_average_values; // generate the NMEA sentences // GPRMC GPRMC = get_GPRMC(); // GPGGA (Global Positioning System Fixed Data) GPGGA = get_GPGGA(); // GPGSA GPGSA = get_GPGSA(); // GPGSV GPGSV = get_GPGSV(); // write to log file if (d_flag_nmea_output_file) { try { // GPRMC nmea_file_descriptor << GPRMC; // GPGGA (Global Positioning System Fixed Data) nmea_file_descriptor << GPGGA; // GPGSA nmea_file_descriptor << GPGSA; // GPGSV nmea_file_descriptor << GPGSV; } catch (const std::exception& ex) { DLOG(INFO) << "NMEA printer can not write on output file" << nmea_filename.c_str(); } } // write to serial device if (nmea_dev_descriptor != -1) { if (write(nmea_dev_descriptor, GPRMC.c_str(), GPRMC.length()) == -1) { DLOG(INFO) << "NMEA printer cannot write on serial device" << nmea_devname.c_str(); return false; } if (write(nmea_dev_descriptor, GPGGA.c_str(), GPGGA.length()) == -1) { DLOG(INFO) << "NMEA printer cannot write on serial device" << nmea_devname.c_str(); return false; } if (write(nmea_dev_descriptor, GPGSA.c_str(), GPGSA.length()) == -1) { DLOG(INFO) << "NMEA printer cannot write on serial device" << nmea_devname.c_str(); return false; } if (write(nmea_dev_descriptor, GPGSV.c_str(), GPGSV.length()) == -1) { DLOG(INFO) << "NMEA printer cannot write on serial device" << nmea_devname.c_str(); return false; } } return true; } char Nmea_Printer::checkSum(const std::string& sentence) { char check = 0; // iterate over the string, XOR each byte with the total sum: for (char c : sentence) { check = char(check ^ c); } // return the result return check; } std::string Nmea_Printer::latitude_to_hm(double lat) { bool north; if (lat < 0.0) { north = false; lat = -lat; } else { north = true; } int deg = static_cast(lat); double mins = lat - static_cast(deg); mins *= 60.0; std::ostringstream out_string; out_string.setf(std::ios::fixed, std::ios::floatfield); out_string.fill('0'); out_string.width(2); out_string << deg; out_string.width(2); out_string << static_cast(mins) << "."; out_string.width(4); out_string << static_cast((mins - static_cast(static_cast(mins))) * 1e4); if (north == true) { out_string << ",N"; } else { out_string << ",S"; } return out_string.str(); } std::string Nmea_Printer::longitude_to_hm(double longitude) { bool east; if (longitude < 0.0) { east = false; longitude = -longitude; } else { east = true; } int deg = static_cast(longitude); double mins = longitude - static_cast(deg); mins *= 60.0; std::ostringstream out_string; out_string.setf(std::ios::fixed, std::ios::floatfield); out_string.width(3); out_string.fill('0'); out_string << deg; out_string.width(2); out_string << static_cast(mins) << "."; out_string.width(4); out_string << static_cast((mins - static_cast(static_cast(mins))) * 1e4); if (east == true) { out_string << ",E"; } else { out_string << ",W"; } return out_string.str(); } std::string Nmea_Printer::get_UTC_NMEA_time(boost::posix_time::ptime d_position_UTC_time) { // UTC Time: hhmmss.sss std::stringstream sentence_str; boost::posix_time::time_duration td = d_position_UTC_time.time_of_day(); int utc_hours; int utc_mins; int utc_seconds; int utc_milliseconds; utc_hours = td.hours(); utc_mins = td.minutes(); utc_seconds = td.seconds(); utc_milliseconds = td.total_milliseconds() - td.total_seconds() * 1000; if (utc_hours < 10) { sentence_str << "0"; // two digits for hours } sentence_str << utc_hours; if (utc_mins < 10) { sentence_str << "0"; // two digits for minutes } sentence_str << utc_mins; if (utc_seconds < 10) { sentence_str << "0"; // two digits for seconds } sentence_str << utc_seconds; if (utc_milliseconds < 10) { sentence_str << ".00"; // three digits for ms sentence_str << utc_milliseconds; } else if (utc_milliseconds < 100) { sentence_str << ".0"; // three digits for ms sentence_str << utc_milliseconds; } else { sentence_str << "."; // three digits for ms sentence_str << utc_milliseconds; } return sentence_str.str(); } std::string Nmea_Printer::get_GPRMC() { // Sample -> $GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,*10 std::stringstream sentence_str; std::array buff{}; outnmea_rmc(buff.data(), &d_PVT_data->pvt_sol); sentence_str << buff.data(); return sentence_str.str(); } std::string Nmea_Printer::get_GPGSA() { // $GPGSA,A,3,07,02,26,27,09,04,15, , , , , ,1.8,1.0,1.5*33 // GSA-GNSS DOP and Active Satellites std::stringstream sentence_str; std::array buff{}; outnmea_gsa(buff.data(), &d_PVT_data->pvt_sol, d_PVT_data->pvt_ssat.data()); sentence_str << buff.data(); return sentence_str.str(); } std::string Nmea_Printer::get_GPGSV() { // GSV-GNSS Satellites in View // $GPGSV,2,1,07,07,79,048,42,02,51,062,43,26,36,256,42,27,27,138,42*71 // Notice that NMEA 2.1 only supports 12 channels std::stringstream sentence_str; std::array buff{}; outnmea_gsv(buff.data(), &d_PVT_data->pvt_sol, d_PVT_data->pvt_ssat.data()); sentence_str << buff.data(); return sentence_str.str(); } std::string Nmea_Printer::get_GPGGA() { std::stringstream sentence_str; std::array buff{}; outnmea_gga(buff.data(), &d_PVT_data->pvt_sol); sentence_str << buff.data(); return sentence_str.str(); // $GPGGA,104427.591,5920.7009,N,01803.2938,E,1,05,3.3,78.2,M,23.2,M,0.0,0000*4A } src/algorithms/PVT/libs/nmea_printer.h000066400000000000000000000066101352176506000202640ustar00rootroot00000000000000/*! * \file nmea_printer.h * \brief Interface of a NMEA 2.1 printer for GNSS-SDR * This class provides a implementation of a subset of the NMEA-0183 standard for interfacing * marine electronic devices as defined by the National Marine Electronics Association (NMEA). * See http://www.nmea.org/ for the NMEA 183 standard * * \author Javier Arribas, 2012. jarribas(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_NMEA_PRINTER_H_ #define GNSS_SDR_NMEA_PRINTER_H_ #include // for ptime #include // for ofstream #include // for shared_ptr #include // for string class Rtklib_Solver; /*! * \brief This class provides a implementation of a subset of the NMEA-0183 standard for interfacing * marine electronic devices as defined by the National Marine Electronics Association (NMEA). * * See http://en.wikipedia.org/wiki/NMEA_0183 */ class Nmea_Printer { public: /*! * \brief Default constructor. */ Nmea_Printer(const std::string& filename, bool flag_nmea_output_file, bool flag_nmea_tty_port, std::string nmea_dump_devname, const std::string& base_path = "."); /*! * \brief Print NMEA PVT and satellite info to the initialized device */ bool Print_Nmea_Line(const std::shared_ptr& pvt_data, bool print_average_values); /*! * \brief Default destructor. */ ~Nmea_Printer(); private: std::string nmea_filename; // String with the NMEA log filename std::string nmea_base_path; std::ofstream nmea_file_descriptor; // Output file stream for NMEA log file std::string nmea_devname; int nmea_dev_descriptor; // NMEA serial device descriptor (i.e. COM port) std::shared_ptr d_PVT_data; int init_serial(const std::string& serial_device); //serial port control void close_serial(); std::string get_GPGGA(); // fix data std::string get_GPGSV(); // satellite data std::string get_GPGSA(); // overall satellite reception data std::string get_GPRMC(); // minimum recommended data std::string get_UTC_NMEA_time(boost::posix_time::ptime d_position_UTC_time); std::string longitude_to_hm(double longitude); std::string latitude_to_hm(double lat); char checkSum(const std::string& sentence); bool print_avg_pos; bool d_flag_nmea_output_file; }; #endif src/algorithms/PVT/libs/pvt_conf.cc000066400000000000000000000045211352176506000175540ustar00rootroot00000000000000/*! * \file pvt_conf.cc * \brief Class that contains all the configuration parameters for a PVT block * \author Carles Fernandez, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pvt_conf.h" Pvt_Conf::Pvt_Conf() { type_of_receiver = 0U; output_rate_ms = 0; display_rate_ms = 0; kml_rate_ms = 1000; gpx_rate_ms = 1000; geojson_rate_ms = 1000; nmea_rate_ms = 1000; max_obs_block_rx_clock_offset_ms = 40; rinex_version = 0; rinexobs_rate_ms = 0; dump = false; dump_mat = true; flag_nmea_tty_port = false; flag_rtcm_server = false; flag_rtcm_tty_port = false; rtcm_tcp_port = 0U; rtcm_station_id = 0U; output_enabled = true; rinex_output_enabled = true; gpx_output_enabled = true; geojson_output_enabled = true; nmea_output_file_enabled = true; kml_output_enabled = true; xml_output_enabled = true; rtcm_output_file_enabled = true; output_path = std::string("."); rinex_output_path = std::string("."); gpx_output_path = std::string("."); geojson_output_path = std::string("."); nmea_output_file_path = std::string("."); kml_output_path = std::string("."); xml_output_path = std::string("."); rtcm_output_file_path = std::string("."); monitor_enabled = false; protobuf_enabled = true; udp_port = 0; show_local_time_zone = false; } src/algorithms/PVT/libs/pvt_conf.h000066400000000000000000000051361352176506000174210ustar00rootroot00000000000000/*! * \file pvt_conf.h * \brief Class that contains all the configuration parameters for the PVT block * \author Carles Fernandez, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PVT_CONF_H_ #define GNSS_SDR_PVT_CONF_H_ #include #include #include class Pvt_Conf { public: uint32_t type_of_receiver; int32_t output_rate_ms; int32_t display_rate_ms; int32_t kml_rate_ms; int32_t gpx_rate_ms; int32_t geojson_rate_ms; int32_t nmea_rate_ms; int32_t rinex_version; int32_t rinexobs_rate_ms; std::map rtcm_msg_rate_ms; bool dump; bool dump_mat; std::string dump_filename; bool flag_nmea_tty_port; std::string nmea_dump_filename; std::string nmea_dump_devname; bool flag_rtcm_server; bool flag_rtcm_tty_port; uint16_t rtcm_tcp_port; uint16_t rtcm_station_id; std::string rtcm_dump_devname; bool output_enabled; bool rinex_output_enabled; bool gpx_output_enabled; bool geojson_output_enabled; bool nmea_output_file_enabled; bool kml_output_enabled; bool xml_output_enabled; bool rtcm_output_file_enabled; int32_t max_obs_block_rx_clock_offset_ms; std::string output_path; std::string rinex_output_path; std::string gpx_output_path; std::string geojson_output_path; std::string nmea_output_file_path; std::string kml_output_path; std::string xml_output_path; std::string rtcm_output_file_path; bool monitor_enabled; bool protobuf_enabled; std::string udp_addresses; int udp_port; bool show_local_time_zone; Pvt_Conf(); }; #endif src/algorithms/PVT/libs/pvt_solution.cc000066400000000000000000000304061352176506000205040ustar00rootroot00000000000000/*! * \file pvt_solution.cc * \brief Implementation of a base class for a PVT solution * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pvt_solution.h" #include "GPS_L1_CA.h" #include "geofunctions.h" #include #include Pvt_Solution::Pvt_Solution() { d_latitude_d = 0.0; d_longitude_d = 0.0; d_height_m = 0.0; d_speed_over_ground_m_s = 0.0; d_course_over_ground_d = 0.0; d_avg_latitude_d = 0.0; d_avg_longitude_d = 0.0; d_avg_height_m = 0.0; d_flag_averaging = false; b_valid_position = false; d_averaging_depth = 0; d_valid_observations = 0; d_rx_pos = arma::zeros(3, 1); d_rx_dt_s = 0.0; } arma::vec Pvt_Solution::rotateSatellite(double const traveltime, const arma::vec &X_sat) { /* * Returns rotated satellite ECEF coordinates due to Earth * rotation during signal travel time * * Inputs: * travelTime - signal travel time * X_sat - satellite's ECEF coordinates * * Returns: * X_sat_rot - rotated satellite's coordinates (ECEF) */ //--- Find rotation angle -------------------------------------------------- double omegatau; omegatau = OMEGA_EARTH_DOT * traveltime; //--- Build a rotation matrix ---------------------------------------------- arma::mat R3 = {{cos(omegatau), sin(omegatau), 0.0}, {-sin(omegatau), cos(omegatau), 0.0}, {0.0, 0.0, 1.0}}; //--- Do the rotation ------------------------------------------------------ arma::vec X_sat_rot; X_sat_rot = R3 * X_sat; return X_sat_rot; } int Pvt_Solution::cart2geo(double X, double Y, double Z, int elipsoid_selection) { /* Conversion of Cartesian coordinates (X,Y,Z) to geographical coordinates (latitude, longitude, h) on a selected reference ellipsoid. Choices of Reference Ellipsoid for Geographical Coordinates 0. International Ellipsoid 1924 1. International Ellipsoid 1967 2. World Geodetic System 1972 3. Geodetic Reference System 1980 4. World Geodetic System 1984 */ const std::array a = {6378388.0, 6378160.0, 6378135.0, 6378137.0, 6378137.0}; const std::array f = {1.0 / 297.0, 1.0 / 298.247, 1.0 / 298.26, 1.0 / 298.257222101, 1.0 / 298.257223563}; double lambda = atan2(Y, X); double ex2 = (2.0 - f[elipsoid_selection]) * f[elipsoid_selection] / ((1.0 - f[elipsoid_selection]) * (1.0 - f[elipsoid_selection])); double c = a[elipsoid_selection] * sqrt(1.0 + ex2); double phi = atan(Z / ((sqrt(X * X + Y * Y) * (1.0 - (2.0 - f[elipsoid_selection])) * f[elipsoid_selection]))); double h = 0.1; double oldh = 0.0; double N; int iterations = 0; do { oldh = h; N = c / sqrt(1 + ex2 * (cos(phi) * cos(phi))); phi = atan(Z / ((sqrt(X * X + Y * Y) * (1.0 - (2.0 - f[elipsoid_selection]) * f[elipsoid_selection] * N / (N + h))))); h = sqrt(X * X + Y * Y) / cos(phi) - N; iterations = iterations + 1; if (iterations > 100) { DLOG(WARNING) << "Failed to approximate h with desired precision. h-oldh= " << h - oldh; break; } } while (std::abs(h - oldh) > 1.0e-12); d_latitude_d = phi * 180.0 / GPS_PI; d_longitude_d = lambda * 180.0 / GPS_PI; d_height_m = h; //todo: refactor this class. Mix of duplicated functions, use either RTKLIB geodetic functions or geofunctions.h return 0; } int Pvt_Solution::tropo(double *ddr_m, double sinel, double hsta_km, double p_mb, double t_kel, double hum, double hp_km, double htkel_km, double hhum_km) { /* Inputs: sinel - sin of elevation angle of satellite hsta_km - height of station in km p_mb - atmospheric pressure in mb at height hp_km t_kel - surface temperature in degrees Kelvin at height htkel_km hum - humidity in % at height hhum_km hp_km - height of pressure measurement in km htkel_km - height of temperature measurement in km hhum_km - height of humidity measurement in km Outputs: ddr_m - range correction (meters) Reference Goad, C.C. & Goodman, L. (1974) A Modified Hopfield Tropospheric Refraction Correction Model. Paper presented at the American Geophysical Union Annual Fall Meeting, San Francisco, December 12-17 Translated to C++ by Carles Fernandez from a Matlab implementation by Kai Borre */ const double a_e = 6378.137; // semi-major axis of earth ellipsoid const double b0 = 7.839257e-5; const double tlapse = -6.5; const double em = -978.77 / (2.8704e6 * tlapse * 1.0e-5); double tkhum = t_kel + tlapse * (hhum_km - htkel_km); double atkel = 7.5 * (tkhum - 273.15) / (237.3 + tkhum - 273.15); double e0 = 0.0611 * hum * pow(10, atkel); double tksea = t_kel - tlapse * htkel_km; double tkelh = tksea + tlapse * hhum_km; double e0sea = e0 * pow((tksea / tkelh), (4 * em)); double tkelp = tksea + tlapse * hp_km; double psea = p_mb * pow((tksea / tkelp), em); if (sinel < 0) { sinel = 0.0; } double tropo_delay = 0.0; bool done = false; double refsea = 77.624e-6 / tksea; double htop = 1.1385e-5 / refsea; refsea = refsea * psea; double ref = refsea * pow(((htop - hsta_km) / htop), 4); double a; double b; double rtop; while (true) { rtop = pow((a_e + htop), 2) - pow((a_e + hsta_km), 2) * (1 - pow(sinel, 2)); // check to see if geometry is crazy if (rtop < 0) { rtop = 0; } rtop = sqrt(rtop) - (a_e + hsta_km) * sinel; a = -sinel / (htop - hsta_km); b = -b0 * (1 - pow(sinel, 2)) / (htop - hsta_km); arma::vec rn = arma::vec(8); rn.zeros(); for (int i = 0; i < 8; i++) { rn(i) = pow(rtop, (i + 1 + 1)); } arma::rowvec alpha = {2 * a, 2 * pow(a, 2) + 4 * b / 3, a * (pow(a, 2) + 3 * b), pow(a, 4) / 5 + 2.4 * pow(a, 2) * b + 1.2 * pow(b, 2), 2 * a * b * (pow(a, 2) + 3 * b) / 3, pow(b, 2) * (6 * pow(a, 2) + 4 * b) * 1.428571e-1, 0, 0}; if (pow(b, 2) > 1.0e-35) { alpha(6) = a * pow(b, 3) / 2; alpha(7) = pow(b, 4) / 9; } double dr = rtop; arma::mat aux_ = alpha * rn; dr = dr + aux_(0, 0); tropo_delay = tropo_delay + dr * ref * 1000; if (done == true) { *ddr_m = tropo_delay; break; } done = true; refsea = (371900.0e-6 / tksea - 12.92e-6) / tksea; htop = 1.1385e-5 * (1255 / tksea + 0.05) / refsea; ref = refsea * e0sea * pow(((htop - hsta_km) / htop), 4); } return 0; } void Pvt_Solution::set_averaging_depth(int depth) { d_averaging_depth = depth; } void Pvt_Solution::set_averaging_flag(bool flag) { d_flag_averaging = flag; } void Pvt_Solution::perform_pos_averaging() { // MOVING AVERAGE PVT bool avg = d_flag_averaging; if (avg == true) { if (d_hist_longitude_d.size() == static_cast(d_averaging_depth)) { // Pop oldest value d_hist_longitude_d.pop_back(); d_hist_latitude_d.pop_back(); d_hist_height_m.pop_back(); // Push new values d_hist_longitude_d.push_front(d_longitude_d); d_hist_latitude_d.push_front(d_latitude_d); d_hist_height_m.push_front(d_height_m); d_avg_latitude_d = 0.0; d_avg_longitude_d = 0.0; d_avg_height_m = 0.0; for (unsigned int i = 0; i < d_hist_longitude_d.size(); i++) { d_avg_latitude_d = d_avg_latitude_d + d_hist_latitude_d.at(i); d_avg_longitude_d = d_avg_longitude_d + d_hist_longitude_d.at(i); d_avg_height_m = d_avg_height_m + d_hist_height_m.at(i); } d_avg_latitude_d = d_avg_latitude_d / static_cast(d_averaging_depth); d_avg_longitude_d = d_avg_longitude_d / static_cast(d_averaging_depth); d_avg_height_m = d_avg_height_m / static_cast(d_averaging_depth); b_valid_position = true; } else { //int current_depth=d_hist_longitude_d.size(); // Push new values d_hist_longitude_d.push_front(d_longitude_d); d_hist_latitude_d.push_front(d_latitude_d); d_hist_height_m.push_front(d_height_m); d_avg_latitude_d = d_latitude_d; d_avg_longitude_d = d_longitude_d; d_avg_height_m = d_height_m; b_valid_position = false; } } else { b_valid_position = true; } } double Pvt_Solution::get_time_offset_s() const { return d_rx_dt_s; } void Pvt_Solution::set_time_offset_s(double offset) { d_rx_dt_s = offset; } double Pvt_Solution::get_latitude() const { return d_latitude_d; } double Pvt_Solution::get_longitude() const { return d_longitude_d; } double Pvt_Solution::get_height() const { return d_height_m; } double Pvt_Solution::get_speed_over_ground() const { return d_speed_over_ground_m_s; } void Pvt_Solution::set_speed_over_ground(double speed_m_s) { d_speed_over_ground_m_s = speed_m_s; } void Pvt_Solution::set_course_over_ground(double cog_deg) { d_course_over_ground_d = cog_deg; } double Pvt_Solution::get_course_over_ground() const { return d_course_over_ground_d; } double Pvt_Solution::get_avg_latitude() const { return d_avg_latitude_d; } double Pvt_Solution::get_avg_longitude() const { return d_avg_longitude_d; } double Pvt_Solution::get_avg_height() const { return d_avg_height_m; } bool Pvt_Solution::is_averaging() const { return d_flag_averaging; } bool Pvt_Solution::is_valid_position() const { return b_valid_position; } void Pvt_Solution::set_valid_position(bool is_valid) { b_valid_position = is_valid; } void Pvt_Solution::set_rx_pos(const arma::vec &pos) { d_rx_pos = pos; d_latitude_d = d_rx_pos(0); d_longitude_d = d_rx_pos(1); d_height_m = d_rx_pos(2); } arma::vec Pvt_Solution::get_rx_pos() const { return d_rx_pos; } boost::posix_time::ptime Pvt_Solution::get_position_UTC_time() const { return d_position_UTC_time; } void Pvt_Solution::set_position_UTC_time(const boost::posix_time::ptime &pt) { d_position_UTC_time = pt; } int Pvt_Solution::get_num_valid_observations() const { return d_valid_observations; } void Pvt_Solution::set_num_valid_observations(int num) { d_valid_observations = num; } src/algorithms/PVT/libs/pvt_solution.h000066400000000000000000000140401352176506000203420ustar00rootroot00000000000000/*! * \file pvt_solution.h * \brief Interface of a base class for a PVT solution * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PVT_SOLUTION_H_ #define GNSS_SDR_PVT_SOLUTION_H_ #if ARMA_NO_BOUND_CHECKING #define ARMA_NO_DEBUG 1 #endif #include #include #include /*! * \brief Base class for a PVT solution * */ class Pvt_Solution { public: Pvt_Solution(); double get_time_offset_s() const; //!< Get RX time offset [s] void set_time_offset_s(double offset); //!< Set RX time offset [s] double get_latitude() const; //!< Get RX position Latitude WGS84 [deg] double get_longitude() const; //!< Get RX position Longitude WGS84 [deg] double get_height() const; //!< Get RX position height WGS84 [m] double get_speed_over_ground() const; //!< Get RX speed over ground [m/s] void set_speed_over_ground(double speed_m_s); //!< Set RX speed over ground [m/s] double get_course_over_ground() const; //!< Get RX course over ground [deg] void set_course_over_ground(double cog_deg); //!< Set RX course over ground [deg] double get_avg_latitude() const; //!< Get RX position averaged Latitude WGS84 [deg] double get_avg_longitude() const; //!< Get RX position averaged Longitude WGS84 [deg] double get_avg_height() const; //!< Get RX position averaged height WGS84 [m] void set_rx_pos(const arma::vec &pos); //!< Set position: Latitude [deg], longitude [deg], height [m] arma::vec get_rx_pos() const; bool is_valid_position() const; void set_valid_position(bool is_valid); boost::posix_time::ptime get_position_UTC_time() const; void set_position_UTC_time(const boost::posix_time::ptime &pt); int get_num_valid_observations() const; //!< Get the number of valid pseudorange observations (valid satellites) void set_num_valid_observations(int num); //!< Set the number of valid pseudorange observations (valid satellites) //averaging void perform_pos_averaging(); void set_averaging_depth(int depth); //!< Set length of averaging window bool is_averaging() const; void set_averaging_flag(bool flag); arma::vec rotateSatellite(double traveltime, const arma::vec &X_sat); /*! * \brief Conversion of Cartesian coordinates (X,Y,Z) to geographical * coordinates (d_latitude_d, d_longitude_d, d_height_m) on a selected reference ellipsoid. * * \param[in] X [m] Cartesian coordinate * \param[in] Y [m] Cartesian coordinate * \param[in] Z [m] Cartesian coordinate * \param[in] elipsoid_selection. Choices of Reference Ellipsoid for Geographical Coordinates: * 0 - International Ellipsoid 1924. * 1 - International Ellipsoid 1967. * 2 - World Geodetic System 1972. * 3 - Geodetic Reference System 1980. * 4 - World Geodetic System 1984. * */ int cart2geo(double X, double Y, double Z, int elipsoid_selection); /*! * \brief Tropospheric correction * * \param[in] sinel - sin of elevation angle of satellite * \param[in] hsta_km - height of station in km * \param[in] p_mb - atmospheric pressure in mb at height hp_km * \param[in] t_kel - surface temperature in degrees Kelvin at height htkel_km * \param[in] hum - humidity in % at height hhum_km * \param[in] hp_km - height of pressure measurement in km * \param[in] htkel_km - height of temperature measurement in km * \param[in] hhum_km - height of humidity measurement in km * * \param[out] ddr_m - range correction (meters) * * * Reference: * Goad, C.C. & Goodman, L. (1974) A Modified Hopfield Tropospheric * Refraction Correction Model. Paper presented at the * American Geophysical Union Annual Fall Meeting, San * Francisco, December 12-17 * * Translated to C++ by Carles Fernandez from a Matlab implementation by Kai Borre */ int tropo(double *ddr_m, double sinel, double hsta_km, double p_mb, double t_kel, double hum, double hp_km, double htkel_km, double hhum_km); private: double d_rx_dt_s; // RX time offset [s] double d_latitude_d; // RX position Latitude WGS84 [deg] double d_longitude_d; // RX position Longitude WGS84 [deg] double d_height_m; // RX position height WGS84 [m] double d_speed_over_ground_m_s; // RX speed over ground [m/s] double d_course_over_ground_d; // RX course over ground [deg] double d_avg_latitude_d; // Averaged latitude in degrees double d_avg_longitude_d; // Averaged longitude in degrees double d_avg_height_m; // Averaged height [m] bool b_valid_position; std::deque d_hist_latitude_d; std::deque d_hist_longitude_d; std::deque d_hist_height_m; bool d_flag_averaging; int d_averaging_depth; // Length of averaging window arma::vec d_rx_pos; boost::posix_time::ptime d_position_UTC_time; int d_valid_observations; }; #endif src/algorithms/PVT/libs/rinex_printer.cc000066400000000000000000017450751352176506000206470ustar00rootroot00000000000000/*! * \file rinex_printer.cc * \brief Implementation of a RINEX 2.11 / 3.02 printer * See http://igscb.jpl.nasa.gov/igscb/data/format/rinex302.pdf * \author Carles Fernandez Prades, 2011. cfernandez(at)cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rinex_printer.h" #include "Beidou_DNAV.h" #include "GLONASS_L1_L2_CA.h" #include "GPS_L1_CA.h" #include "Galileo_E1.h" #include "beidou_dnav_ephemeris.h" #include "beidou_dnav_iono.h" #include "beidou_dnav_utc_model.h" #include "galileo_ephemeris.h" #include "galileo_iono.h" #include "galileo_utc_model.h" #include "glonass_gnav_almanac.h" #include "glonass_gnav_ephemeris.h" #include "glonass_gnav_utc_model.h" #include "gnss_synchro.h" #include "gps_cnav_ephemeris.h" #include "gps_cnav_iono.h" #include "gps_cnav_utc_model.h" #include "gps_ephemeris.h" #include "gps_iono.h" #include "gps_navigation_message.h" #include "gps_utc_model.h" #include #include #include #include #include // for min and max #include #include // for floor #include #include // for cout #include #include #include #include // for getlogin_r() #include #include #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Rinex_Printer::Rinex_Printer(int32_t conf_version, const std::string& base_path) { std::string base_rinex_path = base_path; fs::path full_path(fs::current_path()); const fs::path p(base_rinex_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(base_rinex_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; base_rinex_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { base_rinex_path = p.string(); } if (base_rinex_path != ".") { std::cout << "RINEX files will be stored at " << base_rinex_path << std::endl; } navfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_GPS_NAV"); obsfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_OBS"); sbsfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_SBAS"); navGalfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_GAL_NAV"); navMixfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_MIXED_NAV"); navGlofilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_GLO_NAV"); navBdsfilename = base_rinex_path + fs::path::preferred_separator + Rinex_Printer::createFilename("RINEX_FILE_TYPE_BDS_NAV"); Rinex_Printer::navFile.open(navfilename, std::ios::out | std::ios::in | std::ios::app); Rinex_Printer::obsFile.open(obsfilename, std::ios::out | std::ios::in | std::ios::app); Rinex_Printer::sbsFile.open(sbsfilename, std::ios::out | std::ios::app); Rinex_Printer::navGalFile.open(navGalfilename, std::ios::out | std::ios::in | std::ios::app); Rinex_Printer::navMixFile.open(navMixfilename, std::ios::out | std::ios::in | std::ios::app); Rinex_Printer::navGloFile.open(navGlofilename, std::ios::out | std::ios::in | std::ios::app); Rinex_Printer::navBdsFile.open(navBdsfilename, std::ios::out | std::ios::in | std::ios::app); if (!Rinex_Printer::navFile.is_open() or !Rinex_Printer::obsFile.is_open() or !Rinex_Printer::sbsFile.is_open() or !Rinex_Printer::navGalFile.is_open() or !Rinex_Printer::navMixFile.is_open() or !Rinex_Printer::navGloFile.is_open()) { std::cout << "RINEX files cannot be saved. Wrong permissions?" << std::endl; } // RINEX v3.02 codes satelliteSystem["GPS"] = "G"; satelliteSystem["GLONASS"] = "R"; satelliteSystem["SBAS payload"] = "S"; satelliteSystem["Galileo"] = "E"; satelliteSystem["Beidou"] = "C"; satelliteSystem["Mixed"] = "M"; observationCode["GPS_L1_CA"] = "1C"; // "1C" GPS L1 C/A observationCode["GPS_L1_P"] = "1P"; // "1P" GPS L1 P observationCode["GPS_L1_Z_TRACKING"] = "1W"; // "1W" GPS L1 Z-tracking and similar (AS on) observationCode["GPS_L1_Y"] = "1Y"; // "1Y" GPS L1 Y observationCode["GPS_L1_M "] = "1M"; // "1M" GPS L1 M observationCode["GPS_L1_CODELESS"] = "1N"; // "1N" GPS L1 codeless observationCode["GPS_L2_CA"] = "2C"; // "2C" GPS L2 C/A observationCode["L2_SEMI_CODELESS"] = "2D"; // "2D" GPS L2 L1(C/A)+(P2-P1) semi-codeless observationCode["GPS_L2_L2CM"] = "2S"; // "2S" GPS L2 L2C (M) observationCode["GPS_L2_L2CL"] = "2L"; // "2L" GPS L2 L2C (L) observationCode["GPS_L2_L2CML"] = "2X"; // "2X" GPS L2 L2C (M+L) observationCode["GPS_L2_P"] = "2P"; // "2P" GPS L2 P observationCode["GPS_L2_Z_TRACKING"] = "2W"; // "2W" GPS L2 Z-tracking and similar (AS on) observationCode["GPS_L2_Y"] = "2Y"; // "2Y" GPS L2 Y observationCode["GPS_L2_M"] = "2M"; // "2M" GPS GPS L2 M observationCode["GPS_L2_codeless"] = "2N"; // "2N" GPS L2 codeless observationCode["GPS_L5_I"] = "5I"; // "5I" GPS L5 I observationCode["GPS_L5_Q"] = "5Q"; // "5Q" GPS L5 Q observationCode["GPS_L5_IQ"] = "5X"; // "5X" GPS L5 I+Q observationCode["GLONASS_G1_CA"] = "1C"; // "1C" GLONASS G1 C/A observationCode["GLONASS_G1_P"] = "1P"; // "1P" GLONASS G1 P observationCode["GLONASS_G2_CA"] = "2C"; // "2C" GLONASS G2 C/A (Glonass M) observationCode["GLONASS_G2_P"] = "2P"; // "2P" GLONASS G2 P observationCode["GALILEO_E1_A"] = "1A"; // "1A" GALILEO E1 A (PRS) observationCode["GALILEO_E1_B"] = "1B"; // "1B" GALILEO E1 B (I/NAV OS/CS/SoL) observationCode["GALILEO_E1_C"] = "1C"; // "1C" GALILEO E1 C (no data) observationCode["GALILEO_E1_BC"] = "1X"; // "1X" GALILEO E1 B+C observationCode["GALILEO_E1_ABC"] = "1Z"; // "1Z" GALILEO E1 A+B+C observationCode["GALILEO_E5a_I"] = "5I"; // "5I" GALILEO E5a I (F/NAV OS) observationCode["GALILEO_E5a_Q"] = "5Q"; // "5Q" GALILEO E5a Q (no data) observationCode["GALILEO_E5a_IQ"] = "5X"; // "5X" GALILEO E5a I+Q observationCode["GALILEO_E5b_I"] = "7I"; // "7I" GALILEO E5b I observationCode["GALILEO_E5b_Q"] = "7Q"; // "7Q" GALILEO E5b Q observationCode["GALILEO_E5b_IQ"] = "7X"; // "7X" GALILEO E5b I+Q observationCode["GALILEO_E5_I"] = "8I"; // "8I" GALILEO E5 I observationCode["GALILEO_E5_Q"] = "8Q"; // "8Q" GALILEO E5 Q observationCode["GALILEO_E5_IQ"] = "8X"; // "8X" GALILEO E5 I+Q observationCode["GALILEO_E56_A"] = "6A"; // "6A" GALILEO E6 A observationCode["GALILEO_E56_B"] = "6B"; // "6B" GALILEO E6 B observationCode["GALILEO_E56_B"] = "6C"; // "6C" GALILEO E6 C observationCode["GALILEO_E56_BC"] = "6X"; // "6X" GALILEO E6 B+C observationCode["GALILEO_E56_ABC"] = "6Z"; // "6Z" GALILEO E6 A+B+C observationCode["SBAS_L1_CA"] = "1C"; // "1C" SBAS L1 C/A observationCode["SBAS_L5_I"] = "5I"; // "5I" SBAS L5 I observationCode["SBAS_L5_Q"] = "5Q"; // "5Q" SBAS L5 Q observationCode["SBAS_L5_IQ"] = "5X"; // "5X" SBAS L5 I+Q observationCode["COMPASS_E2_I"] = "2I"; observationCode["COMPASS_E2_Q"] = "2Q"; observationCode["COMPASS_E2_IQ"] = "2X"; observationCode["COMPASS_E5b_I"] = "7I"; observationCode["COMPASS_E5b_Q"] = "7Q"; observationCode["COMPASS_E5b_IQ"] = "7X"; observationCode["COMPASS_E6_I"] = "6I"; observationCode["COMPASS_E6_Q"] = "6Q"; observationCode["COMPASS_E6_IQ"] = "6X"; observationCode["BEIDOU_B1_I"] = "1I"; observationCode["BEIDOU_B1_Q"] = "1Q"; observationCode["BEIDOU_B1_IQ"] = "1X"; observationCode["BEIDOU_B3_I"] = "6I"; observationCode["BEIDOU_B3_Q"] = "6Q"; observationCode["BEIDOU_B3_IQ"] = "6X"; observationType["PSEUDORANGE"] = "C"; observationType["CARRIER_PHASE"] = "L"; observationType["DOPPLER"] = "D"; observationType["SIGNAL_STRENGTH"] = "S"; // RINEX v2.10 and v2.11 codes observationType["PSEUDORANGE_CA_v2"] = "C"; observationType["PSEUDORANGE_P_v2"] = "P"; observationType["CARRIER_PHASE_CA_v2"] = "L"; observationType["DOPPLER_v2"] = "D"; observationType["SIGNAL_STRENGTH_v2"] = "S"; observationCode["GPS_L1_CA_v2"] = "1"; observationCode["GLONASS_G1_CA_v2"] = "1"; if (conf_version == 2) { version = 2; stringVersion = "2.11"; } else { version = 3; stringVersion = "3.02"; } numberTypesObservations = 4; // Number of available types of observable in the system fake_cnav_iode = 1; } Rinex_Printer::~Rinex_Printer() { // close RINEX files int64_t posn, poso, poss, posng, posmn, posnr, posnc; posn = navFile.tellp(); poso = obsFile.tellp(); poss = sbsFile.tellp(); posng = navGalFile.tellp(); posmn = navMixFile.tellp(); posnr = navGloFile.tellp(); posnc = navBdsFile.tellp(); try { Rinex_Printer::navFile.close(); Rinex_Printer::obsFile.close(); Rinex_Printer::sbsFile.close(); Rinex_Printer::navGalFile.close(); Rinex_Printer::navGloFile.close(); Rinex_Printer::navBdsFile.close(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } // If nothing written, erase the files. if (posn == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(navfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (poso == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(obsfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (poss == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(sbsfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (posng == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(navGalfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (posmn == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(navMixfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (posnr == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(navGlofilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } if (posnc == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(navBdsfilename), ec)) { LOG(INFO) << "Error deleting temporary file"; } } } void Rinex_Printer::lengthCheck(const std::string& line) { if (line.length() != 80) { LOG(ERROR) << "Bad defined RINEX line: " << line.length() << " characters (must be 80)" << std::endl << line << std::endl << "----|---1|0---|---2|0---|---3|0---|---4|0---|---5|0---|---6|0---|---7|0---|---8|" << std::endl; } } std::string Rinex_Printer::createFilename(const std::string& type) { const std::string stationName = "GSDR"; // 4-character station name designator boost::gregorian::date today = boost::gregorian::day_clock::local_day(); const int32_t dayOfTheYear = today.day_of_year(); std::stringstream strm0; if (dayOfTheYear < 100) { strm0 << "0"; // three digits for day of the year } if (dayOfTheYear < 10) { strm0 << "0"; // three digits for day of the year } strm0 << dayOfTheYear; std::string dayOfTheYearTag = strm0.str(); std::map fileType; fileType.insert(std::pair("RINEX_FILE_TYPE_OBS", "O")); // O - Observation file. fileType.insert(std::pair("RINEX_FILE_TYPE_GPS_NAV", "N")); // N - GPS navigation message file. fileType.insert(std::pair("RINEX_FILE_TYPE_MET", "M")); // M - Meteorological data file. fileType.insert(std::pair("RINEX_FILE_TYPE_GLO_NAV", "G")); // G - GLONASS navigation file. fileType.insert(std::pair("RINEX_FILE_TYPE_GAL_NAV", "L")); // L - Galileo navigation message file. fileType.insert(std::pair("RINEX_FILE_TYPE_MIXED_NAV", "P")); // P - Mixed GNSS navigation message file. fileType.insert(std::pair("RINEX_FILE_TYPE_GEO_NAV", "H")); // H - SBAS Payload navigation message file. fileType.insert(std::pair("RINEX_FILE_TYPE_SBAS", "B")); // B - SBAS broadcast data file. fileType.insert(std::pair("RINEX_FILE_TYPE_CLK", "C")); // C - Clock file. fileType.insert(std::pair("RINEX_FILE_TYPE_SUMMARY", "S")); // S - Summary file (used e.g., by IGS, not a standard!). fileType.insert(std::pair("RINEX_FILE_TYPE_BDS_NAV", "F")); // G - GLONASS navigation file. boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm pt_tm = boost::posix_time::to_tm(pt); int32_t local_hour = pt_tm.tm_hour; std::stringstream strm; strm << local_hour; std::map Hmap; Hmap.insert(std::pair("0", "a")); Hmap.insert(std::pair("1", "b")); Hmap.insert(std::pair("2", "c")); Hmap.insert(std::pair("3", "d")); Hmap.insert(std::pair("4", "e")); Hmap.insert(std::pair("5", "f")); Hmap.insert(std::pair("6", "g")); Hmap.insert(std::pair("7", "h")); Hmap.insert(std::pair("8", "i")); Hmap.insert(std::pair("9", "j")); Hmap.insert(std::pair("10", "k")); Hmap.insert(std::pair("11", "l")); Hmap.insert(std::pair("12", "m")); Hmap.insert(std::pair("13", "n")); Hmap.insert(std::pair("14", "o")); Hmap.insert(std::pair("15", "p")); Hmap.insert(std::pair("16", "q")); Hmap.insert(std::pair("17", "r")); Hmap.insert(std::pair("18", "s")); Hmap.insert(std::pair("19", "t")); Hmap.insert(std::pair("20", "u")); Hmap.insert(std::pair("21", "v")); Hmap.insert(std::pair("22", "w")); Hmap.insert(std::pair("23", "x")); std::string hourTag = Hmap[strm.str()]; int32_t local_minute = pt_tm.tm_min; std::stringstream strm2; if (local_minute < 10) { strm2 << "0"; // at least two digits for minutes } strm2 << local_minute; std::string minTag = strm2.str(); int32_t local_year = pt_tm.tm_year - 100; // 2012 is 112 std::stringstream strm3; strm3 << local_year; std::string yearTag = strm3.str(); std::string typeOfFile = fileType[type]; std::string filename(stationName + dayOfTheYearTag + hourTag + minTag + "." + yearTag + typeOfFile); return filename; } std::string Rinex_Printer::getLocalTime() { std::string line; line += std::string("GNSS-SDR"); line += std::string(12, ' '); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += Rinex_Printer::leftJustify(username, 20); boost::gregorian::date today = boost::gregorian::day_clock::local_day(); boost::local_time::time_zone_ptr zone(new boost::local_time::posix_time_zone("UTC")); boost::local_time::local_date_time pt = boost::local_time::local_sec_clock::local_time(zone); tm pt_tm = boost::local_time::to_tm(pt); std::stringstream strmHour; int32_t utc_hour = pt_tm.tm_hour; if (utc_hour < 10) { strmHour << "0"; // two digits for hours } strmHour << utc_hour; std::stringstream strmMin; int32_t utc_minute = pt_tm.tm_min; if (utc_minute < 10) { strmMin << "0"; // two digits for minutes } strmMin << utc_minute; if (version == 2) { int32_t day = pt_tm.tm_mday; line += Rinex_Printer::rightJustify(std::to_string(day), 2); line += std::string("-"); std::map months; months[0] = "JAN"; months[1] = "FEB"; months[2] = "MAR"; months[3] = "APR"; months[4] = "MAY"; months[5] = "JUN"; months[6] = "JUL"; months[7] = "AUG"; months[8] = "SEP"; months[9] = "OCT"; months[10] = "NOV"; months[11] = "DEC"; line += months[pt_tm.tm_mon]; line += std::string("-"); line += std::to_string(pt_tm.tm_year - 100); line += std::string(1, ' '); line += strmHour.str(); line += std::string(":"); line += strmMin.str(); line += std::string(5, ' '); } if (version == 3) { line += boost::gregorian::to_iso_string(today); line += std::string(1, ' '); line += strmHour.str(); line += strmMin.str(); std::stringstream strm2; int32_t utc_seconds = pt_tm.tm_sec; if (utc_seconds < 10) { strm2 << "0"; // two digits for seconds } strm2 << utc_seconds; line += strm2.str(); line += std::string(1, ' '); line += std::string("UTC"); line += std::string(1, ' '); } return line; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("R: GLONASS"); line += std::string(10, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GLONASS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction if (version == 3) { line.clear(); line += std::string("GLUT"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GLGP"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 2) { // Set reference time and its clock corrections boost::posix_time::ptime p_utc_ref_time = glonass_gnav_eph.glot_to_utc(glonass_gnav_eph.d_t_b, 0.0); std::string timestring = boost::posix_time::to_iso_string(p_utc_ref_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); line.clear(); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += std::string(3, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 19, 2), 19); line += std::string(20, ' '); line += Rinex_Printer::leftJustify("CORR TO SYSTEM TIME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning std::string line; stringVersion = "3.02"; version = 3; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 1 line.clear(); line += std::string("GLUT"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GLGP"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 3 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 2048), 5); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } } else { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 1024), 5); // valid from 2009 to 2019 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } } line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_iono, const Gps_CNAV_Utc_Model& gps_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning std::string line; stringVersion = "3.02"; version = 3; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 1 line.clear(); line += std::string("GLUT"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GLGP"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 3 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning // Avoid compiler warning, there is not time system correction between Galileo and GLONASS if (galileo_utc_model.A_0G_10) { } std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GAL "); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction line.clear(); line += std::string("GAUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 1 line.clear(); line += std::string("GLUT"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLS_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_LSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.DN_6), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Galileo_Iono& iono, const Galileo_Utc_Model& utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("E: GALILEO"); line += std::string(10, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GALILEO NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GAL "); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.ai0_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.ai1_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.ai2_5, 10, 2), 12); double zero = 0.0; line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction line.clear(); line += std::string("GAUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A0_6, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A1_6, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(utc_model.t0t_6), 7); line += Rinex_Printer::rightJustify(std::to_string(utc_model.WNot_6), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GPGA"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A_0G_10, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A_1G_10, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(utc_model.t_0G_10), 7); line += Rinex_Printer::rightJustify(std::to_string(utc_model.WN_0G_10), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(utc_model.Delta_tLS_6), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.Delta_tLSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.WN_LSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.DN_6), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& iono, const Gps_CNAV_Utc_Model& utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("G: GPS"); line += std::string(14, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::leftJustify("GPS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("GPSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T), 5); /* if ( SBAS ) { line += string(1, ' '); line += leftJustify(asString(d_t_OT_SBAS),5); line += string(1, ' '); line += leftJustify(asString(d_WN_T_SBAS),2); line += string(1, ' '); } else */ line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& iono, const Gps_CNAV_Utc_Model& utc_model, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GAL "); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 3 line.clear(); line += std::string("GPSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction line.clear(); line += std::string("GAUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_Iono& iono, const Gps_Utc_Model& utc_model, const Gps_Ephemeris& eph) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); if (version == 2) { line += std::string("N: GPS NAV DATA"); line += std::string(25, ' '); } if (version == 3) { line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); //todo Add here other systems... line += std::string("G: GPS"); line += std::string(14, ' '); // ... } line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::leftJustify("GPS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); if (version == 2) { line += std::string(2, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("ION ALPHA", 20); } if (version == 3) { line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); if (version == 2) { line += std::string(2, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("ION BETA", 20); } if (version == 3) { line += std::string("GPSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); if (version == 2) { line += std::string(3, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 18, 2), 19); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 18, 2), 19); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 9); if (eph.i_GPS_week < 512) { if (utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 2048), 9); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 9); // valid from 2019 to 2029 } } else { if (utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 1024), 9); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 9); // valid from 2009 to 2019 } } line += std::string(1, ' '); line += Rinex_Printer::leftJustify("DELTA-UTC: A0,A1,T,W", 20); } if (version == 3) { line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { if (utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 2048), 5); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } } else { if (utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 1024), 5); // valid from 2009 to 2019 } else { line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } } /* if ( SBAS ) { line += string(1, ' '); line += leftJustify(asString(d_t_OT_SBAS),5); line += string(1, ' '); line += leftJustify(asString(d_WN_T_SBAS),2); line += string(1, ' '); } else */ line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); if (version == 2) { line += std::string(54, ' '); } if (version == 3) { line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line += std::string(36, ' '); } line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GAL "); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction line.clear(); line += std::string("GAUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 2 line.clear(); line += std::string("GPGA"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_0G_10, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_1G_10, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t_0G_10), 7); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_0G_10), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 3 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 2048), 5); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } } else { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(eph.i_GPS_week + 1024), 5); // valid from 2009 to 2019 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } } line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Beidou_Dnav_Iono& iono, const Beidou_Dnav_Utc_Model& utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); if (version == 3) { line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); //todo: Add here other systems... line += std::string("F: BDS"); line += std::string(14, ' '); // ... } line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::leftJustify("BDS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1, only version 3 supported line.clear(); line += std::string("BDSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("BDSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); line += std::string("BDUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0_UTC, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1_UTC, 15, 2), 16); line += std::string(22, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& gps_eph, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1, only version 3 supported line.clear(); line += std::string("BDSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("BDSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); line += std::string("BDUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A0_UTC, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A1_UTC, 15, 2), 16); line += std::string(22, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 3 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); if (gps_eph.i_GPS_week < 512) { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(gps_eph.i_GPS_week + 2048), 5); // valid from 2019 to 2029 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (gps_eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } } else { if (gps_utc_model.i_WN_T == 0) { line += Rinex_Printer::rightJustify(std::to_string(gps_eph.i_GPS_week + 1024), 5); // valid from 2009 to 2019 } else { line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (gps_eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } } line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_cnav_iono, const Gps_CNAV_Utc_Model& gps_cnav_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model) { std::string line; stringVersion = "3.02"; version = 3; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1, only version 3 supported line.clear(); line += std::string("BDSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("BDSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GPSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); line += std::string("BDUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A0_UTC, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A1_UTC, 15, 2), 16); line += std::string(22, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 3 line.clear(); line += std::string("GPUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_utc_model.d_A0, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_cnav_utc_model.d_A1, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.d_t_OT), 7); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.i_WN_T), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(gps_cnav_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glo_gnav_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1, only version 3 supported line.clear(); line += std::string("BDSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("BDSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 5 system time correction line.clear(); line += std::string("BDUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A0_UTC, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A1_UTC, 15, 2), 16); line += std::string(22, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 1 line.clear(); line += std::string("GLUT"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glo_gnav_utc_model.d_tau_c, 16, 2), 17); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(bds_dnav_utc_model.d_DeltaT_LS), 6); line += Rinex_Printer::rightJustify(std::to_string(bds_dnav_utc_model.d_DeltaT_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(bds_dnav_utc_model.i_WN_LSF), 6); line += Rinex_Printer::rightJustify(std::to_string(bds_dnav_utc_model.i_DN), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += std::string("N: GNSS NAV DATA"); line += std::string(4, ' '); line += std::string("M: MIXED"); line += std::string(12, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GNSS NAVIGATION MESSAGE FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1 line.clear(); line += std::string("GAL "); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 1, only version 3 supported line.clear(); line += std::string("BDSA"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_alpha3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line ionospheric info 2 line.clear(); line += std::string("BDSB"); line += std::string(1, ' '); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta0, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta1, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta2, 10, 2), 12); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_iono.d_beta3, 10, 2), 12); line += std::string(7, ' '); line += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction line.clear(); line += std::string("GAUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line += std::string(10, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line system time correction 1 // -------- Line 5 system time correction line.clear(); line += std::string("BDUT"); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A0_UTC, 16, 2), 18); line += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(bds_dnav_utc_model.d_A1_UTC, 15, 2), 16); line += std::string(22, ' '); line += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 6 leap seconds // For leap second information, see http://www.endruntechnologies.com/leap.htm line.clear(); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLS_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_LSF_6), 6); line += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.DN_6), 6); line += std::string(36, ' '); line += Rinex_Printer::leftJustify("LEAP SECONDS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_sbs_header(std::fstream& out) { std::string line; // -------- Line 1 line.clear(); line = std::string(5, ' '); line += std::string("2.10"); line += std::string(11, ' '); line += Rinex_Printer::leftJustify("B SBAS DATA", 20); line += std::string(20, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += Rinex_Printer::leftJustify(username, 20); // Date of file creation (dd-mmm-yy hhmm) boost::local_time::time_zone_ptr zone(new boost::local_time::posix_time_zone("UTC")); boost::local_time::local_date_time pt = boost::local_time::local_sec_clock::local_time(zone); tm pt_tm = boost::local_time::to_tm(pt); std::stringstream strYear; int32_t utc_year = pt.date().year(); utc_year -= 2000; // two digits for year strYear << utc_year; std::stringstream strMonth; int32_t utc_month = pt.date().month().as_number(); if (utc_month < 10) { strMonth << "0"; // two digits for months } strMonth << utc_month; std::stringstream strmDay; int32_t utc_day = pt.date().day().as_number(); if (utc_day < 10) { strmDay << "0"; // two digits for days } strmDay << utc_day; std::stringstream strmHour; int32_t utc_hour = pt_tm.tm_hour; if (utc_hour < 10) { strmHour << "0"; // two digits for hours } strmHour << utc_hour; std::stringstream strmMin; int32_t utc_minute = pt_tm.tm_min; if (utc_minute < 10) { strmMin << "0"; // two digits for minutes } strmMin << utc_minute; std::string time_str; time_str += strmDay.str(); time_str += "-"; time_str += strMonth.str(); time_str += "-"; time_str += strYear.str(); time_str += " "; time_str += strmHour.str(); time_str += strmMin.str(); line += Rinex_Printer::leftJustify(time_str, 20); line += Rinex_Printer::leftJustify("PGM / RUN BY / DATE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("REC INDEX/TYPE/VERS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT 1 line.clear(); line += Rinex_Printer::leftJustify("BROADCAST DATA FILE FOR GEO SV, GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT 2 line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- End of Header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if ((line_str.find("GLUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLGP", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLGP"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navGlofilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navGlofilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& utc_model) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if ((line_str.find("GAL", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GAL "); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GAUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GAUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A0_6, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A1_6, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.t0t_6), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.WNot_6), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPGA", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPGA"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A_0G_10, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.A_1G_10, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.t_0G_10), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.WN_0G_10), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.Delta_tLS_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.Delta_tLSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.WN_LSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.DN_6), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navGalfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navGalfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_Utc_Model& utc_model, const Gps_Iono& iono, const Gps_Ephemeris& eph) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (version == 2) { if (line_str.find("ION ALPHA", 59) != std::string::npos) { line_aux += std::string(2, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("ION ALPHA", 20); data.push_back(line_aux); } else if (line_str.find("ION BETA", 59) != std::string::npos) { line_aux += std::string(2, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("ION BETA", 20); data.push_back(line_aux); } else if (line_str.find("DELTA-UTC", 59) != std::string::npos) { line_aux += std::string(3, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 18, 2), 19); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 18, 2), 19); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 9); if (eph.i_GPS_week < 512) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 9); // valid from 2019 to 2029 } else { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 9); // valid from 2009 to 2019 } line_aux += std::string(1, ' '); line_aux += Rinex_Printer::leftJustify("DELTA-UTC: A0,A1,T,W", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += std::string(54, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } if (version == 3) { if (line_str.find("GPSA", 0) != std::string::npos) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("GPSB", 0) != std::string::npos) { line_aux += std::string("GPSB"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("GPUT", 0) != std::string::npos) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } else { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } } else { data.push_back(line_str); } } out.close(); out.open(navfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model, const Gps_CNAV_Iono& iono) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("GPSA", 0) != std::string::npos) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("GPSB", 0) != std::string::npos) { line_aux += std::string("GPSB"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("GPUT", 0) != std::string::npos) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model, const Gps_CNAV_Iono& iono, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if ((line_str.find("GAL", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GAL "); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPSA", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPSB", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GPSB"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GAUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GAUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPGA", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPGA"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_0G_10, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_1G_10, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t_0G_10), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_0G_10), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("GPUT", 0) != std::string::npos) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_t_OT), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_T), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("GPSA", 0) != std::string::npos) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GAL", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GAL "); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPSB", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GPSB"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_beta3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } else { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GAUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GAUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPGA", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPGA"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_0G_10, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A_1G_10, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t_0G_10), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_0G_10), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navMixfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navMixfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("GPSA", 0) != std::string::npos) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); if (eph.i_GPS_week < 512) { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 2048), 5); // valid from 2019 to 2029 } else { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T + (eph.i_GPS_week / 256) * 256 + 1024), 5); // valid from 2009 to 2019 } line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLGP", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLGP"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navMixfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navMixfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_iono, const Gps_CNAV_Utc_Model& gps_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("GPSA", 0) != std::string::npos) { line_aux += std::string("GPSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GPUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GPUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A0, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(gps_utc_model.d_A1, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_t_OT), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_T), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLGP", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLGP"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_gps, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(gps_utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navMixfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navMixfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac) { if (glonass_gnav_almanac.i_satellite_freq_channel) { } // Avoid compiler warning // Avoid compiler warning, there is not time system correction between Galileo and GLONASS if (galileo_utc_model.A_0G_10) { } std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if ((line_str.find("GAL", 0) != std::string::npos) && (line_str.find("IONOSPHERIC CORR", 59) != std::string::npos)) { line_aux += std::string("GAL "); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai0_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai1_5, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_iono.ai2_5, 10, 2), 12); double zero = 0.0; line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(zero, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GAUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GAUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A0_6, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(galileo_utc_model.A1_6, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.t0t_6), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WNot_6), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if ((line_str.find("GLUT", 0) != std::string::npos) && (line_str.find("TIME SYSTEM CORR", 59) != std::string::npos)) { line_aux += std::string("GLUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(glonass_gnav_utc_model.d_tau_c, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(0.0, 15, 2), 16); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 7); line_aux += Rinex_Printer::rightJustify(std::to_string(0.0), 5); line_aux += std::string(10, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLS_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_LSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.DN_6), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navMixfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navMixfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::update_nav_header(std::fstream& out, const Beidou_Dnav_Utc_Model& utc_model, const Beidou_Dnav_Iono& iono) { std::vector data; std::string line_aux; int64_t pos = out.tellp(); out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("BDSA", 0) != std::string::npos) { line_aux += std::string("BDSA"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_alpha3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("BDSB", 0) != std::string::npos) { line_aux += std::string("BDSB"); line_aux += std::string(1, ' '); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta0, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta1, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta2, 10, 2), 12); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(iono.d_beta3, 10, 2), 12); line_aux += std::string(7, ' '); line_aux += Rinex_Printer::leftJustify("IONOSPHERIC CORR", 20); data.push_back(line_aux); } else if (line_str.find("BDUT", 0) != std::string::npos) { line_aux += std::string("BDUT"); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A0_UTC, 16, 2), 18); line_aux += Rinex_Printer::rightJustify(Rinex_Printer::doub2for(utc_model.d_A1_UTC, 15, 2), 16); line_aux += std::string(22, ' '); line_aux += Rinex_Printer::leftJustify("TIME SYSTEM CORR", 20); data.push_back(line_aux); } else if (line_str.find("LEAP SECONDS", 59) != std::string::npos) { line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(navfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(navfilename, std::ios::out | std::ios::app); out.seekp(pos); std::cout << "The RINEX Navigation file header has been updated with UTC and IONO info." << std::endl; } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& eph_map) { std::string line; std::map::const_iterator gps_ephemeris_iter; for (gps_ephemeris_iter = eph_map.cbegin(); gps_ephemeris_iter != eph_map.cend(); gps_ephemeris_iter++) { // -------- SV / EPOCH / SV CLK boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_GPS_time(gps_ephemeris_iter->second, gps_ephemeris_iter->second.d_Toc); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string seconds(timestring, 13, 2); if (version == 2) { line += Rinex_Printer::rightJustify(std::to_string(gps_ephemeris_iter->second.i_satellite_PRN), 2); line += std::string(1, ' '); std::string year(timestring, 2, 2); line += year; line += std::string(1, ' '); if (boost::lexical_cast(month) < 10) { line += std::string(1, ' '); line += std::string(month, 1, 1); } else { line += month; } line += std::string(1, ' '); if (boost::lexical_cast(day) < 10) { line += std::string(1, ' '); line += std::string(day, 1, 1); } else { line += day; } line += std::string(1, ' '); if (boost::lexical_cast(hour) < 10) { line += std::string(1, ' '); line += std::string(hour, 1, 1); } else { line += hour; } line += std::string(1, ' '); if (boost::lexical_cast(minutes) < 10) { line += std::string(1, ' '); line += std::string(minutes, 1, 1); } else { line += minutes; } line += std::string(1, ' '); if (boost::lexical_cast(seconds) < 10) { line += std::string(1, ' '); line += std::string(seconds, 1, 1); } else { line += seconds; } line += std::string(1, '.'); std::string decimal = std::string("0"); if (timestring.size() > 16) { std::string decimal(timestring, 16, 1); } line += decimal; line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f2, 18, 2); line += std::string(1, ' '); } if (version == 3) { line += satelliteSystem["GPS"]; if (gps_ephemeris_iter->second.i_satellite_PRN < 10) { line += std::string("0"); } line += std::to_string(gps_ephemeris_iter->second.i_satellite_PRN); std::string year(timestring, 0, 4); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); line += seconds; line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f2, 18, 2); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 1 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } // If there is a discontinued reception the ephemeris is not validated if (gps_ephemeris_iter->second.d_IODE_SF2 == gps_ephemeris_iter->second.d_IODE_SF3) { line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_IODE_SF2, 18, 2); } else { LOG(WARNING) << "Discontinued reception of Frame 2 and 3"; } line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Crs, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Delta_n, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_M_0, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 2 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cuc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_e_eccentricity, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cus, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_sqrt_A, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 3 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Toe, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cic, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_OMEGA0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cis, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 4 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_i_0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Crc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_OMEGA, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_OMEGA_DOT, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 5 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_IDOT, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_code_on_L2), 18, 2); line += std::string(1, ' '); double GPS_week_continuous_number; if (gps_ephemeris_iter->second.i_GPS_week < 512) { GPS_week_continuous_number = static_cast(gps_ephemeris_iter->second.i_GPS_week + 2048); // valid until 2029 } else { GPS_week_continuous_number = static_cast(gps_ephemeris_iter->second.i_GPS_week + 1024); // valid until April 7, 2019 (check http://www.colorado.edu/geography/gcraft/notes/gps/gpseow.htm) } // This week goes with Toe. This is different from the GPS week in the original satellite message! if (gps_ephemeris_iter->second.d_Toe < 7200.0) { GPS_week_continuous_number += 1.0; } line += Rinex_Printer::doub2for(GPS_week_continuous_number, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_code_on_L2), 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 6 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_SV_accuracy), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_SV_health), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_TGD, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_IODC, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 7 line.clear(); if (version == 2) { line += std::string(4, ' '); } if (version == 3) { line += std::string(5, ' '); } double tx_time_of_message = gps_ephemeris_iter->second.d_TOW; if (gps_ephemeris_iter->second.d_Toe < 7200.0) { tx_time_of_message -= 604800.0; // see RINEX 3.03 section 6.13 } line += Rinex_Printer::doub2for(tx_time_of_message, 18, 2); line += std::string(1, ' '); int curve_fit_interval = 4; if (gps_ephemeris_iter->second.satelliteBlock.at(gps_ephemeris_iter->second.i_satellite_PRN) == "IIA") { // Block II/IIA (Table 20-XI IS-GPS-200E ) if ((gps_ephemeris_iter->second.d_IODC > 239) && (gps_ephemeris_iter->second.d_IODC < 248)) { curve_fit_interval = 8; } if (((gps_ephemeris_iter->second.d_IODC > 247) && (gps_ephemeris_iter->second.d_IODC < 256)) || (gps_ephemeris_iter->second.d_IODC == 496)) { curve_fit_interval = 14; } if ((gps_ephemeris_iter->second.d_IODC > 496) && (gps_ephemeris_iter->second.d_IODC < 504)) { curve_fit_interval = 26; } if ((gps_ephemeris_iter->second.d_IODC > 503) && (gps_ephemeris_iter->second.d_IODC < 511)) { curve_fit_interval = 50; } if (((gps_ephemeris_iter->second.d_IODC > 751) && (gps_ephemeris_iter->second.d_IODC < 757)) || (gps_ephemeris_iter->second.d_IODC == 511)) { curve_fit_interval = 74; } if (gps_ephemeris_iter->second.d_IODC == 757) { curve_fit_interval = 98; } } if ((gps_ephemeris_iter->second.satelliteBlock.at(gps_ephemeris_iter->second.i_satellite_PRN) == "IIR") || (gps_ephemeris_iter->second.satelliteBlock.at(gps_ephemeris_iter->second.i_satellite_PRN) == "IIR-M") || (gps_ephemeris_iter->second.satelliteBlock.at(gps_ephemeris_iter->second.i_satellite_PRN) == "IIF") || (gps_ephemeris_iter->second.satelliteBlock.at(gps_ephemeris_iter->second.i_satellite_PRN) == "IIIA")) { // Block IIR/IIR-M/IIF/IIIA (Table 20-XII IS-GPS-200E ) if ((gps_ephemeris_iter->second.d_IODC > 239) && (gps_ephemeris_iter->second.d_IODC < 248)) { curve_fit_interval = 8; } if (((gps_ephemeris_iter->second.d_IODC > 247) && (gps_ephemeris_iter->second.d_IODC < 256)) || (gps_ephemeris_iter->second.d_IODC == 496)) { curve_fit_interval = 14; } if (((gps_ephemeris_iter->second.d_IODC > 496) && (gps_ephemeris_iter->second.d_IODC < 504)) || ((gps_ephemeris_iter->second.d_IODC > 1020) && (gps_ephemeris_iter->second.d_IODC < 1024))) { curve_fit_interval = 26; } } if (curve_fit_interval == 4) { line += Rinex_Printer::doub2for(0.0, 18, 2); } else { line += Rinex_Printer::doub2for(1.0, 18, 2); } line += std::string(1, ' '); line += std::string(18, ' '); // spare line += std::string(1, ' '); line += std::string(18, ' '); // spare if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); } } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& eph_map) { std::string line; std::map::const_iterator gps_ephemeris_iter; for (gps_ephemeris_iter = eph_map.cbegin(); gps_ephemeris_iter != eph_map.cend(); gps_ephemeris_iter++) { // -------- SV / EPOCH / SV CLK boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_GPS_time(gps_ephemeris_iter->second, gps_ephemeris_iter->second.d_Toc); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string seconds(timestring, 13, 2); line += satelliteSystem["GPS"]; if (gps_ephemeris_iter->second.i_satellite_PRN < 10) { line += std::string("0"); } line += std::to_string(gps_ephemeris_iter->second.i_satellite_PRN); std::string year(timestring, 0, 4); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); line += seconds; line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_A_f2, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 1 line.clear(); line += std::string(5, ' '); // If there is no IODE in CNAV, so we check if Toe in message Type 10, Toe in Message type 11 and Toc in message types 30-37. // Whenever these three terms do not match, a data set cutover has occurred and new data must be collected. // See IS-GPS-200H, p. 155 if (!((gps_ephemeris_iter->second.d_Toe1 == gps_ephemeris_iter->second.d_Toe2) && (gps_ephemeris_iter->second.d_Toe1 == gps_ephemeris_iter->second.d_Toc))) // Toe1: Toe in message type 10, Toe2: Toe in message type 11 { // Toe1: Toe in message type 10, Toe2: Toe in message type 11, fake_cnav_iode = fake_cnav_iode + 1; if (fake_cnav_iode == 240) { fake_cnav_iode = 1; } } line += Rinex_Printer::doub2for(fake_cnav_iode, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Crs, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Delta_n, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_M_0, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 2 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cuc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_e_eccentricity, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cus, 18, 2); line += std::string(1, ' '); const double A_REF = 26559710.0; // See IS-GPS-200H, pp. 163 double sqrt_A = sqrt(A_REF + gps_ephemeris_iter->second.d_DELTA_A); line += Rinex_Printer::doub2for(sqrt_A, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 3 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(std::max(gps_ephemeris_iter->second.d_Toe1, gps_ephemeris_iter->second.d_Toe2), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cic, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_OMEGA0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Cis, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 4 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_i_0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_Crc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_OMEGA, 18, 2); line += std::string(1, ' '); const double OMEGA_DOT_REF = -2.6e-9; // semicircles / s, see IS-GPS-200H pp. 164 double OMEGA_DOT = OMEGA_DOT_REF + gps_ephemeris_iter->second.d_DELTA_OMEGA_DOT; line += Rinex_Printer::doub2for(OMEGA_DOT, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 5 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_IDOT, 18, 2); line += std::string(1, ' '); // No data flag for L2 P code double my_zero = 0.0; line += Rinex_Printer::doub2for(my_zero, 18, 2); line += std::string(1, ' '); auto GPS_week_continuous_number = static_cast(gps_ephemeris_iter->second.i_GPS_week); // This week goes with Toe. This is different from the GPS week in the original satellite message! if (gps_ephemeris_iter->second.d_Toe1 < 7200.0) { GPS_week_continuous_number += 1.0; } line += Rinex_Printer::doub2for(GPS_week_continuous_number, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(my_zero, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 6 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_URA), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(static_cast(gps_ephemeris_iter->second.i_signal_health), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_TGD, 18, 2); line += std::string(1, ' '); // no IODC in CNAV, so we fake it (see above) line += Rinex_Printer::doub2for(fake_cnav_iode, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 7 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(gps_ephemeris_iter->second.d_TOW, 18, 2); line += std::string(1, ' '); double curve_fit_interval = 0.0; /// ?? Not defined in CNAV line += Rinex_Printer::doub2for(curve_fit_interval, 18, 2); line += std::string(1, ' '); line += std::string(18, ' '); // spare line += std::string(1, ' '); line += std::string(18, ' '); // spare Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); } } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& eph_map) { std::string line; std::map::const_iterator galileo_ephemeris_iter; line.clear(); for (galileo_ephemeris_iter = eph_map.cbegin(); galileo_ephemeris_iter != eph_map.cend(); galileo_ephemeris_iter++) { // -------- SV / EPOCH / SV CLK boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_Galileo_time(galileo_ephemeris_iter->second, galileo_ephemeris_iter->second.t0e_1); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string seconds(timestring, 13, 2); line += satelliteSystem["Galileo"]; if (galileo_ephemeris_iter->second.i_satellite_PRN < 10) { line += std::string("0"); } line += std::to_string(galileo_ephemeris_iter->second.i_satellite_PRN); std::string year(timestring, 0, 4); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); line += seconds; line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.af0_4, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.af1_4, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.af2_4, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 1 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(static_cast(galileo_ephemeris_iter->second.IOD_ephemeris), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_rs_3, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.delta_n_3, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.M0_1, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 2 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_uc_3, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.e_1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_us_3, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.A_1, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 3 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.t0e_1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_ic_4, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.OMEGA_0_2, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_is_4, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 4 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.i_0_2, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.C_rc_3, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.omega_2, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.OMEGA_dot_3, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 5 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.iDot_2, 18, 2); line += std::string(1, ' '); //double one = 1.0; // INAV E1-B std::string iNAVE1B("1000000001"); int32_t data_source_INAV = Rinex_Printer::toInt(iNAVE1B, 10); line += Rinex_Printer::doub2for(static_cast(data_source_INAV), 18, 2); line += std::string(1, ' '); auto GST_week = static_cast(galileo_ephemeris_iter->second.WN_5); double num_GST_rollovers = floor((GST_week + 1024.0) / 4096.0); double Galileo_week_continuous_number = GST_week + 1024.0 + num_GST_rollovers * 4096.0; line += Rinex_Printer::doub2for(Galileo_week_continuous_number, 18, 2); line += std::string(1, ' '); double zero = 0.0; line += Rinex_Printer::doub2for(zero, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 6 line.clear(); line += std::string(5, ' '); // line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.SISA_3, 18, 2); line += Rinex_Printer::doub2for(zero, 18, 2); // *************** CHANGE THIS WHEN GALILEO SIGNAL IS VALID line += std::string(1, ' '); std::string E1B_HS; std::string E5B_HS; if (galileo_ephemeris_iter->second.E1B_HS_5 == 0) { E1B_HS = "00"; } if (galileo_ephemeris_iter->second.E1B_HS_5 == 1) { E1B_HS = "01"; } if (galileo_ephemeris_iter->second.E1B_HS_5 == 2) { E1B_HS = "10"; } if (galileo_ephemeris_iter->second.E1B_HS_5 == 3) { E1B_HS = "11"; } if (galileo_ephemeris_iter->second.E5b_HS_5 == 0) { E5B_HS = "00"; } if (galileo_ephemeris_iter->second.E5b_HS_5 == 1) { E5B_HS = "01"; } if (galileo_ephemeris_iter->second.E5b_HS_5 == 2) { E5B_HS = "10"; } if (galileo_ephemeris_iter->second.E5b_HS_5 == 3) { E5B_HS = "11"; } if (E1B_HS == "11") { LOG(WARNING) << "Signal Component currently in Test"; } if (E1B_HS == "10") { LOG(WARNING) << "Signal will be out of service"; } if (E1B_HS == "01") { LOG(WARNING) << "Signal out of service"; } E1B_HS = "00"; // *************** CHANGE THIS WHEN GALILEO SIGNAL IS VALID std::string E1B_DVS = std::to_string(galileo_ephemeris_iter->second.E1B_DVS_5); if (E1B_DVS == "1") { LOG(WARNING) << "Navigation data without guarantee"; } E1B_DVS = "0"; // *************** CHANGE THIS WHEN GALILEO SIGNAL IS VALID std::string SVhealth_str = E5B_HS + std::to_string(galileo_ephemeris_iter->second.E5b_DVS_5) + "11" + "1" + E1B_DVS + E1B_HS + std::to_string(galileo_ephemeris_iter->second.E1B_DVS_5); SVhealth_str = "000000000"; // *************** CHANGE THIS WHEN GALILEO SIGNAL IS VALID int32_t SVhealth = Rinex_Printer::toInt(SVhealth_str, 9); line += Rinex_Printer::doub2for(static_cast(SVhealth), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.BGD_E1E5a_5, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.BGD_E1E5b_5, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 7 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(galileo_ephemeris_iter->second.TOW_5, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(zero, 18, 2); line += std::string(1, ' '); line += std::string(18, ' '); // spare line += std::string(1, ' '); line += std::string(18, ' '); // spare Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); } } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& eph_map) { std::string line; std::map::const_iterator glonass_gnav_ephemeris_iter; for (glonass_gnav_ephemeris_iter = eph_map.cbegin(); glonass_gnav_ephemeris_iter != eph_map.cend(); glonass_gnav_ephemeris_iter++) { // -------- SV / EPOCH / SV CLK boost::posix_time::ptime p_utc_time = glonass_gnav_ephemeris_iter->second.glot_to_utc(glonass_gnav_ephemeris_iter->second.d_t_b, 0.0); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string seconds(timestring, 13, 2); if (version == 2) { line += Rinex_Printer::rightJustify(std::to_string(glonass_gnav_ephemeris_iter->second.i_satellite_PRN), 2); line += std::string(1, ' '); std::string year(timestring, 2, 2); line += year; line += std::string(1, ' '); if (boost::lexical_cast(month) < 10) { line += std::string(1, ' '); line += std::string(month, 1, 1); } else { line += month; } line += std::string(1, ' '); if (boost::lexical_cast(day) < 10) { line += std::string(1, ' '); line += std::string(day, 1, 1); } else { line += day; } line += std::string(1, ' '); if (boost::lexical_cast(hour) < 10) { line += std::string(1, ' '); line += std::string(hour, 1, 1); } else { line += hour; } line += std::string(1, ' '); if (boost::lexical_cast(minutes) < 10) { line += std::string(1, ' '); line += std::string(minutes, 1, 1); } else { line += minutes; } line += std::string(1, ' '); if (boost::lexical_cast(seconds) < 10) { line += std::string(1, ' '); line += std::string(seconds, 1, 1); } else { line += seconds; } line += std::string(1, '.'); std::string decimal = std::string("0"); if (timestring.size() > 16) { std::string decimal(timestring, 16, 1); } line += decimal; line += std::string(1, ' '); line += Rinex_Printer::doub2for(-glonass_gnav_ephemeris_iter->second.d_tau_c, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_gamma_n, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_t_k, 18, 2); line += std::string(1, ' '); } if (version == 3) { line += satelliteSystem["GLONASS"]; if (glonass_gnav_ephemeris_iter->second.i_satellite_PRN < 10) { line += std::string("0"); } line += std::to_string(glonass_gnav_ephemeris_iter->second.i_satellite_PRN); std::string year(timestring, 0, 4); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); line += seconds; line += std::string(1, ' '); line += Rinex_Printer::doub2for(-glonass_gnav_ephemeris_iter->second.d_tau_n, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(+glonass_gnav_ephemeris_iter->second.d_gamma_n, 18, 2); line += std::string(1, ' '); //TODO need to define this here. what is nd line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_t_k + p_utc_time.date().day_of_week() * 86400, 18, 2); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 1 line.clear(); // TODO Why is this happening here?. The extra space maybe is intended to help with readability if (version == 2) { line += std::string(3, ' '); } if (version == 3) { line += std::string(4, ' '); } line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_Xn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_VXn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_AXn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_B_n, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 2 line.clear(); if (version == 2) { line += std::string(3, ' '); } if (version == 3) { line += std::string(4, ' '); } line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_Yn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_VYn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_AYn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.i_satellite_freq_channel, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 3 line.clear(); if (version == 2) { line += std::string(3, ' '); } if (version == 3) { line += std::string(4, ' '); } line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_Zn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_VZn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_AZn, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(glonass_gnav_ephemeris_iter->second.d_E_n, 18, 2); if (version == 2) { line += std::string(1, ' '); } Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); } } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& galileo_eph_map) { version = 3; stringVersion = "3.02"; Rinex_Printer::log_rinex_nav(out, gps_eph_map); Rinex_Printer::log_rinex_nav(out, galileo_eph_map); } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& gps_cnav_eph_map, const std::map& galileo_eph_map) { version = 3; stringVersion = "3.02"; Rinex_Printer::log_rinex_nav(out, gps_cnav_eph_map); Rinex_Printer::log_rinex_nav(out, galileo_eph_map); } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& glonass_gnav_eph_map) { Rinex_Printer::log_rinex_nav(out, gps_eph_map); Rinex_Printer::log_rinex_nav(out, glonass_gnav_eph_map); } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& gps_cnav_eph_map, const std::map& glonass_gnav_eph_map) { Rinex_Printer::log_rinex_nav(out, gps_cnav_eph_map); Rinex_Printer::log_rinex_nav(out, glonass_gnav_eph_map); } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& galileo_eph_map, const std::map& glonass_gnav_eph_map) { version = 3; stringVersion = "3.02"; Rinex_Printer::log_rinex_nav(out, galileo_eph_map); Rinex_Printer::log_rinex_nav(out, glonass_gnav_eph_map); } void Rinex_Printer::log_rinex_nav(std::fstream& out, const std::map& eph_map) { std::string line; std::map::const_iterator bds_ephemeris_iter; for (bds_ephemeris_iter = eph_map.cbegin(); bds_ephemeris_iter != eph_map.cend(); bds_ephemeris_iter++) { // -------- SV / EPOCH / SV CLK boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_BDS_time(bds_ephemeris_iter->second, bds_ephemeris_iter->second.d_Toc); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string seconds(timestring, 13, 2); line += satelliteSystem["Beidou"]; if (bds_ephemeris_iter->second.i_satellite_PRN < 10) { line += std::string("0"); } line += std::to_string(bds_ephemeris_iter->second.i_satellite_PRN); std::string year(timestring, 0, 4); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); line += seconds; line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_A_f0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_A_f1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_A_f2, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 1 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_AODE, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Crs, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Delta_n, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_M_0, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 2 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Cuc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_eccentricity, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Cus, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_sqrt_A, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 3 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Toe, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Cic, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_OMEGA0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Cis, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 4 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_i_0, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_Crc, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_OMEGA, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_OMEGA_DOT, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 5 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_IDOT, 18, 2); line += std::string(1, ' '); line += std::string(18, ' '); // spare line += std::string(1, ' '); auto BDS_week_continuous_number = static_cast(bds_ephemeris_iter->second.i_BEIDOU_week); line += Rinex_Printer::doub2for(BDS_week_continuous_number, 18, 2); line += std::string(1, ' '); line += std::string(18, ' '); // spare Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 6 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(static_cast(bds_ephemeris_iter->second.i_SV_accuracy), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(static_cast(bds_ephemeris_iter->second.i_SV_health), 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_TGD1, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_TGD2, 18, 2); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- BROADCAST ORBIT - 7 line.clear(); line += std::string(5, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_TOW, 18, 2); line += std::string(1, ' '); line += Rinex_Printer::doub2for(bds_ephemeris_iter->second.d_AODC, 18, 2); line += std::string(1, ' '); line += std::string(18, ' '); // spare line += std::string(1, ' '); line += std::string(18, ' '); // spare Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); } } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Glonass_Gnav_Ephemeris& eph, const double d_TOW_first_observation, const std::string& glonass_bands) { if (eph.d_m) { } // Avoid compiler warning std::string line; std::map::const_iterator glonass_gnav_ephemeris_iter; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["GLONASS"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); if (version == 2) { line += Rinex_Printer::leftJustify("BLANK OR G = GPS, R = GLONASS, E = GALILEO, M = MIXED", 60); } if (version == 3) { line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); } line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GLONASS OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE if (version == 2) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER NUMBER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 3) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES if (version == 3) { // -------- SYS / OBS TYPES // one line per available system line.clear(); line += satelliteSystem["GLONASS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = 4; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); std::string signal_ = "1G"; std::size_t found_1G = glonass_bands.find(signal_); signal_ = "2G"; std::size_t found_2G = glonass_bands.find(signal_); if (found_1G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G1_CA"]; } if (found_2G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G2_CA"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 2) { // -------- SYS / OBS TYPES line.clear(); std::stringstream strm; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 6); // per type of observation // GLONASS L1 C/A PSEUDORANGE line += Rinex_Printer::rightJustify(observationType["PSEUDORANGE_CA_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS L1 PHASE line += Rinex_Printer::rightJustify(observationType["CARRIER_PHASE_CA_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS DOPPLER L1 line += Rinex_Printer::rightJustify(observationType["DOPPLER_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS L1 SIGNAL STRENGTH line += Rinex_Printer::rightJustify(observationType["SIGNAL_STRENGTH_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("# / TYPES OF OBSERV", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- Signal Strength units (Only version 3) if (version == 3) { // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- TIME OF FIRST OBS boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_UTC_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_utc_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double intpart = 0; double seconds = p_utc_time.time_of_day().seconds() + modf(d_TOW_first_observation, &intpart); line.clear(); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GLO"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS SLOT / FRQ # (On;y version 3) if (version == 3) { // -------- GLONASS SLOT / FRQ # // TODO Need to provide system with list of all satellites and update this accordingly line.clear(); line += Rinex_Printer::rightJustify(std::to_string(0), 3); // Number of satellites in list line += std::string(1, ' '); line += satelliteSystem["GLONASS"]; line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Slot Number line += std::string(1, ' '); line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Frequency Number line += std::string(1, ' '); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS SLOT / FRQ #", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS CODE/PHS/BIS // No GLONASS Phase bias correction used to align code and phase observations. line.clear(); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS COD/PHS/BIS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- END OF HEADER line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& glonass_bands) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GPS/GLO) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME / TYPE if (version == 2) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER NUMBER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 3) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- Line MARKER TYPE line.clear(); line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES if (version == 3) { // one line per available system line.clear(); line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = 4; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L1_CA"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L1_CA"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L1_CA"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L1_CA"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // Find GLONASS Signal in Mixed file uint32_t number_of_observations_glo = 0; std::string signal_("1G"); std::size_t found_1G = glonass_bands.find(signal_); if (found_1G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } signal_ = "2G"; std::size_t found_2G = glonass_bands.find(signal_); if (found_2G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } line.clear(); line += satelliteSystem["GLONASS"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_glo), 3); if (found_1G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G1_CA"]; } if (found_2G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G2_CA"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 2) { // -------- SYS / OBS TYPES line.clear(); std::stringstream strm; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 6); // per type of observation // GLONASS L1 C/A PSEUDORANGE line += Rinex_Printer::rightJustify(observationType["PSEUDORANGE_CA_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS L1 PHASE line += Rinex_Printer::rightJustify(observationType["CARRIER_PHASE_CA_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS DOPPLER L1 line += Rinex_Printer::rightJustify(observationType["DOPPLER_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; // GLONASS L1 SIGNAL STRENGTH line += Rinex_Printer::rightJustify(observationType["SIGNAL_STRENGTH_v2"], 5); line += observationCode["GLONASS_G1_CA_v2"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("# / TYPES OF OBSERV", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- Signal Strength units (only version 3) if (version == 3) { line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS SLOT / FRQ # (On;y version 3) if (version == 3) { // -------- GLONASS SLOT / FRQ # // TODO Need to provide system with list of all satellites and update this accordingly line.clear(); line += Rinex_Printer::rightJustify(std::to_string(0), 3); // Number of satellites in list line += std::string(1, ' '); line += satelliteSystem["GLONASS"]; line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Slot Number line += std::string(1, ' '); line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Frequency Number line += std::string(1, ' '); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS SLOT / FRQ #", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS CODE/PHS/BIS // No GLONASS Phase bias correction used to align code and phase observations. line.clear(); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS COD/PHS/BIS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& glonass_bands) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GPS/GLO) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NUMBER / TYPE if (version == 2) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER NUMBER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 3) { line.clear(); line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = 4; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L2_L2CM"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L2_L2CM"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L2_L2CM"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L2_L2CM"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // Find GLONASS Signal in Mixed file uint32_t number_of_observations_glo = 0; std::string signal_("1G"); std::size_t found_1G = glonass_bands.find(signal_); if (found_1G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } signal_ = "2G"; std::size_t found_2G = glonass_bands.find(signal_); if (found_2G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } line.clear(); line += satelliteSystem["GLONASS"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_glo), 3); if (found_1G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G1_CA"]; } if (found_2G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_G2_CA"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units (only version 3) line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_cnav_eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS SLOT / FRQ # // TODO Need to provide system with list of all satellites and update this accordingly line.clear(); line += Rinex_Printer::rightJustify(std::to_string(0), 3); // Number of satellites in list line += std::string(1, ' '); line += satelliteSystem["GLONASS"]; line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Slot Number line += std::string(1, ' '); line += Rinex_Printer::rightJustify(std::to_string(0), 2); // Frequency Number line += std::string(1, ' '); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS SLOT / FRQ #", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- GLONASS CODE/PHS/BIS // No GLONASS Phase bias correction used to align code and phase observations. line.clear(); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G1_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_CA"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_G2_P"]; line += std::string(1, ' '); line += Rinex_Printer::rightJustify(asString(0.0, 3), 8); line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("GLONASS COD/PHS/BIS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris& galileo_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& galileo_bands, const std::string& glonass_bands) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; version = 3; // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GALILEO/GLONASS) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE line.clear(); line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property line += std::string(40, ' '); line += Rinex_Printer::leftJustify("MARKER TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES line.clear(); uint32_t number_of_observations_gal = 0; std::string signal_("1B"); std::size_t found_1B = galileo_bands.find(signal_); if (found_1B != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "5X"; std::size_t found_5X = galileo_bands.find(signal_); if (found_5X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line.clear(); signal_ = "7X"; std::size_t found_7X = galileo_bands.find(signal_); if (found_7X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line += satelliteSystem["Galileo"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_gal), 3); if (found_1B != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E1_B"]; } if (found_5X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5a_IQ"]; } if (found_7X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5b_IQ"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); uint32_t number_of_observations_glo = 0; signal_ = "1G"; std::size_t found_1G = glonass_bands.find(signal_); if (found_1G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } signal_ = "2G"; std::size_t found_2G = glonass_bands.find(signal_); if (found_2G != std::string::npos) { number_of_observations_glo = number_of_observations_glo + 4; } line += satelliteSystem["GLONASS"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_glo), 3); if (found_1G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_L1_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_L1_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_L1_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_L1_CA"]; } if (found_2G != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GLONASS_L2_CA"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GLONASS_L2_CA"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GLONASS_L2_CA"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GLONASS_L2_CA"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_galileo_time = Rinex_Printer::compute_Galileo_time(galileo_eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_galileo_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double galileo_t = d_TOW_first_observation; double seconds = fmod(galileo_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("Galileo"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_Ephemeris& eph, const double d_TOW_first_observation) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["GPS"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); if (version == 2) { line += Rinex_Printer::leftJustify("BLANK OR G = GPS, R = GLONASS, E = GALILEO, M = MIXED", 60); } if (version == 3) { line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); } line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GPS OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; if (version == 2) { // --------- WAVELENGTH FACTOR // put here real data! line.clear(); line += Rinex_Printer::rightJustify("1", 6); line += Rinex_Printer::rightJustify("1", 6); line += std::string(48, ' '); line += Rinex_Printer::leftJustify("WAVELENGTH FACT L1/2", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 3) { // -------- SYS / OBS TYPES // one line per available system line.clear(); line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = 4; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L1_CA"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L1_CA"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L1_CA"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L1_CA"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 2) { // -------- SYS / OBS TYPES line.clear(); std::stringstream strm; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 6); // per type of observation // GPS L1 PSEUDORANGE line += Rinex_Printer::rightJustify(observationType["PSEUDORANGE_CA_v2"], 5); line += observationCode["GPS_L1_CA_v2"]; // GPS L1 PHASE line += Rinex_Printer::rightJustify(observationType["CARRIER_PHASE_CA_v2"], 5); line += observationCode["GPS_L1_CA_v2"]; // GPS DOPPLER L1 line += Rinex_Printer::rightJustify(observationType["DOPPLER_v2"], 5); line += observationCode["GPS_L1_CA_v2"]; // GPS L1 SIGNAL STRENGTH line += Rinex_Printer::rightJustify(observationType["SIGNAL_STRENGTH_v2"], 5); line += observationCode["GPS_L1_CA_v2"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("# / TYPES OF OBSERV", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } if (version == 3) { // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS /PHASE SHIFTS // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& eph, const double d_TOW_first_observation, const std::string& gps_bands) { std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["GPS"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GPS OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); uint32_t number_of_observations_gps = 0; std::string signal_("2S"); std::size_t found_2S = gps_bands.find(signal_); if (found_2S != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "L5"; std::size_t found_L5 = gps_bands.find(signal_); if (found_L5 != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = number_of_observations_gps; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation if (found_2S != std::string::npos) { // GPS L2 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L2_L2CM"]; // GPS DOPPLER L2 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L2_L2CM"]; } if (found_L5 != std::string::npos) { // GPS L5 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L5_Q"]; // GPS L5 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L5_Q"]; // GPS DOPPLER L5 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L5_Q"]; // GPS L5 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L5_Q"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS /PHASE SHIFTS // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_Ephemeris& eph, const Gps_CNAV_Ephemeris& eph_cnav, const double d_TOW_first_observation, const std::string& gps_bands) { if (eph_cnav.d_i_0) { } // avoid warning, not needed std::string line; // -------- Line 1 line = std::string(5, ' '); line += stringVersion; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["GPS"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GPS OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("GROUND_CRAFT", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); uint32_t number_of_observations_gps = 0; std::string signal_("1C"); std::size_t found_1C = gps_bands.find(signal_); if (found_1C != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "2S"; std::size_t found_2S = gps_bands.find(signal_); if (found_2S != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "L5"; std::size_t found_L5 = gps_bands.find(signal_); if (found_L5 != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = number_of_observations_gps; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation if (found_1C != std::string::npos) { // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L1_CA"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L1_CA"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L1_CA"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L1_CA"]; } if (found_2S != std::string::npos) { // GPS L2 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L2_L2CM"]; // GPS DOPPLER L2 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L2_L2CM"]; } if (found_L5 != std::string::npos) { // GPS L5 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L5_Q"]; // GPS L5 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L5_Q"]; // GPS DOPPLER L5 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L5_Q"]; // GPS L5 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L5_Q"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS /PHASE SHIFTS // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& eph_cnav, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& gps_bands, const std::string& galileo_bands) { std::string line; version = 3; if (eph_cnav.d_e_eccentricity == 0) { // avoid warning } if (galileo_eph.e_1 == 0) { // avoid warning } // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GPS/GALILEO) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); uint32_t number_of_observations_gps = 0; std::string signal_("1C"); std::size_t found_1C = gps_bands.find(signal_); if (found_1C != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "2S"; std::size_t found_2S = gps_bands.find(signal_); if (found_2S != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "L5"; std::size_t found_L5 = gps_bands.find(signal_); if (found_L5 != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = number_of_observations_gps; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation if (found_1C != std::string::npos) { // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L1_CA"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L1_CA"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L1_CA"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L1_CA"]; } if (found_2S != std::string::npos) { // GPS L2 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L2_L2CM"]; // GPS DOPPLER L2 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L2_L2CM"]; } if (found_L5 != std::string::npos) { // GPS L5 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L5_Q"]; // GPS L5 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L5_Q"]; // GPS DOPPLER L5 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L5_Q"]; // GPS L5 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L5_Q"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); uint32_t number_of_observations_gal = 0; signal_ = "1B"; std::size_t found_1B = galileo_bands.find(signal_); if (found_1B != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "5X"; std::size_t found_5X = galileo_bands.find(signal_); if (found_5X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "7X"; std::size_t found_7X = galileo_bands.find(signal_); if (found_7X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line += satelliteSystem["Galileo"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_gal), 3); if (found_1B != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E1_B"]; } if (found_5X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5a_IQ"]; } if (found_7X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5b_IQ"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& eph_cnav, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& gps_bands, const std::string& galileo_bands) { std::string line; version = 3; if (galileo_eph.e_1 == 0) { // avoid warning } // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GPS/GALILEO) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); uint32_t number_of_observations_gps = 0; std::string signal_("2S"); std::size_t found_2S = gps_bands.find(signal_); if (found_2S != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } signal_ = "L5"; std::size_t found_L5 = gps_bands.find(signal_); if (found_L5 != std::string::npos) { number_of_observations_gps = number_of_observations_gps + 4; } line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = number_of_observations_gps; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation if (found_2S != std::string::npos) { // GPS L2 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L2_L2CM"]; // GPS DOPPLER L2 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L2_L2CM"]; // GPS L2 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L2_L2CM"]; } if (found_L5 != std::string::npos) { // GPS L5 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L5_Q"]; // GPS L5 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L5_Q"]; // GPS DOPPLER L5 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L5_Q"]; // GPS L5 SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L5_Q"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); uint32_t number_of_observations_gal = 0; signal_ = "1B"; std::size_t found_1B = galileo_bands.find(signal_); if (found_1B != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "5X"; std::size_t found_5X = galileo_bands.find(signal_); if (found_5X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "7X"; std::size_t found_7X = galileo_bands.find(signal_); if (found_7X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line += satelliteSystem["Galileo"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_gal), 3); if (found_1B != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E1_B"]; } if (found_5X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5a_IQ"]; } if (found_7X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5b_IQ"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph_cnav, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris& eph, const double d_TOW_first_observation, const std::string& bands) { std::string line; version = 3; // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Galileo"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("GALILEO OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system uint32_t number_of_observations = 0; std::string signal_("1B"); std::size_t found_1B = bands.find(signal_); if (found_1B != std::string::npos) { number_of_observations = number_of_observations + 4; } signal_ = "5X"; std::size_t found_5X = bands.find(signal_); if (found_5X != std::string::npos) { number_of_observations = number_of_observations + 4; } line.clear(); signal_ = "7X"; std::size_t found_7X = bands.find(signal_); if (found_7X != std::string::npos) { number_of_observations = number_of_observations + 4; } line.clear(); line += satelliteSystem["Galileo"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations), 3); if (found_1B != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E1_B"]; } if (found_5X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5a_IQ"]; } if (found_7X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5b_IQ"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_galileo_time = Rinex_Printer::compute_Galileo_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_galileo_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double galileo_t = d_TOW_first_observation; double seconds = fmod(galileo_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GAL"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS /PHASE SHIFTS // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& galileo_bands) { if (galileo_eph.e_1) { } // avoid warning, not needed std::string line; version = 3; // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Mixed"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("MIXED (GPS/GALILEO) OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER TYPE // line.clear(); // line += Rinex_Printer::leftJustify("NON_GEODETIC", 20); // put a flag or a property // line += std::string(40, ' '); // line += Rinex_Printer::leftJustify("MARKER TYPE", 20); // Rinex_Printer::lengthCheck(line); // out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system line.clear(); line += satelliteSystem["GPS"]; line += std::string(2, ' '); std::stringstream strm; numberTypesObservations = 4; strm << numberTypesObservations; line += Rinex_Printer::rightJustify(strm.str(), 3); // per type of observation // GPS L1 PSEUDORANGE line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GPS_L1_CA"]; // GPS L1 PHASE line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GPS_L1_CA"]; // GPS DOPPLER L1 line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GPS_L1_CA"]; // GPS L1 CA SIGNAL STRENGTH line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GPS_L1_CA"]; line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; line.clear(); uint32_t number_of_observations_gal = 0; std::string signal_("1B"); std::size_t found_1B = galileo_bands.find(signal_); if (found_1B != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } signal_ = "5X"; std::size_t found_5X = galileo_bands.find(signal_); if (found_5X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line.clear(); signal_ = "7X"; std::size_t found_7X = galileo_bands.find(signal_); if (found_7X != std::string::npos) { number_of_observations_gal = number_of_observations_gal + 4; } line += satelliteSystem["Galileo"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations_gal), 3); if (found_1B != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E1_B"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E1_B"]; } if (found_5X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5a_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5a_IQ"]; } if (found_7X != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["GALILEO_E5b_IQ"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["GALILEO_E5b_IQ"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double gps_t = d_TOW_first_observation; double seconds = fmod(gps_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("GPS"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::rinex_obs_header(std::fstream& out, const Beidou_Dnav_Ephemeris& eph, const double d_TOW_first_observation, const std::string& bands) { std::string line; version = 3; // -------- Line 1 line = std::string(5, ' '); line += "3.02"; line += std::string(11, ' '); line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20); line += satelliteSystem["Beidou"]; line += std::string(19, ' '); line += std::string("RINEX VERSION / TYPE"); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 2 line.clear(); line += Rinex_Printer::leftJustify("G = GPS R = GLONASS E = GALILEO C = BEIDOU S = GEO M = MIXED", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line 3 line.clear(); line += Rinex_Printer::getLocalTime(); line += std::string("PGM / RUN BY / DATE"); line += std::string(1, ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("BEIDOU OBSERVATION DATA FILE GENERATED BY GNSS-SDR", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); std::string gnss_sdr_version(GNSS_SDR_VERSION); line += "GNSS-SDR VERSION "; line += Rinex_Printer::leftJustify(gnss_sdr_version, 43); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line COMMENT line.clear(); line += Rinex_Printer::leftJustify("See https://gnss-sdr.org", 60); line += Rinex_Printer::leftJustify("COMMENT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line MARKER NAME line.clear(); line += Rinex_Printer::leftJustify("DEFAULT MARKER NAME", 60); // put a flag or a property, line += Rinex_Printer::leftJustify("MARKER NAME", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line OBSERVER / AGENCY line.clear(); std::string username; std::array c_username{}; int32_t nGet = getlogin_r(c_username.data(), sizeof(c_username) - 1); if (nGet == 0) { username = c_username.data(); } else { username = "UNKNOWN USER"; } line += leftJustify(username, 20); line += Rinex_Printer::leftJustify("CTTC", 40); // add flag and property line += Rinex_Printer::leftJustify("OBSERVER / AGENCY", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Line REC / TYPE VERS line.clear(); line += Rinex_Printer::leftJustify("GNSS-SDR", 20); // add flag and property line += Rinex_Printer::leftJustify("Software Receiver", 20); // add flag and property // line += Rinex_Printer::leftJustify(google::VersionString(), 20); // add flag and property if (gnss_sdr_version.length() > 20) { gnss_sdr_version.resize(9, ' '); } line += Rinex_Printer::leftJustify(gnss_sdr_version, 20); line += Rinex_Printer::leftJustify("REC # / TYPE / VERS", 20); lengthCheck(line); out << line << std::endl; // -------- ANTENNA TYPE line.clear(); line += Rinex_Printer::leftJustify("Antenna number", 20); // add flag and property line += Rinex_Printer::leftJustify("Antenna type", 20); // add flag and property line += std::string(20, ' '); line += Rinex_Printer::leftJustify("ANT # / TYPE", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- APPROX POSITION (optional for moving platforms) // put here real data! double antena_x = 0.0; double antena_y = 0.0; double antena_z = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_x, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_y, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_z, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("APPROX POSITION XYZ", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- ANTENNA: DELTA H/E/N // put here real data! double antena_h = 0.0; double antena_e = 0.0; double antena_n = 0.0; line.clear(); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_h, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_e, 4), 14); line += Rinex_Printer::rightJustify(Rinex_Printer::asString(antena_n, 4), 14); line += std::string(18, ' '); line += Rinex_Printer::leftJustify("ANTENNA: DELTA H/E/N", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS / OBS TYPES // one line per available system uint32_t number_of_observations = 0; std::string signal_("B1"); std::size_t found_B1 = bands.find(signal_); if (found_B1 != std::string::npos) { number_of_observations = number_of_observations + 4; } signal_ = "B3"; std::size_t found_B3 = bands.find(signal_); if (found_B3 != std::string::npos) { number_of_observations = number_of_observations + 4; } line.clear(); line += satelliteSystem["Beidou"]; line += std::string(2, ' '); line += Rinex_Printer::rightJustify(std::to_string(number_of_observations), 3); if (found_B1 != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["BEIDOU_B1_I"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["BEIDOU_B1_I"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["BEIDOU_B1_I"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["BEIDOU_B1_I"]; } if (found_B3 != std::string::npos) { line += std::string(1, ' '); line += observationType["PSEUDORANGE"]; line += observationCode["BEIDOU_B3_I"]; line += std::string(1, ' '); line += observationType["CARRIER_PHASE"]; line += observationCode["BEIDOU_B3_I"]; line += std::string(1, ' '); line += observationType["DOPPLER"]; line += observationCode["BEIDOU_B3_I"]; line += std::string(1, ' '); line += observationType["SIGNAL_STRENGTH"]; line += observationCode["BEIDOU_B3_I"]; } line += std::string(60 - line.size(), ' '); line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- Signal Strength units line.clear(); line += Rinex_Printer::leftJustify("DBHZ", 20); line += std::string(40, ' '); line += Rinex_Printer::leftJustify("SIGNAL STRENGTH UNIT", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- TIME OF FIRST OBS line.clear(); boost::posix_time::ptime p_bds_time = Rinex_Printer::compute_BDS_time(eph, d_TOW_first_observation); std::string timestring = boost::posix_time::to_iso_string(p_bds_time); std::string year(timestring, 0, 4); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double beidou_t = d_TOW_first_observation; double seconds = fmod(beidou_t, 60); line += Rinex_Printer::rightJustify(year, 6); line += Rinex_Printer::rightJustify(month, 6); line += Rinex_Printer::rightJustify(day, 6); line += Rinex_Printer::rightJustify(hour, 6); line += Rinex_Printer::rightJustify(minutes, 6); line += Rinex_Printer::rightJustify(asString(seconds, 7), 13); line += Rinex_Printer::rightJustify(std::string("BDT"), 8); line += std::string(9, ' '); line += Rinex_Printer::leftJustify("TIME OF FIRST OBS", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- SYS /PHASE SHIFTS // -------- end of header line.clear(); line += std::string(60, ' '); line += Rinex_Printer::leftJustify("END OF HEADER", 20); Rinex_Printer::lengthCheck(line); out << line << std::endl; } void Rinex_Printer::update_obs_header(std::fstream& out __attribute__((unused)), const Glonass_Gnav_Utc_Model& utc_model) { if (utc_model.d_N_4) { // do nothing } } void Rinex_Printer::update_obs_header(std::fstream& out, const Gps_Utc_Model& utc_model) { std::vector data; std::string line_aux; out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (version == 2) { if (line_str.find("TIME OF FIRST OBS", 59) != std::string::npos) // TIME OF FIRST OBS last header annotation might change in the future { data.push_back(line_str); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += std::string(54, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } if (version == 3) { if (line_str.find("TIME OF FIRST OBS", 59) != std::string::npos) { data.push_back(line_str); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } } else { data.push_back(line_str); } } out.close(); out.open(obsfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(obsfilename, std::ios::out | std::ios::in | std::ios::app); out.seekp(0, std::ios_base::end); } void Rinex_Printer::update_obs_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model) { std::vector data; std::string line_aux; out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("TIME OF FIRST OBS", 59) != std::string::npos) { data.push_back(line_str); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(obsfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(obsfilename, std::ios::out | std::ios::in | std::ios::app); out.seekp(0, std::ios_base::end); } void Rinex_Printer::update_obs_header(std::fstream& out, const Galileo_Utc_Model& galileo_utc_model) { std::vector data; std::string line_aux; out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("TIME OF FIRST OBS", 59) != std::string::npos) { data.push_back(line_str); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLS_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.Delta_tLSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.WN_LSF_6), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(galileo_utc_model.DN_6), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(obsfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(obsfilename, std::ios::out | std::ios::in | std::ios::app); out.seekp(0, std::ios_base::end); } void Rinex_Printer::update_obs_header(std::fstream& out, const Beidou_Dnav_Utc_Model& utc_model) { std::vector data; std::string line_aux; out.seekp(0); data.clear(); bool no_more_finds = false; std::string line_str; while (!out.eof()) { std::getline(out, line_str); if (!no_more_finds) { line_aux.clear(); if (line_str.find("TIME OF FIRST OBS", 59) != std::string::npos) { data.push_back(line_str); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LS), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.d_DeltaT_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_WN_LSF), 6); line_aux += Rinex_Printer::rightJustify(std::to_string(utc_model.i_DN), 6); line_aux += std::string(36, ' '); line_aux += Rinex_Printer::leftJustify("LEAP SECONDS", 20); data.push_back(line_aux); } else if (line_str.find("END OF HEADER", 59) != std::string::npos) { data.push_back(line_str); no_more_finds = true; } else { data.push_back(line_str); } } else { data.push_back(line_str); } } out.close(); out.open(obsfilename, std::ios::out | std::ios::trunc); out.seekp(0); for (int32_t i = 0; i < static_cast(data.size()) - 1; i++) { out << data[i] << std::endl; } out.close(); out.open(obsfilename, std::ios::out | std::ios::in | std::ios::app); out.seekp(0, std::ios_base::end); } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Glonass_Gnav_Ephemeris& eph, const double obs_time, const std::map& observables, const std::string& glonass_band) { // RINEX observations timestamps are GPS timestamps. std::string line; double int_sec = 0; // Avoid compiler warning if (!glonass_band.empty()) { } boost::posix_time::ptime p_glonass_time = Rinex_Printer::compute_UTC_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_glonass_time); // double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); // double gps_t = eph.sv_clock_correction(obs_time); std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); double utc_sec = modf(obs_time, &int_sec) + p_glonass_time.time_of_day().seconds(); if (version == 2) { line.clear(); std::string year(timestring, 2, 2); line += std::string(1, ' '); line += year; line += std::string(1, ' '); if (month.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += month.substr(1, 1); } else { line += month; } line += std::string(1, ' '); if (day.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += day.substr(1, 1); } else { line += day; } line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); if (utc_sec < 10) { line += std::string(1, ' '); } line += Rinex_Printer::asString(utc_sec, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch int32_t numSatellitesObserved = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { numSatellitesObserved++; } line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { line += satelliteSystem["GLONASS"]; if (static_cast(observables_iter->second.PRN) < 10) { line += std::string(1, '0'); } line += std::to_string(static_cast(observables_iter->second.PRN)); } // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string lineObs; lineObs.clear(); line.clear(); // GLONASS L1 PSEUDORANGE line += std::string(2, ' '); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } // else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 CA PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GLONASS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } // else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 CA DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } // else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } if (version == 3) { std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); // Add extra 0 if seconds are < 10 if (utc_sec < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(utc_sec, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch int32_t numSatellitesObserved = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { numSatellitesObserved++; } line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string lineObs; lineObs.clear(); lineObs += satelliteSystem["GLONASS"]; if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); // lineObs += std::string(2, ' '); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 CA PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GLONASS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 CA DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS L1 SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double gps_obs_time, const std::map& observables) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; // -------- EPOCH record boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, gps_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = gps_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); if (version == 2) { line.clear(); std::string year(timestring, 2, 2); line += std::string(1, ' '); line += year; line += std::string(1, ' '); if (month.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += month.substr(1, 1); } else { line += month; } line += std::string(1, ' '); if (day.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += day.substr(1, 1); } else { line += day; } line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double second_ = fmod(gps_t, 60); if (second_ < 10) { line += std::string(1, ' '); } line += Rinex_Printer::asString(second_, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); } if (version == 3) { std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); } // Number of satellites observed in current epoch // Get maps with observations std::map observablesG1C; std::map observablesR1C; std::map observablesR2C; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1G")) { observablesR1C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "R") && (sig_ == "2G")) { observablesR2C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "1C")) { observablesG1C.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_glo_map; std::set available_glo_prns; std::set::iterator it; for (observables_iter = observablesR1C.cbegin(); observables_iter != observablesR1C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } for (observables_iter = observablesR2C.cbegin(); observables_iter != observablesR2C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } int32_t numGloSatellitesObserved = available_glo_prns.size(); int32_t numGpsSatellitesObserved = observablesG1C.size(); int32_t numSatellitesObserved = numGloSatellitesObserved + numGpsSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); if (version == 2) { // Add list of GPS satellites for (observables_iter = observablesG1C.cbegin(); observables_iter != observablesG1C.cend(); observables_iter++) { line += satelliteSystem["GPS"]; if (static_cast(observables_iter->second.PRN) < 10) { line += std::string(1, '0'); } line += std::to_string(static_cast(observables_iter->second.PRN)); } // Add list of GLONASS L1 satellites for (observables_iter = observablesR1C.cbegin(); observables_iter != observablesR1C.cend(); observables_iter++) { line += satelliteSystem["GLONASS"]; if (static_cast(observables_iter->second.PRN) < 10) { line += std::string(1, '0'); } line += std::to_string(static_cast(observables_iter->second.PRN)); } // Add list of GLONASS L2 satellites for (observables_iter = observablesR2C.cbegin(); observables_iter != observablesR2C.cend(); observables_iter++) { line += satelliteSystem["GLONASS"]; if (static_cast(observables_iter->second.PRN) < 10) { line += std::string(1, '0'); } line += std::to_string(static_cast(observables_iter->second.PRN)); } } line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- OBSERVATION record std::string s; std::string lineObs; for (observables_iter = observablesG1C.cbegin(); observables_iter != observablesG1C.cend(); observables_iter++) { lineObs.clear(); s.assign(1, observables_iter->second.System); if (version == 3) { // Specify system only if in version 3 if (s == "G") { lineObs += satelliteSystem["GPS"]; } if (s == "R") { lineObs += satelliteSystem["GLONASS"]; // should not happen } if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); } // Pseudorange Measurements lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } std::pair::iterator, std::multimap::iterator> ret; for (it = available_glo_prns.begin(); it != available_glo_prns.end(); it++) { lineObs.clear(); if (version == 3) { lineObs += satelliteSystem["GLONASS"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); } ret = total_glo_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { /// \todo Need to account for pseudorange correction for glonass //double leap_seconds = Rinex_Printer::get_leap_second(glonass_gnav_eph, gps_obs_time); lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GLONASS_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double gps_obs_time, const std::map& observables) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; // -------- EPOCH record boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, gps_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = gps_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with observations std::map observablesG2S; std::map observablesR1C; std::map observablesR2C; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1G")) { observablesR1C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "R") && (sig_ == "2G")) { observablesR2C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesG2S.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_glo_map; std::set available_glo_prns; std::set::iterator it; for (observables_iter = observablesR1C.cbegin(); observables_iter != observablesR1C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } for (observables_iter = observablesR2C.cbegin(); observables_iter != observablesR2C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } int32_t numGloSatellitesObserved = available_glo_prns.size(); int32_t numGpsSatellitesObserved = observablesG2S.size(); int32_t numSatellitesObserved = numGloSatellitesObserved + numGpsSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; // -------- OBSERVATION record std::string s; std::string lineObs; for (observables_iter = observablesG2S.cbegin(); observables_iter != observablesG2S.cend(); observables_iter++) { lineObs.clear(); s.assign(1, observables_iter->second.System); // Specify system only if in version 3 if (s == "G") { lineObs += satelliteSystem["GPS"]; } if (s == "R") { lineObs += satelliteSystem["GLONASS"]; // should not happen } if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); // Pseudorange Measurements lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } std::pair::iterator, std::multimap::iterator> ret; for (it = available_glo_prns.begin(); it != available_glo_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["GLONASS"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_glo_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { /// \todo Need to account for pseudorange correction for glonass //double leap_seconds = Rinex_Printer::get_leap_second(glonass_gnav_eph, gps_obs_time); lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GLONASS_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& galileo_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double galileo_obs_time, const std::map& observables) { if (glonass_gnav_eph.d_m) { } // avoid warning, not needed std::string line; boost::posix_time::ptime p_galileo_time = Rinex_Printer::compute_Galileo_time(galileo_eph, galileo_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_galileo_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double galileo_t = galileo_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(galileo_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with observations std::map observablesE1B; std::map observablesR1C; std::map observablesR2C; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1G")) { observablesR1C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "R") && (sig_ == "2G")) { observablesR2C.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "1B")) { observablesE1B.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_glo_map; std::set available_glo_prns; std::set::iterator it; for (observables_iter = observablesR1C.cbegin(); observables_iter != observablesR1C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } for (observables_iter = observablesR2C.cbegin(); observables_iter != observablesR2C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_glo_map.insert(std::pair(prn_, observables_iter->second)); it = available_glo_prns.find(prn_); if (it == available_glo_prns.end()) { available_glo_prns.insert(prn_); } } int32_t numGloSatellitesObserved = available_glo_prns.size(); int32_t numGalSatellitesObserved = observablesE1B.size(); int32_t numSatellitesObserved = numGalSatellitesObserved + numGloSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string s; std::string lineObs; for (observables_iter = observablesE1B.cbegin(); observables_iter != observablesE1B.cend(); observables_iter++) { lineObs.clear(); s.assign(1, observables_iter->second.System); if (s == "E") { lineObs += satelliteSystem["Galileo"]; } if (s == "R") { lineObs += satelliteSystem["GLONASS"]; // should not happen } if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } std::pair::iterator, std::multimap::iterator> ret; for (it = available_glo_prns.begin(); it != available_glo_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Galileo"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_glo_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GLONASS_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GLONASS SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, const double obs_time, const std::map& observables) { // RINEX observations timestamps are GPS timestamps. std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); if (version == 2) { line.clear(); std::string year(timestring, 2, 2); line += std::string(1, ' '); line += year; line += std::string(1, ' '); if (month.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += month.substr(1, 1); } else { line += month; } line += std::string(1, ' '); if (day.compare(0, 1, "0") == 0) { line += std::string(1, ' '); line += day.substr(1, 1); } else { line += day; } line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double second_ = fmod(gps_t, 60); if (second_ < 10) { line += std::string(1, ' '); } line += Rinex_Printer::asString(second_, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch int32_t numSatellitesObserved = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { numSatellitesObserved++; } line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { line += satelliteSystem["GPS"]; if (static_cast(observables_iter->second.PRN) < 10) { line += std::string(1, '0'); } line += std::to_string(static_cast(observables_iter->second.PRN)); } // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string lineObs; lineObs.clear(); line.clear(); // GPS L1 PSEUDORANGE line += std::string(2, ' '); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L1 CA PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } // else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L1 CA DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); //GPS L1 SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } if (version == 3) { std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch int32_t numSatellitesObserved = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { numSatellitesObserved++; } line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string lineObs; lineObs.clear(); lineObs += satelliteSystem["GPS"]; if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); // lineObs += std::string(2, ' '); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L1 CA PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L1 CA DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); //GPS L1 SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& eph, double obs_time, const std::map& observables) { // RINEX observations timestamps are GPS timestamps. std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch int32_t numSatellitesObserved = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { numSatellitesObserved++; } line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string lineObs; lineObs.clear(); lineObs += satelliteSystem["GPS"]; if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); // lineObs += std::string(2, ' '); //GPS L2 PSEUDORANGE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L2 PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS L2 DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); //GPS L2 SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, const Gps_CNAV_Ephemeris& eph_cnav, double obs_time, const std::map& observables) { if (eph_cnav.d_i_0) { } // avoid warning, not needed // RINEX observations timestamps are GPS timestamps. std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with GPS L1 and L2 observations std::map observablesL1; std::map observablesL2; std::map observablesL5; std::map::const_iterator observables_iter; std::multimap total_mmap; std::multimap::iterator mmap_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "G") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); total_mmap.insert(std::pair(observables_iter->second.PRN, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesL2.insert(std::pair(observables_iter->first, observables_iter->second)); mmap_iter = total_mmap.find(observables_iter->second.PRN); if (mmap_iter == total_mmap.end()) { Gnss_Synchro gs = Gnss_Synchro(); total_mmap.insert(std::pair(observables_iter->second.PRN, gs)); } total_mmap.insert(std::pair(observables_iter->second.PRN, observables_iter->second)); } if ((system_ == "G") && (sig_ == "L5")) { observablesL5.insert(std::pair(observables_iter->first, observables_iter->second)); mmap_iter = total_mmap.find(observables_iter->second.PRN); if (mmap_iter == total_mmap.end()) { Gnss_Synchro gs = Gnss_Synchro(); total_mmap.insert(std::pair(observables_iter->second.PRN, gs)); } total_mmap.insert(std::pair(observables_iter->second.PRN, observables_iter->second)); } } // Fill with zeros satellites with L1 obs but not L2 std::multimap mmap_aux; mmap_aux = total_mmap; for (mmap_iter = mmap_aux.begin(); mmap_iter != mmap_aux.end(); mmap_iter++) { if ((total_mmap.count(mmap_iter->second.PRN)) == 1 && (mmap_iter->second.PRN != 0)) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'G'; gs.Signal[0] = '2'; gs.Signal[1] = 'S'; gs.Signal[2] = '\0'; gs.PRN = mmap_iter->second.PRN; total_mmap.insert(std::pair(mmap_iter->second.PRN, gs)); } } std::set available_prns; std::set::iterator it; for (observables_iter = observablesL1.cbegin(); observables_iter != observablesL1.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); } } for (observables_iter = observablesL2.cbegin(); observables_iter != observablesL2.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); } } for (observables_iter = observablesL5.cbegin(); observables_iter != observablesL5.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); } } int32_t numSatellitesObserved = available_prns.size(); line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string lineObs; std::pair::iterator, std::multimap::iterator> ret; for (it = available_prns.begin(); it != available_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["GPS"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_mmap.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } // else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // GPS SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& eph, double obs_time, const std::map& observables, const std::string& galileo_bands) { // RINEX observations timestamps are Galileo timestamps. // See http://gage14.upc.es/gLAB/HTML/Observation_Rinex_v3.01.html std::string line; boost::posix_time::ptime p_galileo_time = Rinex_Printer::compute_Galileo_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_galileo_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double galileo_t = obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(galileo_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with Galileo observations std::map observablesE1B; std::map observablesE5A; std::map observablesE5B; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "E") && (sig_ == "1B")) { observablesE1B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "5X")) { observablesE5A.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "7X")) { observablesE5B.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::size_t found_1B = galileo_bands.find("1B"); std::size_t found_E5a = galileo_bands.find("5X"); std::size_t found_E5b = galileo_bands.find("7X"); std::multimap total_map; std::set available_prns; std::set::iterator it; if (found_1B != std::string::npos) { for (observables_iter = observablesE1B.cbegin(); observables_iter != observablesE1B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_map.insert(std::pair(prn_, observables_iter->second)); it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); } } } if (found_E5a != std::string::npos) { for (observables_iter = observablesE5A.cbegin(); observables_iter != observablesE5A.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); if (found_1B != std::string::npos) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'E'; gs.Signal[0] = '1'; gs.Signal[1] = 'B'; gs.Signal[2] = '\0'; gs.PRN = prn_; total_map.insert(std::pair(prn_, gs)); } } total_map.insert(std::pair(prn_, observables_iter->second)); } } if (found_E5b != std::string::npos) { for (observables_iter = observablesE5B.cbegin(); observables_iter != observablesE5B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); if (found_1B != std::string::npos) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'E'; gs.Signal[0] = '1'; gs.Signal[1] = 'B'; gs.Signal[2] = '\0'; gs.PRN = prn_; total_map.insert(std::pair(prn_, gs)); } if (found_E5a != std::string::npos) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'E'; gs.Signal[0] = '5'; gs.Signal[1] = 'X'; gs.Signal[2] = '\0'; gs.PRN = prn_; total_map.insert(std::pair(prn_, gs)); } } else { // if 5X is listed but empty if (found_E5a != std::string::npos) { if ((total_map.count(prn_)) == 1) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'E'; gs.Signal[0] = '5'; gs.Signal[1] = 'X'; gs.Signal[2] = '\0'; gs.PRN = prn_; total_map.insert(std::pair(prn_, gs)); } } } total_map.insert(std::pair(prn_, observables_iter->second)); } } int32_t numSatellitesObserved = available_prns.size(); line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string lineObs; std::pair::iterator, std::multimap::iterator> ret; for (it = available_prns.begin(); it != available_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Galileo"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Galileo_Ephemeris& galileo_eph, double gps_obs_time, const std::map& observables) { if (galileo_eph.e_1) { } // avoid warning, not needed std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, gps_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = gps_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with observations std::map observablesG1C; std::map observablesE1B; std::map observablesE5A; std::map observablesE5B; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "E") && (sig_ == "1B")) { observablesE1B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "5X")) { observablesE5A.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "7X")) { observablesE5B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "1C")) { observablesG1C.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_gal_map; std::set available_gal_prns; std::set::iterator it; for (observables_iter = observablesE1B.cbegin(); observables_iter != observablesE1B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5A.cbegin(); observables_iter != observablesE5A.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5B.cbegin(); observables_iter != observablesE5B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } int32_t numGalSatellitesObserved = available_gal_prns.size(); int32_t numGpsSatellitesObserved = observablesG1C.size(); int32_t numSatellitesObserved = numGalSatellitesObserved + numGpsSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string s; std::string lineObs; for (observables_iter = observablesG1C.cbegin(); observables_iter != observablesG1C.cend(); observables_iter++) { lineObs.clear(); s.assign(1, observables_iter->second.System); if (s == "G") { lineObs += satelliteSystem["GPS"]; } if (s == "E") { lineObs += satelliteSystem["Galileo"]; // should not happen } if (static_cast(observables_iter->second.PRN) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(observables_iter->second.PRN)); lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(observables_iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // PHASE lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_phase_rads / GPS_TWO_PI, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(observables_iter->second.CN0_dB_hz, 3), 14); if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } std::pair::iterator, std::multimap::iterator> ret; for (it = available_gal_prns.begin(); it != available_gal_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Galileo"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_gal_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& eph, const Galileo_Ephemeris& galileo_eph, double gps_obs_time, const std::map& observables) { if (galileo_eph.e_1) { } // avoid warning, not needed std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(eph, gps_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = gps_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with observations std::map observablesG2S; std::map observablesGL5; std::map observablesE1B; std::map observablesE5A; std::map observablesE5B; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "E") && (sig_ == "1B")) { observablesE1B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "5X")) { observablesE5A.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "7X")) { observablesE5B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesG2S.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "L5")) { observablesGL5.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_gps_map; std::multimap total_gal_map; std::set available_gal_prns; std::set available_gps_prns; std::set::iterator it; for (observables_iter = observablesE1B.cbegin(); observables_iter != observablesE1B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5A.cbegin(); observables_iter != observablesE5A.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5B.cbegin(); observables_iter != observablesE5B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesG2S.cbegin(); observables_iter != observablesG2S.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gps_map.insert(std::pair(prn_, observables_iter->second)); it = available_gps_prns.find(prn_); if (it == available_gps_prns.end()) { available_gps_prns.insert(prn_); } } for (observables_iter = observablesGL5.cbegin(); observables_iter != observablesGL5.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gps_map.insert(std::pair(prn_, observables_iter->second)); it = available_gps_prns.find(prn_); if (it == available_gps_prns.end()) { available_gps_prns.insert(prn_); } } int32_t numGalSatellitesObserved = available_gal_prns.size(); int32_t numGpsSatellitesObserved = available_gps_prns.size(); int32_t numSatellitesObserved = numGalSatellitesObserved + numGpsSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string s; std::string lineObs; std::pair::iterator, std::multimap::iterator> ret; for (it = available_gps_prns.begin(); it != available_gps_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["GPS"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_gps_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } out << lineObs << std::endl; } for (it = available_gal_prns.begin(); it != available_gal_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Galileo"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_gal_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } //if (lineObs.size() < 80) lineObs += std::string(80 - lineObs.size(), ' '); out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& galileo_eph, double gps_obs_time, const std::map& observables) { if (galileo_eph.e_1) { } // avoid warning, not needed if (gps_cnav_eph.d_e_eccentricity) { } // avoid warning, not needed std::string line; boost::posix_time::ptime p_gps_time = Rinex_Printer::compute_GPS_time(gps_eph, gps_obs_time); std::string timestring = boost::posix_time::to_iso_string(p_gps_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double gps_t = gps_obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(gps_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with observations std::map observablesG2S; std::map observablesGL5; std::map observablesG1C; std::map observablesE1B; std::map observablesE5A; std::map observablesE5B; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "E") && (sig_ == "1B")) { observablesE1B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "5X")) { observablesE5A.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "E") && (sig_ == "7X")) { observablesE5B.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesG2S.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "L5")) { observablesGL5.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "1C")) { observablesG1C.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::multimap total_gps_map; std::multimap total_gal_map; std::set available_gal_prns; std::set available_gps_prns; std::set::iterator it; for (observables_iter = observablesE1B.cbegin(); observables_iter != observablesE1B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5A.cbegin(); observables_iter != observablesE5A.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesE5B.cbegin(); observables_iter != observablesE5B.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gal_map.insert(std::pair(prn_, observables_iter->second)); it = available_gal_prns.find(prn_); if (it == available_gal_prns.end()) { available_gal_prns.insert(prn_); } } for (observables_iter = observablesG1C.cbegin(); observables_iter != observablesG1C.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gps_map.insert(std::pair(prn_, observables_iter->second)); it = available_gps_prns.find(prn_); if (it == available_gps_prns.end()) { available_gps_prns.insert(prn_); } } for (observables_iter = observablesG2S.cbegin(); observables_iter != observablesG2S.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gps_map.insert(std::pair(prn_, observables_iter->second)); it = available_gps_prns.find(prn_); if (it == available_gps_prns.end()) { available_gps_prns.insert(prn_); } } for (observables_iter = observablesGL5.cbegin(); observables_iter != observablesGL5.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_gps_map.insert(std::pair(prn_, observables_iter->second)); it = available_gps_prns.find(prn_); if (it == available_gps_prns.end()) { available_gps_prns.insert(prn_); } } int32_t numGalSatellitesObserved = available_gal_prns.size(); int32_t numGpsSatellitesObserved = available_gps_prns.size(); int32_t numSatellitesObserved = numGalSatellitesObserved + numGpsSatellitesObserved; line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string s; std::string lineObs; std::pair::iterator, std::multimap::iterator> ret; for (it = available_gps_prns.begin(); it != available_gps_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["GPS"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_gps_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } out << lineObs << std::endl; } for (it = available_gal_prns.begin(); it != available_gal_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Galileo"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_gal_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (GALILEO_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } //else // { // lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(lli), 1); // } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // Galileo SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } //if (lineObs.size() < 80) lineObs += std::string(80 - lineObs.size(), ' '); out << lineObs << std::endl; } } void Rinex_Printer::log_rinex_obs(std::fstream& out, const Beidou_Dnav_Ephemeris& eph, double obs_time, const std::map& observables, const std::string& bds_bands) { std::string line; boost::posix_time::ptime p_bds_time = Rinex_Printer::compute_BDS_time(eph, obs_time); std::string timestring = boost::posix_time::to_iso_string(p_bds_time); //double utc_t = nav_msg.utc_time(nav_msg.sv_clock_correction(obs_time)); //double gps_t = eph.sv_clock_correction(obs_time); double bds_t = obs_time; std::string month(timestring, 4, 2); std::string day(timestring, 6, 2); std::string hour(timestring, 9, 2); std::string minutes(timestring, 11, 2); std::string year(timestring, 0, 4); line += std::string(1, '>'); line += std::string(1, ' '); line += year; line += std::string(1, ' '); line += month; line += std::string(1, ' '); line += day; line += std::string(1, ' '); line += hour; line += std::string(1, ' '); line += minutes; line += std::string(1, ' '); double seconds = fmod(bds_t, 60); // Add extra 0 if seconds are < 10 if (seconds < 10) { line += std::string(1, '0'); } line += Rinex_Printer::asString(seconds, 7); line += std::string(2, ' '); // Epoch flag 0: OK 1: power failure between previous and current epoch <1: Special event line += std::string(1, '0'); // Number of satellites observed in current epoch // Get maps with BeiDou observations std::map observablesB1I; std::map observablesB3I; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "C") && (sig_ == "B1")) { observablesB1I.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "C") && (sig_ == "B3")) { observablesB3I.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::size_t found_B1 = bds_bands.find("B1"); std::size_t found_B3 = bds_bands.find("B3"); std::multimap total_map; std::set available_prns; std::set::iterator it; if (found_B1 != std::string::npos) { for (observables_iter = observablesB1I.cbegin(); observables_iter != observablesB1I.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; total_map.insert(std::pair(prn_, observables_iter->second)); it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); } } } if (found_B3 != std::string::npos) { for (observables_iter = observablesB3I.cbegin(); observables_iter != observablesB3I.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; it = available_prns.find(prn_); if (it == available_prns.end()) { available_prns.insert(prn_); if (found_B1 != std::string::npos) { Gnss_Synchro gs = Gnss_Synchro(); gs.System = 'C'; gs.Signal[0] = 'B'; gs.Signal[1] = '1'; gs.Signal[2] = '\0'; gs.PRN = prn_; total_map.insert(std::pair(prn_, gs)); } } total_map.insert(std::pair(prn_, observables_iter->second)); } } int32_t numSatellitesObserved = available_prns.size(); line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3); // Receiver clock offset (optional) // line += rightJustify(asString(clockOffset, 12), 15); line += std::string(80 - line.size(), ' '); Rinex_Printer::lengthCheck(line); out << line << std::endl; std::string lineObs; std::pair::iterator, std::multimap::iterator> ret; for (it = available_prns.begin(); it != available_prns.end(); it++) { lineObs.clear(); lineObs += satelliteSystem["Beidou"]; if (static_cast(*it) < 10) { lineObs += std::string(1, '0'); } lineObs += std::to_string(static_cast(*it)); ret = total_map.equal_range(*it); for (auto iter = ret.first; iter != ret.second; ++iter) { lineObs += Rinex_Printer::rightJustify(asString(iter->second.Pseudorange_m, 3), 14); // Loss of lock indicator (LLI) int32_t lli = 0; // Include in the observation!! if (lli == 0) { lineObs += std::string(1, ' '); } // Signal Strength Indicator (SSI) int32_t ssi = Rinex_Printer::signalStrength(iter->second.CN0_dB_hz); lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // CARRIER PHASE lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_phase_rads / (BEIDOU_DNAV_TWO_PI), 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // DOPPLER lineObs += Rinex_Printer::rightJustify(asString(iter->second.Carrier_Doppler_hz, 3), 14); if (lli == 0) { lineObs += std::string(1, ' '); } lineObs += Rinex_Printer::rightJustify(Rinex_Printer::asString(ssi), 1); // SIGNAL STRENGTH lineObs += Rinex_Printer::rightJustify(asString(iter->second.CN0_dB_hz, 3), 14); } if (lineObs.size() < 80) { lineObs += std::string(80 - lineObs.size(), ' '); } out << lineObs << std::endl; } } void Rinex_Printer::to_date_time(int32_t gps_week, int32_t gps_tow, int& year, int& month, int& day, int& hour, int& minute, int& second) { // represents GPS time (week, TOW) in the date time format of the Gregorian calendar. // -> Leap years are considered, but leap seconds are not. std::array days_per_month{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // seconds in a not leap year const int32_t secs_per_day = 24 * 60 * 60; const int32_t secs_per_week = 7 * secs_per_day; const int32_t secs_per_normal_year = 365 * secs_per_day; const int32_t secs_per_leap_year = secs_per_normal_year + secs_per_day; // the GPS epoch is 06.01.1980 00:00, i.e. midnight 5. / 6. January 1980 // -> seconds since then int32_t secs_since_gps_epoch = gps_week * secs_per_week + gps_tow; // find year, consider leap years bool is_leap_year; int32_t remaining_secs = secs_since_gps_epoch + 5 * secs_per_day; for (int32_t y = 1980; true; y++) { is_leap_year = y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); int32_t secs_in_year_y = is_leap_year ? secs_per_leap_year : secs_per_normal_year; if (secs_in_year_y <= remaining_secs) { remaining_secs -= secs_in_year_y; } else { year = y; //std::cout << "year: year=" << year << " secs_in_year_y="<< secs_in_year_y << " remaining_secs="<< remaining_secs << std::endl; break; } //std::cout << "year: y=" << y << " secs_in_year_y="<< secs_in_year_y << " remaining_secs="<< remaining_secs << std::endl; } // find month for (int32_t m = 1; true; m++) { int32_t secs_in_month_m = days_per_month[m - 1] * secs_per_day; if (is_leap_year && m == 2) // consider February of leap year { secs_in_month_m += secs_per_day; } if (secs_in_month_m <= remaining_secs) { remaining_secs -= secs_in_month_m; } else { month = m; //std::cout << "month: month=" << month << " secs_in_month_m="<< secs_in_month_m << " remaining_secs="<< remaining_secs << std::endl; break; } //std::cout << "month: m=" << m << " secs_in_month_m="<< secs_in_month_m << " remaining_secs="<< remaining_secs << std::endl; } day = remaining_secs / secs_per_day + 1; remaining_secs = remaining_secs % secs_per_day; hour = remaining_secs / (60 * 60); remaining_secs = remaining_secs % (60 * 60); minute = remaining_secs / 60; second = remaining_secs % 60; } //void Rinex_Printer::log_rinex_sbs(std::fstream& out, const Sbas_Raw_Msg& sbs_message) //{ // // line 1: PRN / EPOCH / RCVR // std::stringstream line1; // // // SBAS PRN // line1 << sbs_message.get_prn(); // line1 << " "; // // // gps time of reception // int32_t gps_week; // double gps_sec; // if(sbs_message.get_rx_time_obj().get_gps_time(gps_week, gps_sec)) // { // int32_t year; // int32_t month; // int32_t day; // int32_t hour; // int32_t minute; // int32_t second; // // double gps_sec_one_digit_precicion = round(gps_sec *10)/10; // to prevent rounding towards 60.0sec in the stream output // int32_t gps_tow = trunc(gps_sec_one_digit_precicion); // double sub_sec = gps_sec_one_digit_precicion - double(gps_tow); // // to_date_time(gps_week, gps_tow, year, month, day, hour, minute, second); // line1 << asFixWidthString(year, 2, '0') << " " << asFixWidthString(month, 2, '0') << " " << asFixWidthString(day, 2, '0') << " " << asFixWidthString(hour, 2, '0') << " " << asFixWidthString(minute, 2, '0') << " " << rightJustify(asString(double(second)+sub_sec,1),4,' '); // } // else // { // line1 << std::string(19, ' '); // } // line1 << " "; // // // band // line1 << "L1"; // line1 << " "; // // // Length of data message (bytes) // line1 << asFixWidthString(sbs_message.get_msg().size(), 3, ' '); // line1 << " "; // // File-internal receiver index // line1 << " 0"; // line1 << " "; // // Transmission System Identifier // line1 << "SBA"; // line1 << std::string(35, ' '); // lengthCheck(line1.str()); // out << line1.str() << std::endl; // // // DATA RECORD - 1 // std::stringstream line2; // line2 << " "; // // Message frame identifier // if (sbs_message.get_msg_type() < 10) line2 << " "; // line2 << sbs_message.get_msg_type(); // line2 << std::string(4, ' '); // // First 18 bytes of message (hex) // std::vector msg = sbs_message.get_msg(); // for (size_t i = 0; i < 18 && i < msg.size(); ++i) // { // line2 << std::hex << std::setfill('0') << std::setw(2); // line2 << int(msg[i]) << " "; // } // line2 << std::string(19, ' '); // lengthCheck(line2.str()); // out << line2.str() << std::endl; // // // DATA RECORD - 2 // std::stringstream line3; // line3 << std::string(7, ' '); // // Remaining bytes of message (hex) // for (size_t i = 18; i < 36 && i < msg.size(); ++i) // { // line3 << std::hex << std::setfill('0') << std::setw(2); // line3 << int(msg[i]) << " "; // } // line3 << std::string(31, ' '); // lengthCheck(line3.str()); // out << line3.str() << std::endl; //} int32_t Rinex_Printer::signalStrength(const double snr) { int32_t ss; ss = int(std::min(std::max(int(floor(snr / 6)), 1), 9)); return ss; } boost::posix_time::ptime Rinex_Printer::compute_UTC_time(const Gps_Navigation_Message& nav_msg) { // if we are processing a file -> wait to leap second to resolve the ambiguity else take the week from the local system time // idea: resolve the ambiguity with the leap second http://www.colorado.edu/geography/gcraft/notes/gps/gpseow.htm const double utc_t = nav_msg.utc_time(nav_msg.d_TOW); boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((utc_t + 604800 * static_cast(nav_msg.i_GPS_week)) * 1000)); if (nav_msg.i_GPS_week < 512) { boost::posix_time::ptime p_time(boost::gregorian::date(2019, 4, 7), t); return p_time; } boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rinex_Printer::compute_BDS_time(const Beidou_Dnav_Ephemeris& eph, const double obs_time) { // The RINEX v2.11 v3.00 format uses GPS time for the observations epoch, not UTC time, thus, no leap seconds needed here. // (see Section 3 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt) // (see Pag. 17 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex300.pdf) // --??? No time correction here, since it will be done in the RINEX processor const double bds_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((bds_t + 604800 * static_cast(eph.i_BEIDOU_week % 8192)) * 1000)); boost::posix_time::ptime p_time(boost::gregorian::date(2006, 1, 1), t); return p_time; } boost::posix_time::ptime Rinex_Printer::compute_GPS_time(const Gps_Ephemeris& eph, const double obs_time) { // The RINEX v2.11 v3.00 format uses GPS time for the observations epoch, not UTC time, thus, no leap seconds needed here. // (see Section 3 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt) // (see Pag. 17 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex300.pdf) // No time correction here, since it will be done in the PVT processor boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((obs_time + 604800 * static_cast(eph.i_GPS_week % 1024)) * 1000)); // Handle TOW rollover if (obs_time < 18.0) { t += boost::posix_time::seconds(604800); } // Handle week rollover (valid from 2009 to 2029) if (eph.i_GPS_week < 512) { boost::posix_time::ptime p_time(boost::gregorian::date(2019, 4, 7), t); return p_time; } boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rinex_Printer::compute_GPS_time(const Gps_CNAV_Ephemeris& eph, const double obs_time) { // The RINEX v2.11 v3.00 format uses GPS time for the observations epoch, not UTC time, thus, no leap seconds needed here. // (see Section 3 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt) // (see Pag. 17 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex300.pdf) // --??? No time correction here, since it will be done in the RINEX processor boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((obs_time + 604800 * static_cast(eph.i_GPS_week % 1024)) * 1000)); boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rinex_Printer::compute_Galileo_time(const Galileo_Ephemeris& eph, const double obs_time) { // The RINEX v2.11 v3.00 format uses Galileo time for the observations epoch, not UTC time, thus, no leap seconds needed here. // (see Pag. 17 in http://igscb.jpl.nasa.gov/igscb/data/format/rinex301.pdf) // --??? No time correction here, since it will be done in the RINEX processor double galileo_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((galileo_t + 604800 * static_cast(eph.WN_5)) * 1000)); // boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rinex_Printer::compute_UTC_time(const Glonass_Gnav_Ephemeris& eph, const double obs_time) { double tod = 0.0; double glot2utc = 3 * 3600; double obs_time_glot = 0.0; int32_t i = 0; // Get observation time in nearly GLONASS time. Correction for leap seconds done at the end obs_time_glot = obs_time + glot2utc; // Get seconds of day in glonass time tod = fmod(obs_time_glot, 86400); // Form date and time duration types boost::posix_time::time_duration t1(0, 0, tod); boost::gregorian::date d1(eph.d_yr, 1, 1); boost::gregorian::days d2(eph.d_N_T - 1); boost::posix_time::ptime glo_time(d1 + d2, t1); // Convert to utc boost::posix_time::time_duration t2(0, 0, glot2utc); boost::posix_time::ptime utc_time = glo_time - t2; // Adjust for leap second correction for (i = 0; GLONASS_LEAP_SECONDS[i][0] > 0; i++) { boost::posix_time::time_duration t3(GLONASS_LEAP_SECONDS[i][3], GLONASS_LEAP_SECONDS[i][4], GLONASS_LEAP_SECONDS[i][5]); boost::gregorian::date d3(GLONASS_LEAP_SECONDS[i][0], GLONASS_LEAP_SECONDS[i][1], GLONASS_LEAP_SECONDS[i][2]); boost::posix_time::ptime ls_time(d3, t3); if (utc_time >= ls_time) { // We subtract the leap second when going from gpst to utc, values store as negatives utc_time = utc_time + boost::posix_time::time_duration(0, 0, GLONASS_LEAP_SECONDS[i][6]); break; } } return utc_time; } double Rinex_Printer::get_leap_second(const Glonass_Gnav_Ephemeris& eph, const double gps_obs_time) { double tod = 0.0; double glot2utc = 3 * 3600; double obs_time_glot = 0.0; int32_t i = 0; double leap_second = 0; // Get observation time in nearly GLONASS time. Correction for leap seconds done at the end obs_time_glot = gps_obs_time + glot2utc; // Get seconds of day in glonass time tod = fmod(obs_time_glot, 86400); // Form date and time duration types boost::posix_time::time_duration t1(0, 0, tod); boost::gregorian::date d1(eph.d_yr, 1, 1); boost::gregorian::days d2(eph.d_N_T - 1); boost::posix_time::ptime glo_time(d1 + d2, t1); // Convert to utc boost::posix_time::time_duration t2(0, 0, glot2utc); boost::posix_time::ptime utc_time = glo_time - t2; // Adjust for leap second correction for (i = 0; GLONASS_LEAP_SECONDS[i][0] > 0; i++) { boost::posix_time::time_duration t3(GLONASS_LEAP_SECONDS[i][3], GLONASS_LEAP_SECONDS[i][4], GLONASS_LEAP_SECONDS[i][5]); boost::gregorian::date d3(GLONASS_LEAP_SECONDS[i][0], GLONASS_LEAP_SECONDS[i][1], GLONASS_LEAP_SECONDS[i][2]); boost::posix_time::ptime ls_time(d3, t3); if (utc_time >= ls_time) { // We subtract the leap second when going from gpst to utc leap_second = fabs(GLONASS_LEAP_SECONDS[i][6]); break; } } return leap_second; } /* enum RINEX_enumMarkerType { GEODETIC, //!< GEODETIC Earth-fixed, high-precision monumentation NON_GEODETIC, //!< NON_GEODETIC Earth-fixed, low-precision monumentation SPACEBORNE, //!< SPACEBORNE Orbiting space vehicle AIRBORNE , //!< AIRBORNE Aircraft, balloon, etc. WATER_CRAFT, //!< WATER_CRAFT Mobile water craft GROUND_CRAFT, //!< GROUND_CRAFT Mobile terrestrial vehicle FIXED_BUOY, //!< FIXED_BUOY "Fixed" on water surface FLOATING_BUOY, //!< FLOATING_BUOY Floating on water surface FLOATING_ICE, //!< FLOATING_ICE Floating ice sheet, etc. GLACIER, //!< GLACIER "Fixed" on a glacier BALLISTIC, //!< BALLISTIC Rockets, shells, etc ANIMAL, //!< ANIMAL Animal carrying a receiver HUMAN //!< HUMAN Human being }; */ src/algorithms/PVT/libs/rinex_printer.h000066400000000000000000001121121352176506000204640ustar00rootroot00000000000000/*! * \file rinex_printer.h * \brief Interface of a RINEX 2.11 / 3.01 printer * See http://igscb.jpl.nasa.gov/igscb/data/format/rinex301.pdf * * Receiver Independent EXchange Format (RINEX): * The first proposal for the Receiver Independent Exchange Format RINEX * was developed by the Astronomical Institute of the University of Berne * for the easy exchange of the GPS data to be collected during the large * European GPS campaign EUREF 89, which involved more than 60 GPS receivers * of 4 different manufacturers. * The governing aspect during the development was the fact that most geodetic * processing software for GPS data use a well-defined set of observables: * 1) The carrier-phase measurement at one or both carriers (actually being a * measurement on the beat frequency between the received carrier of the * satellite signal and a receiver-generated reference frequency). * 2) The pseudorange (code) measurement , equivalent to the difference * of the time of reception (expressed in the time frame of the receiver) * and the time of transmission (expressed in the time frame of the satellite) * of a distinct satellite signal. * 3) The observation time being the reading of the receiver clock at the * instant of validity of the carrier-phase and/or the code measurements. * Note: A collection of the formats currently used by the IGS can be found * here: http://igscb.jpl.nasa.gov/components/formats.html * \author Carles Fernandez Prades, 2011. cfernandez(at)cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RINEX_PRINTER_H_ #define GNSS_SDR_RINEX_PRINTER_H_ #include #include // for int32_t #include // for strtol, strtod #include // for fstream #include // for setprecision #include // for map #include // for stringstream #include // for string class Beidou_Dnav_Ephemeris; class Beidou_Dnav_Iono; class Beidou_Dnav_Utc_Model; class Galileo_Ephemeris; class Galileo_Iono; class Galileo_Utc_Model; class Glonass_Gnav_Almanac; class Glonass_Gnav_Ephemeris; class Glonass_Gnav_Utc_Model; class Gnss_Synchro; class Gps_CNAV_Ephemeris; class Gps_CNAV_Iono; class Gps_CNAV_Utc_Model; class Gps_Ephemeris; class Gps_Iono; class Gps_Navigation_Message; class Gps_Utc_Model; /*! * \brief Class that handles the generation of Receiver * INdependent EXchange format (RINEX) files */ class Rinex_Printer { public: /*! * \brief Default constructor. Creates GNSS Navigation and Observables RINEX files and their headers */ Rinex_Printer(int version = 0, const std::string& base_path = "."); /*! * \brief Default destructor. Closes GNSS Navigation and Observables RINEX files */ ~Rinex_Printer(); std::fstream obsFile; //!< Output file stream for RINEX observation file std::fstream navFile; //!< Output file stream for RINEX navigation data file std::fstream sbsFile; //!< Output file stream for RINEX SBAS raw data file std::fstream navGalFile; //!< Output file stream for RINEX Galileo navigation data file std::fstream navGloFile; //!< Output file stream for RINEX GLONASS navigation data file std::fstream navBdsFile; //!< Output file stream for RINEX Galileo navigation data file std::fstream navMixFile; //!< Output file stream for RINEX Mixed navigation data file /*! * \brief Generates the GPS L1 C/A Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_Iono& iono, const Gps_Utc_Model& utc_model, const Gps_Ephemeris& eph); /*! * \brief Generates the GPS L2C(M) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& iono, const Gps_CNAV_Utc_Model& utc_model); /*! * \brief Generates the Galileo Navigation Data header */ void rinex_nav_header(std::fstream& out, const Galileo_Iono& iono, const Galileo_Utc_Model& utc_model); /*! * \brief Generates the Mixed (GPS/Galileo) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model); /*! * \brief Generates the Mixed (GPS CNAV/Galileo) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& iono, const Gps_CNAV_Utc_Model& utc_model, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model); /*! * \brief Generates the GLONASS L1, L2 C/A Navigation Data header */ void rinex_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& utc_model, const Glonass_Gnav_Ephemeris& glonass_gnav_eph); /*! * \brief Generates the Mixed (Galileo/GLONASS) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); /*! * \brief Generates the Mixed (GPS L1 C/A/GLONASS L1, L2) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); /*! * \brief Generates the Mixed (GPS L2C C/A/GLONASS L1, L2) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_iono, const Gps_CNAV_Utc_Model& gps_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); /*! * \brief Generates the BDS B1I or B3I Navigation Data header */ void rinex_nav_header(std::fstream& out, const Beidou_Dnav_Iono& iono, const Beidou_Dnav_Utc_Model& utc_model); /*! * \brief Generates the Mixed GPS L1,L5 + BDS B1I, B3I Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model); /*! * \brief Generates the Mixed GPS L2C + BDS B1I, B3I Navigation Data header */ void rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_cnav_iono, const Gps_CNAV_Utc_Model& gps_cnav_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model); /*! * \brief Generates the Mixed GLONASS L1,L2 + BDS B1I, B3I Navigation Data header */ void rinex_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glo_gnav_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model); /*! * \brief Generates the Mixed (Galileo/BDS B1I, B3I) Navigation Data header */ void rinex_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model); /*! * \brief Generates the GPS Observation data header */ void rinex_obs_header(std::fstream& out, const Gps_Ephemeris& eph, const double d_TOW_first_observation); /*! * \brief Generates the GPS L2 Observation data header */ void rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& eph, const double d_TOW_first_observation, const std::string& gps_bands = "2S"); /*! * \brief Generates the dual frequency GPS L1 & L2/L5 Observation data header */ void rinex_obs_header(std::fstream& out, const Gps_Ephemeris& eph, const Gps_CNAV_Ephemeris& eph_cnav, const double d_TOW_first_observation, const std::string& gps_bands = "1C 2S"); /*! * \brief Generates the Galileo Observation data header. Example: bands("1B"), bands("1B 5X"), bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Galileo_Ephemeris& eph, const double d_TOW_first_observation, const std::string& bands = "1B"); /*! * \brief Generates the Mixed (GPS/Galileo) Observation data header. Example: galileo_bands("1B"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& galileo_bands = "1B"); /*! * \brief Generates the Mixed (GPS/Galileo) Observation data header. Example: galileo_bands("1B"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& eph_cnav, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& gps_bands = "1C 2S", const std::string& galileo_bands = "1B"); /*! * \brief Generates the Mixed (GPS/Galileo) Observation data header. Example: galileo_bands("1B"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& eph_cnav, const Galileo_Ephemeris& galileo_eph, const double d_TOW_first_observation, const std::string& gps_bands = "2S", const std::string& galileo_bands = "1B"); /*! * \brief Generates the GLONASS GNAV Observation data header. Example: bands("1C"), bands("1C 2C"), bands("2C"), ... Default: "1C". */ void rinex_obs_header(std::fstream& out, const Glonass_Gnav_Ephemeris& eph, const double d_TOW_first_observation, const std::string& bands = "1G"); /*! * \brief Generates the Mixed (GPS L1 C/A /GLONASS) Observation data header. Example: galileo_bands("1C"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Gps_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& glonass_bands = "1C"); /*! * \brief Generates the Mixed (Galileo/GLONASS) Observation data header. Example: galileo_bands("1C"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void rinex_obs_header(std::fstream& out, const Galileo_Ephemeris& galileo_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& galileo_bands = "1B", const std::string& glonass_bands = "1C"); /*! * \brief Generates the Mixed (GPS L2C/GLONASS) Observation data header. Example: galileo_bands("1G")... Default: "1G". */ void rinex_obs_header(std::fstream& out, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double d_TOW_first_observation, const std::string& glonass_bands = "1G"); /*! * \brief Generates the a Beidou B1I Observation data header. Example: beidou_bands("B1") */ void rinex_obs_header(std::fstream& out, const Beidou_Dnav_Ephemeris& eph, const double d_TOW_first_observation, const std::string& bands); /*! * \brief Generates the SBAS raw data header */ void rinex_sbs_header(std::fstream& out); /*! * \brief Computes the BDS Time and returns a boost::posix_time::ptime object * \details Function used to convert the observation time into BDT time which is used * as the default time for RINEX files * \param eph BeiDou DNAV Ephemeris object * \param obs_time Observation time in BDT seconds of week */ boost::posix_time::ptime compute_BDS_time(const Beidou_Dnav_Ephemeris& eph, const double obs_time); /*! * \brief Computes the UTC time and returns a boost::posix_time::ptime object */ boost::posix_time::ptime compute_UTC_time(const Gps_Navigation_Message& nav_msg); /*! * \brief Computes the GPS time and returns a boost::posix_time::ptime object */ boost::posix_time::ptime compute_GPS_time(const Gps_Ephemeris& eph, const double obs_time); /*! * \brief Computes the GPS time and returns a boost::posix_time::ptime object */ boost::posix_time::ptime compute_GPS_time(const Gps_CNAV_Ephemeris& eph, const double obs_time); /*! * \brief Computes the Galileo time and returns a boost::posix_time::ptime object */ boost::posix_time::ptime compute_Galileo_time(const Galileo_Ephemeris& eph, const double obs_time); /*! * \brief Computes the UTC Time and returns a boost::posix_time::ptime object * \details Function used as a method to convert the observation time into UTC time which is used * as the default time for RINEX files * \param eph GLONASS GNAV Ephemeris object * \param obs_time Observation time in GPS seconds of week */ boost::posix_time::ptime compute_UTC_time(const Glonass_Gnav_Ephemeris& eph, const double obs_time); /*! * \brief Computes number of leap seconds of GPS relative to UTC * \param eph GLONASS GNAV Ephemeris object * \param gps_obs_time Observation time in GPS seconds of week */ double get_leap_second(const Glonass_Gnav_Ephemeris& eph, const double gps_obs_time); /*! * \brief Writes data from the GPS L1 C/A navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the GPS L2 navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Galileo navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Mixed (GPS/Galileo) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& galileo_eph_map); /*! * \brief Writes data from the Mixed (GPS/Galileo) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_cnav_eph_map, const std::map& galileo_eph_map); /*! * \brief Writes data from the GLONASS GNAV navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Mixed (GPS/GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes data from the Mixed (GPS/GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_cnav_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes data from the Mixed (Galileo/ GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& galileo_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes data from the Beidou B1I navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes GPS L1 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, double obs_time, const std::map& observables); /*! * \brief Writes GPS L2 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& eph, double obs_time, const std::map& observables); /*! * \brief Writes dual frequency GPS L1 and L2 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, const Gps_CNAV_Ephemeris& eph_cnav, double obs_time, const std::map& observables); /*! * \brief Writes Galileo observables into the RINEX file. Example: galileo_bands("1B"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& eph, double obs_time, const std::map& observables, const std::string& galileo_bands = "1B"); /*! * \brief Writes Mixed GPS / Galileo observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Galileo_Ephemeris& galileo_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed GPS / Galileo observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& eph, const Galileo_Ephemeris& galileo_eph, double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed GPS / Galileo observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& galileo_eph, double gps_obs_time, const std::map& observables); /*! * \brief Writes GLONASS GNAV observables into the RINEX file. Example: glonass_bands("1C"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void log_rinex_obs(std::fstream& out, const Glonass_Gnav_Ephemeris& eph, double obs_time, const std::map& observables, const std::string& glonass_bands = "1C"); /*! * \brief Writes Mixed GPS L1 C/A - GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed GPS L2C - GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed Galileo/GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& galileo_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double galileo_obs_time, const std::map& observables); /*! * \brief Writes BDS B1I observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Beidou_Dnav_Ephemeris& eph, double obs_time, const std::map& observables, const std::string& bds_bands); /*! * \brief Represents GPS time in the date time format. Leap years are considered, but leap seconds are not. */ void to_date_time(int gps_week, int gps_tow, int& year, int& month, int& day, int& hour, int& minute, int& second); /*! * \brief Writes raw SBAS messages into the RINEX file */ //void log_rinex_sbs(std::fstream & out, const Sbas_Raw_Msg & sbs_message); void update_nav_header(std::fstream& out, const Gps_Utc_Model& utc_model, const Gps_Iono& gps_iono, const Gps_Ephemeris& eph); void update_nav_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model, const Gps_CNAV_Iono& iono); void update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Gps_Ephemeris& eph, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model); void update_nav_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model, const Gps_CNAV_Iono& iono, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model); void update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& utc_model); void update_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc, const Gps_Ephemeris& eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_iono, const Gps_CNAV_Utc_Model& gps_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Beidou_Dnav_Utc_Model& utc_model, const Beidou_Dnav_Iono& beidou_dnav_iono); void update_obs_header(std::fstream& out, const Gps_Utc_Model& utc_model); void update_obs_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model); void update_obs_header(std::fstream& out, const Galileo_Utc_Model& galileo_utc_model); void update_obs_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); void update_obs_header(std::fstream& out, const Beidou_Dnav_Utc_Model& utc_model); std::map satelliteSystem; //!< GPS, GLONASS, SBAS payload, Galileo or Beidou std::map observationType; //!< PSEUDORANGE, CARRIER_PHASE, DOPPLER, SIGNAL_STRENGTH std::map observationCode; //!< GNSS observation descriptors std::string stringVersion; //!< RINEX version (2.10/2.11 or 3.01/3.02) std::string navfilename; std::string obsfilename; std::string sbsfilename; std::string navGalfilename; std::string navGlofilename; std::string navBdsfilename; std::string navMixfilename; private: int version; // RINEX version (2 for 2.10/2.11 and 3 for 3.01) int numberTypesObservations; // Number of available types of observable in the system. Should be public? /* * Generation of RINEX signal strength indicators */ int signalStrength(const double snr); /* Creates RINEX file names according to the naming convention * * See http://igscb.jpl.nasa.gov/igscb/data/format/rinex301.pdf * Section 4, page 6 * * \param[in] type of RINEX file. Can be: * "RINEX_FILE_TYPE_OBS" - Observation file. * "RINEX_FILE_TYPE_GPS_NAV" - GPS navigation message file. * "RINEX_FILE_TYPE_MET" - Meteorological data file. * "RINEX_FILE_TYPE_GLO_NAV" - GLONASS navigation file. * "RINEX_FILE_TYPE_GAL_NAV" - Galileo navigation message file. * "RINEX_FILE_TYPE_MIXED_NAV" - Mixed GNSS navigation message file. * "RINEX_FILE_TYPE_GEO_NAV" - SBAS Payload navigation message file. * "RINEX_FILE_TYPE_SBAS" - SBAS broadcast data file. * "RINEX_FILE_TYPE_CLK" - Clock file. */ std::string createFilename(const std::string& type); /* * Generates the data for the PGM / RUN BY / DATE line */ std::string getLocalTime(); /* * Checks that the line is 80 characters length */ void lengthCheck(const std::string& line); double fake_cnav_iode; /* * If the string is bigger than length, truncate it from the right. * otherwise, add pad characters to its right. * * Left-justifies the input in a string of the specified * length. If the new length (\a length) is larger than the * current length, the string is extended by the pad * character (\a pad). The default pad character is a * blank. * \param[in] s string to be modified. * \param[in] length new desired length of string. * \param[in] pad character to pad string with (blank by default). * \return a reference to \a s. */ inline std::string& leftJustify(std::string& s, const std::string::size_type length, const char pad = ' '); /* * If the string is bigger than length, truncate it from the right. * otherwise, add pad characters to its right. * * Left-justifies the receiver in a string of the specified * length (const version). If the new length (\a length) is larger * than the current length, the string is extended by the pad * character (\a pad). The default pad character is a * blank. * \param[in] s string to be modified. * \param[in] length new desired length of string. * \param[in] pad character to pad string with (blank by default). * \return a reference to \a s. */ inline std::string leftJustify(const std::string& s, const std::string::size_type length, const char pad = ' ') { std::string t(s); return leftJustify(t, length, pad); } /* * Right-justifies the receiver in a string of the specified * length. If the receiver's data is shorter than the * requested length (\a length), it is padded on the left with * the pad character (\a pad). The default pad * character is a blank. */ inline std::string& rightJustify(std::string& s, const std::string::size_type length, const char pad = ' '); /* * Right-justifies the receiver in a string of the specified * length (const version). If the receiver's data is shorter than the * requested length (\a length), it is padded on the left with * the pad character (\a pad). The default pad * character is a blank.*/ inline std::string rightJustify(const std::string& s, const std::string::size_type length, const char pad = ' ') { std::string t(s); return rightJustify(t, length, pad); } /* * Convert a double to a scientific notation number. * @param d the double to convert * @param length length (in characters) of output, including exponent * @param expLen length (in characters) of the exponent, with sign * @param showSign if true, reserves 1 character for +/- sign * @param checkSwitch if true, keeps the exponential sanity check for * exponentials above three characters in length. If false, it removes * that check. */ inline std::string doub2sci(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool showSign = true, const bool checkSwitch = true); /* * Convert scientific notation to FORTRAN notation. * As an example, the string "1.5636E5" becomes " .15636D6". * Note that the first character of the string will be '-' if * the number is negative or ' ' if the first character is positive. * @param aStr string with number to convert * @param startPos start position of number in string * @param length length (in characters) of number, including exponent. * @param expLen length (in characters of exponent, not including sign. * @param checkSwitch will keep the method running as originally programmed * when set to true. If false, the method will always resize exponentials, * produce an exponential with an E instead of a D, and always have a leading * zero. For example -> 0.87654E-0004 or -0.1234E00005. */ inline std::string& sci2for(std::string& aStr, const std::string::size_type startPos = 0, const std::string::size_type length = std::string::npos, const std::string::size_type expLen = 3, const bool checkSwitch = true); /* * Convert double precision floating point to a string * containing the number in FORTRAN notation. * As an example, the number 156360 becomes ".15636D6". * @param d number to convert. * @param length length (in characters) of number, including exponent. * @param expLen length (in characters of exponent, including sign. * @param checkSwitch if true, keeps the exponential sanity check for * exponentials above three characters in length. If false, it removes * that check. * @return a string containing \a d in FORTRAN notation. */ inline std::string doub2for(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch = true); /* * Convert a string to a double precision floating point number. * @param s string containing a number. * @return double representation of string. */ inline double asDouble(const std::string& s) { return strtod(s.c_str(), nullptr); } inline int toInt(std::string bitString, int sLength); /* * Convert a string to an integer. * @param s string containing a number. * @return int64_t integer representation of string. */ inline int64_t asInt(const std::string& s) { return strtol(s.c_str(), nullptr, 10); } /* * Convert a double to a string in fixed notation. * @param x double. * @param precision the number of decimal places you want displayed. * @return string representation of \a x. */ inline std::string asString(const double x, const std::string::size_type precision = 17); /* * Convert a long double to a string in fixed notation. * @param x long double. * @param precision the number of decimal places you want displayed. * @return string representation of \a x. */ inline std::string asString(const long double x, const std::string::size_type precision = 21); /* * Convert any old object to a string. * The class must have stream operators defined. * @param x object to turn into a string. * @return string representation of \a x. */ template inline std::string asString(const X x); inline std::string asFixWidthString(const int x, const int width, char fill_digit); }; // Implementation of inline functions (modified versions from GPSTk http://www.gpstk.org) inline std::string& Rinex_Printer::leftJustify(std::string& s, const std::string::size_type length, const char pad) { if (length < s.length()) { s = s.substr(0, length); } else { s.append(length - s.length(), pad); } return s; } // if the string is bigger than length, truncate it from the left. // otherwise, add pad characters to its left. inline std::string& Rinex_Printer::rightJustify(std::string& s, const std::string::size_type length, const char pad) { if (length < s.length()) { s = s.substr(s.length() - length, std::string::npos); } else { s.insert(static_cast(0), length - s.length(), pad); } return s; } inline std::string Rinex_Printer::doub2for(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch) { short exponentLength = expLen; /* Validate the assumptions regarding the input arguments */ if (exponentLength < 0) { exponentLength = 1; } if (exponentLength > 3 && checkSwitch) { exponentLength = 3; } std::string toReturn = doub2sci(d, length, exponentLength, true, checkSwitch); sci2for(toReturn, 0, length, exponentLength, checkSwitch); return toReturn; } inline std::string Rinex_Printer::doub2sci(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool showSign, const bool checkSwitch) { std::string toReturn; short exponentLength = expLen; /* Validate the assumptions regarding the input arguments */ if (exponentLength < 0) { exponentLength = 1; } if (exponentLength > 3 && checkSwitch) { exponentLength = 3; } std::stringstream c; c.setf(std::ios::scientific, std::ios::floatfield); // length - 3 for special characters ('.', 'e', '+' or '-') // - exponentlength (e04) // - 1 for the digit before the decimal (2.) // and if showSign == true, // an extra -1 for '-' or ' ' if it's positive or negative int expSize = 0; if (showSign) { expSize = 1; } c.precision(length - 3 - exponentLength - 1 - expSize); c << d; c >> toReturn; return toReturn; } inline std::string& Rinex_Printer::sci2for(std::string& aStr, const std::string::size_type startPos, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch) { std::string::size_type idx = aStr.find('.', startPos); int expAdd = 0; std::string exp; int64_t iexp; //If checkSwitch is false, always redo the exponential. Otherwise, //set it to false. bool redoexp = !checkSwitch; // Check for decimal place within specified boundaries if ((idx <= 0) || (idx >= (startPos + length - expLen - 1))) { // Error: no decimal point in string return aStr; } // Here, account for the possibility that there are // no numbers to the left of the decimal, but do not // account for the possibility of non-scientific // notation (more than one digit to the left of the // decimal) if (idx > startPos) { redoexp = true; // Swap digit and decimal. aStr[idx] = aStr[idx - 1]; aStr[idx - 1] = '.'; // Only add one to the exponent if the number is non-zero if (asDouble(aStr.substr(startPos, length)) != 0.0) { expAdd = 1; } } idx = aStr.find('e', startPos); if (idx == std::string::npos) { idx = aStr.find('E', startPos); if (idx == std::string::npos) { // Error: no 'e' or 'E' in string"; } } // Change the exponent character to D normally, or E of checkSwitch is false. if (checkSwitch) { aStr[idx] = 'D'; } else { aStr[idx] = 'E'; } // Change the exponent itself if (redoexp) { exp = aStr.substr(idx + 1, std::string::npos); iexp = asInt(exp); iexp += expAdd; aStr.erase(idx + 1); if (iexp < 0) { aStr += "-"; iexp -= iexp * 2; } else { aStr += "+"; } aStr += Rinex_Printer::rightJustify(asString(iexp), expLen, '0'); } // if the number is positive, append a space // (if it's negative, there's a leading '-' if (aStr[0] == '.') { aStr.insert(static_cast(0), 1, ' '); } //If checkSwitch is false, add on one leading zero to the string if (!checkSwitch) { aStr.insert(static_cast(1), 1, '0'); } return aStr; } // end sci2for inline std::string asString(const long double x, const std::string::size_type precision) { std::ostringstream ss; ss << std::fixed << std::setprecision(precision) << x; return ss.str(); } inline std::string Rinex_Printer::asString(const double x, const std::string::size_type precision) { std::ostringstream ss; ss << std::fixed << std::setprecision(precision) << x; return ss.str(); } inline std::string Rinex_Printer::asFixWidthString(const int x, const int width, char fill_digit) { std::ostringstream ss; ss << std::setfill(fill_digit) << std::setw(width) << x; return ss.str().substr(ss.str().size() - width); } inline int64_t asInt(const std::string& s) { return strtol(s.c_str(), nullptr, 10); } inline int Rinex_Printer::toInt(std::string bitString, int sLength) { int tempInt; int num = 0; for (int i = 0; i < sLength; i++) { tempInt = bitString[i] - '0'; num |= (1 << (sLength - 1 - i)) * tempInt; } return num; } template inline std::string Rinex_Printer::asString(const X x) { std::ostringstream ss; ss << x; return ss.str(); } #endif src/algorithms/PVT/libs/rtcm.cc000066400000000000000000005701361352176506000167150ustar00rootroot00000000000000/*! * \file rtcm.cc * \brief Implementation of RTCM 3.2 Standard * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rtcm.h" #include "GLONASS_L1_L2_CA.h" #include "GPS_L1_CA.h" #include "GPS_L2C.h" #include "Galileo_E1.h" #include "Galileo_E5a.h" #include // for to_upper_copy #include #include #include #include #include // for std::reverse #include // std::chrono::seconds #include // for std::fmod #include // for strtol #include // for cout #include // for std::stringstream Rtcm::Rtcm(uint16_t port) { RTCM_port = port; preamble = std::bitset<8>("11010011"); reserved_field = std::bitset<6>("000000"); rtcm_message_queue = std::make_shared >(); boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), RTCM_port); servers.emplace_back(io_context, endpoint); server_is_running = false; } Rtcm::~Rtcm() { if (server_is_running) { try { stop_server(); } catch (const boost::exception& e) { LOG(WARNING) << "Boost exception: " << boost::diagnostic_information(e); } catch (const std::exception& ex) { LOG(WARNING) << "STD exception: " << ex.what(); } } } // ***************************************************************************************************** // // TCP Server helper classes // // ***************************************************************************************************** void Rtcm::run_server() { std::cout << "Starting a TCP/IP server of RTCM messages on port " << RTCM_port << std::endl; try { std::thread tq([&] { std::make_shared(io_context, rtcm_message_queue, RTCM_port)->do_read_queue(); }); tq.detach(); std::thread t([&] { io_context.run(); }); server_is_running = true; t.detach(); } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } } void Rtcm::stop_service() { io_context.stop(); } void Rtcm::stop_server() { std::cout << "Stopping TCP/IP server on port " << RTCM_port << std::endl; rtcm_message_queue->push("Goodbye"); // this terminates tq Rtcm::stop_service(); servers.front().close_server(); std::this_thread::sleep_for(std::chrono::seconds(1)); server_is_running = false; } void Rtcm::send_message(const std::string& msg) { rtcm_message_queue->push(msg); } bool Rtcm::is_server_running() const { return server_is_running; } // ***************************************************************************************************** // // TRANSPORT LAYER AS DEFINED AT RTCM STANDARD 10403.2 // // ***************************************************************************************************** std::string Rtcm::add_CRC(const std::string& message_without_crc) const { // ****** Computes Qualcomm CRC-24Q ****** boost::crc_optimal<24, 0x1864CFBU, 0x0, 0x0, false, false> CRC_RTCM; // 1) Converts the string to a vector of uint8_t: boost::dynamic_bitset frame_bits(message_without_crc); std::vector bytes; boost::to_block_range(frame_bits, std::back_inserter(bytes)); std::reverse(bytes.begin(), bytes.end()); // 2) Computes CRC CRC_RTCM.process_bytes(bytes.data(), bytes.size()); std::bitset<24> crc_frame = std::bitset<24>(CRC_RTCM.checksum()); // 3) Builds the complete message std::string complete_message = message_without_crc + crc_frame.to_string(); return bin_to_binary_data(complete_message); } bool Rtcm::check_CRC(const std::string& message) const { boost::crc_optimal<24, 0x1864CFBU, 0x0, 0x0, false, false> CRC_RTCM_CHECK; // Convert message to binary std::string message_bin = Rtcm::binary_data_to_bin(message); // Check CRC std::string crc = message_bin.substr(message_bin.length() - 24, 24); std::bitset<24> read_crc = std::bitset<24>(crc); std::string msg_without_crc = message_bin.substr(0, message_bin.length() - 24); boost::dynamic_bitset frame_bits(msg_without_crc); std::vector bytes; boost::to_block_range(frame_bits, std::back_inserter(bytes)); std::reverse(bytes.begin(), bytes.end()); CRC_RTCM_CHECK.process_bytes(bytes.data(), bytes.size()); std::bitset<24> computed_crc = std::bitset<24>(CRC_RTCM_CHECK.checksum()); if (read_crc == computed_crc) { return true; } return false; } std::string Rtcm::bin_to_binary_data(const std::string& s) const { std::string s_aux; auto remainder = static_cast(std::fmod(s.length(), 8)); std::vector c; c.reserve(s.length()); uint32_t k = 0; if (remainder != 0) { s_aux.assign(s, 0, remainder); boost::dynamic_bitset<> rembits(s_aux); uint64_t n = rembits.to_ulong(); c[0] = static_cast(n); k++; } uint32_t start = std::max(remainder, 0); for (uint32_t i = start; i < s.length() - 1; i = i + 8) { s_aux.assign(s, i, 4); std::bitset<4> bs(s_aux); uint32_t n = bs.to_ulong(); s_aux.assign(s, i + 4, 4); std::bitset<4> bs2(s_aux); uint32_t n2 = bs2.to_ulong(); c[k] = static_cast(n * 16) + static_cast(n2); k++; } std::string ret(c.begin(), c.begin() + k); return ret; } std::string Rtcm::binary_data_to_bin(const std::string& s) const { std::string s_aux; std::stringstream ss; for (char i : s) { auto val = static_cast(i); std::bitset<8> bs(val); ss << bs; } s_aux = ss.str(); return s_aux; } std::string Rtcm::bin_to_hex(const std::string& s) const { std::string s_aux; std::stringstream ss; auto remainder = static_cast(std::fmod(s.length(), 4)); if (remainder != 0) { s_aux.assign(s, 0, remainder); boost::dynamic_bitset<> rembits(s_aux); uint32_t n = rembits.to_ulong(); ss << std::hex << n; } uint32_t start = std::max(remainder, 0); for (uint32_t i = start; i < s.length() - 1; i = i + 4) { s_aux.assign(s, i, 4); std::bitset<4> bs(s_aux); uint32_t n = bs.to_ulong(); ss << std::hex << n; } return boost::to_upper_copy(ss.str()); } std::string Rtcm::hex_to_bin(const std::string& s) const { std::string s_aux; s_aux.clear(); std::stringstream ss; ss << s; std::string s_lower = boost::to_upper_copy(ss.str()); for (uint32_t i = 0; i < s.length(); i++) { uint64_t n; std::istringstream(s_lower.substr(i, 1)) >> std::hex >> n; std::bitset<4> bs(n); s_aux += bs.to_string(); } return s_aux; } uint32_t Rtcm::bin_to_uint(const std::string& s) const { if (s.length() > 32) { LOG(WARNING) << "Cannot convert to a uint32_t"; return 0; } uint32_t reading = strtoul(s.c_str(), nullptr, 2); return reading; } int32_t Rtcm::bin_to_int(const std::string& s) const { if (s.length() > 32) { LOG(WARNING) << "Cannot convert to a int32_t"; return 0; } int32_t reading; // Handle negative numbers if (s.substr(0, 1) != "0") { // Computing two's complement boost::dynamic_bitset<> original_bitset(s); original_bitset.flip(); reading = -(original_bitset.to_ulong() + 1); } else { reading = strtol(s.c_str(), nullptr, 2); } return reading; } int32_t Rtcm::bin_to_sint(const std::string& s) const { if (s.length() > 32) { LOG(WARNING) << "Cannot convert to a int32_t"; return 0; } int32_t reading; int32_t sign; // Check for sign bit as defined RTCM doc if (s.substr(0, 1) != "0") { sign = 1; // Get the magnitude of the value reading = strtol((s.substr(1)).c_str(), nullptr, 2); } else { sign = -1; // Get the magnitude of the value reading = strtol((s.substr(1)).c_str(), nullptr, 2); } return sign * reading; } // Find the sign for glonass data fields (neg = 1, pos = 0) static inline uint64_t glo_sgn(double val) { if (val < 0) { return 1; // If value is negative return 1 } if (val == 0) { return 0; // Positive or equal to zero return 0 } return 0; } double Rtcm::bin_to_double(const std::string& s) const { double reading; if (s.length() > 64) { LOG(WARNING) << "Cannot convert to a double"; return 0; } int64_t reading_int; // Handle negative numbers if (s.substr(0, 1) != "0") { // Computing two's complement boost::dynamic_bitset<> original_bitset(s); original_bitset.flip(); std::string aux; to_string(original_bitset, aux); reading_int = -(strtoll(aux.c_str(), nullptr, 2) + 1); } else { reading_int = strtoll(s.c_str(), nullptr, 2); } reading = static_cast(reading_int); return reading; } uint64_t Rtcm::hex_to_uint(const std::string& s) const { if (s.length() > 32) { LOG(WARNING) << "Cannot convert to a uint64_t"; return 0; } uint64_t reading = strtoul(s.c_str(), nullptr, 16); return reading; } int64_t Rtcm::hex_to_int(const std::string& s) const { if (s.length() > 32) { LOG(WARNING) << "Cannot convert to a int64_t"; return 0; } int64_t reading = strtol(s.c_str(), nullptr, 16); return reading; } std::string Rtcm::build_message(const std::string& data) const { uint32_t msg_length_bits = data.length(); uint32_t msg_length_bytes = std::ceil(static_cast(msg_length_bits) / 8.0); std::bitset<10> message_length = std::bitset<10>(msg_length_bytes); uint32_t zeros_to_fill = 8 * msg_length_bytes - msg_length_bits; std::string b(zeros_to_fill, '0'); std::string msg_content = data + b; std::string msg_without_crc = preamble.to_string() + reserved_field.to_string() + message_length.to_string() + msg_content; return Rtcm::add_CRC(msg_without_crc); } // ***************************************************************************************************** // // MESSAGES AS DEFINED AT RTCM STANDARD 10403.2 // // ***************************************************************************************************** // ******************************************************** // // MESSAGE TYPE 1001 (GPS L1 OBSERVATIONS) // // ******************************************************** std::bitset<64> Rtcm::get_MT1001_4_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t smooth_int, bool sync_flag, bool divergence_free) { uint32_t reference_station_id = ref_id; // Max: 4095 const std::map& observables_ = observables; bool synchronous_GNSS_flag = sync_flag; bool divergence_free_smoothing_indicator = divergence_free; uint32_t smoothing_interval = smooth_int; Rtcm::set_DF002(msg_number); Rtcm::set_DF003(reference_station_id); Rtcm::set_DF004(obs_time); Rtcm::set_DF005(synchronous_GNSS_flag); Rtcm::set_DF006(observables_); Rtcm::set_DF007(divergence_free_smoothing_indicator); Rtcm::set_DF008(smoothing_interval); std::string header = DF002.to_string() + DF003.to_string() + DF004.to_string() + DF005.to_string() + DF006.to_string() + DF007.to_string() + DF008.to_string(); std::bitset<64> header_msg(header); return header_msg; } std::bitset<58> Rtcm::get_MT1001_sat_content(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF009(gnss_synchro); Rtcm::set_DF010(code_indicator); // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF011(gnss_synchro); Rtcm::set_DF012(gnss_synchro); Rtcm::set_DF013(eph, obs_time, gnss_synchro); std::string content = DF009.to_string() + DF010.to_string() + DF011.to_string() + DF012.to_string() + DF013.to_string(); std::bitset<58> content_msg(content); return content_msg; } std::string Rtcm::print_MT1001(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get a map with GPS L1 only observations std::map observablesL1; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "G") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::bitset<64> header = Rtcm::get_MT1001_4_header(1001, obs_time, observablesL1, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (observables_iter = observablesL1.cbegin(); observables_iter != observablesL1.cend(); observables_iter++) { std::bitset<58> content = Rtcm::get_MT1001_sat_content(gps_eph, obs_time, observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } // ******************************************************** // // MESSAGE TYPE 1002 (EXTENDED GPS L1 OBSERVATIONS) // // ******************************************************** std::string Rtcm::print_MT1002(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get a map with GPS L1 only observations std::map observablesL1; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "G") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::bitset<64> header = Rtcm::get_MT1001_4_header(1002, obs_time, observablesL1, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (observables_iter = observablesL1.cbegin(); observables_iter != observablesL1.cend(); observables_iter++) { std::bitset<74> content = Rtcm::get_MT1002_sat_content(gps_eph, obs_time, observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<74> Rtcm::get_MT1002_sat_content(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF009(gnss_synchro); Rtcm::set_DF010(code_indicator); // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF011(gnss_synchro); Rtcm::set_DF012(gnss_synchro); Rtcm::set_DF013(eph, obs_time, gnss_synchro); std::string content = DF009.to_string() + DF010.to_string() + DF011.to_string() + DF012.to_string() + DF013.to_string() + DF014.to_string() + DF015.to_string(); std::bitset<74> content_msg(content); return content_msg; } // ******************************************************** // // MESSAGE TYPE 1003 (GPS L1 & L2 OBSERVATIONS) // // ******************************************************** std::string Rtcm::print_MT1003(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get maps with GPS L1 and L2 observations std::map observablesL1; std::map observablesL2; std::map::const_iterator observables_iter; std::map::const_iterator observables_iter2; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "G") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesL2.insert(std::pair(observables_iter->first, observables_iter->second)); } } // Get common observables std::vector > common_observables; std::vector >::const_iterator common_observables_iter; std::map observablesL1_with_L2; for (observables_iter = observablesL1.cbegin(); observables_iter != observablesL1.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; for (observables_iter2 = observablesL2.cbegin(); observables_iter2 != observablesL2.cend(); observables_iter2++) { if (observables_iter2->second.PRN == prn_) { std::pair p; Gnss_Synchro pr1 = observables_iter->second; Gnss_Synchro pr2 = observables_iter2->second; p = std::make_pair(pr1, pr2); common_observables.push_back(p); observablesL1_with_L2.insert(std::pair(observables_iter->first, observables_iter->second)); } } } std::bitset<64> header = Rtcm::get_MT1001_4_header(1003, obs_time, observablesL1_with_L2, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (common_observables_iter = common_observables.cbegin(); common_observables_iter != common_observables.cend(); common_observables_iter++) { std::bitset<101> content = Rtcm::get_MT1003_sat_content(ephL1, ephL2, obs_time, common_observables_iter->first, common_observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<101> Rtcm::get_MT1003_sat_content(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF009(gnss_synchroL1); Rtcm::set_DF010(code_indicator); // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF011(gnss_synchroL1); Rtcm::set_DF012(gnss_synchroL1); Rtcm::set_DF013(ephL1, obs_time, gnss_synchroL1); std::bitset<2> DF016_ = std::bitset<2>(0); // code indicator 0: C/A or L2C code 1: P(Y) code direct 2:P(Y) code cross-correlated 3: Correlated P/Y Rtcm::set_DF017(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF018(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF019(ephL2, obs_time, gnss_synchroL2); std::string content = DF009.to_string() + DF010.to_string() + DF011.to_string() + DF012.to_string() + DF013.to_string() + DF016_.to_string() + DF017.to_string() + DF018.to_string() + DF019.to_string(); std::bitset<101> content_msg(content); return content_msg; } // ****************************************************************** // // MESSAGE TYPE 1004 (EXTENDED GPS L1 & L2 OBSERVATIONS) // // ****************************************************************** std::string Rtcm::print_MT1004(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get maps with GPS L1 and L2 observations std::map observablesL1; std::map observablesL2; std::map::const_iterator observables_iter; std::map::const_iterator observables_iter2; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "G") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "G") && (sig_ == "2S")) { observablesL2.insert(std::pair(observables_iter->first, observables_iter->second)); } } // Get common observables std::vector > common_observables; std::vector >::const_iterator common_observables_iter; std::map observablesL1_with_L2; for (observables_iter = observablesL1.cbegin(); observables_iter != observablesL1.cend(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; for (observables_iter2 = observablesL2.cbegin(); observables_iter2 != observablesL2.cend(); observables_iter2++) { if (observables_iter2->second.PRN == prn_) { std::pair p; Gnss_Synchro pr1 = observables_iter->second; Gnss_Synchro pr2 = observables_iter2->second; p = std::make_pair(pr1, pr2); common_observables.push_back(p); observablesL1_with_L2.insert(std::pair(observables_iter->first, observables_iter->second)); } } } std::bitset<64> header = Rtcm::get_MT1001_4_header(1004, obs_time, observablesL1_with_L2, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (common_observables_iter = common_observables.cbegin(); common_observables_iter != common_observables.cend(); common_observables_iter++) { std::bitset<125> content = Rtcm::get_MT1004_sat_content(ephL1, ephL2, obs_time, common_observables_iter->first, common_observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<125> Rtcm::get_MT1004_sat_content(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF009(gnss_synchroL1); Rtcm::set_DF010(code_indicator); // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF011(gnss_synchroL1); Rtcm::set_DF012(gnss_synchroL1); Rtcm::set_DF013(ephL1, obs_time, gnss_synchroL1); Rtcm::set_DF014(gnss_synchroL1); Rtcm::set_DF015(gnss_synchroL1); std::bitset<2> DF016_ = std::bitset<2>(0); // code indicator 0: C/A or L2C code 1: P(Y) code direct 2:P(Y) code cross-correlated 3: Correlated P/Y Rtcm::set_DF017(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF018(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF019(ephL2, obs_time, gnss_synchroL2); Rtcm::set_DF020(gnss_synchroL2); std::string content = DF009.to_string() + DF010.to_string() + DF011.to_string() + DF012.to_string() + DF013.to_string() + DF014.to_string() + DF015.to_string() + DF016_.to_string() + DF017.to_string() + DF018.to_string() + DF019.to_string() + DF020.to_string(); std::bitset<125> content_msg(content); return content_msg; } // ******************************************************** // // MESSAGE TYPE 1005 (STATION DESCRIPTION) // // ******************************************************** /* Stationary Antenna Reference Point, No Height Information * Reference Station Id = 2003 GPS Service supported, but not GLONASS or Galileo ARP ECEF-X = 1114104.5999 meters ARP ECEF-Y = -4850729.7108 meters ARP ECEF-Z = 3975521.4643 meters Expected output: D3 00 13 3E D7 D3 02 02 98 0E DE EF 34 B4 BD 62 AC 09 41 98 6F 33 36 0B 98 */ std::bitset<152> Rtcm::get_MT1005_test() { uint32_t mt1005 = 1005; uint32_t reference_station_id = 2003; // Max: 4095 double ECEF_X = 1114104.5999; // units: m double ECEF_Y = -4850729.7108; // units: m double ECEF_Z = 3975521.4643; // units: m std::bitset<1> DF001_; Rtcm::set_DF002(mt1005); Rtcm::set_DF003(reference_station_id); Rtcm::set_DF021(); Rtcm::set_DF022(true); // GPS Rtcm::set_DF023(false); // Glonass Rtcm::set_DF024(false); // Galileo DF141 = std::bitset<1>("0"); // 0: Real, physical reference station DF001_ = std::bitset<1>("0"); // Reserved, set to 0 Rtcm::set_DF025(ECEF_X); DF142 = std::bitset<1>("0"); // Single Receiver Oscillator Indicator Rtcm::set_DF026(ECEF_Y); DF364 = std::bitset<2>("00"); // Quarter Cycle Indicator Rtcm::set_DF027(ECEF_Z); std::string message = DF002.to_string() + DF003.to_string() + DF021.to_string() + DF022.to_string() + DF023.to_string() + DF024.to_string() + DF141.to_string() + DF025.to_string() + DF142.to_string() + DF001_.to_string() + DF026.to_string() + DF364.to_string() + DF027.to_string(); std::bitset<152> test_msg(message); return test_msg; } std::string Rtcm::print_MT1005(uint32_t ref_id, double ecef_x, double ecef_y, double ecef_z, bool gps, bool glonass, bool galileo, bool non_physical, bool single_oscillator, uint32_t quarter_cycle_indicator) { uint32_t msg_number = 1005; std::bitset<1> DF001_; Rtcm::set_DF002(msg_number); Rtcm::set_DF003(ref_id); Rtcm::set_DF021(); Rtcm::set_DF022(gps); Rtcm::set_DF023(glonass); Rtcm::set_DF024(galileo); DF141 = std::bitset<1>(non_physical); DF001_ = std::bitset<1>("0"); Rtcm::set_DF025(ecef_x); DF142 = std::bitset<1>(single_oscillator); Rtcm::set_DF026(ecef_y); DF364 = std::bitset<2>(quarter_cycle_indicator); Rtcm::set_DF027(ecef_z); std::string data = DF002.to_string() + DF003.to_string() + DF021.to_string() + DF022.to_string() + DF023.to_string() + DF024.to_string() + DF141.to_string() + DF025.to_string() + DF142.to_string() + DF001_.to_string() + DF026.to_string() + DF364.to_string() + DF027.to_string(); std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } int32_t Rtcm::read_MT1005(const std::string& message, uint32_t& ref_id, double& ecef_x, double& ecef_y, double& ecef_z, bool& gps, bool& glonass, bool& galileo) { // Convert message to binary std::string message_bin = Rtcm::binary_data_to_bin(message); if (!Rtcm::check_CRC(message)) { LOG(WARNING) << " Bad CRC detected in RTCM message MT1005"; return 1; } // Check than the message number is correct uint32_t preamble_length = 8; uint32_t reserved_field_length = 6; uint32_t index = preamble_length + reserved_field_length; uint32_t read_message_length = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; if (read_message_length != 19) { LOG(WARNING) << " Message MT1005 with wrong length (19 bytes expected, " << read_message_length << " received)"; return 1; } uint32_t msg_number = 1005; Rtcm::set_DF002(msg_number); std::bitset<12> read_msg_number(message_bin.substr(index, 12)); index += 12; if (DF002 != read_msg_number) { LOG(WARNING) << " This is not a MT1005 message"; return 1; } ref_id = Rtcm::bin_to_uint(message_bin.substr(index, 12)); index += 12; index += 6; // ITRF year gps = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; galileo = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; index += 1; // ref_station_indicator ecef_x = Rtcm::bin_to_double(message_bin.substr(index, 38)) / 10000.0; index += 38; index += 1; // single rx oscillator index += 1; // reserved ecef_y = Rtcm::bin_to_double(message_bin.substr(index, 38)) / 10000.0; index += 38; index += 2; // quarter cycle indicator ecef_z = Rtcm::bin_to_double(message_bin.substr(index, 38)) / 10000.0; return 0; } std::string Rtcm::print_MT1005_test() { std::bitset<152> mt1005 = get_MT1005_test(); return Rtcm::build_message(mt1005.to_string()); } // ******************************************************** // // MESSAGE TYPE 1006 (STATION DESCRIPTION PLUS HEIGHT INFORMATION) // // ******************************************************** std::string Rtcm::print_MT1006(uint32_t ref_id, double ecef_x, double ecef_y, double ecef_z, bool gps, bool glonass, bool galileo, bool non_physical, bool single_oscillator, uint32_t quarter_cycle_indicator, double height) { uint32_t msg_number = 1006; std::bitset<1> DF001_; Rtcm::set_DF002(msg_number); Rtcm::set_DF003(ref_id); Rtcm::set_DF021(); Rtcm::set_DF022(gps); Rtcm::set_DF023(glonass); Rtcm::set_DF024(galileo); DF141 = std::bitset<1>(non_physical); DF001_ = std::bitset<1>("0"); Rtcm::set_DF025(ecef_x); DF142 = std::bitset<1>(single_oscillator); Rtcm::set_DF026(ecef_y); DF364 = std::bitset<2>(quarter_cycle_indicator); Rtcm::set_DF027(ecef_z); Rtcm::set_DF028(height); std::string data = DF002.to_string() + DF003.to_string() + DF021.to_string() + DF022.to_string() + DF023.to_string() + DF024.to_string() + DF141.to_string() + DF025.to_string() + DF142.to_string() + DF001_.to_string() + DF026.to_string() + DF364.to_string() + DF027.to_string() + DF028.to_string(); std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } // ******************************************************** // // MESSAGE TYPE 1008 (ANTENNA DESCRIPTOR & SERIAL NUMBER) // // ******************************************************** std::string Rtcm::print_MT1008(uint32_t ref_id, const std::string& antenna_descriptor, uint32_t antenna_setup_id, const std::string& antenna_serial_number) { uint32_t msg_number = 1008; std::bitset<12> DF002_ = std::bitset<12>(msg_number); Rtcm::set_DF003(ref_id); std::string ant_descriptor = antenna_descriptor; uint32_t len = ant_descriptor.length(); if (len > 31) { ant_descriptor = ant_descriptor.substr(0, 31); len = 31; } DF029 = std::bitset<8>(len); std::string DF030_str_; for (char c : ant_descriptor) { std::bitset<8> character = std::bitset<8>(c); DF030_str_ += character.to_string(); } Rtcm::set_DF031(antenna_setup_id); std::string ant_sn(antenna_serial_number); uint32_t len2 = ant_sn.length(); if (len2 > 31) { ant_sn = ant_sn.substr(0, 31); len2 = 31; } DF032 = std::bitset<8>(len2); std::string DF033_str_; for (char c : ant_sn) { std::bitset<8> character = std::bitset<8>(c); DF033_str_ += character.to_string(); } std::string data = DF002_.to_string() + DF003.to_string() + DF029.to_string() + DF030_str_ + DF031.to_string() + DF032.to_string() + DF033_str_; std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } // ******************************************************** // // MESSAGE TYPE 1009 (GLONASS L1 Basic RTK Observables) // // ******************************************************** std::bitset<61> Rtcm::get_MT1009_12_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t smooth_int, bool sync_flag, bool divergence_free) { uint32_t reference_station_id = ref_id; // Max: 4095 const std::map& observables_ = observables; bool synchronous_GNSS_flag = sync_flag; bool divergence_free_smoothing_indicator = divergence_free; uint32_t smoothing_interval = smooth_int; Rtcm::set_DF002(msg_number); Rtcm::set_DF003(reference_station_id); Rtcm::set_DF034(obs_time); Rtcm::set_DF005(synchronous_GNSS_flag); Rtcm::set_DF035(observables_); Rtcm::set_DF036(divergence_free_smoothing_indicator); Rtcm::set_DF037(smoothing_interval); std::string header = DF002.to_string() + DF003.to_string() + DF034.to_string() + DF005.to_string() + DF035.to_string() + DF036.to_string() + DF037.to_string(); std::bitset<61> header_msg(header); return header_msg; } std::bitset<64> Rtcm::get_MT1009_sat_content(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF038(gnss_synchro); Rtcm::set_DF039(code_indicator); Rtcm::set_DF040(eph.i_satellite_freq_channel); Rtcm::set_DF041(gnss_synchro); Rtcm::set_DF042(gnss_synchro); Rtcm::set_DF043(eph, obs_time, gnss_synchro); std::string content = DF038.to_string() + DF039.to_string() + DF040.to_string() + DF041.to_string() + DF042.to_string() + DF043.to_string(); std::bitset<64> content_msg(content); return content_msg; } std::string Rtcm::print_MT1009(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get a map with GLONASS L1 only observations std::map observablesL1; std::map::const_iterator observables_iter; for (observables_iter = observables.begin(); observables_iter != observables.end(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::bitset<61> header = Rtcm::get_MT1009_12_header(1009, obs_time, observablesL1, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (observables_iter = observablesL1.begin(); observables_iter != observablesL1.end(); observables_iter++) { std::bitset<64> content = Rtcm::get_MT1009_sat_content(glonass_gnav_eph, obs_time, observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } // ******************************************************** // // MESSAGE TYPE 1010 (EXTENDED GLONASS L1 OBSERVATIONS) // // ******************************************************** std::string Rtcm::print_MT1010(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get a map with GPS L1 only observations std::map observablesL1; std::map::const_iterator observables_iter; for (observables_iter = observables.begin(); observables_iter != observables.end(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } } std::bitset<61> header = Rtcm::get_MT1009_12_header(1010, obs_time, observablesL1, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (observables_iter = observablesL1.begin(); observables_iter != observablesL1.end(); observables_iter++) { std::bitset<79> content = Rtcm::get_MT1010_sat_content(glonass_gnav_eph, obs_time, observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<79> Rtcm::get_MT1010_sat_content(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF038(gnss_synchro); Rtcm::set_DF039(code_indicator); Rtcm::set_DF040(eph.i_satellite_freq_channel); Rtcm::set_DF041(gnss_synchro); Rtcm::set_DF042(gnss_synchro); Rtcm::set_DF043(eph, obs_time, gnss_synchro); Rtcm::set_DF044(gnss_synchro); Rtcm::set_DF045(gnss_synchro); std::string content = DF038.to_string() + DF039.to_string() + DF040.to_string() + DF041.to_string() + DF042.to_string() + DF043.to_string() + DF044.to_string() + DF045.to_string(); std::bitset<79> content_msg(content); return content_msg; } // ******************************************************** // // MESSAGE TYPE 1011 (GLONASS L1 & L2 OBSERVATIONS) // // ******************************************************** std::string Rtcm::print_MT1011(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get maps with GPS L1 and L2 observations std::map observablesL1; std::map observablesL2; std::map::const_iterator observables_iter; std::map::const_iterator observables_iter2; for (observables_iter = observables.begin(); observables_iter != observables.end(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "R") && (sig_ == "2C")) { observablesL2.insert(std::pair(observables_iter->first, observables_iter->second)); } } // Get common observables std::vector > common_observables; std::vector >::const_iterator common_observables_iter; std::map observablesL1_with_L2; for (observables_iter = observablesL1.begin(); observables_iter != observablesL1.end(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; for (observables_iter2 = observablesL2.begin(); observables_iter2 != observablesL2.end(); observables_iter2++) { if (observables_iter2->second.PRN == prn_) { std::pair p; Gnss_Synchro pr1 = observables_iter->second; Gnss_Synchro pr2 = observables_iter2->second; p = std::make_pair(pr1, pr2); common_observables.push_back(p); observablesL1_with_L2.insert(std::pair(observables_iter->first, observables_iter->second)); } } } std::bitset<61> header = Rtcm::get_MT1009_12_header(1011, obs_time, observablesL1_with_L2, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (common_observables_iter = common_observables.begin(); common_observables_iter != common_observables.end(); common_observables_iter++) { std::bitset<107> content = Rtcm::get_MT1011_sat_content(ephL1, ephL2, obs_time, common_observables_iter->first, common_observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<107> Rtcm::get_MT1011_sat_content(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF038(gnss_synchroL1); Rtcm::set_DF039(code_indicator); Rtcm::set_DF040(ephL1.i_satellite_freq_channel); Rtcm::set_DF041(gnss_synchroL1); Rtcm::set_DF042(gnss_synchroL1); Rtcm::set_DF043(ephL1, obs_time, gnss_synchroL1); std::bitset<2> DF046_ = std::bitset<2>(0); // code indicator 0: C/A or L2C code 1: P(Y) code direct 2:P(Y) code cross-correlated 3: Correlated P/Y Rtcm::set_DF047(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF048(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF049(ephL2, obs_time, gnss_synchroL2); std::string content = DF038.to_string() + DF039.to_string() + DF040.to_string() + DF041.to_string() + DF042.to_string() + DF043.to_string() + DF046_.to_string() + DF047.to_string() + DF048.to_string() + DF049.to_string(); std::bitset<107> content_msg(content); return content_msg; } // ****************************************************************** // // MESSAGE TYPE 1004 (EXTENDED GLONASS L1 & L2 OBSERVATIONS) // // ****************************************************************** std::string Rtcm::print_MT1012(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id) { auto ref_id = static_cast(station_id); uint32_t smooth_int = 0; bool sync_flag = false; bool divergence_free = false; // Get maps with GLONASS L1 and L2 observations std::map observablesL1; std::map observablesL2; std::map::const_iterator observables_iter; std::map::const_iterator observables_iter2; for (observables_iter = observables.begin(); observables_iter != observables.end(); observables_iter++) { std::string system_(&observables_iter->second.System, 1); std::string sig_(observables_iter->second.Signal); if ((system_ == "R") && (sig_ == "1C")) { observablesL1.insert(std::pair(observables_iter->first, observables_iter->second)); } if ((system_ == "R") && (sig_ == "2C")) { observablesL2.insert(std::pair(observables_iter->first, observables_iter->second)); } } // Get common observables std::vector > common_observables; std::vector >::const_iterator common_observables_iter; std::map observablesL1_with_L2; for (observables_iter = observablesL1.begin(); observables_iter != observablesL1.end(); observables_iter++) { uint32_t prn_ = observables_iter->second.PRN; for (observables_iter2 = observablesL2.begin(); observables_iter2 != observablesL2.end(); observables_iter2++) { if (observables_iter2->second.PRN == prn_) { std::pair p; Gnss_Synchro pr1 = observables_iter->second; Gnss_Synchro pr2 = observables_iter2->second; p = std::make_pair(pr1, pr2); common_observables.push_back(p); observablesL1_with_L2.insert(std::pair(observables_iter->first, observables_iter->second)); } } } std::bitset<61> header = Rtcm::get_MT1009_12_header(1012, obs_time, observablesL1_with_L2, ref_id, smooth_int, sync_flag, divergence_free); std::string data = header.to_string(); for (common_observables_iter = common_observables.begin(); common_observables_iter != common_observables.end(); common_observables_iter++) { std::bitset<130> content = Rtcm::get_MT1012_sat_content(ephL1, ephL2, obs_time, common_observables_iter->first, common_observables_iter->second); data += content.to_string(); } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } std::bitset<130> Rtcm::get_MT1012_sat_content(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { bool code_indicator = false; // code indicator 0: C/A code 1: P(Y) code direct Rtcm::set_DF038(gnss_synchroL1); Rtcm::set_DF039(code_indicator); Rtcm::set_DF040(ephL1.i_satellite_freq_channel); Rtcm::set_DF041(gnss_synchroL1); Rtcm::set_DF042(gnss_synchroL1); Rtcm::set_DF043(ephL1, obs_time, gnss_synchroL1); Rtcm::set_DF044(gnss_synchroL1); Rtcm::set_DF045(gnss_synchroL1); std::bitset<2> DF046_ = std::bitset<2>(0); // code indicator 0: C/A or L2C code 1: P(Y) code direct 2:P(Y) code cross-correlated 3: Correlated P/Y Rtcm::set_DF047(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF048(gnss_synchroL1, gnss_synchroL2); Rtcm::set_DF049(ephL2, obs_time, gnss_synchroL2); Rtcm::set_DF050(gnss_synchroL2); std::string content = DF038.to_string() + DF039.to_string() + DF040.to_string() + DF041.to_string() + DF042.to_string() + DF043.to_string() + DF044.to_string() + DF045.to_string() + DF046_.to_string() + DF047.to_string() + DF048.to_string() + DF049.to_string() + DF050.to_string(); std::bitset<130> content_msg(content); return content_msg; } // ******************************************************** // // MESSAGE TYPE 1019 (GPS EPHEMERIS) // // ******************************************************** std::string Rtcm::print_MT1019(const Gps_Ephemeris& gps_eph) { uint32_t msg_number = 1019; Rtcm::set_DF002(msg_number); Rtcm::set_DF009(gps_eph); Rtcm::set_DF076(gps_eph); Rtcm::set_DF077(gps_eph); Rtcm::set_DF078(gps_eph); Rtcm::set_DF079(gps_eph); Rtcm::set_DF071(gps_eph); Rtcm::set_DF081(gps_eph); Rtcm::set_DF082(gps_eph); Rtcm::set_DF083(gps_eph); Rtcm::set_DF084(gps_eph); Rtcm::set_DF085(gps_eph); Rtcm::set_DF086(gps_eph); Rtcm::set_DF087(gps_eph); Rtcm::set_DF088(gps_eph); Rtcm::set_DF089(gps_eph); Rtcm::set_DF090(gps_eph); Rtcm::set_DF091(gps_eph); Rtcm::set_DF092(gps_eph); Rtcm::set_DF093(gps_eph); Rtcm::set_DF094(gps_eph); Rtcm::set_DF095(gps_eph); Rtcm::set_DF096(gps_eph); Rtcm::set_DF097(gps_eph); Rtcm::set_DF098(gps_eph); Rtcm::set_DF099(gps_eph); Rtcm::set_DF100(gps_eph); Rtcm::set_DF101(gps_eph); Rtcm::set_DF102(gps_eph); Rtcm::set_DF103(gps_eph); Rtcm::set_DF137(gps_eph); std::string data; data.clear(); data = DF002.to_string() + DF009.to_string() + DF076.to_string() + DF077.to_string() + DF078.to_string() + DF079.to_string() + DF071.to_string() + DF081.to_string() + DF082.to_string() + DF083.to_string() + DF084.to_string() + DF085.to_string() + DF086.to_string() + DF087.to_string() + DF088.to_string() + DF089.to_string() + DF090.to_string() + DF091.to_string() + DF092.to_string() + DF093.to_string() + DF094.to_string() + DF095.to_string() + DF096.to_string() + DF097.to_string() + DF098.to_string() + DF099.to_string() + DF100.to_string() + DF101.to_string() + DF102.to_string() + DF103.to_string() + DF137.to_string(); if (data.length() != 488) { LOG(WARNING) << "Bad-formatted RTCM MT1019 (488 bits expected, found " << data.length() << ")"; } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } int32_t Rtcm::read_MT1019(const std::string& message, Gps_Ephemeris& gps_eph) { // Convert message to binary std::string message_bin = Rtcm::binary_data_to_bin(message); if (!Rtcm::check_CRC(message)) { LOG(WARNING) << " Bad CRC detected in RTCM message MT1019"; return 1; } uint32_t preamble_length = 8; uint32_t reserved_field_length = 6; uint32_t index = preamble_length + reserved_field_length; uint32_t read_message_length = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; if (read_message_length != 61) { LOG(WARNING) << " Message MT1019 seems too long (61 bytes expected, " << read_message_length << " received)"; return 1; } // Check than the message number is correct uint32_t read_msg_number = Rtcm::bin_to_uint(message_bin.substr(index, 12)); index += 12; if (1019 != read_msg_number) { LOG(WARNING) << " This is not a MT1019 message"; return 1; } // Fill Gps Ephemeris with message data content gps_eph.i_satellite_PRN = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 6))); index += 6; gps_eph.i_GPS_week = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; gps_eph.i_SV_accuracy = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 4))); index += 4; gps_eph.i_code_on_L2 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 2))); index += 2; gps_eph.d_IDOT = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 14))) * I_DOT_LSB; index += 14; gps_eph.d_IODE_SF2 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 8))); gps_eph.d_IODE_SF3 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 8))); index += 8; gps_eph.d_Toc = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 16))) * T_OC_LSB; index += 16; gps_eph.d_A_f2 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 8))) * A_F2_LSB; index += 8; gps_eph.d_A_f1 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * A_F1_LSB; index += 16; gps_eph.d_A_f0 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 22))) * A_F0_LSB; index += 22; gps_eph.d_IODC = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; gps_eph.d_Crs = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_RS_LSB; index += 16; gps_eph.d_Delta_n = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * DELTA_N_LSB; index += 16; gps_eph.d_M_0 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * M_0_LSB; index += 32; gps_eph.d_Cuc = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_UC_LSB; index += 16; gps_eph.d_e_eccentricity = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 32))) * E_LSB; index += 32; gps_eph.d_Cus = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_US_LSB; index += 16; gps_eph.d_sqrt_A = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 32))) * SQRT_A_LSB; index += 32; gps_eph.d_Toe = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 16))) * T_OE_LSB; index += 16; gps_eph.d_Cic = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_IC_LSB; index += 16; gps_eph.d_OMEGA0 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * OMEGA_0_LSB; index += 32; gps_eph.d_Cis = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_IS_LSB; index += 16; gps_eph.d_i_0 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * I_0_LSB; index += 32; gps_eph.d_Crc = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_RC_LSB; index += 16; gps_eph.d_OMEGA = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * OMEGA_LSB; index += 32; gps_eph.d_OMEGA_DOT = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 24))) * OMEGA_DOT_LSB; index += 24; gps_eph.d_TGD = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 8))) * T_GD_LSB; index += 8; gps_eph.i_SV_health = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 6))); index += 6; gps_eph.b_L2_P_data_flag = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; gps_eph.b_fit_interval_flag = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); return 0; } // ******************************************************** // // MESSAGE TYPE 1020 (GLONASS EPHEMERIS) // // ******************************************************** std::string Rtcm::print_MT1020(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { uint32_t msg_number = 1020; uint32_t glonass_gnav_alm_health = 0; uint32_t glonass_gnav_alm_health_ind = 0; uint32_t fifth_str_additional_data_ind = 1; Rtcm::set_DF002(msg_number); Rtcm::set_DF038(glonass_gnav_eph); Rtcm::set_DF040(glonass_gnav_eph); Rtcm::set_DF104(glonass_gnav_alm_health); Rtcm::set_DF105(glonass_gnav_alm_health_ind); Rtcm::set_DF106(glonass_gnav_eph); Rtcm::set_DF107(glonass_gnav_eph); Rtcm::set_DF108(glonass_gnav_eph); Rtcm::set_DF109(glonass_gnav_eph); Rtcm::set_DF110(glonass_gnav_eph); Rtcm::set_DF111(glonass_gnav_eph); Rtcm::set_DF112(glonass_gnav_eph); Rtcm::set_DF113(glonass_gnav_eph); Rtcm::set_DF114(glonass_gnav_eph); Rtcm::set_DF115(glonass_gnav_eph); Rtcm::set_DF116(glonass_gnav_eph); Rtcm::set_DF117(glonass_gnav_eph); Rtcm::set_DF118(glonass_gnav_eph); Rtcm::set_DF119(glonass_gnav_eph); Rtcm::set_DF120(glonass_gnav_eph); Rtcm::set_DF121(glonass_gnav_eph); Rtcm::set_DF122(glonass_gnav_eph); Rtcm::set_DF123(glonass_gnav_eph); Rtcm::set_DF124(glonass_gnav_eph); Rtcm::set_DF125(glonass_gnav_eph); Rtcm::set_DF126(glonass_gnav_eph); Rtcm::set_DF127(glonass_gnav_eph); Rtcm::set_DF128(glonass_gnav_eph); Rtcm::set_DF129(glonass_gnav_eph); Rtcm::set_DF130(glonass_gnav_eph); Rtcm::set_DF131(fifth_str_additional_data_ind); Rtcm::set_DF132(glonass_gnav_utc_model); Rtcm::set_DF133(glonass_gnav_utc_model); Rtcm::set_DF134(glonass_gnav_utc_model); Rtcm::set_DF135(glonass_gnav_utc_model); Rtcm::set_DF136(glonass_gnav_eph); std::string data; data.clear(); data = DF002.to_string() + DF038.to_string() + DF040.to_string() + DF104.to_string() + DF105.to_string() + DF106.to_string() + DF107.to_string() + DF108.to_string() + DF109.to_string() + DF110.to_string() + DF111.to_string() + DF112.to_string() + DF113.to_string() + DF114.to_string() + DF115.to_string() + DF116.to_string() + DF117.to_string() + DF118.to_string() + DF119.to_string() + DF120.to_string() + DF121.to_string() + DF122.to_string() + DF123.to_string() + DF124.to_string() + DF125.to_string() + DF126.to_string() + DF127.to_string() + DF128.to_string() + DF129.to_string() + DF130.to_string() + DF131.to_string() + DF132.to_string() + DF133.to_string() + DF134.to_string() + DF135.to_string() + DF136.to_string() + std::bitset<7>().to_string(); // Reserved bits if (data.length() != 360) { LOG(WARNING) << "Bad-formatted RTCM MT1020 (360 bits expected, found " << data.length() << ")"; } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } int32_t Rtcm::read_MT1020(const std::string& message, Glonass_Gnav_Ephemeris& glonass_gnav_eph, Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { // Convert message to binary std::string message_bin = Rtcm::binary_data_to_bin(message); int32_t glonass_gnav_alm_health = 0; int32_t glonass_gnav_alm_health_ind = 0; int32_t fifth_str_additional_data_ind = 0; if (!Rtcm::check_CRC(message)) { LOG(WARNING) << " Bad CRC detected in RTCM message MT1020"; return 1; } uint32_t preamble_length = 8; uint32_t reserved_field_length = 6; uint32_t index = preamble_length + reserved_field_length; uint32_t read_message_length = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; if (read_message_length != 45) // 360 bits = 45 bytes { LOG(WARNING) << " Message MT1020 seems too long (61 bytes expected, " << read_message_length << " received)"; return 1; } // Check than the message number is correct uint32_t read_msg_number = Rtcm::bin_to_uint(message_bin.substr(index, 12)); index += 12; if (1020 != read_msg_number) { LOG(WARNING) << " This is not a MT1020 message"; return 1; } // Fill Gps Ephemeris with message data content glonass_gnav_eph.i_satellite_slot_number = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 6))); index += 6; glonass_gnav_eph.i_satellite_freq_channel = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 5)) - 7.0); index += 5; glonass_gnav_alm_health = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; if (glonass_gnav_alm_health) { } // Avoid compiler warning glonass_gnav_alm_health_ind = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; if (glonass_gnav_alm_health_ind) { } // Avoid compiler warning glonass_gnav_eph.d_P_1 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 2))); glonass_gnav_eph.d_P_1 = (glonass_gnav_eph.d_P_1 + 1) * 15; index += 2; glonass_gnav_eph.d_t_k += static_cast(Rtcm::bin_to_int(message_bin.substr(index, 5))) * 3600; index += 5; glonass_gnav_eph.d_t_k += static_cast(Rtcm::bin_to_int(message_bin.substr(index, 6))) * 60; index += 6; glonass_gnav_eph.d_t_k += static_cast(Rtcm::bin_to_int(message_bin.substr(index, 1))) * 30; index += 1; glonass_gnav_eph.d_B_n = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass_gnav_eph.d_P_2 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass_gnav_eph.d_t_b = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 7))) * 15 * 60.0; index += 7; // TODO Check for type spec for intS24 glonass_gnav_eph.d_VXn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 24))) * TWO_N20; index += 24; glonass_gnav_eph.d_Xn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 27))) * TWO_N11; index += 27; glonass_gnav_eph.d_AXn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 5))) * TWO_N30; index += 5; glonass_gnav_eph.d_VYn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 24))) * TWO_N20; index += 24; glonass_gnav_eph.d_Yn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 27))) * TWO_N11; index += 27; glonass_gnav_eph.d_AYn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 5))) * TWO_N30; index += 5; glonass_gnav_eph.d_VZn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 24))) * TWO_N20; index += 24; glonass_gnav_eph.d_Zn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 27))) * TWO_N11; index += 27; glonass_gnav_eph.d_AZn = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 5))) * TWO_N30; index += 5; glonass_gnav_eph.d_P_3 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass_gnav_eph.d_gamma_n = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 11))) * TWO_N30; index += 11; glonass_gnav_eph.d_P = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 2))); index += 2; glonass_gnav_eph.d_l3rd_n = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass_gnav_eph.d_tau_n = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 22))) * TWO_N30; index += 22; glonass_gnav_eph.d_Delta_tau_n = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 5))) * TWO_N30; index += 5; glonass_gnav_eph.d_E_n = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 5))); index += 5; glonass_gnav_eph.d_P_4 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; glonass_gnav_eph.d_F_T = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 4))); index += 4; glonass_gnav_eph.d_N_T = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 11))); index += 11; glonass_gnav_eph.d_M = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 2))); index += 2; fifth_str_additional_data_ind = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); index += 1; if (fifth_str_additional_data_ind == true) { glonass_gnav_utc_model.d_N_A = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 11))); index += 11; glonass_gnav_utc_model.d_tau_c = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 32))) * TWO_N31; index += 32; glonass_gnav_utc_model.d_N_4 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 5))); index += 5; glonass_gnav_utc_model.d_tau_gps = static_cast(Rtcm::bin_to_sint(message_bin.substr(index, 22))) * TWO_N30; index += 22; glonass_gnav_eph.d_l5th_n = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); } return 0; } // ******************************************************** // // MESSAGE TYPE 1029 (UNICODE TEXT STRING) // // ******************************************************** std::string Rtcm::print_MT1029(uint32_t ref_id, const Gps_Ephemeris& gps_eph, double obs_time, const std::string& message) { uint32_t msg_number = 1029; Rtcm::set_DF002(msg_number); Rtcm::set_DF003(ref_id); Rtcm::set_DF051(gps_eph, obs_time); Rtcm::set_DF052(gps_eph, obs_time); uint32_t i = 0; bool first = true; std::string text_binary; for (char c : message) { if (isgraph(c)) { i++; first = true; } else if (c == ' ') { i++; first = true; } else { if (!first) { i++; first = true; } else { first = false; } } std::bitset<8> character = std::bitset<8>(c); text_binary += character.to_string(); } std::bitset<7> DF138_ = std::bitset<7>(i); std::bitset<8> DF139_ = std::bitset<8>(message.length()); std::string data = DF002.to_string() + DF003.to_string() + DF051.to_string() + DF052.to_string() + DF138_.to_string() + DF139_.to_string() + text_binary; std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } // ******************************************************** // // MESSAGE TYPE 1045 (GALILEO EPHEMERIS) // // ******************************************************** std::string Rtcm::print_MT1045(const Galileo_Ephemeris& gal_eph) { uint32_t msg_number = 1045; Rtcm::set_DF002(msg_number); Rtcm::set_DF252(gal_eph); Rtcm::set_DF289(gal_eph); Rtcm::set_DF290(gal_eph); Rtcm::set_DF291(gal_eph); Rtcm::set_DF293(gal_eph); Rtcm::set_DF294(gal_eph); Rtcm::set_DF295(gal_eph); Rtcm::set_DF296(gal_eph); Rtcm::set_DF297(gal_eph); Rtcm::set_DF298(gal_eph); Rtcm::set_DF299(gal_eph); Rtcm::set_DF300(gal_eph); Rtcm::set_DF301(gal_eph); Rtcm::set_DF302(gal_eph); Rtcm::set_DF303(gal_eph); Rtcm::set_DF304(gal_eph); Rtcm::set_DF305(gal_eph); Rtcm::set_DF306(gal_eph); Rtcm::set_DF307(gal_eph); Rtcm::set_DF308(gal_eph); Rtcm::set_DF309(gal_eph); Rtcm::set_DF310(gal_eph); Rtcm::set_DF311(gal_eph); Rtcm::set_DF312(gal_eph); Rtcm::set_DF314(gal_eph); Rtcm::set_DF315(gal_eph); uint32_t seven_zero = 0; std::bitset<7> DF001_ = std::bitset<7>(seven_zero); std::string data; data.clear(); data = DF002.to_string() + DF252.to_string() + DF289.to_string() + DF290.to_string() + DF291.to_string() + DF292.to_string() + DF293.to_string() + DF294.to_string() + DF295.to_string() + DF296.to_string() + DF297.to_string() + DF298.to_string() + DF299.to_string() + DF300.to_string() + DF301.to_string() + DF302.to_string() + DF303.to_string() + DF304.to_string() + DF305.to_string() + DF306.to_string() + DF307.to_string() + DF308.to_string() + DF309.to_string() + DF310.to_string() + DF311.to_string() + DF312.to_string() + DF314.to_string() + DF315.to_string() + DF001_.to_string(); if (data.length() != 496) { LOG(WARNING) << "Bad-formatted RTCM MT1045 (496 bits expected, found " << data.length() << ")"; } std::string msg = build_message(data); if (server_is_running) { rtcm_message_queue->push(msg); } return msg; } int32_t Rtcm::read_MT1045(const std::string& message, Galileo_Ephemeris& gal_eph) { // Convert message to binary std::string message_bin = Rtcm::binary_data_to_bin(message); if (!Rtcm::check_CRC(message)) { LOG(WARNING) << " Bad CRC detected in RTCM message MT1045"; return 1; } uint32_t preamble_length = 8; uint32_t reserved_field_length = 6; uint32_t index = preamble_length + reserved_field_length; uint32_t read_message_length = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; if (read_message_length != 62) { LOG(WARNING) << " Message MT1045 seems too long (62 bytes expected, " << read_message_length << " received)"; return 1; } // Check than the message number is correct uint32_t read_msg_number = Rtcm::bin_to_uint(message_bin.substr(index, 12)); index += 12; if (1045 != read_msg_number) { LOG(WARNING) << " This is not a MT1045 message"; return 1; } // Fill Galileo Ephemeris with message data content gal_eph.i_satellite_PRN = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 6))); index += 6; gal_eph.WN_5 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 12))); index += 12; gal_eph.IOD_nav_1 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 10))); index += 10; gal_eph.SISA_3 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 8))); index += 8; gal_eph.iDot_2 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 14))) * I_DOT_2_LSB; index += 14; gal_eph.t0c_4 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 14))) * T0C_4_LSB; index += 14; gal_eph.af2_4 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 6))) * AF2_4_LSB; index += 6; gal_eph.af1_4 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 21))) * AF1_4_LSB; index += 21; gal_eph.af0_4 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 31))) * AF0_4_LSB; index += 31; gal_eph.C_rs_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_RS_3_LSB; index += 16; gal_eph.delta_n_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * DELTA_N_3_LSB; index += 16; gal_eph.M0_1 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * M0_1_LSB; index += 32; gal_eph.C_uc_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_UC_3_LSB; index += 16; gal_eph.e_1 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 32))) * E_1_LSB; index += 32; gal_eph.C_us_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_US_3_LSB; index += 16; gal_eph.A_1 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 32))) * A_1_LSB_GAL; index += 32; gal_eph.t0e_1 = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 14))) * T0E_1_LSB; index += 14; gal_eph.C_ic_4 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_IC_4_LSB; index += 16; gal_eph.OMEGA_0_2 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * OMEGA_0_2_LSB; index += 32; gal_eph.C_is_4 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_IS_4_LSB; index += 16; gal_eph.i_0_2 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * I_0_2_LSB; index += 32; gal_eph.C_rc_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 16))) * C_RC_3_LSB; index += 16; gal_eph.omega_2 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 32))) * OMEGA_2_LSB; index += 32; gal_eph.OMEGA_dot_3 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 24))) * OMEGA_DOT_3_LSB; index += 24; gal_eph.BGD_E1E5a_5 = static_cast(Rtcm::bin_to_int(message_bin.substr(index, 10))); index += 10; gal_eph.E5a_HS = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 2))); index += 2; gal_eph.E5a_DVS = static_cast(Rtcm::bin_to_uint(message_bin.substr(index, 1))); return 0; } // ********************************************************************************************** // // MESSAGE TYPE MSM1 (COMPACT observables) // // ********************************************************************************************** std::string Rtcm::print_MSM_1(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1071; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1071; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1081; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1091; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1071; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_1_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_1_content_signal_data(observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { // Find first element in observables block and define type of message auto observables_iter = observables.begin(); std::string sys(observables_iter->second.System, 1); Rtcm::set_DF002(msg_number); Rtcm::set_DF003(ref_id); Rtcm::set_DF393(more_messages); Rtcm::set_DF409(0); // Issue of Data Station. 0: not utilized std::bitset<7> DF001_ = std::bitset<7>("0000000"); Rtcm::set_DF411(clock_steering_indicator); Rtcm::set_DF412(external_clock_indicator); Rtcm::set_DF417(divergence_free); Rtcm::set_DF418(smooth_int); Rtcm::set_DF394(observables); Rtcm::set_DF395(observables); std::string header = DF002.to_string() + DF003.to_string(); // GNSS Epoch Time Specific to each constellation if ((sys == "R")) { // GLONASS Epoch Time Rtcm::set_DF034(obs_time); header += DF034.to_string(); } else { // GPS, Galileo Epoch Time Rtcm::set_DF004(obs_time); header += DF004.to_string(); } header = header + DF393.to_string() + DF409.to_string() + DF001_.to_string() + DF411.to_string() + DF417.to_string() + DF412.to_string() + DF418.to_string() + DF394.to_string() + DF395.to_string() + Rtcm::set_DF396(observables); return header; } std::string Rtcm::get_MSM_1_content_sat_data(const std::map& observables) { std::string sat_data; sat_data.clear(); Rtcm::set_DF394(observables); uint32_t num_satellites = DF394.count(); std::vector > observables_vector; std::map::const_iterator gnss_synchro_iter; std::vector pos; std::vector::iterator it; for (gnss_synchro_iter = observables.cbegin(); gnss_synchro_iter != observables.cend(); gnss_synchro_iter++) { it = std::find(pos.begin(), pos.end(), 65 - gnss_synchro_iter->second.PRN); if (it == pos.end()) { pos.push_back(65 - gnss_synchro_iter->second.PRN); observables_vector.emplace_back(*gnss_synchro_iter); } } std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(observables_vector); for (uint32_t nsat = 0; nsat < num_satellites; nsat++) { Rtcm::set_DF398(ordered_by_PRN_pos.at(nsat).second); sat_data += DF398.to_string(); } return sat_data; } std::string Rtcm::get_MSM_1_content_signal_data(const std::map& observables) { std::string signal_data; signal_data.clear(); uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF400(ordered_by_PRN_pos.at(cell).second); signal_data += DF400.to_string(); } return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM2 (COMPACT PHASERANGES) // // ********************************************************************************************** std::string Rtcm::print_MSM_2(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1072; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1072; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1082; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1092; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1072; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_1_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_2_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_2_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF401(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF402(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); first_data_type += DF401.to_string(); second_data_type += DF402.to_string(); third_data_type += DF420.to_string(); } signal_data = first_data_type + second_data_type + third_data_type; return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM3 (COMPACT PSEUDORANGES AND PHASERANGES) // // ********************************************************************************************** std::string Rtcm::print_MSM_3(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1073; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1073; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1083; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1093; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1073; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_1_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_3_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_3_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF400(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF401(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF402(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); first_data_type += DF400.to_string(); second_data_type += DF401.to_string(); third_data_type += DF402.to_string(); fourth_data_type += DF420.to_string(); } signal_data = first_data_type + second_data_type + third_data_type + fourth_data_type; return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM4 (FULL PSEUDORANGES AND PHASERANGES PLUS CNR) // // ********************************************************************************************** std::string Rtcm::print_MSM_4(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1074; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1074; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1084; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1094; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1074; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_4_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_4_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_4_content_sat_data(const std::map& observables) { std::string sat_data; std::string first_data_type; std::string second_data_type; Rtcm::set_DF394(observables); uint32_t num_satellites = DF394.count(); std::vector > observables_vector; std::map::const_iterator gnss_synchro_iter; std::vector pos; std::vector::iterator it; for (gnss_synchro_iter = observables.cbegin(); gnss_synchro_iter != observables.cend(); gnss_synchro_iter++) { it = std::find(pos.begin(), pos.end(), 65 - gnss_synchro_iter->second.PRN); if (it == pos.end()) { pos.push_back(65 - gnss_synchro_iter->second.PRN); observables_vector.emplace_back(*gnss_synchro_iter); } } std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(observables_vector); for (uint32_t nsat = 0; nsat < num_satellites; nsat++) { Rtcm::set_DF397(ordered_by_PRN_pos.at(nsat).second); Rtcm::set_DF398(ordered_by_PRN_pos.at(nsat).second); first_data_type += DF397.to_string(); second_data_type += DF398.to_string(); } sat_data = first_data_type + second_data_type; return sat_data; } std::string Rtcm::get_MSM_4_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; std::string fifth_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF400(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF401(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF402(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF403(ordered_by_PRN_pos.at(cell).second); first_data_type += DF400.to_string(); second_data_type += DF401.to_string(); third_data_type += DF402.to_string(); fourth_data_type += DF420.to_string(); fifth_data_type += DF403.to_string(); } signal_data = first_data_type + second_data_type + third_data_type + fourth_data_type + fifth_data_type; return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM5 (FULL PSEUDORANGES, PHASERANGES, PHASERANGERATE PLUS CNR) // // ********************************************************************************************** std::string Rtcm::print_MSM_5(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1075; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1075; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1085; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1095; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1075; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_5_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_5_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_5_content_sat_data(const std::map& observables) { std::string sat_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; Rtcm::set_DF394(observables); uint32_t num_satellites = DF394.count(); std::vector > observables_vector; std::map::const_iterator gnss_synchro_iter; std::vector pos; std::vector::iterator it; for (gnss_synchro_iter = observables.cbegin(); gnss_synchro_iter != observables.cend(); gnss_synchro_iter++) { it = std::find(pos.begin(), pos.end(), 65 - gnss_synchro_iter->second.PRN); if (it == pos.end()) { pos.push_back(65 - gnss_synchro_iter->second.PRN); observables_vector.emplace_back(*gnss_synchro_iter); } } std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(observables_vector); for (uint32_t nsat = 0; nsat < num_satellites; nsat++) { Rtcm::set_DF397(ordered_by_PRN_pos.at(nsat).second); Rtcm::set_DF398(ordered_by_PRN_pos.at(nsat).second); Rtcm::set_DF399(ordered_by_PRN_pos.at(nsat).second); std::bitset<4> reserved = std::bitset<4>("0000"); first_data_type += DF397.to_string(); second_data_type += reserved.to_string(); third_data_type += DF398.to_string(); fourth_data_type += DF399.to_string(); } sat_data = first_data_type + second_data_type + third_data_type + fourth_data_type; return sat_data; } std::string Rtcm::get_MSM_5_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; std::string fifth_data_type; std::string sixth_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF400(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF401(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF402(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF403(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF404(ordered_by_PRN_pos.at(cell).second); first_data_type += DF400.to_string(); second_data_type += DF401.to_string(); third_data_type += DF402.to_string(); fourth_data_type += DF420.to_string(); fifth_data_type += DF403.to_string(); sixth_data_type += DF404.to_string(); } signal_data = first_data_type + second_data_type + third_data_type + fourth_data_type + fifth_data_type + sixth_data_type; return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM6 (FULL PSEUDORANGES AND PHASERANGES PLUS CNR, HIGH RESOLUTION) // // ********************************************************************************************** std::string Rtcm::print_MSM_6(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1076; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1076; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1086; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1096; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (gal_eph.i_satellite_PRN != 0) && (glo_gnav_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1076; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_4_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_6_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_6_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; std::string fifth_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF405(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF406(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF407(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF408(ordered_by_PRN_pos.at(cell).second); first_data_type += DF405.to_string(); second_data_type += DF406.to_string(); third_data_type += DF407.to_string(); fourth_data_type += DF420.to_string(); fifth_data_type += DF408.to_string(); } signal_data = first_data_type + second_data_type + third_data_type + fourth_data_type + fifth_data_type; return signal_data; } // ********************************************************************************************** // // MESSAGE TYPE MSM7 (FULL PSEUDORANGES, PHASERANGES, PHASERANGERATE AND CNR, HIGH RESOLUTION) // // ********************************************************************************************** std::string Rtcm::print_MSM_7(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { uint32_t msg_number = 0; if (gps_eph.i_satellite_PRN != 0) { msg_number = 1077; } if (gps_cnav_eph.i_satellite_PRN != 0) { msg_number = 1077; } if (glo_gnav_eph.i_satellite_PRN != 0) { msg_number = 1087; } if (gal_eph.i_satellite_PRN != 0) { msg_number = 1097; } if (((gps_eph.i_satellite_PRN != 0) || (gps_cnav_eph.i_satellite_PRN != 0)) && (glo_gnav_eph.i_satellite_PRN != 0) && (gal_eph.i_satellite_PRN != 0)) { LOG(WARNING) << "MSM messages for observables from different systems are not defined"; // print two messages? } if (msg_number == 0) { LOG(WARNING) << "Invalid ephemeris provided"; msg_number = 1076; } std::string header = Rtcm::get_MSM_header(msg_number, obs_time, observables, ref_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); std::string sat_data = Rtcm::get_MSM_5_content_sat_data(observables); std::string signal_data = Rtcm::get_MSM_7_content_signal_data(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables); std::string message = build_message(header + sat_data + signal_data); if (server_is_running) { rtcm_message_queue->push(message); } return message; } std::string Rtcm::get_MSM_7_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables) { std::string signal_data; std::string first_data_type; std::string second_data_type; std::string third_data_type; std::string fourth_data_type; std::string fifth_data_type; std::string sixth_data_type; uint32_t Ncells = observables.size(); std::vector > observables_vector; std::map::const_iterator map_iter; for (map_iter = observables.cbegin(); map_iter != observables.cend(); map_iter++) { observables_vector.emplace_back(*map_iter); } std::vector > ordered_by_signal = Rtcm::sort_by_signal(observables_vector); std::reverse(ordered_by_signal.begin(), ordered_by_signal.end()); std::vector > ordered_by_PRN_pos = Rtcm::sort_by_PRN_mask(ordered_by_signal); for (uint32_t cell = 0; cell < Ncells; cell++) { Rtcm::set_DF405(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF406(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF407(ephNAV, ephCNAV, ephFNAV, ephGNAV, obs_time, ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF420(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF408(ordered_by_PRN_pos.at(cell).second); Rtcm::set_DF404(ordered_by_PRN_pos.at(cell).second); first_data_type += DF405.to_string(); second_data_type += DF406.to_string(); third_data_type += DF407.to_string(); fourth_data_type += DF420.to_string(); fifth_data_type += DF408.to_string(); sixth_data_type += DF404.to_string(); } signal_data = first_data_type + second_data_type + third_data_type + fourth_data_type + fifth_data_type + sixth_data_type; return signal_data; } // ***************************************************************************************************** // Some utilities // ***************************************************************************************************** std::vector > Rtcm::sort_by_PRN_mask(const std::vector >& synchro_map) const { std::vector >::const_iterator synchro_map_iter; std::vector > my_vec; struct { bool operator()(const std::pair& a, const std::pair& b) { uint32_t value_a = 64 - a.second.PRN; uint32_t value_b = 64 - b.second.PRN; return value_a < value_b; } } has_lower_pos; for (synchro_map_iter = synchro_map.cbegin(); synchro_map_iter != synchro_map.cend(); synchro_map_iter++) { std::pair p(synchro_map_iter->first, synchro_map_iter->second); my_vec.push_back(p); } std::sort(my_vec.begin(), my_vec.end(), has_lower_pos); std::reverse(my_vec.begin(), my_vec.end()); return my_vec; } std::vector > Rtcm::sort_by_signal(const std::vector >& synchro_map) const { std::vector >::const_iterator synchro_map_iter; std::vector > my_vec; struct { bool operator()(const std::pair& a, const std::pair& b) { uint32_t value_a = 0; uint32_t value_b = 0; std::string system_a(&a.second.System, 1); std::string system_b(&b.second.System, 1); std::string sig_a_(a.second.Signal); std::string sig_a = sig_a_.substr(0, 2); std::string sig_b_(b.second.Signal); std::string sig_b = sig_b_.substr(0, 2); if (system_a == "G") { value_a = gps_signal_map.at(sig_a); } if (system_a == "E") { value_a = galileo_signal_map.at(sig_a); } if (system_b == "G") { value_b = gps_signal_map.at(sig_b); } if (system_b == "E") { value_b = galileo_signal_map.at(sig_b); } return value_a < value_b; } } has_lower_signalID; for (synchro_map_iter = synchro_map.cbegin(); synchro_map_iter != synchro_map.cend(); synchro_map_iter++) { std::pair p(synchro_map_iter->first, synchro_map_iter->second); my_vec.push_back(p); } std::sort(my_vec.begin(), my_vec.end(), has_lower_signalID); return my_vec; } std::map Rtcm::gps_signal_map = [] { std::map gps_signal_map_; // Table 3.5-91 gps_signal_map_["1C"] = 2; gps_signal_map_["1P"] = 3; gps_signal_map_["1W"] = 4; gps_signal_map_["2C"] = 8; gps_signal_map_["2P"] = 9; gps_signal_map_["2W"] = 10; gps_signal_map_["2S"] = 15; gps_signal_map_["2L"] = 16; gps_signal_map_["2X"] = 17; gps_signal_map_["5I"] = 22; gps_signal_map_["5Q"] = 23; gps_signal_map_["5X"] = 24; gps_signal_map_["L5"] = 24; // Workaround. TODO: check if it was I or Q return gps_signal_map_; }(); std::map Rtcm::galileo_signal_map = [] { std::map galileo_signal_map_; // Table 3.5-100 galileo_signal_map_["1C"] = 2; galileo_signal_map_["1A"] = 3; galileo_signal_map_["1B"] = 4; galileo_signal_map_["1X"] = 5; galileo_signal_map_["1Z"] = 6; galileo_signal_map_["6C"] = 8; galileo_signal_map_["6A"] = 9; galileo_signal_map_["6B"] = 10; galileo_signal_map_["6X"] = 11; galileo_signal_map_["6Z"] = 12; galileo_signal_map_["7I"] = 14; galileo_signal_map_["7Q"] = 15; galileo_signal_map_["7X"] = 16; galileo_signal_map_["8I"] = 18; galileo_signal_map_["8Q"] = 19; galileo_signal_map_["8X"] = 20; galileo_signal_map_["5I"] = 22; galileo_signal_map_["5Q"] = 23; galileo_signal_map_["5X"] = 24; return galileo_signal_map_; }(); boost::posix_time::ptime Rtcm::compute_GPS_time(const Gps_Ephemeris& eph, double obs_time) const { const double gps_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((gps_t + 604800 * static_cast(eph.i_GPS_week)) * 1000)); // NOLINT(google-runtime-int) if (eph.i_GPS_week < 512) { boost::posix_time::ptime p_time(boost::gregorian::date(2019, 4, 7), t); return p_time; } boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rtcm::compute_GPS_time(const Gps_CNAV_Ephemeris& eph, double obs_time) const { const double gps_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((gps_t + 604800 * static_cast(eph.i_GPS_week)) * 1000)); // NOLINT(google-runtime-int) boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rtcm::compute_Galileo_time(const Galileo_Ephemeris& eph, double obs_time) const { double galileo_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((galileo_t + 604800 * static_cast(eph.WN_5)) * 1000)); // NOLINT(google-runtime-int) boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); return p_time; } boost::posix_time::ptime Rtcm::compute_GLONASS_time(const Glonass_Gnav_Ephemeris& eph, double obs_time) const { boost::posix_time::ptime p_time = eph.compute_GLONASS_time(obs_time); return p_time; } uint32_t Rtcm::lock_time(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_in_seconds; boost::posix_time::ptime current_time = Rtcm::compute_GPS_time(eph, obs_time); boost::posix_time::ptime last_lock_time = Rtcm::gps_L1_last_lock_time[65 - gnss_synchro.PRN]; if (last_lock_time.is_not_a_date_time()) // || CHECK LLI!!......) { Rtcm::gps_L1_last_lock_time[65 - gnss_synchro.PRN] = current_time; } boost::posix_time::time_duration lock_duration = current_time - Rtcm::gps_L1_last_lock_time[65 - gnss_synchro.PRN]; lock_time_in_seconds = static_cast(lock_duration.total_seconds()); // Debug: // std::cout << "lock time PRN " << gnss_synchro.PRN << ": " << lock_time_in_seconds << " current time: " << current_time << std::endl; return lock_time_in_seconds; } uint32_t Rtcm::lock_time(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_in_seconds; boost::posix_time::ptime current_time = Rtcm::compute_GPS_time(eph, obs_time); boost::posix_time::ptime last_lock_time = Rtcm::gps_L2_last_lock_time[65 - gnss_synchro.PRN]; if (last_lock_time.is_not_a_date_time()) // || CHECK LLI!!......) { Rtcm::gps_L2_last_lock_time[65 - gnss_synchro.PRN] = current_time; } boost::posix_time::time_duration lock_duration = current_time - Rtcm::gps_L2_last_lock_time[65 - gnss_synchro.PRN]; lock_time_in_seconds = static_cast(lock_duration.total_seconds()); return lock_time_in_seconds; } uint32_t Rtcm::lock_time(const Galileo_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_in_seconds; boost::posix_time::ptime current_time = Rtcm::compute_Galileo_time(eph, obs_time); boost::posix_time::ptime last_lock_time; std::string sig_(gnss_synchro.Signal); if (sig_ == "1B") { last_lock_time = Rtcm::gal_E1_last_lock_time[65 - gnss_synchro.PRN]; } if ((sig_ == "5X") || (sig_ == "8X") || (sig_ == "7X")) { last_lock_time = Rtcm::gal_E5_last_lock_time[65 - gnss_synchro.PRN]; } if (last_lock_time.is_not_a_date_time()) // || CHECK LLI!!......) { if (sig_ == "1B") { Rtcm::gal_E1_last_lock_time[65 - gnss_synchro.PRN] = current_time; } if ((sig_ == "5X") || (sig_ == "8X") || (sig_ == "7X")) { Rtcm::gal_E5_last_lock_time[65 - gnss_synchro.PRN] = current_time; } } boost::posix_time::time_duration lock_duration = current_time - current_time; if (sig_ == "1B") { lock_duration = current_time - Rtcm::gal_E1_last_lock_time[65 - gnss_synchro.PRN]; } if ((sig_ == "5X") || (sig_ == "8X") || (sig_ == "7X")) { lock_duration = current_time - Rtcm::gal_E5_last_lock_time[65 - gnss_synchro.PRN]; } lock_time_in_seconds = static_cast(lock_duration.total_seconds()); return lock_time_in_seconds; } uint32_t Rtcm::lock_time(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_in_seconds; boost::posix_time::ptime current_time = Rtcm::compute_GLONASS_time(eph, obs_time); boost::posix_time::ptime last_lock_time; std::string sig_(gnss_synchro.Signal); if (sig_ == "1C") { last_lock_time = Rtcm::glo_L1_last_lock_time[65 - gnss_synchro.PRN]; } if (sig_ == "2C") { last_lock_time = Rtcm::glo_L2_last_lock_time[65 - gnss_synchro.PRN]; } if (last_lock_time.is_not_a_date_time()) // || CHECK LLI!!......) { if (sig_ == "1C") { Rtcm::glo_L1_last_lock_time[65 - gnss_synchro.PRN] = current_time; } if (sig_ == "2C") { Rtcm::glo_L2_last_lock_time[65 - gnss_synchro.PRN] = current_time; } } boost::posix_time::time_duration lock_duration = current_time - current_time; if (sig_ == "1C") { lock_duration = current_time - Rtcm::glo_L1_last_lock_time[65 - gnss_synchro.PRN]; } if (sig_ == "2C") { lock_duration = current_time - Rtcm::glo_L2_last_lock_time[65 - gnss_synchro.PRN]; } lock_time_in_seconds = static_cast(lock_duration.total_seconds()); return lock_time_in_seconds; } uint32_t Rtcm::lock_time_indicator(uint32_t lock_time_period_s) { // Table 3.4-2 if (lock_time_period_s <= 0) { return 0; } if (lock_time_period_s < 24) { return lock_time_period_s; } if (lock_time_period_s < 72) { return (lock_time_period_s + 24) / 2; } if (lock_time_period_s < 168) { return (lock_time_period_s + 120) / 4; } if (lock_time_period_s < 360) { return (lock_time_period_s + 408) / 8; } if (lock_time_period_s < 744) { return (lock_time_period_s + 1176) / 16; } if (lock_time_period_s < 937) { return (lock_time_period_s + 3096) / 32; } return 127; } uint32_t Rtcm::msm_lock_time_indicator(uint32_t lock_time_period_s) { // Table 3.5-74 if (lock_time_period_s < 32) { return 0; } if (lock_time_period_s < 64) { return 1; } if (lock_time_period_s < 128) { return 2; } if (lock_time_period_s < 256) { return 3; } if (lock_time_period_s < 512) { return 4; } if (lock_time_period_s < 1024) { return 5; } if (lock_time_period_s < 2048) { return 6; } if (lock_time_period_s < 4096) { return 7; } if (lock_time_period_s < 8192) { return 8; } if (lock_time_period_s < 16384) { return 9; } if (lock_time_period_s < 32768) { return 10; } if (lock_time_period_s < 65536) { return 11; } if (lock_time_period_s < 131072) { return 12; } if (lock_time_period_s < 262144) { return 13; } if (lock_time_period_s < 524288) { return 14; } return 15; } // clang-format off uint32_t Rtcm::msm_extended_lock_time_indicator(uint32_t lock_time_period_s) { // Table 3.5-75 if( lock_time_period_s < 64 ) return ( lock_time_period_s ); if( 64 <= lock_time_period_s && lock_time_period_s < 128 ) return ( 64 + (lock_time_period_s - 64 ) / 2 ); if( 128 <= lock_time_period_s && lock_time_period_s < 256 ) return ( 96 + (lock_time_period_s - 128 ) / 4 ); if( 256 <= lock_time_period_s && lock_time_period_s < 512 ) return (128 + (lock_time_period_s - 256 ) / 8 ); if( 512 <= lock_time_period_s && lock_time_period_s < 1024 ) return (160 + (lock_time_period_s - 512 ) / 16 ); if( 1024 <= lock_time_period_s && lock_time_period_s < 2048 ) return (192 + (lock_time_period_s - 1024 ) / 32 ); if( 2048 <= lock_time_period_s && lock_time_period_s < 4096 ) return (224 + (lock_time_period_s - 2048 ) / 64 ); if( 4096 <= lock_time_period_s && lock_time_period_s < 8192 ) return (256 + (lock_time_period_s - 4096 ) / 128 ); if( 8192 <= lock_time_period_s && lock_time_period_s < 16384 ) return (288 + (lock_time_period_s - 8192 ) / 256 ); if( 16384 <= lock_time_period_s && lock_time_period_s < 32768 ) return (320 + (lock_time_period_s - 16384 ) / 512 ); if( 32768 <= lock_time_period_s && lock_time_period_s < 65536 ) return (352 + (lock_time_period_s - 32768 ) / 1024 ); if( 65536 <= lock_time_period_s && lock_time_period_s < 131072 ) return (384 + (lock_time_period_s - 65536 ) / 2048 ); if( 131072 <= lock_time_period_s && lock_time_period_s < 262144 ) return (416 + (lock_time_period_s - 131072 ) / 4096 ); if( 262144 <= lock_time_period_s && lock_time_period_s < 524288 ) return (448 + (lock_time_period_s - 262144 ) / 8192 ); if( 524288 <= lock_time_period_s && lock_time_period_s < 1048576 ) return (480 + (lock_time_period_s - 524288 ) / 16384 ); if( 1048576 <= lock_time_period_s && lock_time_period_s < 2097152 ) return (512 + (lock_time_period_s - 1048576 ) / 32768 ); if( 2097152 <= lock_time_period_s && lock_time_period_s < 4194304 ) return (544 + (lock_time_period_s - 2097152 ) / 65536 ); if( 4194304 <= lock_time_period_s && lock_time_period_s < 8388608 ) return (576 + (lock_time_period_s - 4194304 ) / 131072 ); if( 8388608 <= lock_time_period_s && lock_time_period_s < 16777216 ) return (608 + (lock_time_period_s - 8388608 ) / 262144 ); if( 16777216 <= lock_time_period_s && lock_time_period_s < 33554432 ) return (640 + (lock_time_period_s - 16777216) / 524288 ); if( 33554432 <= lock_time_period_s && lock_time_period_s < 67108864 ) return (672 + (lock_time_period_s - 33554432) / 1048576); if( 67108864 <= lock_time_period_s ) return (704 ); return 1023; // will never happen } // clang-format on // ***************************************************************************************************** // // DATA FIELDS AS DEFINED AT RTCM STANDARD 10403.2 // // ***************************************************************************************************** int32_t Rtcm::set_DF002(uint32_t message_number) { if (message_number > 4095) { LOG(WARNING) << "RTCM message number must be between 0 and 4095, but it has been set to " << message_number; } DF002 = std::bitset<12>(message_number); return 0; } int32_t Rtcm::set_DF003(uint32_t ref_station_ID) { // uint32_t station_ID = ref_station_ID; if (ref_station_ID > 4095) { LOG(WARNING) << "RTCM reference station ID must be between 0 and 4095, but it has been set to " << ref_station_ID; } DF003 = std::bitset<12>(ref_station_ID); return 0; } int32_t Rtcm::set_DF004(double obs_time) { // TOW in milliseconds from the beginning of the GPS week, measured in GPS time auto tow = static_cast(std::round(obs_time * 1000)); if (tow > 604799999) { LOG(WARNING) << "To large TOW! Set to the last millisecond of the week"; tow = 604799999; } DF004 = std::bitset<30>(tow); return 0; } int32_t Rtcm::set_DF005(bool sync_flag) { // 0 - No further GNSS observables referenced to the same Epoch Time will be transmitted. This enables the receiver to begin processing // the data immediately after decoding the message. // 1 - The next message will contain observables of another GNSS source referenced to the same Epoch Time. DF005 = std::bitset<1>(sync_flag); return 0; } int32_t Rtcm::set_DF006(const std::map& observables) { // Number of satellites observed in current epoch uint16_t nsats = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { nsats++; } if (nsats > 31) { LOG(WARNING) << "The number of processed GPS satellites must be between 0 and 31, but it seems that you are processing " << nsats; nsats = 31; } DF006 = std::bitset<5>(nsats); return 0; } int32_t Rtcm::set_DF007(bool divergence_free_smoothing_indicator) { // 0 - Divergence-free smoothing not used 1 - Divergence-free smoothing used DF007 = std::bitset<1>(divergence_free_smoothing_indicator); return 0; } int32_t Rtcm::set_DF008(int16_t smoothing_interval) { DF008 = std::bitset<3>(smoothing_interval); return 0; } int32_t Rtcm::set_DF009(const Gnss_Synchro& gnss_synchro) { uint32_t prn_ = gnss_synchro.PRN; if (prn_ > 32) { LOG(WARNING) << "GPS satellite ID must be between 1 and 32, but PRN " << prn_ << " was found"; } DF009 = std::bitset<6>(prn_); return 0; } int32_t Rtcm::set_DF009(const Gps_Ephemeris& gps_eph) { uint32_t prn_ = gps_eph.i_satellite_PRN; if (prn_ > 32) { LOG(WARNING) << "GPS satellite ID must be between 1 and 32, but PRN " << prn_ << " was found"; } DF009 = std::bitset<6>(prn_); return 0; } int32_t Rtcm::set_DF010(bool code_indicator) { DF010 = std::bitset<1>(code_indicator); return 0; } int32_t Rtcm::set_DF011(const Gnss_Synchro& gnss_synchro) { double ambiguity = std::floor(gnss_synchro.Pseudorange_m / 299792.458); auto gps_L1_pseudorange = static_cast(std::round((gnss_synchro.Pseudorange_m - ambiguity * 299792.458) / 0.02)); DF011 = std::bitset<24>(gps_L1_pseudorange); return 0; } int32_t Rtcm::set_DF012(const Gnss_Synchro& gnss_synchro) { const double lambda = GPS_C_M_S / GPS_L1_FREQ_HZ; double ambiguity = std::floor(gnss_synchro.Pseudorange_m / 299792.458); double gps_L1_pseudorange = std::round((gnss_synchro.Pseudorange_m - ambiguity * 299792.458) / 0.02); double gps_L1_pseudorange_c = gps_L1_pseudorange * 0.02 + ambiguity * 299792.458; double L1_phaserange_c = gnss_synchro.Carrier_phase_rads / GPS_TWO_PI; double L1_phaserange_c_r = std::fmod(L1_phaserange_c - gps_L1_pseudorange_c / lambda + 1500.0, 3000.0) - 1500.0; auto gps_L1_phaserange_minus_L1_pseudorange = static_cast(std::round(L1_phaserange_c_r * lambda / 0.0005)); DF012 = std::bitset<20>(gps_L1_phaserange_minus_L1_pseudorange); return 0; } int32_t Rtcm::set_DF013(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_indicator; uint32_t lock_time_period_s = Rtcm::lock_time(eph, obs_time, gnss_synchro); lock_time_indicator = Rtcm::lock_time_indicator(lock_time_period_s); DF013 = std::bitset<7>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF014(const Gnss_Synchro& gnss_synchro) { auto gps_L1_pseudorange_ambiguity = static_cast(std::floor(gnss_synchro.Pseudorange_m / 299792.458)); DF014 = std::bitset<8>(gps_L1_pseudorange_ambiguity); return 0; } int32_t Rtcm::set_DF015(const Gnss_Synchro& gnss_synchro) { double CN0_dB_Hz_est = gnss_synchro.CN0_dB_hz; if (CN0_dB_Hz_est > 63.75) { CN0_dB_Hz_est = 63.75; } auto CN0_dB_Hz = static_cast(std::round(CN0_dB_Hz_est / 0.25)); DF015 = std::bitset<8>(CN0_dB_Hz); return 0; } int32_t Rtcm::set_DF017(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { double ambiguity = std::floor(gnss_synchroL1.Pseudorange_m / 299792.458); double gps_L1_pseudorange = std::round((gnss_synchroL1.Pseudorange_m - ambiguity * 299792.458) / 0.02); double gps_L1_pseudorange_c = gps_L1_pseudorange * 0.02 + ambiguity * 299792.458; double l2_l1_pseudorange = gnss_synchroL2.Pseudorange_m - gps_L1_pseudorange_c; int32_t pseudorange_difference = 0xFFFFE000; // invalid value; if (std::fabs(l2_l1_pseudorange) <= 163.82) { pseudorange_difference = static_cast(std::round(l2_l1_pseudorange / 0.02)); } DF017 = std::bitset<14>(pseudorange_difference); return 0; } int32_t Rtcm::set_DF018(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { const double lambda2 = GPS_C_M_S / GPS_L2_FREQ_HZ; int32_t l2_phaserange_minus_l1_pseudorange = 0xFFF80000; double ambiguity = std::floor(gnss_synchroL1.Pseudorange_m / 299792.458); double gps_L1_pseudorange = std::round((gnss_synchroL1.Pseudorange_m - ambiguity * 299792.458) / 0.02); double gps_L1_pseudorange_c = gps_L1_pseudorange * 0.02 + ambiguity * 299792.458; double L2_phaserange_c = gnss_synchroL2.Carrier_phase_rads / GPS_TWO_PI; double L1_phaserange_c_r = std::fmod(L2_phaserange_c - gps_L1_pseudorange_c / lambda2 + 1500.0, 3000.0) - 1500.0; if (std::fabs(L1_phaserange_c_r * lambda2) <= 262.1435) { l2_phaserange_minus_l1_pseudorange = static_cast(std::round(L1_phaserange_c_r * lambda2 / 0.0005)); } DF018 = std::bitset<20>(l2_phaserange_minus_l1_pseudorange); return 0; } int32_t Rtcm::set_DF019(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_indicator; uint32_t lock_time_period_s = Rtcm::lock_time(eph, obs_time, gnss_synchro); lock_time_indicator = Rtcm::lock_time_indicator(lock_time_period_s); DF019 = std::bitset<7>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF020(const Gnss_Synchro& gnss_synchro) { double CN0_dB_Hz_est = gnss_synchro.CN0_dB_hz; if (CN0_dB_Hz_est > 63.75) { CN0_dB_Hz_est = 63.75; } auto CN0_dB_Hz = static_cast(std::round(CN0_dB_Hz_est / 0.25)); DF020 = std::bitset<8>(CN0_dB_Hz); return 0; } int32_t Rtcm::set_DF021() { uint16_t itfr_year = 0; DF021 = std::bitset<6>(itfr_year); return 0; } int32_t Rtcm::set_DF022(bool gps_indicator) { DF022 = std::bitset<1>(gps_indicator); return 0; } int32_t Rtcm::set_DF023(bool glonass_indicator) { DF023 = std::bitset<1>(glonass_indicator); return 0; } int32_t Rtcm::set_DF024(bool galileo_indicator) { DF024 = std::bitset<1>(galileo_indicator); return 0; } int32_t Rtcm::set_DF025(double antenna_ECEF_X_m) { auto ant_ref_x = static_cast(std::round(antenna_ECEF_X_m * 10000)); DF025 = std::bitset<38>(ant_ref_x); return 0; } int32_t Rtcm::set_DF026(double antenna_ECEF_Y_m) { auto ant_ref_y = static_cast(std::round(antenna_ECEF_Y_m * 10000)); DF026 = std::bitset<38>(ant_ref_y); return 0; } int32_t Rtcm::set_DF027(double antenna_ECEF_Z_m) { auto ant_ref_z = static_cast(std::round(antenna_ECEF_Z_m * 10000)); DF027 = std::bitset<38>(ant_ref_z); return 0; } int32_t Rtcm::set_DF028(double height) { auto h_ = static_cast(std::round(height * 10000)); DF028 = std::bitset<16>(h_); return 0; } int32_t Rtcm::set_DF031(uint32_t antenna_setup_id) { DF031 = std::bitset<8>(antenna_setup_id); return 0; } int32_t Rtcm::set_DF034(double obs_time) { // TOW in milliseconds from the beginning of the GLONASS day, measured in GLONASS time auto tk = static_cast(std::round(obs_time * 1000)); if (tk > 86400999) { LOG(WARNING) << "To large GLONASS Epoch Time (tk)! Set to the last millisecond of the day"; tk = 86400999; } DF034 = std::bitset<27>(tk); return 0; } int32_t Rtcm::set_DF035(const std::map& observables) { // Number of satellites observed in current epoch uint16_t nsats = 0; std::map::const_iterator observables_iter; for (observables_iter = observables.begin(); observables_iter != observables.end(); observables_iter++) { nsats++; } if (nsats > 31) { LOG(WARNING) << "The number of processed GLONASS satellites must be between 0 and 31, but it seems that you are processing " << nsats; nsats = 31; } DF035 = std::bitset<5>(nsats); return 0; } int32_t Rtcm::set_DF036(bool divergence_free_smoothing_indicator) { // 0 - Divergence-free smoothing not used 1 - Divergence-free smoothing used DF036 = std::bitset<1>(divergence_free_smoothing_indicator); return 0; } int32_t Rtcm::set_DF037(int16_t smoothing_interval) { DF037 = std::bitset<3>(smoothing_interval); return 0; } int32_t Rtcm::set_DF038(const Gnss_Synchro& gnss_synchro) { uint32_t prn_ = gnss_synchro.PRN; if (prn_ > 24) { LOG(WARNING) << "GLONASS satellite ID (Slot Number) must be between 1 and 24, but PRN " << prn_ << " was found"; } DF038 = std::bitset<6>(prn_); return 0; } int32_t Rtcm::set_DF038(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { uint32_t prn_ = glonass_gnav_eph.i_satellite_slot_number; if (prn_ > 24) { LOG(WARNING) << "GLONASS satellite ID (Slot Number) must be between 0 and 24, but PRN " << prn_ << " was found"; } DF038 = std::bitset<6>(prn_); return 0; } int32_t Rtcm::set_DF039(bool code_indicator) { DF039 = std::bitset<1>(code_indicator); return 0; } int32_t Rtcm::set_DF040(int32_t frequency_channel_number) { uint32_t freq_ = frequency_channel_number + 7; if (freq_ > 20) { LOG(WARNING) << "GLONASS Satellite Frequency Number Conversion Error." << "Value must be between 0 and 20, but converted channel" << "frequency number " << freq_ << " was found"; } DF040 = std::bitset<5>(freq_); return 0; } int32_t Rtcm::set_DF040(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { uint32_t freq_ = glonass_gnav_eph.i_satellite_freq_channel + 7; if (freq_ > 20) { LOG(WARNING) << "GLONASS Satellite Frequency Number Conversion Error." << "Value must be between 0 and 20, but converted channel" << "frequency number " << freq_ << " was found"; } DF040 = std::bitset<5>(freq_); return 0; } int32_t Rtcm::set_DF041(const Gnss_Synchro& gnss_synchro) { double ambiguity = std::floor(gnss_synchro.Pseudorange_m / 599584.92); auto glonass_L1_pseudorange = static_cast(std::round((gnss_synchro.Pseudorange_m - ambiguity * 599584.92) / 0.02)); DF041 = std::bitset<25>(glonass_L1_pseudorange); return 0; } int32_t Rtcm::set_DF042(const Gnss_Synchro& gnss_synchro) { const double lambda = GLONASS_C_M_S / (GLONASS_L1_CA_FREQ_HZ + (GLONASS_L1_CA_DFREQ_HZ * GLONASS_PRN.at(gnss_synchro.PRN))); double ambiguity = std::floor(gnss_synchro.Pseudorange_m / 599584.92); double glonass_L1_pseudorange = std::round((gnss_synchro.Pseudorange_m - ambiguity * 599584.92) / 0.02); double glonass_L1_pseudorange_c = glonass_L1_pseudorange * 0.02 + ambiguity * 299792.458; double L1_phaserange_c = gnss_synchro.Carrier_phase_rads / GLONASS_TWO_PI; double L1_phaserange_c_r = std::fmod(L1_phaserange_c - glonass_L1_pseudorange_c / lambda + 1500.0, 3000.0) - 1500.0; auto glonass_L1_phaserange_minus_L1_pseudorange = static_cast(std::round(L1_phaserange_c_r * lambda / 0.0005)); DF042 = std::bitset<20>(glonass_L1_phaserange_minus_L1_pseudorange); return 0; } int32_t Rtcm::set_DF043(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_indicator; uint32_t lock_time_period_s = Rtcm::lock_time(eph, obs_time, gnss_synchro); lock_time_indicator = Rtcm::lock_time_indicator(lock_time_period_s); DF043 = std::bitset<7>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF044(const Gnss_Synchro& gnss_synchro) { auto glonass_L1_pseudorange_ambiguity = static_cast(std::floor(gnss_synchro.Pseudorange_m / 599584.916)); DF044 = std::bitset<7>(glonass_L1_pseudorange_ambiguity); return 0; } int32_t Rtcm::set_DF045(const Gnss_Synchro& gnss_synchro) { double CN0_dB_Hz_est = gnss_synchro.CN0_dB_hz; if (CN0_dB_Hz_est > 63.75) { LOG(WARNING) << "GLONASS L1 CNR must be between 0 and 63.75, but CNR " << CN0_dB_Hz_est << " was found. Setting to 63.75 dB-Hz"; CN0_dB_Hz_est = 63.75; } auto CN0_dB_Hz = static_cast(std::round(CN0_dB_Hz_est / 0.25)); DF045 = std::bitset<8>(CN0_dB_Hz); return 0; } int32_t Rtcm::set_DF047(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { double ambiguity = std::floor(gnss_synchroL1.Pseudorange_m / 599584.92); double glonass_L1_pseudorange = std::round((gnss_synchroL1.Pseudorange_m - ambiguity * 599584.92) / 0.02); double glonass_L1_pseudorange_c = glonass_L1_pseudorange * 0.02 + ambiguity * 599584.92; double l2_l1_pseudorange = gnss_synchroL2.Pseudorange_m - glonass_L1_pseudorange_c; int32_t pseudorange_difference = 0xFFFFE000; // invalid value; if (std::fabs(l2_l1_pseudorange) <= 163.82) { pseudorange_difference = static_cast(std::round(l2_l1_pseudorange / 0.02)); } DF047 = std::bitset<14>(pseudorange_difference); return 0; } //TODO Need to consider frequency channel in this fields int32_t Rtcm::set_DF048(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2) { const double lambda2 = GLONASS_C_M_S / GLONASS_L2_CA_FREQ_HZ; int32_t l2_phaserange_minus_l1_pseudorange = 0xFFF80000; double ambiguity = std::floor(gnss_synchroL1.Pseudorange_m / 599584.92); double glonass_L1_pseudorange = std::round((gnss_synchroL1.Pseudorange_m - ambiguity * 599584.92) / 0.02); double glonass_L1_pseudorange_c = glonass_L1_pseudorange * 0.02 + ambiguity * 599584.92; double L2_phaserange_c = gnss_synchroL2.Carrier_phase_rads / GLONASS_TWO_PI; double L1_phaserange_c_r = std::fmod(L2_phaserange_c - glonass_L1_pseudorange_c / lambda2 + 1500.0, 3000.0) - 1500.0; if (std::fabs(L1_phaserange_c_r * lambda2) <= 262.1435) { l2_phaserange_minus_l1_pseudorange = static_cast(std::round(L1_phaserange_c_r * lambda2 / 0.0005)); } DF048 = std::bitset<20>(l2_phaserange_minus_l1_pseudorange); return 0; } int32_t Rtcm::set_DF049(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_indicator; uint32_t lock_time_period_s = Rtcm::lock_time(eph, obs_time, gnss_synchro); lock_time_indicator = Rtcm::lock_time_indicator(lock_time_period_s); DF049 = std::bitset<7>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF050(const Gnss_Synchro& gnss_synchro) { double CN0_dB_Hz_est = gnss_synchro.CN0_dB_hz; if (CN0_dB_Hz_est > 63.75) { CN0_dB_Hz_est = 63.75; } auto CN0_dB_Hz = static_cast(std::round(CN0_dB_Hz_est / 0.25)); DF050 = std::bitset<8>(CN0_dB_Hz); return 0; } int32_t Rtcm::set_DF051(const Gps_Ephemeris& gps_eph, double obs_time) { const double gps_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((gps_t + 604800 * static_cast(gps_eph.i_GPS_week)) * 1000)); std::string now_ptime; if (gps_eph.i_GPS_week < 512) { boost::posix_time::ptime p_time(boost::gregorian::date(2019, 4, 7), t); now_ptime = to_iso_string(p_time); } else { boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); now_ptime = to_iso_string(p_time); } std::string today_ptime = now_ptime.substr(0, 8); boost::gregorian::date d(boost::gregorian::from_undelimited_string(today_ptime)); uint32_t mjd = d.modjulian_day(); DF051 = std::bitset<16>(mjd); return 0; } int32_t Rtcm::set_DF052(const Gps_Ephemeris& gps_eph, double obs_time) { const double gps_t = obs_time; boost::posix_time::time_duration t = boost::posix_time::milliseconds(static_cast((gps_t + 604800 * static_cast(gps_eph.i_GPS_week)) * 1000)); std::string now_ptime; if (gps_eph.i_GPS_week < 512) { boost::posix_time::ptime p_time(boost::gregorian::date(2019, 4, 7), t); now_ptime = to_iso_string(p_time); } else { boost::posix_time::ptime p_time(boost::gregorian::date(1999, 8, 22), t); now_ptime = to_iso_string(p_time); } std::string hours = now_ptime.substr(9, 2); std::string minutes = now_ptime.substr(11, 2); std::string seconds = now_ptime.substr(13, 8); // boost::gregorian::date d(boost::gregorian::from_undelimited_string(today_ptime)); uint32_t seconds_of_day = boost::lexical_cast(hours) * 60 * 60 + boost::lexical_cast(minutes) * 60 + boost::lexical_cast(seconds); DF052 = std::bitset<17>(seconds_of_day); return 0; } int32_t Rtcm::set_DF071(const Gps_Ephemeris& gps_eph) { auto iode = static_cast(gps_eph.d_IODE_SF2); DF071 = std::bitset<8>(iode); return 0; } int32_t Rtcm::set_DF076(const Gps_Ephemeris& gps_eph) { auto week_number = static_cast(gps_eph.i_GPS_week); DF076 = std::bitset<10>(week_number); return 0; } int32_t Rtcm::set_DF077(const Gps_Ephemeris& gps_eph) { auto ura = static_cast(gps_eph.i_SV_accuracy); DF077 = std::bitset<4>(ura); return 0; } int32_t Rtcm::set_DF078(const Gps_Ephemeris& gps_eph) { auto code_on_L2 = static_cast(gps_eph.i_code_on_L2); DF078 = std::bitset<2>(code_on_L2); return 0; } int32_t Rtcm::set_DF079(const Gps_Ephemeris& gps_eph) { auto idot = static_cast(std::round(gps_eph.d_IDOT / I_DOT_LSB)); DF079 = std::bitset<14>(idot); return 0; } int32_t Rtcm::set_DF080(const Gps_Ephemeris& gps_eph) { auto iode = static_cast(gps_eph.d_IODE_SF2); DF080 = std::bitset<8>(iode); return 0; } int32_t Rtcm::set_DF081(const Gps_Ephemeris& gps_eph) { auto toc = static_cast(std::round(gps_eph.d_Toc / T_OC_LSB)); DF081 = std::bitset<16>(toc); return 0; } int32_t Rtcm::set_DF082(const Gps_Ephemeris& gps_eph) { auto af2 = static_cast(std::round(gps_eph.d_A_f2 / A_F2_LSB)); DF082 = std::bitset<8>(af2); return 0; } int32_t Rtcm::set_DF083(const Gps_Ephemeris& gps_eph) { auto af1 = static_cast(std::round(gps_eph.d_A_f1 / A_F1_LSB)); DF083 = std::bitset<16>(af1); return 0; } int32_t Rtcm::set_DF084(const Gps_Ephemeris& gps_eph) { auto af0 = static_cast(std::round(gps_eph.d_A_f0 / A_F0_LSB)); DF084 = std::bitset<22>(af0); return 0; } int32_t Rtcm::set_DF085(const Gps_Ephemeris& gps_eph) { auto iodc = static_cast(gps_eph.d_IODC); DF085 = std::bitset<10>(iodc); return 0; } int32_t Rtcm::set_DF086(const Gps_Ephemeris& gps_eph) { auto crs = static_cast(std::round(gps_eph.d_Crs / C_RS_LSB)); DF086 = std::bitset<16>(crs); return 0; } int32_t Rtcm::set_DF087(const Gps_Ephemeris& gps_eph) { auto delta_n = static_cast(std::round(gps_eph.d_Delta_n / DELTA_N_LSB)); DF087 = std::bitset<16>(delta_n); return 0; } int32_t Rtcm::set_DF088(const Gps_Ephemeris& gps_eph) { auto m0 = static_cast(std::round(gps_eph.d_M_0 / M_0_LSB)); DF088 = std::bitset<32>(m0); return 0; } int32_t Rtcm::set_DF089(const Gps_Ephemeris& gps_eph) { auto cuc = static_cast(std::round(gps_eph.d_Cuc / C_UC_LSB)); DF089 = std::bitset<16>(cuc); return 0; } int32_t Rtcm::set_DF090(const Gps_Ephemeris& gps_eph) { auto ecc = static_cast(std::round(gps_eph.d_e_eccentricity / E_LSB)); DF090 = std::bitset<32>(ecc); return 0; } int32_t Rtcm::set_DF091(const Gps_Ephemeris& gps_eph) { auto cus = static_cast(std::round(gps_eph.d_Cus / C_US_LSB)); DF091 = std::bitset<16>(cus); return 0; } int32_t Rtcm::set_DF092(const Gps_Ephemeris& gps_eph) { auto sqr_a = static_cast(std::round(gps_eph.d_sqrt_A / SQRT_A_LSB)); DF092 = std::bitset<32>(sqr_a); return 0; } int32_t Rtcm::set_DF093(const Gps_Ephemeris& gps_eph) { auto toe = static_cast(std::round(gps_eph.d_Toe / T_OE_LSB)); DF093 = std::bitset<16>(toe); return 0; } int32_t Rtcm::set_DF094(const Gps_Ephemeris& gps_eph) { auto cic = static_cast(std::round(gps_eph.d_Cic / C_IC_LSB)); DF094 = std::bitset<16>(cic); return 0; } int32_t Rtcm::set_DF095(const Gps_Ephemeris& gps_eph) { auto Omega0 = static_cast(std::round(gps_eph.d_OMEGA0 / OMEGA_0_LSB)); DF095 = std::bitset<32>(Omega0); return 0; } int32_t Rtcm::set_DF096(const Gps_Ephemeris& gps_eph) { auto cis = static_cast(std::round(gps_eph.d_Cis / C_IS_LSB)); DF096 = std::bitset<16>(cis); return 0; } int32_t Rtcm::set_DF097(const Gps_Ephemeris& gps_eph) { auto i0 = static_cast(std::round(gps_eph.d_i_0 / I_0_LSB)); DF097 = std::bitset<32>(i0); return 0; } int32_t Rtcm::set_DF098(const Gps_Ephemeris& gps_eph) { auto crc = static_cast(std::round(gps_eph.d_Crc / C_RC_LSB)); DF098 = std::bitset<16>(crc); return 0; } int32_t Rtcm::set_DF099(const Gps_Ephemeris& gps_eph) { auto omega = static_cast(std::round(gps_eph.d_OMEGA / OMEGA_LSB)); DF099 = std::bitset<32>(omega); return 0; } int32_t Rtcm::set_DF100(const Gps_Ephemeris& gps_eph) { auto omegadot = static_cast(std::round(gps_eph.d_OMEGA_DOT / OMEGA_DOT_LSB)); DF100 = std::bitset<24>(omegadot); return 0; } int32_t Rtcm::set_DF101(const Gps_Ephemeris& gps_eph) { auto tgd = static_cast(std::round(gps_eph.d_TGD / T_GD_LSB)); DF101 = std::bitset<8>(tgd); return 0; } int32_t Rtcm::set_DF102(const Gps_Ephemeris& gps_eph) { auto sv_heath = static_cast(gps_eph.i_SV_health); DF102 = std::bitset<6>(sv_heath); return 0; } int32_t Rtcm::set_DF103(const Gps_Ephemeris& gps_eph) { DF103 = std::bitset<1>(gps_eph.b_L2_P_data_flag); return 0; } int32_t Rtcm::set_DF104(uint32_t glonass_gnav_alm_health) { DF104 = std::bitset<1>(glonass_gnav_alm_health); return 0; } int32_t Rtcm::set_DF105(uint32_t glonass_gnav_alm_health_ind) { DF105 = std::bitset<1>(glonass_gnav_alm_health_ind); return 0; } int32_t Rtcm::set_DF106(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { // Convert the value from (15, 30, 45, 60) to (00, 01, 10, 11) auto P_1 = static_cast(std::round(glonass_gnav_eph.d_P_1 / 15.0 - 1.0)); DF106 = std::bitset<2>(P_1); return 0; } int32_t Rtcm::set_DF107(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { uint32_t hrs = 0; uint32_t min = 0; uint32_t sec = 0; uint32_t tk = 0; tk = static_cast(glonass_gnav_eph.d_t_k); hrs = tk / 3600; min = (tk - hrs * 3600) / 60; sec = (tk - hrs * 3600 - min * 60) / 60; std::string _hrs = std::bitset<5>(hrs).to_string(); // string conversion std::string _min = std::bitset<6>(min).to_string(); // string conversion std::string _sec = std::bitset<1>(sec).to_string(); // string conversion // Set hrs, min, sec in designed bit positions DF107 = std::bitset<12>(_hrs + _min + _sec); return 0; } int32_t Rtcm::set_DF108(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { DF108 = std::bitset<1>(glonass_gnav_eph.d_B_n); return 0; } int32_t Rtcm::set_DF109(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { DF109 = std::bitset<1>(glonass_gnav_eph.d_P_2); return 0; } int32_t Rtcm::set_DF110(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto t_b = static_cast(std::round(glonass_gnav_eph.d_t_b / (15 * 60))); DF110 = std::bitset<7>(t_b); return 0; } int32_t Rtcm::set_DF111(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto VXn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_VXn / TWO_N20))); uint32_t VXn_sgn = glo_sgn(glonass_gnav_eph.d_VXn); DF111 = std::bitset<24>(VXn_mag); DF111.set(23, VXn_sgn); return 0; } int32_t Rtcm::set_DF112(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto Xn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_Xn / TWO_N11))); uint32_t Xn_sgn = glo_sgn(glonass_gnav_eph.d_Xn); DF112 = std::bitset<27>(Xn_mag); DF112.set(26, Xn_sgn); return 0; } int32_t Rtcm::set_DF113(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto AXn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_AXn / TWO_N30))); uint32_t AXn_sgn = glo_sgn(glonass_gnav_eph.d_AXn); DF113 = std::bitset<5>(AXn_mag); DF113.set(4, AXn_sgn); return 0; } int32_t Rtcm::set_DF114(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto VYn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_VYn / TWO_N20))); uint32_t VYn_sgn = glo_sgn(glonass_gnav_eph.d_VYn); DF114 = std::bitset<24>(VYn_mag); DF114.set(23, VYn_sgn); return 0; } int32_t Rtcm::set_DF115(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto Yn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_Yn / TWO_N11))); uint32_t Yn_sgn = glo_sgn(glonass_gnav_eph.d_Yn); DF115 = std::bitset<27>(Yn_mag); DF115.set(26, Yn_sgn); return 0; } int32_t Rtcm::set_DF116(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto AYn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_AYn / TWO_N30))); uint32_t AYn_sgn = glo_sgn(glonass_gnav_eph.d_AYn); DF116 = std::bitset<5>(AYn_mag); DF116.set(4, AYn_sgn); return 0; } int32_t Rtcm::set_DF117(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto VZn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_VZn / TWO_N20))); uint32_t VZn_sgn = glo_sgn(glonass_gnav_eph.d_VZn); DF117 = std::bitset<24>(VZn_mag); DF117.set(23, VZn_sgn); return 0; } int32_t Rtcm::set_DF118(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto Zn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_Zn / TWO_N11))); uint32_t Zn_sgn = glo_sgn(glonass_gnav_eph.d_Zn); DF118 = std::bitset<27>(Zn_mag); DF118.set(26, Zn_sgn); return 0; } int32_t Rtcm::set_DF119(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto AZn_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_AZn / TWO_N30))); uint32_t AZn_sgn = glo_sgn(glonass_gnav_eph.d_AZn); DF119 = std::bitset<5>(AZn_mag); DF119.set(4, AZn_sgn); return 0; } int32_t Rtcm::set_DF120(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { uint32_t P3 = static_cast(std::round(glonass_gnav_eph.d_P_3)); DF120 = std::bitset<1>(P3); return 0; } int32_t Rtcm::set_DF121(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto gamma_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_gamma_n / TWO_N40))); uint32_t gamma_sgn = glo_sgn(glonass_gnav_eph.d_gamma_n); DF121 = std::bitset<11>(gamma_mag); DF121.set(10, gamma_sgn); return 0; } int32_t Rtcm::set_DF122(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto P = static_cast(std::round(glonass_gnav_eph.d_P)); DF122 = std::bitset<2>(P); return 0; } int32_t Rtcm::set_DF123(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto ln = static_cast((glonass_gnav_eph.d_l3rd_n)); DF123 = std::bitset<1>(ln); return 0; } int32_t Rtcm::set_DF124(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto tau_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_tau_n / TWO_N30))); uint32_t tau_sgn = glo_sgn(glonass_gnav_eph.d_tau_n); DF124 = std::bitset<22>(tau_mag); DF124.set(21, tau_sgn); return 0; } int32_t Rtcm::set_DF125(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto delta_tau_mag = static_cast(std::round(fabs(glonass_gnav_eph.d_Delta_tau_n / TWO_N30))); uint32_t delta_tau_sgn = glo_sgn(glonass_gnav_eph.d_Delta_tau_n); DF125 = std::bitset<5>(delta_tau_mag); DF125.set(4, delta_tau_sgn); return 0; } int32_t Rtcm::set_DF126(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto ecc = static_cast(std::round(glonass_gnav_eph.d_E_n)); DF126 = std::bitset<5>(ecc); return 0; } int32_t Rtcm::set_DF127(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto P4 = static_cast(std::round(glonass_gnav_eph.d_P_4)); DF127 = std::bitset<1>(P4); return 0; } int32_t Rtcm::set_DF128(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto F_t = static_cast(std::round(glonass_gnav_eph.d_F_T)); DF128 = std::bitset<4>(F_t); return 0; } int32_t Rtcm::set_DF129(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto N_t = static_cast(std::round(glonass_gnav_eph.d_N_T)); DF129 = std::bitset<11>(N_t); return 0; } int32_t Rtcm::set_DF130(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto M = static_cast(std::round(glonass_gnav_eph.d_M)); DF130 = std::bitset<2>(M); return 0; } int32_t Rtcm::set_DF131(uint32_t fifth_str_additional_data_ind) { auto fith_str_data = static_cast(fifth_str_additional_data_ind); DF131 = std::bitset<1>(fith_str_data); return 0; } int32_t Rtcm::set_DF132(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { auto N_A = static_cast(std::round(glonass_gnav_utc_model.d_N_A)); DF132 = std::bitset<11>(N_A); return 0; } int32_t Rtcm::set_DF133(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { auto tau_c = static_cast(std::round(glonass_gnav_utc_model.d_tau_c / TWO_N31)); DF133 = std::bitset<32>(tau_c); return 0; } int32_t Rtcm::set_DF134(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { auto N_4 = static_cast(std::round(glonass_gnav_utc_model.d_N_4)); DF134 = std::bitset<5>(N_4); return 0; } int32_t Rtcm::set_DF135(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { auto tau_gps = static_cast(std::round(glonass_gnav_utc_model.d_tau_gps) / TWO_N30); DF135 = std::bitset<22>(tau_gps); return 0; } int32_t Rtcm::set_DF136(const Glonass_Gnav_Ephemeris& glonass_gnav_eph) { auto l_n = static_cast(std::round(glonass_gnav_eph.d_l5th_n)); DF136 = std::bitset<1>(l_n); return 0; } int32_t Rtcm::set_DF137(const Gps_Ephemeris& gps_eph) { DF137 = std::bitset<1>(gps_eph.b_fit_interval_flag); return 0; } int32_t Rtcm::set_DF248(double obs_time) { // TOW in milliseconds from the beginning of the Galileo week, measured in Galileo time auto tow = static_cast(std::round(obs_time * 1000)); if (tow > 604799999) { LOG(WARNING) << "To large TOW! Set to the last millisecond of the week"; tow = 604799999; } DF248 = std::bitset<30>(tow); return 0; } int32_t Rtcm::set_DF252(const Galileo_Ephemeris& gal_eph) { uint32_t prn_ = gal_eph.i_satellite_PRN; if (prn_ > 63) { LOG(WARNING) << "Galileo satellite ID must be between 0 and 63, but PRN " << prn_ << " was found"; } DF252 = std::bitset<6>(prn_); return 0; } int32_t Rtcm::set_DF289(const Galileo_Ephemeris& gal_eph) { auto galileo_week_number = static_cast(gal_eph.WN_5); if (galileo_week_number > 4095) { LOG(WARNING) << "Error decoding Galileo week number (it has a 4096 roll-off, but " << galileo_week_number << " was detected)"; } DF289 = std::bitset<12>(galileo_week_number); return 0; } int32_t Rtcm::set_DF290(const Galileo_Ephemeris& gal_eph) { auto iod_nav = static_cast(gal_eph.IOD_nav_1); if (iod_nav > 1023) { LOG(WARNING) << "Error decoding Galileo IODnav (it has a max of 1023, but " << iod_nav << " was detected)"; } DF290 = std::bitset<10>(iod_nav); return 0; } int32_t Rtcm::set_DF291(const Galileo_Ephemeris& gal_eph) { auto SISA = static_cast(gal_eph.SISA_3); // SISA = 0; // SIS Accuracy, data content definition not given in Galileo OS SIS ICD, Issue 1.1, Sept 2010 DF291 = std::bitset<8>(SISA); return 0; } int32_t Rtcm::set_DF292(const Galileo_Ephemeris& gal_eph) { auto idot = static_cast(std::round(gal_eph.iDot_2 / FNAV_IDOT_2_LSB)); DF292 = std::bitset<14>(idot); return 0; } int32_t Rtcm::set_DF293(const Galileo_Ephemeris& gal_eph) { auto toc = static_cast(gal_eph.t0c_4); if (toc > 604740) { LOG(WARNING) << "Error decoding Galileo ephemeris time (max of 604740, but " << toc << " was detected)"; } DF293 = std::bitset<14>(toc); return 0; } int32_t Rtcm::set_DF294(const Galileo_Ephemeris& gal_eph) { auto af2 = static_cast(std::round(gal_eph.af2_4 / FNAV_AF2_1_LSB)); DF294 = std::bitset<6>(af2); return 0; } int32_t Rtcm::set_DF295(const Galileo_Ephemeris& gal_eph) { auto af1 = static_cast(std::round(gal_eph.af1_4 / FNAV_AF1_1_LSB)); DF295 = std::bitset<21>(af1); return 0; } int32_t Rtcm::set_DF296(const Galileo_Ephemeris& gal_eph) { int64_t af0 = static_cast(std::round(gal_eph.af0_4 / FNAV_AF0_1_LSB)); DF296 = std::bitset<31>(af0); return 0; } int32_t Rtcm::set_DF297(const Galileo_Ephemeris& gal_eph) { auto crs = static_cast(std::round(gal_eph.C_rs_3 / FNAV_CRS_3_LSB)); DF297 = std::bitset<16>(crs); return 0; } int32_t Rtcm::set_DF298(const Galileo_Ephemeris& gal_eph) { auto delta_n = static_cast(std::round(gal_eph.delta_n_3 / FNAV_DELTAN_3_LSB)); DF298 = std::bitset<16>(delta_n); return 0; } int32_t Rtcm::set_DF299(const Galileo_Ephemeris& gal_eph) { auto m0 = static_cast(std::round(gal_eph.M0_1 / FNAV_M0_2_LSB)); DF299 = std::bitset<32>(m0); return 0; } int32_t Rtcm::set_DF300(const Galileo_Ephemeris& gal_eph) { int32_t cuc = static_cast(std::round(gal_eph.C_uc_3 / FNAV_CUC_3_LSB)); DF300 = std::bitset<16>(cuc); return 0; } int32_t Rtcm::set_DF301(const Galileo_Ephemeris& gal_eph) { auto ecc = static_cast(std::round(gal_eph.e_1 / FNAV_E_2_LSB)); DF301 = std::bitset<32>(ecc); return 0; } int32_t Rtcm::set_DF302(const Galileo_Ephemeris& gal_eph) { auto cus = static_cast(std::round(gal_eph.C_us_3 / FNAV_CUS_3_LSB)); DF302 = std::bitset<16>(cus); return 0; } int32_t Rtcm::set_DF303(const Galileo_Ephemeris& gal_eph) { auto sqr_a = static_cast(std::round(gal_eph.A_1 / FNAV_A12_2_LSB)); DF303 = std::bitset<32>(sqr_a); return 0; } int32_t Rtcm::set_DF304(const Galileo_Ephemeris& gal_eph) { auto toe = static_cast(std::round(gal_eph.t0e_1 / FNAV_T0E_3_LSB)); DF304 = std::bitset<14>(toe); return 0; } int32_t Rtcm::set_DF305(const Galileo_Ephemeris& gal_eph) { auto cic = static_cast(std::round(gal_eph.C_ic_4 / FNAV_CIC_4_LSB)); DF305 = std::bitset<16>(cic); return 0; } int32_t Rtcm::set_DF306(const Galileo_Ephemeris& gal_eph) { auto Omega0 = static_cast(std::round(gal_eph.OMEGA_0_2 / FNAV_OMEGA0_2_LSB)); DF306 = std::bitset<32>(Omega0); return 0; } int32_t Rtcm::set_DF307(const Galileo_Ephemeris& gal_eph) { auto cis = static_cast(std::round(gal_eph.C_is_4 / FNAV_CIS_4_LSB)); DF307 = std::bitset<16>(cis); return 0; } int32_t Rtcm::set_DF308(const Galileo_Ephemeris& gal_eph) { auto i0 = static_cast(std::round(gal_eph.i_0_2 / FNAV_I0_3_LSB)); DF308 = std::bitset<32>(i0); return 0; } int32_t Rtcm::set_DF309(const Galileo_Ephemeris& gal_eph) { int32_t crc = static_cast(std::round(gal_eph.C_rc_3 / FNAV_CRC_3_LSB)); DF309 = std::bitset<16>(crc); return 0; } int32_t Rtcm::set_DF310(const Galileo_Ephemeris& gal_eph) { auto omega = static_cast(std::round(gal_eph.omega_2 / FNAV_OMEGA0_2_LSB)); DF310 = std::bitset<32>(omega); return 0; } int32_t Rtcm::set_DF311(const Galileo_Ephemeris& gal_eph) { auto Omegadot = static_cast(std::round(gal_eph.OMEGA_dot_3 / FNAV_OMEGADOT_2_LSB)); DF311 = std::bitset<24>(Omegadot); return 0; } int32_t Rtcm::set_DF312(const Galileo_Ephemeris& gal_eph) { auto bdg_E1_E5a = static_cast(std::round(gal_eph.BGD_E1E5a_5 / FNAV_BGD_1_LSB)); DF312 = std::bitset<10>(bdg_E1_E5a); return 0; } int32_t Rtcm::set_DF313(const Galileo_Ephemeris& gal_eph) { auto bdg_E5b_E1 = static_cast(std::round(gal_eph.BGD_E1E5b_5)); // bdg_E5b_E1 = 0; // reserved DF313 = std::bitset<10>(bdg_E5b_E1); return 0; } int32_t Rtcm::set_DF314(const Galileo_Ephemeris& gal_eph) { DF314 = std::bitset<2>(gal_eph.E5a_HS); return 0; } int32_t Rtcm::set_DF315(const Galileo_Ephemeris& gal_eph) { DF315 = std::bitset<1>(gal_eph.E5a_DVS); return 0; } int32_t Rtcm::set_DF393(bool more_messages) { DF393 = std::bitset<1>(more_messages); return 0; } int32_t Rtcm::set_DF394(const std::map& gnss_synchro) { DF394.reset(); std::map::const_iterator gnss_synchro_iter; uint32_t mask_position; for (gnss_synchro_iter = gnss_synchro.cbegin(); gnss_synchro_iter != gnss_synchro.cend(); gnss_synchro_iter++) { mask_position = 64 - gnss_synchro_iter->second.PRN; DF394.set(mask_position, true); } return 0; } int32_t Rtcm::set_DF395(const std::map& gnss_synchro) { DF395.reset(); if (gnss_synchro.empty()) { return 1; } std::map::const_iterator gnss_synchro_iter; std::string sig; uint32_t mask_position; for (gnss_synchro_iter = gnss_synchro.cbegin(); gnss_synchro_iter != gnss_synchro.cend(); gnss_synchro_iter++) { std::string sig_(gnss_synchro_iter->second.Signal); sig = sig_.substr(0, 2); std::string sys(&gnss_synchro_iter->second.System, 1); if ((sig == "1C") && (sys == "G")) { mask_position = 32 - 2; DF395.set(mask_position, true); } if ((sig == "2S") && (sys == "G")) { mask_position = 32 - 15; DF395.set(mask_position, true); } if ((sig == "5X") && (sys == "G")) { mask_position = 32 - 24; DF395.set(mask_position, true); } if ((sig == "1B") && (sys == "E")) { mask_position = 32 - 4; DF395.set(mask_position, true); } if ((sig == "5X") && (sys == "E")) { mask_position = 32 - 24; DF395.set(mask_position, true); } if ((sig == "7X") && (sys == "E")) { mask_position = 32 - 16; DF395.set(mask_position, true); } if ((sig == "1C") && (sys == "R")) { mask_position = 32 - 2; DF395.set(mask_position, true); } if ((sig == "2C") && (sys == "R")) { mask_position = 32 - 8; DF395.set(mask_position, true); } } return 0; } std::string Rtcm::set_DF396(const std::map& observables) { std::string DF396; std::map::const_iterator observables_iter; Rtcm::set_DF394(observables); Rtcm::set_DF395(observables); uint32_t num_signals = DF395.count(); uint32_t num_satellites = DF394.count(); if ((num_signals == 0) || (num_satellites == 0)) { std::string s(""); return s; } std::vector > matrix(num_signals, std::vector()); std::string sig; std::vector list_of_sats; std::vector list_of_signals; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { list_of_sats.push_back(observables_iter->second.PRN); std::string sig_(observables_iter->second.Signal); sig = sig_.substr(0, 2); std::string sys(&observables_iter->second.System, 1); if ((sig == "1C") && (sys == "G")) { list_of_signals.push_back(32 - 2); } if ((sig == "2S") && (sys == "G")) { list_of_signals.push_back(32 - 15); } if ((sig == "5X") && (sys == "G")) { list_of_signals.push_back(32 - 24); } if ((sig == "1B") && (sys == "E")) { list_of_signals.push_back(32 - 4); } if ((sig == "5X") && (sys == "E")) { list_of_signals.push_back(32 - 24); } if ((sig == "7X") && (sys == "E")) { list_of_signals.push_back(32 - 16); } } std::sort(list_of_sats.begin(), list_of_sats.end()); list_of_sats.erase(std::unique(list_of_sats.begin(), list_of_sats.end()), list_of_sats.end()); std::sort(list_of_signals.begin(), list_of_signals.end()); std::reverse(list_of_signals.begin(), list_of_signals.end()); list_of_signals.erase(std::unique(list_of_signals.begin(), list_of_signals.end()), list_of_signals.end()); // fill the matrix bool value; for (uint32_t row = 0; row < num_signals; row++) { for (uint32_t sat = 0; sat < num_satellites; sat++) { value = false; for (observables_iter = observables.cbegin(); observables_iter != observables.cend(); observables_iter++) { std::string sig_(observables_iter->second.Signal); sig = sig_.substr(0, 2); std::string sys(&observables_iter->second.System, 1); if ((sig == "1C") && (sys == "G") && (list_of_signals.at(row) == 32 - 2) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } if ((sig == "2S") && (sys == "G") && (list_of_signals.at(row) == 32 - 15) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } if ((sig == "5X") && (sys == "G") && (list_of_signals.at(row) == 32 - 24) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } if ((sig == "1B") && (sys == "E") && (list_of_signals.at(row) == 32 - 4) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } if ((sig == "5X") && (sys == "E") && (list_of_signals.at(row) == 32 - 24) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } if ((sig == "7X") && (sys == "E") && (list_of_signals.at(row) == 32 - 16) && (observables_iter->second.PRN == list_of_sats.at(sat))) { value = true; } } matrix[row].push_back(value); } } // write the matrix column-wise DF396.clear(); for (uint32_t col = 0; col < num_satellites; col++) { for (uint32_t row = 0; row < num_signals; row++) { std::string ss; if (matrix[row].at(col)) { ss = "1"; } else { ss = "0"; } DF396 += ss; } } return DF396; } int32_t Rtcm::set_DF397(const Gnss_Synchro& gnss_synchro) { double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_s = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; uint32_t int_ms = 0; if (rough_range_s == 0.0) { int_ms = 255; } else if ((rough_range_s < 0.0) || (rough_range_s > meters_to_miliseconds * 255.0)) { int_ms = 255; } else { int_ms = static_cast(std::floor(rough_range_s / meters_to_miliseconds / TWO_N10) + 0.5) >> 10; } DF397 = std::bitset<8>(int_ms); return 0; } int32_t Rtcm::set_DF398(const Gnss_Synchro& gnss_synchro) { double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_m = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; uint32_t rr_mod_ms; if ((rough_range_m <= 0.0) || (rough_range_m > meters_to_miliseconds * 255.0)) { rr_mod_ms = 0; } else { rr_mod_ms = static_cast(std::floor(rough_range_m / meters_to_miliseconds / TWO_N10) + 0.5) & 0x3FFU; } DF398 = std::bitset<10>(rr_mod_ms); return 0; } int32_t Rtcm::set_DF399(const Gnss_Synchro& gnss_synchro) { double lambda = 0.0; std::string sig_(gnss_synchro.Signal); std::string sig = sig_.substr(0, 2); if (sig == "1C") { lambda = GPS_C_M_S / GPS_L1_FREQ_HZ; } if (sig == "2S") { lambda = GPS_C_M_S / GPS_L2_FREQ_HZ; } if (sig == "5X") { lambda = GPS_C_M_S / GALILEO_E5A_FREQ_HZ; } if (sig == "1B") { lambda = GPS_C_M_S / GALILEO_E1_FREQ_HZ; } if (sig == "7X") { lambda = GPS_C_M_S / 1.207140e9; // Galileo_E1b_FREQ_HZ; } double rough_phase_range_rate_ms = std::round(-gnss_synchro.Carrier_Doppler_hz * lambda); if (rough_phase_range_rate_ms < -8191) { rough_phase_range_rate_ms = -8192; } if (rough_phase_range_rate_ms > 8191) { rough_phase_range_rate_ms = -8192; } DF399 = std::bitset<14>(static_cast(rough_phase_range_rate_ms)); return 0; } int32_t Rtcm::set_DF400(const Gnss_Synchro& gnss_synchro) { double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_m = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; double psrng_s; int32_t fine_pseudorange; psrng_s = gnss_synchro.Pseudorange_m - rough_range_m; if (psrng_s == 0) { fine_pseudorange = -16384; } else if (std::fabs(psrng_s) > 292.7) { fine_pseudorange = -16384; // 4000h: invalid value } else { fine_pseudorange = static_cast(std::round(psrng_s / meters_to_miliseconds / TWO_N24)); } DF400 = std::bitset<15>(fine_pseudorange); return 0; } int32_t Rtcm::set_DF401(const Gnss_Synchro& gnss_synchro) { double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_m = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; double phrng_m; int64_t fine_phaserange; double lambda = 0.0; std::string sig_(gnss_synchro.Signal); std::string sig = sig_.substr(0, 2); std::string sys(&gnss_synchro.System, 1); if ((sig == "1C") && (sys == "G")) { lambda = GPS_C_M_S / GPS_L1_FREQ_HZ; } if ((sig == "2S") && (sys == "G")) { lambda = GPS_C_M_S / GPS_L2_FREQ_HZ; } if ((sig == "5X") && (sys == "E")) { lambda = GPS_C_M_S / GALILEO_E5A_FREQ_HZ; } if ((sig == "1B") && (sys == "E")) { lambda = GPS_C_M_S / GALILEO_E1_FREQ_HZ; } if ((sig == "7X") && (sys == "E")) { lambda = GPS_C_M_S / 1.207140e9; // Galileo_E1b_FREQ_HZ; } if ((sig == "1C") && (sys == "R")) { lambda = GLONASS_C_M_S / ((GLONASS_L1_CA_FREQ_HZ + (GLONASS_L1_CA_DFREQ_HZ * GLONASS_PRN.at(gnss_synchro.PRN)))); } if ((sig == "2C") && (sys == "R")) { // TODO Need to add slot number and freq number to gnss_syncro lambda = GLONASS_C_M_S / (GLONASS_L2_CA_FREQ_HZ); } phrng_m = (gnss_synchro.Carrier_phase_rads / GPS_TWO_PI) * lambda - rough_range_m; /* Subtract phase - pseudorange integer cycle offset */ /* TODO: check LLI! */ double cp = gnss_synchro.Carrier_phase_rads / GPS_TWO_PI; // ? if (std::fabs(phrng_m - cp) > 1171.0) { cp = std::round(phrng_m / lambda) * lambda; } phrng_m -= cp; if (phrng_m == 0.0) { fine_phaserange = -2097152; } else if (std::fabs(phrng_m) > 1171.0) { fine_phaserange = -2097152; } else { fine_phaserange = static_cast(std::round(phrng_m / meters_to_miliseconds / TWO_N29)); } DF401 = std::bitset<22>(fine_phaserange); return 0; } int32_t Rtcm::set_DF402(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_period_s = 0; uint32_t lock_time_indicator; std::string sig_(gnss_synchro.Signal); std::string sys(&gnss_synchro.System, 1); if ((sig_ == "1C") && (sys == "G")) { lock_time_period_s = Rtcm::lock_time(ephNAV, obs_time, gnss_synchro); } if ((sig_ == "2S") && (sys == "G")) { lock_time_period_s = Rtcm::lock_time(ephCNAV, obs_time, gnss_synchro); } // TODO Should add system for galileo satellites if ((sig_ == "1B") || (sig_ == "5X") || (sig_ == "7X") || (sig_ == "8X")) { lock_time_period_s = Rtcm::lock_time(ephFNAV, obs_time, gnss_synchro); } if ((sig_ == "1C") && (sys == "R")) { lock_time_period_s = Rtcm::lock_time(ephGNAV, obs_time, gnss_synchro); } if ((sig_ == "2C") && (sys == "R")) { lock_time_period_s = Rtcm::lock_time(ephGNAV, obs_time, gnss_synchro); } lock_time_indicator = Rtcm::msm_lock_time_indicator(lock_time_period_s); DF402 = std::bitset<4>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF403(const Gnss_Synchro& gnss_synchro) { uint32_t cnr_dB_Hz; cnr_dB_Hz = static_cast(std::round(gnss_synchro.CN0_dB_hz)); DF403 = std::bitset<6>(cnr_dB_Hz); return 0; } int32_t Rtcm::set_DF404(const Gnss_Synchro& gnss_synchro) { double lambda = 0.0; std::string sig_(gnss_synchro.Signal); std::string sig = sig_.substr(0, 2); int32_t fine_phaserange_rate; std::string sys_(&gnss_synchro.System, 1); if ((sig_ == "1C") && (sys_ == "G")) { lambda = GPS_C_M_S / GPS_L1_FREQ_HZ; } if ((sig_ == "2S") && (sys_ == "G")) { lambda = GPS_C_M_S / GPS_L2_FREQ_HZ; } if ((sig_ == "5X") && (sys_ == "E")) { lambda = GPS_C_M_S / GALILEO_E5A_FREQ_HZ; } if ((sig_ == "1B") && (sys_ == "E")) { lambda = GPS_C_M_S / GALILEO_E1_FREQ_HZ; } if ((sig_ == "7X") && (sys_ == "E")) { lambda = GPS_C_M_S / 1.207140e9; // Galileo_E1b_FREQ_HZ; } if ((sig_ == "1C") && (sys_ == "R")) { lambda = GLONASS_C_M_S / (GLONASS_L1_CA_FREQ_HZ + (GLONASS_L1_CA_DFREQ_HZ * GLONASS_PRN.at(gnss_synchro.PRN))); } if ((sig_ == "2C") && (sys_ == "R")) { //TODO Need to add slot number and freq number to gnss syncro lambda = GLONASS_C_M_S / (GLONASS_L2_CA_FREQ_HZ); } double rough_phase_range_rate = std::round(-gnss_synchro.Carrier_Doppler_hz * lambda); double phrr = (-gnss_synchro.Carrier_Doppler_hz * lambda - rough_phase_range_rate); if (phrr == 0.0) { fine_phaserange_rate = -16384; } else if (std::fabs(phrr) > 1.6384) { fine_phaserange_rate = -16384; } else { fine_phaserange_rate = static_cast(std::round(phrr / 0.0001)); } DF404 = std::bitset<15>(fine_phaserange_rate); return 0; } int32_t Rtcm::set_DF405(const Gnss_Synchro& gnss_synchro) { double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_m = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; double psrng_s; int64_t fine_pseudorange; psrng_s = gnss_synchro.Pseudorange_m - rough_range_m; if (psrng_s == 0.0) { fine_pseudorange = -524288; } else if (std::fabs(psrng_s) > 292.7) { fine_pseudorange = -524288; } else { fine_pseudorange = static_cast(std::round(psrng_s / meters_to_miliseconds / TWO_N29)); } DF405 = std::bitset<20>(fine_pseudorange); return 0; } int32_t Rtcm::set_DF406(const Gnss_Synchro& gnss_synchro) { int64_t fine_phaserange_ex; double meters_to_miliseconds = GPS_C_M_S * 0.001; double rough_range_m = std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10; double phrng_m; double lambda = 0.0; std::string sig_(gnss_synchro.Signal); sig_ = sig_.substr(0, 2); std::string sys_(&gnss_synchro.System, 1); if ((sig_ == "1C") && (sys_ == "G")) { lambda = GPS_C_M_S / GPS_L1_FREQ_HZ; } if ((sig_ == "2S") && (sys_ == "G")) { lambda = GPS_C_M_S / GPS_L2_FREQ_HZ; } if ((sig_ == "5X") && (sys_ == "E")) { lambda = GPS_C_M_S / GALILEO_E5A_FREQ_HZ; } if ((sig_ == "1B") && (sys_ == "E")) { lambda = GPS_C_M_S / GALILEO_E1_FREQ_HZ; } if ((sig_ == "7X") && (sys_ == "E")) { lambda = GPS_C_M_S / 1.207140e9; // Galileo_E1b_FREQ_HZ; } if ((sig_ == "1C") && (sys_ == "R")) { lambda = GLONASS_C_M_S / (GLONASS_L1_CA_FREQ_HZ + (GLONASS_L1_CA_DFREQ_HZ * GLONASS_PRN.at(gnss_synchro.PRN))); } if ((sig_ == "2C") && (sys_ == "R")) { //TODO Need to add slot number and freq number to gnss syncro lambda = GLONASS_C_M_S / (GLONASS_L2_CA_FREQ_HZ); } phrng_m = (gnss_synchro.Carrier_phase_rads / GPS_TWO_PI) * lambda - rough_range_m; /* Subtract phase - pseudorange integer cycle offset */ /* TODO: check LLI! */ double cp = gnss_synchro.Carrier_phase_rads / GPS_TWO_PI; // ? if (std::fabs(phrng_m - cp) > 1171.0) { cp = std::round(phrng_m / lambda) * lambda; } phrng_m -= cp; if (phrng_m == 0.0) { fine_phaserange_ex = -8388608; } else if (std::fabs(phrng_m) > 1171.0) { fine_phaserange_ex = -8388608; } else { fine_phaserange_ex = static_cast(std::round(phrng_m / meters_to_miliseconds / TWO_N31)); } DF406 = std::bitset<24>(fine_phaserange_ex); return 0; } int32_t Rtcm::set_DF407(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro) { uint32_t lock_time_indicator; uint32_t lock_time_period_s = 0; std::string sig_(gnss_synchro.Signal); std::string sys_(&gnss_synchro.System, 1); if ((sig_ == "1C") && (sys_ == "G")) { lock_time_period_s = Rtcm::lock_time(ephNAV, obs_time, gnss_synchro); } if ((sig_ == "2S") && (sys_ == "G")) { lock_time_period_s = Rtcm::lock_time(ephCNAV, obs_time, gnss_synchro); } if (((sig_ == "1B") || (sig_ == "5X") || (sig_ == "7X") || (sig_ == "8X")) && (sys_ == "E")) { lock_time_period_s = Rtcm::lock_time(ephFNAV, obs_time, gnss_synchro); } if ((sig_ == "1C") && (sys_ == "R")) { lock_time_period_s = Rtcm::lock_time(ephGNAV, obs_time, gnss_synchro); } if ((sig_ == "2C") && (sys_ == "R")) { lock_time_period_s = Rtcm::lock_time(ephGNAV, obs_time, gnss_synchro); } lock_time_indicator = Rtcm::msm_extended_lock_time_indicator(lock_time_period_s); DF407 = std::bitset<10>(lock_time_indicator); return 0; } int32_t Rtcm::set_DF408(const Gnss_Synchro& gnss_synchro) { uint32_t cnr_dB_Hz; cnr_dB_Hz = static_cast(std::round(gnss_synchro.CN0_dB_hz / 0.0625)); DF408 = std::bitset<10>(cnr_dB_Hz); return 0; } int32_t Rtcm::set_DF409(uint32_t iods) { DF409 = std::bitset<3>(iods); return 0; } int32_t Rtcm::set_DF411(uint32_t clock_steering_indicator) { DF411 = std::bitset<2>(clock_steering_indicator); return 0; } int32_t Rtcm::set_DF412(uint32_t external_clock_indicator) { DF412 = std::bitset<2>(external_clock_indicator); return 0; } int32_t Rtcm::set_DF417(bool using_divergence_free_smoothing) { DF417 = std::bitset<1>(using_divergence_free_smoothing); return 0; } int32_t Rtcm::set_DF418(int32_t carrier_smoothing_interval_s) { if (carrier_smoothing_interval_s < 0) { DF418 = std::bitset<3>("111"); } else { if (carrier_smoothing_interval_s == 0) { DF418 = std::bitset<3>("000"); } else if (carrier_smoothing_interval_s < 30) { DF418 = std::bitset<3>("001"); } else if (carrier_smoothing_interval_s < 60) { DF418 = std::bitset<3>("010"); } else if (carrier_smoothing_interval_s < 120) { DF418 = std::bitset<3>("011"); } else if (carrier_smoothing_interval_s < 240) { DF418 = std::bitset<3>("100"); } else if (carrier_smoothing_interval_s < 480) { DF418 = std::bitset<3>("101"); } else { DF418 = std::bitset<3>("110"); } } return 0; } int32_t Rtcm::set_DF420(const Gnss_Synchro& gnss_synchro __attribute__((unused))) { // todo: read the value from gnss_synchro bool half_cycle_ambiguity_indicator = true; DF420 = std::bitset<1>(half_cycle_ambiguity_indicator); return 0; } src/algorithms/PVT/libs/rtcm.h000066400000000000000000001621421352176506000165510ustar00rootroot00000000000000/*! * \file rtcm.h * \brief Interface for the RTCM 3.2 Standard * \author Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RTCM_H_ #define GNSS_SDR_RTCM_H_ #include "concurrent_queue.h" #include "galileo_ephemeris.h" #include "glonass_gnav_ephemeris.h" #include "glonass_gnav_utc_model.h" #include "gnss_synchro.h" #include "gps_cnav_ephemeris.h" #include "gps_ephemeris.h" #include #include #include #include #include #include // for size_t #include #include // for memcpy #include #include #include #include #include #include #include #include #include #if BOOST_GREATER_1_65 using b_io_context = boost::asio::io_context; #else using b_io_context = boost::asio::io_service; #endif /*! * \brief This class implements the generation and reading of some Message Types * defined in the RTCM 3.2 Standard, plus some utilities to handle messages. * * Generation of the following Message Types: * 1001, 1002, 1003, 1004, 1005, 1006, 1008, 1019, 1020, 1029, 1045 * * Decoding of the following Message Types: * 1019, 1045 * * Generation of the following Multiple Signal Messages: * MSM1 (message types 1071, 1091) * MSM2 (message types 1072, 1092) * MSM3 (message types 1073, 1093) * MSM4 (message types 1074, 1094) * MSM5 (message types 1075, 1095) * MSM6 (message types 1076, 1096) * MSM7 (message types 1077, 1097) * * RTCM 3 message format (size in bits): * +----------+--------+-----------+--------------------+----------+ * | preamble | 000000 | length | data message | parity | * +----------+--------+-----------+--------------------+----------+ * |<-- 8 --->|<- 6 -->|<-- 10 --->|<--- length x 8 --->|<-- 24 -->| * +----------+--------+-----------+--------------------+----------+ * * * (C) Carles Fernandez-Prades, 2015. cfernandez(at)cttc.es */ class Rtcm { public: Rtcm(uint16_t port = 2101); //!< Default constructor that sets TCP port of the RTCM message server and RTCM Station ID. 2101 is the standard RTCM port according to the Internet Assigned Numbers Authority (IANA). See https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml ~Rtcm(); /*! * \brief Prints message type 1001 (L1-Only GPS RTK Observables) */ std::string print_MT1001(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints message type 1002 (Extended L1-Only GPS RTK Observables) */ std::string print_MT1002(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints message type 1003 (L1 & L2 GPS RTK Observables) */ std::string print_MT1003(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints message type 1004 (Extended L1 & L2 GPS RTK Observables) */ std::string print_MT1004(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints message type 1005 (Stationary Antenna Reference Point) */ std::string print_MT1005(uint32_t ref_id, double ecef_x, double ecef_y, double ecef_z, bool gps, bool glonass, bool galileo, bool non_physical, bool single_oscillator, uint32_t quarter_cycle_indicator); /*! * \brief Verifies and reads messages of type 1005 (Stationary Antenna Reference Point). Returns 1 if anything goes wrong, 0 otherwise. */ int32_t read_MT1005(const std::string& message, uint32_t& ref_id, double& ecef_x, double& ecef_y, double& ecef_z, bool& gps, bool& glonass, bool& galileo); /*! * \brief Prints message type 1006 (Stationary Antenna Reference Point, with Height Information) */ std::string print_MT1006(uint32_t ref_id, double ecef_x, double ecef_y, double ecef_z, bool gps, bool glonass, bool galileo, bool non_physical, bool single_oscillator, uint32_t quarter_cycle_indicator, double height); std::string print_MT1005_test(); //!< For testing purposes /*! * \brief Prints message type 1008 (Antenna Descriptor & Serial Number) */ std::string print_MT1008(uint32_t ref_id, const std::string& antenna_descriptor, uint32_t antenna_setup_id, const std::string& antenna_serial_number); /*! * \brief Prints L1-Only GLONASS RTK Observables * \details This GLONASS message type is not generally used or supported; type 1012 is to be preferred. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return string with message contents */ std::string print_MT1009(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints Extended L1-Only GLONASS RTK Observables * \details This GLONASS message type is used when only L1 data is present and bandwidth is very tight, often 1012 is used in such cases. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return string with message contents */ std::string print_MT1010(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints L1&L2 GLONASS RTK Observables * \details This GLONASS message type is not generally used or supported; type 1012 is to be preferred * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return string with message contents */ std::string print_MT1011(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints Extended L1&L2 GLONASS RTK Observables * \details This GLONASS message type is the most common observational message type, with L1/L2/SNR content. This is one of the most common messages found. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return string with message contents */ std::string print_MT1012(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables, uint16_t station_id); /*! * \brief Prints message type 1019 (GPS Ephemeris), should be broadcast in the event that * the IODC does not match the IODE, and every 2 minutes. */ std::string print_MT1019(const Gps_Ephemeris& gps_eph); /*! * \brief Verifies and reads messages of type 1019 (GPS Ephemeris). Returns 1 if anything goes wrong, 0 otherwise. */ int32_t read_MT1019(const std::string& message, Gps_Ephemeris& gps_eph); /*! * \brief Prints message type 1020 (GLONASS Ephemeris). * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param glonass_gnav_utc_model GLONASS GNAV Clock Information * \return Returns message type as a string type */ std::string print_MT1020(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); /*! * \brief Verifies and reads messages of type 1020 (GLONASS Ephemeris). * \note Code added as part of GSoC 2017 program * \param message Message to read as a string type * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param glonass_gnav_utc_model GLONASS GNAV Clock Information * \return Returns 1 if anything goes wrong, 0 otherwise. */ int32_t read_MT1020(const std::string& message, Glonass_Gnav_Ephemeris& glonass_gnav_eph, Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); /*! * \brief Prints message type 1029 (Unicode Text String) */ std::string print_MT1029(uint32_t ref_id, const Gps_Ephemeris& gps_eph, double obs_time, const std::string& message); /*! * \brief Prints message type 1045 (Galileo Ephemeris), should be broadcast every 2 minutes */ std::string print_MT1045(const Galileo_Ephemeris& gal_eph); /*! * \brief Verifies and reads messages of type 1045 (Galileo Ephemeris). Returns 1 if anything goes wrong, 0 otherwise. */ int32_t read_MT1045(const std::string& message, Galileo_Ephemeris& gal_eph); /*! * \brief Prints messages of type MSM1 (Compact GNSS observables) */ std::string print_MSM_1(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM2 (Compact GNSS phaseranges) */ std::string print_MSM_2(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM3 (Compact GNSS pseudoranges and phaseranges) */ std::string print_MSM_3(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM4 (Full GNSS pseudoranges and phaseranges plus CNR) */ std::string print_MSM_4(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM5 (Full GNSS pseudoranges, phaseranges, phaserange rate and CNR) */ std::string print_MSM_5(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM6 (Full GNSS pseudoranges and phaseranges plus CNR, high resolution) */ std::string print_MSM_6(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); /*! * \brief Prints messages of type MSM7 (Full GNSS pseudoranges, phaseranges, phaserange rate and CNR, high resolution) */ std::string print_MSM_7(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); uint32_t lock_time(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); //!< Returns the time period in which GPS L1 signals have been continually tracked. uint32_t lock_time(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); //!< Returns the time period in which GPS L2 signals have been continually tracked. uint32_t lock_time(const Galileo_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); //!< Returns the time period in which Galileo signals have been continually tracked. /*! * \brief Locks time period in which GLONASS signals have been continually tracked. * \note Code added as part of GSoC 2017 program * \param eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return Returns the time period in which GLONASS signals have been continually tracked. */ uint32_t lock_time(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::string bin_to_hex(const std::string& s) const; //!< Returns a string of hexadecimal symbols from a string of binary symbols std::string hex_to_bin(const std::string& s) const; //!< Returns a string of binary symbols from a string of hexadecimal symbols std::string bin_to_binary_data(const std::string& s) const; //!< Returns a string of binary data from a string of binary symbols std::string binary_data_to_bin(const std::string& s) const; //!< Returns a string of binary symbols from a string of binary data uint32_t bin_to_uint(const std::string& s) const; //!< Returns an uint32_t from a string of binary symbols int32_t bin_to_int(const std::string& s) const; double bin_to_double(const std::string& s) const; //!< Returns double from a string of binary symbols int32_t bin_to_sint(const std::string& s) const; uint64_t hex_to_uint(const std::string& s) const; //!< Returns an uint64_t from a string of hexadecimal symbols int64_t hex_to_int(const std::string& s) const; //!< Returns a int64_t from a string of hexadecimal symbols bool check_CRC(const std::string& message) const; //!< Checks that the CRC of a RTCM package is correct void run_server(); //!< Starts running the server void stop_server(); //!< Stops the server void send_message(const std::string& msg); //!< Sends a message through the server to all connected clients bool is_server_running() const; //!< Returns true if the server is running, false otherwise private: // // Generation of messages content // std::bitset<64> get_MT1001_4_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t smooth_int, bool sync_flag, bool divergence_free); std::bitset<58> get_MT1001_sat_content(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<74> get_MT1002_sat_content(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<101> get_MT1003_sat_content(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<125> get_MT1004_sat_content(const Gps_Ephemeris& ephL1, const Gps_CNAV_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<152> get_MT1005_test(); /*! * \brief Generates contents of message header for types 1009, 1010, 1011 and 1012. GLONASS RTK Message * \note Code added as part of GSoC 2017 program * \param msg_number Message type number, acceptable options include 1009 to 1012 * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \param ref_id * \param smooth_int * \param divergence_free * \return Returns the message header content as set of bits */ std::bitset<61> get_MT1009_12_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t smooth_int, bool sync_flag, bool divergence_free); /*! * \brief Get the contents of the satellite specific portion of a type 1009 Message (GLONASS Basic RTK, L1 Only) * \details Contents generated for each satellite. See table 3.5-11 * \note Code added as part of GSoC 2017 program * \param ephGNAV Ephemeris for GLONASS GNAV in L1 satellites * \param obs_time Time of observation at the moment of printing * \param gnss_synchro Information generated by channels while processing the satellite * \return Returns the message content as set of bits */ std::bitset<64> get_MT1009_sat_content(const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro); /*! * \brief Get the contents of the satellite specific portion of a type 1010 Message (GLONASS Extended RTK, L1 Only) * \details Contents generated for each satellite. See table 3.5-12 * \note Code added as part of GSoC 2017 program * \param ephGNAV Ephemeris for GLONASS GNAV in L1 satellites * \param obs_time Time of observation at the moment of printing * \param gnss_synchro Information generated by channels while processing the satellite * \return Returns the message content as set of bits */ std::bitset<79> get_MT1010_sat_content(const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro); /*! * \brief Get the contents of the satellite specific portion of a type 1011 Message (GLONASS Basic RTK, L1 & L2) * \details Contents generated for each satellite. See table 3.5-13 * \note Code added as part of GSoC 2017 program * \param ephGNAVL1 Ephemeris for GLONASS GNAV in L1 satellites * \param ephGNAVL2 Ephemeris for GLONASS GNAV in L2 satellites * \param obs_time Time of observation at the moment of printing * \param gnss_synchroL1 Information generated by channels while processing the GLONASS GNAV L1 satellite * \param gnss_synchroL2 Information generated by channels while processing the GLONASS GNAV L2 satellite * \return Returns the message content as set of bits */ std::bitset<107> get_MT1011_sat_content(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); /*! * \brief Get the contents of the satellite specific portion of a type 1012 Message (GLONASS Extended RTK, L1 & L2) * \details Contents generated for each satellite. See table 3.5-14 * \note Code added as part of GSoC 2017 program * \param ephGNAVL1 Ephemeris for GLONASS GNAV in L1 satellites * \param ephGNAVL2 Ephemeris for GLONASS GNAV in L2 satellites * \param obs_time Time of observation at the moment of printing * \param gnss_synchroL1 Information generated by channels while processing the GLONASS GNAV L1 satellite * \param gnss_synchroL2 Information generated by channels while processing the GLONASS GNAV L2 satellite * \return Returns the message content as set of bits */ std::bitset<130> get_MT1012_sat_content(const Glonass_Gnav_Ephemeris& ephL1, const Glonass_Gnav_Ephemeris& ephL2, double obs_time, const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::string get_MSM_header(uint32_t msg_number, double obs_time, const std::map& observables, uint32_t ref_id, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); std::string get_MSM_1_content_sat_data(const std::map& observables); std::string get_MSM_4_content_sat_data(const std::map& observables); std::string get_MSM_5_content_sat_data(const std::map& observables); std::string get_MSM_1_content_signal_data(const std::map& observables); std::string get_MSM_2_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); std::string get_MSM_3_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); std::string get_MSM_4_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); std::string get_MSM_5_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); std::string get_MSM_6_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); std::string get_MSM_7_content_signal_data(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const std::map& observables); // // Utilities // static std::map galileo_signal_map; static std::map gps_signal_map; std::vector > sort_by_signal(const std::vector >& synchro_map) const; std::vector > sort_by_PRN_mask(const std::vector >& synchro_map) const; boost::posix_time::ptime compute_GPS_time(const Gps_Ephemeris& eph, double obs_time) const; boost::posix_time::ptime compute_GPS_time(const Gps_CNAV_Ephemeris& eph, double obs_time) const; boost::posix_time::ptime compute_Galileo_time(const Galileo_Ephemeris& eph, double obs_time) const; boost::posix_time::ptime compute_GLONASS_time(const Glonass_Gnav_Ephemeris& eph, double obs_time) const; boost::posix_time::ptime gps_L1_last_lock_time[64]; boost::posix_time::ptime gps_L2_last_lock_time[64]; boost::posix_time::ptime gal_E1_last_lock_time[64]; boost::posix_time::ptime gal_E5_last_lock_time[64]; boost::posix_time::ptime glo_L1_last_lock_time[64]; boost::posix_time::ptime glo_L2_last_lock_time[64]; uint32_t lock_time_indicator(uint32_t lock_time_period_s); uint32_t msm_lock_time_indicator(uint32_t lock_time_period_s); uint32_t msm_extended_lock_time_indicator(uint32_t lock_time_period_s); // // Classes for TCP communication // uint16_t RTCM_port; //uint16_t RTCM_Station_ID; class Rtcm_Message { public: enum { header_length = 6 }; enum { max_body_length = 1029 }; Rtcm_Message() : body_length_(0) { } const char* data() const { return data_; } char* data() { return data_; } inline std::size_t length() const { return header_length + body_length_; } const char* body() const { return data_ + header_length; } char* body() { return data_ + header_length; } std::size_t body_length() const { return body_length_; } void body_length(std::size_t new_length) { body_length_ = new_length; if (body_length_ > max_body_length) { body_length_ = max_body_length; } } inline bool decode_header() { char header[header_length + 1] = ""; std::strncat(header, data_, header_length); if (header[0] != 'G' || header[1] != 'S') { return false; } char header2_[header_length - 1] = ""; std::strncat(header2_, data_ + 2, header_length - 2); body_length_ = std::atoi(header2_); if (body_length_ == 0) { return false; } if (body_length_ > max_body_length) { body_length_ = 0; return false; } return true; } inline void encode_header() { char header[header_length + 1] = ""; std::sprintf(header, "GS%4d", static_cast(body_length_)); std::memcpy(data_, header, header_length); } private: char data_[header_length + max_body_length]; std::size_t body_length_; }; class RtcmListener { public: virtual ~RtcmListener() = default; virtual void deliver(const Rtcm_Message& msg) = 0; }; class Rtcm_Listener_Room { public: inline void join(const std::shared_ptr& participant) { participants_.insert(participant); for (auto msg : recent_msgs_) { participant->deliver(msg); } } inline void leave(const std::shared_ptr& participant) { participants_.erase(participant); } inline void deliver(const Rtcm_Message& msg) { recent_msgs_.push_back(msg); while (recent_msgs_.size() > max_recent_msgs) { recent_msgs_.pop_front(); } for (const auto& participant : participants_) { participant->deliver(msg); } } private: std::set > participants_; enum { max_recent_msgs = 1 }; std::deque recent_msgs_; }; class Rtcm_Session : public RtcmListener, public std::enable_shared_from_this { public: Rtcm_Session(boost::asio::ip::tcp::socket socket, Rtcm_Listener_Room& room) : socket_(std::move(socket)), room_(room) {} inline void start() { room_.join(shared_from_this()); do_read_message_header(); } inline void deliver(const Rtcm_Message& msg) { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { do_write(); } } private: inline void do_read_message_header() { auto self(shared_from_this()); boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.data(), Rtcm_Message::header_length), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec and read_msg_.decode_header()) { do_read_message_body(); } else if (!ec and !read_msg_.decode_header()) { client_says += read_msg_.data(); bool first = true; while (client_says.length() >= 80) { if (first == true) { LOG(INFO) << "Client says:"; first = false; } LOG(INFO) << client_says; client_says = client_says.substr(80, client_says.length() - 80); } do_read_message_header(); } else { std::cout << "Closing connection with RTCM client" << std::endl; room_.leave(shared_from_this()); } }); } inline void do_read_message_body() { auto self(shared_from_this()); boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { room_.deliver(read_msg_); //std::cout << "Delivered message (session): "; //std::cout.write(read_msg_.body(), read_msg_.body_length()); //std::cout << std::endl; do_read_message_header(); } else { std::cout << "Closing connection with RTCM client" << std::endl; room_.leave(shared_from_this()); } }); } inline void do_write() { auto self(shared_from_this()); boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().body(), write_msgs_.front().body_length()), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { write_msgs_.pop_front(); if (!write_msgs_.empty()) { do_write(); } } else { std::cout << "Closing connection with RTCM client" << std::endl; room_.leave(shared_from_this()); } }); } boost::asio::ip::tcp::socket socket_; Rtcm_Listener_Room& room_; Rtcm_Message read_msg_; std::deque write_msgs_; std::string client_says; }; class Tcp_Internal_Client : public std::enable_shared_from_this { public: Tcp_Internal_Client(b_io_context& io_context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator) : io_context_(io_context), socket_(io_context) { do_connect(std::move(endpoint_iterator)); } inline void close() { io_context_.post([this]() { socket_.close(); }); } inline void write(const Rtcm_Message& msg) { io_context_.post( [this, msg]() { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { do_write(); } }); } private: inline void do_connect(boost::asio::ip::tcp::resolver::iterator endpoint_iterator) { boost::asio::async_connect(socket_, std::move(endpoint_iterator), [this](boost::system::error_code ec, boost::asio::ip::tcp::resolver::iterator) { if (!ec) { do_read_message(); } else { std::cout << "Server is down." << std::endl; } }); } inline void do_read_message() { boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.data(), 1029), [this](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { do_read_message(); } else { std::cout << "Error in client" << std::endl; socket_.close(); } }); } inline void do_write() { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), [this](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { write_msgs_.pop_front(); if (!write_msgs_.empty()) { do_write(); } } else { socket_.close(); } }); } b_io_context& io_context_; boost::asio::ip::tcp::socket socket_; Rtcm_Message read_msg_; std::deque write_msgs_; }; class Queue_Reader { public: Queue_Reader(b_io_context& io_context, std::shared_ptr >& queue, int32_t port) : queue_(queue) { boost::asio::ip::tcp::resolver resolver(io_context); std::string host("localhost"); std::string port_str = std::to_string(port); auto queue_endpoint_iterator = resolver.resolve({host.c_str(), port_str.c_str()}); c = std::make_shared(io_context, queue_endpoint_iterator); } inline void do_read_queue() { for (;;) { std::string message; Rtcm_Message msg; queue_->wait_and_pop(message); //message += '\n'; if (message == "Goodbye") { break; } const char* char_msg = message.c_str(); msg.body_length(message.length()); std::memcpy(msg.body(), char_msg, msg.body_length()); msg.encode_header(); c->write(msg); } } private: std::shared_ptr c; std::shared_ptr >& queue_; }; class Tcp_Server { public: Tcp_Server(b_io_context& io_context, const boost::asio::ip::tcp::endpoint& endpoint) : acceptor_(io_context), socket_(io_context) { acceptor_.open(endpoint.protocol()); acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor_.bind(endpoint); acceptor_.listen(); do_accept(); } inline void close_server() { socket_.close(); acceptor_.close(); } private: inline void do_accept() { acceptor_.async_accept(socket_, [this](boost::system::error_code ec) { if (!ec) { if (first_client) { std::cout << "The TCP/IP server of RTCM messages is up and running. Accepting connections ..." << std::endl; first_client = false; } else { std::cout << "Starting RTCM TCP/IP server session..." << std::endl; boost::system::error_code ec2; boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(ec2); if (ec2) { // Error creating remote_endpoint std::cout << "Error getting remote IP address, closing session." << std::endl; LOG(INFO) << "Error getting remote IP address"; start_session = false; } else { std::string remote_addr = endpoint.address().to_string(); std::cout << "Serving client from " << remote_addr << std::endl; LOG(INFO) << "Serving client from " << remote_addr; } } if (start_session) { std::make_shared(std::move(socket_), room_)->start(); } } else { std::cout << "Error when invoking a RTCM session. " << ec << std::endl; } start_session = true; do_accept(); }); } boost::asio::ip::tcp::acceptor acceptor_; boost::asio::ip::tcp::socket socket_; Rtcm_Listener_Room room_; bool first_client = true; bool start_session = true; }; b_io_context io_context; std::shared_ptr > rtcm_message_queue; std::thread t; std::thread tq; std::list servers; bool server_is_running; void stop_service(); // // Transport Layer // std::bitset<8> preamble; std::bitset<6> reserved_field; std::string add_CRC(const std::string& m) const; std::string build_message(const std::string& data) const; // adds 0s to complete a byte and adds the CRC // // Data Fields // std::bitset<12> DF002; int32_t set_DF002(uint32_t message_number); std::bitset<12> DF003; int32_t set_DF003(uint32_t ref_station_ID); std::bitset<30> DF004; int32_t set_DF004(double obs_time); std::bitset<1> DF005; int32_t set_DF005(bool sync_flag); std::bitset<5> DF006; int32_t set_DF006(const std::map& observables); std::bitset<1> DF007; int32_t set_DF007(bool divergence_free_smoothing_indicator); // 0 - Divergence-free smoothing not used 1 - Divergence-free smoothing used std::bitset<3> DF008; int32_t set_DF008(int16_t smoothing_interval); std::bitset<6> DF009; int32_t set_DF009(const Gnss_Synchro& gnss_synchro); int32_t set_DF009(const Gps_Ephemeris& gps_eph); std::bitset<1> DF010; int32_t set_DF010(bool code_indicator); std::bitset<24> DF011; int32_t set_DF011(const Gnss_Synchro& gnss_synchro); std::bitset<20> DF012; int32_t set_DF012(const Gnss_Synchro& gnss_synchro); std::bitset<7> DF013; int32_t set_DF013(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<8> DF014; int32_t set_DF014(const Gnss_Synchro& gnss_synchro); std::bitset<8> DF015; int32_t set_DF015(const Gnss_Synchro& gnss_synchro); std::bitset<14> DF017; int32_t set_DF017(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<20> DF018; int32_t set_DF018(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<7> DF019; int32_t set_DF019(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<8> DF020; int32_t set_DF020(const Gnss_Synchro& gnss_synchro); std::bitset<6> DF021; int32_t set_DF021(); std::bitset<1> DF022; int32_t set_DF022(bool gps_indicator); std::bitset<1> DF023; int32_t set_DF023(bool glonass_indicator); std::bitset<1> DF024; int32_t set_DF024(bool galileo_indicator); std::bitset<38> DF025; int32_t set_DF025(double antenna_ECEF_X_m); std::bitset<38> DF026; int32_t set_DF026(double antenna_ECEF_Y_m); std::bitset<38> DF027; int32_t set_DF027(double antenna_ECEF_Z_m); std::bitset<16> DF028; int32_t set_DF028(double height); std::bitset<8> DF029; std::bitset<8> DF031; int32_t set_DF031(uint32_t antenna_setup_id); std::bitset<8> DF032; /*! * \brief Sets the Data Field value * \note Code added as part of GSoC 2017 program * \param obs_time Time of observation at the moment of printing * \return returns 0 upon success */ int32_t set_DF034(double obs_time); std::bitset<27> DF034; //!< GLONASS Epoch Time (tk) std::bitset<5> DF035; //!< No. of GLONASS Satellite Signals Processed int32_t set_DF035(const std::map& observables); std::bitset<1> DF036; //!< GLONASS Divergence-free Smoothing Indicator int32_t set_DF036(bool divergence_free_smoothing_indicator); std::bitset<3> DF037; //!< GLONASS Smoothing Interval int32_t set_DF037(int16_t smoothing_interval); std::bitset<6> DF038; //!< GLONASS Satellite ID (Satellite Slot Number) int32_t set_DF038(const Gnss_Synchro& gnss_synchro); int32_t set_DF038(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF039; //!< GLONASS L1 Code Indicator int32_t set_DF039(bool code_indicator); std::bitset<5> DF040; //!< GLONASS Satellite Frequency Number int32_t set_DF040(int32_t frequency_channel_number); int32_t set_DF040(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<25> DF041; //!< GLONASS L1 Pseudorange int32_t set_DF041(const Gnss_Synchro& gnss_synchro); std::bitset<20> DF042; //!< GLONASS L1 PhaseRange - L1 Pseudorange int32_t set_DF042(const Gnss_Synchro& gnss_synchro); std::bitset<7> DF043; //!< GLONASS L1 Lock Time Indicator int32_t set_DF043(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<7> DF044; //!< GLONASS Integer L1 Pseudorange Modulus Ambiguity int32_t set_DF044(const Gnss_Synchro& gnss_synchro); std::bitset<8> DF045; //!< GLONASS L1 CNR int32_t set_DF045(const Gnss_Synchro& gnss_synchro); std::bitset<2> DF046; //!< GLONASS L2 code indicator int32_t set_DF046(uint16_t code_indicator); std::bitset<14> DF047; //!< GLONASS L2 - L1 Pseudorange Difference int32_t set_DF047(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<20> DF048; //!< GLONASS L2 PhaseRange - L1 Pseudorange int32_t set_DF048(const Gnss_Synchro& gnss_synchroL1, const Gnss_Synchro& gnss_synchroL2); std::bitset<7> DF049; //!< GLONASS L2 Lock Time Indicator int32_t set_DF049(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<8> DF050; //!< GLONASS L2 CNR int32_t set_DF050(const Gnss_Synchro& gnss_synchro); std::bitset<16> DF051; int32_t set_DF051(const Gps_Ephemeris& gps_eph, double obs_time); std::bitset<17> DF052; int32_t set_DF052(const Gps_Ephemeris& gps_eph, double obs_time); // Contents of GPS Satellite Ephemeris Data, Message Type 1019 std::bitset<8> DF071; int32_t set_DF071(const Gps_Ephemeris& gps_eph); std::bitset<10> DF076; int32_t set_DF076(const Gps_Ephemeris& gps_eph); std::bitset<4> DF077; int32_t set_DF077(const Gps_Ephemeris& gps_eph); std::bitset<2> DF078; int32_t set_DF078(const Gps_Ephemeris& gps_eph); std::bitset<14> DF079; int32_t set_DF079(const Gps_Ephemeris& gps_eph); std::bitset<8> DF080; int32_t set_DF080(const Gps_Ephemeris& gps_eph); std::bitset<16> DF081; int32_t set_DF081(const Gps_Ephemeris& gps_eph); std::bitset<8> DF082; int32_t set_DF082(const Gps_Ephemeris& gps_eph); std::bitset<16> DF083; int32_t set_DF083(const Gps_Ephemeris& gps_eph); std::bitset<22> DF084; int32_t set_DF084(const Gps_Ephemeris& gps_eph); std::bitset<10> DF085; int32_t set_DF085(const Gps_Ephemeris& gps_eph); std::bitset<16> DF086; int32_t set_DF086(const Gps_Ephemeris& gps_eph); std::bitset<16> DF087; int32_t set_DF087(const Gps_Ephemeris& gps_eph); std::bitset<32> DF088; int32_t set_DF088(const Gps_Ephemeris& gps_eph); std::bitset<16> DF089; int32_t set_DF089(const Gps_Ephemeris& gps_eph); std::bitset<32> DF090; int32_t set_DF090(const Gps_Ephemeris& gps_eph); std::bitset<16> DF091; int32_t set_DF091(const Gps_Ephemeris& gps_eph); std::bitset<32> DF092; int32_t set_DF092(const Gps_Ephemeris& gps_eph); std::bitset<16> DF093; int32_t set_DF093(const Gps_Ephemeris& gps_eph); std::bitset<16> DF094; int32_t set_DF094(const Gps_Ephemeris& gps_eph); std::bitset<32> DF095; int32_t set_DF095(const Gps_Ephemeris& gps_eph); std::bitset<16> DF096; int32_t set_DF096(const Gps_Ephemeris& gps_eph); std::bitset<32> DF097; int32_t set_DF097(const Gps_Ephemeris& gps_eph); std::bitset<16> DF098; int32_t set_DF098(const Gps_Ephemeris& gps_eph); std::bitset<32> DF099; int32_t set_DF099(const Gps_Ephemeris& gps_eph); std::bitset<24> DF100; int32_t set_DF100(const Gps_Ephemeris& gps_eph); std::bitset<8> DF101; int32_t set_DF101(const Gps_Ephemeris& gps_eph); std::bitset<6> DF102; int32_t set_DF102(const Gps_Ephemeris& gps_eph); std::bitset<1> DF103; int32_t set_DF103(const Gps_Ephemeris& gps_eph); std::bitset<1> DF104; //!< GLONASS Almanac Health int32_t set_DF104(uint32_t glonass_gnav_alm_health); std::bitset<1> DF105; //!< GLONASS Almanac Health Availability Indicator int32_t set_DF105(uint32_t glonass_gnav_alm_health_ind); std::bitset<2> DF106; //!< GLONASS P1 Word int32_t set_DF106(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<12> DF107; //!< GLONASS Epoch (tk) int32_t set_DF107(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF108; //!< GLONASS MSB of Bn Word int32_t set_DF108(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF109; //!< GLONASS P2 Word int32_t set_DF109(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<7> DF110; //!< GLONASS Ephmeris Epoch (tb) int32_t set_DF110(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<24> DF111; //!< GLONASS Xn first derivative int32_t set_DF111(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<27> DF112; //!< GLONASS Xn int32_t set_DF112(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<5> DF113; //!< GLONASS Xn second derivative int32_t set_DF113(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<24> DF114; //!< GLONASS Yn first derivative int32_t set_DF114(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<27> DF115; //!< GLONASS Yn int32_t set_DF115(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<5> DF116; //!< GLONASS Yn second derivative int32_t set_DF116(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<24> DF117; //!< GLONASS Zn first derivative int32_t set_DF117(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<27> DF118; //!< GLONASS Zn int32_t set_DF118(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<5> DF119; //!< GLONASS Zn second derivative int32_t set_DF119(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF120; //!< GLONASS P3 int32_t set_DF120(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<11> DF121; //!< GLONASS GAMMA_N int32_t set_DF121(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<2> DF122; //!< GLONASS P int32_t set_DF122(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF123; //!< GLONASS ln (third string) int32_t set_DF123(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<22> DF124; //!< GLONASS TAU_N int32_t set_DF124(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<5> DF125; //!< GLONASS DELTA_TAU_N int32_t set_DF125(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<5> DF126; //!< GLONASS Eccentricity int32_t set_DF126(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF127; //!< GLONASS P4 int32_t set_DF127(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<4> DF128; //!< GLONASS F_T int32_t set_DF128(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<11> DF129; //!< GLONASS N_T int32_t set_DF129(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<2> DF130; //!< GLONASS M int32_t set_DF130(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF131; //!< GLONASS Availability of additional data int32_t set_DF131(uint32_t fifth_str_additional_data_ind); std::bitset<11> DF132; //!< GLONASS N_A int32_t set_DF132(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); std::bitset<32> DF133; //!< GLONASS TAU_C int32_t set_DF133(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); std::bitset<5> DF134; //!< GLONASS N_4 int32_t set_DF134(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); std::bitset<22> DF135; //!< GLONASS TAU_GPS int32_t set_DF135(const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); std::bitset<1> DF136; //!< GLONASS L_N (FIFTH STRING) int32_t set_DF136(const Glonass_Gnav_Ephemeris& glonass_gnav_eph); std::bitset<1> DF137; int32_t set_DF137(const Gps_Ephemeris& gps_eph); std::bitset<1> DF141; int32_t set_DF141(const Gps_Ephemeris& gps_eph); std::bitset<1> DF142; int32_t set_DF142(const Gps_Ephemeris& gps_eph); std::bitset<30> DF248; int32_t set_DF248(double obs_time); // Contents of Galileo F/NAV Satellite Ephemeris Data, Message Type 1045 std::bitset<6> DF252; int32_t set_DF252(const Galileo_Ephemeris& gal_eph); std::bitset<12> DF289; int32_t set_DF289(const Galileo_Ephemeris& gal_eph); std::bitset<10> DF290; int32_t set_DF290(const Galileo_Ephemeris& gal_eph); std::bitset<8> DF291; int32_t set_DF291(const Galileo_Ephemeris& gal_eph); std::bitset<14> DF292; int32_t set_DF292(const Galileo_Ephemeris& gal_eph); std::bitset<14> DF293; int32_t set_DF293(const Galileo_Ephemeris& gal_eph); std::bitset<6> DF294; int32_t set_DF294(const Galileo_Ephemeris& gal_eph); std::bitset<21> DF295; int32_t set_DF295(const Galileo_Ephemeris& gal_eph); std::bitset<31> DF296; int32_t set_DF296(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF297; int32_t set_DF297(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF298; int32_t set_DF298(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF299; int32_t set_DF299(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF300; int32_t set_DF300(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF301; int32_t set_DF301(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF302; int32_t set_DF302(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF303; int32_t set_DF303(const Galileo_Ephemeris& gal_eph); std::bitset<14> DF304; int32_t set_DF304(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF305; int32_t set_DF305(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF306; int32_t set_DF306(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF307; int32_t set_DF307(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF308; int32_t set_DF308(const Galileo_Ephemeris& gal_eph); std::bitset<16> DF309; int32_t set_DF309(const Galileo_Ephemeris& gal_eph); std::bitset<32> DF310; int32_t set_DF310(const Galileo_Ephemeris& gal_eph); std::bitset<24> DF311; int32_t set_DF311(const Galileo_Ephemeris& gal_eph); std::bitset<10> DF312; int32_t set_DF312(const Galileo_Ephemeris& gal_eph); std::bitset<10> DF313; int32_t set_DF313(const Galileo_Ephemeris& gal_eph); std::bitset<2> DF314; int32_t set_DF314(const Galileo_Ephemeris& gal_eph); std::bitset<1> DF315; int32_t set_DF315(const Galileo_Ephemeris& gal_eph); std::bitset<2> DF364; // Content of message header for MSM1, MSM2, MSM3, MSM4, MSM5, MSM6 and MSM7 std::bitset<1> DF393; int32_t set_DF393(bool more_messages); //1 indicates that more MSMs follow for given physical time and reference station ID std::bitset<64> DF394; int32_t set_DF394(const std::map& gnss_synchro); std::bitset<32> DF395; int32_t set_DF395(const std::map& gnss_synchro); std::string set_DF396(const std::map& observables); std::bitset<8> DF397; int32_t set_DF397(const Gnss_Synchro& gnss_synchro); std::bitset<10> DF398; int32_t set_DF398(const Gnss_Synchro& gnss_synchro); std::bitset<14> DF399; int32_t set_DF399(const Gnss_Synchro& gnss_synchro); std::bitset<15> DF400; int32_t set_DF400(const Gnss_Synchro& gnss_synchro); std::bitset<22> DF401; int32_t set_DF401(const Gnss_Synchro& gnss_synchro); std::bitset<4> DF402; int32_t set_DF402(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<6> DF403; int32_t set_DF403(const Gnss_Synchro& gnss_synchro); std::bitset<15> DF404; int32_t set_DF404(const Gnss_Synchro& gnss_synchro); std::bitset<20> DF405; int32_t set_DF405(const Gnss_Synchro& gnss_synchro); std::bitset<24> DF406; int32_t set_DF406(const Gnss_Synchro& gnss_synchro); std::bitset<10> DF407; int32_t set_DF407(const Gps_Ephemeris& ephNAV, const Gps_CNAV_Ephemeris& ephCNAV, const Galileo_Ephemeris& ephFNAV, const Glonass_Gnav_Ephemeris& ephGNAV, double obs_time, const Gnss_Synchro& gnss_synchro); std::bitset<10> DF408; int32_t set_DF408(const Gnss_Synchro& gnss_synchro); std::bitset<3> DF409; int32_t set_DF409(uint32_t iods); std::bitset<2> DF411; int32_t set_DF411(uint32_t clock_steering_indicator); std::bitset<2> DF412; int32_t set_DF412(uint32_t external_clock_indicator); std::bitset<1> DF417; int32_t set_DF417(bool using_divergence_free_smoothing); std::bitset<3> DF418; int32_t set_DF418(int32_t carrier_smoothing_interval_s); std::bitset<1> DF420; int32_t set_DF420(const Gnss_Synchro& gnss_synchro); }; #endif src/algorithms/PVT/libs/rtcm_printer.cc000066400000000000000000000406251352176506000204530ustar00rootroot00000000000000/*! * \file rtcm_printer.cc * \brief Implementation of a RTCM 3.2 printer for GNSS-SDR * This class provides a implementation of a subset of the RTCM Standard 10403.2 * for Differential GNSS Services * * \author Carles Fernandez-Prades, 2014. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rtcm_printer.h" #include "galileo_ephemeris.h" #include "glonass_gnav_ephemeris.h" #include "glonass_gnav_utc_model.h" #include "gnss_synchro.h" #include "gps_cnav_ephemeris.h" #include "gps_ephemeris.h" #include "rtcm.h" #include #include #include // for tm #include // for exception #include // for O_RDWR #include // for cout, cerr #include // for tcgetattr #include // for close, write #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Rtcm_Printer::Rtcm_Printer(const std::string& filename, bool flag_rtcm_file_dump, bool flag_rtcm_server, bool flag_rtcm_tty_port, uint16_t rtcm_tcp_port, uint16_t rtcm_station_id, const std::string& rtcm_dump_devname, bool time_tag_name, const std::string& base_path) { boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm timeinfo = boost::posix_time::to_tm(pt); d_rtcm_file_dump = flag_rtcm_file_dump; rtcm_base_path = base_path; if (d_rtcm_file_dump) { fs::path full_path(fs::current_path()); const fs::path p(rtcm_base_path); if (!fs::exists(p)) { std::string new_folder; for (auto& folder : fs::path(rtcm_base_path)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { std::cout << "Could not create the " << new_folder << " folder." << std::endl; rtcm_base_path = full_path.string(); } } new_folder += fs::path::preferred_separator; } } else { rtcm_base_path = p.string(); } if (rtcm_base_path != ".") { std::cout << "RTCM binary file will be stored at " << rtcm_base_path << std::endl; } rtcm_base_path = rtcm_base_path + fs::path::preferred_separator; } if (time_tag_name) { std::stringstream strm0; const int32_t year = timeinfo.tm_year - 100; strm0 << year; const int32_t month = timeinfo.tm_mon + 1; if (month < 10) { strm0 << "0"; } strm0 << month; const int32_t day = timeinfo.tm_mday; if (day < 10) { strm0 << "0"; } strm0 << day << "_"; const int32_t hour = timeinfo.tm_hour; if (hour < 10) { strm0 << "0"; } strm0 << hour; const int32_t min = timeinfo.tm_min; if (min < 10) { strm0 << "0"; } strm0 << min; const int32_t sec = timeinfo.tm_sec; if (sec < 10) { strm0 << "0"; } strm0 << sec; rtcm_filename = filename + "_" + strm0.str() + ".rtcm"; } else { rtcm_filename = filename + ".rtcm"; } rtcm_filename = rtcm_base_path + rtcm_filename; if (d_rtcm_file_dump) { rtcm_file_descriptor.open(rtcm_filename.c_str(), std::ios::out); if (rtcm_file_descriptor.is_open()) { DLOG(INFO) << "RTCM printer writing on " << rtcm_filename.c_str(); } else { std::cout << "File " << rtcm_filename << "cannot be saved. Wrong permissions?" << std::endl; } } rtcm_devname = rtcm_dump_devname; if (flag_rtcm_tty_port == true) { rtcm_dev_descriptor = init_serial(rtcm_devname.c_str()); if (rtcm_dev_descriptor != -1) { DLOG(INFO) << "RTCM printer writing on " << rtcm_devname.c_str(); } } else { rtcm_dev_descriptor = -1; } port = rtcm_tcp_port; station_id = rtcm_station_id; rtcm = std::make_shared(port); if (flag_rtcm_server) { rtcm->run_server(); } } Rtcm_Printer::~Rtcm_Printer() { if (rtcm->is_server_running()) { try { rtcm->stop_server(); } catch (const boost::exception& e) { LOG(WARNING) << "Boost exception: " << boost::diagnostic_information(e); } catch (const std::exception& ex) { LOG(WARNING) << "STD exception: " << ex.what(); } } if (rtcm_file_descriptor.is_open()) { int64_t pos; pos = rtcm_file_descriptor.tellp(); try { rtcm_file_descriptor.close(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } if (pos == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(rtcm_filename), ec)) { LOG(INFO) << "Error deleting temporary RTCM file"; } } } try { close_serial(); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } } bool Rtcm_Printer::Print_Rtcm_MT1001(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables) { std::string m1001 = rtcm->print_MT1001(gps_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1001); return true; } bool Rtcm_Printer::Print_Rtcm_MT1002(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables) { std::string m1002 = rtcm->print_MT1002(gps_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1002); return true; } bool Rtcm_Printer::Print_Rtcm_MT1003(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& cnav_eph, double obs_time, const std::map& observables) { std::string m1003 = rtcm->print_MT1003(gps_eph, cnav_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1003); return true; } bool Rtcm_Printer::Print_Rtcm_MT1004(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& cnav_eph, double obs_time, const std::map& observables) { std::string m1003 = rtcm->print_MT1004(gps_eph, cnav_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1003); return true; } bool Rtcm_Printer::Print_Rtcm_MT1009(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables) { std::string m1009 = rtcm->print_MT1009(glonass_gnav_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1009); return true; } bool Rtcm_Printer::Print_Rtcm_MT1010(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables) { std::string m1010 = rtcm->print_MT1010(glonass_gnav_eph, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1010); return true; } bool Rtcm_Printer::Print_Rtcm_MT1011(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables) { std::string m1011 = rtcm->print_MT1011(glonass_gnav_ephL1, glonass_gnav_ephL2, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1011); return true; } bool Rtcm_Printer::Print_Rtcm_MT1012(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables) { std::string m1012 = rtcm->print_MT1012(glonass_gnav_ephL1, glonass_gnav_ephL2, obs_time, observables, station_id); Rtcm_Printer::Print_Message(m1012); return true; } bool Rtcm_Printer::Print_Rtcm_MT1019(const Gps_Ephemeris& gps_eph) { std::string m1019 = rtcm->print_MT1019(gps_eph); Rtcm_Printer::Print_Message(m1019); return true; } bool Rtcm_Printer::Print_Rtcm_MT1020(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model) { std::string m1020 = rtcm->print_MT1020(glonass_gnav_eph, glonass_gnav_utc_model); Rtcm_Printer::Print_Message(m1020); return true; } bool Rtcm_Printer::Print_Rtcm_MT1045(const Galileo_Ephemeris& gal_eph) { std::string m1045 = rtcm->print_MT1045(gal_eph); Rtcm_Printer::Print_Message(m1045); return true; } bool Rtcm_Printer::Print_Rtcm_MSM(uint32_t msm_number, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages) { std::string msm; if (msm_number == 1) { msm = rtcm->print_MSM_1(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 2) { msm = rtcm->print_MSM_2(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 3) { msm = rtcm->print_MSM_3(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 4) { msm = rtcm->print_MSM_4(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 5) { msm = rtcm->print_MSM_5(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 6) { msm = rtcm->print_MSM_6(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else if (msm_number == 7) { msm = rtcm->print_MSM_7(gps_eph, gps_cnav_eph, gal_eph, glo_gnav_eph, obs_time, observables, station_id, clock_steering_indicator, external_clock_indicator, smooth_int, divergence_free, more_messages); } else { return false; } Rtcm_Printer::Print_Message(msm); return true; } int Rtcm_Printer::init_serial(const std::string& serial_device) { /* * Opens the serial device and sets the default baud rate for a RTCM transmission (9600,8,N,1) */ int32_t fd = 0; // clang-format off struct termios options{}; // clang-format on int64_t BAUD; int64_t DATABITS; int64_t STOPBITS; int64_t PARITYON; int64_t PARITY; fd = open(serial_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC); if (fd == -1) { return fd; // failed to open TTY port } if (fcntl(fd, F_SETFL, 0) == -1) { LOG(INFO) << "Error enabling direct I/O"; // clear all flags on descriptor, enable direct I/O } tcgetattr(fd, &options); // read serial port options BAUD = B9600; // BAUD = B38400; DATABITS = CS8; STOPBITS = 0; PARITYON = 0; PARITY = 0; options.c_cflag = BAUD | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD; // enable receiver, set 8 bit data, ignore control lines // options.c_cflag |= (CLOCAL | CREAD | CS8); options.c_iflag = IGNPAR; // set the new port options tcsetattr(fd, TCSANOW, &options); return fd; } void Rtcm_Printer::close_serial() { if (rtcm_dev_descriptor != -1) { close(rtcm_dev_descriptor); } } bool Rtcm_Printer::Print_Message(const std::string& message) { // write to file if (d_rtcm_file_dump) { try { rtcm_file_descriptor << message << std::endl; } catch (const std::exception& ex) { DLOG(INFO) << "RTCM printer cannot write on the output file " << rtcm_filename.c_str(); return false; } } // write to serial device if (rtcm_dev_descriptor != -1) { if (write(rtcm_dev_descriptor, message.c_str(), message.length()) == -1) { DLOG(INFO) << "RTCM printer cannot write on serial device " << rtcm_devname.c_str(); std::cout << "RTCM printer cannot write on serial device " << rtcm_devname.c_str() << std::endl; return false; } } return true; } std::string Rtcm_Printer::print_MT1005_test() { std::string test = rtcm->print_MT1005_test(); return test; } uint32_t Rtcm_Printer::lock_time(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { return rtcm->lock_time(eph, obs_time, gnss_synchro); } uint32_t Rtcm_Printer::lock_time(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { return rtcm->lock_time(eph, obs_time, gnss_synchro); } uint32_t Rtcm_Printer::lock_time(const Galileo_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { return rtcm->lock_time(eph, obs_time, gnss_synchro); } uint32_t Rtcm_Printer::lock_time(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro) { return rtcm->lock_time(eph, obs_time, gnss_synchro); } src/algorithms/PVT/libs/rtcm_printer.h000066400000000000000000000204461352176506000203140ustar00rootroot00000000000000/*! * \file rtcm_printer.h * \brief Interface of a RTCM 3.2 printer for GNSS-SDR * This class provides a implementation of a subset of the RTCM Standard 10403.2 * for Differential GNSS Services * * \author Carles Fernandez-Prades, 2014. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RTCM_PRINTER_H_ #define GNSS_SDR_RTCM_PRINTER_H_ #include // for int32_t #include // for std::ofstream #include // for std::map #include // std::shared_ptr class Galileo_Ephemeris; class Glonass_Gnav_Ephemeris; class Glonass_Gnav_Utc_Model; class Gnss_Synchro; class Gps_CNAV_Ephemeris; class Gps_Ephemeris; class Rtcm; /*! * \brief This class provides a implementation of a subset of the RTCM Standard 10403.2 messages */ class Rtcm_Printer { public: /*! * \brief Default constructor. */ Rtcm_Printer(const std::string& filename, bool flag_rtcm_file_dump, bool flag_rtcm_server, bool flag_rtcm_tty_port, uint16_t rtcm_tcp_port, uint16_t rtcm_station_id, const std::string& rtcm_dump_devname, bool time_tag_name = true, const std::string& base_path = "."); /*! * \brief Default destructor. */ ~Rtcm_Printer(); bool Print_Rtcm_MT1001(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables); bool Print_Rtcm_MT1002(const Gps_Ephemeris& gps_eph, double obs_time, const std::map& observables); bool Print_Rtcm_MT1003(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& cnav_eph, double obs_time, const std::map& observables); bool Print_Rtcm_MT1004(const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& cnav_eph, double obs_time, const std::map& observables); /*! * \brief Prints L1-Only GLONASS RTK Observables * \details This GLONASS message type is not generally used or supported; type 1012 is to be preferred. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return true or false upon operation success */ bool Print_Rtcm_MT1009(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables); /*! * \brief Prints Extended L1-Only GLONASS RTK Observables * \details This GLONASS message type is used when only L1 data is present and bandwidth is very tight, often 1012 is used in such cases. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return true or false upon operation success */ bool Print_Rtcm_MT1010(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, double obs_time, const std::map& observables); /*! * \brief Prints L1&L2 GLONASS RTK Observables * \details This GLONASS message type is not generally used or supported; type 1012 is to be preferred * \note Code added as part of GSoC 2017 program * \param glonass_gnav_ephL1 GLONASS L1 GNAV Broadcast Ephemeris for satellite * \param glonass_gnav_ephL2 GLONASS L2 GNAV Broadcast Ephemeris for satellite * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return true or false upon operation success */ bool Print_Rtcm_MT1011(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables); /*! * \brief Prints Extended L1&L2 GLONASS RTK Observables * \details This GLONASS message type is the most common observational message type, with L1/L2/SNR content. This is one of the most common messages found. * \note Code added as part of GSoC 2017 program * \param glonass_gnav_ephL1 GLONASS L1 GNAV Broadcast Ephemeris for satellite * \param glonass_gnav_ephL2 GLONASS L2 GNAV Broadcast Ephemeris for satellite * \param obs_time Time of observation at the moment of printing * \param observables Set of observables as defined by the platform * \return true or false upon operation success */ bool Print_Rtcm_MT1012(const Glonass_Gnav_Ephemeris& glonass_gnav_ephL1, const Glonass_Gnav_Ephemeris& glonass_gnav_ephL2, double obs_time, const std::map& observables); bool Print_Rtcm_MT1019(const Gps_Ephemeris& gps_eph); //!< GPS Ephemeris, should be broadcast in the event that the IODC does not match the IODE, and every 2 minutes. bool Print_Rtcm_MT1045(const Galileo_Ephemeris& gal_eph); //!< Galileo Ephemeris, should be broadcast every 2 minutes /*! * \brief Prints GLONASS GNAV Ephemeris * \details This GLONASS message should be broadcast every 2 minutes * \note Code added as part of GSoC 2017 program * \param glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \param utc_model GLONASS GNAV Clock Information broadcast in string 5 * \return true or false upon operation success */ bool Print_Rtcm_MT1020(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& utc_model); bool Print_Rtcm_MSM(uint32_t msm_number, const Gps_Ephemeris& gps_eph, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Galileo_Ephemeris& gal_eph, const Glonass_Gnav_Ephemeris& glo_gnav_eph, double obs_time, const std::map& observables, uint32_t clock_steering_indicator, uint32_t external_clock_indicator, int32_t smooth_int, bool divergence_free, bool more_messages); std::string print_MT1005_test(); //!< For testing purposes uint32_t lock_time(const Gps_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); uint32_t lock_time(const Gps_CNAV_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); uint32_t lock_time(const Galileo_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); /*! * \brief Locks time for logging given GLONASS GNAV Broadcast Ephemeris * \note Code added as part of GSoC 2017 program * \params glonass_gnav_eph GLONASS GNAV Broadcast Ephemeris * \params obs_time Time of observation at the moment of printing * \params observables Set of observables as defined by the platform * \return locked time during logging process */ uint32_t lock_time(const Glonass_Gnav_Ephemeris& eph, double obs_time, const Gnss_Synchro& gnss_synchro); private: std::string rtcm_filename; // String with the RTCM log filename std::string rtcm_base_path; std::ofstream rtcm_file_descriptor; // Output file stream for RTCM log file std::string rtcm_devname; uint16_t port; uint16_t station_id; int32_t rtcm_dev_descriptor; // RTCM serial device descriptor (i.e. COM port) int32_t init_serial(const std::string& serial_device); //serial port control void close_serial(); std::shared_ptr rtcm; bool Print_Message(const std::string& message); bool d_rtcm_file_dump; }; #endif src/algorithms/PVT/libs/rtklib_solver.cc000066400000000000000000002014141352176506000206170ustar00rootroot00000000000000/*! * \file rtklib_solver.cc * \brief PVT solver based on rtklib library functions adapted to the GNSS-SDR * data flow and structures * \authors
    *
  • 2017-2019, Javier Arribas *
  • 2017-2019, Carles Fernandez *
  • 2007-2013, T. Takasu *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017-2019, Javier Arribas * Copyright (C) 2017-2019, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * -----------------------------------------------------------------------*/ #include "rtklib_solver.h" #include "Beidou_B1I.h" #include "Beidou_B3I.h" #include "Beidou_DNAV.h" #include "GLONASS_L1_L2_CA.h" #include "GPS_L1_CA.h" #include "Galileo_E1.h" #include "rtklib_conversions.h" #include "rtklib_rtkpos.h" #include "rtklib_solution.h" #include #include #include #include #include #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif Rtklib_Solver::Rtklib_Solver(int nchannels, std::string dump_filename, bool flag_dump_to_file, bool flag_dump_to_mat, const rtk_t &rtk) { // init empty ephemeris for all the available GNSS channels d_nchannels = nchannels; d_dump_filename = std::move(dump_filename); d_flag_dump_enabled = flag_dump_to_file; d_flag_dump_mat_enabled = flag_dump_to_mat; this->set_averaging_flag(false); rtk_ = rtk; // ############# ENABLE DATA FILE LOG ################# if (d_flag_dump_enabled == true) { if (d_dump_file.is_open() == false) { try { d_dump_file.exceptions(std::ofstream::failbit | std::ofstream::badbit); d_dump_file.open(d_dump_filename.c_str(), std::ios::out | std::ios::binary); LOG(INFO) << "PVT lib dump enabled Log file: " << d_dump_filename.c_str(); } catch (const std::ofstream::failure &e) { LOG(WARNING) << "Exception opening RTKLIB dump file " << e.what(); } } } } Rtklib_Solver::~Rtklib_Solver() { if (d_dump_file.is_open() == true) { auto pos = d_dump_file.tellp(); try { d_dump_file.close(); } catch (const std::exception &ex) { LOG(WARNING) << "Exception in destructor closing the RTKLIB dump file " << ex.what(); } if (pos == 0) { errorlib::error_code ec; if (!fs::remove(fs::path(d_dump_filename), ec)) { std::cerr << "Problem removing temporary file " << d_dump_filename << '\n'; } d_flag_dump_mat_enabled = false; } } if (d_flag_dump_mat_enabled) { try { save_matfile(); } catch (const std::exception &ex) { LOG(WARNING) << "Exception in destructor saving the PVT .mat dump file " << ex.what(); } } } bool Rtklib_Solver::save_matfile() { // READ DUMP FILE std::string dump_filename = d_dump_filename; std::ifstream::pos_type size; int32_t number_of_double_vars = 21; int32_t number_of_uint32_vars = 2; int32_t number_of_uint8_vars = 3; int32_t number_of_float_vars = 2; int32_t epoch_size_bytes = sizeof(double) * number_of_double_vars + sizeof(uint32_t) * number_of_uint32_vars + sizeof(uint8_t) * number_of_uint8_vars + sizeof(float) * number_of_float_vars; std::ifstream dump_file; dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { dump_file.open(dump_filename.c_str(), std::ios::binary | std::ios::ate); } catch (const std::ifstream::failure &e) { std::cerr << "Problem opening dump file:" << e.what() << std::endl; return false; } // count number of epochs and rewind int64_t num_epoch = 0LL; if (dump_file.is_open()) { std::cout << "Generating .mat file for " << dump_filename << std::endl; size = dump_file.tellg(); num_epoch = static_cast(size) / static_cast(epoch_size_bytes); dump_file.seekg(0, std::ios::beg); } else { return false; } auto TOW_at_current_symbol_ms = std::vector(num_epoch); auto week = std::vector(num_epoch); auto RX_time = std::vector(num_epoch); auto user_clk_offset = std::vector(num_epoch); auto pos_x = std::vector(num_epoch); auto pos_y = std::vector(num_epoch); auto pos_z = std::vector(num_epoch); auto vel_x = std::vector(num_epoch); auto vel_y = std::vector(num_epoch); auto vel_z = std::vector(num_epoch); auto cov_xx = std::vector(num_epoch); auto cov_yy = std::vector(num_epoch); auto cov_zz = std::vector(num_epoch); auto cov_xy = std::vector(num_epoch); auto cov_yz = std::vector(num_epoch); auto cov_zx = std::vector(num_epoch); auto latitude = std::vector(num_epoch); auto longitude = std::vector(num_epoch); auto height = std::vector(num_epoch); auto valid_sats = std::vector(num_epoch); auto solution_status = std::vector(num_epoch); auto solution_type = std::vector(num_epoch); auto AR_ratio_factor = std::vector(num_epoch); auto AR_ratio_threshold = std::vector(num_epoch); auto gdop = std::vector(num_epoch); auto pdop = std::vector(num_epoch); auto hdop = std::vector(num_epoch); auto vdop = std::vector(num_epoch); try { if (dump_file.is_open()) { for (int64_t i = 0; i < num_epoch; i++) { dump_file.read(reinterpret_cast(&TOW_at_current_symbol_ms[i]), sizeof(uint32_t)); dump_file.read(reinterpret_cast(&week[i]), sizeof(uint32_t)); dump_file.read(reinterpret_cast(&RX_time[i]), sizeof(double)); dump_file.read(reinterpret_cast(&user_clk_offset[i]), sizeof(double)); dump_file.read(reinterpret_cast(&pos_x[i]), sizeof(double)); dump_file.read(reinterpret_cast(&pos_y[i]), sizeof(double)); dump_file.read(reinterpret_cast(&pos_z[i]), sizeof(double)); dump_file.read(reinterpret_cast(&vel_x[i]), sizeof(double)); dump_file.read(reinterpret_cast(&vel_y[i]), sizeof(double)); dump_file.read(reinterpret_cast(&vel_z[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_xx[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_yy[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_zz[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_xy[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_yz[i]), sizeof(double)); dump_file.read(reinterpret_cast(&cov_zx[i]), sizeof(double)); dump_file.read(reinterpret_cast(&latitude[i]), sizeof(double)); dump_file.read(reinterpret_cast(&longitude[i]), sizeof(double)); dump_file.read(reinterpret_cast(&height[i]), sizeof(double)); dump_file.read(reinterpret_cast(&valid_sats[i]), sizeof(uint8_t)); dump_file.read(reinterpret_cast(&solution_status[i]), sizeof(uint8_t)); dump_file.read(reinterpret_cast(&solution_type[i]), sizeof(uint8_t)); dump_file.read(reinterpret_cast(&AR_ratio_factor[i]), sizeof(float)); dump_file.read(reinterpret_cast(&AR_ratio_threshold[i]), sizeof(float)); dump_file.read(reinterpret_cast(&gdop[i]), sizeof(double)); dump_file.read(reinterpret_cast(&pdop[i]), sizeof(double)); dump_file.read(reinterpret_cast(&hdop[i]), sizeof(double)); dump_file.read(reinterpret_cast(&vdop[i]), sizeof(double)); } } dump_file.close(); } catch (const std::ifstream::failure &e) { std::cerr << "Problem reading dump file:" << e.what() << std::endl; return false; } // WRITE MAT FILE mat_t *matfp; matvar_t *matvar; std::string filename = dump_filename; filename.erase(filename.length() - 4, 4); filename.append(".mat"); matfp = Mat_CreateVer(filename.c_str(), nullptr, MAT_FT_MAT73); if (reinterpret_cast(matfp) != nullptr) { std::array dims{1, static_cast(num_epoch)}; matvar = Mat_VarCreate("TOW_at_current_symbol_ms", MAT_C_UINT32, MAT_T_UINT32, 2, dims.data(), TOW_at_current_symbol_ms.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("week", MAT_C_UINT32, MAT_T_UINT32, 2, dims.data(), week.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("RX_time", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), RX_time.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("user_clk_offset", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), user_clk_offset.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("pos_x", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), pos_x.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("pos_y", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), pos_y.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("pos_z", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), pos_z.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("vel_x", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), vel_x.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("vel_y", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), vel_y.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("vel_z", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), vel_z.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_xx", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_xx.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_yy", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_yy.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_zz", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_zz.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_xy", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_xy.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_yz", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_yz.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("cov_zx", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), cov_zx.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("latitude", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), latitude.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("longitude", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), longitude.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("height", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), height.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("valid_sats", MAT_C_UINT8, MAT_T_UINT8, 2, dims.data(), valid_sats.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("solution_status", MAT_C_UINT8, MAT_T_UINT8, 2, dims.data(), solution_status.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("solution_type", MAT_C_UINT8, MAT_T_UINT8, 2, dims.data(), solution_type.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("AR_ratio_factor", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), AR_ratio_factor.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("AR_ratio_threshold", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), AR_ratio_threshold.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("gdop", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), gdop.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("pdop", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), pdop.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("hdop", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), hdop.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("vdop", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), vdop.data(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); } Mat_Close(matfp); return true; } double Rtklib_Solver::get_gdop() const { return dop_[0]; } double Rtklib_Solver::get_pdop() const { return dop_[1]; } double Rtklib_Solver::get_hdop() const { return dop_[2]; } double Rtklib_Solver::get_vdop() const { return dop_[3]; } Monitor_Pvt Rtklib_Solver::get_monitor_pvt() const { return monitor_pvt; } bool Rtklib_Solver::get_PVT(const std::map &gnss_observables_map, bool flag_averaging) { std::map::const_iterator gnss_observables_iter; std::map::const_iterator galileo_ephemeris_iter; std::map::const_iterator gps_ephemeris_iter; std::map::const_iterator gps_cnav_ephemeris_iter; std::map::const_iterator glonass_gnav_ephemeris_iter; std::map::const_iterator beidou_ephemeris_iter; const Glonass_Gnav_Utc_Model gnav_utc = this->glonass_gnav_utc_model; this->set_averaging_flag(flag_averaging); // ******************************************************************************** // ****** PREPARE THE DATA (SV EPHEMERIS AND OBSERVATIONS) ************************ // ******************************************************************************** int valid_obs = 0; // valid observations counter int glo_valid_obs = 0; // GLONASS L1/L2 valid observations counter obs_data.fill({}); std::vector eph_data(MAXOBS); std::vector geph_data(MAXOBS); // Workaround for NAV/CNAV clash problem bool gps_dual_band = false; bool band1 = false; bool band2 = false; for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); ++gnss_observables_iter) { switch (gnss_observables_iter->second.System) { case 'G': { std::string sig_(gnss_observables_iter->second.Signal); if (sig_ == "1C") { band1 = true; } if (sig_ == "2S") { band2 = true; } } break; default: { } } } if (band1 == true and band2 == true) { gps_dual_band = true; } for (gnss_observables_iter = gnss_observables_map.cbegin(); gnss_observables_iter != gnss_observables_map.cend(); ++gnss_observables_iter) // CHECK INCONSISTENCY when combining GLONASS + other system { switch (gnss_observables_iter->second.System) { case 'E': { std::string sig_(gnss_observables_iter->second.Signal); // Galileo E1 if (sig_ == "1B") { // 1 Gal - find the ephemeris for the current GALILEO SV observation. The SV PRN ID is the map key galileo_ephemeris_iter = galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (galileo_ephemeris_iter != galileo_ephemeris_map.cend()) { // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(galileo_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {}, {}, {}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, galileo_ephemeris_iter->second.WN_5, 0); valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } // Galileo E5 if (sig_ == "5X") { // 1 Gal - find the ephemeris for the current GALILEO SV observation. The SV PRN ID is the map key galileo_ephemeris_iter = galileo_ephemeris_map.find(gnss_observables_iter->second.PRN); if (galileo_ephemeris_iter != galileo_ephemeris_map.cend()) { bool found_E1_obs = false; for (int i = 0; i < valid_obs; i++) { if (eph_data[i].sat == (static_cast(gnss_observables_iter->second.PRN + NSATGPS + NSATGLO))) { obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(obs_data[i + glo_valid_obs], gnss_observables_iter->second, galileo_ephemeris_iter->second.WN_5, 2); // Band 3 (L5/E5) found_E1_obs = true; break; } } if (!found_E1_obs) { // insert Galileo E5 obs as new obs and also insert its ephemeris // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(galileo_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure auto default_code_ = static_cast(CODE_NONE); obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {default_code_, default_code_, default_code_}, {}, {0.0, 0.0, 0.0}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, galileo_ephemeris_iter->second.WN_5, 2); // Band 3 (L5/E5) valid_obs++; } } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } break; } case 'G': { // GPS L1 // 1 GPS - find the ephemeris for the current GPS SV observation. The SV PRN ID is the map key std::string sig_(gnss_observables_iter->second.Signal); if (sig_ == "1C") { gps_ephemeris_iter = gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_ephemeris_iter != gps_ephemeris_map.cend()) { // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(gps_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {}, {}, {}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, gps_ephemeris_iter->second.i_GPS_week, 0); valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->first; } } // GPS L2 (todo: solve NAV/CNAV clash) if ((sig_ == "2S") and (gps_dual_band == false)) { gps_cnav_ephemeris_iter = gps_cnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_cnav_ephemeris_iter != gps_cnav_ephemeris_map.cend()) { // 1. Find the same satellite in GPS L1 band gps_ephemeris_iter = gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_ephemeris_iter != gps_ephemeris_map.cend()) { /* By the moment, GPS L2 observables are not used in pseudorange computations if GPS L1 is available // 2. If found, replace the existing GPS L1 ephemeris with the GPS L2 ephemeris // (more precise!), and attach the L2 observation to the L1 observation in RTKLIB structure for (int i = 0; i < valid_obs; i++) { if (eph_data[i].sat == static_cast(gnss_observables_iter->second.PRN)) { eph_data[i] = eph_to_rtklib(gps_cnav_ephemeris_iter->second); obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(obs_data[i + glo_valid_obs], gnss_observables_iter->second, eph_data[i].week, 1); // Band 2 (L2) break; } } */ } else { // 3. If not found, insert the GPS L2 ephemeris and the observation // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(gps_cnav_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure auto default_code_ = static_cast(CODE_NONE); obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {default_code_, default_code_, default_code_}, {}, {0.0, 0.0, 0.0}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, gps_cnav_ephemeris_iter->second.i_GPS_week, 1); // Band 2 (L2) valid_obs++; } } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } // GPS L5 if (sig_ == "L5") { gps_cnav_ephemeris_iter = gps_cnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_cnav_ephemeris_iter != gps_cnav_ephemeris_map.cend()) { // 1. Find the same satellite in GPS L1 band gps_ephemeris_iter = gps_ephemeris_map.find(gnss_observables_iter->second.PRN); if (gps_ephemeris_iter != gps_ephemeris_map.cend()) { // 2. If found, replace the existing GPS L1 ephemeris with the GPS L5 ephemeris // (more precise!), and attach the L5 observation to the L1 observation in RTKLIB structure for (int i = 0; i < valid_obs; i++) { if (eph_data[i].sat == static_cast(gnss_observables_iter->second.PRN)) { eph_data[i] = eph_to_rtklib(gps_cnav_ephemeris_iter->second); obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(obs_data[i], gnss_observables_iter->second, gps_cnav_ephemeris_iter->second.i_GPS_week, 2); // Band 3 (L5) break; } } } else { // 3. If not found, insert the GPS L5 ephemeris and the observation // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(gps_cnav_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure auto default_code_ = static_cast(CODE_NONE); obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {default_code_, default_code_, default_code_}, {}, {0.0, 0.0, 0.0}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, gps_cnav_ephemeris_iter->second.i_GPS_week, 2); // Band 3 (L5) valid_obs++; } } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } break; } case 'R': //TODO This should be using rtk lib nomenclature { std::string sig_(gnss_observables_iter->second.Signal); // GLONASS GNAV L1 if (sig_ == "1G") { // 1 Glo - find the ephemeris for the current GLONASS SV observation. The SV Slot Number (PRN ID) is the map key glonass_gnav_ephemeris_iter = glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_ephemeris_iter != glonass_gnav_ephemeris_map.cend()) { // convert ephemeris from GNSS-SDR class to RTKLIB structure geph_data[glo_valid_obs] = eph_to_rtklib(glonass_gnav_ephemeris_iter->second, gnav_utc); // convert observation from GNSS-SDR class to RTKLIB structure obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {}, {}, {}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, glonass_gnav_ephemeris_iter->second.d_WN, 0); // Band 0 (L1) glo_valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } // GLONASS GNAV L2 if (sig_ == "2G") { // 1 GLONASS - find the ephemeris for the current GLONASS SV observation. The SV PRN ID is the map key glonass_gnav_ephemeris_iter = glonass_gnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (glonass_gnav_ephemeris_iter != glonass_gnav_ephemeris_map.cend()) { bool found_L1_obs = false; for (int i = 0; i < glo_valid_obs; i++) { if (geph_data[i].sat == (static_cast(gnss_observables_iter->second.PRN + NSATGPS))) { obs_data[i + valid_obs] = insert_obs_to_rtklib(obs_data[i + valid_obs], gnss_observables_iter->second, glonass_gnav_ephemeris_iter->second.d_WN, 1); // Band 1 (L2) found_L1_obs = true; break; } } if (!found_L1_obs) { // insert GLONASS GNAV L2 obs as new obs and also insert its ephemeris // convert ephemeris from GNSS-SDR class to RTKLIB structure geph_data[glo_valid_obs] = eph_to_rtklib(glonass_gnav_ephemeris_iter->second, gnav_utc); // convert observation from GNSS-SDR class to RTKLIB structure obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {}, {}, {}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, glonass_gnav_ephemeris_iter->second.d_WN, 1); // Band 1 (L2) glo_valid_obs++; } } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } break; } case 'C': { // BEIDOU B1I // - find the ephemeris for the current BEIDOU SV observation. The SV PRN ID is the map key std::string sig_(gnss_observables_iter->second.Signal); if (sig_ == "B1") { beidou_ephemeris_iter = beidou_dnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (beidou_ephemeris_iter != beidou_dnav_ephemeris_map.cend()) { // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(beidou_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {}, {}, {}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, beidou_ephemeris_iter->second.i_BEIDOU_week + BEIDOU_DNAV_BDT2GPST_WEEK_NUM_OFFSET, 0); valid_obs++; } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->first; } } // BeiDou B3 if (sig_ == "B3") { beidou_ephemeris_iter = beidou_dnav_ephemeris_map.find(gnss_observables_iter->second.PRN); if (beidou_ephemeris_iter != beidou_dnav_ephemeris_map.cend()) { bool found_B1I_obs = false; for (int i = 0; i < valid_obs; i++) { if (eph_data[i].sat == (static_cast(gnss_observables_iter->second.PRN + NSATGPS + NSATGLO + NSATGAL + NSATQZS))) { obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(obs_data[i + glo_valid_obs], gnss_observables_iter->second, beidou_ephemeris_iter->second.i_BEIDOU_week + BEIDOU_DNAV_BDT2GPST_WEEK_NUM_OFFSET, 2); // Band 3 (L2/G2/B3) found_B1I_obs = true; break; } } if (!found_B1I_obs) { // insert BeiDou B3I obs as new obs and also insert its ephemeris // convert ephemeris from GNSS-SDR class to RTKLIB structure eph_data[valid_obs] = eph_to_rtklib(beidou_ephemeris_iter->second); // convert observation from GNSS-SDR class to RTKLIB structure auto default_code_ = static_cast(CODE_NONE); obsd_t newobs = {{0, 0}, '0', '0', {}, {}, {default_code_, default_code_, default_code_}, {}, {0.0, 0.0, 0.0}, {}}; obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs, gnss_observables_iter->second, beidou_ephemeris_iter->second.i_BEIDOU_week + BEIDOU_DNAV_BDT2GPST_WEEK_NUM_OFFSET, 2); // Band 2 (L2/G2) valid_obs++; } } else // the ephemeris are not available for this SV { DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN; } } break; } default: DLOG(INFO) << "Hybrid observables: Unknown GNSS"; break; } } // ********************************************************************** // ****** SOLVE PVT****************************************************** // ********************************************************************** this->set_valid_position(false); if ((valid_obs + glo_valid_obs) > 3) { int result = 0; nav_t nav_data{}; nav_data.eph = eph_data.data(); nav_data.geph = geph_data.data(); nav_data.n = valid_obs; nav_data.ng = glo_valid_obs; if (gps_iono.valid) { nav_data.ion_gps[0] = gps_iono.d_alpha0; nav_data.ion_gps[1] = gps_iono.d_alpha1; nav_data.ion_gps[2] = gps_iono.d_alpha2; nav_data.ion_gps[3] = gps_iono.d_alpha3; nav_data.ion_gps[4] = gps_iono.d_beta0; nav_data.ion_gps[5] = gps_iono.d_beta1; nav_data.ion_gps[6] = gps_iono.d_beta2; nav_data.ion_gps[7] = gps_iono.d_beta3; } if (!(gps_iono.valid) and gps_cnav_iono.valid) { nav_data.ion_gps[0] = gps_cnav_iono.d_alpha0; nav_data.ion_gps[1] = gps_cnav_iono.d_alpha1; nav_data.ion_gps[2] = gps_cnav_iono.d_alpha2; nav_data.ion_gps[3] = gps_cnav_iono.d_alpha3; nav_data.ion_gps[4] = gps_cnav_iono.d_beta0; nav_data.ion_gps[5] = gps_cnav_iono.d_beta1; nav_data.ion_gps[6] = gps_cnav_iono.d_beta2; nav_data.ion_gps[7] = gps_cnav_iono.d_beta3; } if (galileo_iono.ai0_5 != 0.0) { nav_data.ion_gal[0] = galileo_iono.ai0_5; nav_data.ion_gal[1] = galileo_iono.ai1_5; nav_data.ion_gal[2] = galileo_iono.ai2_5; nav_data.ion_gal[3] = 0.0; } if (beidou_dnav_iono.valid) { nav_data.ion_cmp[0] = beidou_dnav_iono.d_alpha0; nav_data.ion_cmp[1] = beidou_dnav_iono.d_alpha1; nav_data.ion_cmp[2] = beidou_dnav_iono.d_alpha2; nav_data.ion_cmp[3] = beidou_dnav_iono.d_alpha3; nav_data.ion_cmp[4] = beidou_dnav_iono.d_beta0; nav_data.ion_cmp[5] = beidou_dnav_iono.d_beta0; nav_data.ion_cmp[6] = beidou_dnav_iono.d_beta0; nav_data.ion_cmp[7] = beidou_dnav_iono.d_beta3; } if (gps_utc_model.valid) { nav_data.utc_gps[0] = gps_utc_model.d_A0; nav_data.utc_gps[1] = gps_utc_model.d_A1; nav_data.utc_gps[2] = gps_utc_model.d_t_OT; nav_data.utc_gps[3] = gps_utc_model.i_WN_T; nav_data.leaps = gps_utc_model.d_DeltaT_LS; } if (!(gps_utc_model.valid) and gps_cnav_utc_model.valid) { nav_data.utc_gps[0] = gps_cnav_utc_model.d_A0; nav_data.utc_gps[1] = gps_cnav_utc_model.d_A1; nav_data.utc_gps[2] = gps_cnav_utc_model.d_t_OT; nav_data.utc_gps[3] = gps_cnav_utc_model.i_WN_T; nav_data.leaps = gps_cnav_utc_model.d_DeltaT_LS; } if (glonass_gnav_utc_model.valid) { nav_data.utc_glo[0] = glonass_gnav_utc_model.d_tau_c; // ?? nav_data.utc_glo[1] = 0.0; // ?? nav_data.utc_glo[2] = 0.0; // ?? nav_data.utc_glo[3] = 0.0; // ?? } if (galileo_utc_model.A0_6 != 0.0) { nav_data.utc_gal[0] = galileo_utc_model.A0_6; nav_data.utc_gal[1] = galileo_utc_model.A1_6; nav_data.utc_gal[2] = galileo_utc_model.t0t_6; nav_data.utc_gal[3] = galileo_utc_model.WNot_6; nav_data.leaps = galileo_utc_model.Delta_tLS_6; } if (beidou_dnav_utc_model.valid) { nav_data.utc_cmp[0] = beidou_dnav_utc_model.d_A0_UTC; nav_data.utc_cmp[1] = beidou_dnav_utc_model.d_A1_UTC; nav_data.utc_cmp[2] = 0.0; // ?? nav_data.utc_cmp[3] = 0.0; // ?? nav_data.leaps = beidou_dnav_utc_model.d_DeltaT_LS; } /* update carrier wave length using native function call in RTKlib */ for (int i = 0; i < MAXSAT; i++) { for (int j = 0; j < NFREQ; j++) { nav_data.lam[i][j] = satwavelen(i + 1, j, &nav_data); } } result = rtkpos(&rtk_, obs_data.data(), valid_obs + glo_valid_obs, &nav_data); if (result == 0) { LOG(INFO) << "RTKLIB rtkpos error"; DLOG(INFO) << "RTKLIB rtkpos error message: " << rtk_.errbuf; this->set_time_offset_s(0.0); // reset rx time estimation this->set_num_valid_observations(0); } else { this->set_num_valid_observations(rtk_.sol.ns); // record the number of valid satellites used by the PVT solver pvt_sol = rtk_.sol; // DOP computation unsigned int used_sats = 0; for (unsigned int i = 0; i < MAXSAT; i++) { pvt_ssat[i] = rtk_.ssat[i]; if (rtk_.ssat[i].vs == 1) { used_sats++; } } std::vector azel; azel.reserve(used_sats * 2); unsigned int index_aux = 0; for (auto &i : rtk_.ssat) { if (i.vs == 1) { azel[2 * index_aux] = i.azel[0]; azel[2 * index_aux + 1] = i.azel[1]; index_aux++; } } if (index_aux > 0) { dops(index_aux, azel.data(), 0.0, dop_.data()); } this->set_valid_position(true); arma::vec rx_position_and_time(4); rx_position_and_time(0) = pvt_sol.rr[0]; // [m] rx_position_and_time(1) = pvt_sol.rr[1]; // [m] rx_position_and_time(2) = pvt_sol.rr[2]; // [m] //todo: fix this ambiguity in the RTKLIB units in receiver clock offset! if (rtk_.opt.mode == PMODE_SINGLE) { // if the RTKLIB solver is set to SINGLE, the dtr is already expressed in [s] // add also the clock offset from gps to galileo (pvt_sol.dtr[2]) rx_position_and_time(3) = pvt_sol.dtr[0] + pvt_sol.dtr[2]; } else { // the receiver clock offset is expressed in [meters], so we convert it into [s] // add also the clock offset from gps to galileo (pvt_sol.dtr[2]) rx_position_and_time(3) = pvt_sol.dtr[2] + pvt_sol.dtr[0] / GPS_C_M_S; } this->set_rx_pos(rx_position_and_time.rows(0, 2)); // save ECEF position for the next iteration // compute Ground speed and COG double ground_speed_ms = 0.0; std::array pos{}; std::array enuv{}; ecef2pos(pvt_sol.rr, pos.data()); ecef2enu(pos.data(), &pvt_sol.rr[3], enuv.data()); this->set_speed_over_ground(norm_rtk(enuv.data(), 2)); double new_cog; if (ground_speed_ms >= 1.0) { new_cog = atan2(enuv[0], enuv[1]) * R2D; if (new_cog < 0.0) { new_cog += 360.0; } this->set_course_over_ground(new_cog); } this->set_time_offset_s(rx_position_and_time(3)); DLOG(INFO) << "RTKLIB Position at RX TOW = " << gnss_observables_map.begin()->second.RX_time << " in ECEF (X,Y,Z,t[meters]) = " << rx_position_and_time; boost::posix_time::ptime p_time; // gtime_t rtklib_utc_time = gpst2utc(pvt_sol.time); // Corrected RX Time (Non integer multiply of 1 ms of granularity) // Uncorrected RX Time (integer multiply of 1 ms and the same observables time reported in RTCM and RINEX) gtime_t rtklib_time = timeadd(pvt_sol.time, rx_position_and_time(3)); // uncorrected rx time gtime_t rtklib_utc_time = gpst2utc(rtklib_time); p_time = boost::posix_time::from_time_t(rtklib_utc_time.time); p_time += boost::posix_time::microseconds(static_cast(round(rtklib_utc_time.sec * 1e6))); // NOLINT(google-runtime-int) this->set_position_UTC_time(p_time); cart2geo(static_cast(rx_position_and_time(0)), static_cast(rx_position_and_time(1)), static_cast(rx_position_and_time(2)), 4); DLOG(INFO) << "RTKLIB Position at " << boost::posix_time::to_simple_string(p_time) << " is Lat = " << this->get_latitude() << " [deg], Long = " << this->get_longitude() << " [deg], Height= " << this->get_height() << " [m]" << " RX time offset= " << this->get_time_offset_s() << " [s]"; // ######## PVT MONITOR ######### // TOW monitor_pvt.TOW_at_current_symbol_ms = gnss_observables_map.begin()->second.TOW_at_current_symbol_ms; // WEEK monitor_pvt.week = adjgpsweek(nav_data.eph[0].week); // PVT GPS time monitor_pvt.RX_time = gnss_observables_map.begin()->second.RX_time; // User clock offset [s] monitor_pvt.user_clk_offset = rx_position_and_time(3); // ECEF POS X,Y,X [m] + ECEF VEL X,Y,X [m/s] (6 x double) monitor_pvt.pos_x = pvt_sol.rr[0]; monitor_pvt.pos_y = pvt_sol.rr[1]; monitor_pvt.pos_z = pvt_sol.rr[2]; monitor_pvt.vel_x = pvt_sol.rr[3]; monitor_pvt.vel_y = pvt_sol.rr[4]; monitor_pvt.vel_z = pvt_sol.rr[5]; // position variance/covariance (m^2) {c_xx,c_yy,c_zz,c_xy,c_yz,c_zx} (6 x double) monitor_pvt.cov_xx = pvt_sol.qr[0]; monitor_pvt.cov_yy = pvt_sol.qr[1]; monitor_pvt.cov_zz = pvt_sol.qr[2]; monitor_pvt.cov_xy = pvt_sol.qr[3]; monitor_pvt.cov_yz = pvt_sol.qr[4]; monitor_pvt.cov_zx = pvt_sol.qr[5]; // GEO user position Latitude [deg] monitor_pvt.latitude = get_latitude(); // GEO user position Longitude [deg] monitor_pvt.longitude = get_longitude(); // GEO user position Height [m] monitor_pvt.height = get_height(); // NUMBER OF VALID SATS monitor_pvt.valid_sats = pvt_sol.ns; // RTKLIB solution status monitor_pvt.solution_status = pvt_sol.stat; // RTKLIB solution type (0:xyz-ecef,1:enu-baseline) monitor_pvt.solution_type = pvt_sol.type; // AR ratio factor for validation monitor_pvt.AR_ratio_factor = pvt_sol.ratio; // AR ratio threshold for validation monitor_pvt.AR_ratio_threshold = pvt_sol.thres; // GDOP / PDOP/ HDOP/ VDOP monitor_pvt.gdop = dop_[0]; monitor_pvt.pdop = dop_[1]; monitor_pvt.hdop = dop_[2]; monitor_pvt.vdop = dop_[3]; // ######## LOG FILE ######### if (d_flag_dump_enabled == true) { // MULTIPLEXED FILE RECORDING - Record results to file try { double tmp_double; uint32_t tmp_uint32; // TOW tmp_uint32 = gnss_observables_map.begin()->second.TOW_at_current_symbol_ms; d_dump_file.write(reinterpret_cast(&tmp_uint32), sizeof(uint32_t)); // WEEK tmp_uint32 = adjgpsweek(nav_data.eph[0].week); d_dump_file.write(reinterpret_cast(&tmp_uint32), sizeof(uint32_t)); // PVT GPS time tmp_double = gnss_observables_map.begin()->second.RX_time; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // User clock offset [s] tmp_double = rx_position_and_time(3); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // ECEF POS X,Y,X [m] + ECEF VEL X,Y,X [m/s] (6 x double) tmp_double = pvt_sol.rr[0]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.rr[1]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.rr[2]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.rr[3]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.rr[4]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.rr[5]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // position variance/covariance (m^2) {c_xx,c_yy,c_zz,c_xy,c_yz,c_zx} (6 x double) tmp_double = pvt_sol.qr[0]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.qr[1]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.qr[2]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.qr[3]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.qr[4]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); tmp_double = pvt_sol.qr[5]; d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Latitude [deg] tmp_double = get_latitude(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Longitude [deg] tmp_double = get_longitude(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // GEO user position Height [m] tmp_double = get_height(); d_dump_file.write(reinterpret_cast(&tmp_double), sizeof(double)); // NUMBER OF VALID SATS d_dump_file.write(reinterpret_cast(&pvt_sol.ns), sizeof(uint8_t)); // RTKLIB solution status d_dump_file.write(reinterpret_cast(&pvt_sol.stat), sizeof(uint8_t)); // RTKLIB solution type (0:xyz-ecef,1:enu-baseline) d_dump_file.write(reinterpret_cast(&pvt_sol.type), sizeof(uint8_t)); // AR ratio factor for validation d_dump_file.write(reinterpret_cast(&pvt_sol.ratio), sizeof(float)); // AR ratio threshold for validation d_dump_file.write(reinterpret_cast(&pvt_sol.thres), sizeof(float)); // GDOP / PDOP/ HDOP/ VDOP d_dump_file.write(reinterpret_cast(&dop_[0]), sizeof(double)); d_dump_file.write(reinterpret_cast(&dop_[1]), sizeof(double)); d_dump_file.write(reinterpret_cast(&dop_[2]), sizeof(double)); d_dump_file.write(reinterpret_cast(&dop_[3]), sizeof(double)); } catch (const std::ifstream::failure &e) { LOG(WARNING) << "Exception writing RTKLIB dump file " << e.what(); } } } } return is_valid_position(); } src/algorithms/PVT/libs/rtklib_solver.h000066400000000000000000000125341352176506000204640ustar00rootroot00000000000000/*! * \file rtklib_solver.h * \brief PVT solver based on rtklib library functions adapted to the GNSS-SDR * data flow and structures * \authors
    *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
  • 2007-2013, T. Takasu *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017-2019, Javier Arribas * Copyright (C) 2017-2019, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * -------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_SOLVER_H_ #define GNSS_SDR_RTKLIB_SOLVER_H_ #include "beidou_dnav_almanac.h" #include "beidou_dnav_ephemeris.h" #include "beidou_dnav_iono.h" #include "beidou_dnav_utc_model.h" #include "galileo_almanac.h" #include "galileo_ephemeris.h" #include "galileo_iono.h" #include "galileo_utc_model.h" #include "glonass_gnav_almanac.h" #include "glonass_gnav_ephemeris.h" #include "glonass_gnav_utc_model.h" #include "gnss_synchro.h" #include "gps_almanac.h" #include "gps_cnav_ephemeris.h" #include "gps_cnav_iono.h" #include "gps_cnav_utc_model.h" #include "gps_ephemeris.h" #include "gps_iono.h" #include "gps_utc_model.h" #include "monitor_pvt.h" #include "pvt_solution.h" #include "rtklib.h" #include #include #include #include /*! * \brief This class implements a PVT solution based on RTKLIB */ class Rtklib_Solver : public Pvt_Solution { public: Rtklib_Solver(int nchannels, std::string dump_filename, bool flag_dump_to_file, bool flag_dump_to_mat, const rtk_t& rtk); ~Rtklib_Solver(); bool get_PVT(const std::map& gnss_observables_map, bool flag_averaging); sol_t pvt_sol{}; std::array pvt_ssat{}; double get_hdop() const; double get_vdop() const; double get_pdop() const; double get_gdop() const; Monitor_Pvt get_monitor_pvt() const; std::map galileo_ephemeris_map; //!< Map storing new Galileo_Ephemeris std::map gps_ephemeris_map; //!< Map storing new GPS_Ephemeris std::map gps_cnav_ephemeris_map; //!< Map storing new GPS_CNAV_Ephemeris std::map glonass_gnav_ephemeris_map; //!< Map storing new GLONASS GNAV Ephemeris std::map beidou_dnav_ephemeris_map; //!< Map storing new BeiDou DNAV Ephmeris Galileo_Utc_Model galileo_utc_model; Galileo_Iono galileo_iono; std::map galileo_almanac_map; Gps_Utc_Model gps_utc_model; Gps_Iono gps_iono; std::map gps_almanac_map; Gps_CNAV_Iono gps_cnav_iono; Gps_CNAV_Utc_Model gps_cnav_utc_model; Glonass_Gnav_Utc_Model glonass_gnav_utc_model; //!< Map storing GLONASS GNAV UTC Model Glonass_Gnav_Almanac glonass_gnav_almanac; //!< Map storing GLONASS GNAV Almanac Model Beidou_Dnav_Utc_Model beidou_dnav_utc_model; Beidou_Dnav_Iono beidou_dnav_iono; std::map beidou_dnav_almanac_map; private: rtk_t rtk_{}; Monitor_Pvt monitor_pvt{}; std::array obs_data{}; std::array dop_{}; std::string d_dump_filename; std::ofstream d_dump_file; int d_nchannels; // Number of available channels for positioning bool d_flag_dump_enabled; bool d_flag_dump_mat_enabled; bool save_matfile(); }; #endif src/algorithms/PVT/libs/serdes_monitor_pvt.h000066400000000000000000000132211352176506000215220ustar00rootroot00000000000000/*! * \file serdes_monitor_pvt.h * \brief Serialization / Deserialization of Monitor_Pvt objects using * Protocol Buffers * \author Carles Fernandez-Prades, 2019. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_SERDES_MONITOR_PVT_H_ #define GNSS_SDR_SERDES_MONITOR_PVT_H_ #include "monitor_pvt.h" #include "monitor_pvt.pb.h" // file created by Protocol Buffers at compile time #include /*! * \brief This class implements serialization and deserialization of * Monitor_Pvt objects using Protocol Buffers. */ class Serdes_Monitor_Pvt { public: Serdes_Monitor_Pvt() { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; } ~Serdes_Monitor_Pvt() { // google::protobuf::ShutdownProtobufLibrary(); } inline Serdes_Monitor_Pvt(Serdes_Monitor_Pvt&& other) //!< Copy constructor { this->monitor_ = other.monitor_; } inline Serdes_Monitor_Pvt& operator=(const Serdes_Monitor_Pvt& rhs) //!< Copy assignment operator { this->monitor_ = rhs.monitor_; return *this; } inline Serdes_Monitor_Pvt(const Serdes_Monitor_Pvt& other) //!< Move constructor { this->monitor_ = std::move(other.monitor_); } inline Serdes_Monitor_Pvt& operator=(Serdes_Monitor_Pvt&& other) //!< Move assignment operator { if (this != &other) { this->monitor_ = std::move(other.monitor_); } return *this; } inline std::string createProtobuffer(std::shared_ptr monitor) //!< Serialization into a string { monitor_.Clear(); std::string data; monitor_.set_tow_at_current_symbol_ms(monitor->TOW_at_current_symbol_ms); monitor_.set_week(monitor->week); monitor_.set_rx_time(monitor->RX_time); monitor_.set_user_clk_offset(monitor->user_clk_offset); monitor_.set_pos_x(monitor->pos_x); monitor_.set_pos_y(monitor->pos_y); monitor_.set_pos_z(monitor->pos_z); monitor_.set_vel_x(monitor->vel_x); monitor_.set_vel_y(monitor->vel_y); monitor_.set_vel_z(monitor->vel_z); monitor_.set_cov_xx(monitor->cov_xx); monitor_.set_cov_yy(monitor->cov_yy); monitor_.set_cov_zz(monitor->cov_zz); monitor_.set_cov_xy(monitor->cov_xy); monitor_.set_cov_yz(monitor->cov_yz); monitor_.set_cov_zx(monitor->cov_zx); monitor_.set_latitude(monitor->latitude); monitor_.set_longitude(monitor->longitude); monitor_.set_height(monitor->height); monitor_.set_valid_sats(monitor->valid_sats); monitor_.set_solution_status(monitor->solution_status); monitor_.set_solution_type(monitor->solution_type); monitor_.set_ar_ratio_factor(monitor->AR_ratio_factor); monitor_.set_ar_ratio_threshold(monitor->AR_ratio_threshold); monitor_.set_gdop(monitor->gdop); monitor_.set_pdop(monitor->pdop); monitor_.set_hdop(monitor->hdop); monitor_.set_vdop(monitor->vdop); monitor_.SerializeToString(&data); return data; } inline Monitor_Pvt readProtobuffer(const gnss_sdr::MonitorPvt& mon) //!< Deserialization { Monitor_Pvt monitor; monitor.TOW_at_current_symbol_ms = mon.tow_at_current_symbol_ms(); monitor.week = mon.week(); monitor.RX_time = mon.rx_time(); monitor.user_clk_offset = mon.user_clk_offset(); monitor.pos_x = mon.pos_x(); monitor.pos_y = mon.pos_y(); monitor.pos_z = mon.pos_z(); monitor.vel_x = mon.vel_x(); monitor.vel_y = mon.vel_y(); monitor.vel_z = mon.vel_z(); monitor.cov_xx = mon.cov_xx(); monitor.cov_yy = mon.cov_yy(); monitor.cov_zz = mon.cov_zz(); monitor.cov_xy = mon.cov_xy(); monitor.cov_yz = mon.cov_yz(); monitor.cov_zx = mon.cov_zx(); monitor.latitude = mon.latitude(); monitor.longitude = mon.longitude(); monitor.height = mon.height(); monitor.valid_sats = static_cast(mon.valid_sats()); monitor.solution_status = static_cast(mon.solution_status()); monitor.solution_type = static_cast(mon.solution_type()); monitor.AR_ratio_factor = mon.ar_ratio_factor(); monitor.AR_ratio_threshold = mon.ar_ratio_threshold(); monitor.gdop = mon.gdop(); monitor.pdop = mon.pdop(); monitor.hdop = mon.hdop(); monitor.vdop = mon.vdop(); return monitor; } private: gnss_sdr::MonitorPvt monitor_{}; }; #endif // GNSS_SDR_SERDES_MONITOR_PVT_H_ src/algorithms/acquisition/000077500000000000000000000000001352176506000163535ustar00rootroot00000000000000src/algorithms/acquisition/CMakeLists.txt000066400000000000000000000014631352176506000211170ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) add_subdirectory(gnuradio_blocks) add_subdirectory(libs) src/algorithms/acquisition/adapters/000077500000000000000000000000001352176506000201565ustar00rootroot00000000000000src/algorithms/acquisition/adapters/CMakeLists.txt000066400000000000000000000100571352176506000227210ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(ACQ_ADAPTER_SOURCES gps_l1_ca_pcps_acquisition.cc gps_l1_ca_pcps_assisted_acquisition.cc gps_l1_ca_pcps_acquisition_fine_doppler.cc gps_l1_ca_pcps_tong_acquisition.cc gps_l1_ca_pcps_quicksync_acquisition.cc gps_l2_m_pcps_acquisition.cc gps_l5i_pcps_acquisition.cc galileo_e1_pcps_ambiguous_acquisition.cc galileo_e1_pcps_cccwsr_ambiguous_acquisition.cc galileo_e1_pcps_quicksync_ambiguous_acquisition.cc galileo_e1_pcps_tong_ambiguous_acquisition.cc galileo_e1_pcps_8ms_ambiguous_acquisition.cc galileo_e5a_noncoherent_iq_acquisition_caf.cc galileo_e5a_pcps_acquisition.cc glonass_l1_ca_pcps_acquisition.cc glonass_l2_ca_pcps_acquisition.cc beidou_b1i_pcps_acquisition.cc beidou_b3i_pcps_acquisition.cc ) set(ACQ_ADAPTER_HEADERS gps_l1_ca_pcps_acquisition.h gps_l1_ca_pcps_assisted_acquisition.h gps_l1_ca_pcps_acquisition_fine_doppler.h gps_l1_ca_pcps_tong_acquisition.h gps_l1_ca_pcps_quicksync_acquisition.h gps_l2_m_pcps_acquisition.h gps_l5i_pcps_acquisition.h galileo_e1_pcps_ambiguous_acquisition.h galileo_e1_pcps_cccwsr_ambiguous_acquisition.h galileo_e1_pcps_quicksync_ambiguous_acquisition.h galileo_e1_pcps_tong_ambiguous_acquisition.h galileo_e1_pcps_8ms_ambiguous_acquisition.h galileo_e5a_noncoherent_iq_acquisition_caf.h galileo_e5a_pcps_acquisition.h glonass_l1_ca_pcps_acquisition.h glonass_l2_ca_pcps_acquisition.h beidou_b1i_pcps_acquisition.h beidou_b3i_pcps_acquisition.h ) if(ENABLE_FPGA) set(ACQ_ADAPTER_SOURCES ${ACQ_ADAPTER_SOURCES} gps_l1_ca_pcps_acquisition_fpga.cc gps_l2_m_pcps_acquisition_fpga.cc galileo_e1_pcps_ambiguous_acquisition_fpga.cc galileo_e5a_pcps_acquisition_fpga.cc gps_l5i_pcps_acquisition_fpga.cc ) set(ACQ_ADAPTER_HEADERS ${ACQ_ADAPTER_HEADERS} gps_l1_ca_pcps_acquisition_fpga.h gps_l2_m_pcps_acquisition_fpga.h galileo_e1_pcps_ambiguous_acquisition_fpga.h galileo_e5a_pcps_acquisition_fpga.h gps_l5i_pcps_acquisition_fpga.h ) endif() if(ENABLE_OPENCL) set(ACQ_ADAPTER_SOURCES ${ACQ_ADAPTER_SOURCES} gps_l1_ca_pcps_opencl_acquisition.cc ) set(ACQ_ADAPTER_HEADERS ${ACQ_ADAPTER_HEADERS} gps_l1_ca_pcps_opencl_acquisition.h ) endif() list(SORT ACQ_ADAPTER_HEADERS) list(SORT ACQ_ADAPTER_SOURCES) source_group(Headers FILES ${ACQ_ADAPTER_HEADERS}) add_library(acquisition_adapters ${ACQ_ADAPTER_SOURCES} ${ACQ_ADAPTER_HEADERS}) target_link_libraries(acquisition_adapters PUBLIC algorithms_libs gnss_sdr_flags acquisition_gr_blocks acquisition_libs channel_libs core_system_parameters Gnuradio::blocks Volk::volk PRIVATE Boost::boost Gflags::gflags Glog::glog Gnuradio::fft Volkgnsssdr::volkgnsssdr ) target_include_directories(acquisition_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/core/interfaces ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(acquisition_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET acquisition_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/acquisition/adapters/beidou_b1i_pcps_acquisition.cc000066400000000000000000000243141352176506000261300ustar00rootroot00000000000000/*! * \file beidou_b1i_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * BeiDou B1I signals * \authors
    *
  • Sergi Segura, 2018. sergi.segura.munoz(at)gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beidou_b1i_pcps_acquisition.h" #include "Beidou_B1I.h" #include "acq_conf.h" #include "beidou_b1i_signal_processing.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include #include #include #include BeidouB1iPcpsAcquisition::BeidouB1iPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; LOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.ms_per_code = 1; acq_parameters_.it_size = item_size_; num_codes_ = acq_parameters_.sampled_ms; acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (BEIDOU_B1I_CODE_RATE_HZ / BEIDOU_B1I_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / BEIDOU_B1I_CODE_RATE_HZ) * static_cast(acq_parameters_.fs_in))); acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(BEIDOU_B1I_CODE_PERIOD * 1000.0); vector_length_ = std::floor(acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms) * (acq_parameters_.bit_transition_flag ? 2 : 1); code_ = std::vector>(vector_length_); acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void BeidouB1iPcpsAcquisition::stop_acquisition() { } void BeidouB1iPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + ".pfa", 0.0); if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void BeidouB1iPcpsAcquisition::set_doppler_max(uint32_t doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void BeidouB1iPcpsAcquisition::set_doppler_step(uint32_t doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void BeidouB1iPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int BeidouB1iPcpsAcquisition::mag() { return acquisition_->mag(); } void BeidouB1iPcpsAcquisition::init() { acquisition_->init(); set_local_code(); } void BeidouB1iPcpsAcquisition::set_local_code() { std::vector> code(code_length_); beidou_b1i_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < num_codes_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void BeidouB1iPcpsAcquisition::reset() { acquisition_->set_active(true); } void BeidouB1iPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float BeidouB1iPcpsAcquisition::calculate_threshold(float pfa) { //Calculate the threshold uint32_t frequency_bins = 0; frequency_bins = (2 * doppler_max_ + doppler_step_) / doppler_step_; DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; uint32_t ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = static_cast(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void BeidouB1iPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void BeidouB1iPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr BeidouB1iPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } } gr::basic_block_sptr BeidouB1iPcpsAcquisition::get_right_block() { return acquisition_; } void BeidouB1iPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/beidou_b1i_pcps_acquisition.h000066400000000000000000000126741352176506000260000ustar00rootroot00000000000000/*! * \file beidou_b1i_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Beidou B1I signals * \authors
    *
  • Sergi Segura, 2018. sergi.segura.munoz(at)gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEIDOU_B1I_PCPS_ACQUISITION_H_ #define GNSS_SDR_BEIDOU_B1I_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals */ class BeidouB1iPcpsAcquisition : public AcquisitionInterface { public: BeidouB1iPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~BeidouB1iPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "BEIDOU_B1I_PCPS_Acquisition" */ inline std::string implementation() override { return "BEIDOU_B1I_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(uint32_t doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(uint32_t doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int num_codes_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_BEIDOU_B1I_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/beidou_b3i_pcps_acquisition.cc000066400000000000000000000244311352176506000261320ustar00rootroot00000000000000/*! * \file beidou_b3i_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * BeiDou B3I signals * \author Damian Miralles, 2019. dmiralles2009@gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beidou_b3i_pcps_acquisition.h" #include "Beidou_B3I.h" #include "acq_conf.h" #include "beidou_b3i_signal_processing.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include #include #include using google::LogMessage; BeidouB3iPcpsAcquisition::BeidouB3iPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; LOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.ms_per_code = 1; acq_parameters_.it_size = item_size_; num_codes_ = acq_parameters_.sampled_ms; acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (BEIDOU_B3I_CODE_RATE_HZ / BEIDOU_B3I_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / BEIDOU_B3I_CODE_RATE_HZ) * static_cast(acq_parameters_.fs_in))); acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(BEIDOU_B3I_CODE_PERIOD * 1000.0); vector_length_ = std::floor(acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms) * (acq_parameters_.bit_transition_flag ? 2 : 1); code_ = std::vector>(vector_length_); acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void BeidouB3iPcpsAcquisition::stop_acquisition() { } void BeidouB3iPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void BeidouB3iPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void BeidouB3iPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void BeidouB3iPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int BeidouB3iPcpsAcquisition::mag() { return acquisition_->mag(); } void BeidouB3iPcpsAcquisition::init() { acquisition_->init(); } void BeidouB3iPcpsAcquisition::set_local_code() { std::vector> code(code_length_); beidou_b3i_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < num_codes_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void BeidouB3iPcpsAcquisition::reset() { acquisition_->set_active(true); } void BeidouB3iPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float BeidouB3iPcpsAcquisition::calculate_threshold(float pfa) { //Calculate the threshold unsigned int frequency_bins = 0; frequency_bins = (2 * doppler_max_ + doppler_step_) / doppler_step_; DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1.0 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void BeidouB3iPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void BeidouB3iPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr BeidouB3iPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } } gr::basic_block_sptr BeidouB3iPcpsAcquisition::get_right_block() { return acquisition_; } void BeidouB3iPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/beidou_b3i_pcps_acquisition.h000066400000000000000000000126551352176506000260010ustar00rootroot00000000000000/*! * \file beidou_b3i_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Beidou B3I signals * \author Damian Miralles, 2019. dmiralles2009@gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEIDOU_B3I_PCPS_ACQUISITION_H_ #define GNSS_SDR_BEIDOU_B3I_PCPS_ACQUISITION_H_ #include "acq_conf.h" #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for BeiDou B3I signals */ class BeidouB3iPcpsAcquisition : public AcquisitionInterface { public: BeidouB3iPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~BeidouB3iPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "BEIDOU_B1I_PCPS_Acquisition" */ inline std::string implementation() override { return "BEIDOU_B3I_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int num_codes_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_BEIDOU_B3I_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_8ms_ambiguous_acquisition.cc000066400000000000000000000211741352176506000307720ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_8ms_ambiguous_acquisition.cc * \brief Adapts a Galileo PCPS 8ms acquisition block to an * AcquisitionInterface for Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_8ms_ambiguous_acquisition.h" #include "Galileo_E1.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GalileoE1Pcps8msAmbiguousAcquisition::GalileoE1Pcps8msAmbiguousAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 4); if (sampled_ms_ % 4 != 0) { sampled_ms_ = static_cast(sampled_ms_ / 4) * 4; LOG(WARNING) << "coherent_integration_time should be multiple of " << "Galileo code length (4 ms). coherent_integration_time = " << sampled_ms_ << " ms will be used."; } max_dwells_ = configuration_->property(role + ".max_dwells", 1); dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code (4 ms) ----------------- code_length_ = round( fs_in_ / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * static_cast(sampled_ms_ / 4); int samples_per_ms = code_length_ / 4; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = galileo_pcps_8ms_make_acquisition_cc(sampled_ms_, max_dwells_, doppler_max_, fs_in_, samples_per_ms, code_length_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE1Pcps8msAmbiguousAcquisition::stop_acquisition() { } void GalileoE1Pcps8msAmbiguousAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GalileoE1Pcps8msAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GalileoE1Pcps8msAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GalileoE1Pcps8msAmbiguousAcquisition::set_gnss_synchro( Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GalileoE1Pcps8msAmbiguousAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GalileoE1Pcps8msAmbiguousAcquisition::init() { acquisition_cc_->init(); } void GalileoE1Pcps8msAmbiguousAcquisition::set_local_code() { if (item_type_ == "gr_complex") { bool cboc = configuration_->property( "Acquisition" + std::to_string(channel_) + ".cboc", false); std::vector> code(code_length_); std::array Signal_{}; Signal_[0] = gnss_synchro_->Signal[0]; Signal_[1] = gnss_synchro_->Signal[1]; Signal_[2] = '\0'; galileo_e1_code_gen_complex_sampled(code, Signal_, cboc, gnss_synchro_->PRN, fs_in_, 0, false); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_ / 4; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GalileoE1Pcps8msAmbiguousAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } float GalileoE1Pcps8msAmbiguousAcquisition::calculate_threshold(float pfa) { unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE1Pcps8msAmbiguousAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GalileoE1Pcps8msAmbiguousAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GalileoE1Pcps8msAmbiguousAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GalileoE1Pcps8msAmbiguousAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/galileo_e1_pcps_8ms_ambiguous_acquisition.h000066400000000000000000000121541352176506000306320ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_8ms_ambiguous_acquisition.h * \brief Adapts a PCPS 8ms acquisition block to an * AcquisitionInterface for Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_8MS_AMBIGUOUS_ACQUISITION_H_ #define GNSS_SDR_GALILEO_E1_PCPS_8MS_AMBIGUOUS_ACQUISITION_H_ #include "acquisition_interface.h" #include "galileo_pcps_8ms_acquisition_cc.h" #include "gnss_synchro.h" #include #include #include #include class ConfigurationInterface; /*! * \brief Adapts a PCPS 8ms acquisition block to an * AcquisitionInterface for Galileo E1 Signals */ class GalileoE1Pcps8msAmbiguousAcquisition : public AcquisitionInterface { public: GalileoE1Pcps8msAmbiguousAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE1Pcps8msAmbiguousAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_8ms_Ambiguous_Acquisition" */ inline std::string implementation() override { return "Galileo_E1_PCPS_8ms_Ambiguous_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for Galileo E1 PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_state(int state __attribute__((unused))) override{}; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; galileo_pcps_8ms_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_8MS_AMBIGUOUS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_ambiguous_acquisition.cc000066400000000000000000000337251352176506000302100ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_ambiguous_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E1 Signals * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_ambiguous_acquisition.h" #include "Galileo_E1.h" #include "acq_conf.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GalileoE1PcpsAmbiguousAcquisition::GalileoE1PcpsAmbiguousAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; acq_parameters_.ms_per_code = 4; sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", acq_parameters_.ms_per_code); acq_parameters_.sampled_ms = sampled_ms_; if ((acq_parameters_.sampled_ms % acq_parameters_.ms_per_code) != 0) { LOG(WARNING) << "Parameter coherent_integration_time_ms should be a multiple of 4. Setting it to 4"; acq_parameters_.sampled_ms = acq_parameters_.ms_per_code; } bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; acquire_pilot_ = configuration_->property(role + ".acquire_pilot", false); //will be true in future versions max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); if (acq_parameters_.use_automatic_resampler == true and item_type_ != "gr_complex") { LOG(WARNING) << "Galileo E1 acqisition disabled the automatic resampler feature because its item_type is not set to gr_complex"; acq_parameters_.use_automatic_resampler = false; } if (acq_parameters_.use_automatic_resampler) { if (acq_parameters_.fs_in > GALILEO_E1_OPT_ACQ_FS_HZ) { acq_parameters_.resampler_ratio = floor(static_cast(acq_parameters_.fs_in) / GALILEO_E1_OPT_ACQ_FS_HZ); uint32_t decimation = acq_parameters_.fs_in / GALILEO_E1_OPT_ACQ_FS_HZ; while (acq_parameters_.fs_in % decimation > 0) { decimation--; }; acq_parameters_.resampler_ratio = decimation; acq_parameters_.resampled_fs = acq_parameters_.fs_in / static_cast(acq_parameters_.resampler_ratio); } //--- Find number of samples per spreading code (4 ms) ----------------- code_length_ = static_cast(std::floor(static_cast(acq_parameters_.resampled_fs) / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(acq_parameters_.resampled_fs) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GALILEO_E1_CODE_CHIP_RATE_HZ) * static_cast(acq_parameters_.resampled_fs))); } else { //--- Find number of samples per spreading code (4 ms) ----------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GALILEO_E1_CODE_CHIP_RATE_HZ) * static_cast(acq_parameters_.fs_in))); } acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(GALILEO_E1_CODE_PERIOD_MS); vector_length_ = sampled_ms_ * acq_parameters_.samples_per_ms; if (bit_transition_flag_) { vector_length_ *= 2; } code_ = std::vector>(vector_length_); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.it_size = item_size_; acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; doppler_center_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE1PcpsAmbiguousAcquisition::stop_acquisition() { } void GalileoE1PcpsAmbiguousAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GalileoE1PcpsAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void GalileoE1PcpsAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GalileoE1PcpsAmbiguousAcquisition::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_->set_doppler_center(doppler_center_); } void GalileoE1PcpsAmbiguousAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GalileoE1PcpsAmbiguousAcquisition::mag() { return acquisition_->mag(); } void GalileoE1PcpsAmbiguousAcquisition::init() { acquisition_->init(); } void GalileoE1PcpsAmbiguousAcquisition::set_local_code() { bool cboc = configuration_->property( "Acquisition" + std::to_string(channel_) + ".cboc", false); std::vector> code(code_length_); if (acquire_pilot_ == true) { // set local signal generator to Galileo E1 pilot component (1C) std::array pilot_signal = {{'1', 'C', '\0'}}; if (acq_parameters_.use_automatic_resampler) { galileo_e1_code_gen_complex_sampled(code, pilot_signal, cboc, gnss_synchro_->PRN, acq_parameters_.resampled_fs, 0, false); } else { galileo_e1_code_gen_complex_sampled(code, pilot_signal, cboc, gnss_synchro_->PRN, fs_in_, 0, false); } } else { std::array Signal_{}; Signal_[0] = gnss_synchro_->Signal[0]; Signal_[1] = gnss_synchro_->Signal[1]; Signal_[2] = '\0'; if (acq_parameters_.use_automatic_resampler) { galileo_e1_code_gen_complex_sampled(code, Signal_, cboc, gnss_synchro_->PRN, acq_parameters_.resampled_fs, 0, false); } else { galileo_e1_code_gen_complex_sampled(code, Signal_, cboc, gnss_synchro_->PRN, fs_in_, 0, false); } } gsl::span code__span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_ / 4; i++) { std::copy_n(code.data(), code_length_, code__span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GalileoE1PcpsAmbiguousAcquisition::reset() { acquisition_->set_active(true); } void GalileoE1PcpsAmbiguousAcquisition::set_state(int state) { acquisition_->set_state(state); } float GalileoE1PcpsAmbiguousAcquisition::calculate_threshold(float pfa) { unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE1PcpsAmbiguousAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GalileoE1PcpsAmbiguousAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GalileoE1PcpsAmbiguousAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GalileoE1PcpsAmbiguousAcquisition::get_right_block() { return acquisition_; } void GalileoE1PcpsAmbiguousAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/galileo_e1_pcps_ambiguous_acquisition.h000066400000000000000000000131721352176506000300440ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_ambiguous_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E1 Signals * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_ #define GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_ #include "acq_conf.h" #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an * AcquisitionInterface for Galileo E1 Signals */ class GalileoE1PcpsAmbiguousAcquisition : public AcquisitionInterface { public: GalileoE1PcpsAmbiguousAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE1PcpsAmbiguousAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_Ambiguous_Acquisition" */ inline std::string implementation() override { return "Galileo_E1_PCPS_Ambiguous_Acquisition"; } size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for Galileo E1 PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; Acq_Conf acq_parameters_; pcps_acquisition_sptr acquisition_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; bool acquire_pilot_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; int doppler_center_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_ambiguous_acquisition_fpga.cc000066400000000000000000000260051352176506000311760ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_ambiguous_acquisition_fpga.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E1 Signals for the FPGA * \author Marc Majoral, 2019. mmajoral(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_ambiguous_acquisition_fpga.h" #include "Galileo_E1.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include // for fft_complex #include // for gr_complex #include // for volk_32fc_conjugate_32fc #include #include // for copy_n #include // for abs, pow, floor #include // for complex GalileoE1PcpsAmbiguousAcquisitionFpga::GalileoE1PcpsAmbiguousAcquisitionFpga( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { pcpsconf_fpga_t acq_parameters; configuration_ = configuration; std::string default_item_type = "cshort"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); int64_t fs_in = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; uint32_t downsampling_factor = configuration_->property(role + ".downsampling_factor", 4); acq_parameters.downsampling_factor = downsampling_factor; fs_in = fs_in / downsampling_factor; acq_parameters.fs_in = fs_in; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) doppler_max_ = FLAGS_doppler_max; acq_parameters.doppler_max = doppler_max_; uint32_t sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 4); acq_parameters.sampled_ms = sampled_ms; acquire_pilot_ = configuration_->property(role + ".acquire_pilot", false); // could be true in future versions // Find number of samples per spreading code (4 ms) auto code_length = static_cast(std::round(static_cast(fs_in) / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS))); acq_parameters.code_length = code_length; // The FPGA can only use FFT lengths that are a power of two. float nbits = ceilf(log2f((float)code_length * 2)); uint32_t nsamples_total = pow(2, nbits); uint32_t select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 0); acq_parameters.select_queue_Fpga = select_queue_Fpga; std::string default_device_name = "/dev/uio0"; std::string device_name = configuration_->property(role + ".devicename", default_device_name); acq_parameters.device_name = device_name; acq_parameters.samples_per_ms = nsamples_total / sampled_ms; acq_parameters.samples_per_code = nsamples_total; acq_parameters.excludelimit = static_cast(1 + ceil((1.0 / GALILEO_E1_CODE_CHIP_RATE_HZ) * static_cast(fs_in))); // compute all the GALILEO E1 PRN Codes (this is done only once in the class constructor in order to avoid re-computing the PRN codes every time // a channel is assigned) auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // Direct FFT std::vector> code(nsamples_total); // buffer for the local code auto* fft_codes_padded = static_cast(volk_gnsssdr_malloc(nsamples_total * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_all_fft_codes_ = std::vector(nsamples_total * GALILEO_E1_NUMBER_OF_CODES); // memory containing all the possible fft codes for PRN 0 to 32 float max; // temporary maxima search int32_t tmp, tmp2, local_code, fft_data; for (uint32_t PRN = 1; PRN <= GALILEO_E1_NUMBER_OF_CODES; PRN++) { bool cboc = false; // cboc is set to 0 when using the FPGA if (acquire_pilot_ == true) { // set local signal generator to Galileo E1 pilot component (1C) std::array pilot_signal = {{'1', 'C', '\0'}}; galileo_e1_code_gen_complex_sampled(code, pilot_signal, cboc, PRN, fs_in, 0, false); } else { std::array data_signal = {{'1', 'B', '\0'}}; galileo_e1_code_gen_complex_sampled(code, data_signal, cboc, PRN, fs_in, 0, false); } for (uint32_t s = code_length; s < 2 * code_length; s++) { code[s] = code[s - code_length]; } // fill in zero padding for (uint32_t s = 2 * code_length; s < nsamples_total; s++) { code[s] = std::complex(0.0, 0.0); } std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer fft_if->execute(); // Run the FFT of local code volk_32fc_conjugate_32fc(fft_codes_padded, fft_if->get_outbuf(), nsamples_total); // conjugate values // normalize the code max = 0; // initialize maximum value for (uint32_t i = 0; i < nsamples_total; i++) // search for maxima { if (std::abs(fft_codes_padded[i].real()) > max) { max = std::abs(fft_codes_padded[i].real()); } if (std::abs(fft_codes_padded[i].imag()) > max) { max = std::abs(fft_codes_padded[i].imag()); } } // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs // and package codes in a format that is ready to be written to the FPGA for (uint32_t i = 0; i < nsamples_total; i++) { tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, quant_bits_local_code - 1) - 1) / max)); tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, quant_bits_local_code - 1) - 1) / max)); local_code = (tmp & select_lsbits) | ((tmp2 * shl_code_bits) & select_msbits); // put together the real part and the imaginary part fft_data = local_code & select_all_code_bits; d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; } } acq_parameters.all_fft_codes = d_all_fft_codes_.data(); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.max_num_acqs = configuration_->property(role + ".max_num_acqs", 2); // reference for the FPGA FFT-IFFT attenuation factor acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 13); acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); channel_ = 0; doppler_step_ = 0; gnss_synchro_ = nullptr; // temporary buffers that we can release volk_gnsssdr_free(fft_codes_padded); } void GalileoE1PcpsAmbiguousAcquisitionFpga::stop_acquisition() { // this command causes the SW to reset the HW. acquisition_fpga_->reset_acquisition(); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_threshold(float threshold) { DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold; acquisition_fpga_->set_threshold(threshold); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_fpga_->set_doppler_max(doppler_max_); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_fpga_->set_doppler_step(doppler_step_); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_fpga_->set_doppler_center(doppler_center_); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_fpga_->set_gnss_synchro(gnss_synchro_); } signed int GalileoE1PcpsAmbiguousAcquisitionFpga::mag() { return acquisition_fpga_->mag(); } void GalileoE1PcpsAmbiguousAcquisitionFpga::init() { acquisition_fpga_->init(); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_local_code() { acquisition_fpga_->set_local_code(); } void GalileoE1PcpsAmbiguousAcquisitionFpga::reset() { // This command starts the acquisition process acquisition_fpga_->set_active(true); } void GalileoE1PcpsAmbiguousAcquisitionFpga::set_state(int state) { acquisition_fpga_->set_state(state); } void GalileoE1PcpsAmbiguousAcquisitionFpga::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect } void GalileoE1PcpsAmbiguousAcquisitionFpga::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr GalileoE1PcpsAmbiguousAcquisitionFpga::get_left_block() { return nullptr; } gr::basic_block_sptr GalileoE1PcpsAmbiguousAcquisitionFpga::get_right_block() { return nullptr; } src/algorithms/acquisition/adapters/galileo_e1_pcps_ambiguous_acquisition_fpga.h000066400000000000000000000141771352176506000310470ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_ambiguous_acquisition_fpga.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E1 Signals for the FPGA * \author Marc Majoral, 2019. mmajoral(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_FPGA_H_ #define GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_FPGA_H_ #include "acq_conf.h" #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition_fpga.h" #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA * to an AcquisitionInterface for Galileo E1 Signals */ class GalileoE1PcpsAmbiguousAcquisitionFpga : public AcquisitionInterface { public: /*! * \brief Constructor */ GalileoE1PcpsAmbiguousAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); /*! * \brief Destructor */ ~GalileoE1PcpsAmbiguousAcquisitionFpga() = default; /*! * \brief Role */ inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_Ambiguous_Acquisition_Fpga" */ inline std::string implementation() override { return "Galileo_E1_PCPS_Ambiguous_Acquisition_Fpga"; } /*! * \brief Returns size of lv_16sc_t */ size_t item_size() override { return sizeof(int16_t); } /*! * \brief Connect */ void connect(gr::top_block_sptr top_block) override; /*! * \brief Disconnect */ void disconnect(gr::top_block_sptr top_block) override; /*! * \brief Get left block */ gr::basic_block_sptr get_left_block() override; /*! * \brief Get right block */ gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_fpga_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_fpga_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for Galileo E1 PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Set resampler latency */ void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: // the following flags are FPGA-specific and they are using arrange the values of the fft of the local code in the way the FPGA // expects. This arrangement is done in the initialisation to avoid consuming unnecessary clock cycles during tracking. static const uint32_t quant_bits_local_code = 16; static const uint32_t select_lsbits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t select_msbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t select_all_code_bits = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t shl_code_bits = 65536; // shift left by 10 bits ConfigurationInterface* configuration_; pcps_acquisition_fpga_sptr acquisition_fpga_; bool acquire_pilot_; uint32_t channel_; std::weak_ptr channel_fsm_; uint32_t doppler_max_; uint32_t doppler_step_; int32_t doppler_center_; std::string dump_filename_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; std::vector d_all_fft_codes_; // memory that contains all the code ffts }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_FPGA_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_cccwsr_ambiguous_acquisition.cc000066400000000000000000000173351352176506000315530ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_cccwsr_ambiguous_acquisition.cc * \brief Adapts a PCPS CCCWSR acquisition block to an AcquisitionInterface for * Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_cccwsr_ambiguous_acquisition.h" #include "Galileo_E1.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include GalileoE1PcpsCccwsrAmbiguousAcquisition::GalileoE1PcpsCccwsrAmbiguousAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 4); if (sampled_ms_ % 4 != 0) { sampled_ms_ = static_cast(sampled_ms_ / 4) * 4; LOG(WARNING) << "coherent_integration_time should be multiple of " << "Galileo code length (4 ms). coherent_integration_time = " << sampled_ms_ << " ms will be used."; } max_dwells_ = configuration_->property(role + ".max_dwells", 1); dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code (4 ms) ----------------- code_length_ = round( fs_in_ / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * static_cast(sampled_ms_ / 4); int samples_per_ms = code_length_ / 4; code_data_ = std::vector>(vector_length_); code_pilot_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_cccwsr_make_acquisition_cc(sampled_ms_, max_dwells_, doppler_max_, fs_in_, samples_per_ms, code_length_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::stop_acquisition() { } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_threshold(float threshold) { threshold_ = threshold; DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_gnss_synchro( Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GalileoE1PcpsCccwsrAmbiguousAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GalileoE1PcpsCccwsrAmbiguousAcquisition::init() { acquisition_cc_->init(); } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_local_code() { if (item_type_ == "gr_complex") { bool cboc = configuration_->property( "Acquisition" + std::to_string(channel_) + ".cboc", false); std::array signal = {{'1', 'B', '\0'}}; galileo_e1_code_gen_complex_sampled(code_data_, signal, cboc, gnss_synchro_->PRN, fs_in_, 0, false); std::array signal_C = {{'1', 'C', '\0'}}; galileo_e1_code_gen_complex_sampled(code_pilot_, signal_C, cboc, gnss_synchro_->PRN, fs_in_, 0, false); acquisition_cc_->set_local_code(code_data_.data(), code_pilot_.data()); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::set_state(int state) { acquisition_cc_->set_state(state); } float GalileoE1PcpsCccwsrAmbiguousAcquisition::calculate_threshold(float pfa) { if (pfa) { /* Not implemented*/ }; return 0.0; } void GalileoE1PcpsCccwsrAmbiguousAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GalileoE1PcpsCccwsrAmbiguousAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GalileoE1PcpsCccwsrAmbiguousAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GalileoE1PcpsCccwsrAmbiguousAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/galileo_e1_pcps_cccwsr_ambiguous_acquisition.h000066400000000000000000000122461352176506000314110ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_cccwsr_ambiguous_acquisition.h * \brief Adapts a PCPS CCCWSR acquisition block to an AcquisitionInterface for * Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_CCCWSR_AMBIGUOUS_ACQUISITION_H_ #define GNSS_SDR_GALILEO_E1_PCPS_CCCWSR_AMBIGUOUS_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_cccwsr_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief Adapts a PCPS CCCWSR acquisition block to an AcquisitionInterface * for Galileo E1 Signals */ class GalileoE1PcpsCccwsrAmbiguousAcquisition : public AcquisitionInterface { public: GalileoE1PcpsCccwsrAmbiguousAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE1PcpsCccwsrAmbiguousAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_CCCWSR_Ambiguous_Acquisition" */ inline std::string implementation() override { return "Galileo_E1_PCPS_CCCWSR_Ambiguous_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of CCCWSR algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_cccwsr_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_data_; std::vector> code_pilot_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_CCCWSR_AMBIGUOUS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_quicksync_ambiguous_acquisition.cc000066400000000000000000000253161352176506000322760ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_quicksync_ambiguous_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E1 Signals using the QuickSync Algorithm * \author Damian Miralles, 2014. dmiralles2009@gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_quicksync_ambiguous_acquisition.h" #include "Galileo_E1.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GalileoE1PcpsQuickSyncAmbiguousAcquisition::GalileoE1PcpsQuickSyncAmbiguousAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 8); /*--- Find number of samples per spreading code (4 ms) -----------------*/ code_length_ = round( fs_in_ / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS)); int samples_per_ms = round(code_length_ / 4.0); /*Calculate the folding factor value based on the formula described in the paper. This may be a bug, but acquisition also work by variying the folding factor at va- lues different that the expressed in the paper. In adition, it is important to point out that by making the folding factor smaller we were able to get QuickSync work with Galileo. Future work should be directed to test this assumption statistically.*/ //folding_factor_ = static_cast(ceil(sqrt(log2(code_length_)))); folding_factor_ = configuration_->property(role + ".folding_factor", 2); if (sampled_ms_ % (folding_factor_ * 4) != 0) { LOG(WARNING) << "QuickSync Algorithm requires a coherent_integration_time" << " multiple of " << (folding_factor_ * 4) << "ms, Value entered " << sampled_ms_ << " ms"; if (sampled_ms_ < (folding_factor_ * 4)) { sampled_ms_ = static_cast(folding_factor_ * 4); } else { sampled_ms_ = static_cast(sampled_ms_ / (folding_factor_ * 4)) * (folding_factor_ * 4); } LOG(WARNING) << "coherent_integration_time should be multiple of " << "Galileo code length (4 ms). coherent_integration_time = " << sampled_ms_ << " ms will be used."; } // vector_length_ = (sampled_ms_/folding_factor_) * code_length_; vector_length_ = sampled_ms_ * samples_per_ms; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); if (!bit_transition_flag_) { max_dwells_ = configuration_->property(role + ".max_dwells", 1); } else { max_dwells_ = 2; } dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); code_ = std::vector>(code_length_); LOG(INFO) << "Vector Length: " << vector_length_ << ", Samples per ms: " << samples_per_ms << ", Folding factor: " << folding_factor_ << ", Sampled ms: " << sampled_ms_ << ", Code Length: " << code_length_; if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_quicksync_make_acquisition_cc(folding_factor_, sampled_ms_, max_dwells_, doppler_max_, fs_in_, samples_per_ms, code_length_, bit_transition_flag_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector_quicksync(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition_quicksync(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::stop_acquisition() { } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_gnss_synchro( Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GalileoE1PcpsQuickSyncAmbiguousAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::init() { acquisition_cc_->init(); } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_local_code() { if (item_type_ == "gr_complex") { bool cboc = configuration_->property( "Acquisition" + std::to_string(channel_) + ".cboc", false); std::vector> code(code_length_); std::array Signal_{}; Signal_[0] = gnss_synchro_->Signal[0]; Signal_[1] = gnss_synchro_->Signal[1]; Signal_[2] = '\0'; galileo_e1_code_gen_complex_sampled(code, Signal_, cboc, gnss_synchro_->PRN, fs_in_, 0, false); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < (sampled_ms_ / (folding_factor_ * 4)); i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::set_state(int state) { if (item_type_ == "gr_complex") { acquisition_cc_->set_state(state); } } float GalileoE1PcpsQuickSyncAmbiguousAcquisition::calculate_threshold(float pfa) { unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = code_length_ / folding_factor_ * frequency_bins; double exponent = 1.0 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); double lambda = static_cast(code_length_) / static_cast(folding_factor_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GalileoE1PcpsQuickSyncAmbiguousAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GalileoE1PcpsQuickSyncAmbiguousAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GalileoE1PcpsQuickSyncAmbiguousAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/galileo_e1_pcps_quicksync_ambiguous_acquisition.h000066400000000000000000000124561352176506000321410ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_quicksync_ambiguous_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for Galileo E1 Signals * \date June, 2014 * \author Damian Miralles Sanchez. dmiralles2009@gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_QUICKSYNC_AMBIGUOUS_ACQUISITION_H_ #define GNSS_SDR_GALILEO_E1_PCPS_QUICKSYNC_AMBIGUOUS_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_quicksync_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an * AcquisitionInterface for Galileo E1 Signals */ class GalileoE1PcpsQuickSyncAmbiguousAcquisition : public AcquisitionInterface { public: GalileoE1PcpsQuickSyncAmbiguousAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE1PcpsQuickSyncAmbiguousAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_Ambiguous_Acquisition" */ inline std::string implementation() override { return "Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for Galileo E1 PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_quicksync_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; unsigned int folding_factor_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_QUICKSYNC_AMBIGUOUS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e1_pcps_tong_ambiguous_acquisition.cc000066400000000000000000000217001352176506000312250ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_tong_ambiguous_acquisition.cc * \brief Adapts a PCPS Tong acquisition block to an Acq1uisitionInterface for * Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_pcps_tong_ambiguous_acquisition.h" #include "Galileo_E1.h" #include "configuration_interface.h" #include "galileo_e1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GalileoE1PcpsTongAmbiguousAcquisition::GalileoE1PcpsTongAmbiguousAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 4); if (sampled_ms_ % 4 != 0) { sampled_ms_ = static_cast(sampled_ms_ / 4) * 4; LOG(WARNING) << "coherent_integration_time should be multiple of " << "Galileo code length (4 ms). coherent_integration_time = " << sampled_ms_ << " ms will be used."; } tong_init_val_ = configuration->property(role + ".tong_init_val", 1); tong_max_val_ = configuration->property(role + ".tong_max_val", 2); tong_max_dwells_ = configuration->property(role + ".tong_max_dwells", tong_max_val_ + 1); dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code (4 ms) ----------------- code_length_ = round( fs_in_ / (GALILEO_E1_CODE_CHIP_RATE_HZ / GALILEO_E1_B_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * static_cast(sampled_ms_ / 4); int samples_per_ms = code_length_ / 4; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_tong_make_acquisition_cc(sampled_ms_, doppler_max_, fs_in_, samples_per_ms, code_length_, tong_init_val_, tong_max_val_, tong_max_dwells_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE1PcpsTongAmbiguousAcquisition::stop_acquisition() { } void GalileoE1PcpsTongAmbiguousAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GalileoE1PcpsTongAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GalileoE1PcpsTongAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GalileoE1PcpsTongAmbiguousAcquisition::set_gnss_synchro( Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GalileoE1PcpsTongAmbiguousAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GalileoE1PcpsTongAmbiguousAcquisition::init() { acquisition_cc_->init(); } void GalileoE1PcpsTongAmbiguousAcquisition::set_local_code() { if (item_type_ == "gr_complex") { bool cboc = configuration_->property( "Acquisition" + std::to_string(channel_) + ".cboc", false); std::vector> code(code_length_); std::array Signal_{}; Signal_[0] = gnss_synchro_->Signal[0]; Signal_[1] = gnss_synchro_->Signal[1]; Signal_[2] = '\0'; galileo_e1_code_gen_complex_sampled(code, Signal_, cboc, gnss_synchro_->PRN, fs_in_, 0, false); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_ / 4; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GalileoE1PcpsTongAmbiguousAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } void GalileoE1PcpsTongAmbiguousAcquisition::set_state(int state) { acquisition_cc_->set_state(state); } float GalileoE1PcpsTongAmbiguousAcquisition::calculate_threshold(float pfa) { unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE1PcpsTongAmbiguousAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GalileoE1PcpsTongAmbiguousAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GalileoE1PcpsTongAmbiguousAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GalileoE1PcpsTongAmbiguousAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/galileo_e1_pcps_tong_ambiguous_acquisition.h000066400000000000000000000123641352176506000310750ustar00rootroot00000000000000/*! * \file galileo_e1_pcps_tong_ambiguous_acquisition.h * \brief Adapts a PCPS Tong acquisition block to an AcquisitionInterface for * Galileo E1 Signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_PCPS_TONG_AMBIGUOUS_ACQUISITION_H_ #define GNSS_SDR_GALILEO_E1_PCPS_TONG_AMBIGUOUS_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_tong_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief Adapts a PCPS Tong acquisition block to an AcquisitionInterface * for Galileo E1 Signals */ class GalileoE1PcpsTongAmbiguousAcquisition : public AcquisitionInterface { public: GalileoE1PcpsTongAmbiguousAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE1PcpsTongAmbiguousAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E1_PCPS_Tong_Ambiguous_Acquisition" */ inline std::string implementation() override { return "Galileo_E1_PCPS_Tong_Ambiguous_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of TONG algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for Galileo E1 TONG acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_tong_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int tong_init_val_; unsigned int tong_max_val_; unsigned int tong_max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GALILEO_E1_PCPS_TONG_AMBIGUOUS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e5a_noncoherent_iq_acquisition_caf.cc000066400000000000000000000255431352176506000311600ustar00rootroot00000000000000/*! * \file galileo_e5a_noncoherent_iq_acquisition_caf.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Marc Sales, 2014. marcsales92(at)gmail.com * \based on work from: *
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e5a_noncoherent_iq_acquisition_caf.h" #include "Galileo_E5a.h" #include "configuration_interface.h" #include "galileo_e5_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GalileoE5aNoncoherentIQAcquisitionCaf::GalileoE5aNoncoherentIQAcquisitionCaf( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 32000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } CAF_window_hz_ = configuration_->property(role + ".CAF_window_hz", 0); Zero_padding = configuration_->property(role + ".Zero_padding", 0); sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); if (sampled_ms_ > 3) { sampled_ms_ = 3; DLOG(INFO) << "Coherent integration time should be 3 ms or less. Changing to 3ms "; std::cout << "Too high coherent integration time. Changing to 3ms" << std::endl; } if (Zero_padding > 0) { sampled_ms_ = 2; DLOG(INFO) << "Zero padding activated. Changing to 1ms code + 1ms zero padding "; std::cout << "Zero padding activated. Changing to 1ms code + 1ms zero padding" << std::endl; } max_dwells_ = configuration_->property(role + ".max_dwells", 1); dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); //--- Find number of samples per spreading code (1ms)------------------------- code_length_ = round(static_cast(fs_in_) / GALILEO_E5A_CODE_CHIP_RATE_HZ * static_cast(GALILEO_E5A_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * sampled_ms_; codeI_ = std::vector>(vector_length_); codeQ_ = std::vector>(vector_length_); both_signal_components = false; std::string sig_ = configuration_->property("Channel.signal", std::string("5X")); if (sig_.at(0) == '5' && sig_.at(1) == 'X') { both_signal_components = true; } if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = galileo_e5a_noncoherentIQ_make_acquisition_caf_cc(sampled_ms_, max_dwells_, doppler_max_, fs_in_, code_length_, code_length_, bit_transition_flag_, dump_, dump_filename_, both_signal_components, CAF_window_hz_, Zero_padding); } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE5aNoncoherentIQAcquisitionCaf::stop_acquisition() { } void GalileoE5aNoncoherentIQAcquisitionCaf::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GalileoE5aNoncoherentIQAcquisitionCaf::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GalileoE5aNoncoherentIQAcquisitionCaf::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GalileoE5aNoncoherentIQAcquisitionCaf::set_gnss_synchro( Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GalileoE5aNoncoherentIQAcquisitionCaf::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GalileoE5aNoncoherentIQAcquisitionCaf::init() { acquisition_cc_->init(); } void GalileoE5aNoncoherentIQAcquisitionCaf::set_local_code() { if (item_type_ == "gr_complex") { std::vector> codeI(code_length_); std::vector> codeQ(code_length_); if (gnss_synchro_->Signal[0] == '5' && gnss_synchro_->Signal[1] == 'X') { std::array a = {{'5', 'I', '\0'}}; galileo_e5_a_code_gen_complex_sampled(codeI, a, gnss_synchro_->PRN, fs_in_, 0); std::array b = {{'5', 'Q', '\0'}}; galileo_e5_a_code_gen_complex_sampled(codeQ, b, gnss_synchro_->PRN, fs_in_, 0); } else { std::array signal_type_ = {{'5', 'X', '\0'}}; galileo_e5_a_code_gen_complex_sampled(codeI, signal_type_, gnss_synchro_->PRN, fs_in_, 0); } // WARNING: 3ms are coherently integrated. Secondary sequence (1,1,1) // is generated, and modulated in the 'block'. gsl::span codeQ_span(codeQ_.data(), vector_length_); gsl::span codeI_span(codeI_.data(), vector_length_); if (Zero_padding == 0) // if no zero_padding { for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(codeI.data(), code_length_, codeI_span.subspan(i * code_length_, code_length_).data()); if (gnss_synchro_->Signal[0] == '5' && gnss_synchro_->Signal[1] == 'X') { std::copy_n(codeQ.data(), code_length_, codeQ_span.subspan(i * code_length_, code_length_).data()); } } } else { // 1ms code + 1ms zero padding std::copy_n(codeI.data(), code_length_, codeI_.data()); if (gnss_synchro_->Signal[0] == '5' && gnss_synchro_->Signal[1] == 'X') { std::copy_n(codeQ.data(), code_length_, codeQ_.data()); } } acquisition_cc_->set_local_code(codeI_.data(), codeQ_.data()); } } void GalileoE5aNoncoherentIQAcquisitionCaf::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } float GalileoE5aNoncoherentIQAcquisitionCaf::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE5aNoncoherentIQAcquisitionCaf::set_state(int state) { acquisition_cc_->set_state(state); } void GalileoE5aNoncoherentIQAcquisitionCaf::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect internally } void GalileoE5aNoncoherentIQAcquisitionCaf::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect internally } gr::basic_block_sptr GalileoE5aNoncoherentIQAcquisitionCaf::get_left_block() { return acquisition_cc_; } gr::basic_block_sptr GalileoE5aNoncoherentIQAcquisitionCaf::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/galileo_e5a_noncoherent_iq_acquisition_caf.h000066400000000000000000000126771352176506000310260ustar00rootroot00000000000000/*! * \file galileo_e5a_noncoherent_iq_acquisition_caf.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Marc Sales, 2014. marcsales92(at)gmail.com * \based on work from: *
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_H_ #define GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_H_ #include "channel_fsm.h" #include "galileo_e5a_noncoherent_iq_acquisition_caf_cc.h" #include "gnss_synchro.h" #include #include #include class ConfigurationInterface; class GalileoE5aNoncoherentIQAcquisitionCaf : public AcquisitionInterface { public: GalileoE5aNoncoherentIQAcquisitionCaf(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE5aNoncoherentIQAcquisitionCaf() = default; inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E5a_Noncoherent_IQ_Acquisition_CAF" */ inline std::string implementation() override { return "Galileo_E5a_Noncoherent_IQ_Acquisition_CAF"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local Galileo E5a code for PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr acquisition_cc_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; int Zero_padding; int CAF_window_hz_; std::vector> codeI_; std::vector> codeQ_; bool both_signal_components; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_H_ */ src/algorithms/acquisition/adapters/galileo_e5a_pcps_acquisition.cc000066400000000000000000000276151352176506000263030ustar00rootroot00000000000000/*! * \file galileo_e5a_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Antonio Ramos, 2018. antonio.ramos(at)cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e5a_pcps_acquisition.h" #include "Galileo_E5a.h" #include "acq_conf.h" #include "configuration_interface.h" #include "galileo_e5_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include #include GalileoE5aPcpsAcquisition::GalileoE5aPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; DLOG(INFO) << "Role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 32000000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; acq_pilot_ = configuration_->property(role + ".acquire_pilot", false); acq_iq_ = configuration_->property(role + ".acquire_iq", false); if (acq_iq_) { acq_pilot_ = false; } dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; sampled_ms_ = 1; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_ = configuration_->property(role + ".use_CFAR_algorithm", false); acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_; blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); if (acq_parameters_.use_automatic_resampler == true and item_type_ != "gr_complex") { LOG(WARNING) << "Galileo E5a acquisition disabled the automatic resampler feature because its item_type is not set to gr_complex"; acq_parameters_.use_automatic_resampler = false; } if (acq_parameters_.use_automatic_resampler) { if (acq_parameters_.fs_in > GALILEO_E5A_OPT_ACQ_FS_HZ) { acq_parameters_.resampler_ratio = floor(static_cast(acq_parameters_.fs_in) / GALILEO_E5A_OPT_ACQ_FS_HZ); uint32_t decimation = acq_parameters_.fs_in / GALILEO_E5A_OPT_ACQ_FS_HZ; while (acq_parameters_.fs_in % decimation > 0) { decimation--; }; acq_parameters_.resampler_ratio = decimation; acq_parameters_.resampled_fs = acq_parameters_.fs_in / static_cast(acq_parameters_.resampler_ratio); } //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(acq_parameters_.resampled_fs) / (GALILEO_E5A_CODE_CHIP_RATE_HZ / GALILEO_E5A_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(acq_parameters_.resampled_fs) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GALILEO_E5A_CODE_CHIP_RATE_HZ) * static_cast(acq_parameters_.resampled_fs))); } else { acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (GALILEO_E5A_CODE_CHIP_RATE_HZ / GALILEO_E5A_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GALILEO_E5A_CODE_CHIP_RATE_HZ) * static_cast(acq_parameters_.fs_in))); } //--- Find number of samples per spreading code (1ms)------------------------- code_length_ = static_cast(std::round(static_cast(fs_in_) / GALILEO_E5A_CODE_CHIP_RATE_HZ * static_cast(GALILEO_E5A_CODE_LENGTH_CHIPS))); vector_length_ = code_length_ * sampled_ms_; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); } else if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } acq_parameters_.it_size = item_size_; acq_parameters_.sampled_ms = sampled_ms_; acq_parameters_.ms_per_code = 1; acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(GALILEO_E5A_CODE_PERIOD_MS); acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acquisition_ = pcps_make_acquisition(acq_parameters_); channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; doppler_center_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GalileoE5aPcpsAcquisition::stop_acquisition() { } void GalileoE5aPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GalileoE5aPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void GalileoE5aPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GalileoE5aPcpsAcquisition::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_->set_doppler_center(doppler_center_); } void GalileoE5aPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GalileoE5aPcpsAcquisition::mag() { return acquisition_->mag(); } void GalileoE5aPcpsAcquisition::init() { acquisition_->init(); } void GalileoE5aPcpsAcquisition::set_local_code() { std::vector> code(code_length_); std::array signal_{}; signal_[0] = '5'; signal_[2] = '\0'; if (acq_iq_) { signal_[1] = 'X'; } else if (acq_pilot_) { signal_[1] = 'Q'; } else { signal_[1] = 'I'; } if (acq_parameters_.use_automatic_resampler) { galileo_e5_a_code_gen_complex_sampled(code, signal_, gnss_synchro_->PRN, acq_parameters_.resampled_fs, 0); } else { galileo_e5_a_code_gen_complex_sampled(code, signal_, gnss_synchro_->PRN, fs_in_, 0); } gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GalileoE5aPcpsAcquisition::reset() { acquisition_->set_active(true); } float GalileoE5aPcpsAcquisition::calculate_threshold(float pfa) { unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GalileoE5aPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } void GalileoE5aPcpsAcquisition::connect(gr::top_block_sptr top_block __attribute__((unused))) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GalileoE5aPcpsAcquisition::disconnect(gr::top_block_sptr top_block __attribute__((unused))) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GalileoE5aPcpsAcquisition::get_left_block() { return acquisition_; } gr::basic_block_sptr GalileoE5aPcpsAcquisition::get_right_block() { return acquisition_; } void GalileoE5aPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/galileo_e5a_pcps_acquisition.h000066400000000000000000000122511352176506000261330ustar00rootroot00000000000000/*! * \file galileo_e5a_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Antonio Ramos, 2018. antonio.ramos(at)cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GALILEO_E5A_PCPS_ACQUISITION_H_ #define GALILEO_E5A_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include class ConfigurationInterface; class GalileoE5aPcpsAcquisition : public AcquisitionInterface { public: GalileoE5aPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GalileoE5aPcpsAcquisition() = default; inline std::string role() override { return role_; } inline std::string implementation() override { return "Galileo_E5a_Pcps_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local Galileo E5a code for PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: float calculate_threshold(float pfa); ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; size_t item_size_; std::string item_type_; std::string dump_filename_; std::string role_; bool bit_transition_flag_; bool dump_; bool acq_pilot_; bool use_CFAR_; bool blocking_; bool acq_iq_; unsigned int vector_length_; unsigned int code_length_; unsigned int channel_; std::weak_ptr channel_fsm_; unsigned int doppler_max_; unsigned int doppler_step_; int doppler_center_; unsigned int sampled_ms_; unsigned int max_dwells_; unsigned int in_streams_; unsigned int out_streams_; int64_t fs_in_; float threshold_; std::vector> code_; Gnss_Synchro* gnss_synchro_; }; #endif /* GALILEO_E5A_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/galileo_e5a_pcps_acquisition_fpga.cc000066400000000000000000000251411352176506000272700ustar00rootroot00000000000000/*! * \file galileo_e5a_pcps_acquisition_fpga.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals for the FPGA * \author Marc Majoral, 2019. mmajoral(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e5a_pcps_acquisition_fpga.h" #include "Galileo_E5a.h" #include "configuration_interface.h" #include "galileo_e5_signal_processing.h" #include "gnss_sdr_flags.h" #include #include // for fft_complex #include // for gr_complex #include // for volk_32fc_conjugate_32fc #include #include // for copy_n #include // for abs, pow, floor #include // for complex GalileoE5aPcpsAcquisitionFpga::GalileoE5aPcpsAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { pcpsconf_fpga_t acq_parameters; configuration_ = configuration; std::string default_dump_filename = "../data/acquisition.dat"; DLOG(INFO) << "Role " << role; int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 32000000); int64_t fs_in = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; uint32_t downsampling_factor = configuration_->property(role + ".downsampling_factor", 1); acq_parameters.downsampling_factor = downsampling_factor; fs_in = fs_in / downsampling_factor; acq_parameters.fs_in = fs_in; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) doppler_max_ = FLAGS_doppler_max; acq_parameters.doppler_max = doppler_max_; uint32_t sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); acq_parameters.sampled_ms = sampled_ms; acq_pilot_ = configuration_->property(role + ".acquire_pilot", false); acq_iq_ = configuration_->property(role + ".acquire_iq", false); if (acq_iq_) { acq_pilot_ = false; } auto code_length = static_cast(std::round(static_cast(fs_in) / GALILEO_E5A_CODE_CHIP_RATE_HZ * static_cast(GALILEO_E5A_CODE_LENGTH_CHIPS))); acq_parameters.code_length = code_length; // The FPGA can only use FFT lengths that are a power of two. float nbits = ceilf(log2f((float)code_length * 2)); uint32_t nsamples_total = pow(2, nbits); uint32_t select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 1); acq_parameters.select_queue_Fpga = select_queue_Fpga; std::string default_device_name = "/dev/uio0"; std::string device_name = configuration_->property(role + ".devicename", default_device_name); acq_parameters.device_name = device_name; acq_parameters.samples_per_ms = nsamples_total / sampled_ms; acq_parameters.samples_per_code = nsamples_total; acq_parameters.excludelimit = static_cast(1 + ceil((1.0 / GALILEO_E5A_CODE_CHIP_RATE_HZ) * static_cast(fs_in))); // compute all the GALILEO E5 PRN Codes (this is done only once in the class constructor in order to avoid re-computing the PRN codes every time // a channel is assigned) auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // Direct FFT std::vector> code(nsamples_total); // buffer for the local code auto* fft_codes_padded = static_cast(volk_gnsssdr_malloc(nsamples_total * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_all_fft_codes_ = std::vector(nsamples_total * GALILEO_E5A_NUMBER_OF_CODES); // memory containing all the possible fft codes for PRN 0 to 32 float max; // temporary maxima search int32_t tmp, tmp2, local_code, fft_data; for (uint32_t PRN = 1; PRN <= GALILEO_E5A_NUMBER_OF_CODES; PRN++) { std::array signal_; signal_[0] = '5'; signal_[2] = '\0'; if (acq_iq_) { signal_[1] = 'X'; } else if (acq_pilot_) { signal_[1] = 'Q'; } else { signal_[1] = 'I'; } galileo_e5_a_code_gen_complex_sampled(code, signal_, PRN, fs_in, 0); for (uint32_t s = code_length; s < 2 * code_length; s++) { code[s] = code[s - code_length]; } // fill in zero padding for (uint32_t s = 2 * code_length; s < nsamples_total; s++) { code[s] = std::complex(0.0, 0.0); } std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer fft_if->execute(); // Run the FFT of local code volk_32fc_conjugate_32fc(fft_codes_padded, fft_if->get_outbuf(), nsamples_total); // conjugate values max = 0; // initialize maximum value for (uint32_t i = 0; i < nsamples_total; i++) // search for maxima { if (std::abs(fft_codes_padded[i].real()) > max) { max = std::abs(fft_codes_padded[i].real()); } if (std::abs(fft_codes_padded[i].imag()) > max) { max = std::abs(fft_codes_padded[i].imag()); } } // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs // and package codes in a format that is ready to be written to the FPGA for (uint32_t i = 0; i < nsamples_total; i++) { tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, quant_bits_local_code - 1) - 1) / max)); tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, quant_bits_local_code - 1) - 1) / max)); local_code = (tmp & select_lsbits) | ((tmp2 * shl_code_bits) & select_msbits); // put together the real part and the imaginary part fft_data = local_code & select_all_code_bits; d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; } } acq_parameters.all_fft_codes = d_all_fft_codes_.data(); // reference for the FPGA FFT-IFFT attenuation factor acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 13); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.max_num_acqs = configuration_->property(role + ".max_num_acqs", 2); acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); channel_ = 0; doppler_step_ = 0; gnss_synchro_ = nullptr; // temporary buffers that we can release volk_gnsssdr_free(fft_codes_padded); } void GalileoE5aPcpsAcquisitionFpga::stop_acquisition() { // this command causes the SW to reset the HW. acquisition_fpga_->reset_acquisition(); } void GalileoE5aPcpsAcquisitionFpga::set_threshold(float threshold) { DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold; acquisition_fpga_->set_threshold(threshold); } void GalileoE5aPcpsAcquisitionFpga::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_fpga_->set_doppler_max(doppler_max_); } void GalileoE5aPcpsAcquisitionFpga::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_fpga_->set_doppler_step(doppler_step_); } void GalileoE5aPcpsAcquisitionFpga::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_fpga_->set_doppler_center(doppler_center_); } void GalileoE5aPcpsAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_fpga_->set_gnss_synchro(gnss_synchro_); } signed int GalileoE5aPcpsAcquisitionFpga::mag() { return acquisition_fpga_->mag(); } void GalileoE5aPcpsAcquisitionFpga::init() { acquisition_fpga_->init(); } void GalileoE5aPcpsAcquisitionFpga::set_local_code() { acquisition_fpga_->set_local_code(); } void GalileoE5aPcpsAcquisitionFpga::reset() { acquisition_fpga_->set_active(true); } void GalileoE5aPcpsAcquisitionFpga::set_state(int state) { acquisition_fpga_->set_state(state); } void GalileoE5aPcpsAcquisitionFpga::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect } void GalileoE5aPcpsAcquisitionFpga::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr GalileoE5aPcpsAcquisitionFpga::get_left_block() { return nullptr; } gr::basic_block_sptr GalileoE5aPcpsAcquisitionFpga::get_right_block() { return nullptr; } src/algorithms/acquisition/adapters/galileo_e5a_pcps_acquisition_fpga.h000066400000000000000000000144571352176506000271420ustar00rootroot00000000000000/*! * \file galileo_e5a_pcps_acquisition_fpga.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals for the FPGA * \author Marc Majoral, 2019. mmajoral(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E5A_PCPS_ACQUISITION_FPGA_H_ #define GNSS_SDR_GALILEO_E5A_PCPS_ACQUISITION_FPGA_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition_fpga.h" #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA * to an AcquisitionInterface for Galileo E5a signals */ class GalileoE5aPcpsAcquisitionFpga : public AcquisitionInterface { public: /*! * \brief Constructor */ GalileoE5aPcpsAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); /*! * \brief Destructor */ ~GalileoE5aPcpsAcquisitionFpga() = default; /*! * \brief Role */ inline std::string role() override { return role_; } /*! * \brief Returns "Galileo_E5a_Pcps_Acquisition_Fpga" */ inline std::string implementation() override { return "Galileo_E5a_Pcps_Acquisition_Fpga"; } /*! * \brief Returns size of lv_16sc_t */ inline size_t item_size() override { return sizeof(int16_t); } /*! * \brief Connect */ void connect(gr::top_block_sptr top_block) override; /*! * \brief Disconnect */ void disconnect(gr::top_block_sptr top_block) override; /*! * \brief Get left block */ gr::basic_block_sptr get_left_block() override; /*! * \brief Get right block */ gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_fpga_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_fpga_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local Galileo E5a code for PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state) override; /*! * \brief This function is only used in the unit tests */ void set_single_doppler_flag(unsigned int single_doppler_flag); /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Set resampler latency */ void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: // the following flags are FPGA-specific and they are using arrange the values of the fft of the local code in the way the FPGA // expects. This arrangement is done in the initialisation to avoid consuming unnecessary clock cycles during tracking. static const uint32_t quant_bits_local_code = 16; static const uint32_t select_lsbits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t select_msbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t select_all_code_bits = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t shl_code_bits = 65536; // shift left by 10 bits ConfigurationInterface* configuration_; pcps_acquisition_fpga_sptr acquisition_fpga_; std::string item_type_; std::string dump_filename_; std::string role_; bool acq_pilot_; bool acq_iq_; uint32_t channel_; std::weak_ptr channel_fsm_; uint32_t doppler_max_; uint32_t doppler_step_; int32_t doppler_center_; unsigned int in_streams_; unsigned int out_streams_; Gnss_Synchro* gnss_synchro_; std::vector d_all_fft_codes_; // memory that contains all the code ffts }; #endif /* GNSS_SDR_GALILEO_E5A_PCPS_ACQUISITION_FPGA_H_ */ src/algorithms/acquisition/adapters/glonass_l1_ca_pcps_acquisition.cc000066400000000000000000000242051352176506000266320ustar00rootroot00000000000000/*! * \file glonass_l1_ca_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Glonass L1 C/A signals * \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com * \author Luis Esteve, 2017. luis(at)epsilon-formacion.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "glonass_l1_ca_pcps_acquisition.h" #include "GLONASS_L1_L2_CA.h" #include "acq_conf.h" #include "configuration_interface.h" #include "glonass_l1_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GlonassL1CaPcpsAcquisition::GlonassL1CaPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { Acq_Conf acq_parameters = Acq_Conf(); configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.fs_in = fs_in_; acq_parameters.samples_per_chip = static_cast(ceil(GLONASS_L1_CA_CHIP_PERIOD * static_cast(acq_parameters.fs_in))); dump_ = configuration_->property(role + ".dump", false); acq_parameters.dump = dump_; acq_parameters.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters.blocking = blocking_; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters.doppler_max = doppler_max_; sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); acq_parameters.sampled_ms = sampled_ms_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters.dump_filename = dump_filename_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::round(static_cast(fs_in_) / (GLONASS_L1_CA_CODE_RATE_HZ / GLONASS_L1_CA_CODE_LENGTH_CHIPS))); vector_length_ = code_length_ * sampled_ms_; if (bit_transition_flag_) { vector_length_ *= 2; } code_ = std::vector>(vector_length_); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters.it_size = item_size_; acq_parameters.sampled_ms = sampled_ms_; acq_parameters.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters.ms_per_code = 1; acq_parameters.samples_per_code = acq_parameters.samples_per_ms * static_cast(GLONASS_L1_CA_CODE_PERIOD * 1000.0); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acquisition_ = pcps_make_acquisition(acq_parameters); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GlonassL1CaPcpsAcquisition::stop_acquisition() { } void GlonassL1CaPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + ".pfa", 0.0); if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GlonassL1CaPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void GlonassL1CaPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GlonassL1CaPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GlonassL1CaPcpsAcquisition::mag() { return acquisition_->mag(); } void GlonassL1CaPcpsAcquisition::init() { acquisition_->init(); set_local_code(); } void GlonassL1CaPcpsAcquisition::set_local_code() { std::vector> code(code_length_); glonass_l1_ca_code_gen_complex_sampled(code, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GlonassL1CaPcpsAcquisition::reset() { acquisition_->set_active(true); } void GlonassL1CaPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float GlonassL1CaPcpsAcquisition::calculate_threshold(float pfa) { //Calculate the threshold unsigned int frequency_bins = 0; /* for (int doppler = (int)(-doppler_max_); doppler <= (int)doppler_max_; doppler += doppler_step_) { frequency_bins++; } */ frequency_bins = (2 * doppler_max_ + doppler_step_) / doppler_step_; DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = static_cast(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GlonassL1CaPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GlonassL1CaPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GlonassL1CaPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GlonassL1CaPcpsAcquisition::get_right_block() { return acquisition_; } src/algorithms/acquisition/adapters/glonass_l1_ca_pcps_acquisition.h000066400000000000000000000124461352176506000265000ustar00rootroot00000000000000/*! * \file glonass_l1_ca_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Glonass L1 C/A signals * \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com * \author Luis Esteve, 2017. luis(at)epsilon-formacion.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GLONASS_L1_CA_PCPS_ACQUISITION_H_ #define GNSS_SDR_GLONASS_L1_CA_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals */ class GlonassL1CaPcpsAcquisition : public AcquisitionInterface { public: GlonassL1CaPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GlonassL1CaPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GLONASS_L1_CA_PCPS_Acquisition" */ inline std::string implementation() override { return "GLONASS_L1_CA_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GLONASS_L1_CA_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/glonass_l2_ca_pcps_acquisition.cc000066400000000000000000000240251352176506000266330ustar00rootroot00000000000000/*! * \file glonass_l2_ca_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Glonass L2 C/A signals * \author Damian Miralles, 2018, dmiralles2009@gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "glonass_l2_ca_pcps_acquisition.h" #include "GLONASS_L1_L2_CA.h" #include "acq_conf.h" #include "configuration_interface.h" #include "glonass_l2_signal_processing.h" #include "gnss_sdr_flags.h" #include #include #include GlonassL2CaPcpsAcquisition::GlonassL2CaPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { Acq_Conf acq_parameters = Acq_Conf(); configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.fs_in = fs_in_; acq_parameters.samples_per_chip = static_cast(ceil(GLONASS_L2_CA_CHIP_PERIOD * static_cast(acq_parameters.fs_in))); dump_ = configuration_->property(role + ".dump", false); acq_parameters.dump = dump_; acq_parameters.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters.blocking = blocking_; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters.doppler_max = doppler_max_; sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters.dump_filename = dump_filename_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::round(static_cast(fs_in_) / (GLONASS_L2_CA_CODE_RATE_HZ / GLONASS_L2_CA_CODE_LENGTH_CHIPS))); vector_length_ = code_length_ * sampled_ms_; if (bit_transition_flag_) { vector_length_ *= 2; } code_ = std::vector>(vector_length_); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters.it_size = item_size_; acq_parameters.sampled_ms = sampled_ms_; acq_parameters.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters.ms_per_code = 1; acq_parameters.samples_per_code = acq_parameters.samples_per_ms * static_cast(GLONASS_L2_CA_CODE_PERIOD * 1000.0); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acquisition_ = pcps_make_acquisition(acq_parameters); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GlonassL2CaPcpsAcquisition::stop_acquisition() { } void GlonassL2CaPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + ".pfa", 0.0); if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GlonassL2CaPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void GlonassL2CaPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GlonassL2CaPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GlonassL2CaPcpsAcquisition::mag() { return acquisition_->mag(); } void GlonassL2CaPcpsAcquisition::init() { acquisition_->init(); set_local_code(); } void GlonassL2CaPcpsAcquisition::set_local_code() { std::vector> code(code_length_); glonass_l2_ca_code_gen_complex_sampled(code, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GlonassL2CaPcpsAcquisition::reset() { acquisition_->set_active(true); } void GlonassL2CaPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float GlonassL2CaPcpsAcquisition::calculate_threshold(float pfa) { //Calculate the threshold unsigned int frequency_bins = 0; /* for (int doppler = (int)(-doppler_max_); doppler <= (int)doppler_max_; doppler += doppler_step_) { frequency_bins++; } */ frequency_bins = (2 * doppler_max_ + doppler_step_) / doppler_step_; DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = static_cast(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GlonassL2CaPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GlonassL2CaPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GlonassL2CaPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GlonassL2CaPcpsAcquisition::get_right_block() { return acquisition_; } src/algorithms/acquisition/adapters/glonass_l2_ca_pcps_acquisition.h000066400000000000000000000123521352176506000264750ustar00rootroot00000000000000/*! * \file glonass_l2_ca_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Glonass L2 C/A signals * \author Damian Miralles, 2018, dmiralles2009@gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GLONASS_L2_CA_PCPS_ACQUISITION_H_ #define GNSS_SDR_GLONASS_L2_CA_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GLONASS L2 C/A signals */ class GlonassL2CaPcpsAcquisition : public AcquisitionInterface { public: GlonassL2CaPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GlonassL2CaPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GLONASS_L2_CA_PCPS_Acquisition" */ inline std::string implementation() override { return "GLONASS_L2_CA_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GLONASS L2/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GLONASS_L2_CA_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.cc000066400000000000000000000304471352176506000257620ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L1 C/A signals * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena(at)gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_acquisition.h" #include "GPS_L1_CA.h" #include "acq_conf.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include #include #include #include GpsL1CaPcpsAcquisition::GpsL1CaPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); acq_parameters_.sampled_ms = sampled_ms_; acq_parameters_.ms_per_code = 1; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); if (acq_parameters_.use_automatic_resampler == true and item_type_ != "gr_complex") { LOG(WARNING) << "GPS L1 CA acquisition disabled the automatic resampler feature because its item_type is not set to gr_complex"; acq_parameters_.use_automatic_resampler = false; } if (acq_parameters_.use_automatic_resampler) { if (acq_parameters_.fs_in > GPS_L1_CA_OPT_ACQ_FS_HZ) { acq_parameters_.resampler_ratio = floor(static_cast(acq_parameters_.fs_in) / GPS_L1_CA_OPT_ACQ_FS_HZ); uint32_t decimation = acq_parameters_.fs_in / GPS_L1_CA_OPT_ACQ_FS_HZ; while (acq_parameters_.fs_in % decimation > 0) { decimation--; }; acq_parameters_.resampler_ratio = decimation; acq_parameters_.resampled_fs = acq_parameters_.fs_in / static_cast(acq_parameters_.resampler_ratio); } //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(acq_parameters_.resampled_fs) / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(acq_parameters_.resampled_fs) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil(GPS_L1_CA_CHIP_PERIOD * static_cast(acq_parameters_.resampled_fs))); } else { acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil(GPS_L1_CA_CHIP_PERIOD * static_cast(acq_parameters_.fs_in))); } acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(GPS_L1_CA_CODE_PERIOD * 1000.0); vector_length_ = std::floor(acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms) * (acq_parameters_.bit_transition_flag ? 2 : 1); code_ = std::vector>(vector_length_); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.it_size = item_size_; acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; doppler_center_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsAcquisition::stop_acquisition() { } void GpsL1CaPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + ".pfa", 0.0); if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GpsL1CaPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } void GpsL1CaPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GpsL1CaPcpsAcquisition::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_->set_doppler_center(doppler_center_); } void GpsL1CaPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GpsL1CaPcpsAcquisition::mag() { return acquisition_->mag(); } void GpsL1CaPcpsAcquisition::init() { acquisition_->init(); } void GpsL1CaPcpsAcquisition::set_local_code() { std::vector> code(code_length_); if (acq_parameters_.use_automatic_resampler) { gps_l1_ca_code_gen_complex_sampled(code, gnss_synchro_->PRN, acq_parameters_.resampled_fs, 0); } else { gps_l1_ca_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); } gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GpsL1CaPcpsAcquisition::reset() { acquisition_->set_active(true); } void GpsL1CaPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float GpsL1CaPcpsAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL1CaPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GpsL1CaPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GpsL1CaPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GpsL1CaPcpsAcquisition::get_right_block() { return acquisition_; } void GpsL1CaPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h000066400000000000000000000132521352176506000256170ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L1 C/A signals * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena(at)gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_H_ #include "acq_conf.h" #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals */ class GpsL1CaPcpsAcquisition : public AcquisitionInterface { public: GpsL1CaPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_Acquisition" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; int doppler_center_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition_fine_doppler.cc000066400000000000000000000142331352176506000305030ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L1 C/A Signals * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_acquisition_fine_doppler.h" #include "GPS_L1_CA.h" #include "acq_conf.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include GpsL1CaPcpsAcquisitionFineDoppler::GpsL1CaPcpsAcquisitionFineDoppler( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; DLOG(INFO) << "role " << role; Acq_Conf acq_parameters = Acq_Conf(); item_type_ = configuration->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.fs_in = fs_in_; acq_parameters.samples_per_chip = static_cast(ceil(GPS_L1_CA_CHIP_PERIOD * static_cast(acq_parameters.fs_in))); dump_ = configuration->property(role + ".dump", false); acq_parameters.dump = dump_; dump_filename_ = configuration->property(role + ".dump_filename", default_dump_filename); acq_parameters.dump_filename = dump_filename_; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters.doppler_max = doppler_max_; sampled_ms_ = configuration->property(role + ".coherent_integration_time_ms", 1); acq_parameters.sampled_ms = sampled_ms_; max_dwells_ = configuration->property(role + ".max_dwells", 1); acq_parameters.max_dwells = max_dwells_; acq_parameters.blocking_on_standby = configuration->property(role + ".blocking_on_standby", false); //--- Find number of samples per spreading code ------------------------- vector_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS)); acq_parameters.samples_per_ms = vector_length_; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_make_acquisition_fine_doppler_cc(acq_parameters); } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsAcquisitionFineDoppler::stop_acquisition() { } void GpsL1CaPcpsAcquisitionFineDoppler::set_threshold(float threshold) { threshold_ = threshold; acquisition_cc_->set_threshold(threshold_); } void GpsL1CaPcpsAcquisitionFineDoppler::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_cc_->set_doppler_max(doppler_max_); } void GpsL1CaPcpsAcquisitionFineDoppler::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_cc_->set_doppler_step(doppler_step_); } void GpsL1CaPcpsAcquisitionFineDoppler::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_cc_->set_gnss_synchro(gnss_synchro_); } signed int GpsL1CaPcpsAcquisitionFineDoppler::mag() { return acquisition_cc_->mag(); } void GpsL1CaPcpsAcquisitionFineDoppler::init() { acquisition_cc_->init(); } void GpsL1CaPcpsAcquisitionFineDoppler::set_local_code() { gps_l1_ca_code_gen_complex_sampled(code_, gnss_synchro_->PRN, fs_in_, 0); acquisition_cc_->set_local_code(code_.data()); } void GpsL1CaPcpsAcquisitionFineDoppler::reset() { acquisition_cc_->set_active(true); } void GpsL1CaPcpsAcquisitionFineDoppler::set_state(int state) { acquisition_cc_->set_state(state); } void GpsL1CaPcpsAcquisitionFineDoppler::connect(boost::shared_ptr top_block) { if (top_block) { /* top_block is not null */ }; //nothing to disconnect, now the tracking uses gr_sync_decimator } void GpsL1CaPcpsAcquisitionFineDoppler::disconnect(boost::shared_ptr top_block) { if (top_block) { /* top_block is not null */ }; //nothing to disconnect, now the tracking uses gr_sync_decimator } boost::shared_ptr GpsL1CaPcpsAcquisitionFineDoppler::get_left_block() { return acquisition_cc_; } boost::shared_ptr GpsL1CaPcpsAcquisitionFineDoppler::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition_fine_doppler.h000066400000000000000000000117561352176506000303540ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition_fine_doppler.h * \brief Adapts a PCPS acquisition block with fine Doppler estimation to an AcquisitionInterface for * GPS L1 C/A signals * \authors
    *
  • Javier Arribas, 2013. jarribas(at)cttc.es *
* * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FINE_DOPPLER_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FINE_DOPPLER_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition_fine_doppler_cc.h" #include #include #include class ConfigurationInterface; /*! * \brief This class Adapts a PCPS acquisition block with fine Doppler estimation to an AcquisitionInterface for * GPS L1 C/A signals */ class GpsL1CaPcpsAcquisitionFineDoppler : public AcquisitionInterface { public: GpsL1CaPcpsAcquisitionFineDoppler(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsAcquisitionFineDoppler() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_Acquisition_Fine_Doppler" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_Acquisition_Fine_Doppler"; } inline size_t item_size() override { return item_size_; } void connect(boost::shared_ptr top_block) override; void disconnect(boost::shared_ptr top_block) override; boost::shared_ptr get_left_block() override; boost::shared_ptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: pcps_acquisition_fine_doppler_cc_sptr acquisition_cc_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FINE_DOPPLER_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition_fpga.cc000066400000000000000000000237731352176506000267630ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition_fpga.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals for the FPGA * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_acquisition_fpga.h" #include "GPS_L1_CA.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include #include #include // for gr_complex #include // for volk_32fc_conjugate_32fc #include #include // for copy_n #include // for abs, pow, floor #include // for complex GpsL1CaPcpsAcquisitionFpga::GpsL1CaPcpsAcquisitionFpga( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { pcpsconf_fpga_t acq_parameters; configuration_ = configuration; std::string default_item_type = "cshort"; DLOG(INFO) << "role " << role; int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); int64_t fs_in = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; uint32_t downsampling_factor = configuration_->property(role + ".downsampling_factor", 4); acq_parameters.downsampling_factor = downsampling_factor; fs_in = fs_in / downsampling_factor; acq_parameters.fs_in = fs_in; doppler_max_ = configuration_->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) doppler_max_ = FLAGS_doppler_max; acq_parameters.doppler_max = doppler_max_; uint32_t sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); acq_parameters.sampled_ms = sampled_ms; auto code_length = static_cast(std::round(static_cast(fs_in) / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS))); acq_parameters.code_length = code_length; // The FPGA can only use FFT lengths that are a power of two. float nbits = ceilf(log2f((float)code_length * 2)); uint32_t nsamples_total = pow(2, nbits); uint32_t select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 0); acq_parameters.select_queue_Fpga = select_queue_Fpga; std::string default_device_name = "/dev/uio0"; std::string device_name = configuration_->property(role + ".devicename", default_device_name); acq_parameters.device_name = device_name; acq_parameters.samples_per_ms = nsamples_total / sampled_ms; acq_parameters.samples_per_code = nsamples_total; acq_parameters.excludelimit = static_cast(1 + ceil(GPS_L1_CA_CHIP_PERIOD * static_cast(fs_in))); // compute all the GPS L1 PRN Codes (this is done only once upon the class constructor in order to avoid re-computing the PRN codes every time // a channel is assigned) auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // allocate memory to compute all the PRNs and compute all the possible codes std::vector> code(nsamples_total); // buffer for the local code auto* fft_codes_padded = static_cast(volk_gnsssdr_malloc(nsamples_total * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_all_fft_codes_ = std::vector(nsamples_total * NUM_PRNs); // memory containing all the possible fft codes for PRN 0 to 32 float max; int32_t tmp, tmp2, local_code, fft_data; // temporary maxima search for (uint32_t PRN = 1; PRN <= NUM_PRNs; PRN++) { gps_l1_ca_code_gen_complex_sampled(code, PRN, fs_in, 0); // generate PRN code for (uint32_t s = code_length; s < 2 * code_length; s++) { code[s] = code[s - code_length]; } // fill in zero padding for (uint32_t s = 2 * code_length; s < nsamples_total; s++) { code[s] = std::complex(0.0, 0.0); } std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer fft_if->execute(); // Run the FFT of local code volk_32fc_conjugate_32fc(fft_codes_padded, fft_if->get_outbuf(), nsamples_total); // conjugate values max = 0; // initialize maximum value for (uint32_t i = 0; i < nsamples_total; i++) // search for maxima { if (std::abs(fft_codes_padded[i].real()) > max) { max = std::abs(fft_codes_padded[i].real()); } if (std::abs(fft_codes_padded[i].imag()) > max) { max = std::abs(fft_codes_padded[i].imag()); } } // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs // and package codes in a format that is ready to be written to the FPGA for (uint32_t i = 0; i < nsamples_total; i++) { tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, quant_bits_local_code - 1) - 1) / max)); tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, quant_bits_local_code - 1) - 1) / max)); local_code = (tmp & select_lsbits) | ((tmp2 * shl_code_bits) & select_msbits); // put together the real part and the imaginary part fft_data = local_code & select_all_code_bits; d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; } } // acq_parameters acq_parameters.all_fft_codes = d_all_fft_codes_.data(); // reference for the FPGA FFT-IFFT attenuation factor acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 10); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.max_num_acqs = configuration_->property(role + ".max_num_acqs", 2); acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); channel_ = 0; doppler_step_ = 0; gnss_synchro_ = nullptr; // temporary buffers that we can release volk_gnsssdr_free(fft_codes_padded); } void GpsL1CaPcpsAcquisitionFpga::stop_acquisition() { // this command causes the SW to reset the HW. acquisition_fpga_->reset_acquisition(); } void GpsL1CaPcpsAcquisitionFpga::set_threshold(float threshold) { DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold; acquisition_fpga_->set_threshold(threshold); } void GpsL1CaPcpsAcquisitionFpga::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_fpga_->set_doppler_max(doppler_max_); } void GpsL1CaPcpsAcquisitionFpga::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_fpga_->set_doppler_step(doppler_step_); } void GpsL1CaPcpsAcquisitionFpga::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_fpga_->set_doppler_center(doppler_center_); } void GpsL1CaPcpsAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_fpga_->set_gnss_synchro(gnss_synchro_); } signed int GpsL1CaPcpsAcquisitionFpga::mag() { return acquisition_fpga_->mag(); } void GpsL1CaPcpsAcquisitionFpga::init() { acquisition_fpga_->init(); } void GpsL1CaPcpsAcquisitionFpga::set_local_code() { acquisition_fpga_->set_local_code(); } void GpsL1CaPcpsAcquisitionFpga::reset() { // this function starts the acquisition process acquisition_fpga_->set_active(true); } void GpsL1CaPcpsAcquisitionFpga::set_state(int state) { acquisition_fpga_->set_state(state); } void GpsL1CaPcpsAcquisitionFpga::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect } void GpsL1CaPcpsAcquisitionFpga::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr GpsL1CaPcpsAcquisitionFpga::get_left_block() { return nullptr; } gr::basic_block_sptr GpsL1CaPcpsAcquisitionFpga::get_right_block() { return nullptr; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition_fpga.h000066400000000000000000000141421352176506000266130ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition_fpga.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals for the FPGA * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FPGA_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FPGA_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition_fpga.h" #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA * to an AcquisitionInterface for GPS L1 C/A signals */ class GpsL1CaPcpsAcquisitionFpga : public AcquisitionInterface { public: /*! * \brief Constructor */ GpsL1CaPcpsAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); /*! * \brief Destructor */ ~GpsL1CaPcpsAcquisitionFpga() = default; /*! * \brief Role */ inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_Acquisition_Fpga" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_Acquisition_Fpga"; } /*! * \brief Returns size of lv_16sc_t */ inline size_t item_size() override { return sizeof(int16_t); } /*! * \brief Connect */ void connect(gr::top_block_sptr top_block) override; /*! * \brief Disconnect */ void disconnect(gr::top_block_sptr top_block) override; /*! * \brief Get left block */ gr::basic_block_sptr get_left_block() override; /*! * \brief Get right block */ gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_fpga_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_fpga_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Set Resampler Latency */ void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: static const uint32_t NUM_PRNs = 32; // the following flags are FPGA-specific and they are using arrange the values of the fft of the local code in the way the FPGA // expects. This arrangement is done in the initialisation to avoid consuming unnecessary clock cycles during tracking. static const uint32_t quant_bits_local_code = 16; static const uint32_t select_lsbits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t select_msbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t select_all_code_bits = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t shl_code_bits = 65536; // shift left by 10 bits ConfigurationInterface* configuration_; pcps_acquisition_fpga_sptr acquisition_fpga_; uint32_t channel_; std::weak_ptr channel_fsm_; uint32_t doppler_max_; uint32_t doppler_step_; int32_t doppler_center_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; std::vector d_all_fft_codes_; // memory that contains all the code ffts }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_FPGA_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_assisted_acquisition.cc000066400000000000000000000130571352176506000276570ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L1 C/A Signals * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_assisted_acquisition.h" #include "GPS_L1_CA.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include GpsL1CaPcpsAssistedAcquisition::GpsL1CaPcpsAssistedAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration->property(role + ".dump", false); doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } doppler_min_ = configuration->property(role + ".doppler_min", -doppler_max_); sampled_ms_ = configuration->property(role + ".coherent_integration_time_ms", 1); max_dwells_ = configuration->property(role + ".max_dwells", 1); dump_filename_ = configuration->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code ------------------------- vector_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS)); code_ = std::make_shared>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_make_assisted_acquisition_cc(max_dwells_, sampled_ms_, doppler_max_, doppler_min_, fs_in_, vector_length_, dump_, dump_filename_); } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsAssistedAcquisition::stop_acquisition() { } void GpsL1CaPcpsAssistedAcquisition::set_threshold(float threshold) { threshold_ = threshold; acquisition_cc_->set_threshold(threshold_); } void GpsL1CaPcpsAssistedAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_cc_->set_doppler_max(doppler_max_); } void GpsL1CaPcpsAssistedAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_cc_->set_doppler_step(doppler_step_); } void GpsL1CaPcpsAssistedAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_cc_->set_gnss_synchro(gnss_synchro_); } signed int GpsL1CaPcpsAssistedAcquisition::mag() { return acquisition_cc_->mag(); } void GpsL1CaPcpsAssistedAcquisition::init() { acquisition_cc_->init(); } void GpsL1CaPcpsAssistedAcquisition::set_local_code() { gps_l1_ca_code_gen_complex_sampled(code_, gnss_synchro_->PRN, fs_in_, 0); acquisition_cc_->set_local_code(code_.get()); } void GpsL1CaPcpsAssistedAcquisition::reset() { acquisition_cc_->set_active(true); } void GpsL1CaPcpsAssistedAcquisition::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; //nothing to disconnect, now the tracking uses gr_sync_decimator } void GpsL1CaPcpsAssistedAcquisition::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; //nothing to disconnect, now the tracking uses gr_sync_decimator } gr::basic_block_sptr GpsL1CaPcpsAssistedAcquisition::get_left_block() { return acquisition_cc_; } gr::basic_block_sptr GpsL1CaPcpsAssistedAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_assisted_acquisition.h000066400000000000000000000114461352176506000275210ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_assisted_acquisition.h * \brief Adapts a PCPS Assisted acquisition block to an AcquisitionInterface for * GPS L1 C/A signals * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
* * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_ASSISTED_ACQUISITION_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_ASSISTED_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_assisted_acquisition_cc.h" #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals */ class GpsL1CaPcpsAssistedAcquisition : public AcquisitionInterface { public: GpsL1CaPcpsAssistedAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsAssistedAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_Assisted_Acquisition" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_Assisted_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; void set_state(int state __attribute__((unused))) override{}; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: pcps_assisted_acquisition_cc_sptr acquisition_cc_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; int doppler_max_; unsigned int doppler_step_; int doppler_min_; unsigned int sampled_ms_; int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::shared_ptr> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_ASSISTED_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_opencl_acquisition.cc000066400000000000000000000177201352176506000273210ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_opencl_acquisition.cc * \brief Adapts an OpenCL PCPS acquisition block to an * AcquisitionInterface for GPS L1 C/A signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_opencl_acquisition.h" #include "GPS_L1_CA.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include #include #include GpsL1CaPcpsOpenClAcquisition::GpsL1CaPcpsOpenClAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); bit_transition_flag_ = configuration_->property("Acquisition.bit_transition_flag", false); if (!bit_transition_flag_) { max_dwells_ = configuration_->property(role + ".max_dwells", 1); } else { max_dwells_ = 2; } dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code ------------------------- code_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * sampled_ms_; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_make_opencl_acquisition_cc(sampled_ms_, max_dwells_, doppler_max_, fs_in_, code_length_, code_length_, bit_transition_flag_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsOpenClAcquisition::stop_acquisition() { } void GpsL1CaPcpsOpenClAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GpsL1CaPcpsOpenClAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GpsL1CaPcpsOpenClAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GpsL1CaPcpsOpenClAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GpsL1CaPcpsOpenClAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } else { return 0; } } void GpsL1CaPcpsOpenClAcquisition::init() { acquisition_cc_->init(); } void GpsL1CaPcpsOpenClAcquisition::set_local_code() { if (item_type_ == "gr_complex") { std::vector> code(code_length_); gps_l1_ca_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GpsL1CaPcpsOpenClAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } float GpsL1CaPcpsOpenClAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL1CaPcpsOpenClAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GpsL1CaPcpsOpenClAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GpsL1CaPcpsOpenClAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GpsL1CaPcpsOpenClAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_opencl_acquisition.h000066400000000000000000000123031352176506000271530ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_opencl_acquisition.h * \brief Adapts an OpenCL PCPS acquisition block to an * AcquisitionInterface for GPS L1 C/A signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_OPENCL_ACQUISITION_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_OPENCL_ACQUISITION_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_opencl_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts an OpenCL PCPS acquisition block to an * AcquisitionInterface for GPS L1 C/A signals */ class GpsL1CaPcpsOpenClAcquisition : public AcquisitionInterface { public: GpsL1CaPcpsOpenClAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsOpenClAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_OpenCl_Acquisition" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_OpenCl_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; void set_state(int state __attribute__((unused))) override{}; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; inline bool opencl_ready() const { bool ready = this->acquisition_cc_->opencl_ready(); return ready; } private: ConfigurationInterface* configuration_; pcps_opencl_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_OPENCL_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_quicksync_acquisition.cc000066400000000000000000000232341352176506000300470ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_quicksync_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L1 C/A signals using the QuickSync Algorithm * \author Damian Miralles, 2014. dmiralles2009@gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_quicksync_acquisition.h" #include "GPS_L1_CA.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include #include #include GpsL1CaPcpsQuickSyncAcquisition::GpsL1CaPcpsQuickSyncAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 4); //--- Find number of samples per spreading code ------------------------- code_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS)); /* Calculate the folding factor value based on the calculations */ auto temp = static_cast(ceil(sqrt(log2(code_length_)))); folding_factor_ = configuration_->property(role + ".folding_factor", temp); if (sampled_ms_ % folding_factor_ != 0) { LOG(WARNING) << "QuickSync Algorithm requires a coherent_integration_time" << " multiple of " << folding_factor_ << "ms, Value entered " << sampled_ms_ << " ms"; if (sampled_ms_ < folding_factor_) { sampled_ms_ = static_cast(folding_factor_); } else { sampled_ms_ = static_cast(sampled_ms_ / folding_factor_) * folding_factor_; } LOG(WARNING) << " Coherent_integration_time of " << sampled_ms_ << " ms will be used instead."; } vector_length_ = code_length_ * sampled_ms_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); if (!bit_transition_flag_) { max_dwells_ = configuration_->property(role + ".max_dwells", 1); } else { max_dwells_ = 2; } dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); int samples_per_ms = round(code_length_); code_ = std::vector>(code_length_); /* Object relevant information for debugging */ LOG(INFO) << "Implementation: " << this->implementation() << ", Vector Length: " << vector_length_ << ", Samples per ms: " << samples_per_ms << ", Folding factor: " << folding_factor_ << ", Sampled ms: " << sampled_ms_ << ", Code Length: " << code_length_; if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_quicksync_make_acquisition_cc(folding_factor_, sampled_ms_, max_dwells_, doppler_max_, fs_in_, samples_per_ms, code_length_, bit_transition_flag_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, code_length_ * folding_factor_); DLOG(INFO) << "stream_to_vector_quicksync(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsQuickSyncAcquisition::stop_acquisition() { } void GpsL1CaPcpsQuickSyncAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GpsL1CaPcpsQuickSyncAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GpsL1CaPcpsQuickSyncAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GpsL1CaPcpsQuickSyncAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GpsL1CaPcpsQuickSyncAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GpsL1CaPcpsQuickSyncAcquisition::init() { acquisition_cc_->init(); } void GpsL1CaPcpsQuickSyncAcquisition::set_local_code() { if (item_type_ == "gr_complex") { std::vector> code(code_length_); gps_l1_ca_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < (sampled_ms_ / folding_factor_); i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GpsL1CaPcpsQuickSyncAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } void GpsL1CaPcpsQuickSyncAcquisition::set_state(int state) { if (item_type_ == "gr_complex") { acquisition_cc_->set_state(state); } } float GpsL1CaPcpsQuickSyncAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = (code_length_ / folding_factor_) * frequency_bins; double exponent = 1.0 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); double lambda = static_cast(code_length_) / static_cast(folding_factor_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL1CaPcpsQuickSyncAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GpsL1CaPcpsQuickSyncAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GpsL1CaPcpsQuickSyncAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GpsL1CaPcpsQuickSyncAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_quicksync_acquisition.h000066400000000000000000000124401352176506000277060ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_quicksync_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for GPS L1 C/A signals implementing the QuickSync Algorithm. * \date June, 2014 * \author Damian Miralles Sanchez. dmiralles2009@gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_PCPS_QUICKSYNC_ACQUISITION_H_ #define GNSS_SDR_GPS_L1_CA_PCPS_QUICKSYNC_ACQUISITION_H_ #include "channel_fsm.h" #include "configuration_interface.h" #include "gnss_synchro.h" #include "pcps_quicksync_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L1 C/A signals */ class GpsL1CaPcpsQuickSyncAcquisition : public AcquisitionInterface { public: GpsL1CaPcpsQuickSyncAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsQuickSyncAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_QuickSync_Acquisition" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_QuickSync_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_quicksync_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int max_dwells_; unsigned int folding_factor_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L1_CA_PCPS_QUICKSYNC_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l1_ca_pcps_tong_acquisition.cc000066400000000000000000000177731352176506000270200ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_tong_acquisition.cc * \brief Adapts a PCPS Tong acquisition block to an AcquisitionInterface for * GPS L1 C/A signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l1_ca_pcps_tong_acquisition.h" #include "GPS_L1_CA.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_sdr_signal_processing.h" #include #include #include GpsL1CaPcpsTongAcquisition::GpsL1CaPcpsTongAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./data/acquisition.dat"; DLOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); dump_ = configuration_->property(role + ".dump", false); doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } sampled_ms_ = configuration_->property(role + ".coherent_integration_time_ms", 1); tong_init_val_ = configuration->property(role + ".tong_init_val", 1); tong_max_val_ = configuration->property(role + ".tong_max_val", 2); tong_max_dwells_ = configuration->property(role + ".tong_max_dwells", tong_max_val_ + 1); dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); //--- Find number of samples per spreading code ------------------------- code_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS)); vector_length_ = code_length_ * sampled_ms_; code_ = std::vector>(vector_length_); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); acquisition_cc_ = pcps_tong_make_acquisition_cc(sampled_ms_, doppler_max_, fs_in_, code_length_, code_length_, tong_init_val_, tong_max_val_, tong_max_dwells_, dump_, dump_filename_); stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; } else { item_size_ = sizeof(gr_complex); LOG(WARNING) << item_type_ << " unknown acquisition item type"; } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL1CaPcpsTongAcquisition::stop_acquisition() { } void GpsL1CaPcpsTongAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; if (item_type_ == "gr_complex") { acquisition_cc_->set_threshold(threshold_); } } void GpsL1CaPcpsTongAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_max(doppler_max_); } } void GpsL1CaPcpsTongAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; if (item_type_ == "gr_complex") { acquisition_cc_->set_doppler_step(doppler_step_); } } void GpsL1CaPcpsTongAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; if (item_type_ == "gr_complex") { acquisition_cc_->set_gnss_synchro(gnss_synchro_); } } signed int GpsL1CaPcpsTongAcquisition::mag() { if (item_type_ == "gr_complex") { return acquisition_cc_->mag(); } return 0; } void GpsL1CaPcpsTongAcquisition::init() { acquisition_cc_->init(); } void GpsL1CaPcpsTongAcquisition::set_local_code() { if (item_type_ == "gr_complex") { std::vector> code(code_length_); gps_l1_ca_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_, 0); gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < sampled_ms_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_cc_->set_local_code(code_.data()); } } void GpsL1CaPcpsTongAcquisition::reset() { if (item_type_ == "gr_complex") { acquisition_cc_->set_active(true); } } void GpsL1CaPcpsTongAcquisition::set_state(int state) { if (item_type_ == "gr_complex") { acquisition_cc_->set_state(state); } } float GpsL1CaPcpsTongAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL1CaPcpsTongAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); } } void GpsL1CaPcpsTongAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); } } gr::basic_block_sptr GpsL1CaPcpsTongAcquisition::get_left_block() { return stream_to_vector_; } gr::basic_block_sptr GpsL1CaPcpsTongAcquisition::get_right_block() { return acquisition_cc_; } src/algorithms/acquisition/adapters/gps_l1_ca_pcps_tong_acquisition.h000066400000000000000000000122601352176506000266440ustar00rootroot00000000000000/*! * \file gps_l1_ca_pcps_tong_acquisition.h * \brief Adapts a PCPS Tong acquisition block to an AcquisitionInterface for * GPS L1 C/A signals * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L1_CA_TONG_ACQUISITION_H_ #define GNSS_SDR_GPS_L1_CA_TONG_ACQUISITION_H_ #include "channel_fsm.h" #include "configuration_interface.h" #include "gnss_synchro.h" #include "pcps_tong_acquisition_cc.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS Tong acquisition block to an * AcquisitionInterface for GPS L1 C/A signals */ class GpsL1CaPcpsTongAcquisition : public AcquisitionInterface { public: GpsL1CaPcpsTongAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL1CaPcpsTongAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L1_CA_PCPS_Tong_Acquisition" */ inline std::string implementation() override { return "GPS_L1_CA_PCPS_Tong_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_cc_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_cc_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of TONG algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L1/CA TONG acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: ConfigurationInterface* configuration_; pcps_tong_acquisition_cc_sptr acquisition_cc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; unsigned int sampled_ms_; unsigned int tong_init_val_; unsigned int tong_max_val_; unsigned int tong_max_dwells_; int64_t fs_in_; bool dump_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L1_CA_TONG_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l2_m_pcps_acquisition.cc000066400000000000000000000313331352176506000256270ustar00rootroot00000000000000/*! * \file gps_l2_m_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L2 M signals * \authors
    *
  • Javier Arribas, 2015. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l2_m_pcps_acquisition.h" #include "GPS_L2C.h" #include "acq_conf.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_l2c_signal.h" #include #include #include GpsL2MPcpsAcquisition::GpsL2MPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; LOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.ms_per_code = 20; acq_parameters_.sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", acq_parameters_.ms_per_code); if ((acq_parameters_.sampled_ms % acq_parameters_.ms_per_code) != 0) { LOG(WARNING) << "Parameter coherent_integration_time_ms should be a multiple of 20. Setting it to 20"; acq_parameters_.sampled_ms = acq_parameters_.ms_per_code; } acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); if (acq_parameters_.use_automatic_resampler == true and item_type_ != "gr_complex") { LOG(WARNING) << "GPS L2CM acquisition disabled the automatic resampler feature because its item_type is not set to gr_complex"; acq_parameters_.use_automatic_resampler = false; } if (acq_parameters_.use_automatic_resampler) { if (acq_parameters_.fs_in > GPS_L2C_OPT_ACQ_FS_HZ) { acq_parameters_.resampler_ratio = floor(static_cast(acq_parameters_.fs_in) / GPS_L2C_OPT_ACQ_FS_HZ); uint32_t decimation = acq_parameters_.fs_in / GPS_L2C_OPT_ACQ_FS_HZ; while (acq_parameters_.fs_in % decimation > 0) { decimation--; }; acq_parameters_.resampler_ratio = decimation; acq_parameters_.resampled_fs = acq_parameters_.fs_in / static_cast(acq_parameters_.resampler_ratio); } //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(acq_parameters_.resampled_fs) / (GPS_L2_M_CODE_RATE_HZ / GPS_L2_M_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(acq_parameters_.resampled_fs) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GPS_L2_M_CODE_RATE_HZ) * static_cast(acq_parameters_.resampled_fs))); } else { acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (GPS_L2_M_CODE_RATE_HZ / GPS_L2_M_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GPS_L2_M_CODE_RATE_HZ) * static_cast(acq_parameters_.fs_in))); } acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(GPS_L2_M_PERIOD * 1000.0); vector_length_ = acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms * (acq_parameters_.bit_transition_flag ? 2 : 1); code_ = std::vector>(vector_length_); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.it_size = item_size_; acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; doppler_center_ = 0; gnss_synchro_ = nullptr; num_codes_ = acq_parameters_.sampled_ms / acq_parameters_.ms_per_code; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL2MPcpsAcquisition::stop_acquisition() { } void GpsL2MPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GpsL2MPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } // Be aware that Doppler step should be set to 2/(3T) Hz, where T is the coherent integration time (GPS L2 period is 0.02s) // Doppler bin minimum size= 33 Hz void GpsL2MPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GpsL2MPcpsAcquisition::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_->set_doppler_center(doppler_center_); } void GpsL2MPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GpsL2MPcpsAcquisition::mag() { return acquisition_->mag(); } void GpsL2MPcpsAcquisition::init() { acquisition_->init(); } void GpsL2MPcpsAcquisition::set_local_code() { std::vector> code(code_length_); if (acq_parameters_.use_automatic_resampler) { gps_l2c_m_code_gen_complex_sampled(code, gnss_synchro_->PRN, acq_parameters_.resampled_fs); } else { gps_l2c_m_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_); } gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < num_codes_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GpsL2MPcpsAcquisition::reset() { acquisition_->set_active(true); } void GpsL2MPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float GpsL2MPcpsAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1.0 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL2MPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GpsL2MPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GpsL2MPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GpsL2MPcpsAcquisition::get_right_block() { return acquisition_; } void GpsL2MPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/gps_l2_m_pcps_acquisition.h000066400000000000000000000130011352176506000254610ustar00rootroot00000000000000/*! * \file gps_l2_m_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L2 M signals * \authors
    *
  • Javier Arribas, 2015. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_H_ #define GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L2 M signals */ class GpsL2MPcpsAcquisition : public AcquisitionInterface { public: GpsL2MPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL2MPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L2_M_PCPS_Acquisition" */ inline std::string implementation() override { return "GPS_L2_M_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L2/M PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; int doppler_center_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; unsigned int num_codes_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l2_m_pcps_acquisition_fpga.cc000066400000000000000000000231361352176506000266260ustar00rootroot00000000000000/*! * \file gps_l2_m_pcps_acquisition_fpga.cc * \brief Adapts an FPGA-offloaded PCPS acquisition block * to an AcquisitionInterface for GPS L2 M signals * \authors
    *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l2_m_pcps_acquisition_fpga.h" #include "GPS_L2C.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gnss_synchro.h" #include "gps_l2c_signal.h" #include #include // for fft_complex #include // for gr_complex #include // for volk_32fc_conjugate_32fc #include #include // for copy_n #include // for abs, pow, floor #include // for complex GpsL2MPcpsAcquisitionFpga::GpsL2MPcpsAcquisitionFpga( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { pcpsconf_fpga_t acq_parameters; configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; LOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.fs_in = fs_in_; acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) doppler_max_ = FLAGS_doppler_max; acq_parameters.doppler_max = doppler_max_; acq_parameters.sampled_ms = 20; unsigned int code_length = std::round(static_cast(fs_in_) / (GPS_L2_M_CODE_RATE_HZ / static_cast(GPS_L2_M_CODE_LENGTH_CHIPS))); acq_parameters.code_length = code_length; // The FPGA can only use FFT lengths that are a power of two. float nbits = ceilf(log2f((float)code_length)); unsigned int nsamples_total = pow(2, nbits); unsigned int select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 0); acq_parameters.select_queue_Fpga = select_queue_Fpga; std::string default_device_name = "/dev/uio0"; std::string device_name = configuration_->property(role + ".devicename", default_device_name); acq_parameters.device_name = device_name; acq_parameters.samples_per_ms = nsamples_total / acq_parameters.sampled_ms; acq_parameters.samples_per_code = nsamples_total; acq_parameters.downsampling_factor = configuration_->property(role + ".downsampling_factor", 1.0); acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 14); acq_parameters.excludelimit = static_cast(std::round(static_cast(fs_in_) / GPS_L2_M_CODE_RATE_HZ)); // compute all the GPS L2C PRN Codes (this is done only once upon the class constructor in order to avoid re-computing the PRN codes every time // a channel is assigned) auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // Direct FFT // allocate memory to compute all the PRNs and compute all the possible codes std::vector> code(nsamples_total); // buffer for the local code auto* fft_codes_padded = static_cast(volk_gnsssdr_malloc(nsamples_total * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_all_fft_codes_ = std::vector(nsamples_total * NUM_PRNs); // memory containing all the possible fft codes for PRN 0 to 32 float max; // temporary maxima search int32_t tmp, tmp2, local_code, fft_data; for (unsigned int PRN = 1; PRN <= NUM_PRNs; PRN++) { gps_l2c_m_code_gen_complex_sampled(code, PRN, fs_in_); // fill in zero padding for (unsigned int s = code_length; s < nsamples_total; s++) { code[s] = std::complex(0.0, 0.0); } std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer fft_if->execute(); // Run the FFT of local code volk_32fc_conjugate_32fc(fft_codes_padded, fft_if->get_outbuf(), nsamples_total); // conjugate values max = 0; // initialize maximum value for (unsigned int i = 0; i < nsamples_total; i++) // search for maxima { if (std::abs(fft_codes_padded[i].real()) > max) { max = std::abs(fft_codes_padded[i].real()); } if (std::abs(fft_codes_padded[i].imag()) > max) { max = std::abs(fft_codes_padded[i].imag()); } } // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs // and package codes in a format that is ready to be written to the FPGA for (uint32_t i = 0; i < nsamples_total; i++) { tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, QUANT_BITS_LOCAL_CODE - 1) - 1) / max)); tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, QUANT_BITS_LOCAL_CODE - 1) - 1) / max)); local_code = (tmp & SELECT_LSBits) | ((tmp2 * SHL_CODE_BITS) & SELECT_MSBbits); // put together the real part and the imaginary part fft_data = local_code & SELECT_ALL_CODE_BITS; d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; // d_all_fft_codes_[i + nsamples_total * (PRN - 1)] = lv_16sc_t(static_cast(floor(fft_codes_padded[i].real() * (pow(2, QUANT_BITS_LOCAL_CODE - 1) - 1) / max)), // static_cast(floor(fft_codes_padded[i].imag() * (pow(2, QUANT_BITS_LOCAL_CODE - 1) - 1) / max))); } } acq_parameters.all_fft_codes = d_all_fft_codes_.data(); acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); channel_ = 0; doppler_step_ = 0; gnss_synchro_ = nullptr; threshold_ = 0.0; // temporary buffers that we can release volk_gnsssdr_free(fft_codes_padded); } void GpsL2MPcpsAcquisitionFpga::stop_acquisition() { } void GpsL2MPcpsAcquisitionFpga::set_threshold(float threshold) { threshold_ = threshold; DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_fpga_->set_threshold(threshold_); } void GpsL2MPcpsAcquisitionFpga::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_fpga_->set_doppler_max(doppler_max_); } // Be aware that Doppler step should be set to 2/(3T) Hz, where T is the coherent integration time (GPS L2 period is 0.02s) // Doppler bin minimum size= 33 Hz void GpsL2MPcpsAcquisitionFpga::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_fpga_->set_doppler_step(doppler_step_); } void GpsL2MPcpsAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_fpga_->set_gnss_synchro(gnss_synchro_); } signed int GpsL2MPcpsAcquisitionFpga::mag() { return acquisition_fpga_->mag(); } void GpsL2MPcpsAcquisitionFpga::init() { acquisition_fpga_->init(); } void GpsL2MPcpsAcquisitionFpga::set_local_code() { acquisition_fpga_->set_local_code(); } void GpsL2MPcpsAcquisitionFpga::reset() { acquisition_fpga_->set_active(true); } void GpsL2MPcpsAcquisitionFpga::set_state(int state) { acquisition_fpga_->set_state(state); } void GpsL2MPcpsAcquisitionFpga::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect } void GpsL2MPcpsAcquisitionFpga::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to diconnect } gr::basic_block_sptr GpsL2MPcpsAcquisitionFpga::get_left_block() { return nullptr; } gr::basic_block_sptr GpsL2MPcpsAcquisitionFpga::get_right_block() { return nullptr; } src/algorithms/acquisition/adapters/gps_l2_m_pcps_acquisition_fpga.h000066400000000000000000000131241352176506000264640ustar00rootroot00000000000000/*! * \file gps_l2_m_pcps_acquisition_fpga.h * \brief Adapts an FPGA-offloaded PCPS acquisition block * to an AcquisitionInterface for GPS L2 M signals * \authors
    *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_FPGA_H_ #define GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_FPGA_H_ #include "channel_fsm.h" #include "pcps_acquisition_fpga.h" #include // for basic_block_sptr, top_block_sptr #include // for lv_16sc_t #include // for size_t #include // for weak_ptr #include // for string #include class Gnss_Synchro; class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA * to an AcquisitionInterface for GPS L2 M signals */ class GpsL2MPcpsAcquisitionFpga : public AcquisitionInterface { public: GpsL2MPcpsAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL2MPcpsAcquisitionFpga() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L2_M_PCPS_Acquisition_Fpga" */ inline std::string implementation() override { return "GPS_L2_M_PCPS_Acquisition_Fpga"; } inline size_t item_size() override { return sizeof(lv_16sc_t); } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_fpga_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_fpga_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L2/M PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: static const uint32_t NUM_PRNs = 32; static const uint32_t QUANT_BITS_LOCAL_CODE = 16; static const uint32_t SELECT_LSBits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t SELECT_MSBbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t SELECT_ALL_CODE_BITS = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t SHL_CODE_BITS = 65536; // shift left by 10 bits ConfigurationInterface* configuration_; pcps_acquisition_fpga_sptr acquisition_fpga_; std::string item_type_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; int64_t fs_in_; std::string dump_filename_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; std::vector d_all_fft_codes_; // memory that contains all the code ffts }; #endif /* GNSS_SDR_GPS_L2_M_PCPS_ACQUISITION_FPGA_H_ */ src/algorithms/acquisition/adapters/gps_l5i_pcps_acquisition.cc000066400000000000000000000306041352176506000254670ustar00rootroot00000000000000/*! * \file gps_l5i_pcps_acquisition.cc * \brief Adapts a PCPS acquisition block to an Acquisition Interface for * GPS L5i signals * \authors
    *
  • Javier Arribas, 2017. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l5i_pcps_acquisition.h" #include "GPS_L5.h" #include "acq_conf.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_l5_signal.h" #include #include #include GpsL5iPcpsAcquisition::GpsL5iPcpsAcquisition( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { configuration_ = configuration; std::string default_item_type = "gr_complex"; std::string default_dump_filename = "./acquisition.mat"; LOG(INFO) << "role " << role; item_type_ = configuration_->property(role + ".item_type", default_item_type); int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); fs_in_ = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters_.fs_in = fs_in_; dump_ = configuration_->property(role + ".dump", false); acq_parameters_.dump = dump_; acq_parameters_.dump_channel = configuration_->property(role + ".dump_channel", 0); blocking_ = configuration_->property(role + ".blocking", true); acq_parameters_.blocking = blocking_; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) { doppler_max_ = FLAGS_doppler_max; } acq_parameters_.doppler_max = doppler_max_; bit_transition_flag_ = configuration_->property(role + ".bit_transition_flag", false); acq_parameters_.bit_transition_flag = bit_transition_flag_; use_CFAR_algorithm_flag_ = configuration_->property(role + ".use_CFAR_algorithm", true); //will be false in future versions acq_parameters_.use_CFAR_algorithm_flag = use_CFAR_algorithm_flag_; max_dwells_ = configuration_->property(role + ".max_dwells", 1); acq_parameters_.max_dwells = max_dwells_; dump_filename_ = configuration_->property(role + ".dump_filename", default_dump_filename); acq_parameters_.dump_filename = dump_filename_; acq_parameters_.sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); } else { item_size_ = sizeof(gr_complex); } acq_parameters_.ms_per_code = 1; acq_parameters_.it_size = item_size_; num_codes_ = acq_parameters_.sampled_ms; acq_parameters_.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters_.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters_.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters_.blocking_on_standby = configuration_->property(role + ".blocking_on_standby", false); acq_parameters_.use_automatic_resampler = configuration_->property("GNSS-SDR.use_acquisition_resampler", false); if (acq_parameters_.use_automatic_resampler == true and item_type_ != "gr_complex") { LOG(WARNING) << "GPS L5 acquisition disabled the automatic resampler feature because its item_type is not set to gr_complex"; acq_parameters_.use_automatic_resampler = false; } if (acq_parameters_.use_automatic_resampler) { if (acq_parameters_.fs_in > GPS_L5_OPT_ACQ_FS_HZ) { acq_parameters_.resampler_ratio = floor(static_cast(acq_parameters_.fs_in) / GPS_L5_OPT_ACQ_FS_HZ); uint32_t decimation = acq_parameters_.fs_in / GPS_L5_OPT_ACQ_FS_HZ; while (acq_parameters_.fs_in % decimation > 0) { decimation--; }; acq_parameters_.resampler_ratio = decimation; acq_parameters_.resampled_fs = acq_parameters_.fs_in / static_cast(acq_parameters_.resampler_ratio); } //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(acq_parameters_.resampled_fs) / (GPS_L5I_CODE_RATE_HZ / GPS_L5I_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(acq_parameters_.resampled_fs) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GPS_L5I_CODE_RATE_HZ) * static_cast(acq_parameters_.resampled_fs))); } else { acq_parameters_.resampled_fs = fs_in_; //--- Find number of samples per spreading code ------------------------- code_length_ = static_cast(std::floor(static_cast(fs_in_) / (GPS_L5I_CODE_RATE_HZ / GPS_L5I_CODE_LENGTH_CHIPS))); acq_parameters_.samples_per_ms = static_cast(fs_in_) * 0.001; acq_parameters_.samples_per_chip = static_cast(ceil((1.0 / GPS_L5I_CODE_RATE_HZ) * static_cast(acq_parameters_.fs_in))); } acq_parameters_.samples_per_code = acq_parameters_.samples_per_ms * static_cast(GPS_L5I_PERIOD * 1000.0); vector_length_ = std::floor(acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms) * (acq_parameters_.bit_transition_flag ? 2 : 1); code_ = std::vector>(vector_length_); acquisition_ = pcps_make_acquisition(acq_parameters_); DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")"; if (item_type_ == "cbyte") { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } channel_ = 0; threshold_ = 0.0; doppler_step_ = 0; doppler_center_ = 0; gnss_synchro_ = nullptr; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 0) { LOG(ERROR) << "This implementation does not provide an output stream"; } } void GpsL5iPcpsAcquisition::stop_acquisition() { } void GpsL5iPcpsAcquisition::set_threshold(float threshold) { float pfa = configuration_->property(role_ + std::to_string(channel_) + ".pfa", 0.0); if (pfa == 0.0) { pfa = configuration_->property(role_ + ".pfa", 0.0); } if (pfa == 0.0) { threshold_ = threshold; } else { threshold_ = calculate_threshold(pfa); } DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; acquisition_->set_threshold(threshold_); } void GpsL5iPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_->set_doppler_max(doppler_max_); } // Be aware that Doppler step should be set to 2/(3T) Hz, where T is the coherent integration time (GPS L2 period is 0.02s) // Doppler bin minimum size= 33 Hz void GpsL5iPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_->set_doppler_step(doppler_step_); } void GpsL5iPcpsAcquisition::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_->set_doppler_center(doppler_center_); } void GpsL5iPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_->set_gnss_synchro(gnss_synchro_); } signed int GpsL5iPcpsAcquisition::mag() { return acquisition_->mag(); } void GpsL5iPcpsAcquisition::init() { acquisition_->init(); } void GpsL5iPcpsAcquisition::set_local_code() { std::vector> code(code_length_); if (acq_parameters_.use_automatic_resampler) { gps_l5i_code_gen_complex_sampled(code, gnss_synchro_->PRN, acq_parameters_.resampled_fs); } else { gps_l5i_code_gen_complex_sampled(code, gnss_synchro_->PRN, fs_in_); } gsl::span code_span(code_.data(), vector_length_); for (unsigned int i = 0; i < num_codes_; i++) { std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data()); } acquisition_->set_local_code(code_.data()); } void GpsL5iPcpsAcquisition::reset() { acquisition_->set_active(true); } void GpsL5iPcpsAcquisition::set_state(int state) { acquisition_->set_state(state); } float GpsL5iPcpsAcquisition::calculate_threshold(float pfa) { // Calculate the threshold unsigned int frequency_bins = 0; for (int doppler = static_cast(-doppler_max_); doppler <= static_cast(doppler_max_); doppler += doppler_step_) { frequency_bins++; } DLOG(INFO) << "Channel " << channel_ << " Pfa = " << pfa; unsigned int ncells = vector_length_ * frequency_bins; double exponent = 1.0 / static_cast(ncells); double val = pow(1.0 - pfa, exponent); auto lambda = double(vector_length_); boost::math::exponential_distribution mydist(lambda); auto threshold = static_cast(quantile(mydist, val)); return threshold; } void GpsL5iPcpsAcquisition::connect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to connect } else if (item_type_ == "cshort") { // nothing to connect } else if (item_type_ == "cbyte") { // Since a byte-based acq implementation is not available, // we just convert cshorts to gr_complex top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->connect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } void GpsL5iPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { if (item_type_ == "gr_complex") { // nothing to disconnect } else if (item_type_ == "cshort") { // nothing to disconnect } else if (item_type_ == "cbyte") { top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1); top_block->disconnect(float_to_complex_, 0, acquisition_, 0); } else { LOG(WARNING) << item_type_ << " unknown acquisition item type"; } } gr::basic_block_sptr GpsL5iPcpsAcquisition::get_left_block() { if (item_type_ == "gr_complex") { return acquisition_; } if (item_type_ == "cshort") { return acquisition_; } if (item_type_ == "cbyte") { return cbyte_to_float_x2_; } LOG(WARNING) << item_type_ << " unknown acquisition item type"; return nullptr; } gr::basic_block_sptr GpsL5iPcpsAcquisition::get_right_block() { return acquisition_; } void GpsL5iPcpsAcquisition::set_resampler_latency(uint32_t latency_samples) { acquisition_->set_resampler_latency(latency_samples); } src/algorithms/acquisition/adapters/gps_l5i_pcps_acquisition.h000066400000000000000000000127711352176506000253360ustar00rootroot00000000000000/*! * \file gps_l5i_pcps_acquisition.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L5i signals * \authors
    *
  • Javier Arribas, 2017. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_H_ #define GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_H_ #include "channel_fsm.h" #include "complex_byte_to_float_x2.h" #include "gnss_synchro.h" #include "pcps_acquisition.h" #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface * for GPS L5i signals */ class GpsL5iPcpsAcquisition : public AcquisitionInterface { public: GpsL5iPcpsAcquisition(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~GpsL5iPcpsAcquisition() = default; inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L5i_PCPS_Acquisition" */ inline std::string implementation() override { return "GPS_L5i_PCPS_Acquisition"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L2/M PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Sets the resampler latency to account it in the acquisition code delay estimation */ void set_resampler_latency(uint32_t latency_samples) override; private: ConfigurationInterface* configuration_; pcps_acquisition_sptr acquisition_; Acq_Conf acq_parameters_; gr::blocks::float_to_complex::sptr float_to_complex_; complex_byte_to_float_x2_sptr cbyte_to_float_x2_; size_t item_size_; std::string item_type_; unsigned int vector_length_; unsigned int code_length_; bool bit_transition_flag_; bool use_CFAR_algorithm_flag_; unsigned int channel_; std::weak_ptr channel_fsm_; float threshold_; unsigned int doppler_max_; unsigned int doppler_step_; int doppler_center_; unsigned int max_dwells_; int64_t fs_in_; bool dump_; bool blocking_; std::string dump_filename_; std::vector> code_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int num_codes_; unsigned int in_streams_; unsigned int out_streams_; float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_H_ */ src/algorithms/acquisition/adapters/gps_l5i_pcps_acquisition_fpga.cc000066400000000000000000000241061352176506000264640ustar00rootroot00000000000000/*! * \file gps_l5i_pcps_acquisition_fpga.cc * \brief Adapts a PCPS acquisition block to an Acquisition Interface for * GPS L5i signals for the FPGA * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l5i_pcps_acquisition_fpga.h" #include "GPS_L5.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "gps_l5_signal.h" #include #include // for fft_complex #include // for gr_complex #include // for volk_32fc_conjugate_32fc #include #include // for copy_n #include // for abs, pow, floor #include // for complex GpsL5iPcpsAcquisitionFpga::GpsL5iPcpsAcquisitionFpga( ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { pcpsconf_fpga_t acq_parameters; configuration_ = configuration; std::string default_dump_filename = "./data/acquisition.dat"; LOG(INFO) << "role " << role; int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 2048000); int64_t fs_in = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; uint32_t downsampling_factor = configuration_->property(role + ".downsampling_factor", 1); acq_parameters.downsampling_factor = downsampling_factor; fs_in = fs_in / downsampling_factor; acq_parameters.fs_in = fs_in; doppler_max_ = configuration->property(role + ".doppler_max", 5000); if (FLAGS_doppler_max != 0) doppler_max_ = FLAGS_doppler_max; acq_parameters.doppler_max = doppler_max_; uint32_t sampled_ms = configuration_->property(role + ".coherent_integration_time_ms", 1); acq_parameters.sampled_ms = sampled_ms; //--- Find number of samples per spreading code ------------------------- auto code_length = static_cast(std::round(static_cast(fs_in) / (GPS_L5I_CODE_RATE_HZ / static_cast(GPS_L5I_CODE_LENGTH_CHIPS)))); acq_parameters.code_length = code_length; // The FPGA can only use FFT lengths that are a power of two. float nbits = ceilf(log2f((float)code_length * 2)); uint32_t nsamples_total = pow(2, nbits); uint32_t select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 1); acq_parameters.select_queue_Fpga = select_queue_Fpga; std::string default_device_name = "/dev/uio0"; std::string device_name = configuration_->property(role + ".devicename", default_device_name); acq_parameters.device_name = device_name; acq_parameters.samples_per_ms = nsamples_total / sampled_ms; acq_parameters.samples_per_code = nsamples_total; acq_parameters.excludelimit = static_cast(1 + ceil((1.0 / GPS_L5I_CODE_RATE_HZ) * static_cast(fs_in))); // compute all the GPS L5 PRN Codes (this is done only once upon the class constructor in order to avoid re-computing the PRN codes every time // a channel is assigned) auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // Direct FFT std::vector> code(nsamples_total); auto* fft_codes_padded = static_cast(volk_gnsssdr_malloc(nsamples_total * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_all_fft_codes_ = std::vector(nsamples_total * NUM_PRNs); // memory containing all the possible fft codes for PRN 0 to 32 float max; // temporary maxima search int32_t tmp, tmp2, local_code, fft_data; for (uint32_t PRN = 1; PRN <= NUM_PRNs; PRN++) { gps_l5i_code_gen_complex_sampled(code, PRN, fs_in); for (uint32_t s = code_length; s < 2 * code_length; s++) { code[s] = code[s - code_length]; } for (uint32_t s = 2 * code_length; s < nsamples_total; s++) { // fill in zero padding code[s] = std::complex(0.0, 0.0); } std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer fft_if->execute(); // Run the FFT of local code volk_32fc_conjugate_32fc(fft_codes_padded, fft_if->get_outbuf(), nsamples_total); // conjugate values max = 0; // initialize maximum value for (uint32_t i = 0; i < nsamples_total; i++) // search for maxima { if (std::abs(fft_codes_padded[i].real()) > max) { max = std::abs(fft_codes_padded[i].real()); } if (std::abs(fft_codes_padded[i].imag()) > max) { max = std::abs(fft_codes_padded[i].imag()); } } // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs // and package codes in a format that is ready to be written to the FPGA for (uint32_t i = 0; i < nsamples_total; i++) { tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, quant_bits_local_code - 1) - 1) / max)); tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, quant_bits_local_code - 1) - 1) / max)); local_code = (tmp & select_lsbits) | ((tmp2 * shl_code_bits) & select_msbits); // put together the real part and the imaginary part fft_data = local_code & select_all_code_bits; d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; } } acq_parameters.all_fft_codes = d_all_fft_codes_.data(); // reference for the FPGA FFT-IFFT attenuation factor acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 13); acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); acq_parameters.max_num_acqs = configuration_->property(role + ".max_num_acqs", 2); acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); channel_ = 0; doppler_step_ = 0; gnss_synchro_ = nullptr; // temporary buffers that we can release volk_gnsssdr_free(fft_codes_padded); } void GpsL5iPcpsAcquisitionFpga::stop_acquisition() { // this command causes the SW to reset the HW. acquisition_fpga_->reset_acquisition(); } void GpsL5iPcpsAcquisitionFpga::set_threshold(float threshold) { DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold; acquisition_fpga_->set_threshold(threshold); } void GpsL5iPcpsAcquisitionFpga::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; acquisition_fpga_->set_doppler_max(doppler_max_); } // Be aware that Doppler step should be set to 2/(3T) Hz, where T is the coherent integration time (GPS L2 period is 0.02s) // Doppler bin minimum size= 33 Hz void GpsL5iPcpsAcquisitionFpga::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; acquisition_fpga_->set_doppler_step(doppler_step_); } void GpsL5iPcpsAcquisitionFpga::set_doppler_center(int doppler_center) { doppler_center_ = doppler_center; acquisition_fpga_->set_doppler_center(doppler_center_); } void GpsL5iPcpsAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; acquisition_fpga_->set_gnss_synchro(gnss_synchro_); } signed int GpsL5iPcpsAcquisitionFpga::mag() { return acquisition_fpga_->mag(); } void GpsL5iPcpsAcquisitionFpga::init() { acquisition_fpga_->init(); } void GpsL5iPcpsAcquisitionFpga::set_local_code() { acquisition_fpga_->set_local_code(); } void GpsL5iPcpsAcquisitionFpga::reset() { acquisition_fpga_->set_active(true); } void GpsL5iPcpsAcquisitionFpga::set_state(int state) { acquisition_fpga_->set_state(state); } void GpsL5iPcpsAcquisitionFpga::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to connect } void GpsL5iPcpsAcquisitionFpga::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr GpsL5iPcpsAcquisitionFpga::get_left_block() { return nullptr; } gr::basic_block_sptr GpsL5iPcpsAcquisitionFpga::get_right_block() { return nullptr; } src/algorithms/acquisition/adapters/gps_l5i_pcps_acquisition_fpga.h000066400000000000000000000142551352176506000263320ustar00rootroot00000000000000/*! * \file gps_l5i_pcps_acquisition_fpga.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * GPS L5i signals for the FPGA * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_FPGA_H_ #define GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_FPGA_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include "pcps_acquisition_fpga.h" #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA * to an AcquisitionInterface for GPS L5i signals */ class GpsL5iPcpsAcquisitionFpga : public AcquisitionInterface { public: /*! * \brief Constructor */ GpsL5iPcpsAcquisitionFpga(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); /*! * \brief Destructor */ ~GpsL5iPcpsAcquisitionFpga() = default; /*! * \brief Role */ inline std::string role() override { return role_; } /*! * \brief Returns "GPS_L5i_PCPS_Acquisition_Fpga" */ inline std::string implementation() override { return "GPS_L5i_PCPS_Acquisition_Fpga"; } /*! * \brief Returns size of lv_16sc_t */ inline size_t item_size() override { return sizeof(int16_t); } /*! * \brief Connect */ void connect(gr::top_block_sptr top_block) override; /*! * \brief Disconnect */ void disconnect(gr::top_block_sptr top_block) override; /*! * \brief Get left block */ gr::basic_block_sptr get_left_block() override; /*! * \brief Get right block */ gr::basic_block_sptr get_right_block() override; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to efficiently exchange synchronization data between acquisition and * tracking blocks */ void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; /*! * \brief Set acquisition channel unique ID */ inline void set_channel(unsigned int channel) override { channel_ = channel; acquisition_fpga_->set_channel(channel_); } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) override { channel_fsm_ = channel_fsm; acquisition_fpga_->set_channel_fsm(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm */ void set_threshold(float threshold) override; /*! * \brief Set maximum Doppler off grid search */ void set_doppler_max(unsigned int doppler_max) override; /*! * \brief Set Doppler steps for the grid search */ void set_doppler_step(unsigned int doppler_step) override; /*! * \brief Set Doppler center for the grid search */ void set_doppler_center(int doppler_center) override; /*! * \brief Initializes acquisition algorithm. */ void init() override; /*! * \brief Sets local code for GPS L5 PCPS acquisition algorithm. */ void set_local_code() override; /*! * \brief Returns the maximum peak of grid search */ signed int mag() override; /*! * \brief Restart acquisition algorithm */ void reset() override; /*! * \brief If state = 1, it forces the block to start acquiring from the first sample */ void set_state(int state) override; /*! * \brief Stop running acquisition */ void stop_acquisition() override; /*! * \brief Set resampler latency */ void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; private: static const uint32_t NUM_PRNs = 32; // the following flags are FPGA-specific and they are using arrange the values of the fft of the local code in the way the FPGA // expects. This arrangement is done in the initialisation to avoid consuming unnecessary clock cycles during tracking. static const uint32_t quant_bits_local_code = 16; static const uint32_t select_lsbits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t select_msbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t select_all_code_bits = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t shl_code_bits = 65536; // shift left by 10 bits ConfigurationInterface* configuration_; pcps_acquisition_fpga_sptr acquisition_fpga_; std::string item_type_; uint32_t channel_; std::weak_ptr channel_fsm_; uint32_t doppler_max_; uint32_t doppler_step_; int32_t doppler_center_; std::string dump_filename_; Gnss_Synchro* gnss_synchro_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; std::vector d_all_fft_codes_; // memory that contains all the code ffts float calculate_threshold(float pfa); }; #endif /* GNSS_SDR_GPS_L5I_PCPS_ACQUISITION_FPGA_H_ */ src/algorithms/acquisition/gnuradio_blocks/000077500000000000000000000000001352176506000215205ustar00rootroot00000000000000src/algorithms/acquisition/gnuradio_blocks/CMakeLists.txt000066400000000000000000000072021352176506000242610ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(ACQ_GR_BLOCKS_SOURCES pcps_acquisition.cc pcps_assisted_acquisition_cc.cc pcps_acquisition_fine_doppler_cc.cc pcps_tong_acquisition_cc.cc pcps_cccwsr_acquisition_cc.cc pcps_quicksync_acquisition_cc.cc galileo_pcps_8ms_acquisition_cc.cc galileo_e5a_noncoherent_iq_acquisition_caf_cc.cc ) set(ACQ_GR_BLOCKS_HEADERS pcps_acquisition.h pcps_assisted_acquisition_cc.h pcps_acquisition_fine_doppler_cc.h pcps_tong_acquisition_cc.h pcps_cccwsr_acquisition_cc.h pcps_quicksync_acquisition_cc.h galileo_pcps_8ms_acquisition_cc.h galileo_e5a_noncoherent_iq_acquisition_caf_cc.h ) if(ENABLE_FPGA) set(ACQ_GR_BLOCKS_SOURCES ${ACQ_GR_BLOCKS_SOURCES} pcps_acquisition_fpga.cc) set(ACQ_GR_BLOCKS_HEADERS ${ACQ_GR_BLOCKS_HEADERS} pcps_acquisition_fpga.h) endif() if(ENABLE_OPENCL) set(ACQ_GR_BLOCKS_SOURCES ${ACQ_GR_BLOCKS_SOURCES} pcps_opencl_acquisition_cc.cc) set(ACQ_GR_BLOCKS_HEADERS ${ACQ_GR_BLOCKS_HEADERS} pcps_opencl_acquisition_cc.h) endif() list(SORT ACQ_GR_BLOCKS_HEADERS) list(SORT ACQ_GR_BLOCKS_SOURCES) source_group(Headers FILES ${ACQ_GR_BLOCKS_HEADERS}) add_library(acquisition_gr_blocks ${ACQ_GR_BLOCKS_SOURCES} ${ACQ_GR_BLOCKS_HEADERS}) if(${FILESYSTEM_FOUND}) target_compile_definitions(acquisition_gr_blocks PRIVATE -DHAS_STD_FILESYSTEM=1) if(${find_experimental}) target_compile_definitions(acquisition_gr_blocks PRIVATE -DHAS_STD_FILESYSTEM_EXPERIMENTAL=1) endif() target_link_libraries(acquisition_gr_blocks PRIVATE std::filesystem) else() target_link_libraries(acquisition_gr_blocks PRIVATE Boost::filesystem) endif() target_link_libraries(acquisition_gr_blocks PUBLIC algorithms_libs acquisition_libs channel_libs core_system_parameters Armadillo::armadillo Gnuradio::runtime Gnuradio::fft Volk::volk PRIVATE Gflags::gflags Glog::glog Matio::matio Volkgnsssdr::volkgnsssdr ) target_include_directories(acquisition_gr_blocks PUBLIC ${CMAKE_SOURCE_DIR}/src/algorithms/libs PRIVATE ${CMAKE_SOURCE_DIR}/src/core/receiver ) if(CMAKE_BUILD_TYPE MATCHES Rel) target_compile_definitions(acquisition_gr_blocks PUBLIC -DARMA_NO_BOUND_CHECKING=1 ) endif() if(ENABLE_OPENCL) target_link_libraries(acquisition_gr_blocks PUBLIC OpenCL::OpenCL) target_include_directories(acquisition_gr_blocks PUBLIC ${CMAKE_SOURCE_DIR}/src/algorithms/libs/opencl ) endif() if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(acquisition_gr_blocks PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET acquisition_gr_blocks APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ $ ) src/algorithms/acquisition/gnuradio_blocks/galileo_e5a_noncoherent_iq_acquisition_caf_cc.cc000066400000000000000000001151131352176506000331600ustar00rootroot00000000000000/*! * \file galileo_e5a_noncoherent_iq_acquisition_caf_cc.cc * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Marc Sales, 2014. marcsales92(at)gmail.com * \based on work from: *
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e5a_noncoherent_iq_acquisition_caf_cc.h" #include #include #include #include #include #include #include galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr galileo_e5a_noncoherentIQ_make_acquisition_caf_cc( unsigned int sampled_ms, unsigned int max_dwells, unsigned int doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename, bool both_signal_components_, int CAF_window_hz_, int Zero_padding_) { return galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr( new galileo_e5a_noncoherentIQ_acquisition_caf_cc(sampled_ms, max_dwells, doppler_max, fs_in, samples_per_ms, samples_per_code, bit_transition_flag, dump, std::move(dump_filename), both_signal_components_, CAF_window_hz_, Zero_padding_)); } galileo_e5a_noncoherentIQ_acquisition_caf_cc::galileo_e5a_noncoherentIQ_acquisition_caf_cc( unsigned int sampled_ms, unsigned int max_dwells, unsigned int doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename, bool both_signal_components_, int CAF_window_hz_, int Zero_padding_) : gr::block("galileo_e5a_noncoherentIQ_acquisition_caf_cc", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, sizeof(gr_complex))) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_max_dwells = max_dwells; d_well_count = 0; d_doppler_max = doppler_max; if (Zero_padding_ > 0) { d_sampled_ms = 1; } else { d_sampled_ms = sampled_ms; } d_fft_size = sampled_ms * d_samples_per_ms; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_bit_transition_flag = bit_transition_flag; d_buffer_count = 0; d_both_signal_components = both_signal_components_; d_CAF_window_hz = CAF_window_hz_; d_inbuffer.reserve(d_fft_size); d_fft_code_I_A.reserve(d_fft_size); d_magnitudeIA.reserve(d_fft_size); if (d_both_signal_components == true) { d_fft_code_Q_A.reserve(d_fft_size); d_magnitudeQA.reserve(d_fft_size); } // IF COHERENT INTEGRATION TIME > 1 if (d_sampled_ms > 1) { d_fft_code_I_B.reserve(d_fft_size); d_magnitudeIB.reserve(d_fft_size); if (d_both_signal_components == true) { d_fft_code_Q_B.reserve(d_fft_size); d_magnitudeQB.reserve(d_fft_size); } } // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_doppler_resolution = 0; d_threshold = 0; d_doppler_step = 250; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_channel = 0; d_gr_stream_buffer = 0; } galileo_e5a_noncoherentIQ_acquisition_caf_cc::~galileo_e5a_noncoherentIQ_acquisition_caf_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } void galileo_e5a_noncoherentIQ_acquisition_caf_cc::set_local_code(std::complex *codeI, std::complex *codeQ) { // DATA SIGNAL // Three replicas of data primary code. CODE A: (1,1,1) memcpy(d_fft_if->get_inbuf(), codeI, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_I_A.data(), d_fft_if->get_outbuf(), d_fft_size); // SAME FOR PILOT SIGNAL if (d_both_signal_components == true) { // Three replicas of pilot primary code. CODE A: (1,1,1) memcpy(d_fft_if->get_inbuf(), codeQ, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_Q_A.data(), d_fft_if->get_outbuf(), d_fft_size); } // IF INTEGRATION TIME > 1 code, we need to evaluate the other possible combination // Note: max integration time allowed = 3ms (dealt in adapter) if (d_sampled_ms > 1) { // DATA CODE B: First replica is inverted (0,1,1) volk_32fc_s32fc_multiply_32fc(&(d_fft_if->get_inbuf())[0], &codeI[0], gr_complex(-1, 0), d_samples_per_code); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_I_B.data(), d_fft_if->get_outbuf(), d_fft_size); if (d_both_signal_components == true) { // PILOT CODE B: First replica is inverted (0,1,1) volk_32fc_s32fc_multiply_32fc(&(d_fft_if->get_inbuf())[0], &codeQ[0], gr_complex(-1, 0), d_samples_per_code); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_Q_B.data(), d_fft_if->get_outbuf(), d_fft_size); } } } void galileo_e5a_noncoherentIQ_acquisition_caf_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; const double GALILEO_TWO_PI = 6.283185307179600; // Count the number of bins d_num_doppler_bins = 0; for (int doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); for (unsigned int doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = GALILEO_TWO_PI * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); } /* CAF Filtering to resolve doppler ambiguity. Phase and quadrature must be processed * separately before non-coherent integration */ if (d_CAF_window_hz > 0) { d_CAF_vector.reserve(d_num_doppler_bins); d_CAF_vector_I.reserve(d_num_doppler_bins); if (d_both_signal_components == true) { d_CAF_vector_Q.reserve(d_num_doppler_bins); } } } void galileo_e5a_noncoherentIQ_acquisition_caf_cc::set_state(int state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int galileo_e5a_noncoherentIQ_acquisition_caf_cc::general_work(int noutput_items __attribute__((unused)), gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { /* * By J.Arribas, L.Esteve, M.Molina and M.Sales * Acquisition strategy (Kay Borre book + CFAR threshold): * 1. Compute the input signal power estimation * 2. Doppler serial search loop * 3. Perform the FFT-based circular convolution (parallel time search) * 4. OPTIONAL: CAF filter to avoid doppler ambiguity * 5. Record the maximum peak and the associated synchronization parameters * 6. Compute the test statistics and compare to the threshold * 7. Declare positive or negative acquisition using a message port */ int acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL /* States: 0 Stop Channel * 1 Load the buffer until it reaches fft_size * 2 Acquisition algorithm * 3 Positive acquisition * 4 Negative acquisition */ switch (d_state) { case 0: { if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_state = 1; } d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; } case 1: { const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer unsigned int buff_increment; if ((ninput_items[0] + d_buffer_count) <= d_fft_size) { buff_increment = ninput_items[0]; } else { buff_increment = d_fft_size - d_buffer_count; } memcpy(&d_inbuffer[d_buffer_count], in, sizeof(gr_complex) * buff_increment); // If buffer will be full in next iteration if (d_buffer_count >= (d_fft_size - d_gr_stream_buffer)) { d_state = 2; } d_buffer_count += buff_increment; d_sample_counter += static_cast(buff_increment); // sample counter consume_each(buff_increment); break; } case 2: { // Fill last part of the buffer and reset counter const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer if (d_buffer_count < d_fft_size) { memcpy(&d_inbuffer[d_buffer_count], in, sizeof(gr_complex) * (d_fft_size - d_buffer_count)); } d_sample_counter += static_cast(d_fft_size - d_buffer_count); // sample counter // initialize acquisition algorithm int doppler; uint32_t indext = 0; uint32_t indext_IA = 0; uint32_t indext_IB = 0; uint32_t indext_QA = 0; uint32_t indext_QB = 0; float magt = 0.0; float magt_IA = 0.0; float magt_IB = 0.0; float magt_QA = 0.0; float magt_QB = 0.0; float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); d_input_power = 0.0; d_mag = 0.0; d_well_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitudeIA.data(), d_inbuffer.data(), d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitudeIA.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); // 2- Doppler frequency search loop for (unsigned int doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), d_inbuffer.data(), d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // CODE IA // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_I_A.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Search maximum volk_32fc_magnitude_squared_32f(d_magnitudeIA.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_IA, d_magnitudeIA.data(), d_fft_size); // Normalize the maximum value to correct the scale factor introduced by FFTW magt_IA = d_magnitudeIA[indext_IA] / (fft_normalization_factor * fft_normalization_factor); if (d_both_signal_components == true) { // REPEAT FOR ALL CODES. CODE_QA volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_Q_A.data(), d_fft_size); d_ifft->execute(); volk_32fc_magnitude_squared_32f(d_magnitudeQA.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_QA, d_magnitudeQA.data(), d_fft_size); magt_QA = d_magnitudeQA[indext_QA] / (fft_normalization_factor * fft_normalization_factor); } if (d_sampled_ms > 1) // If Integration time > 1 code { // REPEAT FOR ALL CODES. CODE_IB volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_I_B.data(), d_fft_size); d_ifft->execute(); volk_32fc_magnitude_squared_32f(d_magnitudeIB.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_IB, d_magnitudeIB.data(), d_fft_size); magt_IB = d_magnitudeIB[indext_IB] / (fft_normalization_factor * fft_normalization_factor); if (d_both_signal_components == true) { // REPEAT FOR ALL CODES. CODE_QB volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_Q_B.data(), d_fft_size); d_ifft->execute(); volk_32fc_magnitude_squared_32f(d_magnitudeQB.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_QB, d_magnitudeQB.data(), d_fft_size); magt_QB = d_magnitudeIB[indext_QB] / (fft_normalization_factor * fft_normalization_factor); } } // Integrate noncoherently the two best combinations (I² + Q²) // and store the result in the I channel. // If CAF filter to resolve doppler ambiguity is needed, // peak is stored before non-coherent integration. if (d_sampled_ms > 1) // T_integration > 1 code { if (magt_IA >= magt_IB) { if (d_CAF_window_hz > 0) { d_CAF_vector_I[doppler_index] = d_magnitudeIA[indext_IA]; } if (d_both_signal_components) { // Integrate non-coherently I+Q if (magt_QA >= magt_QB) { if (d_CAF_window_hz > 0) { d_CAF_vector_Q[doppler_index] = d_magnitudeQA[indext_QA]; } for (unsigned int i = 0; i < d_fft_size; i++) { d_magnitudeIA[i] += d_magnitudeQA[i]; } } else { if (d_CAF_window_hz > 0) { d_CAF_vector_Q[doppler_index] = d_magnitudeQB[indext_QB]; } for (unsigned int i = 0; i < d_fft_size; i++) { d_magnitudeIA[i] += d_magnitudeQB[i]; } } } volk_gnsssdr_32f_index_max_32u(&indext, d_magnitudeIA.data(), d_fft_size); magt = d_magnitudeIA[indext] / (fft_normalization_factor * fft_normalization_factor); } else { if (d_CAF_window_hz > 0) { d_CAF_vector_I[doppler_index] = d_magnitudeIB[indext_IB]; } if (d_both_signal_components) { // Integrate non-coherently I+Q if (magt_QA >= magt_QB) { if (d_CAF_window_hz > 0) { d_CAF_vector_Q[doppler_index] = d_magnitudeQA[indext_QA]; } for (unsigned int i = 0; i < d_fft_size; i++) { d_magnitudeIB[i] += d_magnitudeQA[i]; } } else { if (d_CAF_window_hz > 0) { d_CAF_vector_Q[doppler_index] = d_magnitudeQB[indext_QB]; } for (unsigned int i = 0; i < d_fft_size; i++) { d_magnitudeIB[i] += d_magnitudeQB[i]; } } } volk_gnsssdr_32f_index_max_32u(&indext, d_magnitudeIB.data(), d_fft_size); magt = d_magnitudeIB[indext] / (fft_normalization_factor * fft_normalization_factor); } } else { if (d_CAF_window_hz > 0) { d_CAF_vector_I[doppler_index] = d_magnitudeIA[indext_IA]; } if (d_both_signal_components) { if (d_CAF_window_hz > 0) { d_CAF_vector_Q[doppler_index] = d_magnitudeQA[indext_QA]; } // NON-Coherent integration of only 1 code for (unsigned int i = 0; i < d_fft_size; i++) { d_magnitudeIA[i] += d_magnitudeQA[i]; } } volk_gnsssdr_32f_index_max_32u(&indext, d_magnitudeIA.data(), d_fft_size); magt = d_magnitudeIA[indext] / (fft_normalization_factor * fft_normalization_factor); } // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; // In case that d_bit_transition_flag = true, we compare the potentially // new maximum test statistics (d_mag/d_input_power) with the value in // d_test_statistics. When the second dwell is being processed, the value // of d_mag/d_input_power could be lower than d_test_statistics (i.e, // the maximum test statistics in the previous dwell is greater than // current d_mag/d_input_power). Note that d_test_statistics is not // restarted between consecutive dwells in multidwell operation. if (d_test_statistics < (d_mag / d_input_power) || !d_bit_transition_flag) { d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; // 5- Compute the test statistics and compare to the threshold d_test_statistics = d_mag / d_input_power; } } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = sizeof(float) * (d_fft_size); // noncomplex file write filename.str(""); filename << "../data/test_statistics_E5a_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); if (d_sampled_ms > 1) // If integration time > 1 code { if (magt_IA >= magt_IB) { d_dump_file.write(reinterpret_cast(d_magnitudeIA.data()), n); } else { d_dump_file.write(reinterpret_cast(d_magnitudeIB.data()), n); } } else { d_dump_file.write(reinterpret_cast(d_magnitudeIA.data()), n); } d_dump_file.close(); } } // std::cout << "d_mag " << d_mag << ".d_sample_counter " << d_sample_counter << ". acq delay " << d_gnss_synchro->Acq_delay_samples<< " indext "<< indext << std::endl; // 6 OPTIONAL: CAF filter to avoid Doppler ambiguity in bit transition. if (d_CAF_window_hz > 0) { int CAF_bins_half; std::array accum{}; CAF_bins_half = d_CAF_window_hz / (2 * d_doppler_step); float weighting_factor; weighting_factor = 0.5 / static_cast(CAF_bins_half); // weighting_factor = 0; // std::cout << "weighting_factor " << weighting_factor << std::endl; // Initialize first iterations for (int doppler_index = 0; doppler_index < CAF_bins_half; doppler_index++) { d_CAF_vector[doppler_index] = 0; for (int i = 0; i < CAF_bins_half + doppler_index + 1; i++) { d_CAF_vector[doppler_index] += d_CAF_vector_I[i] * (1 - weighting_factor * static_cast((doppler_index - i))); } d_CAF_vector[doppler_index] /= 1 + CAF_bins_half + doppler_index - weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2 - weighting_factor * doppler_index * (doppler_index + 1) / 2; // triangles = [n*(n+1)/2] if (d_both_signal_components) { accum[0] = 0; for (int i = 0; i < CAF_bins_half + doppler_index + 1; i++) { accum[0] += d_CAF_vector_Q[i] * (1 - weighting_factor * static_cast(abs(doppler_index - i))); } accum[0] /= 1 + CAF_bins_half + doppler_index - weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2 - weighting_factor * doppler_index * (doppler_index + 1) / 2; // triangles = [n*(n+1)/2] d_CAF_vector[doppler_index] += accum[0]; } } // Body loop for (unsigned int doppler_index = CAF_bins_half; doppler_index < d_num_doppler_bins - CAF_bins_half; doppler_index++) { d_CAF_vector[doppler_index] = 0; for (int i = doppler_index - CAF_bins_half; i < static_cast(doppler_index + CAF_bins_half + 1); i++) { d_CAF_vector[doppler_index] += d_CAF_vector_I[i] * (1 - weighting_factor * static_cast((doppler_index - i))); } d_CAF_vector[doppler_index] /= 1 + 2 * CAF_bins_half - 2 * weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2; if (d_both_signal_components) { accum[0] = 0; for (int i = doppler_index - CAF_bins_half; i < static_cast(doppler_index + CAF_bins_half + 1); i++) { accum[0] += d_CAF_vector_Q[i] * (1 - weighting_factor * static_cast((doppler_index - i))); } accum[0] /= 1 + 2 * CAF_bins_half - 2 * weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2; d_CAF_vector[doppler_index] += accum[0]; } } // Final iterations for (int doppler_index = d_num_doppler_bins - CAF_bins_half; doppler_index < static_cast(d_num_doppler_bins); doppler_index++) { d_CAF_vector[doppler_index] = 0; for (int i = doppler_index - CAF_bins_half; i < static_cast(d_num_doppler_bins); i++) { d_CAF_vector[doppler_index] += d_CAF_vector_I[i] * (1 - weighting_factor * (abs(doppler_index - i))); } d_CAF_vector[doppler_index] /= 1 + CAF_bins_half + (d_num_doppler_bins - doppler_index - 1) - weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2 - weighting_factor * (d_num_doppler_bins - doppler_index - 1) * (d_num_doppler_bins - doppler_index) / 2; if (d_both_signal_components) { accum[0] = 0; for (int i = doppler_index - CAF_bins_half; i < static_cast(d_num_doppler_bins); i++) { accum[0] += d_CAF_vector_Q[i] * (1 - weighting_factor * (abs(doppler_index - i))); } accum[0] /= 1 + CAF_bins_half + (d_num_doppler_bins - doppler_index - 1) - weighting_factor * CAF_bins_half * (CAF_bins_half + 1) / 2 - weighting_factor * (d_num_doppler_bins - doppler_index - 1) * (d_num_doppler_bins - doppler_index) / 2; d_CAF_vector[doppler_index] += accum[0]; } } // Recompute the maximum doppler peak volk_gnsssdr_32f_index_max_32u(&indext, d_CAF_vector.data(), d_num_doppler_bins); doppler = -static_cast(d_doppler_max) + d_doppler_step * indext; d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); // Dump if required, appended at the end of the file if (d_dump) { std::stringstream filename; std::streamsize n = sizeof(float) * (d_num_doppler_bins); // noncomplex file write filename.str(""); filename << "../data/test_statistics_E5a_sat_" << d_gnss_synchro->PRN << "_CAF.dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_CAF_vector.data()), n); d_dump_file.close(); } } if (d_well_count == d_max_dwells) { if (d_test_statistics > d_threshold) { d_state = 3; // Positive acquisition } else { d_state = 4; // Negative acquisition } } else { d_state = 1; } consume_each(d_fft_size - d_buffer_count); d_buffer_count = 0; break; } case 3: { // 7.1- Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; } case 4: { // 7.2- Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } } return 0; } src/algorithms/acquisition/gnuradio_blocks/galileo_e5a_noncoherent_iq_acquisition_caf_cc.h000066400000000000000000000200511352176506000330160ustar00rootroot00000000000000/*! * \file galileo_e5a_noncoherent_iq_acquisition_caf_cc.h * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for * Galileo E5a data and pilot Signals * \author Marc Sales, 2014. marcsales92(at)gmail.com * \based on work from: *
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_CC_H_ #define GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include class galileo_e5a_noncoherentIQ_acquisition_caf_cc; using galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr = boost::shared_ptr; galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr galileo_e5a_noncoherentIQ_make_acquisition_caf_cc( unsigned int sampled_ms, unsigned int max_dwells, unsigned int doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename, bool both_signal_components_, int CAF_window_hz_, int Zero_padding_); /*! * \brief This class implements a Parallel Code Phase Search Acquisition. * * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", * Algorithm 1, for a pseudocode description of this implementation. */ class galileo_e5a_noncoherentIQ_acquisition_caf_cc : public gr::block { public: /*! * \brief Default destructor. */ ~galileo_e5a_noncoherentIQ_acquisition_caf_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline unsigned int mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code, std::complex* codeQ); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(unsigned int channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(unsigned int doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(unsigned int doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend galileo_e5a_noncoherentIQ_acquisition_caf_cc_sptr galileo_e5a_noncoherentIQ_make_acquisition_caf_cc( unsigned int sampled_ms, unsigned int max_dwells, unsigned int doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename, bool both_signal_components_, int CAF_window_hz_, int Zero_padding_); galileo_e5a_noncoherentIQ_acquisition_caf_cc( unsigned int sampled_ms, unsigned int max_dwells, unsigned int doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename, bool both_signal_components_, int CAF_window_hz_, int Zero_padding_); void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift, int doppler_offset); float estimate_input_power(gr_complex* in); std::weak_ptr d_channel_fsm; int64_t d_fs_in; int d_samples_per_ms; int d_sampled_ms; int d_samples_per_code; unsigned int d_doppler_resolution; float d_threshold; std::string d_satellite_str; unsigned int d_doppler_max; unsigned int d_doppler_step; unsigned int d_max_dwells; unsigned int d_well_count; unsigned int d_fft_size; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; unsigned int d_num_doppler_bins; std::vector d_fft_code_I_A; std::vector d_fft_code_I_B; std::vector d_fft_code_Q_A; std::vector d_fft_code_Q_B; std::vector d_inbuffer; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; unsigned int d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitudeIA; std::vector d_magnitudeIB; std::vector d_magnitudeQA; std::vector d_magnitudeQB; float d_input_power; float d_test_statistics; bool d_bit_transition_flag; std::ofstream d_dump_file; bool d_active; int d_state; bool d_dump; bool d_both_signal_components; int d_CAF_window_hz; std::vector d_CAF_vector; std::vector d_CAF_vector_I; std::vector d_CAF_vector_Q; unsigned int d_channel; std::string d_dump_filename; unsigned int d_buffer_count; unsigned int d_gr_stream_buffer; }; #endif /* GALILEO_E5A_NONCOHERENT_IQ_ACQUISITION_CAF_CC_H_ */ src/algorithms/acquisition/gnuradio_blocks/galileo_pcps_8ms_acquisition_cc.cc000066400000000000000000000417221352176506000303420ustar00rootroot00000000000000/*! * \file galileo_pcps_8ms_acquisition_cc.cc * \brief This class implements a Parallel Code Phase Search Acquisition for * Galileo E1 signals with coherent integration time = 8 ms (two codes) * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_pcps_8ms_acquisition_cc.h" #include #include #include #include #include #include #include galileo_pcps_8ms_acquisition_cc_sptr galileo_pcps_8ms_make_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename) { return galileo_pcps_8ms_acquisition_cc_sptr( new galileo_pcps_8ms_acquisition_cc(sampled_ms, max_dwells, doppler_max, fs_in, samples_per_ms, samples_per_code, dump, std::move(dump_filename))); } galileo_pcps_8ms_acquisition_cc::galileo_pcps_8ms_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename) : gr::block("galileo_pcps_8ms_acquisition_cc", gr::io_signature::make(1, 1, sizeof(gr_complex) * sampled_ms * samples_per_ms), gr::io_signature::make(0, 0, sizeof(gr_complex) * sampled_ms * samples_per_ms)) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_sampled_ms = sampled_ms; d_max_dwells = max_dwells; d_well_count = 0; d_doppler_max = doppler_max; d_fft_size = d_sampled_ms * d_samples_per_ms; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_fft_code_A = std::vector(d_fft_size, lv_cmake(0.0F, 0.0F)); d_fft_code_B = std::vector(d_fft_size, lv_cmake(0.0F, 0.0F)); d_magnitude = std::vector(d_fft_size, 0.0F); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_doppler_resolution = 0; d_threshold = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_channel = 0; } galileo_pcps_8ms_acquisition_cc::~galileo_pcps_8ms_acquisition_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } void galileo_pcps_8ms_acquisition_cc::set_local_code(std::complex *code) { // code A: two replicas of a primary code memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_A.data(), d_fft_if->get_outbuf(), d_fft_size); // code B: two replicas of a primary code; the second replica is inverted. volk_32fc_s32fc_multiply_32fc(&(d_fft_if->get_inbuf())[d_samples_per_code], &code[d_samples_per_code], gr_complex(-1, 0), d_samples_per_code); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_B.data(), d_fft_if->get_outbuf(), d_fft_size); } void galileo_pcps_8ms_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; const double GALILEO_TWO_PI = 6.283185307179600; // Count the number of bins d_num_doppler_bins = 0; for (auto doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int32_t doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = static_cast(GALILEO_TWO_PI) * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); } } void galileo_pcps_8ms_acquisition_cc::set_state(int32_t state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int galileo_pcps_8ms_acquisition_cc::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { int32_t acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL switch (d_state) { case 0: { if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_state = 1; } d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; } case 1: { // initialize acquisition algorithm int32_t doppler; uint32_t indext = 0; uint32_t indext_A = 0; uint32_t indext_B = 0; float magt = 0.0; float magt_A = 0.0; float magt_B = 0.0; const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); d_input_power = 0.0; d_mag = 0.0; d_sample_counter += static_cast(d_fft_size); // sample counter d_well_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitude.data(), in, d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); // 2- Doppler frequency search loop for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code A reference using SIMD operations with // VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_A.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Search maximum volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_A, d_magnitude.data(), d_fft_size); // Normalize the maximum value to correct the scale factor introduced by FFTW magt_A = d_magnitude[indext_A] / (fft_normalization_factor * fft_normalization_factor); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code B reference using SIMD operations with // VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_B.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Search maximum volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_B, d_magnitude.data(), d_fft_size); // Normalize the maximum value to correct the scale factor introduced by FFTW magt_B = d_magnitude[indext_B] / (fft_normalization_factor * fft_normalization_factor); // Take the greater magnitude if (magt_A >= magt_B) { magt = magt_A; indext = indext_A; } else { magt = magt_B; indext = indext_B; } // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_ifft->get_outbuf()), n); //write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } // 5- Compute the test statistics and compare to the threshold //d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; d_test_statistics = d_mag / d_input_power; if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else if (d_well_count == d_max_dwells) { d_state = 3; // Negative acquisition } consume_each(1); break; } case 2: { // 6.1- Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += d_fft_size * ninput_items[0]; // sample counter consume_each(ninput_items[0]); acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } case 3: { // 6.2- Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } } return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/galileo_pcps_8ms_acquisition_cc.h000066400000000000000000000152521352176506000302030ustar00rootroot00000000000000/*! * \file galileo_pcps_8ms_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition for * Galileo E1 signals with coherent integration time = 8 ms (two codes) * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_8MS_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_8MS_ACQUISITION_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include class galileo_pcps_8ms_acquisition_cc; using galileo_pcps_8ms_acquisition_cc_sptr = boost::shared_ptr; galileo_pcps_8ms_acquisition_cc_sptr galileo_pcps_8ms_make_acquisition_cc(uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition for * Galileo E1 signals with coherent integration time = 8 ms (two codes) */ class galileo_pcps_8ms_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~galileo_pcps_8ms_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend galileo_pcps_8ms_acquisition_cc_sptr galileo_pcps_8ms_make_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); galileo_pcps_8ms_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); void calculate_magnitudes( gr_complex* fft_begin, int32_t doppler_shift, int32_t doppler_offset); int64_t d_fs_in; int32_t d_samples_per_ms; int32_t d_samples_per_code; uint32_t d_doppler_resolution; float d_threshold; std::string d_satellite_str; uint32_t d_doppler_max; uint32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_max_dwells; uint32_t d_well_count; uint32_t d_fft_size; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; uint32_t d_num_doppler_bins; std::vector d_fft_code_A; std::vector d_fft_code_B; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitude; float d_input_power; float d_test_statistics; std::ofstream d_dump_file; bool d_active; int32_t d_state; bool d_dump; uint32_t d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; }; #endif /* GNSS_SDR_PCPS_8MS_ACQUISITION_CC_H_ */ src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition.cc000066400000000000000000001253601352176506000254130ustar00rootroot00000000000000/*! * \file pcps_acquisition.cc * \brief This class implements a Parallel Code Phase Search Acquisition * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
  • Cillian O'Driscoll, 2017. cillian(at)ieee.org *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_acquisition.h" #include "GLONASS_L1_L2_CA.h" // for GLONASS_TWO_PI #include "GPS_L1_CA.h" // for GPS_TWO_PI #include "gnss_frequencies.h" #include "gnss_sdr_create_directory.h" #include "gnss_synchro.h" #if HAS_STD_FILESYSTEM #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include #else #include #endif #else #include #endif #include #include #include // for from_long #include // for mp #include #include #include // for fill_n, min #include #include // for floor, fmod, rint, ceil #include // for memcpy #include #include #if HAS_STD_FILESYSTEM #if HAS_STD_FILESYSTEM_EXPERIMENTAL namespace fs = std::experimental::filesystem; #else namespace fs = std::filesystem; #endif #else namespace fs = boost::filesystem; #endif pcps_acquisition_sptr pcps_make_acquisition(const Acq_Conf& conf_) { return pcps_acquisition_sptr(new pcps_acquisition(conf_)); } pcps_acquisition::pcps_acquisition(const Acq_Conf& conf_) : gr::block("pcps_acquisition", gr::io_signature::make(1, 1, conf_.it_size), gr::io_signature::make(0, 0, conf_.it_size)) { this->message_port_register_out(pmt::mp("events")); acq_parameters = conf_; d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_positive_acq = 0; d_state = 0; d_doppler_bias = 0; d_num_noncoherent_integrations_counter = 0U; d_consumed_samples = acq_parameters.sampled_ms * acq_parameters.samples_per_ms * (acq_parameters.bit_transition_flag ? 2 : 1); if (acq_parameters.sampled_ms == acq_parameters.ms_per_code) { d_fft_size = d_consumed_samples; } else { d_fft_size = d_consumed_samples * 2; } // d_fft_size = next power of two? //// d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0U; d_threshold = 0.0; d_doppler_step = 0U; d_doppler_center = 0U; d_doppler_center_step_two = 0.0; d_test_statistics = 0.0; d_channel = 0U; if (conf_.it_size == sizeof(gr_complex)) { d_cshort = false; } else { d_cshort = true; } // COD: // Experimenting with the overlap/save technique for handling bit trannsitions // The problem: Circular correlation is asynchronous with the received code. // In effect the first code phase used in the correlation is the current // estimate of the code phase at the start of the input buffer. If this is 1/2 // of the code period a bit transition would move all the signal energy into // adjacent frequency bands at +/- 1/T where T is the integration time. // // We can avoid this by doing linear correlation, effectively doubling the // size of the input buffer and padding the code with zeros. if (acq_parameters.bit_transition_flag) { d_fft_size = d_consumed_samples * 2; acq_parameters.max_dwells = 1; // Activation of acq_parameters.bit_transition_flag invalidates the value of acq_parameters.max_dwells } d_tmp_buffer = std::vector(d_fft_size); d_fft_codes = std::vector>(d_fft_size); d_input_signal = std::vector>(d_fft_size); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); d_gnss_synchro = nullptr; d_worker_active = false; d_data_buffer = std::vector>(d_consumed_samples); if (d_cshort) { d_data_buffer_sc = std::vector(d_consumed_samples); } grid_ = arma::fmat(); narrow_grid_ = arma::fmat(); d_step_two = false; d_num_doppler_bins_step2 = acq_parameters.num_doppler_bins_step2; d_samplesPerChip = acq_parameters.samples_per_chip; d_buffer_count = 0U; // todo: CFAR statistic not available for non-coherent integration if (acq_parameters.max_dwells == 1) { d_use_CFAR_algorithm_flag = acq_parameters.use_CFAR_algorithm_flag; } else { d_use_CFAR_algorithm_flag = false; } d_dump_number = 0LL; d_dump_channel = acq_parameters.dump_channel; d_dump = acq_parameters.dump; d_dump_filename = acq_parameters.dump_filename; if (d_dump) { std::string dump_path; // Get path if (d_dump_filename.find_last_of('/') != std::string::npos) { std::string dump_filename_ = d_dump_filename.substr(d_dump_filename.find_last_of('/') + 1); dump_path = d_dump_filename.substr(0, d_dump_filename.find_last_of('/')); d_dump_filename = dump_filename_; } else { dump_path = std::string("."); } if (d_dump_filename.empty()) { d_dump_filename = "acquisition"; } // remove extension if any if (d_dump_filename.substr(1).find_last_of('.') != std::string::npos) { d_dump_filename = d_dump_filename.substr(0, d_dump_filename.find_last_of('.')); } d_dump_filename = dump_path + fs::path::preferred_separator + d_dump_filename; // create directory if (!gnss_sdr_create_directory(dump_path)) { std::cerr << "GNSS-SDR cannot create dump file for the Acquisition block. Wrong permissions?" << std::endl; d_dump = false; } } } void pcps_acquisition::set_resampler_latency(uint32_t latency_samples) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler acq_parameters.resampler_latency_samples = latency_samples; } void pcps_acquisition::set_local_code(std::complex* code) { // This will check if it's fdma, if yes will update the intermediate frequency and the doppler grid if (is_fdma()) { update_grid_doppler_wipeoffs(); } // COD // Here we want to create a buffer that looks like this: // [ 0 0 0 ... 0 c_0 c_1 ... c_L] // where c_i is the local code and there are L zeros and L chips gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler if (acq_parameters.bit_transition_flag) { int32_t offset = d_fft_size / 2; std::fill_n(d_fft_if->get_inbuf(), offset, gr_complex(0.0, 0.0)); memcpy(d_fft_if->get_inbuf() + offset, code, sizeof(gr_complex) * offset); } else { if (acq_parameters.sampled_ms == acq_parameters.ms_per_code) { memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_consumed_samples); } else { std::fill_n(d_fft_if->get_inbuf(), d_fft_size - d_consumed_samples, gr_complex(0.0, 0.0)); memcpy(d_fft_if->get_inbuf() + d_consumed_samples, code, sizeof(gr_complex) * d_consumed_samples); } } d_fft_if->execute(); // We need the FFT of local code volk_32fc_conjugate_32fc(d_fft_codes.data(), d_fft_if->get_outbuf(), d_fft_size); } bool pcps_acquisition::is_fdma() { // reset the intermediate frequency d_doppler_bias = 0; // Dealing with FDMA system if (strcmp(d_gnss_synchro->Signal, "1G") == 0) { d_doppler_bias = static_cast(DFRQ1_GLO * GLONASS_PRN.at(d_gnss_synchro->PRN)); LOG(INFO) << "Trying to acquire SV PRN " << d_gnss_synchro->PRN << " with freq " << d_doppler_bias << " in Glonass Channel " << GLONASS_PRN.at(d_gnss_synchro->PRN) << std::endl; return true; } if (strcmp(d_gnss_synchro->Signal, "2G") == 0) { d_doppler_bias += static_cast(DFRQ2_GLO * GLONASS_PRN.at(d_gnss_synchro->PRN)); LOG(INFO) << "Trying to acquire SV PRN " << d_gnss_synchro->PRN << " with freq " << d_doppler_bias << " in Glonass Channel " << GLONASS_PRN.at(d_gnss_synchro->PRN) << std::endl; return true; } return false; } void pcps_acquisition::update_local_carrier(gsl::span carrier_vector, float freq) { float phase_step_rad; if (acq_parameters.use_automatic_resampler) { phase_step_rad = GPS_TWO_PI * freq / static_cast(acq_parameters.resampled_fs); } else { phase_step_rad = GPS_TWO_PI * freq / static_cast(acq_parameters.fs_in); } std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(carrier_vector.data(), -phase_step_rad, _phase.data(), carrier_vector.length()); } void pcps_acquisition::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; d_num_doppler_bins = static_cast(std::ceil(static_cast(static_cast(acq_parameters.doppler_max) - static_cast(-acq_parameters.doppler_max)) / static_cast(d_doppler_step))); // Create the carrier Doppler wipeoff signals if (d_grid_doppler_wipeoffs.empty()) { d_grid_doppler_wipeoffs = std::vector>>(d_num_doppler_bins, std::vector>(d_fft_size)); } if (acq_parameters.make_2_steps && (d_grid_doppler_wipeoffs_step_two.empty())) { d_grid_doppler_wipeoffs_step_two = std::vector>>(d_num_doppler_bins_step2, std::vector>(d_fft_size)); } if (d_magnitude_grid.empty()) { d_magnitude_grid = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); } for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { std::fill(d_magnitude_grid[doppler_index].begin(), d_magnitude_grid[doppler_index].end(), 0.0); } update_grid_doppler_wipeoffs(); d_worker_active = false; if (d_dump) { uint32_t effective_fft_size = (acq_parameters.bit_transition_flag ? (d_fft_size / 2) : d_fft_size); grid_ = arma::fmat(effective_fft_size, d_num_doppler_bins, arma::fill::zeros); narrow_grid_ = arma::fmat(effective_fft_size, d_num_doppler_bins_step2, arma::fill::zeros); } } void pcps_acquisition::update_grid_doppler_wipeoffs() { for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int32_t doppler = -static_cast(acq_parameters.doppler_max) + d_doppler_center + d_doppler_step * doppler_index; update_local_carrier(d_grid_doppler_wipeoffs[doppler_index], d_doppler_bias + doppler); } } void pcps_acquisition::update_grid_doppler_wipeoffs_step2() { for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins_step2; doppler_index++) { float doppler = (static_cast(doppler_index) - static_cast(floor(d_num_doppler_bins_step2 / 2.0))) * acq_parameters.doppler_step2; update_local_carrier(d_grid_doppler_wipeoffs_step_two[doppler_index], d_doppler_center_step_two + doppler); } } void pcps_acquisition::set_state(int32_t state) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_active = true; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } void pcps_acquisition::send_positive_acquisition() { // Declare positive acquisition using a message port // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL DLOG(INFO) << "positive acquisition" << ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << ", sample_stamp " << d_sample_counter << ", test statistics value " << d_test_statistics << ", test statistics threshold " << d_threshold << ", code phase " << d_gnss_synchro->Acq_delay_samples << ", doppler " << d_gnss_synchro->Acq_doppler_hz << ", magnitude " << d_mag << ", input signal power " << d_input_power << ", Assist doppler_center " << d_doppler_center; d_positive_acq = 1; if (!d_channel_fsm.expired()) { // the channel FSM is set, so, notify it directly the positive acquisition to minimize delays d_channel_fsm.lock()->Event_valid_acquisition(); } else { this->message_port_pub(pmt::mp("events"), pmt::from_long(1)); } } void pcps_acquisition::send_negative_acquisition() { // Declare negative acquisition using a message port // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL DLOG(INFO) << "negative acquisition" << ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << ", sample_stamp " << d_sample_counter << ", test statistics value " << d_test_statistics << ", test statistics threshold " << d_threshold << ", code phase " << d_gnss_synchro->Acq_delay_samples << ", doppler " << d_gnss_synchro->Acq_doppler_hz << ", magnitude " << d_mag << ", input signal power " << d_input_power; d_positive_acq = 0; this->message_port_pub(pmt::mp("events"), pmt::from_long(2)); } void pcps_acquisition::dump_results(int32_t effective_fft_size) { d_dump_number++; std::string filename = d_dump_filename; filename.append("_"); filename.append(1, d_gnss_synchro->System); filename.append("_"); filename.append(1, d_gnss_synchro->Signal[0]); filename.append(1, d_gnss_synchro->Signal[1]); filename.append("_ch_"); filename.append(std::to_string(d_channel)); filename.append("_"); filename.append(std::to_string(d_dump_number)); filename.append("_sat_"); filename.append(std::to_string(d_gnss_synchro->PRN)); filename.append(".mat"); mat_t* matfp = Mat_CreateVer(filename.c_str(), nullptr, MAT_FT_MAT73); if (matfp == nullptr) { std::cout << "Unable to create or open Acquisition dump file" << std::endl; //acq_parameters.dump = false; } else { std::array dims{static_cast(effective_fft_size), static_cast(d_num_doppler_bins)}; matvar_t* matvar = Mat_VarCreate("acq_grid", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), grid_.memptr(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); dims[0] = static_cast(1); dims[1] = static_cast(1); matvar = Mat_VarCreate("doppler_max", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &acq_parameters.doppler_max, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("doppler_step", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_doppler_step, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("d_positive_acq", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_positive_acq, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); auto aux = static_cast(d_gnss_synchro->Acq_doppler_hz); matvar = Mat_VarCreate("acq_doppler_hz", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); aux = static_cast(d_gnss_synchro->Acq_delay_samples); matvar = Mat_VarCreate("acq_delay_samples", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("test_statistic", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &d_test_statistics, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("threshold", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &d_threshold, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("input_power", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &d_input_power, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("sample_counter", MAT_C_UINT64, MAT_T_UINT64, 1, dims.data(), &d_sample_counter, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("PRN", MAT_C_UINT32, MAT_T_UINT32, 1, dims.data(), &d_gnss_synchro->PRN, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("num_dwells", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_num_noncoherent_integrations_counter, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); if (acq_parameters.make_2_steps) { dims[0] = static_cast(effective_fft_size); dims[1] = static_cast(d_num_doppler_bins_step2); matvar = Mat_VarCreate("acq_grid_narrow", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), narrow_grid_.memptr(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); dims[0] = static_cast(1); dims[1] = static_cast(1); matvar = Mat_VarCreate("doppler_step_narrow", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &acq_parameters.doppler_step2, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); aux = d_doppler_center_step_two - static_cast(floor(d_num_doppler_bins_step2 / 2.0)) * acq_parameters.doppler_step2; matvar = Mat_VarCreate("doppler_grid_narrow_min", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); } Mat_Close(matfp); } } float pcps_acquisition::max_to_input_power_statistic(uint32_t& indext, int32_t& doppler, float input_power, uint32_t num_doppler_bins, int32_t doppler_max, int32_t doppler_step) { float grid_maximum = 0.0; uint32_t index_doppler = 0U; uint32_t tmp_intex_t = 0U; uint32_t index_time = 0U; float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); // Find the correlation peak and the carrier frequency for (uint32_t i = 0; i < num_doppler_bins; i++) { volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_magnitude_grid[i].data(), d_fft_size); if (d_magnitude_grid[i][tmp_intex_t] > grid_maximum) { grid_maximum = d_magnitude_grid[i][tmp_intex_t]; index_doppler = i; index_time = tmp_intex_t; } } indext = index_time; if (!d_step_two) { doppler = -static_cast(doppler_max) + d_doppler_center + doppler_step * static_cast(index_doppler); } else { doppler = static_cast(d_doppler_center_step_two + (static_cast(index_doppler) - static_cast(floor(d_num_doppler_bins_step2 / 2.0))) * acq_parameters.doppler_step2); } float magt = grid_maximum / (fft_normalization_factor * fft_normalization_factor); return magt / input_power; } float pcps_acquisition::first_vs_second_peak_statistic(uint32_t& indext, int32_t& doppler, uint32_t num_doppler_bins, int32_t doppler_max, int32_t doppler_step) { // Look for correlation peaks in the results // Find the highest peak and compare it to the second highest peak // The second peak is chosen not closer than 1 chip to the highest peak float firstPeak = 0.0; uint32_t index_doppler = 0U; uint32_t tmp_intex_t = 0U; uint32_t index_time = 0U; // Find the correlation peak and the carrier frequency for (uint32_t i = 0; i < num_doppler_bins; i++) { volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_magnitude_grid[i].data(), d_fft_size); if (d_magnitude_grid[i][tmp_intex_t] > firstPeak) { firstPeak = d_magnitude_grid[i][tmp_intex_t]; index_doppler = i; index_time = tmp_intex_t; } } indext = index_time; if (!d_step_two) { doppler = -static_cast(doppler_max) + d_doppler_center + doppler_step * static_cast(index_doppler); } else { doppler = static_cast(d_doppler_center_step_two + (static_cast(index_doppler) - static_cast(floor(d_num_doppler_bins_step2 / 2.0))) * acq_parameters.doppler_step2); } // Find 1 chip wide code phase exclude range around the peak int32_t excludeRangeIndex1 = index_time - d_samplesPerChip; int32_t excludeRangeIndex2 = index_time + d_samplesPerChip; // Correct code phase exclude range if the range includes array boundaries if (excludeRangeIndex1 < 0) { excludeRangeIndex1 = d_fft_size + excludeRangeIndex1; } else if (excludeRangeIndex2 >= static_cast(d_fft_size)) { excludeRangeIndex2 = excludeRangeIndex2 - d_fft_size; } int32_t idx = excludeRangeIndex1; memcpy(d_tmp_buffer.data(), d_magnitude_grid[index_doppler].data(), d_fft_size); do { d_tmp_buffer[idx] = 0.0; idx++; if (idx == static_cast(d_fft_size)) { idx = 0; } } while (idx != excludeRangeIndex2); // Find the second highest correlation peak in the same freq. bin --- volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_tmp_buffer.data(), d_fft_size); float secondPeak = d_tmp_buffer[tmp_intex_t]; // Compute the test statistics and compare to the threshold return firstPeak / secondPeak; } void pcps_acquisition::acquisition_core(uint64_t samp_count) { gr::thread::scoped_lock lk(d_setlock); // Initialize acquisition algorithm int32_t doppler = 0; uint32_t indext = 0U; int32_t effective_fft_size = (acq_parameters.bit_transition_flag ? d_fft_size / 2 : d_fft_size); if (d_cshort) { volk_gnsssdr_16ic_convert_32fc(d_data_buffer.data(), d_data_buffer_sc.data(), d_consumed_samples); } memcpy(d_input_signal.data(), d_data_buffer.data(), d_consumed_samples * sizeof(gr_complex)); if (d_fft_size > d_consumed_samples) { for (uint32_t i = d_consumed_samples; i < d_fft_size; i++) { d_input_signal[i] = gr_complex(0.0, 0.0); } } const gr_complex* in = d_input_signal.data(); // Get the input samples pointer d_input_power = 0.0; d_mag = 0.0; d_num_noncoherent_integrations_counter++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << samp_count << ", threshold: " << d_threshold << ", doppler_max: " << acq_parameters.doppler_max << ", doppler_step: " << d_doppler_step << ", use_CFAR_algorithm_flag: " << (d_use_CFAR_algorithm_flag ? "true" : "false"); lk.unlock(); if (d_use_CFAR_algorithm_flag or acq_parameters.bit_transition_flag) { // Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_tmp_buffer.data(), in, d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_tmp_buffer.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); } // Doppler frequency grid loop if (!d_step_two) { for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // Remove Doppler volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal with the local FFT'd code reference volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // Compute the inverse FFT d_ifft->execute(); // Compute squared magnitude (and accumulate in case of non-coherent integration) size_t offset = (acq_parameters.bit_transition_flag ? effective_fft_size : 0); if (d_num_noncoherent_integrations_counter == 1) { volk_32fc_magnitude_squared_32f(d_magnitude_grid[doppler_index].data(), d_ifft->get_outbuf() + offset, effective_fft_size); } else { volk_32fc_magnitude_squared_32f(d_tmp_buffer.data(), d_ifft->get_outbuf() + offset, effective_fft_size); volk_32f_x2_add_32f(d_magnitude_grid[doppler_index].data(), d_magnitude_grid[doppler_index].data(), d_tmp_buffer.data(), effective_fft_size); } // Record results to file if required if (d_dump and d_channel == d_dump_channel) { memcpy(grid_.colptr(doppler_index), d_magnitude_grid[doppler_index].data(), sizeof(float) * effective_fft_size); } } // Compute the test statistic if (d_use_CFAR_algorithm_flag) { d_test_statistics = max_to_input_power_statistic(indext, doppler, d_input_power, d_num_doppler_bins, acq_parameters.doppler_max, d_doppler_step); } else { d_test_statistics = first_vs_second_peak_statistic(indext, doppler, d_num_doppler_bins, acq_parameters.doppler_max, d_doppler_step); } if (acq_parameters.use_automatic_resampler) { // take into account the acquisition resampler ratio d_gnss_synchro->Acq_delay_samples = static_cast(std::fmod(static_cast(indext), acq_parameters.samples_per_code)) * acq_parameters.resampler_ratio; d_gnss_synchro->Acq_delay_samples -= static_cast(acq_parameters.resampler_latency_samples); //account the resampler filter latency d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = rint(static_cast(samp_count) * acq_parameters.resampler_ratio); } else { d_gnss_synchro->Acq_delay_samples = static_cast(std::fmod(static_cast(indext), acq_parameters.samples_per_code)); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = samp_count; } } else { for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins_step2; doppler_index++) { volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs_step_two[doppler_index].data(), d_fft_size); // Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); size_t offset = (acq_parameters.bit_transition_flag ? effective_fft_size : 0); if (d_num_noncoherent_integrations_counter == 1) { volk_32fc_magnitude_squared_32f(d_magnitude_grid[doppler_index].data(), d_ifft->get_outbuf() + offset, effective_fft_size); } else { volk_32fc_magnitude_squared_32f(d_tmp_buffer.data(), d_ifft->get_outbuf() + offset, effective_fft_size); volk_32f_x2_add_32f(d_magnitude_grid[doppler_index].data(), d_magnitude_grid[doppler_index].data(), d_tmp_buffer.data(), effective_fft_size); } // Record results to file if required if (d_dump and d_channel == d_dump_channel) { memcpy(narrow_grid_.colptr(doppler_index), d_magnitude_grid[doppler_index].data(), sizeof(float) * effective_fft_size); } } // Compute the test statistic if (d_use_CFAR_algorithm_flag) { d_test_statistics = max_to_input_power_statistic(indext, doppler, d_input_power, d_num_doppler_bins_step2, static_cast(d_doppler_center_step_two - (static_cast(d_num_doppler_bins_step2) / 2.0) * acq_parameters.doppler_step2), acq_parameters.doppler_step2); } else { d_test_statistics = first_vs_second_peak_statistic(indext, doppler, d_num_doppler_bins_step2, static_cast(d_doppler_center_step_two - (static_cast(d_num_doppler_bins_step2) / 2.0) * acq_parameters.doppler_step2), acq_parameters.doppler_step2); } if (acq_parameters.use_automatic_resampler) { // take into account the acquisition resampler ratio d_gnss_synchro->Acq_delay_samples = static_cast(std::fmod(static_cast(indext), acq_parameters.samples_per_code)) * acq_parameters.resampler_ratio; d_gnss_synchro->Acq_delay_samples -= static_cast(acq_parameters.resampler_latency_samples); //account the resampler filter latency d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = rint(static_cast(samp_count) * acq_parameters.resampler_ratio); d_gnss_synchro->Acq_doppler_step = acq_parameters.doppler_step2; } else { d_gnss_synchro->Acq_delay_samples = static_cast(std::fmod(static_cast(indext), acq_parameters.samples_per_code)); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = samp_count; d_gnss_synchro->Acq_doppler_step = acq_parameters.doppler_step2; } } lk.lock(); if (!acq_parameters.bit_transition_flag) { if (d_test_statistics > d_threshold) { d_active = false; if (acq_parameters.make_2_steps) { if (d_step_two) { send_positive_acquisition(); d_step_two = false; d_state = 0; // Positive acquisition } else { d_step_two = true; // Clear input buffer and make small grid acquisition d_num_noncoherent_integrations_counter = 0; d_positive_acq = 0; d_state = 0; } } else { send_positive_acquisition(); d_state = 0; // Positive acquisition } } else { d_buffer_count = 0; d_state = 1; } if (d_num_noncoherent_integrations_counter == acq_parameters.max_dwells) { if (d_state != 0) { send_negative_acquisition(); } d_state = 0; d_active = false; d_step_two = false; } } else { d_active = false; if (d_test_statistics > d_threshold) { if (acq_parameters.make_2_steps) { if (d_step_two) { send_positive_acquisition(); d_step_two = false; d_state = 0; // Positive acquisition } else { d_step_two = true; // Clear input buffer and make small grid acquisition d_num_noncoherent_integrations_counter = 0U; d_state = 0; } } else { send_positive_acquisition(); d_state = 0; // Positive acquisition } } else { d_state = 0; // Negative acquisition d_step_two = false; send_negative_acquisition(); } } d_worker_active = false; if ((d_num_noncoherent_integrations_counter == acq_parameters.max_dwells) or (d_positive_acq == 1)) { // Record results to file if required if (d_dump and d_channel == d_dump_channel) { pcps_acquisition::dump_results(effective_fft_size); } d_num_noncoherent_integrations_counter = 0U; d_positive_acq = 0; // Reset grid for (uint32_t i = 0; i < d_num_doppler_bins; i++) { for (uint32_t k = 0; k < d_fft_size; k++) { d_magnitude_grid[i][k] = 0.0; } } } } // Called by gnuradio to enable drivers, etc for i/o devices. bool pcps_acquisition::start() { d_sample_counter = 0ULL; return true; } int pcps_acquisition::general_work(int noutput_items __attribute__((unused)), gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items __attribute__((unused))) { /* * By J.Arribas, L.Esteve and M.Molina * Acquisition strategy (Kay Borre book + CFAR threshold): * 1. Compute the input signal power estimation * 2. Doppler serial search loop * 3. Perform the FFT-based circular convolution (parallel time search) * 4. Record the maximum peak and the associated synchronization parameters * 5. Compute the test statistics and compare to the threshold * 6. Declare positive or negative acquisition using a message port */ gr::thread::scoped_lock lk(d_setlock); if (!d_active or d_worker_active) { if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(ninput_items[0]); consume_each(ninput_items[0]); } if (d_step_two) { d_doppler_center_step_two = static_cast(d_gnss_synchro->Acq_doppler_hz); update_grid_doppler_wipeoffs_step2(); d_state = 0; d_active = true; } return 0; } switch (d_state) { case 0: { // Restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_state = 1; d_buffer_count = 0U; if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); } break; } case 1: { uint32_t buff_increment; if (d_cshort) { const auto* in = reinterpret_cast(input_items[0]); // Get the input samples pointer if ((ninput_items[0] + d_buffer_count) <= d_consumed_samples) { buff_increment = ninput_items[0]; } else { buff_increment = d_consumed_samples - d_buffer_count; } memcpy(&d_data_buffer_sc[d_buffer_count], in, sizeof(lv_16sc_t) * buff_increment); } else { const auto* in = reinterpret_cast(input_items[0]); // Get the input samples pointer if ((ninput_items[0] + d_buffer_count) <= d_consumed_samples) { buff_increment = ninput_items[0]; } else { buff_increment = d_consumed_samples - d_buffer_count; } memcpy(&d_data_buffer[d_buffer_count], in, sizeof(gr_complex) * buff_increment); } // If buffer will be full in next iteration if (d_buffer_count >= d_consumed_samples) { d_state = 2; } d_buffer_count += buff_increment; d_sample_counter += static_cast(buff_increment); consume_each(buff_increment); break; } case 2: { // Copy the data to the core and let it know that new data is available if (acq_parameters.blocking) { lk.unlock(); acquisition_core(d_sample_counter); } else { gr::thread::thread d_worker(&pcps_acquisition::acquisition_core, this, d_sample_counter); d_worker_active = true; } consume_each(0); d_buffer_count = 0U; break; } } return 0; } src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition.h000066400000000000000000000227411352176506000252540ustar00rootroot00000000000000/*! * \file pcps_acquisition.h * \brief This class implements a Parallel Code Phase Search Acquisition * * Acquisition strategy (Kay Borre book + CFAR threshold). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform the FFT-based circular convolution (parallel time search) *
  4. Record the maximum peak and the associated synchronization parameters *
  5. Compute the test statistics and compare to the threshold *
  6. Declare positive or negative acquisition using a message queue *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkhauser, 2007. pp 81-84 * * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
  • Cillian O'Driscoll, 2017. cillian(at)ieee.org *
  • Antonio Ramos, 2017. antonio.ramos@cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_ACQUISITION_H_ #define GNSS_SDR_PCPS_ACQUISITION_H_ #if ARMA_NO_BOUND_CHECKING #define ARMA_NO_DEBUG 1 #endif #include "acq_conf.h" #include "channel_fsm.h" #include #include #include #include #include // for gr_complex #include // for scoped_lock #include // for gr_vector_const_void_star #include // for Guidelines Support Library #include // for lv_16sc_t #include #include #include #include #include #include class Gnss_Synchro; class pcps_acquisition; using pcps_acquisition_sptr = boost::shared_ptr; pcps_acquisition_sptr pcps_make_acquisition(const Acq_Conf& conf_); /*! * \brief This class implements a Parallel Code Phase Search Acquisition. * * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", * Algorithm 1, for a pseudocode description of this implementation. */ class pcps_acquisition : public gr::block { public: ~pcps_acquisition() = default; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm and reserves memory. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler acq_parameters.doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_doppler_step = doppler_step; } /*! * \brief Set Doppler center frequency for the grid search. It will refresh the Doppler grid. * \param doppler_center - Frequency center of the search grid [Hz]. */ inline void set_doppler_center(int32_t doppler_center) { gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler if (doppler_center != d_doppler_center) { DLOG(INFO) << " Doppler assistance for Channel: " << d_channel << " => Doppler: " << doppler_center << "[Hz]"; d_doppler_center = doppler_center; update_grid_doppler_wipeoffs(); } } void set_resampler_latency(uint32_t latency_samples); /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_acquisition_sptr pcps_make_acquisition(const Acq_Conf& conf_); pcps_acquisition(const Acq_Conf& conf_); bool d_active; bool d_worker_active; bool d_cshort; bool d_step_two; bool d_use_CFAR_algorithm_flag; bool d_dump; int32_t d_state; int32_t d_positive_acq; uint32_t d_channel; uint32_t d_samplesPerChip; uint32_t d_doppler_step; int32_t d_doppler_center; int32_t d_doppler_bias; uint32_t d_num_noncoherent_integrations_counter; uint32_t d_fft_size; uint32_t d_consumed_samples; uint32_t d_num_doppler_bins; uint32_t d_num_doppler_bins_step2; uint32_t d_dump_channel; uint32_t d_buffer_count; uint64_t d_sample_counter; int64_t d_dump_number; float d_threshold; float d_mag; float d_input_power; float d_test_statistics; float d_doppler_center_step_two; std::string d_dump_filename; std::vector> d_magnitude_grid; std::vector d_tmp_buffer; std::vector> d_input_signal; std::vector>> d_grid_doppler_wipeoffs; std::vector>> d_grid_doppler_wipeoffs_step_two; std::vector> d_fft_codes; std::vector> d_data_buffer; std::vector d_data_buffer_sc; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; std::weak_ptr d_channel_fsm; Acq_Conf acq_parameters; Gnss_Synchro* d_gnss_synchro; arma::fmat grid_; arma::fmat narrow_grid_; void update_local_carrier(gsl::span carrier_vector, float freq); void update_grid_doppler_wipeoffs(); void update_grid_doppler_wipeoffs_step2(); void acquisition_core(uint64_t samp_count); void send_negative_acquisition(); void send_positive_acquisition(); void dump_results(int32_t effective_fft_size); bool is_fdma(); bool start(); float first_vs_second_peak_statistic(uint32_t& indext, int32_t& doppler, uint32_t num_doppler_bins, int32_t doppler_max, int32_t doppler_step); float max_to_input_power_statistic(uint32_t& indext, int32_t& doppler, float input_power, uint32_t num_doppler_bins, int32_t doppler_max, int32_t doppler_step); }; #endif /* GNSS_SDR_PCPS_ACQUISITION_H_*/ src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_fine_doppler_cc.cc000066400000000000000000000706321352176506000306070ustar00rootroot00000000000000/*! * \file pcps_acquisition_fine_doppler_cc.cc * \brief This class implements a Parallel Code Phase Search Acquisition with multi-dwells and fine Doppler estimation * \authors
    *
  • Javier Arribas, 2013. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_acquisition_fine_doppler_cc.h" #include "GPS_L1_CA.h" #include "gnss_sdr_create_directory.h" #include "gps_sdr_signal_processing.h" #if HAS_STD_FILESYSTEM #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include #else #include #endif #else #include #endif #include #include #include #include #include #include // std::rotate, std::fill_n #include #include #if HAS_STD_FILESYSTEM #if HAS_STD_FILESYSTEM_EXPERIMENTAL namespace fs = std::experimental::filesystem; #else namespace fs = std::filesystem; #endif #else namespace fs = boost::filesystem; #endif pcps_acquisition_fine_doppler_cc_sptr pcps_make_acquisition_fine_doppler_cc(const Acq_Conf &conf_) { return pcps_acquisition_fine_doppler_cc_sptr( new pcps_acquisition_fine_doppler_cc(conf_)); } pcps_acquisition_fine_doppler_cc::pcps_acquisition_fine_doppler_cc(const Acq_Conf &conf_) : gr::block("pcps_acquisition_fine_doppler_cc", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, sizeof(gr_complex))) { this->message_port_register_out(pmt::mp("events")); acq_parameters = conf_; d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_fs_in = conf_.fs_in; d_samples_per_ms = conf_.samples_per_ms; d_config_doppler_max = conf_.doppler_max; d_fft_size = d_samples_per_ms; // HS Acquisition d_max_dwells = conf_.max_dwells; d_gnuradio_forecast_samples = d_fft_size; d_state = 0; d_carrier = static_cast(volk_gnsssdr_malloc(d_fft_size * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_fft_codes = static_cast(volk_gnsssdr_malloc(d_fft_size * sizeof(gr_complex), volk_gnsssdr_get_alignment())); d_magnitude = static_cast(volk_gnsssdr_malloc(d_fft_size * sizeof(float), volk_gnsssdr_get_alignment())); d_10_ms_buffer = static_cast(volk_gnsssdr_malloc(50 * d_samples_per_ms * sizeof(gr_complex), volk_gnsssdr_get_alignment())); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = conf_.dump; d_dump_filename = conf_.dump_filename; if (d_dump) { std::string dump_path; // Get path if (d_dump_filename.find_last_of('/') != std::string::npos) { std::string dump_filename_ = d_dump_filename.substr(d_dump_filename.find_last_of('/') + 1); dump_path = d_dump_filename.substr(0, d_dump_filename.find_last_of('/')); d_dump_filename = dump_filename_; } else { dump_path = std::string("."); } if (d_dump_filename.empty()) { d_dump_filename = "acquisition"; } // remove extension if any if (d_dump_filename.substr(1).find_last_of('.') != std::string::npos) { d_dump_filename = d_dump_filename.substr(0, d_dump_filename.find_last_of('.')); } d_dump_filename = dump_path + fs::path::preferred_separator + d_dump_filename; // create directory if (!gnss_sdr_create_directory(dump_path)) { std::cerr << "GNSS-SDR cannot create dump file for the Acquisition block. Wrong permissions?" << std::endl; d_dump = false; } } d_n_samples_in_buffer = 0; d_threshold = 0; d_num_doppler_points = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_well_count = 0; d_channel = 0; d_positive_acq = 0; d_dump_number = 0; d_dump_channel = 0; // this implementation can only produce dumps in channel 0 //todo: migrate config parameters to the unified acquisition config class } // Finds next power of two // for n. If n itself is a // power of two then returns n unsigned int pcps_acquisition_fine_doppler_cc::nextPowerOf2(unsigned int n) { n--; n |= n >> 1U; n |= n >> 2U; n |= n >> 4U; n |= n >> 8U; n |= n >> 16U; n++; return n; } void pcps_acquisition_fine_doppler_cc::set_doppler_step(unsigned int doppler_step) { d_doppler_step = doppler_step; // Create the search grid array d_num_doppler_points = floor(std::abs(2 * d_config_doppler_max) / d_doppler_step); d_grid_data = std::vector>(d_num_doppler_points, std::vector(d_fft_size)); if (d_dump) { grid_ = arma::fmat(d_fft_size, d_num_doppler_points, arma::fill::zeros); } update_carrier_wipeoff(); } pcps_acquisition_fine_doppler_cc::~pcps_acquisition_fine_doppler_cc() { volk_gnsssdr_free(d_carrier); volk_gnsssdr_free(d_fft_codes); volk_gnsssdr_free(d_magnitude); volk_gnsssdr_free(d_10_ms_buffer); } void pcps_acquisition_fine_doppler_cc::set_local_code(std::complex *code) { memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code //Conjugate the local code volk_32fc_conjugate_32fc(d_fft_codes, d_fft_if->get_outbuf(), d_fft_size); } void pcps_acquisition_fine_doppler_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_state = 0; } void pcps_acquisition_fine_doppler_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) { if (noutput_items != 0) { ninput_items_required[0] = d_gnuradio_forecast_samples; //set the required available samples in each call } } void pcps_acquisition_fine_doppler_cc::reset_grid() { d_well_count = 0; for (int i = 0; i < d_num_doppler_points; i++) { //todo: use memset here for (unsigned int j = 0; j < d_fft_size; j++) { d_grid_data[i][j] = 0.0; } } } void pcps_acquisition_fine_doppler_cc::update_carrier_wipeoff() { // create the carrier Doppler wipeoff signals int doppler_hz; float phase_step_rad; d_grid_doppler_wipeoffs = std::vector>>(d_num_doppler_points, std::vector>(d_fft_size)); for (int doppler_index = 0; doppler_index < d_num_doppler_points; doppler_index++) { doppler_hz = d_doppler_step * doppler_index - d_config_doppler_max; // doppler search steps // compute the carrier doppler wipe-off signal and store it phase_step_rad = static_cast(GPS_TWO_PI) * doppler_hz / static_cast(d_fs_in); float _phase[1]; _phase[0] = 0; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase, d_fft_size); } } double pcps_acquisition_fine_doppler_cc::compute_CAF() { float firstPeak = 0.0; int index_doppler = 0; uint32_t tmp_intex_t = 0; uint32_t index_time = 0; // Look for correlation peaks in the results ============================== // Find the highest peak and compare it to the second highest peak // The second peak is chosen not closer than 1 chip to the highest peak //--- Find the correlation peak and the carrier frequency -------------- for (int i = 0; i < d_num_doppler_points; i++) { volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_grid_data[i].data(), d_fft_size); if (d_grid_data[i][tmp_intex_t] > firstPeak) { firstPeak = d_grid_data[i][tmp_intex_t]; index_doppler = i; index_time = tmp_intex_t; } // Record results to file if required if (d_dump and d_channel == d_dump_channel) { memcpy(grid_.colptr(i), d_grid_data[i].data(), sizeof(float) * d_fft_size); } } // -- - Find 1 chip wide code phase exclude range around the peak uint32_t samplesPerChip = ceil(GPS_L1_CA_CHIP_PERIOD * static_cast(this->d_fs_in)); int32_t excludeRangeIndex1 = index_time - samplesPerChip; int32_t excludeRangeIndex2 = index_time + samplesPerChip; // -- - Correct code phase exclude range if the range includes array boundaries if (excludeRangeIndex1 < 0) { excludeRangeIndex1 = d_fft_size + excludeRangeIndex1; } else if (excludeRangeIndex2 >= static_cast(d_fft_size)) { excludeRangeIndex2 = excludeRangeIndex2 - d_fft_size; } int32_t idx = excludeRangeIndex1; do { d_grid_data[index_doppler][idx] = 0.0; idx++; if (idx == static_cast(d_fft_size)) { idx = 0; } } while (idx != excludeRangeIndex2); //--- Find the second highest correlation peak in the same freq. bin --- volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_grid_data[index_doppler].data(), d_fft_size); float secondPeak = d_grid_data[index_doppler][tmp_intex_t]; // 5- Compute the test statistics and compare to the threshold d_test_statistics = firstPeak / secondPeak; // 4- record the maximum peak and the associated synchronization parameters d_gnss_synchro->Acq_delay_samples = static_cast(index_time); d_gnss_synchro->Acq_doppler_hz = static_cast(index_doppler * d_doppler_step - d_config_doppler_max); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; return d_test_statistics; } float pcps_acquisition_fine_doppler_cc::estimate_input_power(gr_vector_const_void_star &input_items) { const auto *in = reinterpret_cast(input_items[0]); //Get the input samples pointer // Compute the input signal power estimation float power = 0; volk_32fc_magnitude_squared_32f(d_magnitude, in, d_fft_size); volk_32f_accumulator_s32f(&power, d_magnitude, d_fft_size); power /= static_cast(d_fft_size); return power; } int pcps_acquisition_fine_doppler_cc::compute_and_accumulate_grid(gr_vector_const_void_star &input_items) { // initialize acquisition algorithm const auto *in = reinterpret_cast(input_items[0]); //Get the input samples pointer DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_config_doppler_max << ", doppler_step: " << d_doppler_step; // 2- Doppler frequency search loop auto *p_tmp_vector = static_cast(volk_gnsssdr_malloc(d_fft_size * sizeof(float), volk_gnsssdr_get_alignment())); for (int doppler_index = 0; doppler_index < d_num_doppler_points; doppler_index++) { // doppler search steps // Perform the carrier wipe-off volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes, d_fft_size); // compute the inverse FFT d_ifft->execute(); // save the grid matrix delay file volk_32fc_magnitude_squared_32f(p_tmp_vector, d_ifft->get_outbuf(), d_fft_size); //accumulate grid values volk_32f_x2_add_32f(d_grid_data[doppler_index].data(), d_grid_data[doppler_index].data(), p_tmp_vector, d_fft_size); } volk_gnsssdr_free(p_tmp_vector); return d_fft_size; //debug // std::cout << "iff=["; // for (int n = 0; n < d_fft_size; n++) // { // std::cout << std::real(d_ifft->get_outbuf()[n]) << "+" << std::imag(d_ifft->get_outbuf()[n]) << "i,"; // } // std::cout << "]\n"; // getchar(); } int pcps_acquisition_fine_doppler_cc::estimate_Doppler() { // Direct FFT int zero_padding_factor = 8; int prn_replicas = 10; int signal_samples = prn_replicas * d_fft_size; //int fft_size_extended = nextPowerOf2(signal_samples * zero_padding_factor); int fft_size_extended = signal_samples * zero_padding_factor; auto fft_operator = std::make_shared(fft_size_extended, true); //zero padding the entire vector std::fill_n(fft_operator->get_inbuf(), fft_size_extended, gr_complex(0.0, 0.0)); //1. generate local code aligned with the acquisition code phase estimation auto *code_replica = static_cast(volk_gnsssdr_malloc(signal_samples * sizeof(gr_complex), volk_gnsssdr_get_alignment())); gps_l1_ca_code_gen_complex_sampled(gsl::span(code_replica, signal_samples * sizeof(gr_complex)), d_gnss_synchro->PRN, d_fs_in, 0); int shift_index = static_cast(d_gnss_synchro->Acq_delay_samples); // Rotate to align the local code replica using acquisition time delay estimation if (shift_index != 0) { std::rotate(code_replica, code_replica + (d_fft_size - shift_index), code_replica + d_fft_size - 1); } for (int n = 0; n < prn_replicas - 1; n++) { memcpy(&code_replica[(n + 1) * d_fft_size], code_replica, d_fft_size * sizeof(gr_complex)); } //2. Perform code wipe-off volk_32fc_x2_multiply_32fc(fft_operator->get_inbuf(), d_10_ms_buffer, code_replica, signal_samples); // 3. Perform the FFT (zero padded!) fft_operator->execute(); // 4. Compute the magnitude and find the maximum auto *p_tmp_vector = static_cast(volk_gnsssdr_malloc(fft_size_extended * sizeof(float), volk_gnsssdr_get_alignment())); volk_32fc_magnitude_squared_32f(p_tmp_vector, fft_operator->get_outbuf(), fft_size_extended); uint32_t tmp_index_freq = 0; volk_gnsssdr_32f_index_max_32u(&tmp_index_freq, p_tmp_vector, fft_size_extended); //case even int counter = 0; auto fftFreqBins = std::vector(fft_size_extended); for (int k = 0; k < (fft_size_extended / 2); k++) { fftFreqBins[counter] = ((static_cast(d_fs_in) / 2.0) * static_cast(k)) / (static_cast(fft_size_extended) / 2.0); counter++; } for (int k = fft_size_extended / 2; k > 0; k--) { fftFreqBins[counter] = ((-static_cast(d_fs_in) / 2.0) * static_cast(k)) / (static_cast(fft_size_extended) / 2.0); counter++; } // 5. Update the Doppler estimation in Hz if (std::abs(fftFreqBins[tmp_index_freq] - d_gnss_synchro->Acq_doppler_hz) < 1000) { d_gnss_synchro->Acq_doppler_hz = static_cast(fftFreqBins[tmp_index_freq]); //std::cout << "FFT maximum present at " << fftFreqBins[tmp_index_freq] << " [Hz]" << std::endl; } else { DLOG(INFO) << "Abs(Grid Doppler - FFT Doppler)=" << std::abs(fftFreqBins[tmp_index_freq] - d_gnss_synchro->Acq_doppler_hz); DLOG(INFO) << "Error estimating fine frequency Doppler"; } // free memory!! volk_gnsssdr_free(code_replica); volk_gnsssdr_free(p_tmp_vector); return d_fft_size; } // Called by gnuradio to enable drivers, etc for i/o devices. bool pcps_acquisition_fine_doppler_cc::start() { d_sample_counter = 0ULL; return true; } void pcps_acquisition_fine_doppler_cc::set_state(int state) { //gr::thread::scoped_lock lock(d_setlock); // require mutex with work function called by the scheduler d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_test_statistics = 0.0; d_active = true; reset_grid(); } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int pcps_acquisition_fine_doppler_cc::general_work(int noutput_items, gr_vector_int &ninput_items __attribute__((unused)), gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { /*! * TODO: High sensitivity acquisition algorithm: * State Machine: * S0. StandBy. If d_active==1 -> S1 * S1. ComputeGrid. Perform the FFT acqusition doppler and delay grid. * Accumulate the search grid matrix (#doppler_bins x #fft_size) * Compare maximum to threshold and decide positive or negative * If T>=gamma -> S4 else * If d_well_count S2 * else -> S5. * S4. Positive_Acq: Send message and stop acq -> S0 * S5. Negative_Acq: Send message and stop acq -> S0 */ int samples_remaining; switch (d_state) { case 0: // S0. StandBy if (d_active == true) { reset_grid(); d_n_samples_in_buffer = 0; d_state = 1; } if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(d_fft_size); // sample counter consume_each(d_fft_size); } break; case 1: // S1. ComputeGrid compute_and_accumulate_grid(input_items); memcpy(&d_10_ms_buffer[d_n_samples_in_buffer], reinterpret_cast(input_items[0]), d_fft_size * sizeof(gr_complex)); d_n_samples_in_buffer += d_fft_size; d_well_count++; if (d_well_count >= d_max_dwells) { d_state = 2; } d_sample_counter += static_cast(d_fft_size); // sample counter consume_each(d_fft_size); break; case 2: // Compute test statistics and decide d_test_statistics = compute_CAF(); if (d_test_statistics > d_threshold) { d_state = 3; //perform fine doppler estimation } else { d_state = 5; //negative acquisition d_n_samples_in_buffer = 0; } break; case 3: // Fine doppler estimation samples_remaining = 10 * d_samples_per_ms - d_n_samples_in_buffer; if (samples_remaining > noutput_items) { memcpy(&d_10_ms_buffer[d_n_samples_in_buffer], reinterpret_cast(input_items[0]), noutput_items * sizeof(gr_complex)); d_n_samples_in_buffer += noutput_items; d_sample_counter += static_cast(noutput_items); // sample counter consume_each(noutput_items); } else { if (samples_remaining > 0) { memcpy(&d_10_ms_buffer[d_n_samples_in_buffer], reinterpret_cast(input_items[0]), samples_remaining * sizeof(gr_complex)); d_sample_counter += static_cast(samples_remaining); // sample counter consume_each(samples_remaining); } estimate_Doppler(); //disabled in repo d_n_samples_in_buffer = 0; d_state = 4; } break; case 4: // Positive_Acq DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; d_positive_acq = 1; d_active = false; // Record results to file if required if (d_dump and d_channel == d_dump_channel) { dump_results(d_fft_size); } // Send message to channel port //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL this->message_port_pub(pmt::mp("events"), pmt::from_long(1)); d_state = 0; if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(noutput_items); // sample counter consume_each(noutput_items); } break; case 5: // Negative_Acq DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; d_positive_acq = 0; d_active = false; // Record results to file if required if (d_dump and d_channel == d_dump_channel) { dump_results(d_fft_size); } // Send message to channel port //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL this->message_port_pub(pmt::mp("events"), pmt::from_long(2)); d_state = 0; if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(noutput_items); // sample counter consume_each(noutput_items); } break; default: d_state = 0; if (!acq_parameters.blocking_on_standby) { d_sample_counter += static_cast(noutput_items); // sample counter consume_each(noutput_items); } break; } return 0; } void pcps_acquisition_fine_doppler_cc::dump_results(int effective_fft_size) { d_dump_number++; std::string filename = d_dump_filename; filename.append("_"); filename.append(1, d_gnss_synchro->System); filename.append("_"); filename.append(1, d_gnss_synchro->Signal[0]); filename.append(1, d_gnss_synchro->Signal[1]); filename.append("_ch_"); filename.append(std::to_string(d_channel)); filename.append("_"); filename.append(std::to_string(d_dump_number)); filename.append("_sat_"); filename.append(std::to_string(d_gnss_synchro->PRN)); filename.append(".mat"); mat_t *matfp = Mat_CreateVer(filename.c_str(), nullptr, MAT_FT_MAT73); if (matfp == nullptr) { std::cout << "Unable to create or open Acquisition dump file" << std::endl; d_dump = false; } else { std::array dims{static_cast(effective_fft_size), static_cast(d_num_doppler_points)}; matvar_t *matvar = Mat_VarCreate("acq_grid", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), grid_.memptr(), 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); dims[0] = static_cast(1); dims[1] = static_cast(1); matvar = Mat_VarCreate("doppler_max", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_config_doppler_max, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("doppler_step", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_doppler_step, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("d_positive_acq", MAT_C_INT32, MAT_T_INT32, 1, dims.data(), &d_positive_acq, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); auto aux = static_cast(d_gnss_synchro->Acq_doppler_hz); matvar = Mat_VarCreate("acq_doppler_hz", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); aux = static_cast(d_gnss_synchro->Acq_delay_samples); matvar = Mat_VarCreate("acq_delay_samples", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("test_statistic", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &d_test_statistics, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("threshold", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &d_threshold, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); aux = 0.0; matvar = Mat_VarCreate("input_power", MAT_C_SINGLE, MAT_T_SINGLE, 1, dims.data(), &aux, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("sample_counter", MAT_C_UINT64, MAT_T_UINT64, 1, dims.data(), &d_sample_counter, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); matvar = Mat_VarCreate("PRN", MAT_C_UINT32, MAT_T_UINT32, 1, dims.data(), &d_gnss_synchro->PRN, 0); Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE Mat_VarFree(matvar); Mat_Close(matfp); } } src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_fine_doppler_cc.h000066400000000000000000000165671352176506000304600ustar00rootroot00000000000000/*! * \file pcps_acquisition_fine_doppler_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition with multi-dwells and fine Doppler estimation * for GPS L1 C/A signal * * Acquisition strategy (Kay Borre book). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform the FFT-based circular convolution (parallel time search) *
  4. Record the maximum peak and the associated synchronization parameters *
  5. Compute the test statistics and compare to the threshold *
  6. Declare positive or negative acquisition using a message port *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkhauser, 2007. pp 81-84 * * \authors
    *
  • Javier Arribas, 2013. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_ACQUISITION_FINE_DOPPLER_CC_H_ #define GNSS_SDR_PCPS_ACQUISITION_FINE_DOPPLER_CC_H_ #if ARMA_NO_BOUND_CHECKING #define ARMA_NO_DEBUG 1 #endif #include "acq_conf.h" #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include #include #include class pcps_acquisition_fine_doppler_cc; using pcps_acquisition_fine_doppler_cc_sptr = boost::shared_ptr; pcps_acquisition_fine_doppler_cc_sptr pcps_make_acquisition_fine_doppler_cc(const Acq_Conf& conf_); /*! * \brief This class implements a Parallel Code Phase Search Acquisition. * */ class pcps_acquisition_fine_doppler_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_acquisition_fine_doppler_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline unsigned int mag() const { return d_test_statistics; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(unsigned int channel) { d_channel = channel; d_dump_channel = d_channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(unsigned int doppler_max) { d_config_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ void set_doppler_step(unsigned int doppler_step); /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state); /*! * \brief Obtains the next power of 2 greater or equal to the input parameter * \param n - Integer value to obtain the next power of 2. */ unsigned int nextPowerOf2(unsigned int n); void dump_results(int effective_fft_size); void forecast(int noutput_items, gr_vector_int& ninput_items_required); /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_acquisition_fine_doppler_cc_sptr pcps_make_acquisition_fine_doppler_cc(const Acq_Conf& conf_); pcps_acquisition_fine_doppler_cc(const Acq_Conf& conf_); int compute_and_accumulate_grid(gr_vector_const_void_star& input_items); int estimate_Doppler(); float estimate_input_power(gr_vector_const_void_star& input_items); double compute_CAF(); void reset_grid(); void update_carrier_wipeoff(); bool start(); Acq_Conf acq_parameters; int64_t d_fs_in; int d_samples_per_ms; int d_max_dwells; int d_gnuradio_forecast_samples; float d_threshold; std::string d_satellite_str; int d_config_doppler_max; int d_num_doppler_points; int d_doppler_step; unsigned int d_fft_size; uint64_t d_sample_counter; gr_complex* d_carrier; gr_complex* d_fft_codes; gr_complex* d_10_ms_buffer; float* d_magnitude; std::vector> d_grid_data; std::vector>> d_grid_doppler_wipeoffs; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; unsigned int d_code_phase; float d_doppler_freq; float d_test_statistics; int d_positive_acq; int d_state; bool d_active; int d_well_count; int d_n_samples_in_buffer; bool d_dump; unsigned int d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; arma::fmat grid_; int64_t d_dump_number; unsigned int d_dump_channel; }; #endif /* pcps_acquisition_fine_doppler_cc*/ src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_fpga.cc000066400000000000000000000267701352176506000264150ustar00rootroot00000000000000/*! * \file pcps_acquisition_fpga.cc * \brief This class implements a Parallel Code Phase Search Acquisition for the FPGA * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_acquisition_fpga.h" #include "gnss_synchro.h" #include #include // for ceil #include // for operator<< #include // for move pcps_acquisition_fpga_sptr pcps_make_acquisition_fpga(pcpsconf_fpga_t conf_) { return pcps_acquisition_fpga_sptr(new pcps_acquisition_fpga(std::move(conf_))); } pcps_acquisition_fpga::pcps_acquisition_fpga(pcpsconf_fpga_t conf_) { acq_parameters = std::move(conf_); d_sample_counter = 0ULL; // Sample Counter d_active = false; d_state = 0; d_fft_size = acq_parameters.samples_per_code; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0U; d_threshold = 0.0; d_doppler_step = 0U; d_doppler_center = 0U; d_doppler_index = 0U; d_test_statistics = 0.0; d_channel = 0U; d_gnss_synchro = nullptr; d_downsampling_factor = acq_parameters.downsampling_factor; d_select_queue_Fpga = acq_parameters.select_queue_Fpga; d_total_block_exp = acq_parameters.total_block_exp; d_make_2_steps = acq_parameters.make_2_steps; d_num_doppler_bins_step2 = acq_parameters.num_doppler_bins_step2; d_doppler_step2 = acq_parameters.doppler_step2; d_doppler_center_step_two = 0.0; d_doppler_max = acq_parameters.doppler_max; d_max_num_acqs = acq_parameters.max_num_acqs; acquisition_fpga = std::make_shared(acq_parameters.device_name, acq_parameters.code_length, acq_parameters.doppler_max, d_fft_size, acq_parameters.fs_in, acq_parameters.sampled_ms, acq_parameters.select_queue_Fpga, acq_parameters.all_fft_codes, acq_parameters.excludelimit); } void pcps_acquisition_fpga::set_local_code() { acquisition_fpga->set_local_code(d_gnss_synchro->PRN); } void pcps_acquisition_fpga::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0; d_mag = 0.0; d_input_power = 0.0; d_num_doppler_bins = static_cast(std::ceil(static_cast(static_cast(d_doppler_max) - static_cast(-d_doppler_max)) / static_cast(d_doppler_step))) + 1; } void pcps_acquisition_fpga::set_state(int32_t state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_active = true; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } void pcps_acquisition_fpga::send_positive_acquisition() { // Declare positive acquisition using a message port // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL DLOG(INFO) << "positive acquisition" << ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << ", sample_stamp " << d_sample_counter << ", test statistics value " << d_test_statistics << ", test statistics threshold " << d_threshold << ", code phase " << d_gnss_synchro->Acq_delay_samples << ", doppler " << d_gnss_synchro->Acq_doppler_hz << ", magnitude " << d_mag << ", input signal power " << d_input_power << ", Assist doppler_center " << d_doppler_center; //the channel FSM is set, so, notify it directly the positive acquisition to minimize delays d_channel_fsm.lock()->Event_valid_acquisition(); } void pcps_acquisition_fpga::send_negative_acquisition() { // Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition" << ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << ", sample_stamp " << d_sample_counter << ", test statistics value " << d_test_statistics << ", test statistics threshold " << d_threshold << ", code phase " << d_gnss_synchro->Acq_delay_samples << ", doppler " << d_gnss_synchro->Acq_doppler_hz << ", magnitude " << d_mag << ", input signal power " << d_input_power; if (acq_parameters.repeat_satellite == true) { d_channel_fsm.lock()->Event_failed_acquisition_repeat(); } else { d_channel_fsm.lock()->Event_failed_acquisition_no_repeat(); } } void pcps_acquisition_fpga::acquisition_core(uint32_t num_doppler_bins, uint32_t doppler_step, int32_t doppler_min) { uint32_t indext = 0U; float firstpeak = 0.0; float secondpeak = 0.0; uint32_t total_block_exp; uint64_t initial_sample; int32_t doppler; acquisition_fpga->set_doppler_sweep(num_doppler_bins, doppler_step, doppler_min); acquisition_fpga->run_acquisition(); acquisition_fpga->read_acquisition_results(&indext, &firstpeak, &secondpeak, &initial_sample, &d_input_power, &d_doppler_index, &total_block_exp); doppler = static_cast(doppler_min) + doppler_step * (d_doppler_index - 1); if (total_block_exp > d_total_block_exp) { // if the attenuation factor of the FPGA FFT-IFFT is smaller than the reference attenuation factor then we need to update the reference attenuation factor std::cout << "changing blk exp..... d_total_block_exp = " << d_total_block_exp << " total_block_exp = " << total_block_exp << " chan = " << d_channel << std::endl; d_total_block_exp = total_block_exp; d_test_statistics = 0; } else { if (secondpeak > 0) { d_test_statistics = firstpeak / secondpeak; } else { d_test_statistics = 0.0; } } d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_sample_counter = initial_sample; if (d_select_queue_Fpga == 0) { if (d_downsampling_factor > 1) { d_gnss_synchro->Acq_delay_samples = static_cast(d_downsampling_factor * (indext)); d_gnss_synchro->Acq_samplestamp_samples = d_downsampling_factor * static_cast(d_sample_counter) - static_cast(44); //33; //41; //+ 81*0.5; // delay due to the downsampling filter in the acquisition } else { d_gnss_synchro->Acq_delay_samples = static_cast(indext); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; // delay due to the downsampling filter in the acquisition } } else { d_gnss_synchro->Acq_delay_samples = static_cast(indext); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; // delay due to the downsampling filter in the acquisition } } void pcps_acquisition_fpga::set_active(bool active) { d_active = active; d_input_power = 0.0; d_mag = 0.0; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step // no CFAR algorithm in the FPGA << ", use_CFAR_algorithm_flag: false"; acquisition_fpga->open_device(); acquisition_fpga->configure_acquisition(); acquisition_fpga->write_local_code(); acquisition_fpga->set_block_exp(d_total_block_exp); acquisition_core(d_num_doppler_bins, d_doppler_step, -d_doppler_max + d_doppler_center); if (!d_make_2_steps) { acquisition_fpga->close_device(); if (d_test_statistics > d_threshold) { d_active = false; send_positive_acquisition(); d_state = 0; // Positive acquisition } else { d_state = 0; d_active = false; send_negative_acquisition(); } } else { if (d_test_statistics > d_threshold) { d_doppler_center_step_two = static_cast(d_gnss_synchro->Acq_doppler_hz); uint32_t num_second_acq = 1; while (num_second_acq < d_max_num_acqs) { acquisition_core(d_num_doppler_bins_step2, d_doppler_step2, d_doppler_center_step_two - static_cast(floor(d_num_doppler_bins_step2 / 2.0)) * d_doppler_step2 + d_doppler_center); if (d_test_statistics > d_threshold) { d_active = false; send_positive_acquisition(); d_state = 0; // Positive acquisition break; } num_second_acq = num_second_acq + 1; } acquisition_fpga->close_device(); if (d_test_statistics <= d_threshold) { d_state = 0; d_active = false; send_negative_acquisition(); } } else { acquisition_fpga->close_device(); d_state = 0; d_active = false; send_negative_acquisition(); } } } void pcps_acquisition_fpga::reset_acquisition(void) { // this function triggers a HW reset of the FPGA PL. acquisition_fpga->open_device(); acquisition_fpga->reset_acquisition(); acquisition_fpga->close_device(); } src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_fpga.h000066400000000000000000000163131352176506000262470ustar00rootroot00000000000000/*! * \file pcps_acquisition_fpga.h * \brief This class implements a Parallel Code Phase Search Acquisition for the FPGA * * * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkhauser, 2007. pp 81-84 * * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.es *
  • Javier Arribas, 2019. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_ACQUISITION_FPGA_H_ #define GNSS_SDR_PCPS_ACQUISITION_FPGA_H_ #include "channel_fsm.h" #include "fpga_acquisition.h" #include #include #include // for uint32_t #include // for shared_ptr #include // for string class Gnss_Synchro; typedef struct { /* pcps acquisition configuration */ uint32_t sampled_ms; uint32_t doppler_max; int64_t fs_in; int32_t samples_per_ms; int32_t samples_per_code; int32_t code_length; uint32_t select_queue_Fpga; std::string device_name; uint32_t* all_fft_codes; // pointer to memory that contains all the code ffts //float downsampling_factor; uint32_t downsampling_factor; uint32_t total_block_exp; uint32_t excludelimit; bool make_2_steps; uint32_t num_doppler_bins_step2; float doppler_step2; bool repeat_satellite; uint32_t max_num_acqs; } pcpsconf_fpga_t; class pcps_acquisition_fpga; using pcps_acquisition_fpga_sptr = boost::shared_ptr; pcps_acquisition_fpga_sptr pcps_make_acquisition_fpga(pcpsconf_fpga_t conf_); /*! * \brief This class implements a Parallel Code Phase Search Acquisition that uses the FPGA. * * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", * Algorithm 1, for a pseudocode description of this implementation. */ class pcps_acquisition_fpga { public: /*! * \brief Destructor */ ~pcps_acquisition_fpga() = default; /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. */ void set_local_code(); /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ void set_active(bool active); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = channel_fsm; } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; acquisition_fpga->set_doppler_max(doppler_max); } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; acquisition_fpga->set_doppler_step(doppler_step); } /*! * \brief Set Doppler center frequency for the grid search. It will refresh the Doppler grid. * \param doppler_center - Frequency center of the search grid [Hz]. */ inline void set_doppler_center(int32_t doppler_center) { if (doppler_center != d_doppler_center) { DLOG(INFO) << " Doppler assistance for Channel: " << d_channel << " => Doppler: " << doppler_center << "[Hz]"; d_doppler_center = doppler_center; } } /*! * \brief This function triggers a HW reset of the FPGA PL. */ void reset_acquisition(void); private: friend pcps_acquisition_fpga_sptr pcps_make_acquisition_fpga(pcpsconf_fpga_t conf_); pcps_acquisition_fpga(pcpsconf_fpga_t conf_); bool d_active; bool d_make_2_steps; uint32_t d_doppler_index; uint32_t d_channel; uint32_t d_doppler_step; int32_t d_doppler_center; uint32_t d_doppler_max; uint32_t d_fft_size; uint32_t d_num_doppler_bins; uint32_t d_downsampling_factor; uint32_t d_select_queue_Fpga; uint32_t d_total_block_exp; uint32_t d_num_doppler_bins_step2; uint32_t d_max_num_acqs; int32_t d_state; uint64_t d_sample_counter; float d_threshold; float d_mag; float d_input_power; float d_test_statistics; float d_doppler_step2; float d_doppler_center_step_two; pcpsconf_fpga_t acq_parameters; Gnss_Synchro* d_gnss_synchro; std::shared_ptr acquisition_fpga; std::weak_ptr d_channel_fsm; void send_negative_acquisition(); void send_positive_acquisition(); void acquisition_core(uint32_t num_doppler_bins, uint32_t doppler_step, int32_t doppler_min); float first_vs_second_peak_statistic(uint32_t& indext, int32_t& doppler, uint32_t num_doppler_bins, int32_t doppler_max, int32_t doppler_step); }; #endif /* GNSS_SDR_PCPS_ACQUISITION_FPGA_H_*/ src/algorithms/acquisition/gnuradio_blocks/pcps_assisted_acquisition_cc.cc000066400000000000000000000437171352176506000277640ustar00rootroot00000000000000/*! * \file pcps_assisted_acquisition_cc.cc * \brief This class implements a Parallel Code Phase Search Acquisition with assistance and multi-dwells * \authors
    *
  • Javier Arribas, 2013. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_assisted_acquisition_cc.h" #include "GPS_L1_CA.h" #include "concurrent_map.h" #include "gps_acq_assist.h" #include #include #include #include #include #include #include #include extern Concurrent_Map global_gps_acq_assist_map; pcps_assisted_acquisition_cc_sptr pcps_make_assisted_acquisition_cc( int32_t max_dwells, uint32_t sampled_ms, int32_t doppler_max, int32_t doppler_min, int64_t fs_in, int32_t samples_per_ms, bool dump, std::string dump_filename) { return pcps_assisted_acquisition_cc_sptr( new pcps_assisted_acquisition_cc(max_dwells, sampled_ms, doppler_max, doppler_min, fs_in, samples_per_ms, dump, std::move(dump_filename))); } pcps_assisted_acquisition_cc::pcps_assisted_acquisition_cc( int32_t max_dwells, uint32_t sampled_ms, int32_t doppler_max, int32_t doppler_min, int64_t fs_in, int32_t samples_per_ms, bool dump, std::string dump_filename) : gr::block("pcps_assisted_acquisition_cc", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, sizeof(gr_complex))) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_sampled_ms = sampled_ms; d_config_doppler_max = doppler_max; d_config_doppler_min = doppler_min; d_fft_size = d_sampled_ms * d_samples_per_ms; // HS Acquisition d_max_dwells = max_dwells; d_gnuradio_forecast_samples = d_fft_size * 4; d_input_power = 0.0; d_state = 0; d_disable_assist = false; d_fft_codes.reserve(d_fft_size); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_doppler_resolution = 0; d_threshold = 0; d_doppler_max = 0; d_doppler_min = 0; d_num_doppler_points = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_well_count = 0; d_channel = 0; } void pcps_assisted_acquisition_cc::set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } pcps_assisted_acquisition_cc::~pcps_assisted_acquisition_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } void pcps_assisted_acquisition_cc::set_local_code(std::complex *code) { memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_fft_size); } void pcps_assisted_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_input_power = 0.0; d_state = 0; d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_codes.data(), d_fft_if->get_outbuf(), d_fft_size); } void pcps_assisted_acquisition_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) { if (noutput_items != 0) { ninput_items_required[0] = d_gnuradio_forecast_samples; //set the required available samples in each call } } void pcps_assisted_acquisition_cc::get_assistance() { Gps_Acq_Assist gps_acq_assisistance; if (global_gps_acq_assist_map.read(this->d_gnss_synchro->PRN, gps_acq_assisistance) == true) { //TODO: use the LO tolerance here if (gps_acq_assisistance.dopplerUncertainty >= 1000) { d_doppler_max = gps_acq_assisistance.d_Doppler0 + gps_acq_assisistance.dopplerUncertainty * 2; d_doppler_min = gps_acq_assisistance.d_Doppler0 - gps_acq_assisistance.dopplerUncertainty * 2; } else { d_doppler_max = gps_acq_assisistance.d_Doppler0 + 1000; d_doppler_min = gps_acq_assisistance.d_Doppler0 - 1000; } this->d_disable_assist = false; std::cout << "Acq assist ENABLED for GPS SV " << this->d_gnss_synchro->PRN << " (Doppler max,Doppler min)=(" << d_doppler_max << "," << d_doppler_min << ")" << std::endl; } else { this->d_disable_assist = true; std::cout << "Acq assist DISABLED for GPS SV " << this->d_gnss_synchro->PRN << std::endl; } } void pcps_assisted_acquisition_cc::reset_grid() { d_well_count = 0; for (int32_t i = 0; i < d_num_doppler_points; i++) { for (uint32_t j = 0; j < d_fft_size; j++) { d_grid_data[i][j] = 0.0; } } } void pcps_assisted_acquisition_cc::redefine_grid() { if (this->d_disable_assist == true) { d_doppler_max = d_config_doppler_max; d_doppler_min = d_config_doppler_min; } // Create the search grid array d_num_doppler_points = floor(std::abs(d_doppler_max - d_doppler_min) / d_doppler_step); d_grid_data = std::vector>(d_num_doppler_points, std::vector(d_fft_size)); // create the carrier Doppler wipeoff signals int32_t doppler_hz; float phase_step_rad; d_grid_doppler_wipeoffs = std::vector>>(d_num_doppler_points, std::vector>(d_fft_size)); for (int32_t doppler_index = 0; doppler_index < d_num_doppler_points; doppler_index++) { doppler_hz = d_doppler_min + d_doppler_step * doppler_index; // doppler search steps // compute the carrier doppler wipe-off signal and store it phase_step_rad = static_cast(GPS_TWO_PI) * doppler_hz / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); } } double pcps_assisted_acquisition_cc::search_maximum() { float magt = 0.0; float fft_normalization_factor; int32_t index_doppler = 0; uint32_t tmp_intex_t = 0; uint32_t index_time = 0; for (int32_t i = 0; i < d_num_doppler_points; i++) { volk_gnsssdr_32f_index_max_32u(&tmp_intex_t, d_grid_data[i].data(), d_fft_size); if (d_grid_data[i][tmp_intex_t] > magt) { magt = d_grid_data[i][index_time]; index_doppler = i; index_time = tmp_intex_t; } } // Normalize the maximum value to correct the scale factor introduced by FFTW fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); magt = magt / (fft_normalization_factor * fft_normalization_factor); // 5- Compute the test statistics and compare to the threshold d_test_statistics = 2 * d_fft_size * magt / (d_input_power * d_well_count); // 4- record the maximum peak and the associated synchronization parameters d_gnss_synchro->Acq_delay_samples = static_cast(index_time); d_gnss_synchro->Acq_doppler_hz = static_cast(index_doppler * d_doppler_step + d_doppler_min); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << d_gnss_synchro->Acq_doppler_hz << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_grid_data[index_doppler].data()), n); // write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } return d_test_statistics; } float pcps_assisted_acquisition_cc::estimate_input_power(gr_vector_const_void_star &input_items) { const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer // 1- Compute the input signal power estimation std::vector p_tmp_vector(d_fft_size); volk_32fc_magnitude_squared_32f(p_tmp_vector.data(), in, d_fft_size); float power; volk_32f_accumulator_s32f(&power, p_tmp_vector.data(), d_fft_size); return (power / static_cast(d_fft_size)); } int32_t pcps_assisted_acquisition_cc::compute_and_accumulate_grid(gr_vector_const_void_star &input_items) { // initialize acquisition algorithm const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 2- Doppler frequency search loop std::vector p_tmp_vector(d_fft_size); for (int32_t doppler_index = 0; doppler_index < d_num_doppler_points; doppler_index++) { // doppler search steps // Perform the carrier wipe-off volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // save the grid matrix delay file volk_32fc_magnitude_squared_32f(p_tmp_vector.data(), d_ifft->get_outbuf(), d_fft_size); const float *old_vector = d_grid_data[doppler_index].data(); volk_32f_x2_add_32f(d_grid_data[doppler_index].data(), old_vector, p_tmp_vector.data(), d_fft_size); } return d_fft_size; } int pcps_assisted_acquisition_cc::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { /*! * TODO: High sensitivity acquisition algorithm: * State Machine: * S0. StandBy. If d_active==1 -> S1 * S1. GetAssist. Define search grid with assistance information. Reset grid matrix -> S2 * S2. ComputeGrid. Perform the FFT acqusition doppler and delay grid. * Accumulate the search grid matrix (#doppler_bins x #fft_size) * Compare maximum to threshold and decide positive or negative * If T>=gamma -> S4 else * If d_well_count S2 * else if !disable_assist -> S3 * else -> S5. * S3. RedefineGrid. Open the grid search to unasisted acquisition. Reset counters and grid. -> S2 * S4. Positive_Acq: Send message and stop acq -> S0 * S5. Negative_Acq: Send message and stop acq -> S0 */ switch (d_state) { case 0: // S0. StandBy if (d_active == true) { d_state = 1; } d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; case 1: // S1. GetAssist get_assistance(); redefine_grid(); reset_grid(); d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); d_state = 2; break; case 2: // S2. ComputeGrid int32_t consumed_samples; consumed_samples = compute_and_accumulate_grid(input_items); d_well_count++; if (d_well_count >= d_max_dwells) { d_state = 3; } d_sample_counter += static_cast(consumed_samples); consume_each(consumed_samples); break; case 3: // Compute test statistics and decide d_input_power = estimate_input_power(input_items); d_test_statistics = search_maximum(); if (d_test_statistics > d_threshold) { d_state = 5; } else { if (d_disable_assist == false) { d_disable_assist = true; std::cout << "Acq assist DISABLED for GPS SV " << this->d_gnss_synchro->PRN << std::endl; d_state = 4; } else { d_state = 6; } } d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; case 4: // RedefineGrid redefine_grid(); reset_grid(); d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); d_state = 2; break; case 5: // Positive_Acq DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; // Send message to channel port //0=STOP_CHANNEL 1=ACQ_SUCCESS 2=ACQ_FAIL this->message_port_pub(pmt::mp("events"), pmt::from_long(1)); // consume samples to not block the GNU Radio flowgraph d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); d_state = 0; break; case 6: // Negative_Acq DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; // Send message to channel port //0=STOP_CHANNEL 1=ACQ_SUCCESS 2=ACQ_FAIL this->message_port_pub(pmt::mp("events"), pmt::from_long(2)); // consume samples to not block the GNU Radio flowgraph d_sample_counter += static_cast(ninput_items[0]); // sample counter consume_each(ninput_items[0]); d_state = 0; break; default: d_state = 0; break; } return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/pcps_assisted_acquisition_cc.h000066400000000000000000000166361352176506000276260ustar00rootroot00000000000000/*! * \file pcps_assisted_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition with assistance and multi-dwells * * Acquisition strategy (Kay Borre book + CFAR threshold). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform the FFT-based circular convolution (parallel time search) *
  4. Record the maximum peak and the associated synchronization parameters *
  5. Compute the test statistics and compare to the threshold *
  6. Declare positive or negative acquisition using a message queue *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkhauser, 2007. pp 81-84 * * \authors
    *
  • Javier Arribas, 2013. jarribas(at)cttc.es *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_ASSISTED_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_ASSISTED_ACQUISITION_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include class pcps_assisted_acquisition_cc; using pcps_assisted_acquisition_cc_sptr = boost::shared_ptr; pcps_assisted_acquisition_cc_sptr pcps_make_assisted_acquisition_cc( int32_t max_dwells, uint32_t sampled_ms, int32_t doppler_max, int32_t doppler_min, int64_t fs_in, int32_t samples_per_ms, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition. * * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", * Algorithm 1, for a pseudocode description of this implementation. */ class pcps_assisted_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_assisted_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_test_statistics; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ void set_doppler_step(uint32_t doppler_step); /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); void forecast(int noutput_items, gr_vector_int& ninput_items_required); private: friend pcps_assisted_acquisition_cc_sptr pcps_make_assisted_acquisition_cc(int32_t max_dwells, uint32_t sampled_ms, int32_t doppler_max, int32_t doppler_min, int64_t fs_in, int32_t samples_per_ms, bool dump, std::string dump_filename); pcps_assisted_acquisition_cc(int32_t max_dwells, uint32_t sampled_ms, int32_t doppler_max, int32_t doppler_min, int64_t fs_in, int32_t samples_per_ms, bool dump, std::string dump_filename); void calculate_magnitudes(gr_complex* fft_begin, int32_t doppler_shift, int32_t doppler_offset); int32_t compute_and_accumulate_grid(gr_vector_const_void_star& input_items); float estimate_input_power(gr_vector_const_void_star& input_items); double search_maximum(); void get_assistance(); void reset_grid(); void redefine_grid(); int64_t d_fs_in; int32_t d_samples_per_ms; int32_t d_max_dwells; uint32_t d_doppler_resolution; int32_t d_gnuradio_forecast_samples; float d_threshold; std::string d_satellite_str; int32_t d_doppler_max; int32_t d_doppler_min; int32_t d_config_doppler_max; int32_t d_config_doppler_min; int32_t d_num_doppler_points; int32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_fft_size; uint64_t d_sample_counter; std::vector d_fft_codes; std::vector> d_grid_data; std::vector>> d_grid_doppler_wipeoffs; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_input_power; float d_test_statistics; std::ofstream d_dump_file; int32_t d_state; bool d_active; bool d_disable_assist; int32_t d_well_count; bool d_dump; uint32_t d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; }; #endif /* GNSS_SDR_PCPS_ASSISTED_ACQUISITION_CC_H_ */ src/algorithms/acquisition/gnuradio_blocks/pcps_cccwsr_acquisition_cc.cc000066400000000000000000000440661352176506000274270ustar00rootroot00000000000000/*! * \file pcps_cccwsr_acquisition_cc.cc * \brief This class implements a Parallel Code Phase Search acquisition * with Coherent Channel Combining With Sign Recovery scheme. * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * D.Borio, C.O'Driscoll, G.Lachapelle, "Coherent, Noncoherent and * Differentially Coherent Combining Techniques for Acquisition of * New Composite GNSS Signals", IEEE Transactions On Aerospace and * Electronic Systems vol. 45 no. 3, July 2009, section IV * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_cccwsr_acquisition_cc.h" #include "GPS_L1_CA.h" // GPS_TWO_PI #include #include #include #include #include #include #include pcps_cccwsr_acquisition_cc_sptr pcps_cccwsr_make_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename) { return pcps_cccwsr_acquisition_cc_sptr( new pcps_cccwsr_acquisition_cc(sampled_ms, max_dwells, doppler_max, fs_in, samples_per_ms, samples_per_code, dump, std::move(dump_filename))); } pcps_cccwsr_acquisition_cc::pcps_cccwsr_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename) : gr::block("pcps_cccwsr_acquisition_cc", gr::io_signature::make(1, 1, sizeof(gr_complex) * sampled_ms * samples_per_ms), gr::io_signature::make(0, 0, sizeof(gr_complex) * sampled_ms * samples_per_ms)) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_sampled_ms = sampled_ms; d_max_dwells = max_dwells; d_well_count = 0; d_doppler_max = doppler_max; d_fft_size = d_sampled_ms * d_samples_per_ms; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_fft_code_data.reserve(d_fft_size); d_fft_code_pilot.reserve(d_fft_size); d_data_correlation.reserve(d_fft_size); d_pilot_correlation.reserve(d_fft_size); d_correlation_plus.reserve(d_fft_size); d_correlation_minus.reserve(d_fft_size); d_magnitude.reserve(d_fft_size); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_doppler_resolution = 0; d_threshold = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_channel = 0; } pcps_cccwsr_acquisition_cc::~pcps_cccwsr_acquisition_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } void pcps_cccwsr_acquisition_cc::set_local_code(std::complex *code_data, std::complex *code_pilot) { // Data code (E1B) memcpy(d_fft_if->get_inbuf(), code_data, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_code_data.data(), d_fft_if->get_outbuf(), d_fft_size); // Pilot code (E1C) memcpy(d_fft_if->get_inbuf(), code_pilot, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code, volk_32fc_conjugate_32fc(d_fft_code_pilot.data(), d_fft_if->get_outbuf(), d_fft_size); } void pcps_cccwsr_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; // Count the number of bins d_num_doppler_bins = 0; for (auto doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int32_t doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = GPS_TWO_PI * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); } } void pcps_cccwsr_acquisition_cc::set_state(int32_t state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int pcps_cccwsr_acquisition_cc::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { int32_t acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL switch (d_state) { case 0: { if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_state = 1; } d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; } case 1: { // initialize acquisition algorithm int32_t doppler; uint32_t indext = 0; uint32_t indext_plus = 0; uint32_t indext_minus = 0; float magt = 0.0; float magt_plus = 0.0; float magt_minus = 0.0; const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); d_sample_counter += static_cast(d_fft_size); // sample counter d_well_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitude.data(), in, d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); // 2- Doppler frequency search loop for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd data code reference (E1B) using SIMD operations // with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_data.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Copy the result of the correlation between wiped--off signal and data code in // d_data_correlation. memcpy(d_data_correlation.data(), d_ifft->get_outbuf(), sizeof(gr_complex) * d_fft_size); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd pilot code reference (E1C) using SIMD operations // with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_code_pilot.data(), d_fft_size); // Compute the inverse FFT d_ifft->execute(); // Copy the result of the correlation between wiped--off signal and pilot code in // d_data_correlation. memcpy(d_pilot_correlation.data(), d_ifft->get_outbuf(), sizeof(gr_complex) * d_fft_size); for (uint32_t i = 0; i < d_fft_size; i++) { d_correlation_plus[i] = std::complex( d_data_correlation[i].real() - d_pilot_correlation[i].imag(), d_data_correlation[i].imag() + d_pilot_correlation[i].real()); d_correlation_minus[i] = std::complex( d_data_correlation[i].real() + d_pilot_correlation[i].imag(), d_data_correlation[i].imag() - d_pilot_correlation[i].real()); } volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_correlation_plus.data(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_plus, d_magnitude.data(), d_fft_size); magt_plus = d_magnitude[indext_plus] / (fft_normalization_factor * fft_normalization_factor); volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_correlation_minus.data(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext_minus, d_magnitude.data(), d_fft_size); magt_minus = d_magnitude[indext_minus] / (fft_normalization_factor * fft_normalization_factor); if (magt_plus >= magt_minus) { magt = magt_plus; indext = indext_plus; } else { magt = magt_minus; indext = indext_minus; } // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_ifft->get_outbuf()), n); // write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } // 5- Compute the test statistics and compare to the threshold //d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; d_test_statistics = d_mag / d_input_power; // 6- Declare positive or negative acquisition using a message port if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else if (d_well_count == d_max_dwells) { d_state = 3; // Negative acquisition } consume_each(1); break; } case 2: { // 6.1- Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } case 3: { // 6.2- Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } } return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/pcps_cccwsr_acquisition_cc.h000066400000000000000000000161251352176506000272640ustar00rootroot00000000000000/*! * \file pcps_cccwsr_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search acquisition * with Coherent Channel Combining With Sign Recovery scheme. * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * D.Borio, C.O'Driscoll, G.Lachapelle, "Coherent, Noncoherent and * Differentially Coherent Combining Techniques for Acquisition of * New Composite GNSS Signals", IEEE Transactions On Aerospace and * Electronic Systems vol. 45 no. 3, July 2009, section IV * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_CCCWSR_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_CCCWSR_ACQUISITION_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include class pcps_cccwsr_acquisition_cc; using pcps_cccwsr_acquisition_cc_sptr = boost::shared_ptr; pcps_cccwsr_acquisition_cc_sptr pcps_cccwsr_make_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition with * Coherent Channel Combining With Sign Recovery scheme. */ class pcps_cccwsr_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_cccwsr_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for CCCWSR acquisition algorithm. * \param data_code - Pointer to the data PRN code. * \param pilot_code - Pointer to the pilot PRN code. */ void set_local_code(std::complex* code_data, std::complex* code_pilot); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of CCCWSR algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Coherent Channel Combining With Sign Recovery Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_cccwsr_acquisition_cc_sptr pcps_cccwsr_make_acquisition_cc(uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); pcps_cccwsr_acquisition_cc(uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool dump, std::string dump_filename); void calculate_magnitudes(gr_complex* fft_begin, int32_t doppler_shift, int32_t doppler_offset); int64_t d_fs_in; int32_t d_samples_per_ms; int32_t d_samples_per_code; uint32_t d_doppler_resolution; float d_threshold; std::string d_satellite_str; uint32_t d_doppler_max; uint32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_max_dwells; uint32_t d_well_count; uint32_t d_fft_size; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; uint32_t d_num_doppler_bins; std::vector d_fft_code_data; std::vector d_fft_code_pilot; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitude; std::vector d_data_correlation; std::vector d_pilot_correlation; std::vector d_correlation_plus; std::vector d_correlation_minus; float d_input_power; float d_test_statistics; std::ofstream d_dump_file; bool d_active; int32_t d_state; bool d_dump; uint32_t d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; }; #endif /* GNSS_SDR_PCPS_CCCWSR_ACQUISITION_CC_H_ */ src/algorithms/acquisition/gnuradio_blocks/pcps_opencl_acquisition_cc.cc000066400000000000000000001014001352176506000274050ustar00rootroot00000000000000/*! * \file pcps_opencl_acquisition_cc.cc * \brief This class implements a Parallel Code Phase Search Acquisition * using OpenCL to offload some functions to the GPU. * * Acquisition strategy (Kay Borre book + CFAR threshold). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform the FFT-based circular convolution (parallel time search) *
  4. Record the maximum peak and the associated synchronization parameters *
  5. Compute the test statistics and compare to the threshold *
  6. Declare positive or negative acquisition using a message port *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkhauser, 2007. pp 81-84 * * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_opencl_acquisition_cc.h" #include "GPS_L1_CA.h" //GPS_TWO_PI #include "opencl/fft_base_kernels.h" #include "opencl/fft_internal.h" #include #include #include #include #include #include #include #include #include #include pcps_opencl_acquisition_cc_sptr pcps_make_opencl_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename) { return pcps_opencl_acquisition_cc_sptr( new pcps_opencl_acquisition_cc(sampled_ms, max_dwells, doppler_max, fs_in, samples_per_ms, samples_per_code, bit_transition_flag, dump, std::move(dump_filename))); } pcps_opencl_acquisition_cc::pcps_opencl_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename) : gr::block("pcps_opencl_acquisition_cc", gr::io_signature::make(1, 1, sizeof(gr_complex) * sampled_ms * samples_per_ms), gr::io_signature::make(0, 0, sizeof(gr_complex) * sampled_ms * samples_per_ms)) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_core_working = false; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_sampled_ms = sampled_ms; d_max_dwells = max_dwells; d_well_count = 0; d_doppler_max = doppler_max; d_fft_size = d_sampled_ms * d_samples_per_ms; d_fft_size_pow2 = pow(2, ceil(log2(2 * d_fft_size))); d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_bit_transition_flag = bit_transition_flag; d_in_dwell_count = 0; d_cl_fft_batch_size = 1; d_in_buffer = std::vector>(d_max_dwells, std::vector(d_fft_size)); d_magnitude.reserve(d_fft_size); d_fft_codes.reserve(d_fft_size_pow2); d_zero_vector = std::vector(d_fft_size_pow2 - d_fft_size, 0.0); d_opencl = init_opencl_environment("math_kernel.cl"); if (d_opencl != 0) { // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); } // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); } pcps_opencl_acquisition_cc::~pcps_opencl_acquisition_cc() { if (d_opencl == 0) { delete d_cl_queue; delete d_cl_buffer_in; delete d_cl_buffer_1; delete d_cl_buffer_2; delete d_cl_buffer_magnitude; delete d_cl_buffer_fft_codes; if (d_num_doppler_bins > 0) { delete[] d_cl_buffer_grid_doppler_wipeoffs; } clFFT_DestroyPlan(d_cl_fft_plan); } try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } int pcps_opencl_acquisition_cc::init_opencl_environment(const std::string &kernel_filename) { // get all platforms (drivers) std::vector all_platforms; cl::Platform::get(&all_platforms); if (all_platforms.empty()) { std::cout << "No OpenCL platforms found. Check OpenCL installation!" << std::endl; return 1; } d_cl_platform = all_platforms[0]; // get default platform std::cout << "Using platform: " << d_cl_platform.getInfo() << std::endl; // get default GPU device of the default platform std::vector gpu_devices; d_cl_platform.getDevices(CL_DEVICE_TYPE_GPU, &gpu_devices); if (gpu_devices.empty()) { std::cout << "No GPU devices found. Check OpenCL installation!" << std::endl; return 2; } d_cl_device = gpu_devices[0]; std::vector device; device.push_back(d_cl_device); std::cout << "Using device: " << d_cl_device.getInfo() << std::endl; cl::Context context(device); d_cl_context = context; // build the program from the source in the file std::ifstream kernel_file(kernel_filename, std::ifstream::in); std::string kernel_code(std::istreambuf_iterator(kernel_file), (std::istreambuf_iterator())); kernel_file.close(); cl::Program::Sources sources; sources.push_back({kernel_code.c_str(), kernel_code.length()}); cl::Program program(context, sources); if (program.build(device) != CL_SUCCESS) { std::cout << " Error building: " << program.getBuildInfo(device[0]) << std::endl; return 3; } d_cl_program = program; // create buffers on the device d_cl_buffer_in = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(gr_complex) * d_fft_size); d_cl_buffer_fft_codes = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(gr_complex) * d_fft_size_pow2); d_cl_buffer_1 = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(gr_complex) * d_fft_size_pow2); d_cl_buffer_2 = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(gr_complex) * d_fft_size_pow2); d_cl_buffer_magnitude = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(float) * d_fft_size); // create queue to which we will push commands for the device. d_cl_queue = new cl::CommandQueue(d_cl_context, d_cl_device); // create FFT plan cl_int err; clFFT_Dim3 dim = {d_fft_size_pow2, 1, 1}; d_cl_fft_plan = clFFT_CreatePlan(d_cl_context(), dim, clFFT_1D, clFFT_InterleavedComplexFormat, &err); if (err != 0) { delete d_cl_queue; delete d_cl_buffer_in; delete d_cl_buffer_1; delete d_cl_buffer_2; delete d_cl_buffer_magnitude; delete d_cl_buffer_fft_codes; std::cout << "Error creating OpenCL FFT plan." << std::endl; return 4; } return 0; } void pcps_opencl_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; // Count the number of bins d_num_doppler_bins = 0; for (int doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); if (d_opencl == 0) { d_cl_buffer_grid_doppler_wipeoffs = new cl::Buffer *[d_num_doppler_bins]; } for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = static_cast(GPS_TWO_PI) * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); if (d_opencl == 0) { d_cl_buffer_grid_doppler_wipeoffs[doppler_index] = new cl::Buffer(d_cl_context, CL_MEM_READ_WRITE, sizeof(gr_complex) * d_fft_size); d_cl_queue->enqueueWriteBuffer(*(d_cl_buffer_grid_doppler_wipeoffs[doppler_index]), CL_TRUE, 0, sizeof(gr_complex) * d_fft_size, d_grid_doppler_wipeoffs[doppler_index].data()); } } // zero padding in buffer_1 (FFT input) if (d_opencl == 0) { d_cl_queue->enqueueWriteBuffer(*d_cl_buffer_1, CL_TRUE, sizeof(gr_complex) * d_fft_size, sizeof(gr_complex) * (d_fft_size_pow2 - d_fft_size), d_zero_vector.data()); } } void pcps_opencl_acquisition_cc::set_local_code(std::complex *code) { if (d_opencl == 0) { d_cl_queue->enqueueWriteBuffer(*d_cl_buffer_2, CL_TRUE, 0, sizeof(gr_complex) * d_fft_size, code); d_cl_queue->enqueueWriteBuffer(*d_cl_buffer_2, CL_TRUE, sizeof(gr_complex) * d_fft_size, sizeof(gr_complex) * (d_fft_size_pow2 - 2 * d_fft_size), d_zero_vector.data()); d_cl_queue->enqueueWriteBuffer(*d_cl_buffer_2, CL_TRUE, sizeof(gr_complex) * (d_fft_size_pow2 - d_fft_size), sizeof(gr_complex) * d_fft_size, code); clFFT_ExecuteInterleaved((*d_cl_queue)(), d_cl_fft_plan, d_cl_fft_batch_size, clFFT_Forward, (*d_cl_buffer_2)(), (*d_cl_buffer_2)(), 0, nullptr, nullptr); // Conjucate the local code cl::Kernel kernel = cl::Kernel(d_cl_program, "conj_vector"); kernel.setArg(0, *d_cl_buffer_2); // input kernel.setArg(1, *d_cl_buffer_fft_codes); // output d_cl_queue->enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(d_fft_size_pow2), cl::NullRange); } else { memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_codes.data(), d_fft_if->get_outbuf(), d_fft_size); } } void pcps_opencl_acquisition_cc::acquisition_core_volk() { // initialize acquisition algorithm int doppler; uint32_t indext = 0; float magt = 0.0; float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); uint64_t samplestamp = d_sample_counter_buffer[d_well_count]; d_input_power = 0.0; d_mag = 0.0; d_well_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_in_buffer[d_well_count].data(), d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); // 2- Doppler frequency search loop for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), d_in_buffer[d_well_count].data(), d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Search maximum volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_ifft->get_outbuf(), d_fft_size); volk_gnsssdr_32f_index_max_32u(&indext, d_magnitude.data(), d_fft_size); // Normalize the maximum value to correct the scale factor introduced by FFTW magt = d_magnitude[indext] / (fft_normalization_factor * fft_normalization_factor); // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; // In case that d_bit_transition_flag = true, we compare the potentially // new maximum test statistics (d_mag/d_input_power) with the value in // d_test_statistics. When the second dwell is being processed, the value // of d_mag/d_input_power could be lower than d_test_statistics (i.e, // the maximum test statistics in the previous dwell is greater than // current d_mag/d_input_power). Note that d_test_statistics is not // restarted between consecutive dwells in multidwell operation. if (d_test_statistics < (d_mag / d_input_power) || !d_bit_transition_flag) { d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = samplestamp; d_gnss_synchro->Acq_doppler_step = d_doppler_step; // 5- Compute the test statistics and compare to the threshold //d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; d_test_statistics = d_mag / d_input_power; } } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_ifft->get_outbuf()), n); //write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } if (!d_bit_transition_flag) { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else if (d_well_count == d_max_dwells) { d_state = 3; // Negative acquisition } } else { if (d_well_count == d_max_dwells) // d_max_dwells = 2 { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else { d_state = 3; // Negative acquisition } } } d_core_working = false; } void pcps_opencl_acquisition_cc::acquisition_core_opencl() { // initialize acquisition algorithm int doppler; uint32_t indext = 0; float magt = 0.0; float fft_normalization_factor = (static_cast(d_fft_size_pow2) * static_cast(d_fft_size)); //This works, but I am not sure why. uint64_t samplestamp = d_sample_counter_buffer[d_well_count]; d_input_power = 0.0; d_mag = 0.0; // write input vector in buffer of OpenCL device d_cl_queue->enqueueWriteBuffer(*d_cl_buffer_in, CL_TRUE, 0, sizeof(gr_complex) * d_fft_size, d_in_buffer[d_well_count].data()); d_well_count++; // struct timeval tv; // long long int begin = 0; // long long int end = 0; // gettimeofday(&tv, NULL); // begin = tv.tv_sec *1e6 + tv.tv_usec; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_in_buffer[d_well_count].data(), d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); cl::Kernel kernel; // 2- Doppler frequency search loop for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; // Multiply input signal with doppler wipe-off kernel = cl::Kernel(d_cl_program, "mult_vectors"); kernel.setArg(0, *d_cl_buffer_in); //input 1 kernel.setArg(1, *d_cl_buffer_grid_doppler_wipeoffs[doppler_index]); //input 2 kernel.setArg(2, *d_cl_buffer_1); //output d_cl_queue->enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(d_fft_size), cl::NullRange); // In the previous operation, we store the result in the first d_fft_size positions // of d_cl_buffer_1. The rest d_fft_size_pow2-d_fft_size already have zeros // (zero-padding is made in init() for optimization purposes). clFFT_ExecuteInterleaved((*d_cl_queue)(), d_cl_fft_plan, d_cl_fft_batch_size, clFFT_Forward, (*d_cl_buffer_1)(), (*d_cl_buffer_2)(), 0, nullptr, nullptr); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference kernel = cl::Kernel(d_cl_program, "mult_vectors"); kernel.setArg(0, *d_cl_buffer_2); //input 1 kernel.setArg(1, *d_cl_buffer_fft_codes); //input 2 kernel.setArg(2, *d_cl_buffer_2); //output d_cl_queue->enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(d_fft_size_pow2), cl::NullRange); // compute the inverse FFT clFFT_ExecuteInterleaved((*d_cl_queue)(), d_cl_fft_plan, d_cl_fft_batch_size, clFFT_Inverse, (*d_cl_buffer_2)(), (*d_cl_buffer_2)(), 0, nullptr, nullptr); // Compute magnitude kernel = cl::Kernel(d_cl_program, "magnitude_squared"); kernel.setArg(0, *d_cl_buffer_2); //input 1 kernel.setArg(1, *d_cl_buffer_magnitude); //output d_cl_queue->enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(d_fft_size), cl::NullRange); // This is the only function that blocks this thread until all previously enqueued // OpenCL commands are completed. d_cl_queue->enqueueReadBuffer(*d_cl_buffer_magnitude, CL_TRUE, 0, sizeof(float) * d_fft_size, d_magnitude.data()); // Search maximum // @TODO: find an efficient way to search the maximum with OpenCL in the GPU. volk_gnsssdr_32f_index_max_32u(&indext, d_magnitude.data(), d_fft_size); // Normalize the maximum value to correct the scale factor introduced by FFTW magt = d_magnitude[indext] / (fft_normalization_factor * fft_normalization_factor); // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; // In case that d_bit_transition_flag = true, we compare the potentially // new maximum test statistics (d_mag/d_input_power) with the value in // d_test_statistics. When the second dwell is being processed, the value // of d_mag/d_input_power could be lower than d_test_statistics (i.e, // the maximum test statistics in the previous dwell is greater than // current d_mag/d_input_power). Note that d_test_statistics is not // restarted between consecutive dwells in multidwell operation. if (d_test_statistics < (d_mag / d_input_power) || !d_bit_transition_flag) { d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = samplestamp; d_gnss_synchro->Acq_doppler_step = d_doppler_step; // 5- Compute the test statistics and compare to the threshold //d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; d_test_statistics = d_mag / d_input_power; } } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_ifft->get_outbuf()), n); //write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } // gettimeofday(&tv, NULL); // end = tv.tv_sec *1e6 + tv.tv_usec; // std::cout << "Acq time = " << (end-begin) << " us" << std::endl; if (!d_bit_transition_flag) { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else if (d_well_count == d_max_dwells) { d_state = 3; // Negative acquisition } } else { if (d_well_count == d_max_dwells) // d_max_dwells = 2 { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else { d_state = 3; // Negative acquisition } } } d_core_working = false; } void pcps_opencl_acquisition_cc::set_state(int state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_in_dwell_count = 0; d_sample_counter_buffer.clear(); } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int pcps_opencl_acquisition_cc::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { int acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL switch (d_state) { case 0: { if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_in_dwell_count = 0; d_sample_counter_buffer.clear(); d_state = 1; } d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter break; } case 1: { if (d_in_dwell_count < d_max_dwells) { // Fill internal buffer with d_max_dwells signal blocks. This step ensures that // consecutive signal blocks will be processed in multi-dwell operation. This is // essential when d_bit_transition_flag = true. uint32_t num_dwells = std::min(static_cast(d_max_dwells - d_in_dwell_count), ninput_items[0]); for (uint32_t i = 0; i < num_dwells; i++) { memcpy(d_in_buffer[d_in_dwell_count++].data(), static_cast(input_items[i]), sizeof(gr_complex) * d_fft_size); d_sample_counter += static_cast(d_fft_size); d_sample_counter_buffer.push_back(d_sample_counter); } if (ninput_items[0] > static_cast(num_dwells)) { d_sample_counter += static_cast(d_fft_size * (ninput_items[0] - num_dwells)); } } else { // We already have d_max_dwells consecutive blocks in the internal buffer, // just skip input blocks. d_sample_counter += static_cast(d_fft_size * ninput_items[0]); } // We create a new thread to process next block if the following // conditions are fulfilled: // 1. There are new blocks in d_in_buffer that have not been processed yet // (d_well_count < d_in_dwell_count). // 2. No other acquisition_core thead is working (!d_core_working). // 3. d_state==1. We need to check again d_state because it can be modified at any // moment by the external thread (may have changed since checked in the switch()). // If the external thread has already declared positive (d_state=2) or negative // (d_state=3) acquisition, we don't have to process next block!! if ((d_well_count < d_in_dwell_count) && !d_core_working && d_state == 1) { d_core_working = true; if (d_opencl == 0) { // Use OpenCL implementation boost::thread(&pcps_opencl_acquisition_cc::acquisition_core_opencl, this); } else { // Use Volk implementation boost::thread(&pcps_opencl_acquisition_cc::acquisition_core_volk, this); } } break; } case 2: { // Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } case 3: { // Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } } consume_each(ninput_items[0]); return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/pcps_opencl_acquisition_cc.h000066400000000000000000000207371352176506000272640ustar00rootroot00000000000000/*! * \file pcps_opencl_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition * using OpenCL to offload some functions to the GPU. * * Acquisition strategy (Kay Borre book + CFAR threshold). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform the FFT-based circular convolution (parallel time search) *
  4. Record the maximum peak and the associated synchronization parameters *
  5. Compute the test statistics and compare to the threshold *
  6. Declare positive or negative acquisition using a message port *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkha user, 2007. pp 81-84 * * \authors
    *
  • Javier Arribas, 2011. jarribas(at)cttc.es *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com *
  • Marc Molina, 2013. marc.molina.pena@gmail.com *
* * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_OPENCL_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_OPENCL_ACQUISITION_CC_H_ #define CL_SILENCE_DEPRECATION #include "channel_fsm.h" #include "gnss_synchro.h" #include "opencl/fft_internal.h" #include #include #include #include "opencl/cl.hpp" #include #include #include #include class pcps_opencl_acquisition_cc; typedef boost::shared_ptr pcps_opencl_acquisition_cc_sptr; pcps_opencl_acquisition_cc_sptr pcps_make_opencl_acquisition_cc( uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition. * * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", * Algorithm 1, for a pseudocode description of this implementation. */ class pcps_opencl_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_opencl_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = channel_fsm; } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } inline bool opencl_ready() const { bool ready = false; if (d_opencl == 0) { ready = true; } return ready; } void acquisition_core_volk(); void acquisition_core_opencl(); /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_opencl_acquisition_cc_sptr pcps_make_opencl_acquisition_cc(uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); pcps_opencl_acquisition_cc(uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int samples_per_ms, int samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift, int doppler_offset); int init_opencl_environment(const std::string& kernel_filename); int64_t d_fs_in; int d_samples_per_ms; int d_samples_per_code; uint32_t d_doppler_resolution; float d_threshold; std::string d_satellite_str; uint32_t d_doppler_max; uint32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_max_dwells; uint32_t d_well_count; uint32_t d_fft_size; uint32_t d_fft_size_pow2; int* d_max_doppler_indexs; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; uint32_t d_num_doppler_bins; std::vector d_fft_codes; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitude; float d_input_power; float d_test_statistics; bool d_bit_transition_flag; std::ofstream d_dump_file; bool d_active; int d_state; bool d_core_working; bool d_dump; uint32_t d_channel; std::string d_dump_filename; std::vector d_zero_vector; std::vector> d_in_buffer; std::vector d_sample_counter_buffer; uint32_t d_in_dwell_count; std::weak_ptr d_channel_fsm; int d_opencl; cl::Platform d_cl_platform; cl::Device d_cl_device; cl::Context d_cl_context; cl::Program d_cl_program; cl::Buffer* d_cl_buffer_in; cl::Buffer* d_cl_buffer_fft_codes; cl::Buffer* d_cl_buffer_1; cl::Buffer* d_cl_buffer_2; cl::Buffer* d_cl_buffer_magnitude; cl::Buffer** d_cl_buffer_grid_doppler_wipeoffs; cl::CommandQueue* d_cl_queue; clFFT_Plan d_cl_fft_plan; cl_int d_cl_fft_batch_size; }; #endif src/algorithms/acquisition/gnuradio_blocks/pcps_quicksync_acquisition_cc.cc000066400000000000000000000632051352176506000301500ustar00rootroot00000000000000/*! * \file pcps_quicksync_acquisition_cc.cc * \brief This class implements a Parallel Code Phase Search Acquisition * \author Damian Miralles Sanchez, 2014. dmiralles2009(at)gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_quicksync_acquisition_cc.h" #include "GPS_L1_CA.h" #include #include #include #include #include #include #include #include pcps_quicksync_acquisition_cc_sptr pcps_quicksync_make_acquisition_cc( uint32_t folding_factor, uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename) { return pcps_quicksync_acquisition_cc_sptr( new pcps_quicksync_acquisition_cc( folding_factor, sampled_ms, max_dwells, doppler_max, fs_in, samples_per_ms, samples_per_code, bit_transition_flag, dump, std::move(dump_filename))); } pcps_quicksync_acquisition_cc::pcps_quicksync_acquisition_cc( uint32_t folding_factor, uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename) : gr::block("pcps_quicksync_acquisition_cc", gr::io_signature::make(1, 1, (sizeof(gr_complex) * sampled_ms * samples_per_ms)), gr::io_signature::make(0, 0, (sizeof(gr_complex) * sampled_ms * samples_per_ms))) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_sampled_ms = sampled_ms; d_max_dwells = max_dwells; d_well_count = 0; d_doppler_max = doppler_max; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_bit_transition_flag = bit_transition_flag; d_folding_factor = folding_factor; // fft size is reduced. d_fft_size = (d_samples_per_code) / d_folding_factor; d_fft_codes.reserve(d_fft_size); d_magnitude.reserve(d_samples_per_code * d_folding_factor); d_magnitude_folded.reserve(d_fft_size); d_possible_delay.reserve(d_folding_factor); d_corr_output_f.reserve(d_folding_factor); /*Create the d_code signal , which would store the values of the code in its original form to perform later correlation in time domain*/ d_code = std::vector(d_samples_per_code, lv_cmake(0.0F, 0.0F)); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_code_folded = std::vector(d_fft_size, lv_cmake(0.0F, 0.0F)); d_signal_folded.reserve(d_fft_size); d_noise_floor_power = 0; d_doppler_resolution = 0; d_threshold = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_channel = 0; } pcps_quicksync_acquisition_cc::~pcps_quicksync_acquisition_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure& e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } } void pcps_quicksync_acquisition_cc::set_local_code(std::complex* code) { /* save a local copy of the code without the folding process to perform corre- lation in time in the final steps of the acquisition stage */ memcpy(d_code.data(), code, sizeof(gr_complex) * d_samples_per_code); memcpy(d_fft_if->get_inbuf(), d_code_folded.data(), sizeof(gr_complex) * (d_fft_size)); /* perform folding of the code by the factorial factor parameter. Notice that folding of the code in the time stage would result in a downsampled spectrum in the frequency domain after applying the fftw operation */ for (uint32_t i = 0; i < d_folding_factor; i++) { std::transform((code + i * d_fft_size), (code + ((i + 1) * d_fft_size)), d_fft_if->get_inbuf(), d_fft_if->get_inbuf(), std::plus()); } d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_codes.data(), d_fft_if->get_outbuf(), d_fft_size); } void pcps_quicksync_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_mag = 0.0; d_input_power = 0.0; if (d_doppler_step == 0) { d_doppler_step = 250; } // Count the number of bins d_num_doppler_bins = 0; for (auto doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_samples_per_code * d_folding_factor)); for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int32_t doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = GPS_TWO_PI * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_samples_per_code * d_folding_factor); } } void pcps_quicksync_acquisition_cc::set_state(int32_t state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_active = true; } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int pcps_quicksync_acquisition_cc::general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items __attribute__((unused))) { /* * By J.Arribas, L.Esteve and M.Molina * Acquisition strategy (Kay Borre book + CFAR threshold): * 1. Compute the input signal power estimation * 2. Doppler serial search loop * 3. Perform the FFT-based circular convolution (parallel time search) * 4. Record the maximum peak and the associated synchronization parameters * 5. Compute the test statistics and compare to the threshold * 6. Declare positive or negative acquisition using a message queue */ // DLOG(INFO) << "START GENERAL WORK"; int32_t acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL switch (d_state) { case 0: { // DLOG(INFO) << "START CASE 0"; if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_well_count = 0; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; d_state = 1; } d_sample_counter += static_cast(d_sampled_ms * d_samples_per_ms * ninput_items[0]); // sample counter consume_each(ninput_items[0]); // DLOG(INFO) << "END CASE 0"; break; } case 1: { // initialize acquisition implementing the QuickSync algorithm // DLOG(INFO) << "START CASE 1"; int32_t doppler; uint32_t indext = 0; float magt = 0.0; const auto* in = reinterpret_cast(input_items[0]); // Get the input samples pointer std::vector in_temp(d_samples_per_code * d_folding_factor); // Create a signal to store a signal of size 1ms, to perform correlation // in time. No folding on this data is required std::vector in_1code(d_samples_per_code); // Stores the values of the correlation output between the local code // and the signal with doppler shift corrected std::vector corr_output(d_samples_per_code); // Stores a copy of the folded version of the signal.This is used for // the FFT operations in future steps of execution*/ // gr_complex in_folded[d_fft_size]; float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); d_input_power = 0.0; d_mag = 0.0; d_test_statistics = 0.0; d_noise_floor_power = 0.0; d_sample_counter += static_cast(d_sampled_ms * d_samples_per_ms); // sample counter d_well_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,algorithm: pcps_quicksync_acquisition" << " ,folding factor: " << d_folding_factor << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step << ", Signal Size: " << d_samples_per_code * d_folding_factor; // 1- Compute the input signal power estimation. This operation is // being performed in a signal of size nxp volk_32fc_magnitude_squared_32f(d_magnitude.data(), in, d_samples_per_code * d_folding_factor); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_samples_per_code * d_folding_factor); d_input_power /= static_cast(d_samples_per_code * d_folding_factor); for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // Ensure that the signal is going to start with all samples // at zero. This is done to avoid over acumulation when performing // the folding process to be stored in d_fft_if->get_inbuf() d_signal_folded = std::vector(d_fft_size, lv_cmake(0.0F, 0.0F)); memcpy(d_fft_if->get_inbuf(), d_signal_folded.data(), sizeof(gr_complex) * (d_fft_size)); // Doppler search steps and then multiplication of the incoming // signal with the doppler wipeoffs to eliminate frequency offset doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; // Perform multiplication of the incoming signal with the // complex exponential vector. This removes the frequency doppler // shift offset volk_32fc_x2_multiply_32fc(in_temp.data(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_samples_per_code * d_folding_factor); // Perform folding of the carrier wiped-off incoming signal. Since // superlinear method is being used the folding factor in the // incoming raw data signal is of d_folding_factor^2 for (int32_t i = 0; i < static_cast(d_folding_factor * d_folding_factor); i++) { std::transform((in_temp.data() + i * d_fft_size), (in_temp.data() + ((i + 1) * d_fft_size)), d_fft_if->get_inbuf(), d_fft_if->get_inbuf(), std::plus()); } // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming // signal with the local FFT'd code reference using SIMD // operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // compute the inverse FFT of the aliased signal d_ifft->execute(); // Compute the magnitude and get the maximum value with its // index position volk_32fc_magnitude_squared_32f(d_magnitude_folded.data(), d_ifft->get_outbuf(), d_fft_size); // Normalize the maximum value to correct the scale factor // introduced by FFTW volk_gnsssdr_32f_index_max_32u(&indext, d_magnitude_folded.data(), d_fft_size); magt = d_magnitude_folded[indext] / (fft_normalization_factor * fft_normalization_factor); // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; // In case that d_bit_transition_flag = true, we compare the potentially // new maximum test statistics (d_mag/d_input_power) with the value in // d_test_statistics. When the second dwell is being processed, the value // of d_mag/d_input_power could be lower than d_test_statistics (i.e, // the maximum test statistics in the previous dwell is greater than // current d_mag/d_input_power). Note that d_test_statistics is not // restarted between consecutive dwells in multidwell operation. if (d_test_statistics < (d_mag / d_input_power) || !d_bit_transition_flag) { uint32_t detected_delay_samples_folded = 0; detected_delay_samples_folded = (indext % d_samples_per_code); std::array complex_acumulator{}; for (int32_t i = 0; i < static_cast(d_folding_factor); i++) { d_possible_delay[i] = detected_delay_samples_folded + (i)*d_fft_size; } for (int32_t i = 0; i < static_cast(d_folding_factor); i++) { // Copy a signal of 1 code length into suggested buffer. // The copied signal must have doppler effect corrected*/ memcpy(in_1code.data(), &in_temp[d_possible_delay[i]], sizeof(gr_complex) * (d_samples_per_code)); // Perform multiplication of the unmodified local // generated code with the incoming signal with doppler // effect corrected and accumulates its value. This // is indeed correlation in time for an specific value // of a shift volk_32fc_x2_multiply_32fc(corr_output.data(), in_1code.data(), d_code.data(), d_samples_per_code); for (int32_t j = 0; j < d_samples_per_code; j++) { complex_acumulator[i] += (corr_output[j]); } } // Obtain maximum value of correlation given the possible delay selected volk_32fc_magnitude_squared_32f(d_corr_output_f.data(), complex_acumulator.data(), d_folding_factor); volk_gnsssdr_32f_index_max_32u(&indext, d_corr_output_f.data(), d_folding_factor); // Now save the real code phase in the gnss_syncro block for use in other stages d_gnss_synchro->Acq_delay_samples = static_cast(d_possible_delay[indext]); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; // 5- Compute the test statistics and compare to the threshold d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; d_test_statistics = d_mag / d_input_power; } } // Record results to file if required if (d_dump) { // Since QuickSYnc performs a folded correlation in frequency by means // of the FFT, it is essential to also keep the values obtained from the // possible delay to show how it is maximize std::stringstream filename; std::streamsize n = sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_magnitude_folded.data()), n); // write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } if (!d_bit_transition_flag) { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else if (d_well_count == d_max_dwells) { d_state = 3; // Negative acquisition } } else { if (d_well_count == d_max_dwells) // d_max_dwells = 2 { if (d_test_statistics > d_threshold) { d_state = 2; // Positive acquisition } else { d_state = 3; // Negative acquisition } } } consume_each(1); break; } case 2: { // DLOG(INFO) << "START CASE 2"; // 6.1- Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "folding factor " << d_folding_factor; DLOG(INFO) << "possible delay correlation output"; for (int32_t i = 0; i < static_cast(d_folding_factor); i++) { DLOG(INFO) << d_possible_delay[i] << "\t\t\t" << d_corr_output_f[i]; } DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude folded " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_sampled_ms * d_samples_per_ms * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); // DLOG(INFO) << "END CASE 2"; break; } case 3: { // DLOG(INFO) << "START CASE 3"; // 6.2- Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "folding factor " << d_folding_factor; DLOG(INFO) << "possible delay corr output"; for (int32_t i = 0; i < static_cast(d_folding_factor); i++) { DLOG(INFO) << d_possible_delay[i] << "\t\t\t" << d_corr_output_f[i]; } DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude folded " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_sampled_ms * d_samples_per_ms * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); // DLOG(INFO) << "END CASE 3"; break; } } return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/pcps_quicksync_acquisition_cc.h000066400000000000000000000200421352176506000300020ustar00rootroot00000000000000/*! * \file pcps_quicksync_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition with the * QuickSync Algorithm * * Acquisition strategy (Kay Borre book CFAR + threshold). *
    *
  1. Compute the input signal power estimation *
  2. Doppler serial search loop *
  3. Perform folding of the incoming signal and local generated code *
  4. Perform the FFT-based circular convolution (parallel time search) *
  5. Record the maximum peak and the associated synchronization parameters *
  6. Compute the test statistics and compare to the threshold *
  7. Declare positive or negative acquisition using a message port *
  8. Obtain the adequate acquisition parameters by correlating the incoming * signal shifted by the possible folded delays *
* * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency * Approach", Birkha user, 2007. pp 81-84 * * \date Jun2 2014 * \author Damian Miralles Sanchez, dmiralles2009@gmail.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_QUICKSYNC_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_QUICKSYNC_ACQUISITION_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include #include #include #include class pcps_quicksync_acquisition_cc; using pcps_quicksync_acquisition_cc_sptr = boost::shared_ptr; pcps_quicksync_acquisition_cc_sptr pcps_quicksync_make_acquisition_cc( uint32_t folding_factor, uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition with * the implementation of the Sparse QuickSync Algorithm. * * Check \ref Navitec2012 "Faster GPS via the Sparse Fourier Transform", * for details of its implementation and functionality. */ class pcps_quicksync_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_quicksync_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for PCPS acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of PCPS algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_quicksync_acquisition_cc_sptr pcps_quicksync_make_acquisition_cc(uint32_t folding_factor, uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); pcps_quicksync_acquisition_cc(uint32_t folding_factor, uint32_t sampled_ms, uint32_t max_dwells, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, bool bit_transition_flag, bool dump, std::string dump_filename); void calculate_magnitudes(gr_complex* fft_begin, int32_t doppler_shift, int32_t doppler_offset); std::vector d_code; uint32_t d_folding_factor; // also referred in the paper as 'p' std::vector d_possible_delay; std::vector d_corr_output_f; std::vector d_magnitude_folded; std::vector d_signal_folded; std::vector d_code_folded; float d_noise_floor_power; int64_t d_fs_in; int32_t d_samples_per_ms; int32_t d_samples_per_code; uint32_t d_doppler_resolution; float d_threshold; std::string d_satellite_str; uint32_t d_doppler_max; uint32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_max_dwells; uint32_t d_well_count; uint32_t d_fft_size; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; uint32_t d_num_doppler_bins; std::vector d_fft_codes; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitude; float d_input_power; float d_test_statistics; bool d_bit_transition_flag; std::ofstream d_dump_file; bool d_active; int32_t d_state; bool d_dump; uint32_t d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; }; #endif /* GNSS_SDR_PCPS_QUICKSYNC_ACQUISITION_CC_H_ */ src/algorithms/acquisition/gnuradio_blocks/pcps_tong_acquisition_cc.cc000066400000000000000000000432361352176506000271100ustar00rootroot00000000000000/*! * \file pcps_tong_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition with * Tong algorithm. * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * Acquisition strategy (Kaplan book + CFAR threshold). *
    *
  1. Compute the input signal power estimation. *
  2. Doppler serial search loop. *
  3. Perform the FFT-based circular convolution (parallel time search). *
  4. Compute the tests statistics for all the cells. *
  5. Accumulate the grid of tests statistics with the previous grids. *
  6. Record the maximum peak and the associated synchronization parameters. *
  7. Compare the maximum averaged test statistics with a threshold. *
  8. If the test statistics exceeds the threshold, increment the Tong counter. *
  9. Otherwise, decrement the Tong counter. *
  10. If the Tong counter is equal to a given maximum value, declare positive *
  11. acquisition. If the Tong counter is equal to zero, declare negative *
  12. acquisition. Otherwise, process the next block. *
* * Kaplan book: D.Kaplan, J.Hegarty, "Understanding GPS. Principles * and Applications", Artech House, 2006, pp 223-227 * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pcps_tong_acquisition_cc.h" #include "GPS_L1_CA.h" // for GPS_TWO_PI #include #include #include #include #include #include #include pcps_tong_acquisition_cc_sptr pcps_tong_make_acquisition_cc( uint32_t sampled_ms, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, uint32_t tong_init_val, uint32_t tong_max_val, uint32_t tong_max_dwells, bool dump, std::string dump_filename) { return pcps_tong_acquisition_cc_sptr( new pcps_tong_acquisition_cc(sampled_ms, doppler_max, fs_in, samples_per_ms, samples_per_code, tong_init_val, tong_max_val, tong_max_dwells, dump, std::move(dump_filename))); } pcps_tong_acquisition_cc::pcps_tong_acquisition_cc( uint32_t sampled_ms, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, uint32_t tong_init_val, uint32_t tong_max_val, uint32_t tong_max_dwells, bool dump, std::string dump_filename) : gr::block("pcps_tong_acquisition_cc", gr::io_signature::make(1, 1, sizeof(gr_complex) * sampled_ms * samples_per_ms), gr::io_signature::make(0, 0, sizeof(gr_complex) * sampled_ms * samples_per_ms)) { this->message_port_register_out(pmt::mp("events")); d_sample_counter = 0ULL; // SAMPLE COUNTER d_active = false; d_state = 0; d_fs_in = fs_in; d_samples_per_ms = samples_per_ms; d_samples_per_code = samples_per_code; d_sampled_ms = sampled_ms; d_dwell_count = 0; d_tong_max_val = tong_max_val; d_tong_max_dwells = tong_max_dwells; d_tong_init_val = tong_init_val; d_tong_count = d_tong_init_val; d_doppler_max = doppler_max; d_fft_size = d_sampled_ms * d_samples_per_ms; d_mag = 0; d_input_power = 0.0; d_num_doppler_bins = 0; d_fft_codes.reserve(d_fft_size); d_magnitude.reserve(d_fft_size); // Direct FFT d_fft_if = std::make_shared(d_fft_size, true); // Inverse FFT d_ifft = std::make_shared(d_fft_size, false); // For dumping samples into a file d_dump = dump; d_dump_filename = std::move(dump_filename); d_doppler_resolution = 0; d_threshold = 0; d_doppler_step = 0; d_gnss_synchro = nullptr; d_code_phase = 0; d_doppler_freq = 0; d_test_statistics = 0; d_channel = 0; } pcps_tong_acquisition_cc::~pcps_tong_acquisition_cc() { try { if (d_dump) { d_dump_file.close(); } } catch (const std::ofstream::failure &e) { std::cerr << "Problem closing Acquisition dump file: " << d_dump_filename << '\n'; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } void pcps_tong_acquisition_cc::set_local_code(std::complex *code) { memcpy(d_fft_if->get_inbuf(), code, sizeof(gr_complex) * d_fft_size); d_fft_if->execute(); // We need the FFT of local code // Conjugate the local code volk_32fc_conjugate_32fc(d_fft_codes.data(), d_fft_if->get_outbuf(), d_fft_size); } void pcps_tong_acquisition_cc::init() { d_gnss_synchro->Flag_valid_acquisition = false; d_gnss_synchro->Flag_valid_symbol_output = false; d_gnss_synchro->Flag_valid_pseudorange = false; d_gnss_synchro->Flag_valid_word = false; d_gnss_synchro->Acq_doppler_step = 0U; d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_mag = 0.0; d_input_power = 0.0; // Count the number of bins d_num_doppler_bins = 0; for (auto doppler = static_cast(-d_doppler_max); doppler <= static_cast(d_doppler_max); doppler += d_doppler_step) { d_num_doppler_bins++; } // Create the carrier Doppler wipeoff signals and allocate data grid. d_grid_doppler_wipeoffs = std::vector>(d_num_doppler_bins, std::vector(d_fft_size)); d_grid_data = std::vector>(d_num_doppler_bins, std::vector(d_fft_size, 0.0)); for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { int32_t doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; float phase_step_rad = GPS_TWO_PI * doppler / static_cast(d_fs_in); std::array _phase{}; volk_gnsssdr_s32f_sincos_32fc(d_grid_doppler_wipeoffs[doppler_index].data(), -phase_step_rad, _phase.data(), d_fft_size); } } void pcps_tong_acquisition_cc::set_state(int32_t state) { d_state = state; if (d_state == 1) { d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_dwell_count = 0; d_tong_count = d_tong_init_val; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { for (uint32_t i = 0; i < d_fft_size; i++) { d_grid_data[doppler_index][i] = 0.0; } } } else if (d_state == 0) { } else { LOG(ERROR) << "State can only be set to 0 or 1"; } } int pcps_tong_acquisition_cc::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items __attribute__((unused))) { int32_t acquisition_message = -1; // 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL switch (d_state) { case 0: { if (d_active) { // restart acquisition variables d_gnss_synchro->Acq_delay_samples = 0.0; d_gnss_synchro->Acq_doppler_hz = 0.0; d_gnss_synchro->Acq_samplestamp_samples = 0ULL; d_gnss_synchro->Acq_doppler_step = 0U; d_dwell_count = 0; d_tong_count = d_tong_init_val; d_mag = 0.0; d_input_power = 0.0; d_test_statistics = 0.0; for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { for (uint32_t i = 0; i < d_fft_size; i++) { d_grid_data[doppler_index][i] = 0.0; } } d_state = 1; } d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); break; } case 1: { // initialize acquisition algorithm int32_t doppler; uint32_t indext = 0; float magt = 0.0; const auto *in = reinterpret_cast(input_items[0]); // Get the input samples pointer float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); d_input_power = 0.0; d_mag = 0.0; d_sample_counter += static_cast(d_fft_size); // sample counter d_dwell_count++; DLOG(INFO) << "Channel: " << d_channel << " , doing acquisition of satellite: " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN << " ,sample stamp: " << d_sample_counter << ", threshold: " << d_threshold << ", doppler_max: " << d_doppler_max << ", doppler_step: " << d_doppler_step; // 1- Compute the input signal power estimation volk_32fc_magnitude_squared_32f(d_magnitude.data(), in, d_fft_size); volk_32f_accumulator_s32f(&d_input_power, d_magnitude.data(), d_fft_size); d_input_power /= static_cast(d_fft_size); // 2- Doppler frequency search loop for (uint32_t doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) { // doppler search steps doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), in, d_grid_doppler_wipeoffs[doppler_index].data(), d_fft_size); // 3- Perform the FFT-based convolution (parallel time search) // Compute the FFT of the carrier wiped--off incoming signal d_fft_if->execute(); // Multiply carrier wiped--off, Fourier transformed incoming signal // with the local FFT'd code reference using SIMD operations with VOLK library volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), d_fft_if->get_outbuf(), d_fft_codes.data(), d_fft_size); // compute the inverse FFT d_ifft->execute(); // Compute magnitude volk_32fc_magnitude_squared_32f(d_magnitude.data(), d_ifft->get_outbuf(), d_fft_size); // Compute vector of test statistics corresponding to current doppler index. volk_32f_s32f_multiply_32f(d_magnitude.data(), d_magnitude.data(), 1 / (fft_normalization_factor * fft_normalization_factor * d_input_power), d_fft_size); // Accumulate test statistics in d_grid_data. volk_32f_x2_add_32f(d_grid_data[doppler_index].data(), d_magnitude.data(), d_grid_data[doppler_index].data(), d_fft_size); // Search maximum volk_gnsssdr_32f_index_max_32u(&indext, d_grid_data[doppler_index].data(), d_fft_size); magt = d_grid_data[doppler_index][indext]; // 4- record the maximum peak and the associated synchronization parameters if (d_mag < magt) { d_mag = magt; d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; d_gnss_synchro->Acq_doppler_step = d_doppler_step; } // Record results to file if required if (d_dump) { std::stringstream filename; std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write filename.str(""); filename << "../data/test_statistics_" << d_gnss_synchro->System << "_" << d_gnss_synchro->Signal[0] << d_gnss_synchro->Signal[1] << "_sat_" << d_gnss_synchro->PRN << "_doppler_" << doppler << ".dat"; d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); d_dump_file.write(reinterpret_cast(d_ifft->get_outbuf()), n); // write directly |abs(x)|^2 in this Doppler bin? d_dump_file.close(); } } // 5- Compute the test statistics and compare to the threshold d_test_statistics = d_mag; if (d_test_statistics > d_threshold * d_dwell_count) { d_tong_count++; if (d_tong_count == d_tong_max_val) { d_state = 2; // Positive acquisition } } else { d_tong_count--; if (d_tong_count == 0) { d_state = 3; // Negative acquisition } } if (d_dwell_count >= d_tong_max_dwells) { d_state = 3; // Negative acquisition } consume_each(1); break; } case 2: { // 6.1- Declare positive acquisition using a message port DLOG(INFO) << "positive acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 1; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } case 3: { // 6.2- Declare negative acquisition using a message port DLOG(INFO) << "negative acquisition"; DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; DLOG(INFO) << "sample_stamp " << d_sample_counter; DLOG(INFO) << "test statistics value " << d_test_statistics; DLOG(INFO) << "test statistics threshold " << d_threshold; DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; DLOG(INFO) << "magnitude " << d_mag; DLOG(INFO) << "input signal power " << d_input_power; d_active = false; d_state = 0; d_sample_counter += static_cast(d_fft_size * ninput_items[0]); // sample counter consume_each(ninput_items[0]); acquisition_message = 2; this->message_port_pub(pmt::mp("events"), pmt::from_long(acquisition_message)); break; } } return noutput_items; } src/algorithms/acquisition/gnuradio_blocks/pcps_tong_acquisition_cc.h000066400000000000000000000172111352176506000267440ustar00rootroot00000000000000/*! * \file pcps_tong_acquisition_cc.h * \brief This class implements a Parallel Code Phase Search Acquisition with * Tong algorithm. * \author Marc Molina, 2013. marc.molina.pena(at)gmail.com * * Acquisition strategy (Kaplan book + CFAR threshold). *
    *
  1. Compute the input signal power estimation. *
  2. Doppler serial search loop. *
  3. Perform the FFT-based circular convolution (parallel time search). *
  4. Compute the tests statistics for all the cells. *
  5. Accumulate the grid of tests statistics with the previous grids. *
  6. Record the maximum peak and the associated synchronization parameters. *
  7. Compare the maximum averaged test statistics with a threshold. *
  8. If the test statistics exceeds the threshold, increment the Tong counter. *
  9. Otherwise, decrement the Tong counter. *
  10. If the Tong counter is equal to a given maximum value, declare positive *
  11. acquisition. If the Tong counter is equa to zero, declare negative *
  12. acquisition. Otherwise, process the next block. *
* * Kaplan book: D.Kaplan, J.Hegarty, "Understanding GPS. Principles * and Applications", Artech House, 2006, pp 223-227 * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PCPS_TONG_ACQUISITION_CC_H_ #define GNSS_SDR_PCPS_TONG_ACQUISITION_CC_H_ #include "channel_fsm.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include class pcps_tong_acquisition_cc; using pcps_tong_acquisition_cc_sptr = boost::shared_ptr; pcps_tong_acquisition_cc_sptr pcps_tong_make_acquisition_cc( uint32_t sampled_ms, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, uint32_t tong_init_val, uint32_t tong_max_val, uint32_t tong_max_dwells, bool dump, std::string dump_filename); /*! * \brief This class implements a Parallel Code Phase Search Acquisition with * Tong algorithm. */ class pcps_tong_acquisition_cc : public gr::block { public: /*! * \brief Default destructor. */ ~pcps_tong_acquisition_cc(); /*! * \brief Set acquisition/tracking common Gnss_Synchro object pointer * to exchange synchronization data between acquisition and tracking blocks. * \param p_gnss_synchro Satellite information shared by the processing blocks. */ inline void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) { d_gnss_synchro = p_gnss_synchro; } /*! * \brief Returns the maximum peak of grid search. */ inline uint32_t mag() const { return d_mag; } /*! * \brief Initializes acquisition algorithm. */ void init(); /*! * \brief Sets local code for TONG acquisition algorithm. * \param code - Pointer to the PRN code. */ void set_local_code(std::complex* code); /*! * \brief Starts acquisition algorithm, turning from standby mode to * active mode * \param active - bool that activates/deactivates the block. */ inline void set_active(bool active) { d_active = active; } /*! * \brief If set to 1, ensures that acquisition starts at the * first available sample. * \param state - int=1 forces start of acquisition */ void set_state(int32_t state); /*! * \brief Set acquisition channel unique ID * \param channel - receiver channel. */ inline void set_channel(uint32_t channel) { d_channel = channel; } /*! * \brief Set channel fsm associated to this acquisition instance */ inline void set_channel_fsm(std::weak_ptr channel_fsm) { d_channel_fsm = std::move(channel_fsm); } /*! * \brief Set statistics threshold of TONG algorithm. * \param threshold - Threshold for signal detection (check \ref Navitec2012, * Algorithm 1, for a definition of this threshold). */ inline void set_threshold(float threshold) { d_threshold = threshold; } /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ inline void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ inline void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Parallel Code Phase Search Acquisition signal processing. */ int general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); private: friend pcps_tong_acquisition_cc_sptr pcps_tong_make_acquisition_cc(uint32_t sampled_ms, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, uint32_t tong_init_val, uint32_t tong_max_val, uint32_t tong_max_dwells, bool dump, std::string dump_filename); pcps_tong_acquisition_cc(uint32_t sampled_ms, uint32_t doppler_max, int64_t fs_in, int32_t samples_per_ms, int32_t samples_per_code, uint32_t tong_init_val, uint32_t tong_max_val, uint32_t tong_max_dwells, bool dump, std::string dump_filename); void calculate_magnitudes(gr_complex* fft_begin, int32_t doppler_shift, int32_t doppler_offset); int64_t d_fs_in; int32_t d_samples_per_ms; int32_t d_samples_per_code; uint32_t d_doppler_resolution; float d_threshold; std::string d_satellite_str; uint32_t d_doppler_max; uint32_t d_doppler_step; uint32_t d_sampled_ms; uint32_t d_dwell_count; uint32_t d_tong_count; uint32_t d_tong_init_val; uint32_t d_tong_max_val; uint32_t d_tong_max_dwells; uint32_t d_fft_size; uint64_t d_sample_counter; std::vector> d_grid_doppler_wipeoffs; uint32_t d_num_doppler_bins; std::vector d_fft_codes; std::vector> d_grid_data; std::shared_ptr d_fft_if; std::shared_ptr d_ifft; Gnss_Synchro* d_gnss_synchro; uint32_t d_code_phase; float d_doppler_freq; float d_mag; std::vector d_magnitude; float d_input_power; float d_test_statistics; std::ofstream d_dump_file; bool d_active; int32_t d_state; bool d_dump; uint32_t d_channel; std::weak_ptr d_channel_fsm; std::string d_dump_filename; }; #endif /* GNSS_SDR_PCPS_TONG_ACQUISITION_CC_H_ */ src/algorithms/acquisition/libs/000077500000000000000000000000001352176506000173045ustar00rootroot00000000000000src/algorithms/acquisition/libs/CMakeLists.txt000066400000000000000000000033171352176506000220500ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # if(ENABLE_FPGA) set(ACQUISITION_LIB_SOURCES fpga_acquisition.cc) set(ACQUISITION_LIB_HEADERS fpga_acquisition.h) endif() set(ACQUISITION_LIB_HEADERS ${ACQUISITION_LIB_HEADERS} acq_conf.h) set(ACQUISITION_LIB_SOURCES ${ACQUISITION_LIB_SOURCES} acq_conf.cc) list(SORT ACQUISITION_LIB_HEADERS) list(SORT ACQUISITION_LIB_SOURCES) source_group(Headers FILES ${ACQUISITION_LIB_HEADERS}) add_library(acquisition_libs ${ACQUISITION_LIB_SOURCES} ${ACQUISITION_LIB_HEADERS} ) target_link_libraries(acquisition_libs PUBLIC Volk::volk PRIVATE Gflags::gflags Glog::glog algorithms_libs core_system_parameters ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(acquisition_libs PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET acquisition_libs APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/acquisition/libs/acq_conf.cc000066400000000000000000000035741352176506000213750ustar00rootroot00000000000000/*! * \file acq_conf.cc * \brief Class that contains all the configuration parameters for generic * acquisition block based on the PCPS algorithm. * \author Carles Fernandez, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "acq_conf.h" Acq_Conf::Acq_Conf() { /* PCPS acquisition configuration */ sampled_ms = 0U; ms_per_code = 0U; max_dwells = 0U; samples_per_chip = 0U; doppler_max = 0U; num_doppler_bins_step2 = 0U; doppler_step2 = 0.0; fs_in = 0LL; samples_per_ms = 0.0; samples_per_code = 0.0; bit_transition_flag = false; use_CFAR_algorithm_flag = false; dump = false; blocking = false; make_2_steps = false; dump_filename = ""; dump_channel = 0U; it_size = sizeof(char); blocking_on_standby = false; use_automatic_resampler = false; resampler_ratio = 1.0; resampled_fs = 0LL; resampler_latency_samples = 0U; } src/algorithms/acquisition/libs/acq_conf.h000066400000000000000000000041151352176506000212270ustar00rootroot00000000000000/*! * \file acq_conf.h * \brief Class that contains all the configuration parameters for generic * acquisition block based on the PCPS algorithm. * \author Carles Fernandez, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_ACQ_CONF_H_ #define GNSS_SDR_ACQ_CONF_H_ #include #include #include class Acq_Conf { public: /* PCPS Acquisition configuration */ uint32_t sampled_ms; uint32_t ms_per_code; uint32_t samples_per_chip; uint32_t max_dwells; uint32_t doppler_max; uint32_t num_doppler_bins_step2; float doppler_step2; int64_t fs_in; float samples_per_ms; float samples_per_code; bool bit_transition_flag; bool use_CFAR_algorithm_flag; bool dump; bool blocking; bool blocking_on_standby; // enable it only for unit testing to avoid sample consume on idle status bool make_2_steps; bool use_automatic_resampler; float resampler_ratio; int64_t resampled_fs; uint32_t resampler_latency_samples; std::string dump_filename; uint32_t dump_channel; size_t it_size; Acq_Conf(); }; #endif src/algorithms/acquisition/libs/fpga_acquisition.cc000066400000000000000000000224661352176506000231520ustar00rootroot00000000000000/*! * \file fpga_acquisition.cc * \brief Highly optimized FPGA vector correlator class * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.cat *
* * Class that controls and executes a highly optimized acquisition HW * accelerator in the FPGA * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "fpga_acquisition.h" #include "GPS_L1_CA.h" // for GPS_TWO_PI #include // for LOG #include // for log2 #include // libraries used by the GIPO #include // for operator<< #include // libraries used by the GIPO #include // for write, close, read, ssize_t #include // for move #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(exp) \ ({ \ decltype(exp) _rc; \ do \ { \ _rc = (exp); \ } \ while (_rc == -1 && errno == EINTR); \ _rc; \ }) #endif Fpga_Acquisition::Fpga_Acquisition(std::string device_name, uint32_t nsamples, uint32_t doppler_max, uint32_t nsamples_total, int64_t fs_in, uint32_t sampled_ms __attribute__((unused)), uint32_t select_queue, uint32_t *all_fft_codes, uint32_t excludelimit) { uint32_t vector_length = nsamples_total; // initial values d_device_name = std::move(device_name); d_fs_in = fs_in; d_vector_length = vector_length; d_excludelimit = excludelimit; d_nsamples = nsamples; // number of samples not including padding d_select_queue = select_queue; d_nsamples_total = nsamples_total; d_doppler_max = doppler_max; d_doppler_step = 0; d_fd = 0; // driver descriptor d_map_base = nullptr; // driver memory map d_all_fft_codes = all_fft_codes; Fpga_Acquisition::open_device(); Fpga_Acquisition::reset_acquisition(); Fpga_Acquisition::fpga_acquisition_test_register(); Fpga_Acquisition::close_device(); d_PRN = 0; DLOG(INFO) << "Acquisition FPGA class created"; } bool Fpga_Acquisition::set_local_code(uint32_t PRN) { // select the code with the chosen PRN d_PRN = PRN; return true; } void Fpga_Acquisition::write_local_code() { d_map_base[9] = LOCAL_CODE_CLEAR_MEM; // write local code for (uint32_t k = 0; k < d_vector_length; k++) { d_map_base[6] = d_all_fft_codes[d_nsamples_total * (d_PRN - 1) + k]; } } void Fpga_Acquisition::open_device() { // open communication with HW accelerator if ((d_fd = open(d_device_name.c_str(), O_RDWR | O_SYNC)) == -1) { LOG(WARNING) << "Cannot open deviceio" << d_device_name; std::cout << "Acq: cannot open deviceio" << d_device_name << std::endl; } d_map_base = reinterpret_cast(mmap(nullptr, PAGE_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED, d_fd, 0)); if (d_map_base == reinterpret_cast(-1)) { LOG(WARNING) << "Cannot map the FPGA acquisition module into user memory"; std::cout << "Acq: cannot map deviceio" << d_device_name << std::endl; } } void Fpga_Acquisition::fpga_acquisition_test_register() { // sanity check : check test register uint32_t writeval = TEST_REG_SANITY_CHECK; uint32_t readval; // write value to test register d_map_base[15] = writeval; // read value from test register readval = d_map_base[15]; if (writeval != readval) { LOG(WARNING) << "Acquisition test register sanity check failed"; } else { LOG(INFO) << "Acquisition test register sanity check success!"; } } void Fpga_Acquisition::run_acquisition(void) { // enable interrupts int32_t reenable = 1; //int32_t disable_int = 0; ssize_t nbytes = TEMP_FAILURE_RETRY(write(d_fd, reinterpret_cast(&reenable), sizeof(int32_t))); if (nbytes != sizeof(int32_t)) { std::cerr << "Error enabling run in the FPGA." << std::endl; } // launch the acquisition process d_map_base[8] = LAUNCH_ACQUISITION; // writing a 1 to reg 8 launches the acquisition process int32_t irq_count; ssize_t nb; // wait for interrupt nb = read(d_fd, &irq_count, sizeof(irq_count)); if (nb != sizeof(irq_count)) { std::cout << "acquisition module Read failed to retrieve 4 bytes!" << std::endl; std::cout << "acquisition module Interrupt number " << irq_count << std::endl; } } void Fpga_Acquisition::set_block_exp(uint32_t total_block_exp) { d_map_base[11] = total_block_exp; } void Fpga_Acquisition::set_doppler_sweep(uint32_t num_sweeps, uint32_t doppler_step, int32_t doppler_min) { float phase_step_rad_real; int32_t phase_step_rad_int; // The doppler step can never be outside the range -pi to +pi, otherwise there would be aliasing // The FPGA expects phase_step_rad between -1 (-pi) to +1 (+pi) phase_step_rad_real = 2.0 * (doppler_min) / static_cast(d_fs_in); phase_step_rad_int = static_cast(phase_step_rad_real * (POW_2_31)); d_map_base[3] = phase_step_rad_int; // repeat the calculation with the doppler step phase_step_rad_real = 2.0 * (doppler_step) / static_cast(d_fs_in); phase_step_rad_int = static_cast(phase_step_rad_real * (POW_2_31)); // * 2^29 (in total it makes x2^31 in two steps to avoid the warnings d_map_base[4] = phase_step_rad_int; // write number of doppler sweeps d_map_base[5] = num_sweeps; } void Fpga_Acquisition::configure_acquisition() { //Fpga_Acquisition::(); d_map_base[0] = d_select_queue; d_map_base[1] = d_vector_length; d_map_base[2] = d_nsamples; d_map_base[7] = static_cast(log2(static_cast(d_vector_length))); // log2 FFTlength d_map_base[12] = d_excludelimit; } void Fpga_Acquisition::read_acquisition_results(uint32_t *max_index, float *firstpeak, float *secondpeak, uint64_t *initial_sample, float *power_sum, uint32_t *doppler_index, uint32_t *total_blk_exp) { uint64_t initial_sample_tmp = 0; uint32_t readval = 0; uint64_t readval_long = 0; uint64_t readval_long_shifted = 0; readval = d_map_base[1]; // read sample counter (LSW) initial_sample_tmp = readval; readval_long = d_map_base[2]; // read sample counter (MSW) readval_long_shifted = readval_long << 32; // 2^32 initial_sample_tmp = initial_sample_tmp + readval_long_shifted; // 2^32 *initial_sample = initial_sample_tmp; readval = d_map_base[3]; // read first peak value *firstpeak = static_cast(readval); readval = d_map_base[4]; // read second peak value *secondpeak = static_cast(readval); readval = d_map_base[5]; // read max index position *max_index = readval; *power_sum = 0; // power sum is not used readval = d_map_base[7]; // read doppler index -- this read releases the interrupt line *doppler_index = readval; readval = d_map_base[8]; // read FFT block exponent *total_blk_exp = readval; } void Fpga_Acquisition::close_device() { auto *aux = const_cast(d_map_base); if (munmap(static_cast(aux), PAGE_SIZE_DEFAULT) == -1) { std::cout << "Failed to unmap memory uio" << std::endl; } close(d_fd); } void Fpga_Acquisition::reset_acquisition(void) { //printf("============ resetting the hw now from the acquisition ==============="); d_map_base[8] = RESET_ACQUISITION; // writing a 2 to d_map_base[8] resets the acquisition. This causes a reset of all // the FPGA HW modules including the multicorrelators } // this function is only used for the unit tests void Fpga_Acquisition::read_fpga_total_scale_factor(uint32_t *total_scale_factor, uint32_t *fw_scale_factor) { uint32_t readval = 0; readval = d_map_base[8]; *total_scale_factor = readval; // only the total scale factor is used for the tests (fw scale factor to be removed) *fw_scale_factor = 0; } void Fpga_Acquisition::read_result_valid(uint32_t *result_valid) { uint32_t readval = 0; readval = d_map_base[0]; *result_valid = readval; } src/algorithms/acquisition/libs/fpga_acquisition.h000066400000000000000000000142051352176506000230040ustar00rootroot00000000000000/*! * \file fpga_acquisition.h * \brief Highly optimized FPGA vector correlator class * \authors
    *
  • Marc Majoral, 2019. mmajoral(at)cttc.cat *
* * Class that controls and executes a highly optimized acquisition HW * accelerator in the FPGA * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_FPGA_ACQUISITION_H_ #define GNSS_SDR_FPGA_ACQUISITION_H_ #include #include /*! * \brief Class that implements carrier wipe-off and correlators. */ class Fpga_Acquisition { public: /*! * \brief Constructor */ Fpga_Acquisition( std::string device_name, uint32_t nsamples, uint32_t doppler_max, uint32_t nsamples_total, int64_t fs_in, uint32_t sampled_ms, uint32_t select_queue, uint32_t *all_fft_codes, uint32_t excludelimit); /*! * \brief Destructor */ ~Fpga_Acquisition() = default; /*! * \brief Select the code with the chosen PRN */ bool set_local_code(uint32_t PRN); /*! * \brief Configure the doppler sweep parameters in the FPGA */ void set_doppler_sweep(uint32_t num_sweeps, uint32_t doppler_step, int32_t doppler_min); /*! * \brief Run the acquisition process in the FPGA */ void run_acquisition(void); /*! * \brief Read the results of the acquisition process */ void read_acquisition_results( uint32_t *max_index, float *firstpeak, float *secondpeak, uint64_t *initial_sample, float *power_sum, uint32_t *doppler_index, uint32_t *total_blk_exp); /*! * \brief Set maximum Doppler grid search * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. */ void set_doppler_max(uint32_t doppler_max) { d_doppler_max = doppler_max; } /*! * \brief Set Doppler steps for the grid search * \param doppler_step - Frequency bin of the search grid [Hz]. */ void set_doppler_step(uint32_t doppler_step) { d_doppler_step = doppler_step; } /*! * \brief Reset the FPGA PL. */ void reset_acquisition(void); /*! * \brief Read the scaling factor that has been used by the FFT-IFFT */ void read_fpga_total_scale_factor(uint32_t *total_scale_factor, uint32_t *fw_scale_factor); /*! * \brief Set the block exponent of the FFT in the FPGA. */ void set_block_exp(uint32_t total_block_exp); /*! * \brief Write the PRN code in the FPGA */ void write_local_code(void); /*! * \brief Write the acquisition parameters into the FPGA */ void configure_acquisition(void); /*! * \brief Open the device driver */ void open_device(); /*! * \brief Close the device driver */ void close_device(); private: // FPGA register parameters static const uint32_t PAGE_SIZE_DEFAULT = 0x10000; // default page size for the multicorrelator memory map static const uint32_t RESET_ACQUISITION = 2; // command to reset the multicorrelator static const uint32_t LAUNCH_ACQUISITION = 1; // command to launch the multicorrelator static const uint32_t TEST_REG_SANITY_CHECK = 0x55AA; // value to check the presence of the test register (to detect the hw) static const uint32_t LOCAL_CODE_CLEAR_MEM = 0x10000000; // command to clear the internal memory of the multicorrelator static const uint32_t MEM_LOCAL_CODE_WR_ENABLE = 0x0C000000; // command to enable the ENA and WR pins of the internal memory of the multicorrelator static const uint32_t POW_2_2 = 4; // 2^2 (used for the conversion of floating point numbers to integers) static const uint32_t POW_2_31 = 2147483648; // 2^31 (used for the conversion of floating point numbers to integers) static const uint32_t SELECT_LSBits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word static const uint32_t SELECT_MSBbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word static const uint32_t SELECT_ALL_CODE_BITS = 0xFFFFFFFF; // Select a 20 bit word static const uint32_t SHL_CODE_BITS = 65536; // shift left by 10 bits int64_t d_fs_in; // data related to the hardware module and the driver int32_t d_fd; // driver descriptor volatile uint32_t *d_map_base; // driver memory map uint32_t *d_all_fft_codes; // memory that contains all the code ffts uint32_t d_vector_length; // number of samples including padding and number of ms uint32_t d_excludelimit; uint32_t d_nsamples_total; // number of samples including padding uint32_t d_nsamples; // number of samples not including padding uint32_t d_select_queue; // queue selection std::string d_device_name; // HW device name uint32_t d_doppler_max; // max doppler uint32_t d_doppler_step; // doppler step uint32_t d_PRN; // PRN // FPGA private functions void fpga_acquisition_test_register(void); void read_result_valid(uint32_t *result_valid); }; #endif /* GNSS_SDR_FPGA_ACQUISITION_H_ */ src/algorithms/channel/000077500000000000000000000000001352176506000154335ustar00rootroot00000000000000src/algorithms/channel/CMakeLists.txt000066400000000000000000000014211352176506000201710ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) add_subdirectory(libs) src/algorithms/channel/adapters/000077500000000000000000000000001352176506000172365ustar00rootroot00000000000000src/algorithms/channel/adapters/CMakeLists.txt000066400000000000000000000032011352176506000217720ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(CHANNEL_ADAPTER_SOURCES channel.cc) set(CHANNEL_ADAPTER_HEADERS channel.h) add_library(channel_adapters ${CHANNEL_ADAPTER_SOURCES} ${CHANNEL_ADAPTER_HEADERS} ) source_group(Headers FILES ${CHANNEL_ADAPTER_HEADERS}) target_link_libraries(channel_adapters PUBLIC Gnuradio::runtime channel_libs core_system_parameters PRIVATE Gflags::gflags Glog::glog gnss_sdr_flags ) target_include_directories(channel_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/core/interfaces ${CMAKE_SOURCE_DIR}/src/core/receiver ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(channel_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET channel_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/channel/adapters/channel.cc000066400000000000000000000204031352176506000211540ustar00rootroot00000000000000/*! * \file channel.cc * \brief Implementation of a GNSS_Channel with a Finite State Machine * \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com * Luis Esteve, 2011. luis(at)epsilon-formacion.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "channel.h" #include "acquisition_interface.h" #include "channel_fsm.h" #include "configuration_interface.h" #include "gnss_sdr_flags.h" #include "telemetry_decoder_interface.h" #include "tracking_interface.h" #include #include // for std::move Channel::Channel(ConfigurationInterface* configuration, uint32_t channel, std::shared_ptr acq, std::shared_ptr trk, std::shared_ptr nav, std::string role, std::string implementation, std::shared_ptr > queue) { acq_ = std::move(acq); trk_ = std::move(trk); nav_ = std::move(nav); role_ = std::move(role); implementation_ = std::move(implementation); channel_ = channel; queue_ = std::move(queue); channel_fsm_ = std::make_shared(); flag_enable_fpga = configuration->property("GNSS-SDR.enable_FPGA", false); acq_->set_channel(channel_); acq_->set_channel_fsm(channel_fsm_); trk_->set_channel(channel_); nav_->set_channel(channel_); gnss_synchro_ = Gnss_Synchro(); gnss_synchro_.Channel_ID = channel_; acq_->set_gnss_synchro(&gnss_synchro_); trk_->set_gnss_synchro(&gnss_synchro_); // Provide a warning to the user about the change of parameter name if (channel_ == 0) { int64_t deprecation_warning = configuration->property("GNSS-SDR.internal_fs_hz", 0); if (deprecation_warning != 0) { std::cout << "WARNING: The global parameter name GNSS-SDR.internal_fs_hz has been DEPRECATED." << std::endl; std::cout << "WARNING: Please replace it by GNSS-SDR.internal_fs_sps in your configuration file." << std::endl; } } // IMPORTANT: Do not change the order between set_doppler_step and set_threshold uint32_t doppler_step = configuration->property("Acquisition_" + implementation_ + std::to_string(channel_) + ".doppler_step", 0); if (doppler_step == 0) { doppler_step = configuration->property("Acquisition_" + implementation_ + ".doppler_step", 500); } if (FLAGS_doppler_step != 0) { doppler_step = static_cast(FLAGS_doppler_step); } DLOG(INFO) << "Channel " << channel_ << " Doppler_step = " << doppler_step; acq_->set_doppler_step(doppler_step); float threshold = configuration->property("Acquisition_" + implementation_ + std::to_string(channel_) + ".threshold", 0.0); if (threshold == 0.0) { threshold = configuration->property("Acquisition_" + implementation_ + ".threshold", 0.0); } acq_->set_threshold(threshold); acq_->init(); repeat_ = configuration->property("Acquisition_" + implementation_ + std::to_string(channel_) + ".repeat_satellite", false); DLOG(INFO) << "Channel " << channel_ << " satellite repeat = " << repeat_; channel_fsm_->set_acquisition(acq_); channel_fsm_->set_tracking(trk_); channel_fsm_->set_telemetry(nav_); channel_fsm_->set_channel(channel_); channel_fsm_->set_queue(queue_); connected_ = false; gnss_signal_ = Gnss_Signal(implementation_); channel_msg_rx = channel_msg_receiver_make_cc(channel_fsm_, repeat_); } void Channel::connect(gr::top_block_sptr top_block) { if (!flag_enable_fpga) { acq_->connect(top_block); } trk_->connect(top_block); nav_->connect(top_block); // Synchronous ports top_block->connect(trk_->get_right_block(), 0, nav_->get_left_block(), 0); // Message ports top_block->msg_connect(nav_->get_left_block(), pmt::mp("telemetry_to_trk"), trk_->get_right_block(), pmt::mp("telemetry_to_trk")); DLOG(INFO) << "tracking -> telemetry_decoder"; // Message ports if (!flag_enable_fpga) { top_block->msg_connect(acq_->get_right_block(), pmt::mp("events"), channel_msg_rx, pmt::mp("events")); } top_block->msg_connect(trk_->get_right_block(), pmt::mp("events"), channel_msg_rx, pmt::mp("events")); connected_ = true; } void Channel::disconnect(gr::top_block_sptr top_block) { if (!connected_) { LOG(WARNING) << "Channel already disconnected internally"; return; } top_block->disconnect(trk_->get_right_block(), 0, nav_->get_left_block(), 0); if (!flag_enable_fpga) { acq_->disconnect(top_block); } trk_->disconnect(top_block); nav_->disconnect(top_block); top_block->msg_disconnect(nav_->get_left_block(), pmt::mp("telemetry_to_trk"), trk_->get_right_block(), pmt::mp("telemetry_to_trk")); if (!flag_enable_fpga) { top_block->msg_disconnect(acq_->get_right_block(), pmt::mp("events"), channel_msg_rx, pmt::mp("events")); } top_block->msg_disconnect(trk_->get_right_block(), pmt::mp("events"), channel_msg_rx, pmt::mp("events")); connected_ = false; } gr::basic_block_sptr Channel::get_left_block() { LOG(ERROR) << "Deprecated call to get_left_block() in channel interface"; return nullptr; } gr::basic_block_sptr Channel::get_left_block_trk() { return trk_->get_left_block(); } gr::basic_block_sptr Channel::get_left_block_acq() { if (flag_enable_fpga) { LOG(ERROR) << "Enabled FPGA and called get_left_block() in channel interface"; } return acq_->get_left_block(); } gr::basic_block_sptr Channel::get_right_block() { return nav_->get_right_block(); } void Channel::set_signal(const Gnss_Signal& gnss_signal) { std::lock_guard lk(mx); gnss_signal_ = gnss_signal; std::string str_aux = gnss_signal_.get_signal_str(); gnss_synchro_.Signal[0] = str_aux[0]; gnss_synchro_.Signal[1] = str_aux[1]; gnss_synchro_.Signal[2] = '\0'; // make sure that string length is only two characters gnss_synchro_.PRN = gnss_signal_.get_satellite().get_PRN(); gnss_synchro_.System = gnss_signal_.get_satellite().get_system_short().c_str()[0]; acq_->set_local_code(); if (flag_enable_fpga) { trk_->set_gnss_synchro(&gnss_synchro_); } nav_->set_satellite(gnss_signal_.get_satellite()); } void Channel::stop_channel() { std::lock_guard lk(mx); bool result = channel_fsm_->Event_stop_channel(); if (!result) { LOG(WARNING) << "Invalid channel event"; return; } DLOG(INFO) << "Channel stop_channel()"; } void Channel::assist_acquisition_doppler(double Carrier_Doppler_hz) { acq_->set_doppler_center(static_cast(Carrier_Doppler_hz)); } void Channel::start_acquisition() { std::lock_guard lk(mx); bool result = false; if (!flag_enable_fpga) { result = channel_fsm_->Event_start_acquisition(); } else { result = channel_fsm_->Event_start_acquisition_fpga(); channel_fsm_->start_acquisition(); } if (!result) { LOG(WARNING) << "Invalid channel event"; return; } DLOG(INFO) << "Channel start_acquisition()"; } src/algorithms/channel/adapters/channel.h000066400000000000000000000106151352176506000210220ustar00rootroot00000000000000/*! * \file channel.h * \brief Interface of a GNSS channel. * \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com * Luis Esteve, 2011. luis(at)epsilon-formacion.com * * It holds blocks for acquisition, tracking, * navigation data extraction and pseudorange calculation. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CHANNEL_H_ #define GNSS_SDR_CHANNEL_H_ #include "channel_fsm.h" #include "channel_interface.h" #include "channel_msg_receiver_cc.h" #include "concurrent_queue.h" #include "gnss_signal.h" #include "gnss_synchro.h" #include #include #include #include #include #include #include class ConfigurationInterface; class AcquisitionInterface; class TrackingInterface; class TelemetryDecoderInterface; /*! * \brief This class represents a GNSS channel. It wraps an AcquisitionInterface, * a Tracking Interface and a TelemetryDecoderInterface, and handles * their interaction through a Finite State Machine * */ class Channel : public ChannelInterface { public: //! Constructor Channel(ConfigurationInterface* configuration, uint32_t channel, std::shared_ptr acq, std::shared_ptr trk, std::shared_ptr nav, std::string role, std::string implementation, std::shared_ptr> queue); ~Channel() = default; //!< Destructor void connect(gr::top_block_sptr top_block) override; //!< connects the tracking block to the top_block and to the telemetry void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; //!< gets the gnuradio tracking block pointer gr::basic_block_sptr get_left_block_trk() override; //!< gets the gnuradio tracking block pointer gr::basic_block_sptr get_left_block_acq() override; //!< gets the gnuradio tracking block pointer gr::basic_block_sptr get_right_block() override; inline std::string role() override { return role_; } //! Returns "Channel" inline std::string implementation() override { return implementation_; } inline size_t item_size() override { return 0; } inline Gnss_Signal get_signal() const override { return gnss_signal_; } void start_acquisition() override; //!< Start the State Machine void stop_channel() override; //!< Stop the State Machine void set_signal(const Gnss_Signal& gnss_signal_) override; //!< Sets the channel GNSS signal void assist_acquisition_doppler(double Carrier_Doppler_hz) override; inline std::shared_ptr acquisition() { return acq_; } inline std::shared_ptr tracking() { return trk_; } inline std::shared_ptr telemetry() { return nav_; } void msg_handler_events(pmt::pmt_t msg); private: channel_msg_receiver_cc_sptr channel_msg_rx; std::shared_ptr acq_; std::shared_ptr trk_; std::shared_ptr nav_; std::string role_; std::string implementation_; bool flag_enable_fpga; uint32_t channel_; Gnss_Synchro gnss_synchro_{}; Gnss_Signal gnss_signal_; bool connected_; bool repeat_; std::shared_ptr channel_fsm_; std::shared_ptr> queue_; std::mutex mx; }; #endif // GNSS_SDR_CHANNEL_H_ src/algorithms/channel/libs/000077500000000000000000000000001352176506000163645ustar00rootroot00000000000000src/algorithms/channel/libs/CMakeLists.txt000066400000000000000000000033001352176506000211200ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(CHANNEL_FSM_SOURCES channel_fsm.cc channel_msg_receiver_cc.cc ) set(CHANNEL_FSM_HEADERS channel_fsm.h channel_msg_receiver_cc.h ) list(SORT CHANNEL_FSM_HEADERS) list(SORT CHANNEL_FSM_SOURCES) source_group(Headers FILES ${CHANNEL_FSM_HEADERS}) add_library(channel_libs ${CHANNEL_FSM_SOURCES} ${CHANNEL_FSM_HEADERS}) target_link_libraries(channel_libs PUBLIC core_system_parameters Gnuradio::runtime Gnuradio::pmt PRIVATE core_libs Boost::boost Gflags::gflags Glog::glog ) target_include_directories(channel_libs PUBLIC ${CMAKE_SOURCE_DIR}/src/core/receiver ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(channel_libs PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET channel_libs APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/channel/libs/channel_fsm.cc000066400000000000000000000122061352176506000211510ustar00rootroot00000000000000/*! * \file channel_fsm.cc * \brief Implementation of a State Machine for channel * \authors Javier Arribas, 2019. javiarribas@gmail.com * Antonio Ramos, 2017. antonio.ramos(at)cttc.es * Luis Esteve, 2011. luis(at)epsilon-formacion.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "channel_fsm.h" #include "channel_event.h" #include #include ChannelFsm::ChannelFsm() { acq_ = nullptr; trk_ = nullptr; channel_ = 0U; d_state = 0U; } ChannelFsm::ChannelFsm(std::shared_ptr acquisition) : acq_(std::move(acquisition)) { trk_ = nullptr; channel_ = 0U; d_state = 0U; } bool ChannelFsm::Event_stop_channel() { std::lock_guard lk(mx); DLOG(INFO) << "CH = " << channel_ << ". Ev stop channel"; switch (d_state) { case 0: // already in stanby break; case 1: // acquisition d_state = 0; stop_acquisition(); break; case 2: // tracking d_state = 0; stop_tracking(); break; default: break; } return true; } bool ChannelFsm::Event_start_acquisition_fpga() { std::lock_guard lk(mx); if ((d_state == 1) || (d_state == 2)) { return false; } d_state = 1; DLOG(INFO) << "CH = " << channel_ << ". Ev start acquisition FPGA"; return true; } bool ChannelFsm::Event_start_acquisition() { std::lock_guard lk(mx); if ((d_state == 1) || (d_state == 2)) { return false; } d_state = 1; start_acquisition(); DLOG(INFO) << "CH = " << channel_ << ". Ev start acquisition"; return true; } bool ChannelFsm::Event_valid_acquisition() { std::lock_guard lk(mx); if (d_state != 1) { return false; } d_state = 2; start_tracking(); DLOG(INFO) << "CH = " << channel_ << ". Ev valid acquisition"; return true; } bool ChannelFsm::Event_failed_acquisition_repeat() { std::lock_guard lk(mx); if (d_state != 1) { return false; } d_state = 1; start_acquisition(); DLOG(INFO) << "CH = " << channel_ << ". Ev failed acquisition repeat"; return true; } bool ChannelFsm::Event_failed_acquisition_no_repeat() { std::lock_guard lk(mx); if (d_state != 1) { return false; } d_state = 3; request_satellite(); DLOG(INFO) << "CH = " << channel_ << ". Ev failed acquisition no repeat"; return true; } bool ChannelFsm::Event_failed_tracking_standby() { std::lock_guard lk(mx); if (d_state != 2) { return false; } d_state = 0U; notify_stop_tracking(); DLOG(INFO) << "CH = " << channel_ << ". Ev failed tracking standby"; return true; } void ChannelFsm::set_acquisition(std::shared_ptr acquisition) { std::lock_guard lk(mx); acq_ = std::move(acquisition); } void ChannelFsm::set_tracking(std::shared_ptr tracking) { std::lock_guard lk(mx); trk_ = std::move(tracking); } void ChannelFsm::set_telemetry(std::shared_ptr telemetry) { std::lock_guard lk(mx); nav_ = std::move(telemetry); } void ChannelFsm::set_queue(std::shared_ptr> queue) { std::lock_guard lk(mx); queue_ = std::move(queue); } void ChannelFsm::set_channel(uint32_t channel) { std::lock_guard lk(mx); channel_ = channel; } void ChannelFsm::stop_acquisition() { acq_->stop_acquisition(); } void ChannelFsm::stop_tracking() { trk_->stop_tracking(); } void ChannelFsm::start_acquisition() { acq_->reset(); nav_->reset(); } void ChannelFsm::start_tracking() { trk_->start_tracking(); queue_->push(pmt::make_any(channel_event_make(channel_, 1))); } void ChannelFsm::request_satellite() { queue_->push(pmt::make_any(channel_event_make(channel_, 0))); } void ChannelFsm::notify_stop_tracking() { queue_->push(pmt::make_any(channel_event_make(channel_, 2))); } src/algorithms/channel/libs/channel_fsm.h000066400000000000000000000054411352176506000210160ustar00rootroot00000000000000/*! * \file channel_fsm.h * \brief Interface of the State Machine for channel * \authors Javier Arribas, 2019. javiarribas@gmail.com * Antonio Ramos, 2017. antonio.ramos(at)cttc.es * Luis Esteve, 2011. luis(at)epsilon-formacion.com * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CHANNEL_FSM_H #define GNSS_SDR_CHANNEL_FSM_H #include "acquisition_interface.h" #include "concurrent_queue.h" #include "telemetry_decoder_interface.h" #include "tracking_interface.h" #include #include #include #include /*! * \brief This class implements a State Machine for channel */ class ChannelFsm { public: ChannelFsm(); ChannelFsm(std::shared_ptr acquisition); void set_acquisition(std::shared_ptr acquisition); void set_tracking(std::shared_ptr tracking); void set_telemetry(std::shared_ptr telemetry); void set_queue(std::shared_ptr> queue); void set_channel(uint32_t channel); void start_acquisition(); // FSM EVENTS bool Event_start_acquisition(); bool Event_start_acquisition_fpga(); bool Event_valid_acquisition(); bool Event_stop_channel(); bool Event_failed_acquisition_repeat(); bool Event_failed_acquisition_no_repeat(); bool Event_failed_tracking_standby(); private: void start_tracking(); void stop_acquisition(); void stop_tracking(); void request_satellite(); void notify_stop_tracking(); std::shared_ptr acq_; std::shared_ptr trk_; std::shared_ptr nav_; std::shared_ptr> queue_; uint32_t channel_; uint32_t d_state; std::mutex mx; }; #endif // GNSS_SDR_CHANNEL_FSM_H src/algorithms/channel/libs/channel_msg_receiver_cc.cc000066400000000000000000000070521352176506000235060ustar00rootroot00000000000000/*! * \file channel_msg_receiver_cc.cc * \brief GNU Radio block that receives asynchronous channel messages from acquisition and tracking blocks * \author Javier Arribas, 2016. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "channel_msg_receiver_cc.h" #include #include #include #include #include #include #include channel_msg_receiver_cc_sptr channel_msg_receiver_make_cc(std::shared_ptr channel_fsm, bool repeat) { return channel_msg_receiver_cc_sptr(new channel_msg_receiver_cc(std::move(channel_fsm), repeat)); } channel_msg_receiver_cc::channel_msg_receiver_cc(std::shared_ptr channel_fsm, bool repeat) : gr::block("channel_msg_receiver_cc", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)) { this->message_port_register_in(pmt::mp("events")); this->set_msg_handler(pmt::mp("events"), boost::bind(&channel_msg_receiver_cc::msg_handler_events, this, _1)); d_channel_fsm = std::move(channel_fsm); d_repeat = repeat; } void channel_msg_receiver_cc::msg_handler_events(pmt::pmt_t msg) { bool result = false; try { int64_t message = pmt::to_long(std::move(msg)); switch (message) { case 1: // positive acquisition // Now the acquisition block can optionally trigger the event valid acquisition internally, // in order to reduce acquisition to tracking delay. result = d_channel_fsm->Event_valid_acquisition(); break; case 2: // negative acquisition if (d_repeat == true) { result = d_channel_fsm->Event_failed_acquisition_repeat(); } else { result = d_channel_fsm->Event_failed_acquisition_no_repeat(); } break; case 3: // tracking loss of lock event result = d_channel_fsm->Event_failed_tracking_standby(); break; default: LOG(WARNING) << "Default case, invalid message."; break; } } catch (boost::bad_any_cast& e) { LOG(WARNING) << "msg_handler_telemetry Bad any cast!"; } if (!result) { LOG(WARNING) << "msg_handler_telemetry invalid event"; } } src/algorithms/channel/libs/channel_msg_receiver_cc.h000066400000000000000000000043021352176506000233430ustar00rootroot00000000000000/*! * \file channel_msg_receiver_cc.h * \brief GNU Radio block that receives asynchronous channel messages from acquisition and tracking blocks * \author Javier Arribas, 2016. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CHANNEL_MSG_RECEIVER_CC_H #define GNSS_SDR_CHANNEL_MSG_RECEIVER_CC_H #include "channel_fsm.h" #include #include #include class channel_msg_receiver_cc; using channel_msg_receiver_cc_sptr = boost::shared_ptr; channel_msg_receiver_cc_sptr channel_msg_receiver_make_cc(std::shared_ptr channel_fsm, bool repeat); /*! * \brief GNU Radio block that receives asynchronous channel messages from acquisition and tracking blocks */ class channel_msg_receiver_cc : public gr::block { public: ~channel_msg_receiver_cc() = default; //!< Default destructor private: friend channel_msg_receiver_cc_sptr channel_msg_receiver_make_cc(std::shared_ptr channel_fsm, bool repeat); channel_msg_receiver_cc(std::shared_ptr channel_fsm, bool repeat); std::shared_ptr d_channel_fsm; bool d_repeat; // todo: change FSM to include repeat value void msg_handler_events(pmt::pmt_t msg); }; #endif src/algorithms/conditioner/000077500000000000000000000000001352176506000163405ustar00rootroot00000000000000src/algorithms/conditioner/CMakeLists.txt000066400000000000000000000013721352176506000211030ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) src/algorithms/conditioner/adapters/000077500000000000000000000000001352176506000201435ustar00rootroot00000000000000src/algorithms/conditioner/adapters/CMakeLists.txt000066400000000000000000000032461352176506000227100ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(COND_ADAPTER_SOURCES signal_conditioner.cc array_signal_conditioner.cc ) set(COND_ADAPTER_HEADERS signal_conditioner.h array_signal_conditioner.h ) list(SORT COND_ADAPTER_HEADERS) list(SORT COND_ADAPTER_SOURCES) source_group(Headers FILES ${COND_ADAPTER_HEADERS}) add_library(conditioner_adapters ${COND_ADAPTER_SOURCES} ${COND_ADAPTER_HEADERS}) target_link_libraries(conditioner_adapters PUBLIC Gnuradio::runtime PRIVATE Gflags::gflags Glog::glog ) target_include_directories(conditioner_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/core/interfaces ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(conditioner_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET conditioner_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/conditioner/adapters/array_signal_conditioner.cc000066400000000000000000000070651352176506000255320ustar00rootroot00000000000000/*! * \file array_signal_conditioner.cc * \brief It wraps blocks to change data type, filter and resample input data, adapted to array receiver * \author Javier Arribas jarribas (at) cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "array_signal_conditioner.h" #include "configuration_interface.h" #include #include // Constructor ArraySignalConditioner::ArraySignalConditioner(ConfigurationInterface *configuration, std::shared_ptr data_type_adapt, std::shared_ptr in_filt, std::shared_ptr res, std::string role, std::string implementation) : data_type_adapt_(std::move(data_type_adapt)), in_filt_(std::move(in_filt)), res_(std::move(res)), role_(std::move(role)), implementation_(std::move(implementation)) { connected_ = false; if (configuration) { }; } void ArraySignalConditioner::connect(gr::top_block_sptr top_block) { // note: the array signal conditioner do not have data type adapter, and must use the array input filter (multichannel) if (connected_) { LOG(WARNING) << "Array Signal conditioner already connected internally"; return; } //data_type_adapt_->connect(top_block); in_filt_->connect(top_block); res_->connect(top_block); //top_block->connect(data_type_adapt_->get_right_block(), 0, in_filt_->get_left_block(), 0); //DLOG(INFO) << "data_type_adapter -> input_filter"; top_block->connect(in_filt_->get_right_block(), 0, res_->get_left_block(), 0); DLOG(INFO) << "Array input_filter -> resampler"; connected_ = true; } void ArraySignalConditioner::disconnect(gr::top_block_sptr top_block) { if (!connected_) { LOG(WARNING) << "Array Signal conditioner already disconnected internally"; return; } //top_block->disconnect(data_type_adapt_->get_right_block(), 0, // in_filt_->get_left_block(), 0); top_block->disconnect(in_filt_->get_right_block(), 0, res_->get_left_block(), 0); //data_type_adapt_->disconnect(top_block); in_filt_->disconnect(top_block); res_->disconnect(top_block); connected_ = false; } gr::basic_block_sptr ArraySignalConditioner::get_left_block() { //return data_type_adapt_->get_left_block(); return in_filt_->get_left_block(); } gr::basic_block_sptr ArraySignalConditioner::get_right_block() { return res_->get_right_block(); } src/algorithms/conditioner/adapters/array_signal_conditioner.h000066400000000000000000000057211352176506000253710ustar00rootroot00000000000000/*! * \file array_signal_conditioner.h * \brief It wraps blocks to change data type, filter and resample input data, adapted to array receiver * \author Javier Arribas jarribas (at) cttc.es * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_ARRAY_SIGNAL_CONDITIONER_H_ #define GNSS_SDR_ARRAY_SIGNAL_CONDITIONER_H_ #include "gnss_block_interface.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class wraps blocks to change data_type_adapter, input_filter and resampler * to be applied to the input flow of sampled signal. */ class ArraySignalConditioner : public GNSSBlockInterface { public: //! Constructor ArraySignalConditioner(ConfigurationInterface *configuration, std::shared_ptr data_type_adapt, std::shared_ptr in_filt, std::shared_ptr res, std::string role, std::string implementation); //! Destructor ~ArraySignalConditioner() = default; void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; inline std::string role() override { return role_; } //! Returns "Array_Signal_Conditioner" inline std::string implementation() override { return "Array_Signal_Conditioner"; } inline size_t item_size() override { return 0; } inline std::shared_ptr data_type_adapter() { return data_type_adapt_; } inline std::shared_ptr input_filter() { return in_filt_; } inline std::shared_ptr resampler() { return res_; } private: std::shared_ptr data_type_adapt_; std::shared_ptr in_filt_; std::shared_ptr res_; std::string role_; std::string implementation_; bool connected_; }; #endif /*GNSS_SDR_SIGNAL_CONDITIONER_H_*/ src/algorithms/conditioner/adapters/signal_conditioner.cc000066400000000000000000000064361352176506000243350ustar00rootroot00000000000000/*! * \file signal_conditioner.cc * \brief It holds blocks to change data type, filter and resample input data. * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "signal_conditioner.h" #include "configuration_interface.h" #include #include // Constructor SignalConditioner::SignalConditioner(ConfigurationInterface *configuration, std::shared_ptr data_type_adapt, std::shared_ptr in_filt, std::shared_ptr res, std::string role, std::string implementation) : data_type_adapt_(std::move(data_type_adapt)), in_filt_(std::move(in_filt)), res_(std::move(res)), role_(std::move(role)), implementation_(std::move(implementation)) { connected_ = false; if (configuration) { }; } void SignalConditioner::connect(gr::top_block_sptr top_block) { if (connected_) { LOG(WARNING) << "Signal conditioner already connected internally"; return; } data_type_adapt_->connect(top_block); in_filt_->connect(top_block); res_->connect(top_block); top_block->connect(data_type_adapt_->get_right_block(), 0, in_filt_->get_left_block(), 0); DLOG(INFO) << "data_type_adapter -> input_filter"; top_block->connect(in_filt_->get_right_block(), 0, res_->get_left_block(), 0); DLOG(INFO) << "input_filter -> resampler"; connected_ = true; } void SignalConditioner::disconnect(gr::top_block_sptr top_block) { if (!connected_) { LOG(WARNING) << "Signal conditioner already disconnected internally"; return; } top_block->disconnect(data_type_adapt_->get_right_block(), 0, in_filt_->get_left_block(), 0); top_block->disconnect(in_filt_->get_right_block(), 0, res_->get_left_block(), 0); data_type_adapt_->disconnect(top_block); in_filt_->disconnect(top_block); res_->disconnect(top_block); connected_ = false; } gr::basic_block_sptr SignalConditioner::get_left_block() { return data_type_adapt_->get_left_block(); } gr::basic_block_sptr SignalConditioner::get_right_block() { return res_->get_right_block(); } src/algorithms/conditioner/adapters/signal_conditioner.h000066400000000000000000000056231352176506000241740ustar00rootroot00000000000000/*! * \file signal_conditioner.h * \brief It wraps blocks to change data type, filter and resample input data. * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_SIGNAL_CONDITIONER_H_ #define GNSS_SDR_SIGNAL_CONDITIONER_H_ #include "gnss_block_interface.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class wraps blocks to change data_type_adapter, input_filter and resampler * to be applied to the input flow of sampled signal. */ class SignalConditioner : public GNSSBlockInterface { public: //! Constructor SignalConditioner(ConfigurationInterface *configuration, std::shared_ptr data_type_adapt, std::shared_ptr in_filt, std::shared_ptr res, std::string role, std::string implementation); //! Destructor ~SignalConditioner() = default; void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; inline std::string role() override { return role_; } inline std::string implementation() override { return "Signal_Conditioner"; } //!< Returns "Signal_Conditioner" inline size_t item_size() override { return 0; } inline std::shared_ptr data_type_adapter() { return data_type_adapt_; } inline std::shared_ptr input_filter() { return in_filt_; } inline std::shared_ptr resampler() { return res_; } private: std::shared_ptr data_type_adapt_; std::shared_ptr in_filt_; std::shared_ptr res_; std::string role_; std::string implementation_; bool connected_; }; #endif /*GNSS_SDR_SIGNAL_CONDITIONER_H_*/ src/algorithms/data_type_adapter/000077500000000000000000000000001352176506000174755ustar00rootroot00000000000000src/algorithms/data_type_adapter/CMakeLists.txt000066400000000000000000000014361352176506000222410ustar00rootroot00000000000000# # Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) add_subdirectory(gnuradio_blocks) src/algorithms/data_type_adapter/adapters/000077500000000000000000000000001352176506000213005ustar00rootroot00000000000000src/algorithms/data_type_adapter/adapters/CMakeLists.txt000066400000000000000000000037641352176506000240520ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(DATATYPE_ADAPTER_SOURCES byte_to_short.cc ibyte_to_cbyte.cc ibyte_to_complex.cc ibyte_to_cshort.cc ishort_to_cshort.cc ishort_to_complex.cc ) set(DATATYPE_ADAPTER_HEADERS byte_to_short.h ibyte_to_cbyte.h ibyte_to_complex.h ibyte_to_cshort.h ishort_to_cshort.h ishort_to_complex.h ) list(SORT DATATYPE_ADAPTER_HEADERS) list(SORT DATATYPE_ADAPTER_SOURCES) source_group(Headers FILES ${DATATYPE_ADAPTER_HEADERS}) add_library(data_type_adapters ${DATATYPE_ADAPTER_SOURCES} ${DATATYPE_ADAPTER_HEADERS} ) target_link_libraries(data_type_adapters PUBLIC Gnuradio::blocks data_type_gr_blocks algorithms_libs core_system_parameters PRIVATE Gflags::gflags Glog::glog Volk::volk ) target_include_directories(data_type_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/algorithms/libs ${CMAKE_SOURCE_DIR}/src/core/interfaces ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(data_type_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET data_type_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/data_type_adapter/adapters/byte_to_short.cc000066400000000000000000000062321352176506000244760ustar00rootroot00000000000000/*! * \file byte_to_short.cc * \brief Adapts an 8-bits sample stream (IF) to a short int stream (IF) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "byte_to_short.h" #include "configuration_interface.h" #include #include #include ByteToShort::ByteToShort(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(std::move(role)), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "byte"; std::string default_output_item_type = "short"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); size_t item_size = sizeof(int16_t); gr_char_to_short_ = gr::blocks::char_to_short::make(); DLOG(INFO) << "data_type_adapter_(" << gr_char_to_short_->unique_id() << ")"; if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void ByteToShort::connect(gr::top_block_sptr top_block) { if (dump_) { top_block->connect(gr_char_to_short_, 0, file_sink_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } void ByteToShort::disconnect(gr::top_block_sptr top_block) { if (dump_) { top_block->disconnect(gr_char_to_short_, 0, file_sink_, 0); } } gr::basic_block_sptr ByteToShort::get_left_block() { return gr_char_to_short_; } gr::basic_block_sptr ByteToShort::get_right_block() { return gr_char_to_short_; } src/algorithms/data_type_adapter/adapters/byte_to_short.h000066400000000000000000000050101352176506000243310ustar00rootroot00000000000000/*! * \file byte_to_short.h * \brief Adapts an 8-bits sample stream (IF) to a short int stream (IF) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BYTE_TO_SHORT_H_ #define GNSS_SDR_BYTE_TO_SHORT_H_ #include "gnss_block_interface.h" #include #include #include class ConfigurationInterface; /*! * \brief Adapts an 8-bits sample stream (IF) to a short int stream (IF) * */ class ByteToShort : public GNSSBlockInterface { public: ByteToShort(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams); ~ByteToShort() = default; inline std::string role() override { return role_; } //! Returns "Byte_To_Short" inline std::string implementation() override { return "Byte_To_Short"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: gr::blocks::char_to_short::sptr gr_char_to_short_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; }; #endif src/algorithms/data_type_adapter/adapters/ibyte_to_cbyte.cc000066400000000000000000000106011352176506000246110ustar00rootroot00000000000000/*! * \file ibyte_to_cbyte.cc * \brief Adapts an I/Q interleaved byte (unsigned char) sample stream * into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ibyte_to_cbyte.h" #include "configuration_interface.h" #include #include IbyteToCbyte::IbyteToCbyte(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "byte"; std::string default_output_item_type = "lv_8sc_t"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); size_t item_size = sizeof(lv_8sc_t); ibyte_to_cbyte_ = make_interleaved_byte_to_complex_byte(); DLOG(INFO) << "data_type_adapter_(" << ibyte_to_cbyte_->unique_id() << ")"; if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (inverted_spectrum) { conjugate_ic_ = make_conjugate_ic(); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void IbyteToCbyte::connect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->connect(ibyte_to_cbyte_, 0, conjugate_ic_, 0); top_block->connect(conjugate_ic_, 0, file_sink_, 0); } else { top_block->connect(ibyte_to_cbyte_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->connect(ibyte_to_cbyte_, 0, conjugate_ic_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } } void IbyteToCbyte::disconnect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->disconnect(ibyte_to_cbyte_, 0, conjugate_ic_, 0); top_block->disconnect(conjugate_ic_, 0, file_sink_, 0); } else { top_block->disconnect(ibyte_to_cbyte_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->disconnect(ibyte_to_cbyte_, 0, conjugate_ic_, 0); } } } gr::basic_block_sptr IbyteToCbyte::get_left_block() { return ibyte_to_cbyte_; } gr::basic_block_sptr IbyteToCbyte::get_right_block() { if (inverted_spectrum) { return conjugate_ic_; } return ibyte_to_cbyte_; } src/algorithms/data_type_adapter/adapters/ibyte_to_cbyte.h000066400000000000000000000053221352176506000244570ustar00rootroot00000000000000/*! * \file ibyte_to_cbyte.h * \brief \brief Adapts an I/Q interleaved byte (unsigned char) sample stream * into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_IBYTE_TO_CBYTE_H_ #define GNSS_SDR_IBYTE_TO_CBYTE_H_ #include "conjugate_ic.h" #include "gnss_block_interface.h" #include "interleaved_byte_to_complex_byte.h" #include #include class ConfigurationInterface; /*! * \briefAdapts an I/Q interleaved byte (unsigned char) sample stream * into a std::complex stream */ class IbyteToCbyte : public GNSSBlockInterface { public: IbyteToCbyte(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~IbyteToCbyte() = default; inline std::string role() override { return role_; } //! Returns "Ibyte_To_Cbyte" inline std::string implementation() override { return "Ibyte_To_Cbyte"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: interleaved_byte_to_complex_byte_sptr ibyte_to_cbyte_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; conjugate_ic_sptr conjugate_ic_; bool inverted_spectrum; }; #endif src/algorithms/data_type_adapter/adapters/ibyte_to_complex.cc000066400000000000000000000110051352176506000251510ustar00rootroot00000000000000/*! * \file ibyte_to_complex.cc * \brief Adapts an I/Q interleaved byte integer sample stream to a gr_complex (float) stream * \author Javier Arribas, jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ibyte_to_complex.h" #include "configuration_interface.h" #include IbyteToComplex::IbyteToComplex(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "byte"; std::string default_output_item_type = "gr_complex"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); size_t item_size = sizeof(gr_complex); gr_interleaved_char_to_complex_ = gr::blocks::interleaved_char_to_complex::make(); DLOG(INFO) << "data_type_adapter_(" << gr_interleaved_char_to_complex_->unique_id() << ")"; if (inverted_spectrum) { conjugate_cc_ = make_conjugate_cc(); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void IbyteToComplex::connect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->connect(gr_interleaved_char_to_complex_, 0, conjugate_cc_, 0); top_block->connect(conjugate_cc_, 0, file_sink_, 0); } else { top_block->connect(gr_interleaved_char_to_complex_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->connect(gr_interleaved_char_to_complex_, 0, conjugate_cc_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } } void IbyteToComplex::disconnect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->disconnect(gr_interleaved_char_to_complex_, 0, conjugate_cc_, 0); top_block->disconnect(conjugate_cc_, 0, file_sink_, 0); } else { top_block->disconnect(gr_interleaved_char_to_complex_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->disconnect(gr_interleaved_char_to_complex_, 0, conjugate_cc_, 0); } } } gr::basic_block_sptr IbyteToComplex::get_left_block() { return gr_interleaved_char_to_complex_; } gr::basic_block_sptr IbyteToComplex::get_right_block() { if (inverted_spectrum) { return conjugate_cc_; } return gr_interleaved_char_to_complex_; } src/algorithms/data_type_adapter/adapters/ibyte_to_complex.h000066400000000000000000000053451352176506000250250ustar00rootroot00000000000000/*! * \file ibyte_to_complex.h * \brief Adapts an I/Q interleaved byte integer sample stream to a gr_complex (float) stream * \author Javier Arribas, jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_IBYTE_TO_COMPLEX_H_ #define GNSS_SDR_IBYTE_TO_COMPLEX_H_ #include "conjugate_cc.h" #include "gnss_block_interface.h" #include "gnss_synchro.h" #include #include #include class ConfigurationInterface; /*! * \brief Adapts an I/Q interleaved byte integer sample stream to a gr_complex (float) stream * */ class IbyteToComplex : public GNSSBlockInterface { public: IbyteToComplex(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~IbyteToComplex() = default; inline std::string role() override { return role_; } //! Returns "Ibyte_To_Complex" inline std::string implementation() override { return "Ibyte_To_Complex"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: gr::blocks::interleaved_char_to_complex::sptr gr_interleaved_char_to_complex_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; conjugate_cc_sptr conjugate_cc_; bool inverted_spectrum; }; #endif src/algorithms/data_type_adapter/adapters/ibyte_to_cshort.cc000066400000000000000000000107071352176506000250140ustar00rootroot00000000000000/*! * \file ibyte_to_cshort.cc * \brief Adapts an I/Q interleaved short integer (16 bits) sample stream * to a gr_complex (float) stream * \author Carles Fernandez-Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ibyte_to_cshort.h" #include "configuration_interface.h" #include #include IbyteToCshort::IbyteToCshort(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "byte"; std::string default_output_item_type = "cshort"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); size_t item_size = sizeof(lv_16sc_t); interleaved_byte_to_complex_short_ = make_interleaved_byte_to_complex_short(); DLOG(INFO) << "data_type_adapter_(" << interleaved_byte_to_complex_short_->unique_id() << ")"; if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (inverted_spectrum) { conjugate_sc_ = make_conjugate_sc(); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void IbyteToCshort::connect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->connect(interleaved_byte_to_complex_short_, 0, conjugate_sc_, 0); top_block->connect(conjugate_sc_, 0, file_sink_, 0); } else { top_block->connect(interleaved_byte_to_complex_short_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->connect(interleaved_byte_to_complex_short_, 0, conjugate_sc_, 0); } } } void IbyteToCshort::disconnect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->disconnect(interleaved_byte_to_complex_short_, 0, conjugate_sc_, 0); top_block->disconnect(conjugate_sc_, 0, file_sink_, 0); } else { top_block->disconnect(interleaved_byte_to_complex_short_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->disconnect(interleaved_byte_to_complex_short_, 0, conjugate_sc_, 0); } } } gr::basic_block_sptr IbyteToCshort::get_left_block() { return interleaved_byte_to_complex_short_; } gr::basic_block_sptr IbyteToCshort::get_right_block() { if (inverted_spectrum) { return conjugate_sc_; } return interleaved_byte_to_complex_short_; } src/algorithms/data_type_adapter/adapters/ibyte_to_cshort.h000066400000000000000000000053011352176506000246500ustar00rootroot00000000000000/*! * \file ibyte_to_cshort.h * \brief Adapts a short interleaved sample stream into a std::complex stream * \author Carles Fernandez-Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_IBYTE_TO_CSHORT_H_ #define GNSS_SDR_IBYTE_TO_CSHORT_H_ #include "conjugate_sc.h" #include "gnss_block_interface.h" #include "interleaved_byte_to_complex_short.h" #include #include class ConfigurationInterface; /*! * \brief Adapts a short integer (16 bits) interleaved sample stream into a std::complex stream * */ class IbyteToCshort : public GNSSBlockInterface { public: IbyteToCshort(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~IbyteToCshort() = default; inline std::string role() override { return role_; } //! Returns "Ibyte_To_Cshort" inline std::string implementation() override { return "Ibyte_To_Cshort"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: interleaved_byte_to_complex_short_sptr interleaved_byte_to_complex_short_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; conjugate_sc_sptr conjugate_sc_; bool inverted_spectrum; }; #endif src/algorithms/data_type_adapter/adapters/ishort_to_complex.cc000066400000000000000000000110321352176506000253450ustar00rootroot00000000000000/*! * \file ishort_to_complex.cc * \brief Adapts an I/Q interleaved short integer sample stream to a gr_complex (float) stream * \author Javier Arribas, jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ishort_to_complex.h" #include "configuration_interface.h" #include IshortToComplex::IshortToComplex(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "short"; std::string default_output_item_type = "gr_complex"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); size_t item_size = sizeof(gr_complex); gr_interleaved_short_to_complex_ = gr::blocks::interleaved_short_to_complex::make(); DLOG(INFO) << "data_type_adapter_(" << gr_interleaved_short_to_complex_->unique_id() << ")"; if (inverted_spectrum) { conjugate_cc_ = make_conjugate_cc(); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void IshortToComplex::connect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->connect(gr_interleaved_short_to_complex_, 0, conjugate_cc_, 0); top_block->connect(conjugate_cc_, 0, file_sink_, 0); } else { top_block->connect(gr_interleaved_short_to_complex_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->connect(gr_interleaved_short_to_complex_, 0, conjugate_cc_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } } void IshortToComplex::disconnect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->disconnect(gr_interleaved_short_to_complex_, 0, conjugate_cc_, 0); top_block->disconnect(conjugate_cc_, 0, file_sink_, 0); } else { top_block->disconnect(gr_interleaved_short_to_complex_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->disconnect(gr_interleaved_short_to_complex_, 0, conjugate_cc_, 0); } } } gr::basic_block_sptr IshortToComplex::get_left_block() { return gr_interleaved_short_to_complex_; } gr::basic_block_sptr IshortToComplex::get_right_block() { if (inverted_spectrum) { return conjugate_cc_; } return gr_interleaved_short_to_complex_; } src/algorithms/data_type_adapter/adapters/ishort_to_complex.h000066400000000000000000000053301352176506000252130ustar00rootroot00000000000000/*! * \file ishort_to_complex.h * \brief Adapts an I/Q interleaved short integer sample stream to a gr_complex (float) stream * \author Javier Arribas, jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_ISHORT_TO_COMPLEX_H_ #define GNSS_SDR_ISHORT_TO_COMPLEX_H_ #include "conjugate_cc.h" #include "gnss_block_interface.h" #include #include #include class ConfigurationInterface; /*! * \brief Adapts an I/Q interleaved short integer sample stream to a gr_complex (float) stream * */ class IshortToComplex : public GNSSBlockInterface { public: IshortToComplex(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~IshortToComplex() = default; inline std::string role() override { return role_; } //! Returns "Ishort_To_Complex" inline std::string implementation() override { return "Ishort_To_Complex"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: gr::blocks::interleaved_short_to_complex::sptr gr_interleaved_short_to_complex_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; conjugate_cc_sptr conjugate_cc_; bool inverted_spectrum; }; #endif src/algorithms/data_type_adapter/adapters/ishort_to_cshort.cc000066400000000000000000000111231352176506000252010ustar00rootroot00000000000000/*! * \file ishort_to_cshort.cc * \brief Adapts an I/Q interleaved short integer (16 bits) sample stream * to a gr_complex (float) stream * \author Carles Fernandez-Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "ishort_to_cshort.h" #include "configuration_interface.h" #include #include IshortToCshort::IshortToCshort(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "short"; std::string default_output_item_type = "cshort"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); size_t item_size = sizeof(lv_16sc_t); interleaved_short_to_complex_short_ = make_interleaved_short_to_complex_short(); DLOG(INFO) << "data_type_adapter_(" << interleaved_short_to_complex_short_->unique_id() << ")"; if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (inverted_spectrum) { conjugate_sc_ = make_conjugate_sc(); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void IshortToCshort::connect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->connect(interleaved_short_to_complex_short_, 0, conjugate_sc_, 0); top_block->connect(conjugate_sc_, 0, file_sink_, 0); } else { top_block->connect(interleaved_short_to_complex_short_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->connect(interleaved_short_to_complex_short_, 0, conjugate_sc_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } } void IshortToCshort::disconnect(gr::top_block_sptr top_block) { if (dump_) { if (inverted_spectrum) { top_block->disconnect(interleaved_short_to_complex_short_, 0, conjugate_sc_, 0); top_block->disconnect(conjugate_sc_, 0, file_sink_, 0); } else { top_block->disconnect(interleaved_short_to_complex_short_, 0, file_sink_, 0); } } else { if (inverted_spectrum) { top_block->disconnect(interleaved_short_to_complex_short_, 0, conjugate_sc_, 0); } } } gr::basic_block_sptr IshortToCshort::get_left_block() { return interleaved_short_to_complex_short_; } gr::basic_block_sptr IshortToCshort::get_right_block() { if (inverted_spectrum) { return conjugate_sc_; } return interleaved_short_to_complex_short_; } src/algorithms/data_type_adapter/adapters/ishort_to_cshort.h000066400000000000000000000053141352176506000250500ustar00rootroot00000000000000/*! * \file ishort_to_cshort.h * \brief Adapts a short interleaved sample stream into a std::complex stream * \author Carles Fernandez-Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_ISHORT_TO_CSHORT_H_ #define GNSS_SDR_ISHORT_TO_CSHORT_H_ #include "conjugate_sc.h" #include "gnss_block_interface.h" #include "interleaved_short_to_complex_short.h" #include #include class ConfigurationInterface; /*! * \brief Adapts a short integer (16 bits) interleaved sample stream into a std::complex stream * */ class IshortToCshort : public GNSSBlockInterface { public: IshortToCshort(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~IshortToCshort() = default; inline std::string role() override { return role_; } //! Returns "Ishort_To_Cshort" inline std::string implementation() override { return "Ishort_To_Cshort"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: interleaved_short_to_complex_short_sptr interleaved_short_to_complex_short_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; conjugate_sc_sptr conjugate_sc_; bool inverted_spectrum; }; #endif src/algorithms/data_type_adapter/gnuradio_blocks/000077500000000000000000000000001352176506000226425ustar00rootroot00000000000000src/algorithms/data_type_adapter/gnuradio_blocks/CMakeLists.txt000066400000000000000000000030761352176506000254100ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(DATA_TYPE_GR_BLOCKS_SOURCES interleaved_byte_to_complex_byte.cc interleaved_short_to_complex_short.cc interleaved_byte_to_complex_short.cc ) set(DATA_TYPE_GR_BLOCKS_HEADERS interleaved_byte_to_complex_byte.h interleaved_short_to_complex_short.h interleaved_byte_to_complex_short.h ) list(SORT DATA_TYPE_GR_BLOCKS_HEADERS) list(SORT DATA_TYPE_GR_BLOCKS_SOURCES) source_group(Headers FILES ${DATA_TYPE_GR_BLOCKS_HEADERS}) add_library(data_type_gr_blocks ${DATA_TYPE_GR_BLOCKS_SOURCES} ${DATA_TYPE_GR_BLOCKS_HEADERS} ) target_link_libraries(data_type_gr_blocks PUBLIC Gnuradio::runtime Boost::boost algorithms_libs PRIVATE Volk::volk ) set_property(TARGET data_type_gr_blocks APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_byte_to_complex_byte.cc000066400000000000000000000053261352176506000317600ustar00rootroot00000000000000/*! * \file interleaved_byte_to_complex_byte.cc * \brief Adapts an 8-bits interleaved sample stream into a 16-bits complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "interleaved_byte_to_complex_byte.h" #include #include interleaved_byte_to_complex_byte_sptr make_interleaved_byte_to_complex_byte() { return interleaved_byte_to_complex_byte_sptr(new interleaved_byte_to_complex_byte()); } interleaved_byte_to_complex_byte::interleaved_byte_to_complex_byte() : sync_decimator("interleaved_byte_to_complex_byte", gr::io_signature::make(1, 1, sizeof(int8_t)), gr::io_signature::make(1, 1, sizeof(lv_8sc_t)), // lv_8sc_t is a Volk's typedef for std::complex 2) { const int alignment_multiple = volk_get_alignment() / sizeof(lv_8sc_t); set_alignment(std::max(1, alignment_multiple)); } int interleaved_byte_to_complex_byte::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); // This could be put into a Volk kernel int8_t real_part; int8_t imag_part; for (int number = 0; number < noutput_items; number++) { // lv_cmake(r, i) defined at volk/volk_complex.h real_part = *in++; imag_part = *in++; *out++ = lv_cmake(real_part, imag_part); } return noutput_items; } src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_byte_to_complex_byte.h000066400000000000000000000040451352176506000316170ustar00rootroot00000000000000/*! * \file interleaved_byte_to_complex_byte.h * \brief Adapts an 8-bits interleaved sample stream into a 16-bits complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_INTERLEAVED_BYTE_TO_COMPLEX_BYTE_H_ #define GNSS_SDR_INTERLEAVED_BYTE_TO_COMPLEX_BYTE_H_ #include #include class interleaved_byte_to_complex_byte; using interleaved_byte_to_complex_byte_sptr = boost::shared_ptr; interleaved_byte_to_complex_byte_sptr make_interleaved_byte_to_complex_byte(); /*! * \brief This class adapts an 8-bits interleaved sample stream * into a 16-bits complex stream (std::complex) */ class interleaved_byte_to_complex_byte : public gr::sync_decimator { public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend interleaved_byte_to_complex_byte_sptr make_interleaved_byte_to_complex_byte(); interleaved_byte_to_complex_byte(); }; #endif src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_byte_to_complex_short.cc000066400000000000000000000054241352176506000321530ustar00rootroot00000000000000/*! * \file interleaved_byte_to_complex_short.cc * \brief Adapts a byte (8-bits) interleaved sample stream into a std::complex stream * \author Javier Arribas (jarribas(at)cttc.es) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "interleaved_byte_to_complex_short.h" #include #include interleaved_byte_to_complex_short_sptr make_interleaved_byte_to_complex_short() { return interleaved_byte_to_complex_short_sptr(new interleaved_byte_to_complex_short()); } interleaved_byte_to_complex_short::interleaved_byte_to_complex_short() : sync_decimator("interleaved_byte_to_complex_short", gr::io_signature::make(1, 1, sizeof(int8_t)), gr::io_signature::make(1, 1, sizeof(lv_16sc_t)), // lv_16sc_t is a Volk's typedef for std::complex 2) { const int alignment_multiple = volk_get_alignment() / sizeof(lv_16sc_t); set_alignment(std::max(1, alignment_multiple)); } int interleaved_byte_to_complex_short::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); // This could be put into a Volk kernel int8_t real_part; int8_t imag_part; for (int number = 0; number < noutput_items; number++) { // lv_cmake(r, i) defined at volk/volk_complex.h real_part = *in++; imag_part = *in++; *out++ = lv_cmake(static_cast(real_part), static_cast(imag_part)); } return noutput_items; } src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_byte_to_complex_short.h000066400000000000000000000041201352176506000320050ustar00rootroot00000000000000/*! * \file interleaved_byte_to_complex_short.h * \brief Adapts a byte (8-bits) interleaved sample stream into a std::complex stream * \author Javier Arribas (jarribas(at)cttc.es) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_INTERLEAVED_BYTE_TO_COMPLEX_SHORT_H_ #define GNSS_SDR_INTERLEAVED_BYTE_TO_COMPLEX_SHORT_H_ #include #include class interleaved_byte_to_complex_short; using interleaved_byte_to_complex_short_sptr = boost::shared_ptr; interleaved_byte_to_complex_short_sptr make_interleaved_byte_to_complex_short(); /*! * \brief This class adapts a short (16-bits) interleaved sample stream * into a std::complex stream */ class interleaved_byte_to_complex_short : public gr::sync_decimator { public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend interleaved_byte_to_complex_short_sptr make_interleaved_byte_to_complex_short(); interleaved_byte_to_complex_short(); }; #endif // GNSS_SDR_INTERLEAVED_BYTE_TO_COMPLEX_SHORT_H_ src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_short_to_complex_short.cc000066400000000000000000000054101352176506000323420ustar00rootroot00000000000000/*! * \file interleaved_short_to_complex_short.cc * \brief Adapts a short (16-bits) interleaved sample stream into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "interleaved_short_to_complex_short.h" #include #include interleaved_short_to_complex_short_sptr make_interleaved_short_to_complex_short() { return interleaved_short_to_complex_short_sptr(new interleaved_short_to_complex_short()); } interleaved_short_to_complex_short::interleaved_short_to_complex_short() : sync_decimator("interleaved_short_to_complex_short", gr::io_signature::make(1, 1, sizeof(int16_t)), gr::io_signature::make(1, 1, sizeof(lv_16sc_t)), // lv_16sc_t is a Volk's typedef for std::complex 2) { const int alignment_multiple = volk_get_alignment() / sizeof(lv_16sc_t); set_alignment(std::max(1, alignment_multiple)); } int interleaved_short_to_complex_short::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); // This could be put into a Volk kernel int16_t real_part; int16_t imag_part; for (int number = 0; number < noutput_items; number++) { // lv_cmake(r, i) defined at volk/volk_complex.h real_part = *in++; imag_part = *in++; *out++ = lv_cmake(real_part, imag_part); } return noutput_items; } src/algorithms/data_type_adapter/gnuradio_blocks/interleaved_short_to_complex_short.h000066400000000000000000000041511352176506000322050ustar00rootroot00000000000000/*! * \file interleaved_short_to_complex_short.h * \brief Adapts a short (16-bits) interleaved sample stream into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_INTERLEAVED_SHORT_TO_COMPLEX_SHORT_H_ #define GNSS_SDR_INTERLEAVED_SHORT_TO_COMPLEX_SHORT_H_ #include #include class interleaved_short_to_complex_short; using interleaved_short_to_complex_short_sptr = boost::shared_ptr; interleaved_short_to_complex_short_sptr make_interleaved_short_to_complex_short(); /*! * \brief This class adapts a short (16-bits) interleaved sample stream * into a std::complex stream */ class interleaved_short_to_complex_short : public gr::sync_decimator { public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend interleaved_short_to_complex_short_sptr make_interleaved_short_to_complex_short(); interleaved_short_to_complex_short(); }; #endif // GNSS_SDR_INTERLEAVED_SHORT_TO_COMPLEX_SHORT_H_ src/algorithms/input_filter/000077500000000000000000000000001352176506000165275ustar00rootroot00000000000000src/algorithms/input_filter/CMakeLists.txt000066400000000000000000000014341352176506000212710ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(adapters) add_subdirectory(gnuradio_blocks) src/algorithms/input_filter/adapters/000077500000000000000000000000001352176506000203325ustar00rootroot00000000000000src/algorithms/input_filter/adapters/CMakeLists.txt000066400000000000000000000042631352176506000230770ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(INPUT_FILTER_ADAPTER_SOURCES fir_filter.cc freq_xlating_fir_filter.cc beamformer_filter.cc pulse_blanking_filter.cc notch_filter.cc notch_filter_lite.cc ) set(INPUT_FILTER_ADAPTER_HEADERS fir_filter.h freq_xlating_fir_filter.h beamformer_filter.h pulse_blanking_filter.h notch_filter.h notch_filter_lite.h ) list(SORT INPUT_FILTER_ADAPTER_HEADERS) list(SORT INPUT_FILTER_ADAPTER_SOURCES) source_group(Headers FILES ${INPUT_FILTER_ADAPTER_HEADERS}) add_library(input_filter_adapters ${INPUT_FILTER_ADAPTER_SOURCES} ${INPUT_FILTER_ADAPTER_HEADERS} ) target_link_libraries(input_filter_adapters PUBLIC Gnuradio::blocks Gnuradio::filter input_filter_gr_blocks algorithms_libs PRIVATE Gflags::gflags Glog::glog Volk::volk ) target_include_directories(input_filter_adapters PUBLIC ${CMAKE_SOURCE_DIR}/src/algorithms/libs ${CMAKE_SOURCE_DIR}/src/core/interfaces ) if(NOT (GNURADIO_VERSION VERSION_LESS "3.8")) target_compile_definitions(input_filter_adapters PUBLIC -DGR_GREATER_38=1) endif() if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(input_filter_adapters PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET input_filter_adapters APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/input_filter/adapters/beamformer_filter.cc000066400000000000000000000070121352176506000243250ustar00rootroot00000000000000/*! * \file beamformer_filter.cc * \brief Interface of an adapter of a digital beamformer * \author Javier Arribas jarribas (at) cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beamformer_filter.h" #include "beamformer.h" #include "configuration_interface.h" #include #include BeamformerFilter::BeamformerFilter( ConfigurationInterface* configuration, const std::string& role, unsigned int in_stream, unsigned int out_stream) : role_(role), in_stream_(in_stream), out_stream_(out_stream) { std::string default_item_type = "gr_complex"; std::string default_dump_file = "./data/input_filter.dat"; item_type_ = configuration->property(role + ".item_type", default_item_type); dump_ = configuration->property(role + ".dump", false); DLOG(INFO) << "dump_ is " << dump_; dump_filename_ = configuration->property(role + ".dump_filename", default_dump_file); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); beamformer_ = make_beamformer_sptr(); DLOG(INFO) << "Item size " << item_size_; DLOG(INFO) << "resampler(" << beamformer_->unique_id() << ")"; } else { LOG(WARNING) << item_type_ << " unrecognized item type for beamformer"; item_size_ = sizeof(gr_complex); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size_, dump_filename_.c_str()); DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; } samples_ = 0ULL; if (in_stream_ > 8) { LOG(ERROR) << "This implementation only supports eight input streams"; } if (out_stream_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void BeamformerFilter::connect(gr::top_block_sptr top_block) { if (dump_) { top_block->connect(beamformer_, 0, file_sink_, 0); DLOG(INFO) << "connected beamformer output to file sink"; } else { DLOG(INFO) << "nothing to connect internally"; } } void BeamformerFilter::disconnect(gr::top_block_sptr top_block) { if (dump_) { top_block->disconnect(beamformer_, 0, file_sink_, 0); } } gr::basic_block_sptr BeamformerFilter::get_left_block() { return beamformer_; } gr::basic_block_sptr BeamformerFilter::get_right_block() { return beamformer_; } src/algorithms/input_filter/adapters/beamformer_filter.h000066400000000000000000000050021352176506000241640ustar00rootroot00000000000000/*! * \file beamformer_filter.h * \brief Interface of an adapter of a digital beamformer * \author Javier Arribas jarribas (at) cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEAMFORMER_FILTER_H_ #define GNSS_SDR_BEAMFORMER_FILTER_H_ #include "gnss_block_interface.h" #include #include #include class ConfigurationInterface; /*! * \brief Interface of an adapter of a direct resampler conditioner block * to a SignalConditionerInterface */ class BeamformerFilter : public GNSSBlockInterface { public: BeamformerFilter(ConfigurationInterface* configuration, const std::string& role, unsigned int in_stream, unsigned int out_stream); ~BeamformerFilter() = default; inline std::string role() override { return role_; } //! returns "Direct_Resampler" inline std::string implementation() override { return "Beamformer_Filter"; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: std::string role_; unsigned int in_stream_; unsigned int out_stream_; std::string item_type_; size_t item_size_; uint64_t samples_; bool dump_; std::string dump_filename_; gr::block_sptr beamformer_; gr::block_sptr file_sink_; }; #endif /* GNSS_SDR_BEAMFORMER_FILTER_H_ */ src/algorithms/input_filter/adapters/fir_filter.cc000066400000000000000000000423711352176506000227750ustar00rootroot00000000000000/*! * \file fir_filter.cc * \brief Adapts a gnuradio gr_fir_filter designed with gr_remez * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * Carles Fernandez-Prades, 2015 cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "fir_filter.h" #include "configuration_interface.h" #include #include #include #include FirFilter::FirFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(std::move(role)), in_streams_(in_streams), out_streams_(out_streams) { size_t item_size; (*this).init(); if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); fir_filter_ccf_ = gr::filter::fir_filter_ccf::make(1, taps_); DLOG(INFO) << "input_filter(" << fir_filter_ccf_->unique_id() << ")"; if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "cshort")) { item_size = sizeof(lv_16sc_t); cshort_to_float_x2_ = make_cshort_to_float_x2(); fir_filter_fff_1_ = gr::filter::fir_filter_fff::make(1, taps_); fir_filter_fff_2_ = gr::filter::fir_filter_fff::make(1, taps_); DLOG(INFO) << "I input_filter(" << fir_filter_fff_1_->unique_id() << ")"; DLOG(INFO) << "Q input_filter(" << fir_filter_fff_2_->unique_id() << ")"; float_to_short_1_ = gr::blocks::float_to_short::make(); float_to_short_2_ = gr::blocks::float_to_short::make(); short_x2_to_cshort_ = make_short_x2_to_cshort(); if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); cshort_to_float_x2_ = make_cshort_to_float_x2(); fir_filter_fff_1_ = gr::filter::fir_filter_fff::make(1, taps_); fir_filter_fff_2_ = gr::filter::fir_filter_fff::make(1, taps_); DLOG(INFO) << "I input_filter(" << fir_filter_fff_1_->unique_id() << ")"; DLOG(INFO) << "Q input_filter(" << fir_filter_fff_2_->unique_id() << ")"; float_to_complex_ = gr::blocks::float_to_complex::make(); if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); fir_filter_fff_1_ = gr::filter::fir_filter_fff::make(1, taps_); fir_filter_fff_2_ = gr::filter::fir_filter_fff::make(1, taps_); DLOG(INFO) << "I input_filter(" << fir_filter_fff_1_->unique_id() << ")"; DLOG(INFO) << "Q input_filter(" << fir_filter_fff_2_->unique_id() << ")"; float_to_complex_ = gr::blocks::float_to_complex::make(); if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "cbyte")) { item_size = sizeof(lv_8sc_t); cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); fir_filter_fff_1_ = gr::filter::fir_filter_fff::make(1, taps_); fir_filter_fff_2_ = gr::filter::fir_filter_fff::make(1, taps_); DLOG(INFO) << "I input_filter(" << fir_filter_fff_1_->unique_id() << ")"; DLOG(INFO) << "Q input_filter(" << fir_filter_fff_2_->unique_id() << ")"; float_to_char_1_ = gr::blocks::float_to_char::make(); float_to_char_2_ = gr::blocks::float_to_char::make(); char_x2_cbyte_ = make_byte_x2_to_complex_byte(); if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } } else { LOG(ERROR) << " Unknown item type conversion"; } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void FirFilter::connect(gr::top_block_sptr top_block) { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->connect(fir_filter_ccf_, 0, file_sink_, 0); } else { DLOG(INFO) << "Nothing to connect internally"; } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "cshort")) { top_block->connect(cshort_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->connect(cshort_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->connect(fir_filter_fff_1_, 0, float_to_short_1_, 0); top_block->connect(fir_filter_fff_2_, 0, float_to_short_2_, 0); top_block->connect(float_to_short_1_, 0, short_x2_to_cshort_, 0); top_block->connect(float_to_short_2_, 0, short_x2_to_cshort_, 1); if (dump_) { top_block->connect(short_x2_to_cshort_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "gr_complex")) { top_block->connect(cbyte_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->connect(cbyte_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->connect(fir_filter_fff_1_, 0, float_to_complex_, 0); top_block->connect(fir_filter_fff_2_, 0, float_to_complex_, 1); if (dump_) { top_block->connect(float_to_complex_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "cbyte")) { top_block->connect(cbyte_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->connect(cbyte_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->connect(fir_filter_fff_1_, 0, float_to_char_1_, 0); top_block->connect(fir_filter_fff_2_, 0, float_to_char_2_, 0); top_block->connect(float_to_char_1_, 0, char_x2_cbyte_, 0); top_block->connect(float_to_char_2_, 0, char_x2_cbyte_, 1); if (dump_) { top_block->connect(char_x2_cbyte_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "gr_complex")) { top_block->connect(cshort_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->connect(cshort_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->connect(fir_filter_fff_1_, 0, float_to_complex_, 0); top_block->connect(fir_filter_fff_2_, 0, float_to_complex_, 1); if (dump_) { top_block->connect(float_to_complex_, 0, file_sink_, 0); } } else { LOG(ERROR) << " Unknown item type conversion"; } } void FirFilter::disconnect(gr::top_block_sptr top_block) { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->disconnect(fir_filter_ccf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "gr_complex")) { top_block->disconnect(fir_filter_fff_2_, 0, float_to_complex_, 1); top_block->disconnect(fir_filter_fff_1_, 0, float_to_complex_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->disconnect(cbyte_to_float_x2_, 0, fir_filter_fff_1_, 0); if (dump_) { top_block->disconnect(float_to_complex_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "cshort")) { top_block->disconnect(cshort_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->disconnect(cshort_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->disconnect(fir_filter_fff_1_, 0, float_to_short_1_, 0); top_block->disconnect(fir_filter_fff_2_, 0, float_to_short_2_, 0); top_block->disconnect(float_to_short_1_, 0, short_x2_to_cshort_, 0); top_block->disconnect(float_to_short_2_, 0, short_x2_to_cshort_, 1); if (dump_) { top_block->disconnect(short_x2_to_cshort_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "cbyte")) { top_block->disconnect(float_to_char_2_, 0, char_x2_cbyte_, 1); top_block->disconnect(float_to_char_1_, 0, char_x2_cbyte_, 0); top_block->disconnect(fir_filter_fff_2_, 0, float_to_char_2_, 0); top_block->disconnect(fir_filter_fff_1_, 0, float_to_char_1_, 0); top_block->disconnect(cbyte_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->disconnect(cbyte_to_float_x2_, 1, fir_filter_fff_2_, 0); if (dump_) { top_block->disconnect(char_x2_cbyte_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "gr_complex")) { top_block->disconnect(cshort_to_float_x2_, 0, fir_filter_fff_1_, 0); top_block->disconnect(cshort_to_float_x2_, 1, fir_filter_fff_2_, 0); top_block->disconnect(fir_filter_fff_1_, 0, float_to_complex_, 0); top_block->disconnect(fir_filter_fff_2_, 0, float_to_complex_, 1); if (dump_) { top_block->disconnect(float_to_complex_, 0, file_sink_, 0); } } else { LOG(ERROR) << " Unknown item type conversion"; } } gr::basic_block_sptr FirFilter::get_left_block() { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { return fir_filter_ccf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "cshort")) { return cshort_to_float_x2_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "gr_complex")) { return cbyte_to_float_x2_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "cbyte")) { return cbyte_to_float_x2_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "gr_complex")) { return cshort_to_float_x2_; } LOG(WARNING) << "Unknown item type conversion"; return nullptr; } gr::basic_block_sptr FirFilter::get_right_block() { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { return fir_filter_ccf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "cshort")) { return short_x2_to_cshort_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "gr_complex")) { return float_to_complex_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cbyte") && (output_item_type_ == "cbyte")) { return char_x2_cbyte_; } if ((taps_item_type_ == "float") && (input_item_type_ == "cshort") && (output_item_type_ == "gr_complex")) { return float_to_complex_; } LOG(WARNING) << "Unknown input filter taps item type"; return nullptr; } void FirFilter::init() { std::string default_input_item_type = "gr_complex"; std::string default_output_item_type = "gr_complex"; std::string default_taps_item_type = "float"; std::string default_dump_filename = "../data/input_filter.dat"; int default_number_of_taps = 6; unsigned int default_number_of_bands = 2; std::vector default_bands = {0.0, 0.4, 0.6, 1.0}; std::vector default_ampl = {1.0, 1.0, 0.0, 0.0}; std::vector default_error_w = {1.0, 1.0}; std::string default_filter_type = "bandpass"; int default_grid_density = 16; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); output_item_type_ = config_->property(role_ + ".output_item_type", default_output_item_type); taps_item_type_ = config_->property(role_ + ".taps_item_type", default_taps_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); int number_of_taps = config_->property(role_ + ".number_of_taps", default_number_of_taps); unsigned int number_of_bands = config_->property(role_ + ".number_of_bands", default_number_of_bands); std::vector bands; std::vector ampl; std::vector error_w; std::string option; double option_value; for (unsigned int i = 0; i < number_of_bands; i++) { option = ".band" + std::to_string(i + 1) + "_begin"; option_value = config_->property(role_ + option, default_bands[i]); bands.push_back(option_value); option = ".band" + std::to_string(i + 1) + "_end"; option_value = config_->property(role_ + option, default_bands[i]); bands.push_back(option_value); option = ".ampl" + std::to_string(i + 1) + "_begin"; option_value = config_->property(role_ + option, default_bands[i]); ampl.push_back(option_value); option = ".ampl" + std::to_string(i + 1) + "_end"; option_value = config_->property(role_ + option, default_bands[i]); ampl.push_back(option_value); option = ".band" + std::to_string(i + 1) + "_error"; option_value = config_->property(role_ + option, default_bands[i]); error_w.push_back(option_value); } std::string filter_type = config_->property(role_ + ".filter_type", default_filter_type); int grid_density = config_->property(role_ + ".grid_density", default_grid_density); // pm_remez implements the Parks-McClellan FIR filter design. // It calculates the optimal (in the Chebyshev/minimax sense) FIR filter // impulse response given a set of band edges, the desired response on // those bands, and the weight given to the error in those bands. std::vector taps_d = gr::filter::pm_remez(number_of_taps - 1, bands, ampl, error_w, filter_type, grid_density); taps_.reserve(taps_d.size()); for (double& it : taps_d) { taps_.push_back(float(it)); } } src/algorithms/input_filter/adapters/fir_filter.h000066400000000000000000000100021352176506000226210ustar00rootroot00000000000000/*! * \file fir_filter.h * \brief Adapts a gnuradio gr_fir_filter designed with pm_remez * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_FIR_FILTER_H_ #define GNSS_SDR_FIR_FILTER_H_ #include "byte_x2_to_complex_byte.h" #include "complex_byte_to_float_x2.h" #include "cshort_to_float_x2.h" #include "gnss_block_interface.h" #include "short_x2_to_cshort.h" #include #include #include #include #include #ifdef GR_GREATER_38 #include #else #include #include #endif #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a GNU Radio gr_fir_filter designed with pm_remez * * See Parks-McClellan FIR filter design, http://en.wikipedia.org/wiki/Parks-McClellan_filter_design_algorithm * Calculates the optimal (in the Chebyshev/minimax sense) FIR filter impulse response * given a set of band edges, the desired response on those bands, and the weight given * to the error in those bands. */ class FirFilter : public GNSSBlockInterface { public: //! Constructor FirFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams); //! Destructor ~FirFilter() = default; inline std::string role() override { return role_; } //! Returns "Fir_Filter" inline std::string implementation() override { return "Fir_Filter"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: gr::filter::fir_filter_ccf::sptr fir_filter_ccf_; ConfigurationInterface* config_; bool dump_; std::string dump_filename_; std::string input_item_type_; std::string output_item_type_; std::string taps_item_type_; std::vector taps_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; void init(); complex_byte_to_float_x2_sptr cbyte_to_float_x2_; gr::filter::fir_filter_fff::sptr fir_filter_fff_1_; gr::filter::fir_filter_fff::sptr fir_filter_fff_2_; gr::blocks::float_to_char::sptr float_to_char_1_; gr::blocks::float_to_char::sptr float_to_char_2_; byte_x2_to_complex_byte_sptr char_x2_cbyte_; gr::blocks::float_to_complex::sptr float_to_complex_; cshort_to_float_x2_sptr cshort_to_float_x2_; gr::blocks::float_to_short::sptr float_to_short_1_; gr::blocks::float_to_short::sptr float_to_short_2_; short_x2_to_cshort_sptr short_x2_to_cshort_; }; #endif src/algorithms/input_filter/adapters/freq_xlating_fir_filter.cc000066400000000000000000000421121352176506000255310ustar00rootroot00000000000000/*! * \file freq_xlating_fir_filter.cc * \brief Adapts a gnuradio gr_freq_xlating_fir_filter designed with gr_remez or gr_firdes * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * Antonio Ramos, 2017. antonio.ramos(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "freq_xlating_fir_filter.h" #include "configuration_interface.h" #include #include #include #include #include #include FreqXlatingFirFilter::FreqXlatingFirFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(std::move(role)), in_streams_(in_streams), out_streams_(out_streams) { std::string default_input_item_type = "gr_complex"; std::string default_output_item_type = "gr_complex"; std::string default_taps_item_type = "float"; std::string default_dump_filename = "../data/input_filter.dat"; double default_intermediate_freq = 0.0; double default_sampling_freq = 4000000.0; int default_number_of_taps = 6; unsigned int default_number_of_bands = 2; std::vector default_bands = {0.0, 0.4, 0.6, 1.0}; std::vector default_ampl = {1.0, 1.0, 0.0, 0.0}; std::vector default_error_w = {1.0, 1.0}; std::string default_filter_type = "bandpass"; int default_grid_density = 16; int default_decimation_factor = 1; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); output_item_type_ = config_->property(role_ + ".output_item_type", default_output_item_type); taps_item_type_ = config_->property(role_ + ".taps_item_type", default_taps_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); intermediate_freq_ = config_->property(role_ + ".IF", default_intermediate_freq); sampling_freq_ = config_->property(role_ + ".sampling_frequency", default_sampling_freq); int number_of_taps = config_->property(role_ + ".number_of_taps", default_number_of_taps); unsigned int number_of_bands = config_->property(role_ + ".number_of_bands", default_number_of_bands); std::string filter_type = config_->property(role_ + ".filter_type", default_filter_type); decimation_factor_ = config_->property(role_ + ".decimation_factor", default_decimation_factor); if (filter_type != "lowpass") { std::vector taps_d; std::vector bands; std::vector ampl; std::vector error_w; std::string option; double option_value; for (unsigned int i = 0; i < number_of_bands; i++) { option = ".band" + std::to_string(i + 1) + "_begin"; option_value = config_->property(role_ + option, default_bands[i]); bands.push_back(option_value); option = ".band" + std::to_string(i + 1) + "_end"; option_value = config_->property(role_ + option, default_bands[i]); bands.push_back(option_value); option = ".ampl" + std::to_string(i + 1) + "_begin"; option_value = config_->property(role_ + option, default_bands[i]); ampl.push_back(option_value); option = ".ampl" + std::to_string(i + 1) + "_end"; option_value = config_->property(role_ + option, default_bands[i]); ampl.push_back(option_value); option = ".band" + std::to_string(i + 1) + "_error"; option_value = config_->property(role_ + option, default_bands[i]); error_w.push_back(option_value); } int grid_density = config_->property(role_ + ".grid_density", default_grid_density); taps_d = gr::filter::pm_remez(number_of_taps - 1, bands, ampl, error_w, filter_type, grid_density); taps_.reserve(taps_d.size()); for (double& it : taps_d) { taps_.push_back(static_cast(it)); } } else { double default_bw = (sampling_freq_ / decimation_factor_) / 2; double bw_ = config_->property(role_ + ".bw", default_bw); double default_tw = bw_ / 10.0; double tw_ = config_->property(role_ + ".tw", default_tw); taps_ = gr::filter::firdes::low_pass(1.0, sampling_freq_, bw_, tw_); } size_t item_size; LOG(INFO) << "Created freq_xlating_fir_filter with " << taps_.size() << " taps"; if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); //output input_size_ = sizeof(gr_complex); //input freq_xlating_fir_filter_ccf_ = gr::filter::freq_xlating_fir_filter_ccf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_ccf_->unique_id() << ")"; } else if ((taps_item_type_ == "float") && (input_item_type_ == "float") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); input_size_ = sizeof(float); //input freq_xlating_fir_filter_fcf_ = gr::filter::freq_xlating_fir_filter_fcf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_fcf_->unique_id() << ")"; } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); input_size_ = sizeof(int16_t); //input freq_xlating_fir_filter_scf_ = gr::filter::freq_xlating_fir_filter_scf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_scf_->unique_id() << ")"; } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "cshort")) { item_size = sizeof(lv_16sc_t); input_size_ = sizeof(int16_t); //input freq_xlating_fir_filter_scf_ = gr::filter::freq_xlating_fir_filter_scf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_scf_->unique_id() << ")"; complex_to_float_ = gr::blocks::complex_to_float::make(); float_to_short_1_ = gr::blocks::float_to_short::make(); float_to_short_2_ = gr::blocks::float_to_short::make(); short_x2_to_cshort_ = make_short_x2_to_cshort(); } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "gr_complex")) { item_size = sizeof(gr_complex); input_size_ = sizeof(int8_t); //input gr_char_to_short_ = gr::blocks::char_to_short::make(); freq_xlating_fir_filter_scf_ = gr::filter::freq_xlating_fir_filter_scf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_scf_->unique_id() << ")"; } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "cbyte")) { item_size = sizeof(lv_8sc_t); input_size_ = sizeof(int8_t); //input gr_char_to_short_ = gr::blocks::char_to_short::make(); freq_xlating_fir_filter_scf_ = gr::filter::freq_xlating_fir_filter_scf::make(decimation_factor_, taps_, intermediate_freq_, sampling_freq_); DLOG(INFO) << "input_filter(" << freq_xlating_fir_filter_scf_->unique_id() << ")"; complex_to_complex_byte_ = make_complex_float_to_complex_byte(); } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; item_size = sizeof(gr_complex); //avoids uninitialization input_size_ = sizeof(gr_complex); //avoids uninitialization } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; std::cout << "Dumping output into file " << dump_filename_ << std::endl; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void FreqXlatingFirFilter::connect(gr::top_block_sptr top_block) { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->connect(freq_xlating_fir_filter_ccf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "float") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->connect(freq_xlating_fir_filter_fcf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->connect(freq_xlating_fir_filter_scf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "cshort")) { top_block->connect(freq_xlating_fir_filter_scf_, 0, complex_to_float_, 0); top_block->connect(complex_to_float_, 0, float_to_short_1_, 0); top_block->connect(complex_to_float_, 1, float_to_short_2_, 0); top_block->connect(float_to_short_1_, 0, short_x2_to_cshort_, 0); top_block->connect(float_to_short_2_, 0, short_x2_to_cshort_, 0); if (dump_) { top_block->connect(short_x2_to_cshort_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "gr_complex")) { top_block->connect(gr_char_to_short_, 0, freq_xlating_fir_filter_scf_, 0); if (dump_) { top_block->connect(freq_xlating_fir_filter_scf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "cbyte")) { top_block->connect(gr_char_to_short_, 0, freq_xlating_fir_filter_scf_, 0); top_block->connect(freq_xlating_fir_filter_scf_, 0, complex_to_complex_byte_, 0); if (dump_) { top_block->connect(complex_to_complex_byte_, 0, file_sink_, 0); } } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; } } void FreqXlatingFirFilter::disconnect(gr::top_block_sptr top_block) { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->disconnect(freq_xlating_fir_filter_ccf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "float") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->disconnect(freq_xlating_fir_filter_fcf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "gr_complex")) { if (dump_) { top_block->disconnect(freq_xlating_fir_filter_scf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "cshort")) { top_block->disconnect(freq_xlating_fir_filter_scf_, 0, complex_to_float_, 0); top_block->disconnect(complex_to_float_, 0, float_to_short_1_, 0); top_block->disconnect(complex_to_float_, 1, float_to_short_2_, 0); top_block->disconnect(float_to_short_1_, 0, short_x2_to_cshort_, 0); top_block->disconnect(float_to_short_2_, 0, short_x2_to_cshort_, 0); if (dump_) { top_block->disconnect(short_x2_to_cshort_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "gr_complex")) { top_block->disconnect(gr_char_to_short_, 0, freq_xlating_fir_filter_scf_, 0); if (dump_) { top_block->disconnect(freq_xlating_fir_filter_scf_, 0, file_sink_, 0); } } else if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "cbyte")) { top_block->disconnect(gr_char_to_short_, 0, freq_xlating_fir_filter_scf_, 0); top_block->disconnect(freq_xlating_fir_filter_scf_, 0, complex_to_complex_byte_, 0); if (dump_) { top_block->disconnect(complex_to_complex_byte_, 0, file_sink_, 0); } } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; } } gr::basic_block_sptr FreqXlatingFirFilter::get_left_block() { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_ccf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "float") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_fcf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_scf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "cshort")) { return freq_xlating_fir_filter_scf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "gr_complex")) { return gr_char_to_short_; } if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "cbyte")) { return gr_char_to_short_; } LOG(WARNING) << " Unknown input filter input/output item type conversion"; return nullptr; } gr::basic_block_sptr FreqXlatingFirFilter::get_right_block() { if ((taps_item_type_ == "float") && (input_item_type_ == "gr_complex") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_ccf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "float") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_fcf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_scf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "short") && (output_item_type_ == "cshort")) { return short_x2_to_cshort_; } if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "gr_complex")) { return freq_xlating_fir_filter_scf_; } if ((taps_item_type_ == "float") && (input_item_type_ == "byte") && (output_item_type_ == "cbyte")) { return complex_to_complex_byte_; } LOG(WARNING) << " Unknown input filter input/output item type conversion"; return nullptr; } src/algorithms/input_filter/adapters/freq_xlating_fir_filter.h000066400000000000000000000105221352176506000253730ustar00rootroot00000000000000/*! * \file freq_xlating_fir_filter.h * \brief Adapts a gnuradio gr_freq_xlating_fir_filter designed with gr_remez * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_FREQ_XLATING_FIR_FILTER_H_ #define GNSS_SDR_FREQ_XLATING_FIR_FILTER_H_ #include "complex_float_to_complex_byte.h" #include "gnss_block_interface.h" #include "short_x2_to_cshort.h" #ifdef GR_GREATER_38 #include #else #include #include #include #endif #include #include #include #include #include #include class ConfigurationInterface; /*! * \brief This class adapts a gnuradio gr_freq_xlating_fir_filter designed with pm_remez * * Construct a FIR filter with the given taps and a composite frequency * translation that shifts intermediate_freq_ down to zero Hz. The frequency * translation logically comes before the filtering operation. * * See Parks-McClellan FIR filter design, http://en.wikipedia.org/wiki/Parks-McClellan_filter_design_algorithm * Calculates the optimal (in the Chebyshev/minimax sense) FIR filter impulse response * given a set of band edges, the desired response on those bands, and the weight given * to the error in those bands. */ class FreqXlatingFirFilter : public GNSSBlockInterface { public: FreqXlatingFirFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams); ~FreqXlatingFirFilter() = default; inline std::string role() override { return role_; } //! Returns "Freq_Xlating_Fir_Filter" inline std::string implementation() override { return "Freq_Xlating_Fir_Filter"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: gr::filter::freq_xlating_fir_filter_ccf::sptr freq_xlating_fir_filter_ccf_; gr::filter::freq_xlating_fir_filter_fcf::sptr freq_xlating_fir_filter_fcf_; gr::filter::freq_xlating_fir_filter_scf::sptr freq_xlating_fir_filter_scf_; ConfigurationInterface* config_; int decimation_factor_; bool dump_; std::string dump_filename_; std::string input_item_type_; size_t input_size_; std::string output_item_type_; std::string taps_item_type_; std::vector taps_; double intermediate_freq_; double sampling_freq_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; gr::blocks::complex_to_float::sptr complex_to_float_; gr::blocks::char_to_short::sptr gr_char_to_short_; gr::blocks::float_to_short::sptr float_to_short_1_; gr::blocks::float_to_short::sptr float_to_short_2_; short_x2_to_cshort_sptr short_x2_to_cshort_; complex_float_to_complex_byte_sptr complex_to_complex_byte_; }; #endif // GNSS_SDR_FREQ_XLATING_FIR_FILTER_H_ src/algorithms/input_filter/adapters/notch_filter.cc000066400000000000000000000103141352176506000233200ustar00rootroot00000000000000/*! * \file notch_filter.cc * \brief Adapts a gnuradio gr_notch_filter * \author Antonio Ramos, 2017. antonio.ramosdet(at)gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "notch_filter.h" #include "configuration_interface.h" #include "notch_cc.h" #include #include NotchFilter::NotchFilter(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { size_t item_size_; float pfa; float default_pfa = 0.001; float p_c_factor; float default_p_c_factor = 0.9; int length_; int default_length_ = 32; int n_segments_est; int default_n_segments_est = 12500; int n_segments_reset; int default_n_segments_reset = 5000000; std::string default_item_type = "gr_complex"; std::string default_dump_file = "./data/input_filter.dat"; item_type_ = configuration->property(role + ".item_type", default_item_type); dump_ = configuration->property(role + ".dump", false); DLOG(INFO) << "dump_ is " << dump_; dump_filename_ = configuration->property(role + ".dump_filename", default_dump_file); pfa = configuration->property(role + ".pfa", default_pfa); p_c_factor = configuration->property(role + ".p_c_factor", default_p_c_factor); length_ = configuration->property(role + ".length", default_length_); n_segments_est = configuration->property(role + ".segments_est", default_n_segments_est); n_segments_reset = configuration->property(role + ".segments_reset", default_n_segments_reset); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); notch_filter_ = make_notch_filter(pfa, p_c_factor, length_, n_segments_est, n_segments_reset); DLOG(INFO) << "Item size " << item_size_; DLOG(INFO) << "input filter(" << notch_filter_->unique_id() << ")"; } else { LOG(WARNING) << item_type_ << " unrecognized item type for notch filter"; item_size_ = sizeof(gr_complex); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size_, dump_filename_.c_str()); DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void NotchFilter::connect(gr::top_block_sptr top_block) { if (dump_) { top_block->connect(notch_filter_, 0, file_sink_, 0); DLOG(INFO) << "connected notch filter output to file sink"; } else { DLOG(INFO) << "nothing to connect internally"; } } void NotchFilter::disconnect(gr::top_block_sptr top_block) { if (dump_) { top_block->disconnect(notch_filter_, 0, file_sink_, 0); } } gr::basic_block_sptr NotchFilter::get_left_block() { return notch_filter_; } gr::basic_block_sptr NotchFilter::get_right_block() { return notch_filter_; } src/algorithms/input_filter/adapters/notch_filter.h000066400000000000000000000044631352176506000231720ustar00rootroot00000000000000/*! * \file notch_filter.h * \brief Adapter of a multistate Notch filter * \author Antonio Ramos, 2017. antonio.ramosdet(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_NOTCH_FILTER_H_ #define GNSS_SDR_NOTCH_FILTER_H_ #include "gnss_block_interface.h" #include "notch_cc.h" #include #include #include class ConfigurationInterface; class NotchFilter : public GNSSBlockInterface { public: NotchFilter(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~NotchFilter() = default; std::string role() { return role_; } //! Returns "Notch_Filter" std::string implementation() { return "Notch_Filter"; } size_t item_size() { return 0; } void connect(gr::top_block_sptr top_block); void disconnect(gr::top_block_sptr top_block); gr::basic_block_sptr get_left_block(); gr::basic_block_sptr get_right_block(); private: bool dump_; std::string dump_filename_; std::string role_; std::string item_type_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; notch_sptr notch_filter_; }; #endif // GNSS_SDR_NOTCH_FILTER_H_ src/algorithms/input_filter/adapters/notch_filter_lite.cc000066400000000000000000000113771352176506000243470ustar00rootroot00000000000000/*! * \file notch_filter_lite.cc * \brief Adapts a gnuradio gr_notch_filter_lite * \author Antonio Ramos, 2017. antonio.ramosdet(at)gmail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "notch_filter_lite.h" #include "configuration_interface.h" #include "notch_lite_cc.h" #include #include #include NotchFilterLite::NotchFilterLite(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { size_t item_size_; float p_c_factor; float default_p_c_factor = 0.9; float pfa; float default_pfa = 0.001; int length_; int default_length_ = 32; int n_segments_est; int default_n_segments_est = 12500; int n_segments_reset; int default_n_segments_reset = 5000000; float default_samp_freq = 4000000; float samp_freq = configuration->property("SignalSource.sampling_frequency", default_samp_freq); float default_coeff_rate = samp_freq * 0.1; float coeff_rate; std::string default_item_type = "gr_complex"; std::string default_dump_file = "./data/input_filter.dat"; item_type_ = configuration->property(role + ".item_type", default_item_type); dump_ = configuration->property(role + ".dump", false); DLOG(INFO) << "dump_ is " << dump_; dump_filename_ = configuration->property(role + ".dump_filename", default_dump_file); p_c_factor = configuration->property(role + ".p_c_factor", default_p_c_factor); pfa = configuration->property(role + ".pfa", default_pfa); coeff_rate = configuration->property(role + ".coeff_rate", default_coeff_rate); length_ = configuration->property(role + ".length", default_length_); n_segments_est = configuration->property(role + ".segments_est", default_n_segments_est); n_segments_reset = configuration->property(role + ".segments_reset", default_n_segments_reset); int n_segments_coeff = static_cast((samp_freq / coeff_rate) / static_cast(length_)); n_segments_coeff = std::max(1, n_segments_coeff); if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); notch_filter_lite_ = make_notch_filter_lite(p_c_factor, pfa, length_, n_segments_est, n_segments_reset, n_segments_coeff); DLOG(INFO) << "Item size " << item_size_; DLOG(INFO) << "input filter(" << notch_filter_lite_->unique_id() << ")"; } else { LOG(WARNING) << item_type_ << " unrecognized item type for notch filter"; item_size_ = sizeof(gr_complex); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; file_sink_ = gr::blocks::file_sink::make(item_size_, dump_filename_.c_str()); DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void NotchFilterLite::connect(gr::top_block_sptr top_block) { if (dump_) { top_block->connect(notch_filter_lite_, 0, file_sink_, 0); DLOG(INFO) << "connected notch filter output to file sink"; } else { DLOG(INFO) << "nothing to connect internally"; } } void NotchFilterLite::disconnect(gr::top_block_sptr top_block) { if (dump_) { top_block->disconnect(notch_filter_lite_, 0, file_sink_, 0); } } gr::basic_block_sptr NotchFilterLite::get_left_block() { return notch_filter_lite_; } gr::basic_block_sptr NotchFilterLite::get_right_block() { return notch_filter_lite_; } src/algorithms/input_filter/adapters/notch_filter_lite.h000066400000000000000000000045731352176506000242110ustar00rootroot00000000000000/*! * \file notch_filter_lite.h * \brief Adapts a light version of a multistate notch filter * \author Antonio Ramos, 2017. antonio.ramosdet(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_NOTCH_FILTER_LITE_H_ #define GNSS_SDR_NOTCH_FILTER_LITE_H_ #include "gnss_block_interface.h" #include "notch_lite_cc.h" #include #include #include class ConfigurationInterface; class NotchFilterLite : public GNSSBlockInterface { public: NotchFilterLite(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams); ~NotchFilterLite() = default; std::string role() { return role_; } //! Returns "Notch_Filter_Lite" std::string implementation() { return "Notch_Filter_Lite"; } size_t item_size() { return 0; } void connect(gr::top_block_sptr top_block); void disconnect(gr::top_block_sptr top_block); gr::basic_block_sptr get_left_block(); gr::basic_block_sptr get_right_block(); private: bool dump_; std::string dump_filename_; std::string role_; std::string item_type_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; notch_lite_sptr notch_filter_lite_; }; #endif // GNSS_SDR_NOTCH_FILTER_LITE_H_ src/algorithms/input_filter/adapters/pulse_blanking_filter.cc000066400000000000000000000142421352176506000252060ustar00rootroot00000000000000/*! * \file pulse_blanking_filter.cc * \brief Instantiates the GNSS-SDR pulse blanking filter * \author Javier Arribas 2017 * Antonio Ramos 2017 * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pulse_blanking_filter.h" #include "configuration_interface.h" #include #include #include #include #include #include PulseBlankingFilter::PulseBlankingFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams) : config_(configuration), role_(std::move(role)), in_streams_(in_streams), out_streams_(out_streams) { size_t item_size; xlat_ = false; std::string default_input_item_type = "gr_complex"; std::string default_output_item_type = "gr_complex"; std::string default_dump_filename = "../data/input_filter.dat"; DLOG(INFO) << "role " << role_; input_item_type_ = config_->property(role_ + ".input_item_type", default_input_item_type); output_item_type_ = config_->property(role_ + ".output_item_type", default_output_item_type); dump_ = config_->property(role_ + ".dump", false); dump_filename_ = config_->property(role_ + ".dump_filename", default_dump_filename); float default_pfa_ = 0.04; float pfa = config_->property(role_ + ".pfa", default_pfa_); int default_length_ = 32; int length_ = config_->property(role_ + ".length", default_length_); int default_n_segments_est = 12500; int n_segments_est = config_->property(role_ + ".segments_est", default_n_segments_est); int default_n_segments_reset = 5000000; int n_segments_reset = config_->property(role_ + ".segments_reset", default_n_segments_reset); if (input_item_type_ == "gr_complex") { item_size = sizeof(gr_complex); //output input_size_ = sizeof(gr_complex); //input pulse_blanking_cc_ = make_pulse_blanking_cc(pfa, length_, n_segments_est, n_segments_reset); } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; item_size = sizeof(gr_complex); //avoids uninitialization input_size_ = sizeof(gr_complex); //avoids uninitialization } double default_if = 0.0; double if_aux = config_->property(role_ + ".if", default_if); double if_ = config_->property(role_ + ".IF", if_aux); if (std::abs(if_) > 1.0) { xlat_ = true; double default_sampling_freq = 4000000.0; double sampling_freq_ = config_->property(role_ + ".sampling_frequency", default_sampling_freq); double default_bw = 2000000.0; double bw_ = config_->property(role_ + ".bw", default_bw); double default_tw = bw_ / 10.0; double tw_ = config_->property(role_ + ".tw", default_tw); const std::vector taps = gr::filter::firdes::low_pass(1.0, sampling_freq_, bw_, tw_); freq_xlating_ = gr::filter::freq_xlating_fir_filter_ccf::make(1, taps, if_, sampling_freq_); } if (dump_) { DLOG(INFO) << "Dumping output into file " << dump_filename_; std::cout << "Dumping output into file " << dump_filename_ << std::endl; file_sink_ = gr::blocks::file_sink::make(item_size, dump_filename_.c_str()); } if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } void PulseBlankingFilter::connect(gr::top_block_sptr top_block) { if (input_item_type_ == "gr_complex") { if (dump_) { top_block->connect(pulse_blanking_cc_, 0, file_sink_, 0); } if (xlat_) { top_block->connect(freq_xlating_, 0, pulse_blanking_cc_, 0); } } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; } } void PulseBlankingFilter::disconnect(gr::top_block_sptr top_block) { if (input_item_type_ == "gr_complex") { if (dump_) { top_block->disconnect(pulse_blanking_cc_, 0, file_sink_, 0); } if (xlat_) { top_block->disconnect(freq_xlating_, 0, pulse_blanking_cc_, 0); } } else { LOG(ERROR) << " Unknown input filter input/output item type conversion"; } } gr::basic_block_sptr PulseBlankingFilter::get_left_block() { if (input_item_type_ == "gr_complex") { if (xlat_) { return freq_xlating_; } return pulse_blanking_cc_; } LOG(ERROR) << " Unknown input filter input/output item type conversion"; return nullptr; } gr::basic_block_sptr PulseBlankingFilter::get_right_block() { if (input_item_type_ == "gr_complex") { return pulse_blanking_cc_; } LOG(ERROR) << " Unknown input filter input/output item type conversion"; return nullptr; } src/algorithms/input_filter/adapters/pulse_blanking_filter.h000066400000000000000000000053641352176506000250550ustar00rootroot00000000000000/*! * \file pulse_blanking_filter.h * \brief Instantiates the GNSS-SDR pulse blanking filter * \author Javier Arribas 2017 * Antonio Ramos 2017 * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PULSE_BLANKING_FILTER_H_ #define GNSS_SDR_PULSE_BLANKING_FILTER_H_ #include "gnss_block_interface.h" #include "pulse_blanking_cc.h" #include #ifdef GR_GREATER_38 #include #else #include #endif #include class ConfigurationInterface; class PulseBlankingFilter : public GNSSBlockInterface { public: PulseBlankingFilter(ConfigurationInterface* configuration, std::string role, unsigned int in_streams, unsigned int out_streams); ~PulseBlankingFilter() = default; inline std::string role() override { return role_; } //! Returns "Pulse_Blanking_Filter" inline std::string implementation() override { return "Pulse_Blanking_Filter"; } inline size_t item_size() override { return 0; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: ConfigurationInterface* config_; bool dump_; bool xlat_; std::string dump_filename_; std::string input_item_type_; size_t input_size_; std::string output_item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::file_sink::sptr file_sink_; pulse_blanking_cc_sptr pulse_blanking_cc_; gr::filter::freq_xlating_fir_filter_ccf::sptr freq_xlating_; }; #endif // GNSS_SDR_PULSE_BLANKING_FILTER_H_ src/algorithms/input_filter/gnuradio_blocks/000077500000000000000000000000001352176506000216745ustar00rootroot00000000000000src/algorithms/input_filter/gnuradio_blocks/CMakeLists.txt000066400000000000000000000033401352176506000244340ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(INPUT_FILTER_GR_BLOCKS_SOURCES beamformer.cc pulse_blanking_cc.cc notch_cc.cc notch_lite_cc.cc ) set(INPUT_FILTER_GR_BLOCKS_HEADERS beamformer.h pulse_blanking_cc.h notch_cc.h notch_lite_cc.h ) list(SORT INPUT_FILTER_GR_BLOCKS_HEADERS) list(SORT INPUT_FILTER_GR_BLOCKS_SOURCES) source_group(Headers FILES ${INPUT_FILTER_GR_BLOCKS_HEADERS}) add_library(input_filter_gr_blocks ${INPUT_FILTER_GR_BLOCKS_SOURCES} ${INPUT_FILTER_GR_BLOCKS_HEADERS}) target_link_libraries(input_filter_gr_blocks PUBLIC Gnuradio::blocks Gnuradio::filter Volkgnsssdr::volkgnsssdr PRIVATE Log4cpp::log4cpp ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(input_filter_gr_blocks PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET input_filter_gr_blocks APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/input_filter/gnuradio_blocks/beamformer.cc000066400000000000000000000051771352176506000243340ustar00rootroot00000000000000/*! * \file beamformer.cc * * \brief Unpacks byte samples to NSR 2 bits samples * \author Javier Arribas jarribas (at) cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beamformer.h" #include beamformer_sptr make_beamformer_sptr() { return beamformer_sptr(new beamformer()); } beamformer::beamformer() : gr::sync_block("beamformer", gr::io_signature::make(GNSS_SDR_BEAMFORMER_CHANNELS, GNSS_SDR_BEAMFORMER_CHANNELS, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) { } int beamformer::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { auto *out = reinterpret_cast(output_items[0]); // channel output buffers // gr_complex *ch1 = (gr_complex *) input_items[0]; // gr_complex *ch2 = (gr_complex *) input_items[1]; // gr_complex *ch3 = (gr_complex *) input_items[2]; // gr_complex *ch4 = (gr_complex *) input_items[3]; // gr_complex *ch5 = (gr_complex *) input_items[4]; // gr_complex *ch6 = (gr_complex *) input_items[5]; // gr_complex *ch7 = (gr_complex *) input_items[6]; // gr_complex *ch8 = (gr_complex *) input_items[7]; // NON-VOLK beamforming operation //TODO: Implement VOLK SIMD-accelerated beamformer! gr_complex sum; for (int n = 0; n < noutput_items; n++) { sum = gr_complex(0, 0); for (unsigned int i = 0; i < weight_vector.size(); i++) { sum = sum + (reinterpret_cast(input_items[i]))[n] * weight_vector[i]; } out[n] = sum; } return noutput_items; } src/algorithms/input_filter/gnuradio_blocks/beamformer.h000066400000000000000000000037551352176506000241760ustar00rootroot00000000000000/*! * \file beamformer.h * * \brief Simple spatial filter using RAW array input and beamforming coefficients * \author Javier Arribas jarribas (at) cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEAMFORMER_H #define GNSS_SDR_BEAMFORMER_H #include #include class beamformer; using beamformer_sptr = boost::shared_ptr; beamformer_sptr make_beamformer_sptr(); const int GNSS_SDR_BEAMFORMER_CHANNELS = 8; /*! * \brief This class implements a real-time software-defined spatial filter using the CTTC GNSS experimental antenna array input and a set of dynamically reloadable weights */ class beamformer : public gr::sync_block { public: ~beamformer() = default; int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend beamformer_sptr make_beamformer_sptr(); beamformer(); std::vector weight_vector = std::vector(GNSS_SDR_BEAMFORMER_CHANNELS, gr_complex(1.0, 0.0)); }; #endif src/algorithms/input_filter/gnuradio_blocks/notch_cc.cc000066400000000000000000000143621352176506000237710ustar00rootroot00000000000000/*! * \file notch_cc.cc * \brief Implements a multi state notch filter algorithm * \author Antonio Ramos (antonio.ramosdet(at)gmail.com) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "notch_cc.h" #include #include #include #include #include notch_sptr make_notch_filter(float pfa, float p_c_factor, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset) { return notch_sptr(new Notch(pfa, p_c_factor, length_, n_segments_est, n_segments_reset)); } Notch::Notch(float pfa, float p_c_factor, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset) : gr::block("Notch", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) { const int32_t alignment_multiple = volk_get_alignment() / sizeof(gr_complex); set_alignment(std::max(1, alignment_multiple)); set_history(2); this->pfa = pfa; noise_pow_est = 0.0; this->p_c_factor = gr_complex(p_c_factor, 0.0); this->length_ = length_; // Set the number of samples per segment filter_state_ = false; // Initial state of the filter n_deg_fred = 2 * length_; // Number of dregrees of freedom n_segments = 0; this->n_segments_est = n_segments_est; // Set the number of segments for noise power estimation this->n_segments_reset = n_segments_reset; // Set the period (in segments) when the noise power is estimated z_0 = gr_complex(0.0, 0.0); boost::math::chi_squared_distribution my_dist_(n_deg_fred); thres_ = boost::math::quantile(boost::math::complement(my_dist_, pfa)); c_samples = static_cast(volk_malloc(length_ * sizeof(gr_complex), volk_get_alignment())); angle_ = static_cast(volk_malloc(length_ * sizeof(float), volk_get_alignment())); power_spect = static_cast(volk_malloc(length_ * sizeof(float), volk_get_alignment())); last_out = gr_complex(0.0, 0.0); d_fft = std::unique_ptr(new gr::fft::fft_complex(length_, true)); } Notch::~Notch() { volk_free(c_samples); volk_free(angle_); volk_free(power_spect); } void Notch::forecast(int noutput_items __attribute__((unused)), gr_vector_int &ninput_items_required) { for (int &aux : ninput_items_required) { aux = length_; } } int Notch::general_work(int noutput_items, gr_vector_int &ninput_items __attribute__((unused)), gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { int32_t index_out = 0; float sig2dB = 0.0; float sig2lin = 0.0; lv_32fc_t dot_prod_; const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); in++; while ((index_out + length_) < noutput_items) { if ((n_segments < n_segments_est) && (filter_state_ == false)) { memcpy(d_fft->get_inbuf(), in, sizeof(gr_complex) * length_); d_fft->execute(); volk_32fc_s32f_power_spectrum_32f(power_spect, d_fft->get_outbuf(), 1.0, length_); volk_32f_s32f_calc_spectral_noise_floor_32f(&sig2dB, power_spect, 15.0, length_); sig2lin = std::pow(10.0, (sig2dB / 10.0)) / (static_cast(n_deg_fred)); noise_pow_est = (static_cast(n_segments) * noise_pow_est + sig2lin) / (static_cast(n_segments + 1)); memcpy(out, in, sizeof(gr_complex) * length_); } else { volk_32fc_x2_conjugate_dot_prod_32fc(&dot_prod_, in, in, length_); if ((lv_creal(dot_prod_) / noise_pow_est) > thres_) { if (filter_state_ == false) { filter_state_ = true; last_out = gr_complex(0, 0); } volk_32fc_x2_multiply_conjugate_32fc(c_samples, in, (in - 1), length_); volk_32fc_s32f_atan2_32f(angle_, c_samples, static_cast(1.0), length_); for (int32_t aux = 0; aux < length_; aux++) { z_0 = std::exp(gr_complex(0, 1) * (*(angle_ + aux))); *(out + aux) = *(in + aux) - z_0 * (*(in + aux - 1)) + p_c_factor * z_0 * last_out; last_out = *(out + aux); } } else { if (n_segments > n_segments_reset) { n_segments = 0; } filter_state_ = false; memcpy(out, in, sizeof(gr_complex) * length_); } } index_out += length_; n_segments++; in += length_; out += length_; } consume_each(index_out); return index_out; } src/algorithms/input_filter/gnuradio_blocks/notch_cc.h000066400000000000000000000050531352176506000236300ustar00rootroot00000000000000/*! * \file notch_cc.h * \brief Implements a notch filter algorithm * \author Antonio Ramos (antonio.ramosdet(at)gmail.com) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_NOTCH_H_ #define GNSS_SDR_NOTCH_H_ #include #include #include #include #include class Notch; using notch_sptr = boost::shared_ptr; notch_sptr make_notch_filter( float pfa, float p_c_factor, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); /*! * \brief This class implements a real-time software-defined multi state notch filter */ class Notch : public gr::block { public: ~Notch(); void forecast(int noutput_items, gr_vector_int &ninput_items_required); int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend notch_sptr make_notch_filter(float pfa, float p_c_factor, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); Notch(float pfa, float p_c_factor, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); float pfa; float noise_pow_est; float thres_; int32_t length_; int32_t n_deg_fred; uint32_t n_segments; uint32_t n_segments_est; uint32_t n_segments_reset; bool filter_state_; gr_complex last_out; gr_complex z_0; gr_complex p_c_factor; gr_complex *c_samples; float *angle_; float *power_spect; std::unique_ptr d_fft; }; #endif // GNSS_SDR_NOTCH_H_ src/algorithms/input_filter/gnuradio_blocks/notch_lite_cc.cc000066400000000000000000000153441352176506000250070ustar00rootroot00000000000000/*! * \file notch_lite_cc.cc * \brief Implements a multi state notch filter algorithm * \author Antonio Ramos (antonio.ramosdet(at)gmail.com) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "notch_lite_cc.h" #include #include #include #include #include notch_lite_sptr make_notch_filter_lite(float p_c_factor, float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset, int32_t n_segments_coeff) { return notch_lite_sptr(new NotchLite(p_c_factor, pfa, length_, n_segments_est, n_segments_reset, n_segments_coeff)); } NotchLite::NotchLite(float p_c_factor, float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset, int32_t n_segments_coeff) : gr::block("NotchLite", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) { const int32_t alignment_multiple = volk_get_alignment() / sizeof(gr_complex); set_alignment(std::max(1, alignment_multiple)); set_history(2); this->p_c_factor = gr_complex(p_c_factor, 0.0); this->n_segments_est = n_segments_est; this->n_segments_reset = n_segments_reset; this->n_segments_coeff_reset = n_segments_coeff; this->n_segments_coeff = 0; this->length_ = length_; set_output_multiple(length_); this->pfa = pfa; n_segments = 0; n_deg_fred = 2 * length_; noise_pow_est = 0.0; filter_state_ = false; z_0 = gr_complex(0.0, 0.0); last_out = gr_complex(0.0, 0.0); boost::math::chi_squared_distribution my_dist_(n_deg_fred); thres_ = boost::math::quantile(boost::math::complement(my_dist_, pfa)); c_samples1 = gr_complex(0.0, 0.0); c_samples2 = gr_complex(0.0, 0.0); angle1 = 0.0; angle2 = 0.0; power_spect = static_cast(volk_malloc(length_ * sizeof(float), volk_get_alignment())); d_fft = std::unique_ptr(new gr::fft::fft_complex(length_, true)); } NotchLite::~NotchLite() { volk_free(power_spect); } void NotchLite::forecast(int noutput_items __attribute__((unused)), gr_vector_int &ninput_items_required) { for (int &aux : ninput_items_required) { aux = length_; } } int NotchLite::general_work(int noutput_items, gr_vector_int &ninput_items __attribute__((unused)), gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { int32_t index_out = 0; float sig2dB = 0.0; float sig2lin = 0.0; lv_32fc_t dot_prod_; const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); in++; while ((index_out + length_) < noutput_items) { if ((n_segments < n_segments_est) && (filter_state_ == false)) { memcpy(d_fft->get_inbuf(), in, sizeof(gr_complex) * length_); d_fft->execute(); volk_32fc_s32f_power_spectrum_32f(power_spect, d_fft->get_outbuf(), 1.0, length_); volk_32f_s32f_calc_spectral_noise_floor_32f(&sig2dB, power_spect, 15.0, length_); sig2lin = std::pow(10.0, (sig2dB / 10.0)) / static_cast(n_deg_fred); noise_pow_est = (static_cast(n_segments) * noise_pow_est + sig2lin) / static_cast(n_segments + 1); memcpy(out, in, sizeof(gr_complex) * length_); } else { volk_32fc_x2_conjugate_dot_prod_32fc(&dot_prod_, in, in, length_); if ((lv_creal(dot_prod_) / noise_pow_est) > thres_) { if (filter_state_ == false) { filter_state_ = true; last_out = gr_complex(0, 0); n_segments_coeff = 0; } if (n_segments_coeff == 0) { volk_32fc_x2_multiply_conjugate_32fc(&c_samples1, (in + 1), in, 1); volk_32fc_s32f_atan2_32f(&angle1, &c_samples1, static_cast(1.0), 1); volk_32fc_x2_multiply_conjugate_32fc(&c_samples2, (in + length_ - 1), (in + length_ - 2), 1); volk_32fc_s32f_atan2_32f(&angle2, &c_samples2, static_cast(1.0), 1); float angle_ = (angle1 + angle2) / 2.0; z_0 = std::exp(gr_complex(0, 1) * angle_); } for (int32_t aux = 0; aux < length_; aux++) { *(out + aux) = *(in + aux) - z_0 * (*(in + aux - 1)) + p_c_factor * z_0 * last_out; last_out = *(out + aux); } n_segments_coeff++; n_segments_coeff = n_segments_coeff % n_segments_coeff_reset; } else { if (n_segments > n_segments_reset) { n_segments = 0; } filter_state_ = false; memcpy(out, in, sizeof(gr_complex) * length_); } } index_out += length_; n_segments++; in += length_; out += length_; } consume_each(index_out); return index_out; } src/algorithms/input_filter/gnuradio_blocks/notch_lite_cc.h000066400000000000000000000054751352176506000246550ustar00rootroot00000000000000/*! * \file notch_lite_cc.h * \brief Implements a notch filter light algorithm * \author Antonio Ramos (antonio.ramosdet(at)gmail.com) * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_NOTCH_LITE_H_ #define GNSS_SDR_NOTCH_LITE_H_ #include #include #include #include #include class NotchLite; using notch_lite_sptr = boost::shared_ptr; notch_lite_sptr make_notch_filter_lite( float p_c_factor, float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset, int32_t n_segments_coeff); /*! * \brief This class implements a real-time software-defined multi state notch filter light version */ class NotchLite : public gr::block { public: ~NotchLite(); void forecast(int noutput_items, gr_vector_int &ninput_items_required); int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend notch_lite_sptr make_notch_filter_lite(float p_c_factor, float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset, int32_t n_segments_coeff); NotchLite(float p_c_factor, float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset, int32_t n_segments_coeff); int32_t length_; int32_t n_segments; int32_t n_segments_est; int32_t n_segments_reset; int32_t n_segments_coeff_reset; int32_t n_segments_coeff; int32_t n_deg_fred; float pfa; float thres_; float noise_pow_est; bool filter_state_; gr_complex last_out; gr_complex z_0; gr_complex p_c_factor; gr_complex c_samples1; gr_complex c_samples2; float angle1; float angle2; float *power_spect; std::unique_ptr d_fft; }; #endif // GNSS_SDR_NOTCH_LITE_H_ src/algorithms/input_filter/gnuradio_blocks/pulse_blanking_cc.cc000066400000000000000000000115401352176506000256460ustar00rootroot00000000000000/*! * \file pulse_blanking_cc.cc * \brief Implements a pulse blanking algorithm * \author Javier Arribas (jarribas(at)cttc.es) * Antonio Ramos (antonio.ramosdet(at)gmail.com) * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pulse_blanking_cc.h" #include #include #include #include pulse_blanking_cc_sptr make_pulse_blanking_cc(float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset) { return pulse_blanking_cc_sptr(new pulse_blanking_cc(pfa, length_, n_segments_est, n_segments_reset)); } pulse_blanking_cc::pulse_blanking_cc(float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset) : gr::block("pulse_blanking_cc", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) { const int32_t alignment_multiple = volk_get_alignment() / sizeof(gr_complex); set_alignment(std::max(1, alignment_multiple)); this->pfa = pfa; this->length_ = length_; last_filtered = false; n_segments = 0; this->n_segments_est = n_segments_est; this->n_segments_reset = n_segments_reset; noise_power_estimation = 0.0; n_deg_fred = 2 * length_; boost::math::chi_squared_distribution my_dist_(n_deg_fred); thres_ = boost::math::quantile(boost::math::complement(my_dist_, pfa)); zeros_ = static_cast(volk_malloc(length_ * sizeof(gr_complex), volk_get_alignment())); for (int32_t aux = 0; aux < length_; aux++) { zeros_[aux] = gr_complex(0.0, 0.0); } } pulse_blanking_cc::~pulse_blanking_cc() { volk_free(zeros_); } void pulse_blanking_cc::forecast(int noutput_items __attribute__((unused)), gr_vector_int &ninput_items_required) { for (int &aux : ninput_items_required) { aux = length_; } } int pulse_blanking_cc::general_work(int noutput_items, gr_vector_int &ninput_items __attribute__((unused)), gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); auto *magnitude = static_cast(volk_malloc(noutput_items * sizeof(float), volk_get_alignment())); volk_32fc_magnitude_squared_32f(magnitude, in, noutput_items); int32_t sample_index = 0; float segment_energy; while ((sample_index + length_) < noutput_items) { volk_32f_accumulator_s32f(&segment_energy, (magnitude + sample_index), length_); if ((n_segments < n_segments_est) && (last_filtered == false)) { noise_power_estimation = (static_cast(n_segments) * noise_power_estimation + segment_energy / static_cast(n_deg_fred)) / static_cast(n_segments + 1); memcpy(out, in, sizeof(gr_complex) * length_); } else { if ((segment_energy / noise_power_estimation) > thres_) { memcpy(out, zeros_, sizeof(gr_complex) * length_); last_filtered = true; } else { memcpy(out, in, sizeof(gr_complex) * length_); last_filtered = false; if (n_segments > n_segments_reset) { n_segments = 0; } } } in += length_; out += length_; sample_index += length_; n_segments++; } volk_free(magnitude); consume_each(sample_index); return sample_index; } src/algorithms/input_filter/gnuradio_blocks/pulse_blanking_cc.h000066400000000000000000000046501352176506000255140ustar00rootroot00000000000000/*! * \file pulse_blanking_cc.h * \brief Implements a pulse blanking algorithm * \author Javier Arribas (jarribas(at)cttc.es) * Antonio Ramos (antonio.ramosdet(at)gmail.com) * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PULSE_BLANKING_H_ #define GNSS_SDR_PULSE_BLANKING_H_ #include #include #include class pulse_blanking_cc; using pulse_blanking_cc_sptr = boost::shared_ptr; pulse_blanking_cc_sptr make_pulse_blanking_cc( float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); class pulse_blanking_cc : public gr::block { public: ~pulse_blanking_cc(); void forecast(int noutput_items, gr_vector_int &ninput_items_required); int general_work(int noutput_items __attribute__((unused)), gr_vector_int &ninput_items __attribute__((unused)), gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); private: friend pulse_blanking_cc_sptr make_pulse_blanking_cc(float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); pulse_blanking_cc(float pfa, int32_t length_, int32_t n_segments_est, int32_t n_segments_reset); int32_t length_; int32_t n_segments; int32_t n_segments_est; int32_t n_segments_reset; int32_t n_deg_fred; bool last_filtered; float noise_power_estimation; float thres_; float pfa; gr_complex *zeros_; }; #endif // GNSS_SDR_PULSE_BLANKING_H_ src/algorithms/libs/000077500000000000000000000000001352176506000147545ustar00rootroot00000000000000src/algorithms/libs/CMakeLists.txt000066400000000000000000000124061352176506000175170ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # add_subdirectory(rtklib) set(GNSS_SPLIBS_SOURCES gps_l2c_signal.cc gps_l5_signal.cc galileo_e1_signal_processing.cc gnss_signal_processing.cc gps_sdr_signal_processing.cc glonass_l1_signal_processing.cc glonass_l2_signal_processing.cc pass_through.cc galileo_e5_signal_processing.cc beidou_b1i_signal_processing.cc beidou_b3i_signal_processing.cc complex_byte_to_float_x2.cc byte_x2_to_complex_byte.cc cshort_to_float_x2.cc short_x2_to_cshort.cc complex_float_to_complex_byte.cc conjugate_cc.cc conjugate_sc.cc conjugate_ic.cc gnss_sdr_create_directory.cc geofunctions.cc ) set(GNSS_SPLIBS_HEADERS gps_l2c_signal.h gps_l5_signal.h galileo_e1_signal_processing.h gnss_signal_processing.h gps_sdr_signal_processing.h glonass_l1_signal_processing.h glonass_l2_signal_processing.h pass_through.h galileo_e5_signal_processing.h beidou_b1i_signal_processing.h beidou_b3i_signal_processing.h complex_byte_to_float_x2.h byte_x2_to_complex_byte.h cshort_to_float_x2.h short_x2_to_cshort.h complex_float_to_complex_byte.h conjugate_cc.h conjugate_sc.h conjugate_ic.h gnss_sdr_create_directory.h gnss_circular_deque.h geofunctions.h ) if(ENABLE_OPENCL) set(GNSS_SPLIBS_SOURCES ${GNSS_SPLIBS_SOURCES} opencl/fft_execute.cc # Needs OpenCL opencl/fft_setup.cc # Needs OpenCL opencl/fft_kernelstring.cc # Needs OpenCL ) endif() list(SORT GNSS_SPLIBS_HEADERS) list(SORT GNSS_SPLIBS_SOURCES) source_group(Headers FILES ${GNSS_SPLIBS_HEADERS}) add_library(algorithms_libs ${GNSS_SPLIBS_SOURCES} ${GNSS_SPLIBS_HEADERS}) if(${FILESYSTEM_FOUND}) target_compile_definitions(algorithms_libs PRIVATE -DHAS_STD_FILESYSTEM=1) if(${find_experimental}) target_compile_definitions(algorithms_libs PRIVATE -DHAS_STD_FILESYSTEM_EXPERIMENTAL=1) endif() target_link_libraries(algorithms_libs PRIVATE std::filesystem) else() target_link_libraries(algorithms_libs PRIVATE Boost::filesystem Boost::system) endif() target_include_directories(algorithms_libs PUBLIC ${CMAKE_SOURCE_DIR}/src/algorithms/libs/gsl/include ) target_link_libraries(algorithms_libs PUBLIC Armadillo::armadillo Boost::boost Gflags::gflags Gnuradio::runtime Gnuradio::blocks PRIVATE core_system_parameters Volk::volk ${ORC_LIBRARIES} Volkgnsssdr::volkgnsssdr Glog::glog ) if(ENABLE_OPENCL) target_link_libraries(algorithms_libs PUBLIC OpenCL::OpenCL) target_include_directories(algorithms_libs PUBLIC $ ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(algorithms_libs PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() ############################################################################### source_group(Headers FILES gnss_sdr_flags.h) add_library(gnss_sdr_flags gnss_sdr_flags.cc gnss_sdr_flags.h) if(${FILESYSTEM_FOUND}) target_compile_definitions(gnss_sdr_flags PRIVATE -DHAS_STD_FILESYSTEM=1) if(${find_experimental}) target_compile_definitions(gnss_sdr_flags PRIVATE -DHAS_STD_FILESYSTEM_EXPERIMENTAL=1) endif() target_link_libraries(gnss_sdr_flags PRIVATE std::filesystem) else() target_link_libraries(gnss_sdr_flags PRIVATE Boost::filesystem) endif() target_link_libraries(gnss_sdr_flags PUBLIC Gflags::gflags ) if(${GFLAGS_GREATER_20}) target_compile_definitions(gnss_sdr_flags PRIVATE -DGFLAGS_GREATER_2_0=1) endif() target_compile_definitions(gnss_sdr_flags PRIVATE -DGNSSSDR_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}" ) if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(gnss_sdr_flags PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) endif() endif() set_property(TARGET gnss_sdr_flags APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/libs/beidou_b1i_signal_processing.cc000066400000000000000000000160641352176506000230650ustar00rootroot00000000000000/*! * \file beidou_b1i_signal_processing.cc * \brief This class implements various functions for BeiDou B1I signal * \author Sergi Segura, 2018. sergi.segura.munoz(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beidou_b1i_signal_processing.h" #include #include #include auto auxCeil = [](float x) { return static_cast(static_cast((x) + 1)); }; void beidou_b1i_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 2046; std::bitset<_code_length> G1{}; std::bitset<_code_length> G2{}; std::bitset<11> G1_register(std::string("01010101010")); std::bitset<11> G2_register(std::string("01010101010")); bool feedback1, feedback2; bool aux; uint32_t lcv, lcv2; uint32_t delay; int32_t prn_idx; const std::array delays = {712 /*PRN1*/, 1581, 1414, 1550, 581, 771, 1311, 1043, 1549, 359, 710, 1579, 1548, 1103, 579, 769, 358, 709, 1411, 1547, 1102, 578, 357, 1577, 1410, 1546, 1101, 707, 1576, 1409, 1545, 354 /*PRN32*/, 705}; const std::array phase1 = {1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 8, 8, 8, 9, 9, 10}; const std::array phase2 = {3, 4, 5, 6, 8, 9, 10, 11, 7, 4, 5, 6, 8, 9, 10, 11, 5, 6, 8, 9, 10, 11, 6, 8, 9, 10, 11, 8, 9, 10, 11, 9, 10, 11, 10, 11, 11}; // compute delay array index for given PRN number prn_idx = _prn - 1; // A simple error check if ((prn_idx < 0) || (prn_idx > 32)) { return; } // Generate G1 & G2 Register for (lcv = 0; lcv < _code_length; lcv++) { G1[lcv] = G1_register[0]; G2[lcv] = G2_register[-(phase1[prn_idx] - 11)] xor G2_register[-(phase2[prn_idx] - 11)]; feedback1 = G1_register[0] xor G1_register[1] xor G1_register[2] xor G1_register[3] xor G1_register[4] xor G1_register[10]; feedback2 = G2_register[0] xor G2_register[2] xor G2_register[3] xor G2_register[6] xor G2_register[7] xor G2_register[8] xor G2_register[9] xor G2_register[10]; for (lcv2 = 0; lcv2 < 10; lcv2++) { G1_register[lcv2] = G1_register[lcv2 + 1]; G2_register[lcv2] = G2_register[lcv2 + 1]; } G1_register[10] = feedback1; G2_register[10] = feedback2; } // Set the delay delay = _code_length - delays[prn_idx] * 0; //********************************** delay += _chip_shift; delay %= _code_length; // Generate PRN from G1 and G2 Registers for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[(lcv + _chip_shift) % _code_length] xor G2[delay]; if (aux == true) { _dest[lcv] = 1; } else { _dest[lcv] = -1; } delay++; delay %= _code_length; } } void beidou_b1i_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 2046; std::array b1i_code_int{}; beidou_b1i_code_gen_int(gsl::span(b1i_code_int.data(), _code_length), _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = static_cast(b1i_code_int[ii]); } } void beidou_b1i_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 2046; std::array b1i_code_int{}; beidou_b1i_code_gen_int(gsl::span(b1i_code_int.data(), _code_length), _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = std::complex(static_cast(b1i_code_int[ii]), 0.0F); } } /* * Generates complex GPS L1 C/A code for the desired SV ID and sampled to specific sampling frequency */ void beidou_b1i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift) { // This function is based on the GNU software GPS for MATLAB in the Kay Borre book std::array, 2046> _code{}; int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; float aux; const int32_t _codeFreqBasis = 2046000; // Hz const int32_t _codeLength = 2046; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / static_cast(_codeFreqBasis / _codeLength)); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(_codeFreqBasis); // C/A chip period in sec beidou_b1i_code_gen_complex(_code, _prn, _chip_shift); // generate C/A code 1 sample per chip for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read C/A code values -------------------- // The length of the index array depends on the sampling frequency - // number of samples per millisecond (because one C/A code period is one // millisecond). aux = (_ts * (i + 1)) / _tc; _codeValueIndex = auxCeil(aux) - 1; // --- Make the digitized version of the C/A code ------------------ // The "upsampled" code is made by selecting values form the CA code // chip array (caCode) for the time instances of each sample. if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = _code[_codeLength - 1]; } else { _dest[i] = _code[_codeValueIndex]; // repeat the chip -> upsample } } } src/algorithms/libs/beidou_b1i_signal_processing.h000066400000000000000000000046251352176506000227270ustar00rootroot00000000000000/*! * \file beidou_b1i_signal_processing.h * \brief This class implements various functions for BeiDou B1I signals * \author Sergi Segura, 2018. sergi.segura.munoz(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEIDOU_B1I_SDR_SIGNAL_PROCESSING_H_ #define GNSS_SDR_BEIDOU_B1I_SDR_SIGNAL_PROCESSING_H_ #include #include #include //! Generates int32_t GPS L1 C/A code for the desired SV ID and code shift void beidou_b1i_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates float GPS L1 C/A code for the desired SV ID and code shift void beidou_b1i_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates complex GPS L1 C/A code for the desired SV ID and code shift, and sampled to specific sampling frequency void beidou_b1i_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift); //! Generates N complex GPS L1 C/A codes for the desired SV ID and code shift void beidou_b1i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, uint32_t _ncodes); //! Generates complex GPS L1 C/A code for the desired SV ID and code shift void beidou_b1i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift); #endif /* BEIDOU_B1I_SDR_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/beidou_b3i_signal_processing.cc000066400000000000000000000243641352176506000230710ustar00rootroot00000000000000/*! * \file beidou_b3i_signal_processing.cc * \brief This class implements various functions for BeiDou B1I signal * \author Damian Miralles, 2019. dmiralles2009@gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2015 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "beidou_b3i_signal_processing.h" #include #include #include auto auxCeil = [](float x) { return static_cast(static_cast((x) + 1)); }; void beidou_b3i_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 10230; std::bitset<_code_length> G1{}; std::bitset<_code_length> G2{}; auto G1_register = std::bitset<13>{}.set(); // All true auto G2_register = std::bitset<13>{}.set(); // All true auto G1_register_reset = std::bitset<13>{}.set(); G1_register_reset.reset(0); G1_register_reset.reset(1); // {false, false, true, true, true, true, true, true, true, true, true, true, true}; bool feedback1, feedback2, aux; uint32_t lcv, lcv2, delay; int32_t prn_idx = _prn - 1; const std::array, 63> G2_register_shifted = {std::bitset<13>(std::string("1010111111111")), std::bitset<13>(std::string("1111000101011")), std::bitset<13>(std::string("1011110001010")), std::bitset<13>(std::string("1111111111011")), std::bitset<13>(std::string("1100100011111")), std::bitset<13>(std::string("1001001100100")), std::bitset<13>(std::string("1111111010010")), std::bitset<13>(std::string("1110111111101")), std::bitset<13>(std::string("1010000000010")), std::bitset<13>(std::string("0010000011011")), std::bitset<13>(std::string("1110101110000")), std::bitset<13>(std::string("0010110011110")), std::bitset<13>(std::string("0110010010101")), std::bitset<13>(std::string("0111000100110")), std::bitset<13>(std::string("1000110001001")), std::bitset<13>(std::string("1110001111100")), std::bitset<13>(std::string("0010011000101")), std::bitset<13>(std::string("0000011101100")), std::bitset<13>(std::string("1000101010111")), std::bitset<13>(std::string("0001011011110")), std::bitset<13>(std::string("0010000101101")), std::bitset<13>(std::string("0010110001010")), std::bitset<13>(std::string("0001011001111")), std::bitset<13>(std::string("0011001100010")), std::bitset<13>(std::string("0011101001000")), std::bitset<13>(std::string("0100100101001")), std::bitset<13>(std::string("1011011010011")), std::bitset<13>(std::string("1010111100010")), std::bitset<13>(std::string("0001011110101")), std::bitset<13>(std::string("0111111111111")), std::bitset<13>(std::string("0110110001111")), std::bitset<13>(std::string("1010110001001")), std::bitset<13>(std::string("1001010101011")), std::bitset<13>(std::string("1100110100101")), std::bitset<13>(std::string("1101001011101")), std::bitset<13>(std::string("1111101110100")), std::bitset<13>(std::string("0010101100111")), std::bitset<13>(std::string("1110100010000")), std::bitset<13>(std::string("1101110010000")), std::bitset<13>(std::string("1101011001110")), std::bitset<13>(std::string("1000000110100")), std::bitset<13>(std::string("0101111011001")), std::bitset<13>(std::string("0110110111100")), std::bitset<13>(std::string("1101001110001")), std::bitset<13>(std::string("0011100100010")), std::bitset<13>(std::string("0101011000101")), std::bitset<13>(std::string("1001111100110")), std::bitset<13>(std::string("1111101001000")), std::bitset<13>(std::string("0000101001001")), std::bitset<13>(std::string("1000010101100")), std::bitset<13>(std::string("1111001001100")), std::bitset<13>(std::string("0100110001111")), std::bitset<13>(std::string("0000000011000")), std::bitset<13>(std::string("1000000000100")), std::bitset<13>(std::string("0011010100110")), std::bitset<13>(std::string("1011001000110")), std::bitset<13>(std::string("0111001111000")), std::bitset<13>(std::string("0010111001010")), std::bitset<13>(std::string("1100111110110")), std::bitset<13>(std::string("1001001000101")), std::bitset<13>(std::string("0111000100000")), std::bitset<13>(std::string("0011001000010")), std::bitset<13>(std::string("0010001001110"))}; // A simple error check if ((prn_idx < 0) || (prn_idx > 63)) { return; } // Assign shifted G2 register based on prn number G2_register = G2_register_shifted[prn_idx]; // Generate G1 and G2 Register for (lcv = 0; lcv < _code_length; lcv++) { G1[lcv] = G1_register[0]; G2[lcv] = G2_register[0]; feedback1 = G1_register[0] xor G1_register[9] xor G1_register[10] xor G1_register[12]; feedback2 = G2_register[0] xor G2_register[1] xor G2_register[3] xor G2_register[4] xor G2_register[6] xor G2_register[7] xor G2_register[8] xor G2_register[12]; for (lcv2 = 0; lcv2 < 12; lcv2++) { G1_register[lcv2] = G1_register[lcv2 + 1]; G2_register[lcv2] = G2_register[lcv2 + 1]; } G1_register[12] = feedback1; G2_register[12] = feedback2; // Reset G1 register if sequence found if (G1_register == G1_register_reset) { G1_register = std::bitset<13>{}.set(); // All true } } delay = _code_length; delay += _chip_shift; delay %= _code_length; // Generate PRN from G1 and G2 Registers for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[(lcv + _chip_shift) % _code_length] xor G2[delay]; if (aux == true) { _dest[lcv] = 1; } else { _dest[lcv] = -1; } delay++; delay %= _code_length; } } void beidou_b3i_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 10230; std::array b3i_code_int{}; beidou_b3i_code_gen_int(b3i_code_int, _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = static_cast(b3i_code_int[ii]); } } void beidou_b3i_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 10230; std::array b3i_code_int{}; beidou_b3i_code_gen_int(b3i_code_int, _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = std::complex(static_cast(b3i_code_int[ii]), 0.0F); } } void beidou_b3i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int _fs, uint32_t _chip_shift) { // This function is based on the GNU software GPS for MATLAB in the Kay Borre book std::array, 10230> _code{}; int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; float aux; const int32_t _codeFreqBasis = 10230000; // Hz const int32_t _codeLength = 10230; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / static_cast(_codeFreqBasis / _codeLength)); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(_codeFreqBasis); // C/A chip period in sec beidou_b3i_code_gen_complex(_code, _prn, _chip_shift); // generate C/A code 1 sample per chip for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read C/A code values -------------------- // The length of the index array depends on the sampling frequency - // number of samples per millisecond (because one C/A code period is one // millisecond). aux = (_ts * (i + 1)) / _tc; _codeValueIndex = auxCeil(aux) - 1; // --- Make the digitized version of the C/A code ------------------ // The "upsampled" code is made by selecting values form the CA code // chip array (caCode) for the time instances of each sample. if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = _code[_codeLength - 1]; } else { _dest[i] = _code[_codeValueIndex]; // repeat the chip -> upsample } } } src/algorithms/libs/beidou_b3i_signal_processing.h000066400000000000000000000045751352176506000227350ustar00rootroot00000000000000/*! * \file beidou_b3i_signal_processing.h * \brief This class implements various functions for BeiDou B3I signals * \author Damian Miralles, 2019. dmiralles2009@gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BEIDOU_B3I_SIGNAL_PROCESSING_H_ #define GNSS_SDR_BEIDOU_B3I_SIGNAL_PROCESSING_H_ #include #include #include //! Generates int BeiDou B3I code for the desired SV ID and code shift void beidou_b3i_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates float BeiDou B3I code for the desired SV ID and code shift void beidou_b3i_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates complex BeiDou B3I code for the desired SV ID and code shift, and sampled to specific sampling frequency void beidou_b3i_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift); //! Generates N complex BeiDou B3I codes for the desired SV ID and code shift void beidou_b3i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int _fs, uint32_t _chip_shift, uint32_t _ncodes); //! Generates complex BeiDou B3I code for the desired SV ID and code shift void beidou_b3i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_BEIDOU_B3I_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/byte_x2_to_complex_byte.cc000066400000000000000000000054301352176506000221150ustar00rootroot00000000000000/*! * \file byte_x2_to_complex_byte.cc * \brief Adapts two signed char streams into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "byte_x2_to_complex_byte.h" #include #include #include // for max #include // for complex #include // for int8_t byte_x2_to_complex_byte_sptr make_byte_x2_to_complex_byte() { return byte_x2_to_complex_byte_sptr(new byte_x2_to_complex_byte()); } byte_x2_to_complex_byte::byte_x2_to_complex_byte() : sync_block("byte_x2_to_complex_byte", gr::io_signature::make(2, 2, sizeof(int8_t)), // int8_t, defined in stdint.h and included in volk.h (signed char) gr::io_signature::make(1, 1, sizeof(lv_8sc_t))) // lv_8sc_t is a Volk's typedef for std::complex { const int alignment_multiple = volk_gnsssdr_get_alignment() / sizeof(lv_8sc_t); set_alignment(std::max(1, alignment_multiple)); } int byte_x2_to_complex_byte::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in0 = reinterpret_cast(input_items[0]); const auto *in1 = reinterpret_cast(input_items[1]); auto *out = reinterpret_cast(output_items[0]); // This could be put into a volk kernel int8_t real_part; int8_t imag_part; for (int number = 0; number < noutput_items; number++) { // lv_cmake(r, i) defined at volk/volk_complex.h real_part = *in0++; imag_part = *in1++; *out++ = lv_cmake(real_part, imag_part); } return noutput_items; } src/algorithms/libs/byte_x2_to_complex_byte.h000066400000000000000000000037151352176506000217630ustar00rootroot00000000000000/*! * \file byte_x2_to_complex_byte.h * \brief Adapts two signed char streams into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_BYTE_X2_TO_COMPLEX_BYTE_H_ #define GNSS_SDR_BYTE_X2_TO_COMPLEX_BYTE_H_ #include #include #include // for gr_vector_const_void_star class byte_x2_to_complex_byte; using byte_x2_to_complex_byte_sptr = boost::shared_ptr; byte_x2_to_complex_byte_sptr make_byte_x2_to_complex_byte(); /*! * \brief This class adapts two signed char streams * into a std::complex stream */ class byte_x2_to_complex_byte : public gr::sync_block { private: friend byte_x2_to_complex_byte_sptr make_byte_x2_to_complex_byte(); byte_x2_to_complex_byte(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/complex_byte_to_float_x2.cc000066400000000000000000000046031352176506000222600ustar00rootroot00000000000000/*! * \file complex_byte_to_float_x2.cc * \brief Adapts a std::complex stream into two 16-bits (short) streams * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "complex_byte_to_float_x2.h" #include #include #include // for max complex_byte_to_float_x2_sptr make_complex_byte_to_float_x2() { return complex_byte_to_float_x2_sptr(new complex_byte_to_float_x2()); } complex_byte_to_float_x2::complex_byte_to_float_x2() : sync_block("complex_byte_to_float_x2", gr::io_signature::make(1, 1, sizeof(lv_8sc_t)), // lv_8sc_t is a Volk's typedef for std::complex gr::io_signature::make(2, 2, sizeof(float))) { const int alignment_multiple = volk_get_alignment() / sizeof(float); set_alignment(std::max(1, alignment_multiple)); } int complex_byte_to_float_x2::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out0 = reinterpret_cast(output_items[0]); auto *out1 = reinterpret_cast(output_items[1]); const float scalar = 1; volk_8ic_s32f_deinterleave_32f_x2(out0, out1, in, scalar, noutput_items); return noutput_items; } src/algorithms/libs/complex_byte_to_float_x2.h000066400000000000000000000037411352176506000221240ustar00rootroot00000000000000/*! * \file complex_byte_to_float_x2.h * \brief Adapts a std::complex stream into two 16-bits (short) streams * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_COMPLEX_BYTE_TO_FLOAT_X2_H_ #define GNSS_SDR_COMPLEX_BYTE_TO_FLOAT_X2_H_ #include #include #include // for gr_vector_const_void_star class complex_byte_to_float_x2; using complex_byte_to_float_x2_sptr = boost::shared_ptr; complex_byte_to_float_x2_sptr make_complex_byte_to_float_x2(); /*! * \brief This class adapts a std::complex stream * into two 16-bits (short) streams */ class complex_byte_to_float_x2 : public gr::sync_block { private: friend complex_byte_to_float_x2_sptr make_complex_byte_to_float_x2(); complex_byte_to_float_x2(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/complex_float_to_complex_byte.cc000066400000000000000000000045701352176506000234010ustar00rootroot00000000000000/*! * \file complex_float_to_complex_byte.cc * \brief Adapts a gr_complex stream into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "complex_float_to_complex_byte.h" #include #include #include // for max complex_float_to_complex_byte_sptr make_complex_float_to_complex_byte() { return complex_float_to_complex_byte_sptr(new complex_float_to_complex_byte()); } complex_float_to_complex_byte::complex_float_to_complex_byte() : sync_block("complex_float_to_complex_byte", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(lv_8sc_t))) // lv_8sc_t is a Volk's typedef for std::complex { const int alignment_multiple = volk_gnsssdr_get_alignment() / sizeof(lv_8sc_t); set_alignment(std::max(1, alignment_multiple)); } int complex_float_to_complex_byte::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); volk_gnsssdr_32fc_convert_8ic(out, in, noutput_items); return noutput_items; } src/algorithms/libs/complex_float_to_complex_byte.h000066400000000000000000000040121352176506000232320ustar00rootroot00000000000000/*! * \file complex_float_to_complex_byte.h * \brief Adapts a gr_complex stream into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_COMPLEX_FLOAT_TO_COMPLEX_BYTE_H_ #define GNSS_SDR_COMPLEX_FLOAT_TO_COMPLEX_BYTE_H_ #include #include #include // for gr_vector_const_void_star class complex_float_to_complex_byte; using complex_float_to_complex_byte_sptr = boost::shared_ptr; complex_float_to_complex_byte_sptr make_complex_float_to_complex_byte(); /*! * \brief This class adapts a gr_complex stream into a std::complex stream */ class complex_float_to_complex_byte : public gr::sync_block { private: friend complex_float_to_complex_byte_sptr make_complex_float_to_complex_byte(); complex_float_to_complex_byte(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/conjugate_cc.cc000066400000000000000000000040261352176506000177110ustar00rootroot00000000000000/*! * \file conjugate_cc.cc * \brief Conjugate a stream of gr_complex * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "conjugate_cc.h" #include #include #include // for max conjugate_cc_sptr make_conjugate_cc() { return conjugate_cc_sptr(new conjugate_cc()); } conjugate_cc::conjugate_cc() : gr::sync_block("conjugate_cc", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) { const int alignment_multiple = volk_get_alignment() / sizeof(gr_complex); set_alignment(std::max(1, alignment_multiple)); } int conjugate_cc::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); volk_32fc_conjugate_32fc(out, in, noutput_items); return noutput_items; } src/algorithms/libs/conjugate_cc.h000066400000000000000000000034401352176506000175520ustar00rootroot00000000000000/*! * \file conjugate_cc.h * \brief Conjugate a stream of gr_complex * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CONJUGATE_CC_H_ #define GNSS_SDR_CONJUGATE_CC_H_ #include #include #include // for gr_vector_const_void_star class conjugate_cc; using conjugate_cc_sptr = boost::shared_ptr; conjugate_cc_sptr make_conjugate_cc(); /*! * \brief This class adapts a std::complex stream * into two 32-bits (float) streams */ class conjugate_cc : public gr::sync_block { private: friend conjugate_cc_sptr make_conjugate_cc(); conjugate_cc(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/conjugate_ic.cc000066400000000000000000000040771352176506000177250ustar00rootroot00000000000000/*! * \file conjugate_ic.cc * \brief Conjugate a stream of lv_8sc_t ( std::complex ) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "conjugate_ic.h" #include #include #include // for max conjugate_ic_sptr make_conjugate_ic() { return conjugate_ic_sptr(new conjugate_ic()); } conjugate_ic::conjugate_ic() : gr::sync_block("conjugate_ic", gr::io_signature::make(1, 1, sizeof(lv_8sc_t)), gr::io_signature::make(1, 1, sizeof(lv_8sc_t))) { const int alignment_multiple = volk_gnsssdr_get_alignment() / sizeof(lv_8sc_t); set_alignment(std::max(1, alignment_multiple)); } int conjugate_ic::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); volk_gnsssdr_8ic_conjugate_8ic(out, in, noutput_items); return noutput_items; } src/algorithms/libs/conjugate_ic.h000066400000000000000000000034651352176506000175670ustar00rootroot00000000000000/*! * \file conjugate_ic.h * \brief Conjugate a stream of lv_8sc_t ( std::complex ) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CONJUGATE_IC_H_ #define GNSS_SDR_CONJUGATE_IC_H_ #include #include #include // for gr_vector_const_void_star class conjugate_ic; using conjugate_ic_sptr = boost::shared_ptr; conjugate_ic_sptr make_conjugate_ic(); /*! * \brief This class adapts a std::complex stream * into two 32-bits (float) streams */ class conjugate_ic : public gr::sync_block { private: friend conjugate_ic_sptr make_conjugate_ic(); conjugate_ic(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/conjugate_sc.cc000066400000000000000000000041061352176506000177300ustar00rootroot00000000000000/*! * \file conjugate_sc.h * \brief Conjugate a stream of lv_16sc_t ( std::complex ) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "conjugate_sc.h" #include #include #include // for max conjugate_sc_sptr make_conjugate_sc() { return conjugate_sc_sptr(new conjugate_sc()); } conjugate_sc::conjugate_sc() : gr::sync_block("conjugate_sc", gr::io_signature::make(1, 1, sizeof(lv_16sc_t)), gr::io_signature::make(1, 1, sizeof(lv_16sc_t))) { const int alignment_multiple = volk_gnsssdr_get_alignment() / sizeof(lv_16sc_t); set_alignment(std::max(1, alignment_multiple)); } int conjugate_sc::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out = reinterpret_cast(output_items[0]); volk_gnsssdr_16ic_conjugate_16ic(out, in, noutput_items); return noutput_items; } src/algorithms/libs/conjugate_sc.h000066400000000000000000000034671352176506000176030ustar00rootroot00000000000000/*! * \file conjugate_sc.h * \brief Conjugate a stream of lv_16sc_t ( std::complex ) * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CONJUGATE_SC_H_ #define GNSS_SDR_CONJUGATE_SC_H_ #include #include #include // for gr_vector_const_void_star class conjugate_sc; using conjugate_sc_sptr = boost::shared_ptr; conjugate_sc_sptr make_conjugate_sc(); /*! * \brief This class adapts a std::complex stream * into two 32-bits (float) streams */ class conjugate_sc : public gr::sync_block { private: friend conjugate_sc_sptr make_conjugate_sc(); conjugate_sc(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/cshort_to_float_x2.cc000066400000000000000000000044461352176506000210750ustar00rootroot00000000000000/*! * \file cshort_to_float_x2.cc * \brief Adapts a std::complex stream into two float streams * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "cshort_to_float_x2.h" #include #include #include // for max cshort_to_float_x2_sptr make_cshort_to_float_x2() { return cshort_to_float_x2_sptr(new cshort_to_float_x2()); } cshort_to_float_x2::cshort_to_float_x2() : sync_block("cshort_to_float_x2", gr::io_signature::make(1, 1, sizeof(lv_16sc_t)), // lv_8sc_t is a Volk's typedef for std::complex gr::io_signature::make(2, 2, sizeof(float))) { const int alignment_multiple = volk_get_alignment() / sizeof(lv_16sc_t); set_alignment(std::max(1, alignment_multiple)); } int cshort_to_float_x2::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in = reinterpret_cast(input_items[0]); auto *out0 = reinterpret_cast(output_items[0]); auto *out1 = reinterpret_cast(output_items[1]); const float scalar = 1; volk_16ic_s32f_deinterleave_32f_x2(out0, out1, in, scalar, noutput_items); return noutput_items; } src/algorithms/libs/cshort_to_float_x2.h000066400000000000000000000036031352176506000207310ustar00rootroot00000000000000/*! * \file cshort_to_float_x2.h * \brief Adapts a std::complex stream into two float streams * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CSHORT_TO_FLOAT_X2_H_ #define GNSS_SDR_CSHORT_TO_FLOAT_X2_H_ #include #include #include // for gr_vector_const_void_star class cshort_to_float_x2; using cshort_to_float_x2_sptr = boost::shared_ptr; cshort_to_float_x2_sptr make_cshort_to_float_x2(); /*! * \brief This class adapts a std::complex stream * into two 32-bits (float) streams */ class cshort_to_float_x2 : public gr::sync_block { private: friend cshort_to_float_x2_sptr make_cshort_to_float_x2(); cshort_to_float_x2(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/galileo_e1_signal_processing.cc000066400000000000000000000255221352176506000230630ustar00rootroot00000000000000/*! * \file galileo_e1_signal_processing.cc * \brief This library implements various functions for Galileo E1 signals * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e1_signal_processing.h" #include "Galileo_E1.h" #include "gnss_signal_processing.h" #include #include #include void galileo_e1_code_gen_int(gsl::span _dest, const std::array& _Signal, int32_t _prn) { std::string _galileo_signal = _Signal.data(); int32_t prn = _prn - 1; int32_t index = 0; // A simple error check if ((_prn < 1) || (_prn > 50)) { return; } if (_galileo_signal.rfind("1B") != std::string::npos && _galileo_signal.length() >= 2) { for (char i : GALILEO_E1_B_PRIMARY_CODE[prn]) { hex_to_binary_converter(_dest.subspan(index, 4), i); index += 4; } } else if (_galileo_signal.rfind("1C") != std::string::npos && _galileo_signal.length() >= 2) { for (char i : GALILEO_E1_C_PRIMARY_CODE[prn]) { hex_to_binary_converter(_dest.subspan(index, 4), i); index += 4; } } } void galileo_e1_sinboc_11_gen_int(gsl::span _dest, gsl::span _prn) { const uint32_t _length_in = GALILEO_E1_B_CODE_LENGTH_CHIPS; auto _period = static_cast(_dest.size() / _length_in); for (uint32_t i = 0; i < _length_in; i++) { for (uint32_t j = 0; j < (_period / 2); j++) { _dest[i * _period + j] = _prn[i]; } for (uint32_t j = (_period / 2); j < _period; j++) { _dest[i * _period + j] = -_prn[i]; } } } void galileo_e1_sinboc_61_gen_int(gsl::span _dest, gsl::span _prn) { const uint32_t _length_in = GALILEO_E1_B_CODE_LENGTH_CHIPS; auto _period = static_cast(_dest.size() / _length_in); for (uint32_t i = 0; i < _length_in; i++) { for (uint32_t j = 0; j < _period; j += 2) { _dest[i * _period + j] = _prn[i]; } for (uint32_t j = 1; j < _period; j += 2) { _dest[i * _period + j] = -_prn[i]; } } } void galileo_e1_code_gen_sinboc11_float(gsl::span _dest, const std::array& _Signal, uint32_t _prn) { std::string _galileo_signal = _Signal.data(); const auto _codeLength = static_cast(GALILEO_E1_B_CODE_LENGTH_CHIPS); std::array primary_code_E1_chips{}; galileo_e1_code_gen_int(primary_code_E1_chips, _Signal, _prn); // generate Galileo E1 code, 1 sample per chip for (uint32_t i = 0; i < _codeLength; i++) { _dest[2 * i] = static_cast(primary_code_E1_chips[i]); _dest[2 * i + 1] = -_dest[2 * i]; } } void galileo_e1_gen_float(gsl::span _dest, gsl::span _prn, const std::array& _Signal) { std::string _galileo_signal = _Signal.data(); const uint32_t _codeLength = 12 * GALILEO_E1_B_CODE_LENGTH_CHIPS; const float alpha = sqrt(10.0 / 11.0); const float beta = sqrt(1.0 / 11.0); std::array sinboc_11{}; std::array sinboc_61{}; gsl::span sinboc_11_(sinboc_11.data(), _codeLength); gsl::span sinboc_61_(sinboc_61.data(), _codeLength); galileo_e1_sinboc_11_gen_int(sinboc_11_, _prn); // generate sinboc(1,1) 12 samples per chip galileo_e1_sinboc_61_gen_int(sinboc_61_, _prn); // generate sinboc(6,1) 12 samples per chip if (_galileo_signal.rfind("1B") != std::string::npos && _galileo_signal.length() >= 2) { for (uint32_t i = 0; i < _codeLength; i++) { _dest[i] = alpha * static_cast(sinboc_11[i]) + beta * static_cast(sinboc_61[i]); } } else if (_galileo_signal.rfind("1C") != std::string::npos && _galileo_signal.length() >= 2) { for (uint32_t i = 0; i < _codeLength; i++) { _dest[i] = alpha * static_cast(sinboc_11[i]) - beta * static_cast(sinboc_61[i]); } } } void galileo_e1_code_gen_float_sampled(gsl::span _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, bool _secondary_flag) { // This function is based on the GNU software GPS for MATLAB in Kay Borre's book std::string _galileo_signal = _Signal.data(); uint32_t _samplesPerCode; const int32_t _codeFreqBasis = GALILEO_E1_CODE_CHIP_RATE_HZ; // Hz std::vector primary_code_E1_chips(static_cast(GALILEO_E1_B_CODE_LENGTH_CHIPS)); _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(_codeFreqBasis) / GALILEO_E1_B_CODE_LENGTH_CHIPS)); const int32_t _samplesPerChip = (_cboc == true) ? 12 : 2; const uint32_t delay = ((static_cast(GALILEO_E1_B_CODE_LENGTH_CHIPS) - _chip_shift) % static_cast(GALILEO_E1_B_CODE_LENGTH_CHIPS)) * _samplesPerCode / GALILEO_E1_B_CODE_LENGTH_CHIPS; galileo_e1_code_gen_int(primary_code_E1_chips, _Signal, _prn); // generate Galileo E1 code, 1 sample per chip const uint32_t _codeLength = _samplesPerChip * GALILEO_E1_B_CODE_LENGTH_CHIPS; std::unique_ptr _signal_E1{new float[_codeLength]}; gsl::span _signal_E1_span(_signal_E1, _codeLength); if (_cboc == true) { galileo_e1_gen_float(_signal_E1_span, primary_code_E1_chips, _Signal); // generate cboc 12 samples per chip } else { std::vector _signal_E1_int(static_cast(_codeLength)); galileo_e1_sinboc_11_gen_int(_signal_E1_int, primary_code_E1_chips); // generate sinboc(1,1) 2 samples per chip for (uint32_t ii = 0; ii < _codeLength; ++ii) { _signal_E1_span[ii] = static_cast(_signal_E1_int[ii]); } } if (_fs != _samplesPerChip * _codeFreqBasis) { std::unique_ptr _resampled_signal{new float[_samplesPerCode]}; resampler(gsl::span(_signal_E1, _codeLength), gsl::span(_resampled_signal, _samplesPerCode), _samplesPerChip * _codeFreqBasis, _fs); // resamples code to fs _signal_E1 = std::move(_resampled_signal); } uint32_t size_signal_E1 = _codeLength; if (_fs != _samplesPerChip * _codeFreqBasis) { size_signal_E1 = _samplesPerCode; } gsl::span _signal_E1_span_aux(_signal_E1, size_signal_E1); if (_galileo_signal.rfind("1C") != std::string::npos && _galileo_signal.length() >= 2 && _secondary_flag) { std::unique_ptr _signal_E1C_secondary{new float[static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH) * _samplesPerCode]}; gsl::span _signal_E1C_secondary_span(_signal_E1C_secondary, static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH) * _samplesPerCode); for (uint32_t i = 0; i < static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH); i++) { for (unsigned k = 0; k < _samplesPerCode; k++) { _signal_E1C_secondary_span[i * _samplesPerCode + k] = _signal_E1_span_aux[k] * (GALILEO_E1_C_SECONDARY_CODE.at(i) == '0' ? 1.0F : -1.0F); } } _samplesPerCode *= static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH); _signal_E1 = std::move(_signal_E1C_secondary); } if (_galileo_signal.rfind("1C") != std::string::npos && _galileo_signal.length() >= 2 && _secondary_flag) { size_signal_E1 = static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH) * _samplesPerCode; } gsl::span _signal_E1_span_aux2(_signal_E1, size_signal_E1); for (uint32_t i = 0; i < _samplesPerCode; i++) { _dest[(i + delay) % _samplesPerCode] = _signal_E1_span_aux2[i]; } } void galileo_e1_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, bool _secondary_flag) { std::string _galileo_signal = _Signal.data(); const int32_t _codeFreqBasis = GALILEO_E1_CODE_CHIP_RATE_HZ; // Hz auto _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(_codeFreqBasis) / GALILEO_E1_B_CODE_LENGTH_CHIPS)); if (_galileo_signal.rfind("1C") != std::string::npos && _galileo_signal.length() >= 2 && _secondary_flag) { _samplesPerCode *= static_cast(GALILEO_E1_C_SECONDARY_CODE_LENGTH); } std::vector real_code(_samplesPerCode); galileo_e1_code_gen_float_sampled(real_code, _Signal, _cboc, _prn, _fs, _chip_shift, _secondary_flag); for (uint32_t ii = 0; ii < _samplesPerCode; ++ii) { _dest[ii] = std::complex(real_code[ii], 0.0F); } } void galileo_e1_code_gen_float_sampled(gsl::span _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift) { galileo_e1_code_gen_float_sampled(_dest, _Signal, _cboc, _prn, _fs, _chip_shift, false); } void galileo_e1_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift) { galileo_e1_code_gen_complex_sampled(_dest, _Signal, _cboc, _prn, _fs, _chip_shift, false); } src/algorithms/libs/galileo_e1_signal_processing.h000066400000000000000000000055131352176506000227230ustar00rootroot00000000000000/*! * \file galileo_e1_signal_processing.h * \brief This library implements various functions for Galileo E1 signals * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E1_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GALILEO_E1_SIGNAL_PROCESSING_H_ #include #include #include #include /*! * \brief This function generates Galileo E1 code (can select E1B or E1C sinboc). * */ void galileo_e1_code_gen_sinboc11_float(gsl::span _dest, const std::array& _Signal, uint32_t _prn); /*! * \brief This function generates Galileo E1 code (can select E1B or E1C, cboc or sinboc * and the sample frequency _fs). * */ void galileo_e1_code_gen_float_sampled(gsl::span _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, bool _secondary_flag); /*! * \brief This function generates Galileo E1 code (can select E1B or E1C, cboc or sinboc * and the sample frequency _fs). * */ void galileo_e1_code_gen_float_sampled(gsl::span _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift); /*! * \brief This function generates Galileo E1 code (can select E1B or E1C, cboc or sinboc * and the sample frequency _fs). * */ void galileo_e1_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, bool _secondary_flag); /*! * \brief galileo_e1_code_gen_complex_sampled without _secondary_flag for backward compatibility. */ void galileo_e1_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, bool _cboc, uint32_t _prn, int32_t _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_GALILEO_E1_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/galileo_e5_signal_processing.cc000066400000000000000000000143021352176506000230610ustar00rootroot00000000000000/*! * \file galileo_e5_signal_processing.cc * \brief This library implements various functions for Galileo E5 signals such * as replica code generation * \author Marc Sales, 2014. marcsales92(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "galileo_e5_signal_processing.h" #include "Galileo_E5a.h" #include "gnss_signal_processing.h" #include #include void galileo_e5_a_code_gen_complex_primary(gsl::span> _dest, int32_t _prn, const std::array& _Signal) { uint32_t prn = _prn - 1; uint32_t index = 0; std::array a{}; if ((_prn < 1) || (_prn > 50)) { return; } if (_Signal[0] == '5' && _Signal[1] == 'Q') { for (size_t i = 0; i < GALILEO_E5A_Q_PRIMARY_CODE[prn].length() - 1; i++) { hex_to_binary_converter(a, GALILEO_E5A_Q_PRIMARY_CODE[prn].at(i)); _dest[index] = std::complex(0.0, float(a[0])); _dest[index + 1] = std::complex(0.0, float(a[1])); _dest[index + 2] = std::complex(0.0, float(a[2])); _dest[index + 3] = std::complex(0.0, float(a[3])); index = index + 4; } // last 2 bits are filled up zeros hex_to_binary_converter(a, GALILEO_E5A_Q_PRIMARY_CODE[prn].at(GALILEO_E5A_Q_PRIMARY_CODE[prn].length() - 1)); _dest[index] = std::complex(float(0.0), a[0]); _dest[index + 1] = std::complex(float(0.0), a[1]); } else if (_Signal[0] == '5' && _Signal[1] == 'I') { for (size_t i = 0; i < GALILEO_E5A_I_PRIMARY_CODE[prn].length() - 1; i++) { hex_to_binary_converter(a, GALILEO_E5A_I_PRIMARY_CODE[prn].at(i)); _dest[index] = std::complex(float(a[0]), 0.0); _dest[index + 1] = std::complex(float(a[1]), 0.0); _dest[index + 2] = std::complex(float(a[2]), 0.0); _dest[index + 3] = std::complex(float(a[3]), 0.0); index = index + 4; } // last 2 bits are filled up zeros hex_to_binary_converter(a, GALILEO_E5A_I_PRIMARY_CODE[prn].at(GALILEO_E5A_I_PRIMARY_CODE[prn].length() - 1)); _dest[index] = std::complex(float(a[0]), 0.0); _dest[index + 1] = std::complex(float(a[1]), 0.0); } else if (_Signal[0] == '5' && _Signal[1] == 'X') { std::array b{}; for (size_t i = 0; i < GALILEO_E5A_I_PRIMARY_CODE[prn].length() - 1; i++) { hex_to_binary_converter(a, GALILEO_E5A_I_PRIMARY_CODE[prn].at(i)); hex_to_binary_converter(b, GALILEO_E5A_Q_PRIMARY_CODE[prn].at(i)); _dest[index] = std::complex(float(a[0]), float(b[0])); _dest[index + 1] = std::complex(float(a[1]), float(b[1])); _dest[index + 2] = std::complex(float(a[2]), float(b[2])); _dest[index + 3] = std::complex(float(a[3]), float(b[3])); index = index + 4; } // last 2 bits are filled up zeros hex_to_binary_converter(a, GALILEO_E5A_I_PRIMARY_CODE[prn].at(GALILEO_E5A_I_PRIMARY_CODE[prn].length() - 1)); hex_to_binary_converter(b, GALILEO_E5A_Q_PRIMARY_CODE[prn].at(GALILEO_E5A_Q_PRIMARY_CODE[prn].length() - 1)); _dest[index] = std::complex(float(a[0]), float(b[0])); _dest[index + 1] = std::complex(float(a[1]), float(b[1])); } } void galileo_e5_a_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, uint32_t _prn, int32_t _fs, uint32_t _chip_shift) { uint32_t _samplesPerCode; uint32_t delay; const uint32_t _codeLength = GALILEO_E5A_CODE_LENGTH_CHIPS; const int32_t _codeFreqBasis = GALILEO_E5A_CODE_CHIP_RATE_HZ; std::unique_ptr> _code{new std::complex[_codeLength]}; gsl::span> _code_span(_code, _codeLength); galileo_e5_a_code_gen_complex_primary(_code_span, _prn, _Signal); _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(_codeFreqBasis) / static_cast(_codeLength))); delay = ((_codeLength - _chip_shift) % _codeLength) * _samplesPerCode / _codeLength; if (_fs != _codeFreqBasis) { std::unique_ptr> _resampled_signal{new std::complex[_samplesPerCode]}; resampler(_code_span, gsl::span>(_resampled_signal, _samplesPerCode), _codeFreqBasis, _fs); // resamples code to fs _code = std::move(_resampled_signal); } uint32_t size_code = _codeLength; if (_fs != _codeFreqBasis) { size_code = _samplesPerCode; } gsl::span> _code_span_aux(_code, size_code); for (uint32_t i = 0; i < _samplesPerCode; i++) { _dest[(i + delay) % _samplesPerCode] = _code_span_aux[i]; } } src/algorithms/libs/galileo_e5_signal_processing.h000066400000000000000000000041261352176506000227260ustar00rootroot00000000000000/*! * \file galileo_e5_signal_processing.h * \brief This library implements various functions for Galileo E5 signals such * as replica code generation * \author Marc Sales, 2014. marcsales92(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GALILEO_E5_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GALILEO_E5_SIGNAL_PROCESSING_H_ #include #include #include #include /*! * \brief Generates Galileo E5a code at 1 sample/chip * bool _pilot generates E5aQ code if true and E5aI (data signal) if false. */ void galileo_e5_a_code_gen_complex_primary(gsl::span> _dest, int32_t _prn, const std::array& _Signal); /*! * \brief Generates Galileo E5a complex code, shifted to the desired chip and sampled at a frequency fs * bool _pilot generates E5aQ code if true and E5aI (data signal) if false. */ void galileo_e5_a_code_gen_complex_sampled(gsl::span> _dest, const std::array& _Signal, uint32_t _prn, int32_t _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_GALILEO_E5_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/geofunctions.cc000066400000000000000000000642701352176506000177770ustar00rootroot00000000000000/*! * \file geofunctions.cc * \brief A set of coordinate transformations functions and helpers, * some of them migrated from MATLAB, for geographic information systems. * \author Javier Arribas, 2018. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "geofunctions.h" #include #include // for sin, cos, sqrt, abs, pow const double STRP_PI = 3.1415926535898; // Pi as defined in IS-GPS-200E arma::mat Skew_symmetric(const arma::vec &a) { arma::mat A = arma::zeros(3, 3); A << 0.0 << -a(2) << a(1) << arma::endr << a(2) << 0.0 << -a(0) << arma::endr << -a(1) << a(0) << 0 << arma::endr; // {{0, -a(2), a(1)}, // {a(2), 0, -a(0)}, // {-a(1), a(0), 0}}; return A; } double WGS84_g0(double Lat_rad) { const double k = 0.001931853; // normal gravity constant const double e2 = 0.00669438002290; // the square of the first numerical eccentricity const double nge = 9.7803253359; // normal gravity value on the equator (m/sec^2) double b = sin(Lat_rad); // Lat in degrees b = b * b; double g0 = nge * (1 + k * b) / (sqrt(1 - e2 * b)); return g0; } double WGS84_geocentric_radius(double Lat_geodetic_rad) { // WGS84 earth model Geocentric radius (Eq. 2.88) const double WGS84_A = 6378137.0; // Semi-major axis of the Earth, a [m] const double WGS84_IF = 298.257223563; // Inverse flattening of the Earth const double WGS84_F = (1.0 / WGS84_IF); // The flattening of the Earth // double WGS84_B=(WGS84_A*(1-WGS84_F)); // Semi-minor axis of the Earth [m] double WGS84_E = (sqrt(2 * WGS84_F - WGS84_F * WGS84_F)); // Eccentricity of the Earth // transverse radius of curvature double R_E = WGS84_A / sqrt(1 - WGS84_E * WGS84_E * sin(Lat_geodetic_rad) * sin(Lat_geodetic_rad)); // (Eq. 2.66) // geocentric radius at the Earth surface double r_eS = R_E * sqrt(cos(Lat_geodetic_rad) * cos(Lat_geodetic_rad) + (1 - WGS84_E * WGS84_E) * (1 - WGS84_E * WGS84_E) * sin(Lat_geodetic_rad) * sin(Lat_geodetic_rad)); // (Eq. 2.88) return r_eS; } int topocent(double *Az, double *El, double *D, const arma::vec &x, const arma::vec &dx) { double lambda; double phi; double h; const double dtr = STRP_PI / 180.0; const double a = 6378137.0; // semi-major axis of the reference ellipsoid WGS-84 const double finv = 298.257223563; // inverse of flattening of the reference ellipsoid WGS-84 // Transform x into geodetic coordinates togeod(&phi, &lambda, &h, a, finv, x(0), x(1), x(2)); double cl = cos(lambda * dtr); double sl = sin(lambda * dtr); double cb = cos(phi * dtr); double sb = sin(phi * dtr); arma::mat F = {{-sl, -sb * cl, cb * cl}, {cl, -sb * sl, cb * sl}, {0.0, cb, sb}}; arma::vec local_vector; local_vector = arma::htrans(F) * dx; double E = local_vector(0); double N = local_vector(1); double U = local_vector(2); double hor_dis; hor_dis = sqrt(E * E + N * N); if (hor_dis < 1.0E-20) { *Az = 0.0; *El = 90.0; } else { *Az = atan2(E, N) / dtr; *El = atan2(U, hor_dis) / dtr; } if (*Az < 0) { *Az = *Az + 360.0; } *D = sqrt(dx(0) * dx(0) + dx(1) * dx(1) + dx(2) * dx(2)); return 0; } int togeod(double *dphi, double *dlambda, double *h, double a, double finv, double X, double Y, double Z) { *h = 0.0; const double tolsq = 1.e-10; // tolerance to accept convergence const int maxit = 10; // max number of iterations const double rtd = 180.0 / STRP_PI; // compute square of eccentricity double esq; if (finv < 1.0E-20) { esq = 0.0; } else { esq = (2.0 - 1.0 / finv) / finv; } // first guess double P = sqrt(X * X + Y * Y); // P is distance from spin axis // direct calculation of longitude if (P > 1.0E-20) { *dlambda = atan2(Y, X) * rtd; } else { *dlambda = 0.0; } // correct longitude bound if (*dlambda < 0) { *dlambda = *dlambda + 360.0; } double r = sqrt(P * P + Z * Z); // r is distance from origin (0,0,0) double sinphi; if (r > 1.0E-20) { sinphi = Z / r; } else { sinphi = 0.0; } *dphi = asin(sinphi); // initial value of height = distance from origin minus // approximate distance from origin to surface of ellipsoid if (r < 1.0E-20) { *h = 0.0; return 1; } *h = r - a * (1 - sinphi * sinphi / finv); // iterate double cosphi; double N_phi; double dP; double dZ; double oneesq = 1.0 - esq; for (int i = 0; i < maxit; i++) { sinphi = sin(*dphi); cosphi = cos(*dphi); // compute radius of curvature in prime vertical direction N_phi = a / sqrt(1.0 - esq * sinphi * sinphi); // compute residuals in P and Z dP = P - (N_phi + (*h)) * cosphi; dZ = Z - (N_phi * oneesq + (*h)) * sinphi; // update height and latitude *h = *h + (sinphi * dZ + cosphi * dP); *dphi = *dphi + (cosphi * dZ - sinphi * dP) / (N_phi + (*h)); // test for convergence if ((dP * dP + dZ * dZ) < tolsq) { break; } if (i == (maxit - 1)) { // LOG(WARNING) << "The computation of geodetic coordinates did not converge"; } } *dphi = (*dphi) * rtd; return 0; } arma::vec Gravity_ECEF(const arma::vec &r_eb_e) { // Parameters const double R_0 = 6378137.0; // WGS84 Equatorial radius in meters const double mu = 3.986004418E14; // WGS84 Earth gravitational constant (m^3 s^-2) const double J_2 = 1.082627E-3; // WGS84 Earth's second gravitational constant const double omega_ie = 7.292115E-5; // Earth rotation rate (rad/s) // Calculate distance from center of the Earth double mag_r = sqrt(arma::as_scalar(r_eb_e.t() * r_eb_e)); // If the input position is 0,0,0, produce a dummy output arma::vec g = arma::zeros(3, 1); if (mag_r != 0) { // Calculate gravitational acceleration using (2.142) double z_scale = 5 * pow((r_eb_e(2) / mag_r), 2); arma::vec tmp_vec = {(1 - z_scale) * r_eb_e(0), (1 - z_scale) * r_eb_e(1), (3 - z_scale) * r_eb_e(2)}; arma::vec gamma_ = (-mu / pow(mag_r, 3)) * (r_eb_e + 1.5 * J_2 * pow(R_0 / mag_r, 2) * tmp_vec); // Add centripetal acceleration using (2.133) g(0) = gamma_(0) + pow(omega_ie, 2) * r_eb_e(0); g(1) = gamma_(1) + pow(omega_ie, 2) * r_eb_e(1); g(2) = gamma_(2); } return g; } arma::vec LLH_to_deg(const arma::vec &LLH) { const double rtd = 180.0 / STRP_PI; arma::vec deg = arma::zeros(3, 1); deg(0) = LLH(0) * rtd; deg(1) = LLH(1) * rtd; deg(2) = LLH(2); return deg; } double degtorad(double angleInDegrees) { double angleInRadians = (STRP_PI / 180.0) * angleInDegrees; return angleInRadians; } double radtodeg(double angleInRadians) { double angleInDegrees = (180.0 / STRP_PI) * angleInRadians; return angleInDegrees; } double mstoknotsh(double MetersPerSeconds) { double knots = mstokph(MetersPerSeconds) * 0.539957; return knots; } double mstokph(double MetersPerSeconds) { double kph = 3600.0 * MetersPerSeconds / 1e3; return kph; } arma::vec CTM_to_Euler(const arma::mat &C) { // Calculate Euler angles using (2.23) arma::mat CTM = {{C(0, 0), C(0, 1), C(0, 2)}, {C(1, 0), C(1, 1), C(1, 2)}, {C(2, 0), C(2, 1), C(2, 2)}}; arma::vec eul = arma::zeros(3, 1); eul(0) = atan2(CTM(1, 2), CTM(2, 2)); // roll if (CTM(0, 2) < -1.0) { CTM(0, 2) = -1.0; } if (CTM(0, 2) > 1.0) { CTM(0, 2) = 1.0; } eul(1) = -asin(CTM(0, 2)); // pitch eul(2) = atan2(CTM(0, 1), CTM(0, 0)); // yaw return eul; } arma::mat Euler_to_CTM(const arma::vec &eul) { // Eq.2.15 // Euler angles to Attitude matrix is equivalent to rotate the body // in the three axes: // arma::mat Ax= {{1,0,0}, {0,cos(Att_phi),sin(Att_phi)} ,{0,-sin(Att_phi),cos(Att_phi)}}; // arma::mat Ay= {{cos(Att_theta), 0, -sin(Att_theta)}, {0,1,0} , {sin(Att_theta), 0, cos(Att_theta)}}; // arma::mat Az= {{cos(Att_psi), sin(Att_psi), 0}, {-sin(Att_psi), cos(Att_psi), 0},{0,0,1}}; // arma::mat C_b_n=Ax*Ay*Az; // Attitude expressed in the LOCAL FRAME (NED) // C_b_n=C_b_n.t(); // Precalculate sines and cosines of the Euler angles double sin_phi = sin(eul(0)); double cos_phi = cos(eul(0)); double sin_theta = sin(eul(1)); double cos_theta = cos(eul(1)); double sin_psi = sin(eul(2)); double cos_psi = cos(eul(2)); // Calculate coordinate transformation matrix using (2.22) arma::mat C = {{cos_theta * cos_psi, cos_theta * sin_psi, -sin_theta}, {-cos_phi * sin_psi + sin_phi * sin_theta * cos_psi, cos_phi * cos_psi + sin_phi * sin_theta * sin_psi, sin_phi * cos_theta}, {sin_phi * sin_psi + cos_phi * sin_theta * cos_psi, -sin_phi * cos_psi + cos_phi * sin_theta * sin_psi, cos_phi * cos_theta}}; return C; } arma::vec cart2geo(const arma::vec &XYZ, int elipsoid_selection) { const std::array a{6378388.0, 6378160.0, 6378135.0, 6378137.0, 6378137.0}; const std::array f{1.0 / 297.0, 1.0 / 298.247, 1.0 / 298.26, 1.0 / 298.257222101, 1.0 / 298.257223563}; double lambda = atan2(XYZ[1], XYZ[0]); double ex2 = (2.0 - f[elipsoid_selection]) * f[elipsoid_selection] / ((1.0 - f[elipsoid_selection]) * (1.0 - f[elipsoid_selection])); double c = a[elipsoid_selection] * sqrt(1.0 + ex2); double phi = atan(XYZ[2] / ((sqrt(XYZ[0] * XYZ[0] + XYZ[1] * XYZ[1]) * (1.0 - (2.0 - f[elipsoid_selection])) * f[elipsoid_selection]))); double h = 0.1; double oldh = 0.0; double N; int iterations = 0; do { oldh = h; N = c / sqrt(1.0 + ex2 * (cos(phi) * cos(phi))); phi = atan(XYZ[2] / ((sqrt(XYZ[0] * XYZ[0] + XYZ[1] * XYZ[1]) * (1.0 - (2.0 - f[elipsoid_selection]) * f[elipsoid_selection] * N / (N + h))))); h = sqrt(XYZ[0] * XYZ[0] + XYZ[1] * XYZ[1]) / cos(phi) - N; iterations = iterations + 1; if (iterations > 100) { // std::cout << "Failed to approximate h with desired precision. h-oldh= " << h - oldh; break; } } while (std::fabs(h - oldh) > 1.0e-12); arma::vec LLH = {{phi, lambda, h}}; // radians return LLH; } void ECEF_to_Geo(const arma::vec &r_eb_e, const arma::vec &v_eb_e, const arma::mat &C_b_e, arma::vec &LLH, arma::vec &v_eb_n, arma::mat &C_b_n) { // Compute the Latitude of the ECEF position LLH = cart2geo(r_eb_e, 4); // ECEF -> WGS84 geographical // Calculate ECEF to Geographical coordinate transformation matrix using (2.150) double cos_lat = cos(LLH(0)); double sin_lat = sin(LLH(0)); double cos_long = cos(LLH(1)); double sin_long = sin(LLH(1)); // C++11 and arma >= 5.2 // arma::mat C_e_n = {{-sin_lat * cos_long, -sin_lat * sin_long, cos_lat}, // {-sin_long, cos_long, 0}, // {-cos_lat * cos_long, -cos_lat * sin_long, -sin_lat}}; //ECEF to Geo arma::mat C_e_n = arma::zeros(3, 3); C_e_n << -sin_lat * cos_long << -sin_lat * sin_long << cos_lat << arma::endr << -sin_long << cos_long << 0 << arma::endr << -cos_lat * cos_long << -cos_lat * sin_long << -sin_lat << arma::endr; // ECEF to Geo // Transform velocity using (2.73) v_eb_n = C_e_n * v_eb_e; C_b_n = C_e_n * C_b_e; // Attitude conversion from ECEF to NED } void Geo_to_ECEF(const arma::vec &LLH, const arma::vec &v_eb_n, const arma::mat &C_b_n, arma::vec &r_eb_e, arma::vec &v_eb_e, arma::mat &C_b_e) { // Parameters double R_0 = 6378137.0; // WGS84 Equatorial radius in meters double e = 0.0818191908425; // WGS84 eccentricity // Calculate transverse radius of curvature using (2.105) double R_E = R_0 / sqrt(1.0 - (e * sin(LLH(0))) * (e * sin(LLH(0)))); // Convert position using (2.112) double cos_lat = cos(LLH(0)); double sin_lat = sin(LLH(0)); double cos_long = cos(LLH(1)); double sin_long = sin(LLH(1)); r_eb_e = {(R_E + LLH(2)) * cos_lat * cos_long, (R_E + LLH(2)) * cos_lat * sin_long, ((1 - e * e) * R_E + LLH(2)) * sin_lat}; // Calculate ECEF to Geo coordinate transformation matrix using (2.150) // C++11 and arma>=5.2 // arma::mat C_e_n = {{-sin_lat * cos_long, -sin_lat * sin_long, cos_lat}, // {-sin_long, cos_long, 0}, // {-cos_lat * cos_long, -cos_lat * sin_long, -sin_lat}}; arma::mat C_e_n = arma::zeros(3, 3); C_e_n << -sin_lat * cos_long << -sin_lat * sin_long << cos_lat << arma::endr << -sin_long << cos_long << 0 << arma::endr << -cos_lat * cos_long << -cos_lat * sin_long << -sin_lat << arma::endr; // Transform velocity using (2.73) v_eb_e = C_e_n.t() * v_eb_n; // Transform attitude using (2.15) C_b_e = C_e_n.t() * C_b_n; } void pv_Geo_to_ECEF(double L_b, double lambda_b, double h_b, const arma::vec &v_eb_n, arma::vec &r_eb_e, arma::vec &v_eb_e) { // Parameters const double R_0 = 6378137.0; // WGS84 Equatorial radius in meters const double e = 0.0818191908425; // WGS84 eccentricity // Calculate transverse radius of curvature using (2.105) double R_E = R_0 / sqrt(1 - pow(e * sin(L_b), 2)); // Convert position using (2.112) double cos_lat = cos(L_b); double sin_lat = sin(L_b); double cos_long = cos(lambda_b); double sin_long = sin(lambda_b); r_eb_e = {(R_E + h_b) * cos_lat * cos_long, (R_E + h_b) * cos_lat * sin_long, ((1 - pow(e, 2)) * R_E + h_b) * sin_lat}; // Calculate ECEF to Geo coordinate transformation matrix using (2.150) arma::mat C_e_n = arma::zeros(3, 3); C_e_n << -sin_lat * cos_long << -sin_lat * sin_long << cos_lat << arma::endr << -sin_long << cos_long << 0 << arma::endr << -cos_lat * cos_long << -cos_lat * sin_long << -sin_lat << arma::endr; // Transform velocity using (2.73) v_eb_e = C_e_n.t() * v_eb_n; } double great_circle_distance(double lat1, double lon1, double lat2, double lon2) { // The Haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes. // generally used geo measurement function double R = 6378.137; // Radius of earth in KM double dLat = lat2 * STRP_PI / 180.0 - lat1 * STRP_PI / 180.0; double dLon = lon2 * STRP_PI / 180.0 - lon1 * STRP_PI / 180.0; double a = sin(dLat / 2.0) * sin(dLat / 2.0) + cos(lat1 * STRP_PI / 180.0) * cos(lat2 * STRP_PI / 180.0) * sin(dLon / 2) * sin(dLon / 2.0); double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a)); double d = R * c; return d * 1000.0; // meters } void cart2utm(const arma::vec &r_eb_e, int zone, arma::vec &r_enu) { // Transformation of (X,Y,Z) to (E,N,U) in UTM, zone 'zone' // // Inputs: // r_eb_e - Cartesian coordinates. Coordinates are referenced // with respect to the International Terrestrial Reference // Frame 1996 (ITRF96) // zone - UTM zone of the given position // // Outputs: // r_enu - UTM coordinates (Easting, Northing, Uping) // // Originally written in Matlab by Kai Borre, Nov. 1994 // Implemented in C++ by J.Arribas // // This implementation is based upon // O. Andersson & K. Poder (1981) Koordinattransformationer // ved Geod\ae{}tisk Institut. Landinspekt\oe{}ren // Vol. 30: 552--571 and Vol. 31: 76 // // An excellent, general reference (KW) is // R. Koenig & K.H. Weise (1951) Mathematische Grundlagen der // h\"oheren Geod\"asie und Kartographie. // Erster Band, Springer Verlag // // Explanation of variables used: // f flattening of ellipsoid // a semi major axis in m // m0 1 - scale at central meridian; for UTM 0.0004 // Q_n normalized meridian quadrant // E0 Easting of central meridian // L0 Longitude of central meridian // bg constants for ellipsoidal geogr. to spherical geogr. // gb constants for spherical geogr. to ellipsoidal geogr. // gtu constants for ellipsoidal N, E to spherical N, E // utg constants for spherical N, E to ellipoidal N, E // tolutm tolerance for utm, 1.2E-10*meridian quadrant // tolgeo tolerance for geographical, 0.00040 second of arc // // B, L refer to latitude and longitude. Southern latitude is negative // International ellipsoid of 1924, valid for ED50 double a = 6378388.0; double f = 1.0 / 297.0; double ex2 = (2.0 - f) * f / ((1.0 - f) * (1.0 - f)); double c = a * sqrt(1.0 + ex2); arma::vec vec = r_eb_e; vec(2) = vec(2) - 4.5; double alpha = 0.756e-6; arma::mat R = {{1.0, -alpha, 0.0}, {alpha, 1.0, 0.0}, {0.0, 0.0, 1.0}}; arma::vec trans = {89.5, 93.8, 127.6}; double scale = 0.9999988; arma::vec v = scale * R * vec + trans; // coordinate vector in ED50 double L = atan2(v(1), v(0)); double N1 = 6395000.0; // preliminary value double B = atan2(v(2) / ((1.0 - f) * (1.0 - f) * N1), arma::norm(v.subvec(0, 1)) / N1); // preliminary value double U = 0.1; double oldU = 0.0; int iterations = 0; while (fabs(U - oldU) > 1.0E-4) { oldU = U; N1 = c / sqrt(1.0 + ex2 * (cos(B) * cos(B))); B = atan2(v(2) / ((1.0 - f) * (1.0 - f) * N1 + U), arma::norm(v.subvec(0, 1)) / (N1 + U)); U = arma::norm(v.subvec(0, 1)) / cos(B) - N1; iterations = iterations + 1; if (iterations > 100) { std::cout << "Failed to approximate U with desired precision. U-oldU:" << U - oldU << std::endl; break; } } // Normalized meridian quadrant, KW p. 50 (96), p. 19 (38b), p. 5 (21) double m0 = 0.0004; double n = f / (2.0 - f); double m = n * n * (1.0 / 4.0 + n * n / 64.0); double w = (a * (-n - m0 + m * (1.0 - m0))) / (1.0 + n); double Q_n = a + w; // Easting and longitude of central meridian double E0 = 500000.0; double L0 = (zone - 30) * 6.0 - 3.0; // Check tolerance for reverse transformation // double tolutm = STRP_PI / 2.0 * 1.2e-10 * Q_n; // double tolgeo = 0.000040; // Coefficients of trigonometric series // // ellipsoidal to spherical geographical, KW p .186 --187, (51) - (52) // bg[1] = n * (-2 + n * (2 / 3 + n * (4 / 3 + n * (-82 / 45)))); // bg[2] = n ^ 2 * (5 / 3 + n * (-16 / 15 + n * (-13 / 9))); // bg[3] = n ^ 3 * (-26 / 15 + n * 34 / 21); // bg[4] = n ^ 4 * 1237 / 630; // // spherical to ellipsoidal geographical, KW p.190 --191, (61) - (62) % gb[1] = n * (2 + n * (-2 / 3 + n * (-2 + n * 116 / 45))); // gb[2] = n ^ 2 * (7 / 3 + n * (-8 / 5 + n * (-227 / 45))); // gb[3] = n ^ 3 * (56 / 15 + n * (-136 / 35)); // gb[4] = n ^ 4 * 4279 / 630; // // spherical to ellipsoidal N, E, KW p.196, (69) % gtu[1] = n * (1 / 2 + n * (-2 / 3 + n * (5 / 16 + n * 41 / 180))); // gtu[2] = n ^ 2 * (13 / 48 + n * (-3 / 5 + n * 557 / 1440)); // gtu[3] = n ^ 3 * (61 / 240 + n * (-103 / 140)); // gtu[4] = n ^ 4 * 49561 / 161280; // // ellipsoidal to spherical N, E, KW p.194, (65) % utg[1] = n * (-1 / 2 + n * (2 / 3 + n * (-37 / 96 + n * 1 / 360))); // utg[2] = n ^ 2 * (-1 / 48 + n * (-1 / 15 + n * 437 / 1440)); // utg[3] = n ^ 3 * (-17 / 480 + n * 37 / 840); // utg[4] = n ^ 4 * (-4397 / 161280); // // With f = 1 / 297 we get arma::colvec bg = {-3.37077907e-3, 4.73444769e-6, -8.29914570e-9, 1.58785330e-11}; arma::colvec gb = {3.37077588e-3, 6.62769080e-6, 1.78718601e-8, 5.49266312e-11}; arma::colvec gtu = {8.41275991e-4, 7.67306686e-7, 1.21291230e-9, 2.48508228e-12}; arma::colvec utg = {-8.41276339e-4, -5.95619298e-8, -1.69485209e-10, -2.20473896e-13}; // Ellipsoidal latitude, longitude to spherical latitude, longitude bool neg_geo = false; if (B < 0.0) { neg_geo = true; } double Bg_r = fabs(B); double res_clensin = clsin(bg, 4, 2.0 * Bg_r); Bg_r = Bg_r + res_clensin; L0 = L0 * STRP_PI / 180.0; double Lg_r = L - L0; // Spherical latitude, longitude to complementary spherical latitude % i.e.spherical N, E double cos_BN = cos(Bg_r); double Np = atan2(sin(Bg_r), cos(Lg_r) * cos_BN); double Ep = atanh(sin(Lg_r) * cos_BN); // Spherical normalized N, E to ellipsoidal N, E Np = 2.0 * Np; Ep = 2.0 * Ep; double dN; double dE; clksin(gtu, 4, Np, Ep, &dN, &dE); Np = Np / 2.0; Ep = Ep / 2.0; Np = Np + dN; Ep = Ep + dE; double N = Q_n * Np; double E = Q_n * Ep + E0; if (neg_geo) { N = -N + 20000000.0; } r_enu(0) = E; r_enu(1) = N; r_enu(2) = U; } double clsin(const arma::colvec &ar, int degree, double argument) { // Clenshaw summation of sinus of argument. // // result = clsin(ar, degree, argument); // // Originally written in Matlab by Kai Borre // Implemented in C++ by J.Arribas double cos_arg = 2.0 * cos(argument); double hr1 = 0.0; double hr = 0.0; double hr2; for (int t = degree; t > 0; t--) { hr2 = hr1; hr1 = hr; hr = ar(t - 1) + cos_arg * hr1 - hr2; } return (hr * sin(argument)); } void clksin(const arma::colvec &ar, int degree, double arg_real, double arg_imag, double *re, double *im) { // Clenshaw summation of sinus with complex argument // [re, im] = clksin(ar, degree, arg_real, arg_imag); // // Originally written in Matlab by Kai Borre // Implemented in C++ by J.Arribas double sin_arg_r = sin(arg_real); double cos_arg_r = cos(arg_real); double sinh_arg_i = sinh(arg_imag); double cosh_arg_i = cosh(arg_imag); double r = 2.0 * cos_arg_r * cosh_arg_i; double i = -2.0 * sin_arg_r * sinh_arg_i; double hr1 = 0.0; double hr = 0.0; double hi1 = 0.0; double hi = 0.0; double hi2; double hr2; for (int t = degree; t > 0; t--) { hr2 = hr1; hr1 = hr; hi2 = hi1; hi1 = hi; double z = ar(t - 1) + r * hr1 - i * hi - hr2; hi = i * hr1 + r * hi1 - hi2; hr = z; } r = sin_arg_r * cosh_arg_i; i = cos_arg_r * sinh_arg_i; *re = r * hr - i * hi; *im = r * hi + i * hr; } int findUtmZone(double latitude_deg, double longitude_deg) { // Function finds the UTM zone number for given longitude and latitude. // The longitude value must be between -180 (180 degree West) and 180 (180 // degree East) degree. The latitude must be within -80 (80 degree South) and // 84 (84 degree North). // // utmZone = findUtmZone(latitude, longitude); // // Latitude and longitude must be in decimal degrees (e.g. 15.5 degrees not // 15 deg 30 min). // // Originally written in Matlab by Darius Plausinaitis // Implemented in C++ by J.Arribas // Check value bounds if ((longitude_deg > 180.0) || (longitude_deg < -180.0)) { std::cout << "Longitude value exceeds limits (-180:180).\n"; } if ((latitude_deg > 84.0) || (latitude_deg < -80.0)) { std::cout << "Latitude value exceeds limits (-80:84).\n"; } // // Find zone // // Start at 180 deg west = -180 deg int utmZone = floor((180 + longitude_deg) / 6) + 1; // Correct zone numbers for particular areas if (latitude_deg > 72.0) { // Corrections for zones 31 33 35 37 if ((longitude_deg >= 0.0) && (longitude_deg < 9.0)) { utmZone = 31; } else if ((longitude_deg >= 9.0) && (longitude_deg < 21.0)) { utmZone = 33; } else if ((longitude_deg >= 21.0) && (longitude_deg < 33.0)) { utmZone = 35; } else if ((longitude_deg >= 33.0) && (longitude_deg < 42.0)) { utmZone = 37; } } else if ((latitude_deg >= 56.0) && (latitude_deg < 64.0)) { // Correction for zone 32 if ((longitude_deg >= 3.0) && (longitude_deg < 12.0)) { utmZone = 32; } } return utmZone; } src/algorithms/libs/geofunctions.h000066400000000000000000000147771352176506000176500ustar00rootroot00000000000000/*! * \file geofunctions.h * \brief A set of coordinate transformations functions and helpers, * some of them migrated from MATLAB, for geographic information systems. * \author Javier Arribas, 2018. jarribas(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GEOFUNCTIONS_H #define GNSS_SDR_GEOFUNCTIONS_H #if ARMA_NO_BOUND_CHECKING #define ARMA_NO_DEBUG 1 #endif #include arma::mat Skew_symmetric(const arma::vec &a); //!< Calculates skew-symmetric matrix double WGS84_g0(double Lat_rad); double WGS84_geocentric_radius(double Lat_geodetic_rad); /*! * \brief Transformation of vector dx into topocentric coordinate * system with origin at x * Inputs: * x - vector origin coordinates (in ECEF system [X; Y; Z;]) * dx - vector ([dX; dY; dZ;]). * * Outputs: * D - vector length. Units like the input * Az - azimuth from north positive clockwise, degrees * El - elevation angle, degrees * * Based on a Matlab function by Kai Borre */ int topocent(double *Az, double *El, double *D, const arma::vec &x, const arma::vec &dx); /*! * \brief Subroutine to calculate geodetic coordinates latitude, longitude, * height given Cartesian coordinates X,Y,Z, and reference ellipsoid * values semi-major axis (a) and the inverse of flattening (finv). * * The output units of angular quantities will be in decimal degrees * (15.5 degrees not 15 deg 30 min). The output units of h will be the * same as the units of X,Y,Z,a. * * Inputs: * a - semi-major axis of the reference ellipsoid * finv - inverse of flattening of the reference ellipsoid * X,Y,Z - Cartesian coordinates * * Outputs: * dphi - latitude * dlambda - longitude * h - height above reference ellipsoid * * Based in a Matlab function by Kai Borre */ int togeod(double *dphi, double *dlambda, double *h, double a, double finv, double X, double Y, double Z); arma::vec Gravity_ECEF(const arma::vec &r_eb_e); //!< Calculates acceleration due to gravity resolved about ECEF-frame /*! * \brief Conversion of Cartesian coordinates (X,Y,Z) to geographical * coordinates (latitude, longitude, h) on a selected reference ellipsoid. * * Choices of Reference Ellipsoid for Geographical Coordinates * 0. International Ellipsoid 1924 * 1. International Ellipsoid 1967 * 2. World Geodetic System 1972 * 3. Geodetic Reference System 1980 * 4. World Geodetic System 1984 */ arma::vec cart2geo(const arma::vec &XYZ, int elipsoid_selection); arma::vec LLH_to_deg(const arma::vec &LLH); double degtorad(double angleInDegrees); double radtodeg(double angleInRadians); double mstoknotsh(double MetersPerSeconds); double mstokph(double MetersPerSeconds); arma::vec CTM_to_Euler(const arma::mat &C); arma::mat Euler_to_CTM(const arma::vec &eul); void ECEF_to_Geo(const arma::vec &r_eb_e, const arma::vec &v_eb_e, const arma::mat &C_b_e, arma::vec &LLH, arma::vec &v_eb_n, arma::mat &C_b_n); /*! * \brief From Geographic to ECEF coordinates * * Inputs: * LLH latitude (rad), longitude (rad), height (m) * v_eb_n velocity of body frame w.r.t. ECEF frame, resolved along * north, east, and down (m/s) * C_b_n body-to-NED coordinate transformation matrix * * Outputs: * r_eb_e Cartesian position of body frame w.r.t. ECEF frame, resolved * along ECEF-frame axes (m) * v_eb_e velocity of body frame w.r.t. ECEF frame, resolved along * ECEF-frame axes (m/s) * C_b_e body-to-ECEF-frame coordinate transformation matrix * */ void Geo_to_ECEF(const arma::vec &LLH, const arma::vec &v_eb_n, const arma::mat &C_b_n, arma::vec &r_eb_e, arma::vec &v_eb_e, arma::mat &C_b_e); /*! * \brief Converts curvilinear to Cartesian position and velocity * resolving axes from NED to ECEF * This function created 11/4/2012 by Paul Groves * * Inputs: * L_b latitude (rad) * lambda_b longitude (rad) * h_b height (m) * v_eb_n velocity of body frame w.r.t. ECEF frame, resolved along * north, east, and down (m/s) * * Outputs: * r_eb_e Cartesian position of body frame w.r.t. ECEF frame, resolved * along ECEF-frame axes (m) * v_eb_e velocity of body frame w.r.t. ECEF frame, resolved along * ECEF-frame axes (m/s) */ void pv_Geo_to_ECEF(double L_b, double lambda_b, double h_b, const arma::vec &v_eb_n, arma::vec &r_eb_e, arma::vec &v_eb_e); /*! * \brief The Haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes. */ double great_circle_distance(double lat1, double lon1, double lat2, double lon2); /*! * \brief Transformation of ECEF (X,Y,Z) to (E,N,U) in UTM, zone 'zone'. */ void cart2utm(const arma::vec &r_eb_e, int zone, arma::vec &r_enu); /*! * \brief Function finds the UTM zone number for given longitude and latitude. */ int findUtmZone(double latitude_deg, double longitude_deg); /*! * \brief Clenshaw summation of sinus of argument. */ double clsin(const arma::colvec &ar, int degree, double argument); /*! * \brief Clenshaw summation of sinus with complex argument. */ void clksin(const arma::colvec &ar, int degree, double arg_real, double arg_imag, double *re, double *im); #endif src/algorithms/libs/glonass_l1_signal_processing.cc000066400000000000000000000122171352176506000231210ustar00rootroot00000000000000/*! * \file glonass_l1_signal_processing.cc * \brief This class implements various functions for GLONASS L1 CA signals * \author Javier Arribas, 2011. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "glonass_l1_signal_processing.h" #include #include auto auxCeil = [](float x) { return static_cast(static_cast((x) + 1)); }; void glonass_l1_ca_code_gen_complex(gsl::span> _dest, uint32_t _chip_shift) { const uint32_t _code_length = 511; std::bitset<_code_length> G1{}; auto G1_register = std::bitset<9>{}.set(); // All true bool feedback1; bool aux; uint32_t delay; uint32_t lcv, lcv2; /* Generate G1 Register */ for (lcv = 0; lcv < _code_length; lcv++) { G1[lcv] = G1_register[2]; feedback1 = G1_register[4] xor G1_register[0]; for (lcv2 = 0; lcv2 < 8; lcv2++) { G1_register[lcv2] = G1_register[lcv2 + 1]; } G1_register[8] = feedback1; } /* Generate PRN from G1 Register */ for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[lcv]; if (aux == true) { _dest[lcv] = std::complex(1, 0); } else { _dest[lcv] = std::complex(-1, 0); } } /* Set the delay */ delay = _code_length; delay += _chip_shift; delay %= _code_length; /* Generate PRN from G1 and G2 Registers */ for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[(lcv + _chip_shift) % _code_length]; if (aux == true) { _dest[lcv] = std::complex(1, 0); } else { _dest[lcv] = std::complex(-1, 0); } delay++; delay %= _code_length; } } /* * Generates complex GLONASS L1 C/A code for the desired SV ID and sampled to specific sampling frequency */ void glonass_l1_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift) { // This function is based on the GNU software GPS for MATLAB in the Kay Borre book std::array, 511> _code{}; int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; float aux; const int32_t _codeFreqBasis = 511000; // Hz const int32_t _codeLength = 511; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / static_cast(_codeFreqBasis / _codeLength)); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(_codeFreqBasis); // C/A chip period in sec glonass_l1_ca_code_gen_complex(_code, _chip_shift); // generate C/A code 1 sample per chip for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read C/A code values -------------------- // The length of the index array depends on the sampling frequency - // number of samples per millisecond (because one C/A code period is one // millisecond). aux = (_ts * (i + 1)) / _tc; _codeValueIndex = auxCeil(aux) - 1; // --- Make the digitized version of the C/A code ------------------ // The "upsampled" code is made by selecting values form the CA code // chip array (caCode) for the time instances of each sample. if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = _code[_codeLength - 1]; } else { _dest[i] = _code[_codeValueIndex]; // repeat the chip -> upsample } } } src/algorithms/libs/glonass_l1_signal_processing.h000066400000000000000000000040611352176506000227610ustar00rootroot00000000000000/*! * \file glonass_l1_signal_processing.h * \brief This class implements various functions for GLONASS L1 CA signals * \author Gabriel Araujo, 2017. gabriel.araujo(at)ieee.org * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GLONASS_SDR_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GLONASS_SDR_SIGNAL_PROCESSING_H_ #include #include #include //! Generates complex GLONASS L1 C/A code for the desired SV ID and code shift, and sampled to specific sampling frequency void glonass_l1_ca_code_gen_complex(gsl::span> _dest, uint32_t _chip_shift); //! Generates N complex GLONASS L1 C/A codes for the desired SV ID and code shift void glonass_l1_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift, uint32_t _ncodes); //! Generates complex GLONASS L1 C/A code for the desired SV ID and code shift void glonass_l1_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_GLONASS_SDR_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/glonass_l2_signal_processing.cc000066400000000000000000000122271352176506000231230ustar00rootroot00000000000000/*! * \file glonass_l2_signal_processing.cc * \brief This class implements various functions for GLONASS L2 CA signals * \author Damian Miralles, 2018, dmiralles2009(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "glonass_l2_signal_processing.h" #include #include auto auxCeil = [](float x) { return static_cast(static_cast((x) + 1)); }; void glonass_l2_ca_code_gen_complex(gsl::span> _dest, uint32_t _chip_shift) { const uint32_t _code_length = 511; std::bitset<_code_length> G1{}; auto G1_register = std::bitset<9>{}.set(); // All true bool feedback1; bool aux; uint32_t delay; uint32_t lcv, lcv2; /* Generate G1 Register */ for (lcv = 0; lcv < _code_length; lcv++) { G1[lcv] = G1_register[2]; feedback1 = G1_register[4] xor G1_register[0]; for (lcv2 = 0; lcv2 < 8; lcv2++) { G1_register[lcv2] = G1_register[lcv2 + 1]; } G1_register[8] = feedback1; } /* Generate PRN from G1 Register */ for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[lcv]; if (aux == true) { _dest[lcv] = std::complex(1, 0); } else { _dest[lcv] = std::complex(-1, 0); } } /* Set the delay */ delay = _code_length; delay += _chip_shift; delay %= _code_length; /* Generate PRN from G1 and G2 Registers */ for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[(lcv + _chip_shift) % _code_length]; if (aux == true) { _dest[lcv] = std::complex(1, 0); } else { _dest[lcv] = std::complex(-1, 0); } delay++; delay %= _code_length; } } /* * Generates complex GLONASS L2 C/A code for the desired SV ID and sampled to specific sampling frequency */ void glonass_l2_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift) { // This function is based on the GNU software GPS for MATLAB in the Kay Borre book std::array, 511> _code{}; int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; float aux; const int32_t _codeFreqBasis = 511000; // Hz const int32_t _codeLength = 511; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / static_cast(_codeFreqBasis / _codeLength)); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(_codeFreqBasis); // C/A chip period in sec glonass_l2_ca_code_gen_complex(_code, _chip_shift); // generate C/A code 1 sample per chip for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read C/A code values -------------------- // The length of the index array depends on the sampling frequency - // number of samples per millisecond (because one C/A code period is one // millisecond). aux = (_ts * (i + 1)) / _tc; _codeValueIndex = auxCeil(aux) - 1; // --- Make the digitized version of the C/A code ------------------ // The "upsampled" code is made by selecting values form the CA code // chip array (caCode) for the time instances of each sample. if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = _code[_codeLength - 1]; } else { _dest[i] = _code[_codeValueIndex]; // repeat the chip -> upsample } } } src/algorithms/libs/glonass_l2_signal_processing.h000066400000000000000000000040571352176506000227670ustar00rootroot00000000000000/*! * \file glonass_l2_signal_processing.h * \brief This class implements various functions for GLONASS L2 CA signals * \author Damian Miralles, 2018, dmiralles2009(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GLONASS_L2_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GLONASS_L2_SIGNAL_PROCESSING_H_ #include #include #include //! Generates complex GLONASS L2 C/A code for the desired SV ID and code shift, and sampled to specific sampling frequency void glonass_l2_ca_code_gen_complex(gsl::span> _dest, uint32_t _chip_shift); //! Generates N complex GLONASS L2 C/A codes for the desired SV ID and code shift void glonass_l2_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift, uint32_t _ncodes); //! Generates complex GLONASS L2 C/A code for the desired SV ID and code shift void glonass_l2_ca_code_gen_complex_sampled(gsl::span> _dest, int32_t _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_GLONASS_L2_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/gnss_circular_deque.h000066400000000000000000000107541352176506000211550ustar00rootroot00000000000000/*! * \file gnss_circular_deque.h * \brief This class implements a circular deque for Gnss_Synchro * * \author Antonio Ramos, 2018. antonio.ramosdet(at)gmail.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_CIRCULAR_DEQUE_H_ #define GNSS_SDR_CIRCULAR_DEQUE_H_ #include #include template class Gnss_circular_deque { public: Gnss_circular_deque(); //!< Default constructor Gnss_circular_deque(const unsigned int max_size, const unsigned int nchann); //!< nchann = number of channels; max_size = channel capacity unsigned int size(const unsigned int ch); //!< Returns the number of available elements in a channel T& at(const unsigned int ch, const unsigned int pos); //!< Returns a reference to an element T& front(const unsigned int ch); //!< Returns a reference to the first element in the deque T& back(const unsigned int ch); //!< Returns a reference to the last element in the deque void push_back(const unsigned int ch, const T& new_data); //!< Inserts an element at the end of the deque void pop_front(const unsigned int ch); //!< Removes the first element of the deque void clear(const unsigned int ch); //!< Removes all the elements of the deque (Sets size to 0). Capacity is not modified void reset(const unsigned int max_size, const unsigned int nchann); //!< Removes all the elements in all the channels. Re-sets the number of channels and their capacity void reset(); //!< Removes all the channels (Sets nchann to 0) private: std::vector> d_data; }; template Gnss_circular_deque::Gnss_circular_deque() { reset(); } template Gnss_circular_deque::Gnss_circular_deque(const unsigned int max_size, const unsigned int nchann) { reset(max_size, nchann); } template unsigned int Gnss_circular_deque::size(const unsigned int ch) { return d_data.at(ch).size(); } template T& Gnss_circular_deque::back(const unsigned int ch) { return d_data.at(ch).back(); } template T& Gnss_circular_deque::front(const unsigned int ch) { return d_data.at(ch).front(); } template T& Gnss_circular_deque::at(const unsigned int ch, const unsigned int pos) { return d_data.at(ch).at(pos); } template void Gnss_circular_deque::clear(const unsigned int ch) { d_data.at(ch).clear(); } template void Gnss_circular_deque::reset(const unsigned int max_size, const unsigned int nchann) { d_data.clear(); if (max_size > 0 and nchann > 0) { for (unsigned int i = 0; i < nchann; i++) { d_data.push_back(boost::circular_buffer(max_size)); } } } template void Gnss_circular_deque::reset() { d_data.clear(); } template void Gnss_circular_deque::pop_front(const unsigned int ch) { d_data.at(ch).pop_front(); } template void Gnss_circular_deque::push_back(const unsigned int ch, const T& new_data) { d_data.at(ch).push_back(new_data); } #endif /* GNSS_SDR_CIRCULAR_DEQUE_H_ */ src/algorithms/libs/gnss_sdr_create_directory.cc000066400000000000000000000061221352176506000225150ustar00rootroot00000000000000/*! * \file gnss_sdr_create_directory.cc * \brief Create a directory * \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gnss_sdr_create_directory.h" #include // for exception #include // for ofstream #if HAS_STD_FILESYSTEM #include namespace errorlib = std; #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for create_directories, exists #include // for path, operator<< #include // for filesystem #include // for error_code namespace fs = boost::filesystem; namespace errorlib = boost::system; #endif bool gnss_sdr_create_directory(const std::string& foldername) { std::string new_folder; for (auto& folder : fs::path(foldername)) { new_folder += folder.string(); errorlib::error_code ec; if (!fs::exists(new_folder)) { if (!fs::create_directory(new_folder, ec)) { return false; } if (static_cast(ec)) { return false; } } new_folder += fs::path::preferred_separator; } // Check if we have writing permissions std::string test_file = foldername + "/test_file.txt"; std::ofstream os_test_file; os_test_file.open(test_file.c_str(), std::ios::out | std::ios::binary); if (os_test_file.is_open()) { errorlib::error_code ec; os_test_file.close(); if (!fs::remove(test_file, ec)) { return false; } if (static_cast(ec)) { return false; } return true; } return false; } src/algorithms/libs/gnss_sdr_create_directory.h000066400000000000000000000024571352176506000223660ustar00rootroot00000000000000/*! * \file gnss_sdr_create_directory.h * \brief Create a directory * \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GNSS_SDR_CREATE_DIRECTORY_H_ #define GNSS_SDR_GNSS_SDR_CREATE_DIRECTORY_H_ #include bool gnss_sdr_create_directory(const std::string& foldername); #endif src/algorithms/libs/gnss_sdr_flags.cc000066400000000000000000000214411352176506000202630ustar00rootroot00000000000000/*! * \file gnss_sdr_flags.cc * \brief Helper file for gnss-sdr commandline flags * \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gnss_sdr_flags.h" #include #include #include #if HAS_STD_FILESYSTEM #if HAS_STD_FILESYSTEM_EXPERIMENTAL #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else #include // for exists namespace fs = boost::filesystem; #endif DEFINE_string(c, "-", "Path to the configuration file (if set, overrides --config_file)."); DEFINE_string(config_file, std::string(GNSSSDR_INSTALL_DIR "/share/gnss-sdr/conf/default.conf"), "Path to the configuration file."); DEFINE_string(s, "-", "If defined, path to the file containing the signal samples (overrides the configuration file and --signal_source)."); DEFINE_string(signal_source, "-", "If defined, path to the file containing the signal samples (overrides the configuration file)."); DEFINE_int32(doppler_max, 0, "If defined, sets the maximum Doppler value in the search grid, in Hz (overrides the configuration file)."); DEFINE_int32(doppler_step, 0, "If defined, sets the frequency step in the search grid, in Hz (overrides the configuration file)."); DEFINE_int32(cn0_samples, 20, "Number of correlator outputs used for CN0 estimation."); DEFINE_int32(cn0_min, 25, "Minimum valid CN0 (in dB-Hz)."); DEFINE_int32(max_carrier_lock_fail, 5000, "Maximum number of carrier lock failures before dropping a satellite."); DEFINE_int32(max_lock_fail, 50, "Maximum number of code lock failures before dropping a satellite."); //cos(2xError_angle)=0.7 -> Error_angle=22 deg DEFINE_double(carrier_lock_th, 0.7, "Carrier lock threshold (in rad)."); DEFINE_string(RINEX_version, "-", "If defined, specifies the RINEX version (2.11 or 3.02). Overrides the configuration file."); DEFINE_double(dll_bw_hz, 0.0, "If defined, bandwidth of the DLL low pass filter, in Hz (overrides the configuration file)."); DEFINE_double(pll_bw_hz, 0.0, "If defined, bandwidth of the PLL low pass filter, in Hz (overrides the configuration file)."); #if GFLAGS_GREATER_2_0 static bool ValidateC(const char* flagname, const std::string& value) { if (fs::exists(value) or value == "-") { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ". The file '" << value << "' does not exist." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateConfigFile(const char* flagname, const std::string& value) { if (fs::exists(value) or value == std::string(GNSSSDR_INSTALL_DIR "/share/gnss-sdr/conf/default.conf")) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ". The file '" << value << "' does not exist." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateS(const char* flagname, const std::string& value) { if (fs::exists(value) or value == "-") { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ". The file '" << value << "' does not exist." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateSignalSource(const char* flagname, const std::string& value) { if (fs::exists(value) or value == "-") { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ". The file '" << value << "' does not exist." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateDopplerMax(const char* flagname, int32_t value) { const int32_t max_value = 1000000; if (value >= 0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " Hz." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateDopplerStep(const char* flagname, int32_t value) { const int32_t max_value = 10000; if (value >= 0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " Hz." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateCn0Samples(const char* flagname, int32_t value) { const int32_t max_value = 10000; if (value > 0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " samples." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateCn0Min(const char* flagname, int32_t value) { const int32_t max_value = 100; if (value > 0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " dB-Hz." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateMaxLockFail(const char* flagname, int32_t value) { const int32_t max_value = 10000; if (value > 0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " fails." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateCarrierLockTh(const char* flagname, double value) { const double max_value = 1.508; if (value > 0.0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " rad." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidateDllBw(const char* flagname, double value) { const double max_value = 10000.0; if (value >= 0.0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " Hz." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } static bool ValidatePllBw(const char* flagname, double value) { const double max_value = 10000.0; if (value >= 0.0 && value < max_value) { // value is ok return true; } std::cout << "Invalid value for flag -" << flagname << ": " << value << ". Allowed range is 0 < " << flagname << " < " << max_value << " Hz." << std::endl; std::cout << "GNSS-SDR program ended." << std::endl; return false; } DEFINE_validator(c, &ValidateC); DEFINE_validator(config_file, &ValidateConfigFile); DEFINE_validator(s, &ValidateS); DEFINE_validator(signal_source, &ValidateSignalSource); DEFINE_validator(doppler_max, &ValidateDopplerMax); DEFINE_validator(doppler_step, &ValidateDopplerStep); DEFINE_validator(cn0_samples, &ValidateCn0Samples); DEFINE_validator(cn0_min, &ValidateCn0Min); DEFINE_validator(max_lock_fail, &ValidateMaxLockFail); DEFINE_validator(carrier_lock_th, &ValidateCarrierLockTh); DEFINE_validator(dll_bw_hz, &ValidateDllBw); DEFINE_validator(pll_bw_hz, &ValidatePllBw); #endif src/algorithms/libs/gnss_sdr_flags.h000066400000000000000000000055201352176506000201250ustar00rootroot00000000000000/*! * \file gnss_sdr_flags.h * \brief Helper file for gnss-sdr commandline flags * \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_FLAGS_H_ #define GNSS_SDR_FLAGS_H_ #include DECLARE_string(c); //!< Path to the configuration file. DECLARE_string(config_file); //!< Path to the configuration file. DECLARE_string(log_dir); //!< Path to the folder in which logging will be stored. // Declare flags for signal sources DECLARE_string(s); //!< Path to the file containing the signal samples. DECLARE_string(signal_source); //!< Path to the file containing the signal samples. // Declare flags for acquisition blocks DECLARE_int32(doppler_max); //!< If defined, maximum Doppler value in the search grid, in Hz (overrides the configuration file). DECLARE_int32(doppler_step); //!< If defined, sets the frequency step in the search grid, in Hz, in Hz (overrides the configuration file). // Declare flags for tracking blocks DECLARE_int32(cn0_samples); //!< Number of correlator outputs used for CN0 estimation. DECLARE_int32(cn0_min); //!< Minimum valid CN0 (in dB-Hz). DECLARE_int32(max_lock_fail); //!< Maximum number of code lock failures before dropping a satellite. DECLARE_int32(max_carrier_lock_fail); //!< Maximum number of carrier lock failures before dropping a satellite. DECLARE_double(carrier_lock_th); //!< Carrier lock threshold (in rad). DECLARE_double(dll_bw_hz); //!< Bandwidth of the DLL low pass filter, in Hz (overrides the configuration file). DECLARE_double(pll_bw_hz); //!< Bandwidth of the PLL low pass filter, in Hz (overrides the configuration file). // Declare flags for PVT DECLARE_string(RINEX_version); //!< If defined, specifies the RINEX version (2.11 or 3.02). Overrides the configuration file. #endif src/algorithms/libs/gnss_signal_processing.cc000066400000000000000000000142171352176506000220330ustar00rootroot00000000000000/*! * \file gnss_signal_processing.cc * \brief This library gathers a few functions used by the algorithms of gnss-sdr, * regardless of system used * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gnss_signal_processing.h" #include "GPS_L1_CA.h" #include auto auxCeil2 = [](float x) { return static_cast(static_cast((x) + 1)); }; void complex_exp_gen(gsl::span> _dest, double _f, double _fs) { gr::fxpt_nco d_nco; d_nco.set_freq((GPS_TWO_PI * _f) / _fs); d_nco.sincos(_dest.data(), _dest.size(), 1); } void complex_exp_gen_conj(gsl::span> _dest, double _f, double _fs) { gr::fxpt_nco d_nco; d_nco.set_freq(-(GPS_TWO_PI * _f) / _fs); d_nco.sincos(_dest.data(), _dest.size(), 1); } void hex_to_binary_converter(gsl::span _dest, char _from) { switch (_from) { case '0': _dest[0] = 1; _dest[1] = 1; _dest[2] = 1; _dest[3] = 1; break; case '1': _dest[0] = 1; _dest[1] = 1; _dest[2] = 1; _dest[3] = -1; break; case '2': _dest[0] = 1; _dest[1] = 1; _dest[2] = -1; _dest[3] = 1; break; case '3': _dest[0] = 1; _dest[1] = 1; _dest[2] = -1; _dest[3] = -1; break; case '4': _dest[0] = 1; _dest[1] = -1; _dest[2] = 1; _dest[3] = 1; break; case '5': _dest[0] = 1; _dest[1] = -1; _dest[2] = 1; _dest[3] = -1; break; case '6': _dest[0] = 1; _dest[1] = -1; _dest[2] = -1; _dest[3] = 1; break; case '7': _dest[0] = 1; _dest[1] = -1; _dest[2] = -1; _dest[3] = -1; break; case '8': _dest[0] = -1; _dest[1] = 1; _dest[2] = 1; _dest[3] = 1; break; case '9': _dest[0] = -1; _dest[1] = 1; _dest[2] = 1; _dest[3] = -1; break; case 'A': _dest[0] = -1; _dest[1] = 1; _dest[2] = -1; _dest[3] = 1; break; case 'B': _dest[0] = -1; _dest[1] = 1; _dest[2] = -1; _dest[3] = -1; break; case 'C': _dest[0] = -1; _dest[1] = -1; _dest[2] = 1; _dest[3] = 1; break; case 'D': _dest[0] = -1; _dest[1] = -1; _dest[2] = 1; _dest[3] = -1; break; case 'E': _dest[0] = -1; _dest[1] = -1; _dest[2] = -1; _dest[3] = 1; break; case 'F': _dest[0] = -1; _dest[1] = -1; _dest[2] = -1; _dest[3] = -1; break; default: break; } } void resampler(const gsl::span _from, gsl::span _dest, float _fs_in, float _fs_out) { uint32_t _codeValueIndex; float aux; // --- Find time constants ------------------------------------------------- const float _t_in = 1 / _fs_in; // Incoming sampling period in sec const float _t_out = 1 / _fs_out; // Out sampling period in sec for (uint32_t i = 0; i < _dest.size() - 1; i++) { // === Digitizing ================================================== // --- compute index array to read sampled values ------------------ aux = (_t_out * (i + 1)) / _t_in; _codeValueIndex = auxCeil2(aux) - 1; // if repeat the chip -> upsample by nearest neighborhood interpolation _dest[i] = _from[_codeValueIndex]; } // --- Correct the last index (due to number rounding issues) ----------- _dest[_dest.size() - 1] = _from[_from.size() - 1]; } void resampler(gsl::span> _from, gsl::span> _dest, float _fs_in, float _fs_out) { uint32_t _codeValueIndex; float aux; // --- Find time constants ------------------------------------------------- const float _t_in = 1 / _fs_in; // Incoming sampling period in sec const float _t_out = 1 / _fs_out; // Out sampling period in sec for (uint32_t i = 0; i < _dest.size() - 1; i++) { // === Digitizing ================================================== // --- compute index array to read sampled values ------------------ aux = (_t_out * (i + 1)) / _t_in; _codeValueIndex = auxCeil2(aux) - 1; // if repeat the chip -> upsample by nearest neighborhood interpolation _dest[i] = _from[_codeValueIndex]; } // --- Correct the last index (due to number rounding issues) ----------- _dest[_dest.size() - 1] = _from[_from.size() - 1]; } src/algorithms/libs/gnss_signal_processing.h000066400000000000000000000045471352176506000217020ustar00rootroot00000000000000/*! * \file gnss_signal_processing.h * \brief This library gathers a few functions used by the algorithms of gnss-sdr, * regardless of system used * * \author Luis Esteve, 2012. luis(at)epsilon-formacion.com * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GNSS_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GNSS_SIGNAL_PROCESSING_H_ #include #include #include /*! * \brief This function generates a complex exponential in _dest. * */ void complex_exp_gen(gsl::span> _dest, double _f, double _fs); /*! * \brief This function generates a conjugate complex exponential in _dest. * */ void complex_exp_gen_conj(gsl::span> _dest, double _f, double _fs); /*! * \brief This function makes a conversion from hex (the input is a char) * to binary (the output are 4 ints with +1 or -1 values). * */ void hex_to_binary_converter(gsl::span _dest, char _from); /*! * \brief This function resamples a sequence of float values. * */ void resampler(const gsl::span _from, gsl::span _dest, float _fs_in, float _fs_out); /*! * \brief This function resamples a sequence of complex values. * */ void resampler(gsl::span> _from, gsl::span> _dest, float _fs_in, float _fs_out); #endif /* GNSS_SDR_GNSS_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/gps_l2c_signal.cc000066400000000000000000000103741352176506000201560ustar00rootroot00000000000000/*! * \file gps_l2c_signal.cc * \brief This class implements signal generators for the GPS L2C signals * \author Javier Arribas, 2015. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l2c_signal.h" #include "GPS_L2C.h" #include #include #include uint32_t gps_l2c_m_shift(uint32_t x) { return static_cast((x >> 1U) xor ((x & 1U) * 0445112474U)); } void gps_l2c_m_code(gsl::span _dest, uint32_t _prn) { uint32_t x; x = GPS_L2C_M_INIT_REG[_prn - 1]; for (int32_t n = 0; n < GPS_L2_M_CODE_LENGTH_CHIPS; n++) { _dest[n] = static_cast(x & 1U); x = gps_l2c_m_shift(x); } } void gps_l2c_m_code_gen_complex(gsl::span> _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { gps_l2c_m_code(_code, _prn); } for (int32_t i = 0; i < GPS_L2_M_CODE_LENGTH_CHIPS; i++) { _dest[i] = std::complex(1.0 - 2.0 * _code[i], 0.0); } } void gps_l2c_m_code_gen_float(gsl::span _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { gps_l2c_m_code(_code, _prn); } for (int32_t i = 0; i < GPS_L2_M_CODE_LENGTH_CHIPS; i++) { _dest[i] = 1.0 - 2.0 * static_cast(_code[i]); } } /* * Generates complex GPS L2C M code for the desired SV ID and sampled to specific sampling frequency */ void gps_l2c_m_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs) { std::array _code{}; if (_prn > 0 and _prn < 51) { gps_l2c_m_code(_code, _prn); } int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; const int32_t _codeLength = GPS_L2_M_CODE_LENGTH_CHIPS; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(GPS_L2_M_CODE_RATE_HZ) / static_cast(_codeLength))); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(GPS_L2_M_CODE_RATE_HZ); // L2C chip period in sec for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read L2C code values -------------------- _codeValueIndex = std::ceil((_ts * (static_cast(i) + 1)) / _tc) - 1; // --- Make the digitized version of the L2C code ------------------ if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = std::complex(1.0 - 2.0 * _code[_codeLength - 1], 0); } else { _dest[i] = std::complex(1.0 - 2.0 * _code[_codeValueIndex], 0); // repeat the chip -> upsample } } } src/algorithms/libs/gps_l2c_signal.h000066400000000000000000000034261352176506000200200ustar00rootroot00000000000000/*! * \file gps_l2c_signal.h * \brief This class implements signal generators for the GPS L2C signals * \author Javier Arribas, 2015. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L2C_SIGNAL_H_ #define GNSS_SDR_GPS_L2C_SIGNAL_H_ #include #include #include //! Generates complex GPS L2C M code for the desired SV ID void gps_l2c_m_code_gen_complex(gsl::span> _dest, uint32_t _prn); void gps_l2c_m_code_gen_float(gsl::span _dest, uint32_t _prn); //! Generates complex GPS L2C M code for the desired SV ID, and sampled to specific sampling frequency void gps_l2c_m_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs); #endif /* GNSS_GPS_L2C_SIGNAL_H_ */ src/algorithms/libs/gps_l5_signal.cc000066400000000000000000000243541352176506000200210ustar00rootroot00000000000000/*! * \file gps_l5_signal.cc * \brief This class implements signal generators for the GPS L5 signals * \author Javier Arribas, 2017. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_l5_signal.h" #include "GPS_L5.h" #include #include #include std::deque l5i_xa_shift(std::deque xa) // GPS-IS-705E Figure 3-4 pp. 15 { if (xa == std::deque{true, true, true, true, true, true, true, true, true, true, true, false, true}) { return std::deque{true, true, true, true, true, true, true, true, true, true, true, true, true}; } std::deque out(xa.begin(), xa.end() - 1); out.push_front(xa[12] xor xa[11] xor xa[9] xor xa[8]); return out; } std::deque l5q_xa_shift(std::deque xa) { if (xa == std::deque{true, true, true, true, true, true, true, true, true, true, true, false, true}) { return std::deque{true, true, true, true, true, true, true, true, true, true, true, true, true}; } std::deque out(xa.begin(), xa.end() - 1); out.push_front(xa[12] xor xa[11] xor xa[9] xor xa[8]); return out; } std::deque l5i_xb_shift(std::deque xb) // GPS-IS-705E Figure 3-5 pp. 16 { std::deque out(xb.begin(), xb.end() - 1); out.push_front(xb[12] xor xb[11] xor xb[7] xor xb[6] xor xb[5] xor xb[3] xor xb[2] xor xb[0]); return out; } std::deque l5q_xb_shift(std::deque xb) { std::deque out(xb.begin(), xb.end() - 1); out.push_front(xb[12] xor xb[11] xor xb[7] xor xb[6] xor xb[5] xor xb[3] xor xb[2] xor xb[0]); return out; } std::deque make_l5i_xa() { std::deque xa = {true, true, true, true, true, true, true, true, true, true, true, true, true}; std::deque y(GPS_L5I_CODE_LENGTH_CHIPS, false); for (int32_t i = 0; i < GPS_L5I_CODE_LENGTH_CHIPS; i++) { y[i] = xa[12]; xa = l5i_xa_shift(xa); } return y; } std::deque make_l5i_xb() { std::deque xb = {true, true, true, true, true, true, true, true, true, true, true, true, true}; std::deque y(GPS_L5I_CODE_LENGTH_CHIPS, false); for (int32_t i = 0; i < GPS_L5I_CODE_LENGTH_CHIPS; i++) { y[i] = xb[12]; xb = l5i_xb_shift(xb); } return y; } std::deque make_l5q_xa() { std::deque xa = {true, true, true, true, true, true, true, true, true, true, true, true, true}; std::deque y(GPS_L5Q_CODE_LENGTH_CHIPS, false); for (int32_t i = 0; i < GPS_L5Q_CODE_LENGTH_CHIPS; i++) { y[i] = xa[12]; xa = l5q_xa_shift(xa); } return y; } std::deque make_l5q_xb() { std::deque xb = {true, true, true, true, true, true, true, true, true, true, true, true, true}; std::deque y(GPS_L5Q_CODE_LENGTH_CHIPS, false); for (int32_t i = 0; i < GPS_L5Q_CODE_LENGTH_CHIPS; i++) { y[i] = xb[12]; xb = l5q_xb_shift(xb); } return y; } void make_l5i(gsl::span _dest, int32_t prn) { int32_t xb_offset = GPS_L5I_INIT_REG[prn]; std::deque xb = make_l5i_xb(); std::deque xa = make_l5i_xa(); std::deque xb_shift(GPS_L5I_CODE_LENGTH_CHIPS, false); for (int32_t n = 0; n < GPS_L5I_CODE_LENGTH_CHIPS; n++) { xb_shift[n] = xb[(xb_offset + n) % GPS_L5I_CODE_LENGTH_CHIPS]; } for (int32_t n = 0; n < GPS_L5I_CODE_LENGTH_CHIPS; n++) { _dest[n] = xa[n] xor xb_shift[n]; } } void make_l5q(gsl::span _dest, int32_t prn) { int32_t xb_offset = GPS_L5Q_INIT_REG[prn]; std::deque xb = make_l5q_xb(); std::deque xa = make_l5q_xa(); std::deque xb_shift(GPS_L5Q_CODE_LENGTH_CHIPS, false); for (int32_t n = 0; n < GPS_L5Q_CODE_LENGTH_CHIPS; n++) { xb_shift[n] = xb[(xb_offset + n) % GPS_L5Q_CODE_LENGTH_CHIPS]; } for (int32_t n = 0; n < GPS_L5Q_CODE_LENGTH_CHIPS; n++) { _dest[n] = xa[n] xor xb_shift[n]; } } void gps_l5i_code_gen_complex(gsl::span> _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5i(_code, _prn - 1); } for (int32_t i = 0; i < GPS_L5I_CODE_LENGTH_CHIPS; i++) { _dest[i] = std::complex(1.0 - 2.0 * static_cast(_code[i]), 0.0); } } void gps_l5i_code_gen_float(gsl::span _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5i(_code, _prn - 1); } for (int32_t i = 0; i < GPS_L5I_CODE_LENGTH_CHIPS; i++) { _dest[i] = 1.0 - 2.0 * static_cast(_code[i]); } } /* * Generates complex GPS L5i code for the desired SV ID and sampled to specific sampling frequency */ void gps_l5i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5i(_code, _prn - 1); } int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; const int32_t _codeLength = GPS_L5I_CODE_LENGTH_CHIPS; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(GPS_L5I_CODE_RATE_HZ) / static_cast(_codeLength))); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(GPS_L5I_CODE_RATE_HZ); // L5I primary chip period in sec for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read L5 code values --------------------- _codeValueIndex = static_cast(std::ceil(_ts * static_cast(i + 1) / _tc)) - 1; // --- Make the digitized version of the L5I code ------------------ if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = std::complex(1.0 - 2.0 * _code[_codeLength - 1], 0.0); } else { _dest[i] = std::complex(1.0 - 2.0 * _code[_codeValueIndex], 0.0); // repeat the chip -> upsample } } } void gps_l5q_code_gen_complex(gsl::span> _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5q(_code, _prn - 1); } for (int32_t i = 0; i < GPS_L5Q_CODE_LENGTH_CHIPS; i++) { _dest[i] = std::complex(1.0 - 2.0 * static_cast(_code[i]), 0.0); } } void gps_l5q_code_gen_float(gsl::span _dest, uint32_t _prn) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5q(_code, _prn - 1); } for (int32_t i = 0; i < GPS_L5Q_CODE_LENGTH_CHIPS; i++) { _dest[i] = 1.0 - 2.0 * static_cast(_code[i]); } } /* * Generates complex GPS L5Q code for the desired SV ID and sampled to specific sampling frequency */ void gps_l5q_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs) { std::array _code{}; if (_prn > 0 and _prn < 51) { make_l5q(_code, _prn - 1); } int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; const int32_t _codeLength = GPS_L5Q_CODE_LENGTH_CHIPS; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / (static_cast(GPS_L5Q_CODE_RATE_HZ) / static_cast(_codeLength))); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(GPS_L5Q_CODE_RATE_HZ); // L5Q chip period in sec for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read L5 code values --------------------- _codeValueIndex = static_cast(std::ceil(_ts * static_cast(i + 1) / _tc)) - 1; // --- Make the digitized version of the L5Q code ------------------ if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) ----------- _dest[i] = std::complex(1.0 - 2.0 * _code[_codeLength - 1], 0); } else { _dest[i] = std::complex(1.0 - 2.0 * _code[_codeValueIndex], 0); // repeat the chip -> upsample } } } src/algorithms/libs/gps_l5_signal.h000066400000000000000000000044321352176506000176560ustar00rootroot00000000000000/*! * \file gps_l5_signal.h * \brief This class implements signal generators for the GPS L5 signals * \author Javier Arribas, 2017. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_L5_SIGNAL_H_ #define GNSS_SDR_GPS_L5_SIGNAL_H_ #include #include #include //! Generates complex GPS L5I code for the desired SV ID void gps_l5i_code_gen_complex(gsl::span> _dest, uint32_t _prn); //! Generates real GPS L5I code for the desired SV ID void gps_l5i_code_gen_float(gsl::span _dest, uint32_t _prn); //! Generates complex GPS L5Q code for the desired SV ID void gps_l5q_code_gen_complex(gsl::span> _dest, uint32_t _prn); //! Generates real GPS L5Q code for the desired SV ID void gps_l5q_code_gen_float(gsl::span _dest, uint32_t _prn); //! Generates complex GPS L5I code for the desired SV ID, and sampled to specific sampling frequency void gps_l5i_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs); //! Generates complex GPS L5Q code for the desired SV ID, and sampled to specific sampling frequency void gps_l5q_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs); #endif /* GNSS_SDR_GPS_L5_SIGNAL_H_ */ src/algorithms/libs/gps_sdr_signal_processing.cc000066400000000000000000000155561352176506000225310ustar00rootroot00000000000000/*! * \file gps_sdr_signal_processing.cc * \brief This class implements various functions for GPS L1 CA signals * \author Javier Arribas, 2011. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "gps_sdr_signal_processing.h" #include #include auto auxCeil = [](float x) { return static_cast(static_cast((x) + 1)); }; void gps_l1_ca_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 1023; std::bitset<_code_length> G1{}; std::bitset<_code_length> G2{}; std::bitset<10> G1_register{}; std::bitset<10> G2_register{}; bool feedback1, feedback2; bool aux; uint32_t lcv, lcv2; uint32_t delay; int32_t prn_idx; // G2 Delays as defined in GPS-ISD-200D const std::array delays = {5 /*PRN1*/, 6, 7, 8, 17, 18, 139, 140, 141, 251, 252, 254, 255, 256, 257, 258, 469, 470, 471, 472, 473, 474, 509, 512, 513, 514, 515, 516, 859, 860, 861, 862 /*PRN32*/, 145 /*PRN120*/, 175, 52, 21, 237, 235, 886, 657, 634, 762, 355, 1012, 176, 603, 130, 359, 595, 68, 386 /*PRN138*/}; // compute delay array index for given PRN number if (120 <= _prn && _prn <= 138) { prn_idx = _prn - 88; // SBAS PRNs are at array indices 31 to 50 (offset: -120+33-1 =-88) } else { prn_idx = _prn - 1; } // A simple error check if ((prn_idx < 0) || (prn_idx > 51)) { return; } for (lcv = 0; lcv < 10; lcv++) { G1_register[lcv] = true; G2_register[lcv] = true; } // Generate G1 & G2 Register for (lcv = 0; lcv < _code_length; lcv++) { G1[lcv] = G1_register[0]; G2[lcv] = G2_register[0]; feedback1 = G1_register[7] xor G1_register[0]; feedback2 = G2_register[8] xor G2_register[7] xor G2_register[4] xor G2_register[2] xor G2_register[1] xor G2_register[0]; for (lcv2 = 0; lcv2 < 9; lcv2++) { G1_register[lcv2] = G1_register[lcv2 + 1]; G2_register[lcv2] = G2_register[lcv2 + 1]; } G1_register[9] = feedback1; G2_register[9] = feedback2; } // Set the delay delay = _code_length - delays[prn_idx]; delay += _chip_shift; delay %= _code_length; // Generate PRN from G1 and G2 Registers for (lcv = 0; lcv < _code_length; lcv++) { aux = G1[(lcv + _chip_shift) % _code_length] xor G2[delay]; if (aux == true) { _dest[lcv] = 1; } else { _dest[lcv] = -1; } delay++; delay %= _code_length; } } void gps_l1_ca_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 1023; std::array ca_code_int{}; gps_l1_ca_code_gen_int(ca_code_int, _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = static_cast(ca_code_int[ii]); } } void gps_l1_ca_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift) { const uint32_t _code_length = 1023; std::array ca_code_int{}; gps_l1_ca_code_gen_int(ca_code_int, _prn, _chip_shift); for (uint32_t ii = 0; ii < _code_length; ++ii) { _dest[ii] = std::complex(static_cast(ca_code_int[ii]), 0.0F); } } /* * Generates complex GPS L1 C/A code for the desired SV ID and sampled to specific sampling frequency * NOTICE: the number of samples is rounded towards zero (integer truncation) */ void gps_l1_ca_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift) { // This function is based on the GNU software GPS for MATLAB in the Kay Borre book std::array, 1023> _code{}; int32_t _samplesPerCode, _codeValueIndex; float _ts; float _tc; float aux; const int32_t _codeFreqBasis = 1023000; // Hz const int32_t _codeLength = 1023; // --- Find number of samples per spreading code --------------------------- _samplesPerCode = static_cast(static_cast(_fs) / static_cast(_codeFreqBasis / _codeLength)); // --- Find time constants ------------------------------------------------- _ts = 1.0 / static_cast(_fs); // Sampling period in sec _tc = 1.0 / static_cast(_codeFreqBasis); // C/A chip period in sec gps_l1_ca_code_gen_complex(_code, _prn, _chip_shift); // generate C/A code 1 sample per chip for (int32_t i = 0; i < _samplesPerCode; i++) { // === Digitizing ================================================== // --- Make index array to read C/A code values -------------------- // The length of the index array depends on the sampling frequency - // number of samples per millisecond (because one C/A code period is one // millisecond). aux = (_ts * (i + 1)) / _tc; _codeValueIndex = auxCeil(aux) - 1; // --- Make the digitized version of the C/A code ------------------- // The "upsampled" code is made by selecting values form the CA code // chip array (caCode) for the time instances of each sample. if (i == _samplesPerCode - 1) { // --- Correct the last index (due to number rounding issues) _dest[i] = _code[_codeLength - 1]; } else { _dest[i] = _code[_codeValueIndex]; // repeat the chip -> upsample } } } src/algorithms/libs/gps_sdr_signal_processing.h000066400000000000000000000045631352176506000223670ustar00rootroot00000000000000/*! * \file gps_sdr_signal_processing.h * \brief This class implements various functions for GPS L1 CA signals * \author Javier Arribas, 2011. jarribas(at)cttc.es * * Detailed description of the file here if needed. * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_GPS_SDR_SIGNAL_PROCESSING_H_ #define GNSS_SDR_GPS_SDR_SIGNAL_PROCESSING_H_ #include #include #include //! Generates int GPS L1 C/A code for the desired SV ID and code shift void gps_l1_ca_code_gen_int(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates float GPS L1 C/A code for the desired SV ID and code shift void gps_l1_ca_code_gen_float(gsl::span _dest, int32_t _prn, uint32_t _chip_shift); //! Generates complex GPS L1 C/A code for the desired SV ID and code shift, and sampled to specific sampling frequency void gps_l1_ca_code_gen_complex(gsl::span> _dest, int32_t _prn, uint32_t _chip_shift); //! Generates N complex GPS L1 C/A codes for the desired SV ID and code shift void gps_l1_ca_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift, uint32_t _ncodes); //! Generates complex GPS L1 C/A code for the desired SV ID and code shift void gps_l1_ca_code_gen_complex_sampled(gsl::span> _dest, uint32_t _prn, int32_t _fs, uint32_t _chip_shift); #endif /* GNSS_SDR_GPS_SDR_SIGNAL_PROCESSING_H_ */ src/algorithms/libs/gsl/000077500000000000000000000000001352176506000155415ustar00rootroot00000000000000src/algorithms/libs/gsl/include/000077500000000000000000000000001352176506000171645ustar00rootroot00000000000000src/algorithms/libs/gsl/include/gsl/000077500000000000000000000000001352176506000177515ustar00rootroot00000000000000src/algorithms/libs/gsl/include/gsl/gsl000066400000000000000000000016421352176506000204640ustar00rootroot00000000000000// // gsl-lite is based on GSL: Guidelines Support Library. // For more information see https://github.com/martinmoene/gsl-lite // // Copyright (c) 2015 Martin Moene // Copyright (c) 2015 Microsoft Corporation. All rights reserved. // // This code is licensed under the MIT License (MIT). // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // mimic MS include hierarchy #pragma once #ifndef GSL_GSL_H_INCLUDED #define GSL_GSL_H_INCLUDED #include "gsl-lite.hpp" #endif // GSL_GSL_H_INCLUDED src/algorithms/libs/gsl/include/gsl/gsl-lite.hpp000066400000000000000000002543161352176506000222150ustar00rootroot00000000000000// // gsl-lite is based on GSL: Guidelines Support Library. // For more information see https://github.com/martinmoene/gsl-lite // // Copyright (c) 2015-2018 Martin Moene // Copyright (c) 2015-2018 Microsoft Corporation. All rights reserved. // // This code is licensed under the MIT License (MIT). // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #ifndef GSL_GSL_LITE_HPP_INCLUDED #define GSL_GSL_LITE_HPP_INCLUDED #include #include #include #include #include #include #include #include #include #include #define gsl_lite_MAJOR 0 #define gsl_lite_MINOR 34 #define gsl_lite_PATCH 0 #define gsl_lite_VERSION gsl_STRINGIFY(gsl_lite_MAJOR) "." gsl_STRINGIFY(gsl_lite_MINOR) "." gsl_STRINGIFY(gsl_lite_PATCH) // gsl-lite backward compatibility: #ifdef gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR #define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR #pragma message("gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR is deprecated since gsl-lite 0.7.0; replace with gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR, or consider span(with_container, cont).") #endif // M-GSL compatibility: #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #define gsl_CONFIG_CONTRACT_VIOLATION_THROWS 1 #endif #if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) #define gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 #endif #if defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) #define gsl_CONFIG_CONTRACT_LEVEL_OFF 1 #endif // Configuration: Features #ifndef gsl_FEATURE_WITH_CONTAINER_TO_STD #define gsl_FEATURE_WITH_CONTAINER_TO_STD 99 #endif #ifndef gsl_FEATURE_MAKE_SPAN_TO_STD #define gsl_FEATURE_MAKE_SPAN_TO_STD 99 #endif #ifndef gsl_FEATURE_BYTE_SPAN_TO_STD #define gsl_FEATURE_BYTE_SPAN_TO_STD 99 #endif #ifndef gsl_FEATURE_IMPLICIT_MACRO #define gsl_FEATURE_IMPLICIT_MACRO 0 #endif #ifndef gsl_FEATURE_OWNER_MACRO #define gsl_FEATURE_OWNER_MACRO 1 #endif #ifndef gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD #define gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD 0 #endif // Configuration: Other #ifndef gsl_CONFIG_DEPRECATE_TO_LEVEL #define gsl_CONFIG_DEPRECATE_TO_LEVEL 0 #endif #ifndef gsl_CONFIG_SPAN_INDEX_TYPE #define gsl_CONFIG_SPAN_INDEX_TYPE size_t #endif #ifndef gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR #define gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR 0 #endif #ifndef gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF #define gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF 0 #endif #ifndef gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS #define gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS 0 #endif #ifndef gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON #define gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON 1 #endif #ifndef gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR #define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR 0 #endif #if defined(gsl_CONFIG_CONTRACT_LEVEL_ON) #define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x11 #elif defined(gsl_CONFIG_CONTRACT_LEVEL_OFF) #define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x00 #elif defined(gsl_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY) #define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x01 #elif defined(gsl_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY) #define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x10 #else #define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x11 #endif #if 2 <= defined(gsl_CONFIG_CONTRACT_VIOLATION_THROWS) + defined(gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES) + defined(gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER) #error only one of gsl_CONFIG_CONTRACT_VIOLATION_THROWS, gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES and gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER may be defined. #elif defined(gsl_CONFIG_CONTRACT_VIOLATION_THROWS) #define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 1 #define gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER_V 0 #elif defined(gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES) #define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 #define gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER_V 0 #elif defined(gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER) #define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 #define gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER_V 1 #else #define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 #define gsl_CONFIG_CONTRACT_VIOLATION_CALLS_HANDLER_V 0 #endif // C++ language version detection (C++20 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef gsl_CPLUSPLUS #if defined(_MSVC_LANG) && !defined(__clang__) #define gsl_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) #else #define gsl_CPLUSPLUS __cplusplus #endif #endif #define gsl_CPP98_OR_GREATER (gsl_CPLUSPLUS >= 199711L) #define gsl_CPP11_OR_GREATER (gsl_CPLUSPLUS >= 201103L) #define gsl_CPP14_OR_GREATER (gsl_CPLUSPLUS >= 201402L) #define gsl_CPP17_OR_GREATER (gsl_CPLUSPLUS >= 201703L) #define gsl_CPP20_OR_GREATER (gsl_CPLUSPLUS >= 202000L) // C++ language version (represent 98 as 3): #define gsl_CPLUSPLUS_V (gsl_CPLUSPLUS / 100 - (gsl_CPLUSPLUS > 200000 ? 2000 : 1994)) // half-open range [lo..hi): #define gsl_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi)) // Compiler versions: // // MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) // MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) // MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) #if defined(_MSC_VER) && !defined(__clang__) #define gsl_COMPILER_MSVC_VER (_MSC_VER) #define gsl_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) #else #define gsl_COMPILER_MSVC_VER 0 #define gsl_COMPILER_MSVC_VERSION 0 #endif #define gsl_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch)) #if defined(__clang__) #define gsl_COMPILER_CLANG_VERSION gsl_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else #define gsl_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) #define gsl_COMPILER_GNUC_VERSION gsl_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else #define gsl_COMPILER_GNUC_VERSION 0 #endif // Method enabling (C++98, VC120 (VS2013) cannot use __VA_ARGS__) #define gsl_REQUIRES_0(VA) \ template ::type = 0> #define gsl_REQUIRES_T(VA) \ , typename = typename std::enable_if<(VA), gsl::detail::enabler>::type #define gsl_REQUIRES_R(R, VA) \ typename std::enable_if::type #define gsl_REQUIRES_A(VA) \ , typename std::enable_if::type = nullptr // Compiler non-strict aliasing: #if defined(__clang__) || defined(__GNUC__) #define gsl_may_alias __attribute__((__may_alias__)) #else #define gsl_may_alias #endif // Presence of gsl, language and library features: #define gsl_IN_STD(v) (((v) == 98 ? 3 : (v)) >= gsl_CPLUSPLUS_V) #define gsl_DEPRECATE_TO_LEVEL(level) (level <= gsl_CONFIG_DEPRECATE_TO_LEVEL) #define gsl_FEATURE_TO_STD(feature) (gsl_IN_STD(gsl_FEATURE(feature##_TO_STD))) #define gsl_FEATURE(feature) (gsl_FEATURE_##feature) #define gsl_CONFIG(feature) (gsl_CONFIG_##feature) #define gsl_HAVE(feature) (gsl_HAVE_##feature) // Presence of wide character support: #ifdef __DJGPP__ #define gsl_HAVE_WCHAR 0 #else #define gsl_HAVE_WCHAR 1 #endif // Presence of language & library features: #ifdef _HAS_CPP0X #define gsl_HAS_CPP0X _HAS_CPP0X #else #define gsl_HAS_CPP0X 0 #endif #define gsl_CPP11_100 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1600) #define gsl_CPP11_110 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1700) #define gsl_CPP11_120 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1800) #define gsl_CPP11_140 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) #define gsl_CPP14_000 (gsl_CPP14_OR_GREATER) #define gsl_CPP14_120 (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1800) #define gsl_CPP14_140 (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) #define gsl_CPP17_000 (gsl_CPP17_OR_GREATER) #define gsl_CPP17_140 (gsl_CPP17_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) #define gsl_CPP11_140_CPP0X_90 (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VER >= 1500 && gsl_HAS_CPP0X)) #define gsl_CPP11_140_CPP0X_100 (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VER >= 1600 && gsl_HAS_CPP0X)) // Presence of C++11 language features: #define gsl_HAVE_AUTO gsl_CPP11_100 #define gsl_HAVE_NULLPTR gsl_CPP11_100 #define gsl_HAVE_RVALUE_REFERENCE gsl_CPP11_100 #define gsl_HAVE_ENUM_CLASS gsl_CPP11_110 #define gsl_HAVE_ALIAS_TEMPLATE gsl_CPP11_120 #define gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG gsl_CPP11_120 #define gsl_HAVE_EXPLICIT gsl_CPP11_120 #define gsl_HAVE_INITIALIZER_LIST gsl_CPP11_120 #define gsl_HAVE_CONSTEXPR_11 gsl_CPP11_140 #define gsl_HAVE_IS_DEFAULT gsl_CPP11_140 #define gsl_HAVE_IS_DELETE gsl_CPP11_140 #define gsl_HAVE_NOEXCEPT gsl_CPP11_140 #if gsl_CPP11_OR_GREATER // see above #endif // Presence of C++14 language features: #define gsl_HAVE_CONSTEXPR_14 gsl_CPP14_000 #define gsl_HAVE_DECLTYPE_AUTO gsl_CPP14_140 // Presence of C++17 language features: // MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 #define gsl_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE gsl_CPP17_000 #define gsl_HAVE_DEDUCTION_GUIDES (gsl_CPP17_000 && !gsl_BETWEEN(gsl_COMPILER_MSVC_VERSION, 1, 999)) // Presence of C++ library features: #define gsl_HAVE_ADDRESSOF gsl_CPP17_000 #define gsl_HAVE_ARRAY gsl_CPP11_110 #define gsl_HAVE_TYPE_TRAITS gsl_CPP11_110 #define gsl_HAVE_TR1_TYPE_TRAITS gsl_CPP11_110 #define gsl_HAVE_CONTAINER_DATA_METHOD gsl_CPP11_140_CPP0X_90 #define gsl_HAVE_STD_DATA gsl_CPP17_000 #define gsl_HAVE_SIZED_TYPES gsl_CPP11_140 #define gsl_HAVE_MAKE_SHARED gsl_CPP11_140_CPP0X_100 #define gsl_HAVE_SHARED_PTR gsl_CPP11_140_CPP0X_100 #define gsl_HAVE_UNIQUE_PTR gsl_CPP11_140_CPP0X_100 #define gsl_HAVE_MAKE_UNIQUE gsl_CPP14_120 #define gsl_HAVE_UNCAUGHT_EXCEPTIONS gsl_CPP17_140 #define gsl_HAVE_ADD_CONST gsl_HAVE_TYPE_TRAITS #define gsl_HAVE_INTEGRAL_CONSTANT gsl_HAVE_TYPE_TRAITS #define gsl_HAVE_REMOVE_CONST gsl_HAVE_TYPE_TRAITS #define gsl_HAVE_REMOVE_REFERENCE gsl_HAVE_TYPE_TRAITS #define gsl_HAVE_TR1_ADD_CONST gsl_HAVE_TR1_TYPE_TRAITS #define gsl_HAVE_TR1_INTEGRAL_CONSTANT gsl_HAVE_TR1_TYPE_TRAITS #define gsl_HAVE_TR1_REMOVE_CONST gsl_HAVE_TR1_TYPE_TRAITS #define gsl_HAVE_TR1_REMOVE_REFERENCE gsl_HAVE_TR1_TYPE_TRAITS // C++ feature usage: #if gsl_HAVE(ADDRESSOF) #define gsl_ADDRESSOF(x) std::addressof(x) #else #define gsl_ADDRESSOF(x) (&x) #endif #if gsl_HAVE(CONSTEXPR_11) #define gsl_constexpr constexpr #else #define gsl_constexpr /*constexpr*/ #endif #if gsl_HAVE(CONSTEXPR_14) #define gsl_constexpr14 constexpr #else #define gsl_constexpr14 /*constexpr*/ #endif #if gsl_HAVE(EXPLICIT) #define gsl_explicit explicit #else #define gsl_explicit /*explicit*/ #endif #if gsl_FEATURE(IMPLICIT_MACRO) #define implicit /*implicit*/ #endif #if gsl_HAVE(IS_DELETE) #define gsl_is_delete = delete #else #define gsl_is_delete #endif #if gsl_HAVE(IS_DELETE) #define gsl_is_delete_access public #else #define gsl_is_delete_access private #endif #if !gsl_HAVE(NOEXCEPT) || gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) #define gsl_noexcept /*noexcept*/ #else #define gsl_noexcept noexcept #endif #if gsl_HAVE(NULLPTR) #define gsl_nullptr nullptr #else #define gsl_nullptr NULL #endif #define gsl_DIMENSION_OF(a) (sizeof(a) / sizeof(0 [a])) // Other features: #define gsl_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR \ (gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG && gsl_HAVE_CONTAINER_DATA_METHOD) // Note: !defined(__NVCC__) doesn't work with nvcc here: #define gsl_HAVE_UNCONSTRAINED_SPAN_CONTAINER_CTOR \ (gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR && (__NVCC__ == 0)) // GSL API (e.g. for CUDA platform): #ifndef gsl_api #ifdef __CUDACC__ #define gsl_api __host__ __device__ #else #define gsl_api /*gsl_api*/ #endif #endif // Additional includes: #if gsl_HAVE(ARRAY) #include #endif #if gsl_HAVE(INITIALIZER_LIST) #include #endif #if gsl_HAVE(TYPE_TRAITS) #include #elif gsl_HAVE(TR1_TYPE_TRAITS) #include #endif #if gsl_HAVE(SIZED_TYPES) #include #endif // MSVC warning suppression macros: #if gsl_COMPILER_MSVC_VERSION >= 140 #define gsl_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] #define gsl_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress \ : code)) #define gsl_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable \ : codes)) #define gsl_RESTORE_MSVC_WARNINGS() __pragma(warning(pop)) #else #define gsl_SUPPRESS_MSGSL_WARNING(expr) #define gsl_SUPPRESS_MSVC_WARNING(code, descr) #define gsl_DISABLE_MSVC_WARNINGS(codes) #define gsl_RESTORE_MSVC_WARNINGS() #endif // Suppress the following MSVC GSL warnings: // - C26410: gsl::r.32: the parameter 'ptr' is a reference to const unique pointer, use const T* or const T& instead // - C26415: gsl::r.30: smart pointer parameter 'ptr' is used only to access contained pointer. Use T* or T& instead // - C26418: gsl::r.36: shared pointer parameter 'ptr' is not copied or moved. Use T* or T& instead // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; // use brace initialization, gsl::narrow_cast or gsl::narow // - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' // - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' // - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead // - C26482, gsl::b.2 : only index into arrays using constant expressions // - C26490: gsl::t.1 : don't use reinterpret_cast gsl_DISABLE_MSVC_WARNINGS(26410 26415 26418 26472 26439 26440 26473 26481 26482 26490) namespace gsl { // forward declare span<>: template class span; // C++11 emulation: namespace std11 { #if gsl_HAVE(ADD_CONST) using std::add_const; #elif gsl_HAVE(TR1_ADD_CONST) using std::tr1::add_const; #else template struct add_const { typedef const T type; }; #endif // gsl_HAVE( ADD_CONST ) #if gsl_HAVE(REMOVE_CONST) using std::remove_const; using std::remove_cv; using std::remove_volatile; #elif gsl_HAVE(TR1_REMOVE_CONST) using std::tr1::remove_const; using std::tr1::remove_cv; using std::tr1::remove_volatile; #else template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; template struct remove_volatile { typedef T type; }; template struct remove_volatile { typedef T type; }; template struct remove_cv { typedef typename remove_volatile::type>::type type; }; #endif // gsl_HAVE( REMOVE_CONST ) #if gsl_HAVE(INTEGRAL_CONSTANT) using std::false_type; using std::integral_constant; using std::true_type; #elif gsl_HAVE(TR1_INTEGRAL_CONSTANT) using std::tr1::false_type; using std::tr1::integral_constant; using std::tr1::true_type; #else template struct integral_constant { enum { value = v }; }; typedef integral_constant true_type; typedef integral_constant false_type; #endif } // namespace std11 // C++17 emulation: namespace std17 { template struct bool_constant : std11::integral_constant { }; #if gsl_CPP11_120 template using void_t = void; #endif #if gsl_HAVE(STD_DATA) using std::data; using std::size; #elif gsl_HAVE(CONSTRAINED_SPAN_CONTAINER_CTOR) template inline gsl_constexpr auto size(const T (&)[N]) gsl_noexcept -> size_t { return N; } template inline gsl_constexpr auto size(C const &cont) -> decltype(cont.size()) { return cont.size(); } template inline gsl_constexpr auto data(T (&arr)[N]) gsl_noexcept -> T * { return &arr[0]; } template inline gsl_constexpr auto data(C &cont) -> decltype(cont.data()) { return cont.data(); } template inline gsl_constexpr auto data(C const &cont) -> decltype(cont.data()) { return cont.data(); } template inline gsl_constexpr auto data(std::initializer_list il) gsl_noexcept -> E const * { return il.begin(); } #endif // span_HAVE( DATA ) } // namespace std17 namespace detail { /// for nsel_REQUIRES_T /*enum*/ class enabler { }; #if gsl_HAVE(TYPE_TRAITS) template struct is_span_oracle : std::false_type { }; template struct is_span_oracle > : std::true_type { }; template struct is_span : is_span_oracle::type> { }; template struct is_std_array_oracle : std::false_type { }; #if gsl_HAVE(ARRAY) template struct is_std_array_oracle > : std::true_type { }; #endif template struct is_std_array : is_std_array_oracle::type> { }; template struct is_array : std::false_type { }; template struct is_array : std::true_type { }; template struct is_array : std::true_type { }; #if gsl_CPP11_140 && !gsl_BETWEEN(gsl_COMPILER_GNUC_VERSION, 1, 500) template struct has_size_and_data : std::false_type { }; template struct has_size_and_data< C, std17::void_t< decltype(std17::size(std::declval())), decltype(std17::data(std::declval()))> > : std::true_type { }; template struct is_compatible_element : std::false_type { }; template struct is_compatible_element< C, E, std17::void_t()))> > : std::is_convertible()))>::type (*)[], E (*)[]> { }; template struct is_container : std17::bool_constant< !is_span::value && !is_array::value && !is_std_array::value && has_size_and_data::value> { }; template struct is_compatible_container : std17::bool_constant< is_container::value && is_compatible_element::value> { }; #else // gsl_CPP11_140 template < class C, class E gsl_REQUIRES_T((!is_span::value && !is_array::value && !is_std_array::value && (std::is_convertible()))>::type (*)[], E (*)[]>::value) // && has_size_and_data< C >::value )), class = decltype(std17::size(std::declval())), class = decltype(std17::data(std::declval()))> struct is_compatible_container : std::true_type { }; #endif // gsl_CPP11_140 #endif // gsl_HAVE( TYPE_TRAITS ) } // namespace detail // // GSL.util: utilities // // index type for all container indexes/subscripts/sizes typedef gsl_CONFIG_SPAN_INDEX_TYPE index; // p0122r3 uses std::ptrdiff_t // // GSL.owner: ownership pointers // #if gsl_HAVE(SHARED_PTR) using std::make_shared; using std::shared_ptr; using std::unique_ptr; #if gsl_HAVE(MAKE_UNIQUE) using std::make_unique; #endif #endif #if gsl_HAVE(ALIAS_TEMPLATE) #if gsl_HAVE(TYPE_TRAITS) template ::value)> using owner = T; #else template using owner = T; #endif #else template struct owner { typedef T type; }; #endif #define gsl_HAVE_OWNER_TEMPLATE gsl_HAVE_ALIAS_TEMPLATE #if gsl_FEATURE(OWNER_MACRO) #if gsl_HAVE(OWNER_TEMPLATE) #define Owner(t) ::gsl::owner #else #define Owner(t) ::gsl::owner::type #endif #endif // // GSL.assert: assertions // #define gsl_ELIDE_CONTRACT_EXPECTS (0 == (gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x01)) #define gsl_ELIDE_CONTRACT_ENSURES (0 == (gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x10)) #if gsl_ELIDE_CONTRACT_EXPECTS #define Expects(x) /* Expects elided */ #elif gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) #define Expects(x) ::gsl::fail_fast_assert((x), "GSL: Precondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__)) #elif gsl_CONFIG(CONTRACT_VIOLATION_CALLS_HANDLER_V) #define Expects(x) ::gsl::fail_fast_assert((x), #x, "GSL: Precondition failure", __FILE__, __LINE__) #else #define Expects(x) ::gsl::fail_fast_assert((x)) #endif #if gsl_ELIDE_CONTRACT_EXPECTS #define gsl_EXPECTS_UNUSED_PARAM(x) /* Make param unnamed if Expects elided */ #else #define gsl_EXPECTS_UNUSED_PARAM(x) x #endif #if gsl_ELIDE_CONTRACT_ENSURES #define Ensures(x) /* Ensures elided */ #elif gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) #define Ensures(x) ::gsl::fail_fast_assert((x), "GSL: Postcondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__)) #elif gsl_CONFIG(CONTRACT_VIOLATION_CALLS_HANDLER_V) #define Ensures(x) ::gsl::fail_fast_assert((x), #x, "GSL: Postcondition failure", __FILE__, __LINE__) #else #define Ensures(x) ::gsl::fail_fast_assert((x)) #endif #define gsl_STRINGIFY(x) gsl_STRINGIFY_(x) #define gsl_STRINGIFY_(x) #x struct fail_fast : public std::logic_error { gsl_api explicit fail_fast(char const *const message) : std::logic_error(message) {} }; #if gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) gsl_api inline void fail_fast_assert(bool cond, char const *const message) { if (!cond) throw fail_fast(message); } #elif gsl_CONFIG(CONTRACT_VIOLATION_CALLS_HANDLER_V) // Should be defined by user gsl_api void fail_fast_assert_handler(char const *const expression, char const *const message, char const *const file, int line); gsl_api inline void fail_fast_assert(bool cond, char const *const expression, char const *const message, char const *const file, int line) { if (!cond) fail_fast_assert_handler(expression, message, file, line); } #else gsl_api inline void fail_fast_assert(bool cond) gsl_noexcept { #ifdef __CUDA_ARCH__ assert(cond); #else // __CUDA_ARCH__ if (!cond) std::terminate(); #endif // __CUDA_ARCH__ } #endif // // GSL.util: utilities // #if gsl_FEATURE(EXPERIMENTAL_RETURN_GUARD) // Add uncaught_exceptions for pre-2017 MSVC, GCC and Clang // Return unsigned char to save stack space, uncaught_exceptions can only increase by 1 in a scope namespace detail { inline unsigned char to_uchar(unsigned x) gsl_noexcept { return static_cast(x); } } // namespace detail namespace std11 { #if gsl_HAVE(UNCAUGHT_EXCEPTIONS) inline unsigned char uncaught_exceptions() gsl_noexcept { return detail::to_uchar(std::uncaught_exceptions()); } #elif gsl_COMPILER_MSVC_VERSION extern "C" char *__cdecl _getptd(); inline unsigned char uncaught_exceptions() gsl_noexcept { return detail::to_uchar(*reinterpret_cast(_getptd() + (sizeof(void *) == 8 ? 0x100 : 0x90))); } #elif gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_GNUC_VERSION extern "C" char *__cxa_get_globals(); inline unsigned char uncaught_exceptions() gsl_noexcept { return detail::to_uchar(*reinterpret_cast(__cxa_get_globals() + sizeof(void *))); } #endif } // namespace std11 #endif #if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110 template class final_action { public: gsl_api explicit final_action(F action) gsl_noexcept : action_(std::move(action)), invoke_(true) { } gsl_api final_action(final_action &&other) gsl_noexcept : action_(std::move(other.action_)), invoke_(other.invoke_) { other.invoke_ = false; } gsl_api virtual ~final_action() gsl_noexcept { if (invoke_) action_(); } gsl_is_delete_access : gsl_api final_action(final_action const &) gsl_is_delete; gsl_api final_action &operator=(final_action const &) gsl_is_delete; gsl_api final_action &operator=(final_action &&) gsl_is_delete; protected: gsl_api void dismiss() gsl_noexcept { invoke_ = false; } private: F action_; bool invoke_; }; template gsl_api inline final_action finally(F const &action) gsl_noexcept { return final_action(action); } template gsl_api inline final_action finally(F && action) gsl_noexcept { return final_action(std::forward(action)); } #if gsl_FEATURE(EXPERIMENTAL_RETURN_GUARD) template class final_action_return : public final_action { public: gsl_api explicit final_action_return(F &&action) gsl_noexcept : final_action(std::move(action)), exception_count(std11::uncaught_exceptions()) { } gsl_api final_action_return(final_action_return &&other) gsl_noexcept : final_action(std::move(other)), exception_count(std11::uncaught_exceptions()) { } gsl_api ~final_action_return() override { if (std11::uncaught_exceptions() != exception_count) this->dismiss(); } gsl_is_delete_access : gsl_api final_action_return(final_action_return const &) gsl_is_delete; gsl_api final_action_return &operator=(final_action_return const &) gsl_is_delete; private: unsigned char exception_count; }; template gsl_api inline final_action_return on_return(F const &action) gsl_noexcept { return final_action_return(action); } template gsl_api inline final_action_return on_return(F && action) gsl_noexcept { return final_action_return(std::forward(action)); } template class final_action_error : public final_action { public: gsl_api explicit final_action_error(F &&action) gsl_noexcept : final_action(std::move(action)), exception_count(std11::uncaught_exceptions()) { } gsl_api final_action_error(final_action_error &&other) gsl_noexcept : final_action(std::move(other)), exception_count(std11::uncaught_exceptions()) { } gsl_api ~final_action_error() override { if (std11::uncaught_exceptions() == exception_count) this->dismiss(); } gsl_is_delete_access : gsl_api final_action_error(final_action_error const &) gsl_is_delete; gsl_api final_action_error &operator=(final_action_error const &) gsl_is_delete; private: unsigned char exception_count; }; template gsl_api inline final_action_error on_error(F const &action) gsl_noexcept { return final_action_error(action); } template gsl_api inline final_action_error on_error(F && action) gsl_noexcept { return final_action_error(std::forward(action)); } #endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) #else // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110 class final_action { public: typedef void (*Action)(); gsl_api final_action(Action action) : action_(action), invoke_(true) { } gsl_api final_action(final_action const &other) : action_(other.action_), invoke_(other.invoke_) { other.invoke_ = false; } gsl_api virtual ~final_action() { if (invoke_) action_(); } protected: gsl_api void dismiss() { invoke_ = false; } private: gsl_api final_action &operator=(final_action const &); private: Action action_; mutable bool invoke_; }; template gsl_api inline final_action finally(F const &f) { return final_action((f)); } #if gsl_FEATURE(EXPERIMENTAL_RETURN_GUARD) class final_action_return : public final_action { public: gsl_api explicit final_action_return(Action action) : final_action(action), exception_count(std11::uncaught_exceptions()) { } gsl_api ~final_action_return() { if (std11::uncaught_exceptions() != exception_count) this->dismiss(); } private: gsl_api final_action_return &operator=(final_action_return const &); private: unsigned char exception_count; }; template gsl_api inline final_action_return on_return(F const &action) { return final_action_return(action); } class final_action_error : public final_action { public: gsl_api explicit final_action_error(Action action) : final_action(action), exception_count(std11::uncaught_exceptions()) { } gsl_api ~final_action_error() { if (std11::uncaught_exceptions() == exception_count) this->dismiss(); } private: gsl_api final_action_error &operator=(final_action_error const &); private: unsigned char exception_count; }; template gsl_api inline final_action_error on_error(F const &action) { return final_action_error(action); } #endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) #endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION == 110 #if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 template gsl_api inline gsl_constexpr T narrow_cast(U && u) gsl_noexcept { return static_cast(std::forward(u)); } #else template gsl_api inline T narrow_cast(U u) gsl_noexcept { return static_cast(u); } #endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 struct narrowing_error : public std::exception { }; #if gsl_HAVE(TYPE_TRAITS) namespace detail { template struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> { }; } // namespace detail #endif template gsl_api inline T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) { #if gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) throw narrowing_error(); #else std::terminate(); #endif } #if gsl_HAVE(TYPE_TRAITS) #if gsl_COMPILER_MSVC_VERSION // Suppress MSVC level 4 warning C4127 (conditional expression is constant) if (0, !detail::is_same_signedness::value && ((t < T()) != (u < U()))) #else if (!detail::is_same_signedness::value && ((t < T()) != (u < U()))) #endif #else // Don't assume T() works: if ((t < 0) != (u < 0)) #endif { #if gsl_CONFIG(CONTRACT_VIOLATION_THROWS_V) throw narrowing_error(); #else std::terminate(); #endif } return t; } // // at() - Bounds-checked way of accessing static arrays, std::array, std::vector. // template gsl_api inline gsl_constexpr14 T &at(T(&arr)[N], size_t pos) { Expects(pos < N); return arr[pos]; } template gsl_api inline gsl_constexpr14 typename Container::value_type &at(Container & cont, size_t pos) { Expects(pos < cont.size()); return cont[pos]; } template gsl_api inline gsl_constexpr14 typename Container::value_type const &at(Container const &cont, size_t pos) { Expects(pos < cont.size()); return cont[pos]; } #if gsl_HAVE(INITIALIZER_LIST) template gsl_api inline const gsl_constexpr14 T at(std::initializer_list cont, size_t pos) { Expects(pos < cont.size()); return *(cont.begin() + pos); } #endif template gsl_api inline gsl_constexpr T &at(span s, size_t pos) { return s.at(pos); } // // GSL.views: views // // // not_null<> - Wrap any indirection and enforce non-null. // template class not_null { #if gsl_CONFIG(NOT_NULL_EXPLICIT_CTOR) #define gsl_not_null_explicit explicit #else #define gsl_not_null_explicit /*explicit*/ #endif #if gsl_CONFIG(NOT_NULL_GET_BY_CONST_REF) typedef T const &get_result_t; #else typedef T get_result_t; #endif public: #if gsl_HAVE(TYPE_TRAITS) static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); #endif template ::value)) #endif > gsl_api gsl_constexpr14 gsl_not_null_explicit #if gsl_HAVE(RVALUE_REFERENCE) not_null(U &&u) : ptr_(std::forward(u)) #else not_null(U const &u) : ptr_(u) #endif { Expects(ptr_ != gsl_nullptr); } #undef gsl_not_null_explicit #if gsl_HAVE(IS_DEFAULT) gsl_api ~not_null() = default; gsl_api gsl_constexpr not_null(not_null &&other) = default; gsl_api gsl_constexpr not_null(not_null const &other) = default; gsl_api not_null &operator=(not_null &&other) = default; gsl_api not_null &operator=(not_null const &other) = default; #else gsl_api ~not_null(){}; gsl_api gsl_constexpr not_null(not_null const &other) : ptr_(other.ptr_) {} gsl_api not_null &operator=(not_null const &other) { ptr_ = other.ptr_; return *this; } #if gsl_HAVE(RVALUE_REFERENCE) gsl_api gsl_constexpr not_null(not_null &&other) : ptr_(std::move(other.get())) { } gsl_api not_null &operator=(not_null &&other) { ptr_ = std::move(other.get()); return *this; } #endif #endif template ::value)) #endif > gsl_api gsl_constexpr not_null(not_null const &other) : ptr_(other.get()) { } gsl_api gsl_constexpr14 get_result_t get() const { // Without cheating and changing ptr_ from the outside, this check is superfluous: Ensures(ptr_ != gsl_nullptr); return ptr_; } gsl_api gsl_constexpr operator get_result_t() const { return get(); } gsl_api gsl_constexpr get_result_t operator->() const { return get(); } #if gsl_HAVE(DECLTYPE_AUTO) gsl_api gsl_constexpr decltype(auto) operator*() const { return *get(); } #endif gsl_is_delete_access : // prevent compilation when initialized with a nullptr or literal 0: #if gsl_HAVE(NULLPTR) gsl_api not_null(std::nullptr_t) gsl_is_delete; gsl_api not_null &operator=(std::nullptr_t) gsl_is_delete; #else gsl_api not_null(int) gsl_is_delete; gsl_api not_null &operator=(int) gsl_is_delete; #endif // unwanted operators...pointers only point to single objects! gsl_api not_null &operator++() gsl_is_delete; gsl_api not_null &operator--() gsl_is_delete; gsl_api not_null operator++(int) gsl_is_delete; gsl_api not_null operator--(int) gsl_is_delete; gsl_api not_null &operator+(size_t) gsl_is_delete; gsl_api not_null &operator+=(size_t) gsl_is_delete; gsl_api not_null &operator-(size_t) gsl_is_delete; gsl_api not_null &operator-=(size_t) gsl_is_delete; gsl_api not_null &operator+=(std::ptrdiff_t) gsl_is_delete; gsl_api not_null &operator-=(std::ptrdiff_t) gsl_is_delete; gsl_api void operator[](std::ptrdiff_t) const gsl_is_delete; private: T ptr_; }; // not_null with implicit constructor, allowing copy-initialization: template class not_null_ic : public not_null { public: template ::value)) #endif > gsl_api gsl_constexpr14 #if gsl_HAVE(RVALUE_REFERENCE) not_null_ic(U &&u) : not_null(std::forward(u)) #else not_null_ic(U const &u) : not_null(u) #endif { } }; // more not_null unwanted operators template std::ptrdiff_t operator-(not_null const &, not_null const &) gsl_is_delete; template not_null operator-(not_null const &, std::ptrdiff_t) gsl_is_delete; template not_null operator+(not_null const &, std::ptrdiff_t) gsl_is_delete; template not_null operator+(std::ptrdiff_t, not_null const &) gsl_is_delete; // not_null comparisons template gsl_api inline gsl_constexpr bool operator==(not_null const &l, not_null const &r) { return l.get() == r.get(); } template gsl_api inline gsl_constexpr bool operator<(not_null const &l, not_null const &r) { return l.get() < r.get(); } template gsl_api inline gsl_constexpr bool operator!=(not_null const &l, not_null const &r) { return !(l == r); } template gsl_api inline gsl_constexpr bool operator<=(not_null const &l, not_null const &r) { return !(r < l); } template gsl_api inline gsl_constexpr bool operator>(not_null const &l, not_null const &r) { return (r < l); } template gsl_api inline gsl_constexpr bool operator>=(not_null const &l, not_null const &r) { return !(l < r); } // // Byte-specific type. // #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) enum class gsl_may_alias byte : unsigned char { }; #else struct gsl_may_alias byte { typedef unsigned char type; type v; }; #endif #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) #define gsl_ENABLE_IF_INTEGRAL_T(T) \ gsl_REQUIRES_T((std::is_integral::value)) #else #define gsl_ENABLE_IF_INTEGRAL_T(T) #endif template gsl_api inline gsl_constexpr byte to_byte(T v) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return static_cast(v); #elif gsl_HAVE(CONSTEXPR_11) return {static_cast(v)}; #else byte b = {static_cast(v)}; return b; #endif } template gsl_api inline gsl_constexpr IntegerType to_integer(byte b) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return static_cast::type>(b); #else return b.v; #endif } gsl_api inline gsl_constexpr unsigned char to_uchar(byte b) gsl_noexcept { return to_integer(b); } gsl_api inline gsl_constexpr unsigned char to_uchar(int i) gsl_noexcept { return static_cast(i); } #if !gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) gsl_api inline gsl_constexpr bool operator==(byte l, byte r) gsl_noexcept { return l.v == r.v; } gsl_api inline gsl_constexpr bool operator!=(byte l, byte r) gsl_noexcept { return !(l == r); } gsl_api inline gsl_constexpr bool operator<(byte l, byte r) gsl_noexcept { return l.v < r.v; } gsl_api inline gsl_constexpr bool operator<=(byte l, byte r) gsl_noexcept { return !(r < l); } gsl_api inline gsl_constexpr bool operator>(byte l, byte r) gsl_noexcept { return (r < l); } gsl_api inline gsl_constexpr bool operator>=(byte l, byte r) gsl_noexcept { return !(l < r); } #endif template gsl_api inline gsl_constexpr14 byte &operator<<=(byte &b, IntegerType shift) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return b = to_byte(to_uchar(b) << shift); #else b.v = to_uchar(b.v << shift); return b; #endif } template gsl_api inline gsl_constexpr byte operator<<(byte b, IntegerType shift) gsl_noexcept { return to_byte(to_uchar(b) << shift); } template gsl_api inline gsl_constexpr14 byte &operator>>=(byte &b, IntegerType shift) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return b = to_byte(to_uchar(b) >> shift); #else b.v = to_uchar(b.v >> shift); return b; #endif } template gsl_api inline gsl_constexpr byte operator>>(byte b, IntegerType shift) gsl_noexcept { return to_byte(to_uchar(b) >> shift); } gsl_api inline gsl_constexpr14 byte &operator|=(byte &l, byte r) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return l = to_byte(to_uchar(l) | to_uchar(r)); #else l.v = to_uchar(l) | to_uchar(r); return l; #endif } gsl_api inline gsl_constexpr byte operator|(byte l, byte r) gsl_noexcept { return to_byte(to_uchar(l) | to_uchar(r)); } gsl_api inline gsl_constexpr14 byte &operator&=(byte &l, byte r) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return l = to_byte(to_uchar(l) & to_uchar(r)); #else l.v = to_uchar(l) & to_uchar(r); return l; #endif } gsl_api inline gsl_constexpr byte operator&(byte l, byte r)gsl_noexcept { return to_byte(to_uchar(l) & to_uchar(r)); } gsl_api inline gsl_constexpr14 byte &operator^=(byte &l, byte r) gsl_noexcept { #if gsl_HAVE(ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE) return l = to_byte(to_uchar(l) ^ to_uchar(r)); #else l.v = to_uchar(l) ^ to_uchar(r); return l; #endif } gsl_api inline gsl_constexpr byte operator^(byte l, byte r) gsl_noexcept { return to_byte(to_uchar(l) ^ to_uchar(r)); } gsl_api inline gsl_constexpr byte operator~(byte b) gsl_noexcept { return to_byte(~to_uchar(b)); } #if gsl_FEATURE_TO_STD(WITH_CONTAINER) // Tag to select span constructor taking a container (prevent ms-gsl warning C26426): struct with_container_t { gsl_constexpr with_container_t() gsl_noexcept {} }; const gsl_constexpr with_container_t with_container; #endif // // span<> - A 1D view of contiguous T's, replace (*,len). // template class span { template friend class span; public: typedef index index_type; typedef T element_type; typedef typename std11::remove_cv::type value_type; typedef T &reference; typedef T *pointer; typedef T const *const_pointer; typedef T const &const_reference; typedef pointer iterator; typedef const_pointer const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; typedef typename std::iterator_traits::difference_type difference_type; // 26.7.3.2 Constructors, copy, and assignment [span.cons] gsl_api gsl_constexpr14 span() gsl_noexcept : first_(gsl_nullptr), last_(gsl_nullptr) { Expects(size() == 0); } #if !gsl_DEPRECATE_TO_LEVEL(5) #if gsl_HAVE(NULLPTR) gsl_api gsl_constexpr14 span(std::nullptr_t, index_type gsl_EXPECTS_UNUSED_PARAM(size_in)) : first_(nullptr), last_(nullptr) { Expects(size_in == 0); } #endif #if gsl_HAVE(IS_DELETE) gsl_api gsl_constexpr span(reference data_in) : span(&data_in, 1) { } gsl_api gsl_constexpr span(element_type &&) = delete; #endif #endif // deprecate gsl_api gsl_constexpr14 span(pointer data_in, index_type size_in) : first_(data_in), last_(data_in + size_in) { Expects(size_in == 0 || (size_in > 0 && data_in != gsl_nullptr)); } gsl_api gsl_constexpr14 span(pointer first_in, pointer last_in) : first_(first_in), last_(last_in) { Expects(first_in <= last_in); } #if !gsl_DEPRECATE_TO_LEVEL(5) template gsl_api gsl_constexpr14 span(U *&data_in, index_type size_in) : first_(data_in), last_(data_in + size_in) { Expects(size_in == 0 || (size_in > 0 && data_in != gsl_nullptr)); } template gsl_api gsl_constexpr14 span(U *const &data_in, index_type size_in) : first_(data_in), last_(data_in + size_in) { Expects(size_in == 0 || (size_in > 0 && data_in != gsl_nullptr)); } #endif // deprecate #if !gsl_DEPRECATE_TO_LEVEL(5) template gsl_api gsl_constexpr span(U (&arr)[N]) gsl_noexcept : first_(gsl_ADDRESSOF(arr[0])), last_(gsl_ADDRESSOF(arr[0]) + N) { } #else template ::value)) #endif > gsl_api gsl_constexpr span(element_type (&arr)[N]) gsl_noexcept : first_(gsl_ADDRESSOF(arr[0])), last_(gsl_ADDRESSOF(arr[0]) + N) { } #endif // deprecate #if gsl_HAVE(ARRAY) #if !gsl_DEPRECATE_TO_LEVEL(5) template gsl_api gsl_constexpr span(std::array &arr) : first_(arr.data()), last_(arr.data() + N) { } template gsl_api gsl_constexpr span(std::array const &arr) : first_(arr.data()), last_(arr.data() + N) { } #else template ::value)) #endif > gsl_api gsl_constexpr span(std::array &arr) : first_(arr.data()), last_(arr.data() + N) { } template ::value)) #endif > gsl_api gsl_constexpr span(std::array const &arr) : first_(arr.data()), last_(arr.data() + N) { } #endif // deprecate #endif // gsl_HAVE( ARRAY ) #if gsl_HAVE(CONSTRAINED_SPAN_CONTAINER_CTOR) template ::value))> gsl_api gsl_constexpr span(Container &cont) : first_(std17::data(cont)), last_(std17::data(cont) + std17::size(cont)) { } template ::value && detail::is_compatible_container::value))> gsl_api gsl_constexpr span(Container const &cont) : first_(std17::data(cont)), last_(std17::data(cont) + std17::size(cont)) { } #elif gsl_HAVE(UNCONSTRAINED_SPAN_CONTAINER_CTOR) template gsl_api gsl_constexpr span(Container &cont) : first_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0])), last_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0]) + cont.size()) { } template gsl_api gsl_constexpr span(Container const &cont) : first_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0])), last_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0]) + cont.size()) { } #endif #if gsl_FEATURE_TO_STD(WITH_CONTAINER) template gsl_api gsl_constexpr span(with_container_t, Container &cont) : first_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0])), last_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0]) + cont.size()) { } template gsl_api gsl_constexpr span(with_container_t, Container const &cont) : first_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0])), last_(cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF(cont[0]) + cont.size()) { } #endif #if !gsl_DEPRECATE_TO_LEVEL(4) // constructor taking shared_ptr deprecated since 0.29.0 #if gsl_HAVE(SHARED_PTR) gsl_api gsl_constexpr span(shared_ptr const &ptr) : first_(ptr.get()), last_(ptr.get() ? ptr.get() + 1 : gsl_nullptr) { } #endif // constructors taking unique_ptr deprecated since 0.29.0 #if gsl_HAVE(UNIQUE_PTR) #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) template ::type> #else template #endif gsl_api gsl_constexpr span(unique_ptr const &ptr, index_type count) : first_(ptr.get()), last_(ptr.get() + count) { } gsl_api gsl_constexpr span(unique_ptr const &ptr) : first_(ptr.get()), last_(ptr.get() ? ptr.get() + 1 : gsl_nullptr) { } #endif #endif // deprecate shared_ptr, unique_ptr #if gsl_HAVE(IS_DEFAULT) && !gsl_BETWEEN(gsl_COMPILER_GNUC_VERSION, 430, 600) gsl_api gsl_constexpr span(span &&) gsl_noexcept = default; gsl_api gsl_constexpr span(span const &) = default; #else gsl_api gsl_constexpr span(span const &other) : first_(other.begin()), last_(other.end()) { } #endif #if gsl_HAVE(IS_DEFAULT) ~span() = default; #else ~span() { } #endif #if gsl_HAVE(IS_DEFAULT) gsl_api gsl_constexpr14 span &operator=(span &&) gsl_noexcept = default; gsl_api gsl_constexpr14 span &operator=(span const &) gsl_noexcept = default; #else gsl_api span &operator=(span other) gsl_noexcept { other.swap(*this); return *this; } #endif template ::value)) #endif > gsl_api gsl_constexpr span(span const &other) : first_(other.begin()), last_(other.end()) { } #if 0 // Converting from other span ? template< class U > operator=(); #endif // 26.7.3.3 Subviews [span.sub] gsl_api gsl_constexpr14 span first(index_type count) const gsl_noexcept { Expects(0 <= count && count <= this->size()); return span(this->data(), count); } gsl_api gsl_constexpr14 span last(index_type count) const gsl_noexcept { Expects(0 <= count && count <= this->size()); return span(this->data() + this->size() - count, count); } gsl_api gsl_constexpr14 span subspan(index_type offset) const gsl_noexcept { Expects(0 <= offset && offset <= this->size()); return span(this->data() + offset, this->size() - offset); } gsl_api gsl_constexpr14 span subspan(index_type offset, index_type count) const gsl_noexcept { Expects( 0 <= offset && offset <= this->size() && 0 <= count && count <= this->size() - offset); return span(this->data() + offset, count); } // 26.7.3.4 Observers [span.obs] gsl_api gsl_constexpr index_type size() const gsl_noexcept { return narrow_cast(last_ - first_); } gsl_api gsl_constexpr std::ptrdiff_t ssize() const gsl_noexcept { return narrow_cast(last_ - first_); } gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept { return size() * narrow_cast(sizeof(element_type)); } gsl_api gsl_constexpr bool empty() const gsl_noexcept { return size() == 0; } // 26.7.3.5 Element access [span.elem] gsl_api gsl_constexpr reference operator[](index_type pos) const { return at(pos); } gsl_api gsl_constexpr reference operator()(index_type pos) const { return at(pos); } gsl_api gsl_constexpr14 reference at(index_type pos) const { Expects(pos < size()); return first_[pos]; } gsl_api gsl_constexpr pointer data() const gsl_noexcept { return first_; } // 26.7.3.6 Iterator support [span.iterators] gsl_api gsl_constexpr iterator begin() const gsl_noexcept { return iterator(first_); } gsl_api gsl_constexpr iterator end() const gsl_noexcept { return iterator(last_); } gsl_api gsl_constexpr const_iterator cbegin() const gsl_noexcept { #if gsl_CPP11_OR_GREATER return {begin()}; #else return const_iterator(begin()); #endif } gsl_api gsl_constexpr const_iterator cend() const gsl_noexcept { #if gsl_CPP11_OR_GREATER return {end()}; #else return const_iterator(end()); #endif } gsl_api gsl_constexpr reverse_iterator rbegin() const gsl_noexcept { return reverse_iterator(end()); } gsl_api gsl_constexpr reverse_iterator rend() const gsl_noexcept { return reverse_iterator(begin()); } gsl_api gsl_constexpr const_reverse_iterator crbegin() const gsl_noexcept { return const_reverse_iterator(cend()); } gsl_api gsl_constexpr const_reverse_iterator crend() const gsl_noexcept { return const_reverse_iterator(cbegin()); } gsl_api void swap(span &other) gsl_noexcept { using std::swap; swap(first_, other.first_); swap(last_, other.last_); } #if !gsl_DEPRECATE_TO_LEVEL(3) // member length() deprecated since 0.29.0 gsl_api gsl_constexpr index_type length() const gsl_noexcept { return size(); } // member length_bytes() deprecated since 0.29.0 gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept { return size_bytes(); } #endif #if !gsl_DEPRECATE_TO_LEVEL(2) // member as_bytes(), as_writeable_bytes deprecated since 0.17.0 gsl_api span as_bytes() const gsl_noexcept { return span(reinterpret_cast(data()), size_bytes()); // NOLINT } gsl_api span as_writeable_bytes() const gsl_noexcept { return span(reinterpret_cast(data()), size_bytes()); // NOLINT } #endif template gsl_api span as_span() const gsl_noexcept { Expects((this->size_bytes() % sizeof(U)) == 0); return span(reinterpret_cast(this->data()), this->size_bytes() / sizeof(U)); // NOLINT } private: pointer first_; pointer last_; }; // class template argument deduction guides: #if gsl_HAVE(DEDUCTION_GUIDES) // gsl_CPP17_OR_GREATER template span(T(&)[N])->span; template span(std::array &)->span; template span(std::array const &)->span; template span(Container &)->span; template span(Container const &)->span; #endif // gsl_HAVE( DEDUCTION_GUIDES ) // 26.7.3.7 Comparison operators [span.comparison] #if gsl_CONFIG(ALLOWS_NONSTRICT_SPAN_COMPARISON) template gsl_api inline gsl_constexpr bool operator==(span const &l, span const &r) { return l.size() == r.size() && (l.begin() == r.begin() || std::equal(l.begin(), l.end(), r.begin())); } template gsl_api inline gsl_constexpr bool operator<(span const &l, span const &r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } #else template gsl_api inline gsl_constexpr bool operator==(span const &l, span const &r) { return l.size() == r.size() && (l.begin() == r.begin() || std::equal(l.begin(), l.end(), r.begin())); } template gsl_api inline gsl_constexpr bool operator<(span const &l, span const &r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } #endif template gsl_api inline gsl_constexpr bool operator!=(span const &l, span const &r) { return !(l == r); } template gsl_api inline gsl_constexpr bool operator<=(span const &l, span const &r) { return !(r < l); } template gsl_api inline gsl_constexpr bool operator>(span const &l, span const &r) { return (r < l); } template gsl_api inline gsl_constexpr bool operator>=(span const &l, span const &r) { return !(l < r); } // span algorithms template gsl_api inline gsl_constexpr std::size_t size(span const &spn) { return static_cast(spn.size()); } template gsl_api inline gsl_constexpr std::ptrdiff_t ssize(span const &spn) { return spn.ssize(); } namespace detail { template gsl_api inline OI copy_n(II first, N count, OI result) { if (count > 0) { *result++ = *first; for (N i = 1; i < count; ++i) { *result++ = *++first; } } return result; } } // namespace detail template gsl_api inline void copy(span src, span dest) { #if gsl_CPP14_OR_GREATER // gsl_HAVE( TYPE_TRAITS ) (circumvent Travis clang 3.4) static_assert(std::is_assignable::value, "Cannot assign elements of source span to elements of destination span"); #endif Expects(dest.size() >= src.size()); detail::copy_n(src.data(), src.size(), dest.data()); } // span creator functions (see ctors) template gsl_api inline span as_bytes(span spn) gsl_noexcept { return span(reinterpret_cast(spn.data()), spn.size_bytes()); // NOLINT } template gsl_api inline span as_writeable_bytes(span spn) gsl_noexcept { return span(reinterpret_cast(spn.data()), spn.size_bytes()); // NOLINT } #if gsl_FEATURE_TO_STD(MAKE_SPAN) template gsl_api inline gsl_constexpr span make_span(T * ptr, typename span::index_type count) { return span(ptr, count); } template gsl_api inline gsl_constexpr span make_span(T * first, T * last) { return span(first, last); } template gsl_api inline gsl_constexpr span make_span(T(&arr)[N]) { return span(gsl_ADDRESSOF(arr[0]), N); } #if gsl_HAVE(ARRAY) template gsl_api inline gsl_constexpr span make_span(std::array & arr) { return span(arr); } template gsl_api inline gsl_constexpr span make_span(std::array const &arr) { return span(arr); } #endif #if gsl_HAVE(CONSTRAINED_SPAN_CONTAINER_CTOR) && gsl_HAVE(AUTO) template ()))> gsl_api inline gsl_constexpr auto make_span(Container & cont) ->span::type> { return span::type>(cont); } template ()))> gsl_api inline gsl_constexpr auto make_span(Container const &cont) ->span::type> { return span::type>(cont); } #else template gsl_api inline span make_span(std::vector & cont) { return span(with_container, cont); } template gsl_api inline span make_span(std::vector const &cont) { return span(with_container, cont); } #endif #if gsl_FEATURE_TO_STD(WITH_CONTAINER) template gsl_api inline gsl_constexpr span make_span(with_container_t, Container & cont) gsl_noexcept { return span(with_container, cont); } template gsl_api inline gsl_constexpr span make_span(with_container_t, Container const &cont) gsl_noexcept { return span(with_container, cont); } #endif // gsl_FEATURE_TO_STD( WITH_CONTAINER ) template gsl_api inline span make_span(Ptr & ptr) { return span(ptr); } template gsl_api inline span make_span(Ptr & ptr, typename span::index_type count) { return span(ptr, count); } #endif // gsl_FEATURE_TO_STD( MAKE_SPAN ) #if gsl_FEATURE_TO_STD(BYTE_SPAN) template gsl_api inline gsl_constexpr span byte_span(T & t) gsl_noexcept { return span(reinterpret_cast(&t), sizeof(T)); } template gsl_api inline gsl_constexpr span byte_span(T const &t) gsl_noexcept { return span(reinterpret_cast(&t), sizeof(T)); } #endif // gsl_FEATURE_TO_STD( BYTE_SPAN ) // // basic_string_span: // template class basic_string_span; namespace detail { template struct is_basic_string_span_oracle : std11::false_type { }; template struct is_basic_string_span_oracle > : std11::true_type { }; template struct is_basic_string_span : is_basic_string_span_oracle::type> { }; template gsl_api inline gsl_constexpr14 std::size_t string_length(T *ptr, std::size_t max) { if (ptr == gsl_nullptr || max <= 0) return 0; std::size_t len = 0; while (len < max && ptr[len]) // NOLINT ++len; return len; } } // namespace detail // // basic_string_span<> - A view of contiguous characters, replace (*,len). // template class basic_string_span { public: typedef T element_type; typedef span span_type; typedef typename span_type::index_type index_type; typedef typename span_type::difference_type difference_type; typedef typename span_type::pointer pointer; typedef typename span_type::reference reference; typedef typename span_type::iterator iterator; typedef typename span_type::const_iterator const_iterator; typedef typename span_type::reverse_iterator reverse_iterator; typedef typename span_type::const_reverse_iterator const_reverse_iterator; // construction: #if gsl_HAVE(IS_DEFAULT) gsl_api gsl_constexpr basic_string_span() gsl_noexcept = default; #else gsl_api gsl_constexpr basic_string_span() gsl_noexcept { } #endif #if gsl_HAVE(NULLPTR) gsl_api gsl_constexpr basic_string_span(std::nullptr_t ptr) gsl_noexcept : span_(ptr, index_type(0)) { } #endif gsl_api gsl_constexpr basic_string_span(pointer ptr) : span_(remove_z(ptr, (std::numeric_limits::max)())) { } gsl_api gsl_constexpr basic_string_span(pointer ptr, index_type count) : span_(ptr, count) { } gsl_api gsl_constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) { } template gsl_api gsl_constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(gsl_ADDRESSOF(arr[0]), N)) { } #if gsl_HAVE(ARRAY) template gsl_api gsl_constexpr basic_string_span(std::array::type, N> &arr) : span_(remove_z(arr)) { } template gsl_api gsl_constexpr basic_string_span(std::array::type, N> const &arr) : span_(remove_z(arr)) { } #endif #if gsl_HAVE(CONSTRAINED_SPAN_CONTAINER_CTOR) // Exclude: array, [basic_string,] basic_string_span template ::value && !detail::is_basic_string_span::value && std::is_convertible::value && std::is_convertible().data())>::value))> gsl_api gsl_constexpr basic_string_span(Container &cont) : span_((cont)) { } // Exclude: array, [basic_string,] basic_string_span template ::value && !detail::is_basic_string_span::value && std::is_convertible::value && std::is_convertible().data())>::value))> gsl_api gsl_constexpr basic_string_span(Container const &cont) : span_((cont)) { } #elif gsl_HAVE(UNCONSTRAINED_SPAN_CONTAINER_CTOR) template gsl_api gsl_constexpr basic_string_span(Container &cont) : span_(cont) { } template gsl_api gsl_constexpr basic_string_span(Container const &cont) : span_(cont) { } #else template gsl_api gsl_constexpr basic_string_span(span const &rhs) : span_(rhs) { } #endif #if gsl_FEATURE_TO_STD(WITH_CONTAINER) template gsl_api gsl_constexpr basic_string_span(with_container_t, Container &cont) : span_(with_container, cont) { } #endif #if gsl_HAVE(IS_DEFAULT) #if gsl_BETWEEN(gsl_COMPILER_GNUC_VERSION, 440, 600) gsl_api gsl_constexpr basic_string_span(basic_string_span const &rhs) = default; gsl_api gsl_constexpr basic_string_span(basic_string_span &&rhs) = default; #else gsl_api gsl_constexpr basic_string_span(basic_string_span const &rhs) gsl_noexcept = default; gsl_api gsl_constexpr basic_string_span(basic_string_span &&rhs) gsl_noexcept = default; #endif #endif template ::pointer, pointer>::value)) #endif > gsl_api gsl_constexpr basic_string_span(basic_string_span const &rhs) : span_(reinterpret_cast(rhs.data()), rhs.length()) // NOLINT { } #if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 template ::pointer, pointer>::value))> gsl_api gsl_constexpr basic_string_span(basic_string_span &&rhs) : span_(reinterpret_cast(rhs.data()), rhs.length()) // NOLINT { } #endif template gsl_api gsl_constexpr basic_string_span( std::basic_string::type, CharTraits, Allocator> &str) : span_(gsl_ADDRESSOF(str[0]), str.length()) { } template gsl_api gsl_constexpr basic_string_span( std::basic_string::type, CharTraits, Allocator> const &str) : span_(gsl_ADDRESSOF(str[0]), str.length()) { } // destruction, assignment: #if gsl_HAVE(IS_DEFAULT) gsl_api ~basic_string_span() gsl_noexcept = default; gsl_api basic_string_span &operator=(basic_string_span const &rhs) gsl_noexcept = default; gsl_api basic_string_span &operator=(basic_string_span &&rhs) gsl_noexcept = default; #endif // sub span: gsl_api gsl_constexpr basic_string_span first(index_type count) const { return span_.first(count); } gsl_api gsl_constexpr basic_string_span last(index_type count) const { return span_.last(count); } gsl_api gsl_constexpr basic_string_span subspan(index_type offset) const { return span_.subspan(offset); } gsl_api gsl_constexpr basic_string_span subspan(index_type offset, index_type count) const { return span_.subspan(offset, count); } // observers: gsl_api gsl_constexpr index_type length() const gsl_noexcept { return span_.size(); } gsl_api gsl_constexpr index_type size() const gsl_noexcept { return span_.size(); } gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept { return span_.size_bytes(); } gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept { return span_.size_bytes(); } gsl_api gsl_constexpr bool empty() const gsl_noexcept { return size() == 0; } gsl_api gsl_constexpr reference operator[](index_type idx) const { return span_[idx]; } gsl_api gsl_constexpr reference operator()(index_type idx) const { return span_[idx]; } gsl_api gsl_constexpr pointer data() const gsl_noexcept { return span_.data(); } gsl_api iterator begin() const gsl_noexcept { return span_.begin(); } gsl_api iterator end() const gsl_noexcept { return span_.end(); } gsl_api reverse_iterator rbegin() const gsl_noexcept { return span_.rbegin(); } gsl_api reverse_iterator rend() const gsl_noexcept { return span_.rend(); } // const version not in p0123r2: gsl_api const_iterator cbegin() const gsl_noexcept { return span_.cbegin(); } gsl_api const_iterator cend() const gsl_noexcept { return span_.cend(); } gsl_api const_reverse_iterator crbegin() const gsl_noexcept { return span_.crbegin(); } gsl_api const_reverse_iterator crend() const gsl_noexcept { return span_.crend(); } private: gsl_api static gsl_constexpr14 span_type remove_z(pointer const &sz, std::size_t max) { return span_type(sz, detail::string_length(sz, max)); } #if gsl_HAVE(ARRAY) template gsl_api static gsl_constexpr14 span_type remove_z(std::array::type, N> &arr) { return remove_z(gsl_ADDRESSOF(arr[0]), narrow_cast(N)); } template gsl_api static gsl_constexpr14 span_type remove_z(std::array::type, N> const &arr) { return remove_z(gsl_ADDRESSOF(arr[0]), narrow_cast(N)); } #endif private: span_type span_; }; // basic_string_span comparison functions: #if gsl_CONFIG(ALLOWS_NONSTRICT_SPAN_COMPARISON) template gsl_api inline gsl_constexpr14 bool operator==(basic_string_span const &l, U const &u) gsl_noexcept { const basic_string_span::type> r(u); return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); } template gsl_api inline gsl_constexpr14 bool operator<(basic_string_span const &l, U const &u) gsl_noexcept { const basic_string_span::type> r(u); return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) template ::value))> gsl_api inline gsl_constexpr14 bool operator==(U const &u, basic_string_span const &r) gsl_noexcept { const basic_string_span::type> l(u); return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); } template ::value))> gsl_api inline gsl_constexpr14 bool operator<(U const &u, basic_string_span const &r) gsl_noexcept { const basic_string_span::type> l(u); return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } #endif #else //gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) template gsl_api inline gsl_constexpr14 bool operator==(basic_string_span const &l, basic_string_span const &r) gsl_noexcept { return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); } template gsl_api inline gsl_constexpr14 bool operator<(basic_string_span const &l, basic_string_span const &r) gsl_noexcept { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } #endif // gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) template gsl_api inline gsl_constexpr14 bool operator!=(basic_string_span const &l, U const &r) gsl_noexcept { return !(l == r); } template gsl_api inline gsl_constexpr14 bool operator<=(basic_string_span const &l, U const &r) gsl_noexcept { #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) || !gsl_CONFIG(ALLOWS_NONSTRICT_SPAN_COMPARISON) return !(r < l); #else basic_string_span::type> rr(r); return !(rr < l); #endif } template gsl_api inline gsl_constexpr14 bool operator>(basic_string_span const &l, U const &r) gsl_noexcept { #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) || !gsl_CONFIG(ALLOWS_NONSTRICT_SPAN_COMPARISON) return (r < l); #else basic_string_span::type> rr(r); return (rr < l); #endif } template gsl_api inline gsl_constexpr14 bool operator>=(basic_string_span const &l, U const &r) gsl_noexcept { return !(l < r); } #if gsl_HAVE(DEFAULT_FUNCTION_TEMPLATE_ARG) template ::value))> gsl_api inline gsl_constexpr14 bool operator!=(U const &l, basic_string_span const &r) gsl_noexcept { return !(l == r); } template ::value))> gsl_api inline gsl_constexpr14 bool operator<=(U const &l, basic_string_span const &r) gsl_noexcept { return !(r < l); } template ::value))> gsl_api inline gsl_constexpr14 bool operator>(U const &l, basic_string_span const &r) gsl_noexcept { return (r < l); } template ::value))> gsl_api inline gsl_constexpr14 bool operator>=(U const &l, basic_string_span const &r) gsl_noexcept { return !(l < r); } #endif // gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) // convert basic_string_span to byte span: template gsl_api inline span as_bytes(basic_string_span spn) gsl_noexcept { return span(reinterpret_cast(spn.data()), spn.size_bytes()); // NOLINT } // // String types: // typedef char *zstring; typedef const char *czstring; #if gsl_HAVE(WCHAR) typedef wchar_t *zwstring; typedef const wchar_t *cwzstring; #endif typedef basic_string_span string_span; typedef basic_string_span cstring_span; #if gsl_HAVE(WCHAR) typedef basic_string_span wstring_span; typedef basic_string_span cwstring_span; #endif // to_string() allow (explicit) conversions from string_span to string #if 0 template< class T > gsl_api inline std::basic_string< typename std::remove_const::type > to_string( basic_string_span spn ) { std::string( spn.data(), spn.length() ); } #else gsl_api inline std::string to_string(string_span const &spn) { return std::string(spn.data(), spn.length()); } gsl_api inline std::string to_string(cstring_span const &spn) { return std::string(spn.data(), spn.length()); } #if gsl_HAVE(WCHAR) gsl_api inline std::wstring to_string(wstring_span const &spn) { return std::wstring(spn.data(), spn.length()); } gsl_api inline std::wstring to_string(cwstring_span const &spn) { return std::wstring(spn.data(), spn.length()); } #endif // gsl_HAVE( WCHAR ) #endif // to_string() // // Stream output for string_span types // namespace detail { template gsl_api void write_padding(Stream &os, std::streamsize n) { for (std::streamsize i = 0; i < n; ++i) os.rdbuf()->sputc(os.fill()); } template gsl_api Stream &write_to_stream(Stream &os, Span const &spn) { typename Stream::sentry sentry(os); if (!os) return os; const std::streamsize length = narrow(spn.length()); // Whether, and how, to pad const bool pad = (length < os.width()); const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right; if (left_pad) write_padding(os, os.width() - length); // Write span characters os.rdbuf()->sputn(spn.begin(), length); if (pad && !left_pad) write_padding(os, os.width() - length); // Reset output stream width os.width(0); return os; } } // namespace detail template gsl_api std::basic_ostream &operator<<(std::basic_ostream &os, string_span const &spn) { return detail::write_to_stream(os, spn); } template gsl_api std::basic_ostream &operator<<(std::basic_ostream &os, cstring_span const &spn) { return detail::write_to_stream(os, spn); } #if gsl_HAVE(WCHAR) template gsl_api std::basic_ostream &operator<<(std::basic_ostream &os, wstring_span const &spn) { return detail::write_to_stream(os, spn); } template gsl_api std::basic_ostream &operator<<(std::basic_ostream &os, cwstring_span const &spn) { return detail::write_to_stream(os, spn); } #endif // gsl_HAVE( WCHAR ) // // ensure_sentinel() // // Provides a way to obtain a span from a contiguous sequence // that ends with a (non-inclusive) sentinel value. // // Will fail-fast if sentinel cannot be found before max elements are examined. // namespace detail { template gsl_api static span ensure_sentinel(T *seq, SizeType max = (std::numeric_limits::max)()) { typedef T *pointer; gsl_SUPPRESS_MSVC_WARNING(26429, "f.23: symbol 'cur' is never tested for nullness, it can be marked as not_null") pointer cur = seq; while (static_cast(cur - seq) < max && *cur != Sentinel) ++cur; Expects(*cur == Sentinel); return span(seq, narrow_cast::index_type>(cur - seq)); } } // namespace detail // // ensure_z - creates a string_span for a czstring or cwzstring. // Will fail fast if a null-terminator cannot be found before // the limit of size_type. // template gsl_api inline span ensure_z(T *const &sz, size_t max = (std::numeric_limits::max)()) { return detail::ensure_sentinel(sz, max); } template gsl_api inline span ensure_z(T(&sz)[N]) { return ensure_z(gsl_ADDRESSOF(sz[0]), N); } #if gsl_HAVE(TYPE_TRAITS) template gsl_api inline span::type> ensure_z(Container & cont) { return ensure_z(cont.data(), cont.length()); } #endif // // basic_zstring_span<> - A view of contiguous null-terminated characters, replace (*,len). // template class basic_zstring_span { public: typedef T element_type; typedef span span_type; typedef typename span_type::index_type index_type; typedef typename span_type::difference_type difference_type; typedef element_type *czstring_type; typedef basic_string_span string_span_type; gsl_api gsl_constexpr14 basic_zstring_span(span_type s) : span_(s) { // expects a zero-terminated span Expects(s[s.size() - 1] == '\0'); } #if gsl_HAVE(IS_DEFAULT) gsl_api gsl_constexpr basic_zstring_span(basic_zstring_span const &other) = default; gsl_api gsl_constexpr basic_zstring_span(basic_zstring_span &&other) = default; gsl_api gsl_constexpr14 basic_zstring_span &operator=(basic_zstring_span const &other) = default; gsl_api gsl_constexpr14 basic_zstring_span &operator=(basic_zstring_span &&other) = default; #else gsl_api gsl_constexpr basic_zstring_span(basic_zstring_span const &other) : span_(other.span_) { } gsl_api gsl_constexpr basic_zstring_span &operator=(basic_zstring_span const &other) { span_ = other.span_; return *this; } #endif gsl_api gsl_constexpr bool empty() const gsl_noexcept { return span_.size() == 0; } gsl_api gsl_constexpr string_span_type as_string_span() const gsl_noexcept { return string_span_type(span_.data(), span_.size() > 1 ? span_.size() - 1 : 0); } gsl_api gsl_constexpr string_span_type ensure_z() const { return gsl::ensure_z(span_.data(), span_.size()); } gsl_api gsl_constexpr czstring_type assume_z() const gsl_noexcept { return span_.data(); } private: span_type span_; }; // // zString types: // typedef basic_zstring_span zstring_span; typedef basic_zstring_span czstring_span; #if gsl_HAVE(WCHAR) typedef basic_zstring_span wzstring_span; typedef basic_zstring_span cwzstring_span; #endif } // namespace gsl #if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 namespace std { template <> struct hash { public: std::size_t operator()(gsl::byte v) const gsl_noexcept { return gsl::to_integer(v); } }; } // namespace std #endif gsl_RESTORE_MSVC_WARNINGS() #endif // GSL_GSL_LITE_HPP_INCLUDED // end of file src/algorithms/libs/opencl/000077500000000000000000000000001352176506000162345ustar00rootroot00000000000000src/algorithms/libs/opencl/cl.hpp000066400000000000000000012260371352176506000173560ustar00rootroot00000000000000/******************************************************************************* * Copyright (c) 2008-2015 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and/or associated documentation files (the * "Materials"), to deal in the Materials without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Materials, and to * permit persons to whom the Materials are furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ******************************************************************************/ /*! \file * * \brief C++ bindings for OpenCL 1.0 (rev 48), OpenCL 1.1 (rev 33) and * OpenCL 1.2 (rev 15) * \author Benedict R. Gaster, Laurent Morichetti and Lee Howes * * Additions and fixes from: * Brian Cole, March 3rd 2010 and April 2012 * Matt Gruenke, April 2012. * Bruce Merry, February 2013. * Tom Deakin and Simon McIntosh-Smith, July 2013 * * \version 1.2.8 * \date October 2015 * * Optional extension support * * cl * cl_ext_device_fission * #define USE_CL_DEVICE_FISSION */ /*! \mainpage * \section intro Introduction * For many large applications C++ is the language of choice and so it seems * reasonable to define C++ bindings for OpenCL. * * * The interface is contained with a single C++ header file \em cl.hpp and all * definitions are contained within the namespace \em cl. There is no additional * requirement to include \em cl.h and to use either the C++ or original C * bindings it is enough to simply include \em cl.hpp. * * The bindings themselves are lightweight and correspond closely to the * underlying C API. Using the C++ bindings introduces no additional execution * overhead. * * For detail documentation on the bindings see: * * The OpenCL C++ Wrapper API 1.2 (revision 09) * http://www.khronos.org/registry/cl/specs/opencl-cplusplus-1.2.pdf * * \section example Example * * The following example shows a general use case for the C++ * bindings, including support for the optional exception feature and * also the supplied vector and string classes, see following sections for * decriptions of these features. * * \code * #define __CL_ENABLE_EXCEPTIONS * * #if defined(__APPLE__) || defined(__MACOSX) * #include * #else * #include * #endif * #include * #include * #include * * const char * helloStr = "__kernel void " * "hello(void) " * "{ " * " " * "} "; * * int * main(void) * { * cl_int err = CL_SUCCESS; * try { * * std::vector platforms; * cl::Platform::get(&platforms); * if (platforms.size() == 0) { * std::cout << "Platform size 0\n"; * return -1; * } * * cl_context_properties properties[] = * { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; * cl::Context context(CL_DEVICE_TYPE_CPU, properties); * * std::vector devices = context.getInfo(); * * cl::Program::Sources source(1, * std::make_pair(helloStr,strlen(helloStr))); * cl::Program program_ = cl::Program(context, source); * program_.build(devices); * * cl::Kernel kernel(program_, "hello", &err); * * cl::Event event; * cl::CommandQueue queue(context, devices[0], 0, &err); * queue.enqueueNDRangeKernel( * kernel, * cl::NullRange, * cl::NDRange(4,4), * cl::NullRange, * nullptr, * &event); * * event.wait(); * } * catch (cl::Error err) { * std::cerr * << "ERROR: " * << err.what() * << "(" * << err.err() * << ")" * << std::endl; * } * * return EXIT_SUCCESS; * } * * \endcode * */ #ifndef CL_HPP_ #define CL_HPP_ #ifdef _WIN32 #include #if defined(USE_DX_INTEROP) #include #include #endif #endif // _WIN32 #if defined(_MSC_VER) #include #endif // _MSC_VER // #if defined(USE_CL_DEVICE_FISSION) #include #endif #if defined(__APPLE__) || defined(__MACOSX) #define CL_SILENCE_DEPRECATION #include #else #include #endif // !__APPLE__ #if (_MSC_VER >= 1700) || (__cplusplus >= 201103L) #define CL_HPP_RVALUE_REFERENCES_SUPPORTED #define CL_HPP_CPP11_ATOMICS_SUPPORTED #include #endif #if (__cplusplus >= 201103L) #define CL_HPP_NOEXCEPT noexcept #else #define CL_HPP_NOEXCEPT #endif // To avoid accidentally taking ownership of core OpenCL types // such as cl_kernel constructors are made explicit // under OpenCL 1.2 #if defined(CL_VERSION_1_2) && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #define __CL_EXPLICIT_CONSTRUCTORS explicit #else // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #define __CL_EXPLICIT_CONSTRUCTORS #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) // Define deprecated prefixes and suffixes to ensure compilation // in case they are not pre-defined #if !defined(CL_EXT_PREFIX__VERSION_1_1_DEPRECATED) #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_EXT_PREFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED) #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_EXT_PREFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_CALLBACK) #define CL_CALLBACK #endif //CL_CALLBACK #include #include #include #if defined(__CL_ENABLE_EXCEPTIONS) #include #endif // #if defined(__CL_ENABLE_EXCEPTIONS) #if !defined(__NO_STD_VECTOR) #include #endif #if !defined(__NO_STD_STRING) #include #endif #if defined(__ANDROID__) || defined(linux) || defined(__APPLE__) || defined(__MACOSX) #include #endif // linux #include /*! \namespace cl * * \brief The OpenCL C++ bindings are defined within this namespace. * */ namespace cl { class Memory; /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) || (defined(CL_VERSION_1_1) && !defined(CL_VERSION_1_2)) #define __INIT_CL_EXT_FCN_PTR(name) \ if (!pfn_##name) \ { \ pfn_##name = (PFN_##name) \ clGetExtensionFunctionAddress(#name); \ if (!pfn_##name) \ { \ } \ } #endif // #if defined(CL_VERSION_1_1) #if defined(CL_VERSION_1_2) #define __INIT_CL_EXT_FCN_PTR_PLATFORM(platform, name) \ if (!pfn_##name) \ { \ pfn_##name = (PFN_##name) \ clGetExtensionFunctionAddressForPlatform(platform, #name); \ if (!pfn_##name) \ { \ } \ } #endif // #if defined(CL_VERSION_1_1) class Program; class Device; class Context; class CommandQueue; class Memory; class Buffer; #if defined(__CL_ENABLE_EXCEPTIONS) /*! \brief Exception class * * This may be thrown by API functions when __CL_ENABLE_EXCEPTIONS is defined. */ class Error : public std::exception { private: cl_int err_; const char* errStr_; public: /*! \brief Create a new CL error exception for a given error code * and corresponding message. * * \param err error code value. * * \param errStr a descriptive string that must remain in scope until * handling of the exception has concluded. If set, it * will be returned by what(). */ Error(cl_int err, const char* errStr = nullptr) : err_(err), errStr_(errStr) { } ~Error() throw() {} /*! \brief Get error string associated with exception * * \return A memory pointer to the error message string. */ virtual const char* what() const throw() { if (errStr_ == nullptr) { return "empty"; } else { return errStr_; } } /*! \brief Get error code associated with exception * * \return The error code. */ cl_int err(void) const { return err_; } }; #define __ERR_STR(x) #x #else #define __ERR_STR(x) nullptr #endif // __CL_ENABLE_EXCEPTIONS namespace detail { #if defined(__CL_ENABLE_EXCEPTIONS) static inline cl_int errHandler( cl_int err, const char* errStr = nullptr) { if (err != CL_SUCCESS) { throw Error(err, errStr); } return err; } #else static inline cl_int errHandler(cl_int err, const char* errStr = nullptr) { (void)errStr; // suppress unused variable warning return err; } #endif // __CL_ENABLE_EXCEPTIONS } // namespace detail //! \cond DOXYGEN_DETAIL #if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) #define __GET_DEVICE_INFO_ERR __ERR_STR(clGetDeviceInfo) #define __GET_PLATFORM_INFO_ERR __ERR_STR(clGetPlatformInfo) #define __GET_DEVICE_IDS_ERR __ERR_STR(clGetDeviceIDs) #define __GET_PLATFORM_IDS_ERR __ERR_STR(clGetPlatformIDs) #define __GET_CONTEXT_INFO_ERR __ERR_STR(clGetContextInfo) #define __GET_EVENT_INFO_ERR __ERR_STR(clGetEventInfo) #define __GET_EVENT_PROFILE_INFO_ERR __ERR_STR(clGetEventProfileInfo) #define __GET_MEM_OBJECT_INFO_ERR __ERR_STR(clGetMemObjectInfo) #define __GET_IMAGE_INFO_ERR __ERR_STR(clGetImageInfo) #define __GET_SAMPLER_INFO_ERR __ERR_STR(clGetSamplerInfo) #define __GET_KERNEL_INFO_ERR __ERR_STR(clGetKernelInfo) #if defined(CL_VERSION_1_2) #define __GET_KERNEL_ARG_INFO_ERR __ERR_STR(clGetKernelArgInfo) #endif // #if defined(CL_VERSION_1_2) #define __GET_KERNEL_WORK_GROUP_INFO_ERR __ERR_STR(clGetKernelWorkGroupInfo) #define __GET_PROGRAM_INFO_ERR __ERR_STR(clGetProgramInfo) #define __GET_PROGRAM_BUILD_INFO_ERR __ERR_STR(clGetProgramBuildInfo) #define __GET_COMMAND_QUEUE_INFO_ERR __ERR_STR(clGetCommandQueueInfo) #define __CREATE_CONTEXT_ERR __ERR_STR(clCreateContext) #define __CREATE_CONTEXT_FROM_TYPE_ERR __ERR_STR(clCreateContextFromType) #define __GET_SUPPORTED_IMAGE_FORMATS_ERR __ERR_STR(clGetSupportedImageFormats) #define __CREATE_BUFFER_ERR __ERR_STR(clCreateBuffer) #define __COPY_ERR __ERR_STR(cl::copy) #define __CREATE_SUBBUFFER_ERR __ERR_STR(clCreateSubBuffer) #define __CREATE_GL_BUFFER_ERR __ERR_STR(clCreateFromGLBuffer) #define __CREATE_GL_RENDER_BUFFER_ERR __ERR_STR(clCreateFromGLBuffer) #define __GET_GL_OBJECT_INFO_ERR __ERR_STR(clGetGLObjectInfo) #if defined(CL_VERSION_1_2) #define __CREATE_IMAGE_ERR __ERR_STR(clCreateImage) #define __CREATE_GL_TEXTURE_ERR __ERR_STR(clCreateFromGLTexture) #define __IMAGE_DIMENSION_ERR __ERR_STR(Incorrect image dimensions) #endif // #if defined(CL_VERSION_1_2) #define __CREATE_SAMPLER_ERR __ERR_STR(clCreateSampler) #define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR __ERR_STR(clSetMemObjectDestructorCallback) #define __CREATE_USER_EVENT_ERR __ERR_STR(clCreateUserEvent) #define __SET_USER_EVENT_STATUS_ERR __ERR_STR(clSetUserEventStatus) #define __SET_EVENT_CALLBACK_ERR __ERR_STR(clSetEventCallback) #define __WAIT_FOR_EVENTS_ERR __ERR_STR(clWaitForEvents) #define __CREATE_KERNEL_ERR __ERR_STR(clCreateKernel) #define __SET_KERNEL_ARGS_ERR __ERR_STR(clSetKernelArg) #define __CREATE_PROGRAM_WITH_SOURCE_ERR __ERR_STR(clCreateProgramWithSource) #define __CREATE_PROGRAM_WITH_BINARY_ERR __ERR_STR(clCreateProgramWithBinary) #if defined(CL_VERSION_1_2) #define __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR __ERR_STR(clCreateProgramWithBuiltInKernels) #endif // #if defined(CL_VERSION_1_2) #define __BUILD_PROGRAM_ERR __ERR_STR(clBuildProgram) #if defined(CL_VERSION_1_2) #define __COMPILE_PROGRAM_ERR __ERR_STR(clCompileProgram) #define __LINK_PROGRAM_ERR __ERR_STR(clLinkProgram) #endif // #if defined(CL_VERSION_1_2) #define __CREATE_KERNELS_IN_PROGRAM_ERR __ERR_STR(clCreateKernelsInProgram) #define __CREATE_COMMAND_QUEUE_ERR __ERR_STR(clCreateCommandQueue) #define __SET_COMMAND_QUEUE_PROPERTY_ERR __ERR_STR(clSetCommandQueueProperty) #define __ENQUEUE_READ_BUFFER_ERR __ERR_STR(clEnqueueReadBuffer) #define __ENQUEUE_READ_BUFFER_RECT_ERR __ERR_STR(clEnqueueReadBufferRect) #define __ENQUEUE_WRITE_BUFFER_ERR __ERR_STR(clEnqueueWriteBuffer) #define __ENQUEUE_WRITE_BUFFER_RECT_ERR __ERR_STR(clEnqueueWriteBufferRect) #define __ENQEUE_COPY_BUFFER_ERR __ERR_STR(clEnqueueCopyBuffer) #define __ENQEUE_COPY_BUFFER_RECT_ERR __ERR_STR(clEnqueueCopyBufferRect) #define __ENQUEUE_FILL_BUFFER_ERR __ERR_STR(clEnqueueFillBuffer) #define __ENQUEUE_READ_IMAGE_ERR __ERR_STR(clEnqueueReadImage) #define __ENQUEUE_WRITE_IMAGE_ERR __ERR_STR(clEnqueueWriteImage) #define __ENQUEUE_COPY_IMAGE_ERR __ERR_STR(clEnqueueCopyImage) #define __ENQUEUE_FILL_IMAGE_ERR __ERR_STR(clEnqueueFillImage) #define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR __ERR_STR(clEnqueueCopyImageToBuffer) #define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR __ERR_STR(clEnqueueCopyBufferToImage) #define __ENQUEUE_MAP_BUFFER_ERR __ERR_STR(clEnqueueMapBuffer) #define __ENQUEUE_MAP_IMAGE_ERR __ERR_STR(clEnqueueMapImage) #define __ENQUEUE_UNMAP_MEM_OBJECT_ERR __ERR_STR(clEnqueueUnMapMemObject) #define __ENQUEUE_NDRANGE_KERNEL_ERR __ERR_STR(clEnqueueNDRangeKernel) #define __ENQUEUE_TASK_ERR __ERR_STR(clEnqueueTask) #define __ENQUEUE_NATIVE_KERNEL __ERR_STR(clEnqueueNativeKernel) #if defined(CL_VERSION_1_2) #define __ENQUEUE_MIGRATE_MEM_OBJECTS_ERR __ERR_STR(clEnqueueMigrateMemObjects) #endif // #if defined(CL_VERSION_1_2) #define __ENQUEUE_ACQUIRE_GL_ERR __ERR_STR(clEnqueueAcquireGLObjects) #define __ENQUEUE_RELEASE_GL_ERR __ERR_STR(clEnqueueReleaseGLObjects) #define __RETAIN_ERR __ERR_STR(Retain Object) #define __RELEASE_ERR __ERR_STR(Release Object) #define __FLUSH_ERR __ERR_STR(clFlush) #define __FINISH_ERR __ERR_STR(clFinish) #define __VECTOR_CAPACITY_ERR __ERR_STR(Vector capacity error) /** * CL 1.2 version that uses device fission. */ #if defined(CL_VERSION_1_2) #define __CREATE_SUB_DEVICES __ERR_STR(clCreateSubDevices) #else #define __CREATE_SUB_DEVICES __ERR_STR(clCreateSubDevicesEXT) #endif // #if defined(CL_VERSION_1_2) /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) || (defined(CL_VERSION_1_1) && !defined(CL_VERSION_1_2)) #define __ENQUEUE_MARKER_ERR __ERR_STR(clEnqueueMarker) #define __ENQUEUE_WAIT_FOR_EVENTS_ERR __ERR_STR(clEnqueueWaitForEvents) #define __ENQUEUE_BARRIER_ERR __ERR_STR(clEnqueueBarrier) #define __UNLOAD_COMPILER_ERR __ERR_STR(clUnloadCompiler) #define __CREATE_GL_TEXTURE_2D_ERR __ERR_STR(clCreateFromGLTexture2D) #define __CREATE_GL_TEXTURE_3D_ERR __ERR_STR(clCreateFromGLTexture3D) #define __CREATE_IMAGE2D_ERR __ERR_STR(clCreateImage2D) #define __CREATE_IMAGE3D_ERR __ERR_STR(clCreateImage3D) #endif // #if defined(CL_VERSION_1_1) #endif // __CL_USER_OVERRIDE_ERROR_STRINGS //! \endcond /** * CL 1.2 marker and barrier commands */ #if defined(CL_VERSION_1_2) #define __ENQUEUE_MARKER_WAIT_LIST_ERR __ERR_STR(clEnqueueMarkerWithWaitList) #define __ENQUEUE_BARRIER_WAIT_LIST_ERR __ERR_STR(clEnqueueBarrierWithWaitList) #endif // #if defined(CL_VERSION_1_2) #if !defined(__USE_DEV_STRING) && !defined(__NO_STD_STRING) typedef std::string STRING_CLASS; #elif !defined(__USE_DEV_STRING) /*! \class string * \brief Simple string class, that provides a limited subset of std::string * functionality but avoids many of the issues that come with that class. * \note Deprecated. Please use std::string as default or * re-define the string class to match the std::string * interface by defining STRING_CLASS */ class CL_EXT_PREFIX__VERSION_1_1_DEPRECATED string CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED { private: ::size_t size_; char* str_; public: //! \brief Constructs an empty string, allocating no memory. string(void) : size_(0), str_(nullptr) { } /*! \brief Constructs a string populated from an arbitrary value of * specified size. * * An extra '\0' is added, in case none was contained in str. * * \param str the initial value of the string instance. Note that '\0' * characters receive no special treatment. If nullptr, * the string is left empty, with a size of 0. * * \param size the number of characters to copy from str. */ string(const char* str, ::size_t size) : size_(size), str_(nullptr) { if (size > 0) { str_ = new char[size_ + 1]; if (str_ != nullptr) { memcpy(str_, str, size_ * sizeof(char)); str_[size_] = '\0'; } else { size_ = 0; } } } /*! \brief Constructs a string populated from a null-terminated value. * * \param str the null-terminated initial value of the string instance. * If nullptr, the string is left empty, with a size of 0. */ string(const char* str) : size_(0), str_(nullptr) { if (str) { size_ = ::strlen(str); } if (size_ > 0) { str_ = new char[size_ + 1]; if (str_ != nullptr) { memcpy(str_, str, (size_ + 1) * sizeof(char)); } } } void resize(::size_t n) { if (size_ == n) { return; } if (n == 0) { if (str_) { delete[] str_; } str_ = nullptr; size_ = 0; } else { char* newString = new char[n + 1]; ::size_t copySize = n; if (size_ < n) { copySize = size_; } size_ = n; if (str_) { memcpy(newString, str_, (copySize + 1) * sizeof(char)); } if (copySize < size_) { memset(newString + copySize, 0, size_ - copySize); } newString[size_] = '\0'; delete[] str_; str_ = newString; } } const char& operator[](::size_t pos) const { return str_[pos]; } char& operator[](::size_t pos) { return str_[pos]; } /*! \brief Copies the value of another string to this one. * * \param rhs the string to copy. * * \returns a reference to the modified instance. */ string& operator=(const string& rhs) { if (this == &rhs) { return *this; } if (str_ != nullptr) { delete[] str_; str_ = nullptr; size_ = 0; } if (rhs.size_ == 0 || rhs.str_ == nullptr) { str_ = nullptr; size_ = 0; } else { str_ = new char[rhs.size_ + 1]; size_ = rhs.size_; if (str_ != nullptr) { memcpy(str_, rhs.str_, (size_ + 1) * sizeof(char)); } else { size_ = 0; } } return *this; } /*! \brief Constructs a string by copying the value of another instance. * * \param rhs the string to copy. */ string(const string& rhs) : size_(0), str_(nullptr) { *this = rhs; } //! \brief Destructor - frees memory used to hold the current value. ~string() { delete[] str_; str_ = nullptr; } //! \brief Queries the length of the string, excluding any added '\0's. ::size_t size(void) const { return size_; } //! \brief Queries the length of the string, excluding any added '\0's. ::size_t length(void) const { return size(); } /*! \brief Returns a pointer to the private copy held by this instance, * or "" if empty/unset. */ const char* c_str(void) const { return (str_) ? str_ : ""; } }; typedef cl::string STRING_CLASS; #endif // #elif !defined(__USE_DEV_STRING) #if !defined(__USE_DEV_VECTOR) && !defined(__NO_STD_VECTOR) #define VECTOR_CLASS std::vector #elif !defined(__USE_DEV_VECTOR) #define VECTOR_CLASS cl::vector #if !defined(__MAX_DEFAULT_VECTOR_SIZE) #define __MAX_DEFAULT_VECTOR_SIZE 10 #endif /*! \class vector * \brief Fixed sized vector implementation that mirroring * * \note Deprecated. Please use std::vector as default or * re-define the vector class to match the std::vector * interface by defining VECTOR_CLASS * \note Not recommended for use with custom objects as * current implementation will construct N elements * * std::vector functionality. * \brief Fixed sized vector compatible with std::vector. * * \note * This differs from std::vector<> not just in memory allocation, * but also in terms of when members are constructed, destroyed, * and assigned instead of being copy constructed. * * \param T type of element contained in the vector. * * \param N maximum size of the vector. */ template class CL_EXT_PREFIX__VERSION_1_1_DEPRECATED vector { private: T data_[N]; unsigned int size_; public: //! \brief Constructs an empty vector with no memory allocated. vector() : size_(static_cast(0)) { } //! \brief Deallocates the vector's memory and destroys all of its elements. ~vector() { clear(); } //! \brief Returns the number of elements currently contained. unsigned int size(void) const { return size_; } /*! \brief Empties the vector of all elements. * \note * This does not deallocate memory but will invoke destructors * on contained elements. */ void clear() { while (!empty()) { pop_back(); } } /*! \brief Appends an element after the last valid element. * Calling this on a vector that has reached capacity will throw an * exception if exceptions are enabled. */ void push_back(const T& x) { if (size() < N) { new (&data_[size_]) T(x); size_++; } else { detail::errHandler(CL_MEM_OBJECT_ALLOCATION_FAILURE, __VECTOR_CAPACITY_ERR); } } /*! \brief Removes the last valid element from the vector. * Calling this on an empty vector will throw an exception * if exceptions are enabled. */ void pop_back(void) { if (size_ != 0) { --size_; data_[size_].~T(); } else { detail::errHandler(CL_MEM_OBJECT_ALLOCATION_FAILURE, __VECTOR_CAPACITY_ERR); } } /*! \brief Constructs with a value copied from another. * * \param vec the vector to copy. */ vector(const vector& vec) : size_(vec.size_) { if (size_ != 0) { assign(vec.begin(), vec.end()); } } /*! \brief Constructs with a specified number of initial elements. * * \param size number of initial elements. * * \param val value of initial elements. */ vector(unsigned int size, const T& val = T()) : size_(0) { for (unsigned int i = 0; i < size; i++) { push_back(val); } } /*! \brief Overwrites the current content with that copied from another * instance. * * \param rhs vector to copy. * * \returns a reference to this. */ vector& operator=(const vector& rhs) { if (this == &rhs) { return *this; } if (rhs.size_ != 0) { assign(rhs.begin(), rhs.end()); } else { clear(); } return *this; } /*! \brief Tests equality against another instance. * * \param vec the vector against which to compare. */ bool operator==(vector& vec) { if (size() != vec.size()) { return false; } for (unsigned int i = 0; i < size(); ++i) { if (operator[](i) != vec[i]) { return false; } } return true; } //! \brief Conversion operator to T*. operator T*() { return data_; } //! \brief Conversion operator to const T*. operator const T*() const { return data_; } //! \brief Tests whether this instance has any elements. bool empty(void) const { return size_ == 0; } //! \brief Returns the maximum number of elements this instance can hold. unsigned int max_size(void) const { return N; } //! \brief Returns the maximum number of elements this instance can hold. unsigned int capacity() const { return N; } //! \brief Resizes the vector to the given size void resize(unsigned int newSize, T fill = T()) { if (newSize > N) { detail::errHandler(CL_MEM_OBJECT_ALLOCATION_FAILURE, __VECTOR_CAPACITY_ERR); } else { while (size_ < newSize) { new (&data_[size_]) T(fill); size_++; } while (size_ > newSize) { --size_; data_[size_].~T(); } } } /*! \brief Returns a reference to a given element. * * \param index which element to access. * * \note * The caller is responsible for ensuring index is >= 0 and < size(). */ T& operator[](int index) { return data_[index]; } /*! \brief Returns a const reference to a given element. * * \param index which element to access. * * \note * The caller is responsible for ensuring index is >= 0 and < size(). */ const T& operator[](int index) const { return data_[index]; } /*! \brief Assigns elements of the vector based on a source iterator range. * * \param start Beginning iterator of source range * \param end Enditerator of source range * * \note * Will throw an exception if exceptions are enabled and size exceeded. */ template void assign(I start, I end) { clear(); while (start != end) { push_back(*start); start++; } } /*! \class iterator * \brief Const iterator class for vectors */ class iterator { private: const vector* vec_; int index_; /** * Internal iterator constructor to capture reference * to the vector it iterates over rather than taking * the vector by copy. */ iterator(const vector& vec, int index) : vec_(&vec) { if (!vec.empty()) { index_ = index; } else { index_ = -1; } } public: iterator(void) : index_(-1), vec_(nullptr) { } iterator(const iterator& rhs) : vec_(rhs.vec_), index_(rhs.index_) { } ~iterator(void) {} static iterator begin(const cl::vector& vec) { iterator i(vec, 0); return i; } static iterator end(const cl::vector& vec) { iterator i(vec, vec.size()); return i; } bool operator==(iterator i) { return ((vec_ == i.vec_) && (index_ == i.index_)); } bool operator!=(iterator i) { return (!(*this == i)); } iterator& operator++() { ++index_; return *this; } iterator operator++(int) { iterator retVal(*this); ++index_; return retVal; } iterator& operator--() { --index_; return *this; } iterator operator--(int) { iterator retVal(*this); --index_; return retVal; } const T& operator*() const { return (*vec_)[index_]; } }; iterator begin(void) { return iterator::begin(*this); } iterator begin(void) const { return iterator::begin(*this); } iterator end(void) { return iterator::end(*this); } iterator end(void) const { return iterator::end(*this); } T& front(void) { return data_[0]; } T& back(void) { return data_[size_]; } const T& front(void) const { return data_[0]; } const T& back(void) const { return data_[size_ - 1]; } } CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; #endif // #if !defined(__USE_DEV_VECTOR) && !defined(__NO_STD_VECTOR) namespace detail { #define __DEFAULT_NOT_INITIALIZED 1 #define __DEFAULT_BEING_INITIALIZED 2 #define __DEFAULT_INITIALIZED 4 /* * Compare and exchange primitives are needed for handling of defaults */ #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED inline int compare_exchange(std::atomic* dest, int exchange, int comparand) #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED inline int compare_exchange(volatile int* dest, int exchange, int comparand) #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED { #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED std::atomic_compare_exchange_strong(dest, &comparand, exchange); return comparand; #elif _MSC_VER return (int)(_InterlockedCompareExchange( (volatile long*)dest, (long)exchange, (long)comparand)); #else // !_MSC_VER && !CL_HPP_CPP11_ATOMICS_SUPPORTED return (__sync_val_compare_and_swap( dest, comparand, exchange)); #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED } inline void fence() { #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED std::atomic_thread_fence(std::memory_order_seq_cst); #elif _MSC_VER // !CL_HPP_CPP11_ATOMICS_SUPPORTED _ReadWriteBarrier(); #else // !_MSC_VER && !CL_HPP_CPP11_ATOMICS_SUPPORTED __sync_synchronize(); #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED } } // namespace detail /*! \brief class used to interface between C++ and * OpenCL C calls that require arrays of size_t values, whose * size is known statically. */ template class size_t { private: ::size_t data_[N]; public: //! \brief Initialize size_t to all 0s size_t() { for (int i = 0; i < N; ++i) { data_[i] = 0; } } ::size_t& operator[](int index) { return data_[index]; } const ::size_t& operator[](int index) const { return data_[index]; } //! \brief Conversion operator to T*. operator ::size_t*() { return data_; } //! \brief Conversion operator to const T*. operator const ::size_t*() const { return data_; } }; namespace detail { // Generic getInfoHelper. The final parameter is used to guide overload // resolution: the actual parameter passed is an int, which makes this // a worse conversion sequence than a specialization that declares the // parameter as an int. template inline cl_int getInfoHelper(Functor f, cl_uint name, T* param, long) { return f(name, sizeof(T), param, nullptr); } // Specialized getInfoHelper for VECTOR_CLASS params template inline cl_int getInfoHelper(Func f, cl_uint name, VECTOR_CLASS* param, long) { ::size_t required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } T* value = (T*)alloca(required); err = f(name, required, value, nullptr); if (err != CL_SUCCESS) { return err; } param->assign(&value[0], &value[required / sizeof(T)]); return CL_SUCCESS; } /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper(Func f, cl_uint name, VECTOR_CLASS* param, int, typename T::cl_type = nullptr) { ::size_t required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } typename T::cl_type* value = (typename T::cl_type*)alloca(required); err = f(name, required, value, nullptr); if (err != CL_SUCCESS) { return err; } ::size_t elements = required / sizeof(typename T::cl_type); param->assign(&value[0], &value[elements]); for (::size_t i = 0; i < elements; i++) { if (value[i] != nullptr) { err = (*param)[i].retain(); if (err != CL_SUCCESS) { return err; } } } return CL_SUCCESS; } // Specialized for getInfo template inline cl_int getInfoHelper(Func f, cl_uint name, VECTOR_CLASS* param, int) { cl_int err = f(name, param->size() * sizeof(char*), &(*param)[0], nullptr); if (err != CL_SUCCESS) { return err; } return CL_SUCCESS; } // Specialized GetInfoHelper for STRING_CLASS params template inline cl_int getInfoHelper(Func f, cl_uint name, STRING_CLASS* param, long) { #if defined(__NO_STD_VECTOR) || defined(__NO_STD_STRING) ::size_t required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } char* value = (char*)alloca(required); err = f(name, required, value, nullptr); if (err != CL_SUCCESS) { return err; } *param = value; return CL_SUCCESS; #else ::size_t required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } // std::string has a constant data member // a char vector does not VECTOR_CLASS value(required); err = f(name, required, value.data(), nullptr); if (err != CL_SUCCESS) { return err; } if (param) { param->assign(value.begin(), value.end()); } #endif return CL_SUCCESS; } // Specialized GetInfoHelper for cl::size_t params template inline cl_int getInfoHelper(Func f, cl_uint name, size_t* param, long) { ::size_t required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } ::size_t* value = (::size_t*)alloca(required); err = f(name, required, value, nullptr); if (err != CL_SUCCESS) { return err; } for (int i = 0; i < N; ++i) { (*param)[i] = value[i]; } return CL_SUCCESS; } template struct ReferenceHandler; /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper(Func f, cl_uint name, T* param, int, typename T::cl_type = nullptr) { typename T::cl_type value; cl_int err = f(name, sizeof(value), &value, nullptr); if (err != CL_SUCCESS) { return err; } *param = value; if (value != nullptr) { err = param->retain(); if (err != CL_SUCCESS) { return err; } } return CL_SUCCESS; } #define __PARAM_NAME_INFO_1_0(F) \ F(cl_platform_info, CL_PLATFORM_PROFILE, STRING_CLASS) \ F(cl_platform_info, CL_PLATFORM_VERSION, STRING_CLASS) \ F(cl_platform_info, CL_PLATFORM_NAME, STRING_CLASS) \ F(cl_platform_info, CL_PLATFORM_VENDOR, STRING_CLASS) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS, STRING_CLASS) \ \ F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, ::size_t) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, VECTOR_CLASS< ::size_t>) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, ::size_t) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, ::size_t) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, ::size_t) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, ::size_t) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, ::size_t) \ F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, ::size_t) \ F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, ::size_t) \ F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) \ F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ F(cl_device_info, CL_DEVICE_NAME, STRING_CLASS) \ F(cl_device_info, CL_DEVICE_VENDOR, STRING_CLASS) \ F(cl_device_info, CL_DRIVER_VERSION, STRING_CLASS) \ F(cl_device_info, CL_DEVICE_PROFILE, STRING_CLASS) \ F(cl_device_info, CL_DEVICE_VERSION, STRING_CLASS) \ F(cl_device_info, CL_DEVICE_EXTENSIONS, STRING_CLASS) \ \ F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ F(cl_context_info, CL_CONTEXT_DEVICES, VECTOR_CLASS) \ F(cl_context_info, CL_CONTEXT_PROPERTIES, VECTOR_CLASS) \ \ F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_int) \ \ F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ \ F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ F(cl_mem_info, CL_MEM_SIZE, ::size_t) \ F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ \ F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, ::size_t) \ F(cl_image_info, CL_IMAGE_ROW_PITCH, ::size_t) \ F(cl_image_info, CL_IMAGE_SLICE_PITCH, ::size_t) \ F(cl_image_info, CL_IMAGE_WIDTH, ::size_t) \ F(cl_image_info, CL_IMAGE_HEIGHT, ::size_t) \ F(cl_image_info, CL_IMAGE_DEPTH, ::size_t) \ \ F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_bool) \ F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_addressing_mode) \ F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_filter_mode) \ \ F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ F(cl_program_info, CL_PROGRAM_DEVICES, VECTOR_CLASS) \ F(cl_program_info, CL_PROGRAM_SOURCE, STRING_CLASS) \ F(cl_program_info, CL_PROGRAM_BINARY_SIZES, VECTOR_CLASS< ::size_t>) \ F(cl_program_info, CL_PROGRAM_BINARIES, VECTOR_CLASS) \ \ F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, STRING_CLASS) \ F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, STRING_CLASS) \ \ F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, STRING_CLASS) \ F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ \ F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, ::size_t) \ F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::size_t<3>) \ F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ \ F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) #if defined(CL_VERSION_1_1) #define __PARAM_NAME_INFO_1_1(F) \ F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) \ F(cl_device_info, CL_DEVICE_OPENCL_C_VERSION, STRING_CLASS) \ \ F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ F(cl_mem_info, CL_MEM_OFFSET, ::size_t) \ \ F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, ::size_t) \ F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ \ F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) #endif // CL_VERSION_1_1 #if defined(CL_VERSION_1_2) #define __PARAM_NAME_INFO_1_2(F) \ F(cl_image_info, CL_IMAGE_BUFFER, cl::Buffer) \ \ F(cl_program_info, CL_PROGRAM_NUM_KERNELS, ::size_t) \ F(cl_program_info, CL_PROGRAM_KERNEL_NAMES, STRING_CLASS) \ \ F(cl_program_build_info, CL_PROGRAM_BINARY_TYPE, cl_program_binary_type) \ \ F(cl_kernel_info, CL_KERNEL_ATTRIBUTES, STRING_CLASS) \ \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ADDRESS_QUALIFIER, cl_kernel_arg_address_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ACCESS_QUALIFIER, cl_kernel_arg_access_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_NAME, STRING_CLASS) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_NAME, STRING_CLASS) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_QUALIFIER, cl_kernel_arg_type_qualifier) \ \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE, cl_device_id) \ F(cl_device_info, CL_DEVICE_PARTITION_PROPERTIES, VECTOR_CLASS) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPE, VECTOR_CLASS) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, ::size_t) \ F(cl_device_info, CL_DEVICE_PARTITION_AFFINITY_DOMAIN, cl_device_affinity_domain) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS, STRING_CLASS) #endif // #if defined(CL_VERSION_1_2) #if defined(USE_CL_DEVICE_FISSION) #define __PARAM_NAME_DEVICE_FISSION(F) \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl_device_id) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, VECTOR_CLASS) \ F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, VECTOR_CLASS) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT, cl_uint) \ F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, VECTOR_CLASS) #endif // USE_CL_DEVICE_FISSION template struct param_traits { }; #define __CL_DECLARE_PARAM_TRAITS(token, param_name, T) \ struct token; \ template <> \ struct param_traits \ { \ enum \ { \ value = param_name \ }; \ typedef T param_type; \ }; __PARAM_NAME_INFO_1_0(__CL_DECLARE_PARAM_TRAITS) #if defined(CL_VERSION_1_1) __PARAM_NAME_INFO_1_1(__CL_DECLARE_PARAM_TRAITS) #endif // CL_VERSION_1_1 #if defined(CL_VERSION_1_2) __PARAM_NAME_INFO_1_2(__CL_DECLARE_PARAM_TRAITS) #endif // CL_VERSION_1_1 #if defined(USE_CL_DEVICE_FISSION) __PARAM_NAME_DEVICE_FISSION(__CL_DECLARE_PARAM_TRAITS); #endif // USE_CL_DEVICE_FISSION #ifdef CL_PLATFORM_ICD_SUFFIX_KHR __CL_DECLARE_PARAM_TRAITS(cl_platform_info, CL_PLATFORM_ICD_SUFFIX_KHR, STRING_CLASS) #endif #ifdef CL_DEVICE_PROFILING_TIMER_OFFSET_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, cl_ulong) #endif #ifdef CL_DEVICE_GLOBAL_FREE_MEMORY_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_GLOBAL_FREE_MEMORY_AMD, VECTOR_CLASS< ::size_t>) #endif #ifdef CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_WIDTH_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_SIMD_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_WAVEFRONT_WIDTH_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_WAVEFRONT_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_BANKS_AMD __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_LOCAL_MEM_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, cl_uint) #endif #ifdef CL_DEVICE_REGISTERS_PER_BLOCK_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_REGISTERS_PER_BLOCK_NV, cl_uint) #endif #ifdef CL_DEVICE_WARP_SIZE_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_WARP_SIZE_NV, cl_uint) #endif #ifdef CL_DEVICE_GPU_OVERLAP_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_GPU_OVERLAP_NV, cl_bool) #endif #ifdef CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV, cl_bool) #endif #ifdef CL_DEVICE_INTEGRATED_MEMORY_NV __CL_DECLARE_PARAM_TRAITS(cl_device_info, CL_DEVICE_INTEGRATED_MEMORY_NV, cl_bool) #endif // Convenience functions template inline cl_int getInfo(Func f, cl_uint name, T* param) { return getInfoHelper(f, name, param, 0); } template struct GetInfoFunctor0 { Func f_; const Arg0& arg0_; cl_int operator()( cl_uint param, ::size_t size, void* value, ::size_t* size_ret) { return f_(arg0_, param, size, value, size_ret); } }; template struct GetInfoFunctor1 { Func f_; const Arg0& arg0_; const Arg1& arg1_; cl_int operator()( cl_uint param, ::size_t size, void* value, ::size_t* size_ret) { return f_(arg0_, arg1_, param, size, value, size_ret); } }; template inline cl_int getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) { GetInfoFunctor0 f0 = {f, arg0}; return getInfoHelper(f0, name, param, 0); } template inline cl_int getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) { GetInfoFunctor1 f0 = {f, arg0, arg1}; return getInfoHelper(f0, name, param, 0); } template struct ReferenceHandler { }; #if defined(CL_VERSION_1_2) /** * OpenCL 1.2 devices do have retain/release. */ template <> struct ReferenceHandler { /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int retain(cl_device_id device) { return ::clRetainDevice(device); } /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int release(cl_device_id device) { return ::clReleaseDevice(device); } }; #else // #if defined(CL_VERSION_1_2) /** * OpenCL 1.1 devices do not have retain/release. */ template <> struct ReferenceHandler { // cl_device_id does not have retain(). static cl_int retain(cl_device_id) { return CL_SUCCESS; } // cl_device_id does not have release(). static cl_int release(cl_device_id) { return CL_SUCCESS; } }; #endif // #if defined(CL_VERSION_1_2) template <> struct ReferenceHandler { // cl_platform_id does not have retain(). static cl_int retain(cl_platform_id) { return CL_SUCCESS; } // cl_platform_id does not have release(). static cl_int release(cl_platform_id) { return CL_SUCCESS; } }; template <> struct ReferenceHandler { static cl_int retain(cl_context context) { return ::clRetainContext(context); } static cl_int release(cl_context context) { return ::clReleaseContext(context); } }; template <> struct ReferenceHandler { static cl_int retain(cl_command_queue queue) { return ::clRetainCommandQueue(queue); } static cl_int release(cl_command_queue queue) { return ::clReleaseCommandQueue(queue); } }; template <> struct ReferenceHandler { static cl_int retain(cl_mem memory) { return ::clRetainMemObject(memory); } static cl_int release(cl_mem memory) { return ::clReleaseMemObject(memory); } }; template <> struct ReferenceHandler { static cl_int retain(cl_sampler sampler) { return ::clRetainSampler(sampler); } static cl_int release(cl_sampler sampler) { return ::clReleaseSampler(sampler); } }; template <> struct ReferenceHandler { static cl_int retain(cl_program program) { return ::clRetainProgram(program); } static cl_int release(cl_program program) { return ::clReleaseProgram(program); } }; template <> struct ReferenceHandler { static cl_int retain(cl_kernel kernel) { return ::clRetainKernel(kernel); } static cl_int release(cl_kernel kernel) { return ::clReleaseKernel(kernel); } }; template <> struct ReferenceHandler { static cl_int retain(cl_event event) { return ::clRetainEvent(event); } static cl_int release(cl_event event) { return ::clReleaseEvent(event); } }; // Extracts version number with major in the upper 16 bits, minor in the lower 16 static cl_uint getVersion(const char* versionInfo) { int highVersion = 0; int lowVersion = 0; int index = 7; while (versionInfo[index] != '.') { highVersion *= 10; highVersion += versionInfo[index] - '0'; ++index; } ++index; while (versionInfo[index] != ' ' && versionInfo[index] != '\0') { lowVersion *= 10; lowVersion += versionInfo[index] - '0'; ++index; } return (highVersion << 16) | lowVersion; } static cl_uint getPlatformVersion(cl_platform_id platform) { ::size_t size = 0; clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 0, nullptr, &size); char* versionInfo = (char*)alloca(size); clGetPlatformInfo(platform, CL_PLATFORM_VERSION, size, &versionInfo[0], &size); return getVersion(versionInfo); } static cl_uint getDevicePlatformVersion(cl_device_id device) { cl_platform_id platform; clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(platform), &platform, nullptr); return getPlatformVersion(platform); } #if defined(CL_VERSION_1_2) && defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) static cl_uint getContextPlatformVersion(cl_context context) { // The platform cannot be queried directly, so we first have to grab a // device and obtain its context ::size_t size = 0; clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, nullptr, &size); if (size == 0) return 0; cl_device_id* devices = (cl_device_id*)alloca(size); clGetContextInfo(context, CL_CONTEXT_DEVICES, size, devices, nullptr); return getDevicePlatformVersion(devices[0]); } #endif // #if defined(CL_VERSION_1_2) && defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) template class Wrapper { public: typedef T cl_type; protected: cl_type object_; public: Wrapper() : object_(nullptr) {} Wrapper(const cl_type& obj) : object_(obj) {} ~Wrapper() { if (object_ != nullptr) { release(); } } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; if (object_ != nullptr) { detail::errHandler(retain(), __RETAIN_ERR); } } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT { object_ = rhs.object_; rhs.object_ = nullptr; } #endif Wrapper& operator=(const Wrapper& rhs) { if (this != &rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs.object_; if (object_ != nullptr) { detail::errHandler(retain(), __RETAIN_ERR); } } return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) Wrapper& operator=(Wrapper&& rhs) { if (this != &rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs.object_; rhs.object_ = nullptr; } return *this; } #endif Wrapper& operator=(const cl_type& rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs; return *this; } cl_type operator()() const { return object_; } cl_type& operator()() { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); cl_int retain() const { return ReferenceHandler::retain(object_); } cl_int release() const { return ReferenceHandler::release(object_); } }; template <> class Wrapper { public: typedef cl_device_id cl_type; protected: cl_type object_; bool referenceCountable_; static bool isReferenceCountable(cl_device_id device) { bool retVal = false; if (device != nullptr) { int version = getDevicePlatformVersion(device); if (version > ((1 << 16) + 1)) { retVal = true; } } return retVal; } public: Wrapper() : object_(nullptr), referenceCountable_(false) { } Wrapper(const cl_type& obj) : object_(obj), referenceCountable_(false) { referenceCountable_ = isReferenceCountable(obj); } ~Wrapper() { if (object_ != nullptr) { release(); } } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; referenceCountable_ = isReferenceCountable(object_); if (object_ != nullptr) { detail::errHandler(retain(), __RETAIN_ERR); } } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT { object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = nullptr; rhs.referenceCountable_ = false; } #endif Wrapper& operator=(const Wrapper& rhs) { if (this != &rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; if (object_ != nullptr) { detail::errHandler(retain(), __RETAIN_ERR); } } return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) Wrapper& operator=(Wrapper&& rhs) { if (this != &rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = nullptr; rhs.referenceCountable_ = false; } return *this; } #endif Wrapper& operator=(const cl_type& rhs) { if (object_ != nullptr) { detail::errHandler(release(), __RELEASE_ERR); } object_ = rhs; referenceCountable_ = isReferenceCountable(object_); return *this; } cl_type operator()() const { return object_; } cl_type& operator()() { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); template friend inline cl_int getInfoHelper(Func, cl_uint, VECTOR_CLASS*, int, typename U::cl_type); cl_int retain() const { if (referenceCountable_) { return ReferenceHandler::retain(object_); } else { return CL_SUCCESS; } } cl_int release() const { if (referenceCountable_) { return ReferenceHandler::release(object_); } else { return CL_SUCCESS; } } }; } // namespace detail //! \endcond /*! \stuct ImageFormat * \brief Adds constructors and member functions for cl_image_format. * * \see cl_image_format */ struct ImageFormat : public cl_image_format { //! \brief Default constructor - performs no initialization. ImageFormat() {} //! \brief Initializing constructor. ImageFormat(cl_channel_order order, cl_channel_type type) { image_channel_order = order; image_channel_data_type = type; } //! \brief Assignment operator. ImageFormat& operator=(const ImageFormat& rhs) { if (this != &rhs) { this->image_channel_data_type = rhs.image_channel_data_type; this->image_channel_order = rhs.image_channel_order; } return *this; } }; /*! \brief Class interface for cl_device_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_device_id */ class Device : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Device() : detail::Wrapper() {} /*! \brief Constructor from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ __CL_EXPLICIT_CONSTRUCTORS Device(const cl_device_id& device) : detail::Wrapper(device) {} /*! \brief Returns the first device on the default context. * * \see Context::getDefault() */ static Device getDefault(cl_int* err = nullptr); /*! \brief Assignment operator from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ Device& operator=(const cl_device_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Device(const Device& dev) : detail::Wrapper(dev) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Device& operator=(const Device& dev) { detail::Wrapper::operator=(dev); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Device(Device&& dev) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(dev)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Device& operator=(Device&& dev) { detail::Wrapper::operator=(std::move(dev)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) //! \brief Wrapper for clGetDeviceInfo(). template cl_int getInfo(cl_device_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetDeviceInfo, object_, name, param), __GET_DEVICE_INFO_ERR); } //! \brief Wrapper for clGetDeviceInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_device_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /** * CL 1.2 version */ #if defined(CL_VERSION_1_2) //! \brief Wrapper for clCreateSubDevicesEXT(). cl_int createSubDevices( const cl_device_partition_property* properties, VECTOR_CLASS* devices) { cl_uint n = 0; cl_int err = clCreateSubDevices(object_, properties, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES); } cl_device_id* ids = (cl_device_id*)alloca(n * sizeof(cl_device_id)); err = clCreateSubDevices(object_, properties, n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES); } devices->assign(&ids[0], &ids[n]); return CL_SUCCESS; } #endif // #if defined(CL_VERSION_1_2) /** * CL 1.1 version that uses device fission. */ #if defined(CL_VERSION_1_1) #if defined(USE_CL_DEVICE_FISSION) cl_int createSubDevices( const cl_device_partition_property_ext* properties, VECTOR_CLASS* devices) { typedef CL_API_ENTRY cl_int(CL_API_CALL * PFN_clCreateSubDevicesEXT)( cl_device_id /*in_device*/, const cl_device_partition_property_ext* /* properties */, cl_uint /*num_entries*/, cl_device_id* /*out_devices*/, cl_uint* /*num_devices*/) CL_EXT_SUFFIX__VERSION_1_1; static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = nullptr; __INIT_CL_EXT_FCN_PTR(clCreateSubDevicesEXT); cl_uint n = 0; cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES); } cl_device_id* ids = (cl_device_id*)alloca(n * sizeof(cl_device_id)); err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES); } devices->assign(&ids[0], &ids[n]); return CL_SUCCESS; } #endif // #if defined(USE_CL_DEVICE_FISSION) #endif // #if defined(CL_VERSION_1_1) }; /*! \brief Class interface for cl_platform_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_platform_id */ class Platform : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Platform() : detail::Wrapper() {} /*! \brief Constructor from cl_platform_id. * * This simply copies the platform ID value, which is an inexpensive operation. */ __CL_EXPLICIT_CONSTRUCTORS Platform(const cl_platform_id& platform) : detail::Wrapper(platform) {} /*! \brief Assignment operator from cl_platform_id. * * This simply copies the platform ID value, which is an inexpensive operation. */ Platform& operator=(const cl_platform_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetPlatformInfo(). cl_int getInfo(cl_platform_info name, STRING_CLASS* param) const { return detail::errHandler( detail::getInfo(&::clGetPlatformInfo, object_, name, param), __GET_PLATFORM_INFO_ERR); } //! \brief Wrapper for clGetPlatformInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_platform_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Gets a list of devices for this platform. * * Wraps clGetDeviceIDs(). */ cl_int getDevices( cl_device_type type, VECTOR_CLASS* devices) const { cl_uint n = 0; if (devices == nullptr) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } cl_int err = ::clGetDeviceIDs(object_, type, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } cl_device_id* ids = (cl_device_id*)alloca(n * sizeof(cl_device_id)); err = ::clGetDeviceIDs(object_, type, n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } devices->assign(&ids[0], &ids[n]); return CL_SUCCESS; } #if defined(USE_DX_INTEROP) /*! \brief Get the list of available D3D10 devices. * * \param d3d_device_source. * * \param d3d_object. * * \param d3d_device_set. * * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device * values returned in devices can be used to identify a specific OpenCL * device. If \a devices argument is nullptr, this argument is ignored. * * \return One of the following values: * - CL_SUCCESS if the function is executed successfully. * * The application can query specific capabilities of the OpenCL device(s) * returned by cl::getDevices. This can be used by the application to * determine which device(s) to use. * * \note In the case that exceptions are enabled and a return value * other than CL_SUCCESS is generated, then cl::Error exception is * generated. */ cl_int getDevices( cl_d3d10_device_source_khr d3d_device_source, void* d3d_object, cl_d3d10_device_set_khr d3d_device_set, VECTOR_CLASS* devices) const { typedef CL_API_ENTRY cl_int(CL_API_CALL * PFN_clGetDeviceIDsFromD3D10KHR)( cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source, void* d3d_object, cl_d3d10_device_set_khr d3d_device_set, cl_uint num_entries, cl_device_id* devices, cl_uint* num_devices); if (devices == nullptr) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = nullptr; __INIT_CL_EXT_FCN_PTR_PLATFORM(object_, clGetDeviceIDsFromD3D10KHR); cl_uint n = 0; cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } cl_device_id* ids = (cl_device_id*)alloca(n * sizeof(cl_device_id)); err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } devices->assign(&ids[0], &ids[n]); return CL_SUCCESS; } #endif /*! \brief Gets a list of available platforms. * * Wraps clGetPlatformIDs(). */ static cl_int get( VECTOR_CLASS* platforms) { cl_uint n = 0; if (platforms == nullptr) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_PLATFORM_IDS_ERR); } cl_int err = ::clGetPlatformIDs(0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } cl_platform_id* ids = (cl_platform_id*)alloca( n * sizeof(cl_platform_id)); err = ::clGetPlatformIDs(n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } platforms->assign(&ids[0], &ids[n]); return CL_SUCCESS; } /*! \brief Gets the first available platform. * * Wraps clGetPlatformIDs(), returning the first result. */ static cl_int get( Platform* platform) { cl_uint n = 0; if (platform == nullptr) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_PLATFORM_IDS_ERR); } cl_int err = ::clGetPlatformIDs(0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } cl_platform_id* ids = (cl_platform_id*)alloca( n * sizeof(cl_platform_id)); err = ::clGetPlatformIDs(n, ids, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } *platform = ids[0]; return CL_SUCCESS; } /*! \brief Gets the first available platform, returning it by value. * * Wraps clGetPlatformIDs(), returning the first result. */ static Platform get( cl_int* errResult = nullptr) { Platform platform; cl_uint n = 0; cl_int err = ::clGetPlatformIDs(0, nullptr, &n); if (err != CL_SUCCESS) { detail::errHandler(err, __GET_PLATFORM_IDS_ERR); if (errResult != nullptr) { *errResult = err; } return Platform(); } cl_platform_id* ids = (cl_platform_id*)alloca( n * sizeof(cl_platform_id)); err = ::clGetPlatformIDs(n, ids, nullptr); if (err != CL_SUCCESS) { detail::errHandler(err, __GET_PLATFORM_IDS_ERR); if (errResult != nullptr) { *errResult = err; } return Platform(); } return Platform(ids[0]); } static Platform getDefault( cl_int* errResult = nullptr) { return get(errResult); } #if defined(CL_VERSION_1_2) //! \brief Wrapper for clUnloadCompiler(). cl_int unloadCompiler() { return ::clUnloadPlatformCompiler(object_); } #endif // #if defined(CL_VERSION_1_2) }; // class Platform /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) || (defined(CL_VERSION_1_1) && !defined(CL_VERSION_1_2)) /** * Unload the OpenCL compiler. * \note Deprecated for OpenCL 1.2. Use Platform::unloadCompiler instead. */ inline CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int UnloadCompiler() CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; inline cl_int UnloadCompiler() { return ::clUnloadCompiler(); } #endif // #if defined(CL_VERSION_1_1) /*! \brief Class interface for cl_context. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_context as the original. For details, see * clRetainContext() and clReleaseContext(). * * \see cl_context */ class Context : public detail::Wrapper { private: #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED static std::atomic default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED static volatile int default_initialized_; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED static Context default_; static volatile cl_int default_error_; public: /*! \brief Constructs a context including a list of specified devices. * * Wraps clCreateContext(). */ Context( const VECTOR_CLASS& devices, cl_context_properties* properties = nullptr, void(CL_CALLBACK* notifyFptr)( const char*, const void*, ::size_t, void*) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; ::size_t numDevices = devices.size(); cl_device_id* deviceIDs = (cl_device_id*)alloca(numDevices * sizeof(cl_device_id)); for (::size_t deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateContext( properties, (cl_uint)numDevices, deviceIDs, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != nullptr) { *err = error; } } Context( const Device& device, cl_context_properties* properties = nullptr, void(CL_CALLBACK* notifyFptr)( const char*, const void*, ::size_t, void*) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; cl_device_id deviceID = device(); object_ = ::clCreateContext( properties, 1, &deviceID, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructs a context including all or a subset of devices of a specified type. * * Wraps clCreateContextFromType(). */ Context( cl_device_type type, cl_context_properties* properties = nullptr, void(CL_CALLBACK* notifyFptr)( const char*, const void*, ::size_t, void*) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; #if !defined(__APPLE__) && !defined(__MACOS) cl_context_properties prop[4] = {CL_CONTEXT_PLATFORM, 0, 0, 0}; if (properties == nullptr) { // Get a valid platform ID as we cannot send in a blank one VECTOR_CLASS platforms; error = Platform::get(&platforms); if (error != CL_SUCCESS) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } return; } // Check the platforms we found for a device of our specified type cl_context_properties platform_id = 0; for (unsigned int i = 0; i < platforms.size(); i++) { VECTOR_CLASS devices; #if defined(__CL_ENABLE_EXCEPTIONS) try { #endif error = platforms[i].getDevices(type, &devices); #if defined(__CL_ENABLE_EXCEPTIONS) } catch (Error) { } // Catch if exceptions are enabled as we don't want to exit if first platform has no devices of type // We do error checking next anyway, and can throw there if needed #endif // Only squash CL_SUCCESS and CL_DEVICE_NOT_FOUND if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } } if (devices.size() > 0) { platform_id = (cl_context_properties)platforms[i](); break; } } if (platform_id == 0) { detail::errHandler(CL_DEVICE_NOT_FOUND, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = CL_DEVICE_NOT_FOUND; } return; } prop[1] = platform_id; properties = &prop[0]; } #endif object_ = ::clCreateContextFromType( properties, type, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Context(const Context& ctx) : detail::Wrapper(ctx) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Context& operator=(const Context& ctx) { detail::Wrapper::operator=(ctx); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Context(Context&& ctx) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(ctx)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Context& operator=(Context&& ctx) { detail::Wrapper::operator=(std::move(ctx)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Returns a singleton context including all devices of CL_DEVICE_TYPE_DEFAULT. * * \note All calls to this function return the same cl_context as the first. */ static Context getDefault(cl_int* err = nullptr) { int state = detail::compare_exchange( &default_initialized_, __DEFAULT_BEING_INITIALIZED, __DEFAULT_NOT_INITIALIZED); if (state & __DEFAULT_INITIALIZED) { if (err != nullptr) { *err = default_error_; } return default_; } if (state & __DEFAULT_BEING_INITIALIZED) { // Assume writes will propagate eventually... while (default_initialized_ != __DEFAULT_INITIALIZED) { detail::fence(); } if (err != nullptr) { *err = default_error_; } return default_; } cl_int error; default_ = Context( CL_DEVICE_TYPE_DEFAULT, nullptr, nullptr, nullptr, &error); detail::fence(); default_error_ = error; // Assume writes will propagate eventually... default_initialized_ = __DEFAULT_INITIALIZED; detail::fence(); if (err != nullptr) { *err = default_error_; } return default_; } //! \brief Default constructor - initializes to nullptr. Context() : detail::Wrapper() {} /*! \brief Constructor from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the cl_context * into the new Context object. */ __CL_EXPLICIT_CONSTRUCTORS Context(const cl_context& context) : detail::Wrapper(context) {} /*! \brief Assignment operator from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseContext() on the value previously held by this instance. */ Context& operator=(const cl_context& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetContextInfo(). template cl_int getInfo(cl_context_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetContextInfo, object_, name, param), __GET_CONTEXT_INFO_ERR); } //! \brief Wrapper for clGetContextInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_context_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Gets a list of supported image formats. * * Wraps clGetSupportedImageFormats(). */ cl_int getSupportedImageFormats( cl_mem_flags flags, cl_mem_object_type type, VECTOR_CLASS* formats) const { cl_uint numEntries; if (!formats) { return CL_SUCCESS; } cl_int err = ::clGetSupportedImageFormats( object_, flags, type, 0, nullptr, &numEntries); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } if (numEntries > 0) { ImageFormat* value = (ImageFormat*) alloca(numEntries * sizeof(ImageFormat)); err = ::clGetSupportedImageFormats( object_, flags, type, numEntries, (cl_image_format*)value, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } formats->assign(&value[0], &value[numEntries]); } else { formats->clear(); } return CL_SUCCESS; } }; inline Device Device::getDefault(cl_int* err) { cl_int error; Device device; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } } else { device = context.getInfo()[0]; if (err != nullptr) { *err = CL_SUCCESS; } } return device; } #ifdef _WIN32 #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) std::atomic Context::default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) volatile int Context::default_initialized_ = __DEFAULT_NOT_INITIALIZED; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) Context Context::default_; __declspec(selectany) volatile cl_int Context::default_error_ = CL_SUCCESS; #else // !_WIN32 #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) std::atomic Context::default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) volatile int Context::default_initialized_ = __DEFAULT_NOT_INITIALIZED; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) Context Context::default_; __attribute__((weak)) volatile cl_int Context::default_error_ = CL_SUCCESS; #endif // !_WIN32 /*! \brief Class interface for cl_event. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_event as the original. For details, see * clRetainEvent() and clReleaseEvent(). * * \see cl_event */ class Event : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Event() : detail::Wrapper() {} /*! \brief Constructor from cl_event - takes ownership. * * This effectively transfers ownership of a refcount on the cl_event * into the new Event object. */ __CL_EXPLICIT_CONSTRUCTORS Event(const cl_event& event) : detail::Wrapper(event) {} /*! \brief Assignment operator from cl_event - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseEvent() on the value previously held by this instance. */ Event& operator=(const cl_event& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetEventInfo(). template cl_int getInfo(cl_event_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetEventInfo, object_, name, param), __GET_EVENT_INFO_ERR); } //! \brief Wrapper for clGetEventInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_event_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } //! \brief Wrapper for clGetEventProfilingInfo(). template cl_int getProfilingInfo(cl_profiling_info name, T* param) const { return detail::errHandler(detail::getInfo( &::clGetEventProfilingInfo, object_, name, param), __GET_EVENT_PROFILE_INFO_ERR); } //! \brief Wrapper for clGetEventProfilingInfo() that returns by value. template typename detail::param_traits::param_type getProfilingInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_profiling_info, name>::param_type param; cl_int result = getProfilingInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Blocks the calling thread until this event completes. * * Wraps clWaitForEvents(). */ cl_int wait() const { return detail::errHandler( ::clWaitForEvents(1, &object_), __WAIT_FOR_EVENTS_ERR); } #if defined(CL_VERSION_1_1) /*! \brief Registers a user callback function for a specific command execution status. * * Wraps clSetEventCallback(). */ cl_int setCallback( cl_int type, void(CL_CALLBACK* pfn_notify)(cl_event, cl_int, void*), void* user_data = nullptr) { return detail::errHandler( ::clSetEventCallback( object_, type, pfn_notify, user_data), __SET_EVENT_CALLBACK_ERR); } #endif /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ static cl_int waitForEvents(const VECTOR_CLASS& events) { return detail::errHandler( ::clWaitForEvents( (cl_uint)events.size(), (events.size() > 0) ? (cl_event*)&events.front() : nullptr), __WAIT_FOR_EVENTS_ERR); } }; #if defined(CL_VERSION_1_1) /*! \brief Class interface for user events (a subset of cl_event's). * * See Event for details about copy semantics, etc. */ class UserEvent : public Event { public: /*! \brief Constructs a user event on a given context. * * Wraps clCreateUserEvent(). */ UserEvent( const Context& context, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateUserEvent( context(), &error); detail::errHandler(error, __CREATE_USER_EVENT_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. UserEvent() : Event() {} /*! \brief Sets the execution status of a user event object. * * Wraps clSetUserEventStatus(). */ cl_int setStatus(cl_int status) { return detail::errHandler( ::clSetUserEventStatus(object_, status), __SET_USER_EVENT_STATUS_ERR); } }; #endif /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ inline static cl_int WaitForEvents(const VECTOR_CLASS& events) { return detail::errHandler( ::clWaitForEvents( (cl_uint)events.size(), (events.size() > 0) ? (cl_event*)&events.front() : nullptr), __WAIT_FOR_EVENTS_ERR); } /*! \brief Class interface for cl_mem. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_mem as the original. For details, see * clRetainMemObject() and clReleaseMemObject(). * * \see cl_mem */ class Memory : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Memory() : detail::Wrapper() {} /*! \brief Constructor from cl_mem - takes ownership. * * This effectively transfers ownership of a refcount on the cl_mem * into the new Memory object. */ __CL_EXPLICIT_CONSTRUCTORS Memory(const cl_mem& memory) : detail::Wrapper(memory) {} /*! \brief Assignment operator from cl_mem - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseMemObject() on the value previously held by this instance. */ Memory& operator=(const cl_mem& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Memory(const Memory& mem) : detail::Wrapper(mem) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Memory& operator=(const Memory& mem) { detail::Wrapper::operator=(mem); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Memory(Memory&& mem) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(mem)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Memory& operator=(Memory&& mem) { detail::Wrapper::operator=(std::move(mem)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) //! \brief Wrapper for clGetMemObjectInfo(). template cl_int getInfo(cl_mem_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetMemObjectInfo, object_, name, param), __GET_MEM_OBJECT_INFO_ERR); } //! \brief Wrapper for clGetMemObjectInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_mem_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } #if defined(CL_VERSION_1_1) /*! \brief Registers a callback function to be called when the memory object * is no longer needed. * * Wraps clSetMemObjectDestructorCallback(). * * Repeated calls to this function, for a given cl_mem value, will append * to the list of functions called (in reverse order) when memory object's * resources are freed and the memory object is deleted. * * \note * The registered callbacks are associated with the underlying cl_mem * value - not the Memory class instance. */ cl_int setDestructorCallback( void(CL_CALLBACK* pfn_notify)(cl_mem, void*), void* user_data = nullptr) { return detail::errHandler( ::clSetMemObjectDestructorCallback( object_, pfn_notify, user_data), __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); } #endif }; // Pre-declare copy functions class Buffer; template cl_int copy(IteratorType startIterator, IteratorType endIterator, cl::Buffer& buffer); template cl_int copy(const cl::Buffer& buffer, IteratorType startIterator, IteratorType endIterator); template cl_int copy(const CommandQueue& queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer& buffer); template cl_int copy(const CommandQueue& queue, const cl::Buffer& buffer, IteratorType startIterator, IteratorType endIterator); /*! \brief Class interface for Buffer Memory Objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Buffer : public Memory { public: /*! \brief Constructs a Buffer in a specified context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. */ Buffer( const Context& context, cl_mem_flags flags, ::size_t size, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructs a Buffer in the default context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. * * \see Context::getDefault() */ Buffer( cl_mem_flags flags, ::size_t size, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(err); object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } /*! * \brief Construct a Buffer from a host container via iterators. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template Buffer( IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if (readOnly) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if (useHostPtr) { flags |= CL_MEM_USE_HOST_PTR; } ::size_t size = sizeof(DataType) * (endIterator - startIterator); Context context = Context::getDefault(err); if (useHostPtr) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, nullptr, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if (!useHostPtr) { error = cl::copy(startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } /*! * \brief Construct a Buffer from a host container via iterators using a specified context. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template Buffer(const Context& context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr); /*! * \brief Construct a Buffer from a host container via iterators using a specified queue. * If useHostPtr is specified iterators must represent contiguous data. */ template Buffer(const CommandQueue& queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr); //! \brief Default constructor - initializes to nullptr. Buffer() : Memory() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Buffer(const cl_mem& buffer) : Memory(buffer) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Buffer& operator=(const cl_mem& rhs) { Memory::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Buffer(const Buffer& buf) : Memory(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Buffer& operator=(const Buffer& buf) { Memory::operator=(buf); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Buffer(Buffer&& buf) CL_HPP_NOEXCEPT : Memory(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Buffer& operator=(Buffer&& buf) { Memory::operator=(std::move(buf)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) #if defined(CL_VERSION_1_1) /*! \brief Creates a new buffer object from this. * * Wraps clCreateSubBuffer(). */ Buffer createSubBuffer( cl_mem_flags flags, cl_buffer_create_type buffer_create_type, const void* buffer_create_info, cl_int* err = nullptr) { Buffer result; cl_int error; result.object_ = ::clCreateSubBuffer( object_, flags, buffer_create_type, buffer_create_info, &error); detail::errHandler(error, __CREATE_SUBBUFFER_ERR); if (err != nullptr) { *err = error; } return result; } #endif }; #if defined(USE_DX_INTEROP) /*! \brief Class interface for creating OpenCL buffers from ID3D10Buffer's. * * This is provided to facilitate interoperability with Direct3D. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferD3D10 : public Buffer { public: typedef CL_API_ENTRY cl_mem(CL_API_CALL* PFN_clCreateFromD3D10BufferKHR)( cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, cl_int* errcode_ret); /*! \brief Constructs a BufferD3D10, in a specified context, from a * given ID3D10Buffer. * * Wraps clCreateFromD3D10BufferKHR(). */ BufferD3D10( const Context& context, cl_mem_flags flags, ID3D10Buffer* bufobj, cl_int* err = nullptr) { static PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR = nullptr; #if defined(CL_VERSION_1_2) vector props = context.getInfo(); cl_platform platform = -1; for (int i = 0; i < props.size(); ++i) { if (props[i] == CL_CONTEXT_PLATFORM) { platform = props[i + 1]; } } __INIT_CL_EXT_FCN_PTR_PLATFORM(platform, clCreateFromD3D10BufferKHR); #endif #if defined(CL_VERSION_1_1) __INIT_CL_EXT_FCN_PTR(clCreateFromD3D10BufferKHR); #endif cl_int error; object_ = pfn_clCreateFromD3D10BufferKHR( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_BUFFER_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. BufferD3D10() : Buffer() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS BufferD3D10(const cl_mem& buffer) : Buffer(buffer) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferD3D10& operator=(const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ BufferD3D10(const BufferD3D10& buf) : Buffer(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferD3D10& operator=(const BufferD3D10& buf) { Buffer::operator=(buf); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ BufferD3D10(BufferD3D10&& buf) CL_HPP_NOEXCEPT : Buffer(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferD3D10& operator=(BufferD3D10&& buf) { Buffer::operator=(std::move(buf)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif /*! \brief Class interface for GL Buffer Memory Objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferGL : public Buffer { public: /*! \brief Constructs a BufferGL in a specified context, from a given * GL buffer. * * Wraps clCreateFromGLBuffer(). */ BufferGL( const Context& context, cl_mem_flags flags, cl_GLuint bufobj, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateFromGLBuffer( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_BUFFER_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. BufferGL() : Buffer() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS BufferGL(const cl_mem& buffer) : Buffer(buffer) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferGL& operator=(const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ BufferGL(const BufferGL& buf) : Buffer(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferGL& operator=(const BufferGL& buf) { Buffer::operator=(buf); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ BufferGL(BufferGL&& buf) CL_HPP_NOEXCEPT : Buffer(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferGL& operator=(BufferGL&& buf) { Buffer::operator=(std::move(buf)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) //! \brief Wrapper for clGetGLObjectInfo(). cl_int getObjectInfo( cl_gl_object_type* type, cl_GLuint* gl_object_name) { return detail::errHandler( ::clGetGLObjectInfo(object_, type, gl_object_name), __GET_GL_OBJECT_INFO_ERR); } }; /*! \brief C++ base class for Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image : public Memory { protected: //! \brief Default constructor - initializes to nullptr. Image() : Memory() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image(const cl_mem& image) : Memory(image) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image& operator=(const cl_mem& rhs) { Memory::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image(const Image& img) : Memory(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image& operator=(const Image& img) { Memory::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image(Image&& img) CL_HPP_NOEXCEPT : Memory(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image& operator=(Image&& img) { Memory::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) public: //! \brief Wrapper for clGetImageInfo(). template cl_int getImageInfo(cl_image_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetImageInfo, object_, name, param), __GET_IMAGE_INFO_ERR); } //! \brief Wrapper for clGetImageInfo() that returns by value. template typename detail::param_traits::param_type getImageInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_image_info, name>::param_type param; cl_int result = getImageInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; #if defined(CL_VERSION_1_2) /*! \brief Class interface for 1D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image1D : public Image { public: /*! \brief Constructs a 1D Image in a specified context. * * Wraps clCreateImage(). */ Image1D( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t width, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = { CL_MEM_OBJECT_IMAGE1D, width, 0, 0, 0, 0, 0, 0, 0, nullptr}; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. Image1D() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image1D(const cl_mem& image1D) : Image(image1D) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image1D& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1D(const Image1D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1D& operator=(const Image1D& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1D(Image1D&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1D& operator=(Image1D&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; /*! \class Image1DBuffer * \brief Image interface for 1D buffer images. */ class Image1DBuffer : public Image { public: Image1DBuffer( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t width, const Buffer& buffer, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = { CL_MEM_OBJECT_IMAGE1D_BUFFER, width, 0, 0, 0, 0, 0, 0, 0, buffer()}; object_ = ::clCreateImage( context(), flags, &format, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image1DBuffer() {} __CL_EXPLICIT_CONSTRUCTORS Image1DBuffer(const cl_mem& image1D) : Image(image1D) {} Image1DBuffer& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1DBuffer(const Image1DBuffer& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1DBuffer& operator=(const Image1DBuffer& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1DBuffer(Image1DBuffer&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1DBuffer& operator=(Image1DBuffer&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; /*! \class Image1DArray * \brief Image interface for arrays of 1D images. */ class Image1DArray : public Image { public: Image1DArray( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t arraySize, ::size_t width, ::size_t rowPitch, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = { CL_MEM_OBJECT_IMAGE1D_ARRAY, width, 0, 0, // height, depth (unused) arraySize, rowPitch, 0, 0, 0, nullptr}; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image1DArray() {} __CL_EXPLICIT_CONSTRUCTORS Image1DArray(const cl_mem& imageArray) : Image(imageArray) {} Image1DArray& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1DArray(const Image1DArray& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1DArray& operator=(const Image1DArray& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1DArray(Image1DArray&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1DArray& operator=(Image1DArray&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif // #if defined(CL_VERSION_1_2) /*! \brief Class interface for 2D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image2D : public Image { public: /*! \brief Constructs a 1D Image in a specified context. * * Wraps clCreateImage(). */ Image2D( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t width, ::size_t height, ::size_t row_pitch = 0, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; bool useCreateImage; #if defined(CL_VERSION_1_2) && defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif defined(CL_VERSION_1_2) useCreateImage = true; #else useCreateImage = false; #endif #if defined(CL_VERSION_1_2) if (useCreateImage) { cl_image_desc desc = { CL_MEM_OBJECT_IMAGE2D, width, height, 0, 0, // depth, array size (unused) row_pitch, 0, 0, 0, nullptr}; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif // #if defined(CL_VERSION_1_2) #if !defined(CL_VERSION_1_2) || defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) if (!useCreateImage) { object_ = ::clCreateImage2D( context(), flags, &format, width, height, row_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE2D_ERR); if (err != nullptr) { *err = error; } } #endif // #if !defined(CL_VERSION_1_2) || defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) } //! \brief Default constructor - initializes to nullptr. Image2D() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image2D(const cl_mem& image2D) : Image(image2D) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image2D& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2D(const Image2D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2D& operator=(const Image2D& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2D(Image2D&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2D& operator=(Image2D&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #if !defined(CL_VERSION_1_2) /*! \brief Class interface for GL 2D Image Memory objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory * \note Deprecated for OpenCL 1.2. Please use ImageGL instead. */ class CL_EXT_PREFIX__VERSION_1_1_DEPRECATED Image2DGL CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED : public Image2D { public: /*! \brief Constructs an Image2DGL in a specified context, from a given * GL Texture. * * Wraps clCreateFromGLTexture2D(). */ Image2DGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateFromGLTexture2D( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_2D_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. Image2DGL() : Image2D() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image2DGL(const cl_mem& image) : Image2D(image) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image2DGL& operator=(const cl_mem& rhs) { Image2D::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2DGL(const Image2DGL& img) : Image2D(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2DGL& operator=(const Image2DGL& img) { Image2D::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2DGL(Image2DGL&& img) CL_HPP_NOEXCEPT : Image2D(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2DGL& operator=(Image2DGL&& img) { Image2D::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif // #if !defined(CL_VERSION_1_2) #if defined(CL_VERSION_1_2) /*! \class Image2DArray * \brief Image interface for arrays of 2D images. */ class Image2DArray : public Image { public: Image2DArray( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t arraySize, ::size_t width, ::size_t height, ::size_t rowPitch, ::size_t slicePitch, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = { CL_MEM_OBJECT_IMAGE2D_ARRAY, width, height, 0, // depth (unused) arraySize, rowPitch, slicePitch, 0, 0, nullptr}; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image2DArray() {} __CL_EXPLICIT_CONSTRUCTORS Image2DArray(const cl_mem& imageArray) : Image(imageArray) {} Image2DArray& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2DArray(const Image2DArray& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2DArray& operator=(const Image2DArray& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2DArray(Image2DArray&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2DArray& operator=(Image2DArray&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif // #if defined(CL_VERSION_1_2) /*! \brief Class interface for 3D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image3D : public Image { public: /*! \brief Constructs a 3D Image in a specified context. * * Wraps clCreateImage(). */ Image3D( const Context& context, cl_mem_flags flags, ImageFormat format, ::size_t width, ::size_t height, ::size_t depth, ::size_t row_pitch = 0, ::size_t slice_pitch = 0, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; bool useCreateImage; #if defined(CL_VERSION_1_2) && defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif defined(CL_VERSION_1_2) useCreateImage = true; #else useCreateImage = false; #endif #if defined(CL_VERSION_1_2) if (useCreateImage) { cl_image_desc desc = { CL_MEM_OBJECT_IMAGE3D, width, height, depth, 0, // array size (unused) row_pitch, slice_pitch, 0, 0, nullptr}; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif // #if defined(CL_VERSION_1_2) #if !defined(CL_VERSION_1_2) || defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) if (!useCreateImage) { object_ = ::clCreateImage3D( context(), flags, &format, width, height, depth, row_pitch, slice_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE3D_ERR); if (err != nullptr) { *err = error; } } #endif // #if !defined(CL_VERSION_1_2) || defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) } //! \brief Default constructor - initializes to nullptr. Image3D() : Image() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image3D(const cl_mem& image3D) : Image(image3D) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image3D& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image3D(const Image3D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image3D& operator=(const Image3D& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image3D(Image3D&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image3D& operator=(Image3D&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #if !defined(CL_VERSION_1_2) /*! \brief Class interface for GL 3D Image Memory objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image3DGL : public Image3D { public: /*! \brief Constructs an Image3DGL in a specified context, from a given * GL Texture. * * Wraps clCreateFromGLTexture3D(). */ Image3DGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateFromGLTexture3D( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_3D_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. Image3DGL() : Image3D() {} /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ __CL_EXPLICIT_CONSTRUCTORS Image3DGL(const cl_mem& image) : Image3D(image) {} /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image3DGL& operator=(const cl_mem& rhs) { Image3D::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image3DGL(const Image3DGL& img) : Image3D(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image3DGL& operator=(const Image3DGL& img) { Image3D::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image3DGL(Image3DGL&& img) CL_HPP_NOEXCEPT : Image3D(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image3DGL& operator=(Image3DGL&& img) { Image3D::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif // #if !defined(CL_VERSION_1_2) #if defined(CL_VERSION_1_2) /*! \class ImageGL * \brief general image interface for GL interop. * We abstract the 2D and 3D GL images into a single instance here * that wraps all GL sourced images on the grounds that setup information * was performed by OpenCL anyway. */ class ImageGL : public Image { public: ImageGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateFromGLTexture( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_ERR); if (err != nullptr) { *err = error; } } ImageGL() : Image() {} __CL_EXPLICIT_CONSTRUCTORS ImageGL(const cl_mem& image) : Image(image) {} ImageGL& operator=(const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ ImageGL(const ImageGL& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ ImageGL& operator=(const ImageGL& img) { Image::operator=(img); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ ImageGL(ImageGL&& img) CL_HPP_NOEXCEPT : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ ImageGL& operator=(ImageGL&& img) { Image::operator=(std::move(img)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) }; #endif // #if defined(CL_VERSION_1_2) /*! \brief Class interface for GL Render Buffer Memory Objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferRenderGL : #if defined(CL_VERSION_1_2) public ImageGL #else // #if defined(CL_VERSION_1_2) public Image2DGL #endif //#if defined(CL_VERSION_1_2) { public: /*! \brief Constructs a BufferRenderGL in a specified context, from a given * GL Renderbuffer. * * Wraps clCreateFromGLRenderbuffer(). */ BufferRenderGL( const Context& context, cl_mem_flags flags, cl_GLuint bufobj, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateFromGLRenderbuffer( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_RENDER_BUFFER_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. #if defined(CL_VERSION_1_2) BufferRenderGL() : ImageGL(){}; #else // #if defined(CL_VERSION_1_2) BufferRenderGL() : Image2DGL(){}; #endif //#if defined(CL_VERSION_1_2) /*! \brief Constructor from cl_mem - takes ownership. * * See Memory for further details. */ #if defined(CL_VERSION_1_2) __CL_EXPLICIT_CONSTRUCTORS BufferRenderGL(const cl_mem& buffer) : ImageGL(buffer) { } #else // #if defined(CL_VERSION_1_2) __CL_EXPLICIT_CONSTRUCTORS BufferRenderGL(const cl_mem& buffer) : Image2DGL(buffer) { } #endif //#if defined(CL_VERSION_1_2) /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferRenderGL& operator=(const cl_mem& rhs) { #if defined(CL_VERSION_1_2) ImageGL::operator=(rhs); #else // #if defined(CL_VERSION_1_2) Image2DGL::operator=(rhs); #endif //#if defined(CL_VERSION_1_2) return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ #if defined(CL_VERSION_1_2) BufferRenderGL(const BufferRenderGL& buf) : ImageGL(buf) { } #else // #if defined(CL_VERSION_1_2) BufferRenderGL(const BufferRenderGL& buf) : Image2DGL(buf) { } #endif //#if defined(CL_VERSION_1_2) /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferRenderGL& operator=(const BufferRenderGL& rhs) { #if defined(CL_VERSION_1_2) ImageGL::operator=(rhs); #else // #if defined(CL_VERSION_1_2) Image2DGL::operator=(rhs); #endif //#if defined(CL_VERSION_1_2) return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ #if defined(CL_VERSION_1_2) BufferRenderGL(BufferRenderGL&& buf) CL_HPP_NOEXCEPT : ImageGL(std::move(buf)) { } #else // #if defined(CL_VERSION_1_2) BufferRenderGL(BufferRenderGL&& buf) CL_HPP_NOEXCEPT : Image2DGL(std::move(buf)) { } #endif //#if defined(CL_VERSION_1_2) /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferRenderGL& operator=(BufferRenderGL&& buf) { #if defined(CL_VERSION_1_2) ImageGL::operator=(std::move(buf)); #else // #if defined(CL_VERSION_1_2) Image2DGL::operator=(std::move(buf)); #endif //#if defined(CL_VERSION_1_2) return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) //! \brief Wrapper for clGetGLObjectInfo(). cl_int getObjectInfo( cl_gl_object_type* type, cl_GLuint* gl_object_name) { return detail::errHandler( ::clGetGLObjectInfo(object_, type, gl_object_name), __GET_GL_OBJECT_INFO_ERR); } }; /*! \brief Class interface for cl_sampler. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_sampler as the original. For details, see * clRetainSampler() and clReleaseSampler(). * * \see cl_sampler */ class Sampler : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Sampler() {} /*! \brief Constructs a Sampler in a specified context. * * Wraps clCreateSampler(). */ Sampler( const Context& context, cl_bool normalized_coords, cl_addressing_mode addressing_mode, cl_filter_mode filter_mode, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateSampler( context(), normalized_coords, addressing_mode, filter_mode, &error); detail::errHandler(error, __CREATE_SAMPLER_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructor from cl_sampler - takes ownership. * * This effectively transfers ownership of a refcount on the cl_sampler * into the new Sampler object. */ __CL_EXPLICIT_CONSTRUCTORS Sampler(const cl_sampler& sampler) : detail::Wrapper(sampler) {} /*! \brief Assignment operator from cl_sampler - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseSampler() on the value previously held by this instance. */ Sampler& operator=(const cl_sampler& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Sampler(const Sampler& sam) : detail::Wrapper(sam) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Sampler& operator=(const Sampler& sam) { detail::Wrapper::operator=(sam); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Sampler(Sampler&& sam) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(sam)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Sampler& operator=(Sampler&& sam) { detail::Wrapper::operator=(std::move(sam)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) //! \brief Wrapper for clGetSamplerInfo(). template cl_int getInfo(cl_sampler_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetSamplerInfo, object_, name, param), __GET_SAMPLER_INFO_ERR); } //! \brief Wrapper for clGetSamplerInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_sampler_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; class Program; class CommandQueue; class Kernel; //! \brief Class interface for specifying NDRange values. class NDRange { private: size_t<3> sizes_; cl_uint dimensions_; public: //! \brief Default constructor - resulting range has zero dimensions. NDRange() : dimensions_(0) { } //! \brief Constructs one-dimensional range. NDRange(::size_t size0) : dimensions_(1) { sizes_[0] = size0; } //! \brief Constructs two-dimensional range. NDRange(::size_t size0, ::size_t size1) : dimensions_(2) { sizes_[0] = size0; sizes_[1] = size1; } //! \brief Constructs three-dimensional range. NDRange(::size_t size0, ::size_t size1, ::size_t size2) : dimensions_(3) { sizes_[0] = size0; sizes_[1] = size1; sizes_[2] = size2; } /*! \brief Conversion operator to const ::size_t *. * * \returns a pointer to the size of the first dimension. */ operator const ::size_t*() const { return (const ::size_t*)sizes_; } //! \brief Queries the number of dimensions in the range. ::size_t dimensions() const { return dimensions_; } }; //! \brief A zero-dimensional range. static const NDRange NullRange; //! \brief Local address wrapper for use with Kernel::setArg struct LocalSpaceArg { ::size_t size_; }; namespace detail { template struct KernelArgumentHandler { static ::size_t size(const T&) { return sizeof(T); } static const T* ptr(const T& value) { return &value; } }; template <> struct KernelArgumentHandler { static ::size_t size(const LocalSpaceArg& value) { return value.size_; } static const void* ptr(const LocalSpaceArg&) { return nullptr; } }; } // namespace detail //! \endcond /*! __local * \brief Helper function for generating LocalSpaceArg objects. * Deprecated. Replaced with Local. */ inline CL_EXT_PREFIX__VERSION_1_1_DEPRECATED LocalSpaceArg __local(::size_t size) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; inline LocalSpaceArg __local(::size_t size) { LocalSpaceArg ret = {size}; return ret; } /*! Local * \brief Helper function for generating LocalSpaceArg objects. */ inline LocalSpaceArg Local(::size_t size) { LocalSpaceArg ret = {size}; return ret; } //class KernelFunctor; /*! \brief Class interface for cl_kernel. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_kernel as the original. For details, see * clRetainKernel() and clReleaseKernel(). * * \see cl_kernel */ class Kernel : public detail::Wrapper { public: inline Kernel(const Program& program, const char* name, cl_int* err = nullptr); //! \brief Default constructor - initializes to nullptr. Kernel() {} /*! \brief Constructor from cl_kernel - takes ownership. * * This effectively transfers ownership of a refcount on the cl_kernel * into the new Kernel object. */ __CL_EXPLICIT_CONSTRUCTORS Kernel(const cl_kernel& kernel) : detail::Wrapper(kernel) {} /*! \brief Assignment operator from cl_kernel - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseKernel() on the value previously held by this instance. */ Kernel& operator=(const cl_kernel& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Kernel(const Kernel& kernel) : detail::Wrapper(kernel) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Kernel& operator=(const Kernel& kernel) { detail::Wrapper::operator=(kernel); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Kernel(Kernel&& kernel) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(kernel)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Kernel& operator=(Kernel&& kernel) { detail::Wrapper::operator=(std::move(kernel)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) template cl_int getInfo(cl_kernel_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetKernelInfo, object_, name, param), __GET_KERNEL_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } #if defined(CL_VERSION_1_2) template cl_int getArgInfo(cl_uint argIndex, cl_kernel_arg_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetKernelArgInfo, object_, argIndex, name, param), __GET_KERNEL_ARG_INFO_ERR); } template typename detail::param_traits::param_type getArgInfo(cl_uint argIndex, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_arg_info, name>::param_type param; cl_int result = getArgInfo(argIndex, name, ¶m); if (err != nullptr) { *err = result; } return param; } #endif // #if defined(CL_VERSION_1_2) template cl_int getWorkGroupInfo( const Device& device, cl_kernel_work_group_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetKernelWorkGroupInfo, object_, device(), name, param), __GET_KERNEL_WORK_GROUP_INFO_ERR); } template typename detail::param_traits::param_type getWorkGroupInfo(const Device& device, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_work_group_info, name>::param_type param; cl_int result = getWorkGroupInfo(device, name, ¶m); if (err != nullptr) { *err = result; } return param; } template cl_int setArg(cl_uint index, const T& value) { return detail::errHandler( ::clSetKernelArg( object_, index, detail::KernelArgumentHandler::size(value), detail::KernelArgumentHandler::ptr(value)), __SET_KERNEL_ARGS_ERR); } cl_int setArg(cl_uint index, ::size_t size, const void* argPtr) { return detail::errHandler( ::clSetKernelArg(object_, index, size, argPtr), __SET_KERNEL_ARGS_ERR); } }; /*! \class Program * \brief Program interface that implements cl_program. */ class Program : public detail::Wrapper { public: typedef VECTOR_CLASS > Binaries; typedef VECTOR_CLASS > Sources; Program( const STRING_CLASS& source, bool build = false, cl_int* err = nullptr) { cl_int error; const char* strings = source.c_str(); const ::size_t length = source.size(); Context context = Context::getDefault(err); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, "", nullptr, nullptr); detail::errHandler(error, __BUILD_PROGRAM_ERR); } if (err != nullptr) { *err = error; } } Program( const Context& context, const STRING_CLASS& source, bool build = false, cl_int* err = nullptr) { cl_int error; const char* strings = source.c_str(); const ::size_t length = source.size(); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, "", nullptr, nullptr); detail::errHandler(error, __BUILD_PROGRAM_ERR); } if (err != nullptr) { *err = error; } } Program( const Context& context, const Sources& sources, cl_int* err = nullptr) { cl_int error; const ::size_t n = (::size_t)sources.size(); ::size_t* lengths = (::size_t*)alloca(n * sizeof(::size_t)); const char** strings = (const char**)alloca(n * sizeof(const char*)); for (::size_t i = 0; i < n; ++i) { strings[i] = sources[(int)i].first; lengths[i] = sources[(int)i].second; } object_ = ::clCreateProgramWithSource( context(), (cl_uint)n, strings, lengths, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (err != nullptr) { *err = error; } } /** * Construct a program object from a list of devices and a per-device list of binaries. * \param context A valid OpenCL context in which to construct the program. * \param devices A vector of OpenCL device objects for which the program will be created. * \param binaries A vector of pairs of a pointer to a binary object and its length. * \param binaryStatus An optional vector that on completion will be resized to * match the size of binaries and filled with values to specify if each binary * was successfully loaded. * Set to CL_SUCCESS if the binary was successfully loaded. * Set to CL_INVALID_VALUE if the length is 0 or the binary pointer is nullptr. * Set to CL_INVALID_BINARY if the binary provided is not valid for the matching device. * \param err if non-nullptr will be set to CL_SUCCESS on successful operation or one of the following errors: * CL_INVALID_CONTEXT if context is not a valid context. * CL_INVALID_VALUE if the length of devices is zero; or if the length of binaries does not match the length of devices; * or if any entry in binaries is nullptr or has length 0. * CL_INVALID_DEVICE if OpenCL devices listed in devices are not in the list of devices associated with context. * CL_INVALID_BINARY if an invalid program binary was encountered for any device. binaryStatus will return specific status for each device. * CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the OpenCL implementation on the host. */ Program( const Context& context, const VECTOR_CLASS& devices, const Binaries& binaries, VECTOR_CLASS* binaryStatus = nullptr, cl_int* err = nullptr) { cl_int error; const ::size_t numDevices = devices.size(); // Catch size mismatch early and return if (binaries.size() != numDevices) { error = CL_INVALID_VALUE; detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != nullptr) { *err = error; } return; } ::size_t* lengths = (::size_t*)alloca(numDevices * sizeof(::size_t)); const unsigned char** images = (const unsigned char**)alloca(numDevices * sizeof(const unsigned char**)); for (::size_t i = 0; i < numDevices; ++i) { images[i] = (const unsigned char*)binaries[i].first; lengths[i] = binaries[(int)i].second; } cl_device_id* deviceIDs = (cl_device_id*)alloca(numDevices * sizeof(cl_device_id)); for (::size_t deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } if (binaryStatus) { binaryStatus->resize(numDevices); } object_ = ::clCreateProgramWithBinary( context(), (cl_uint)devices.size(), deviceIDs, lengths, images, (binaryStatus != nullptr && numDevices > 0) ? &binaryStatus->front() : nullptr, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != nullptr) { *err = error; } } #if defined(CL_VERSION_1_2) /** * Create program using builtin kernels. * \param kernelNames Semi-colon separated list of builtin kernel names */ Program( const Context& context, const VECTOR_CLASS& devices, const STRING_CLASS& kernelNames, cl_int* err = nullptr) { cl_int error; ::size_t numDevices = devices.size(); cl_device_id* deviceIDs = (cl_device_id*)alloca(numDevices * sizeof(cl_device_id)); for (::size_t deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateProgramWithBuiltInKernels( context(), (cl_uint)devices.size(), deviceIDs, kernelNames.c_str(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR); if (err != nullptr) { *err = error; } } #endif // #if defined(CL_VERSION_1_2) Program() { } __CL_EXPLICIT_CONSTRUCTORS Program(const cl_program& program) : detail::Wrapper(program) {} Program& operator=(const cl_program& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Program(const Program& program) : detail::Wrapper(program) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Program& operator=(const Program& program) { detail::Wrapper::operator=(program); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Program(Program&& program) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(program)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Program& operator=(Program&& program) { detail::Wrapper::operator=(std::move(program)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) cl_int build( const VECTOR_CLASS& devices, const char* options = nullptr, void(CL_CALLBACK* notifyFptr)(cl_program, void*) = nullptr, void* data = nullptr) const { ::size_t numDevices = devices.size(); cl_device_id* deviceIDs = (cl_device_id*)alloca(numDevices * sizeof(cl_device_id)); for (::size_t deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } return detail::errHandler( ::clBuildProgram( object_, (cl_uint) devices.size(), deviceIDs, options, notifyFptr, data), __BUILD_PROGRAM_ERR); } cl_int build( const char* options = nullptr, void(CL_CALLBACK* notifyFptr)(cl_program, void*) = nullptr, void* data = nullptr) const { return detail::errHandler( ::clBuildProgram( object_, 0, nullptr, options, notifyFptr, data), __BUILD_PROGRAM_ERR); } #if defined(CL_VERSION_1_2) cl_int compile( const char* options = nullptr, void(CL_CALLBACK* notifyFptr)(cl_program, void*) = nullptr, void* data = nullptr) const { return detail::errHandler( ::clCompileProgram( object_, 0, nullptr, options, 0, nullptr, nullptr, notifyFptr, data), __COMPILE_PROGRAM_ERR); } #endif template cl_int getInfo(cl_program_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetProgramInfo, object_, name, param), __GET_PROGRAM_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_program_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } template cl_int getBuildInfo( const Device& device, cl_program_build_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetProgramBuildInfo, object_, device(), name, param), __GET_PROGRAM_BUILD_INFO_ERR); } template typename detail::param_traits::param_type getBuildInfo(const Device& device, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_program_build_info, name>::param_type param; cl_int result = getBuildInfo(device, name, ¶m); if (err != nullptr) { *err = result; } return param; } cl_int createKernels(VECTOR_CLASS* kernels) { cl_uint numKernels; cl_int err = ::clCreateKernelsInProgram(object_, 0, nullptr, &numKernels); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } Kernel* value = (Kernel*)alloca(numKernels * sizeof(Kernel)); err = ::clCreateKernelsInProgram( object_, numKernels, (cl_kernel*)value, nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } kernels->assign(&value[0], &value[numKernels]); return CL_SUCCESS; } }; #if defined(CL_VERSION_1_2) inline Program linkProgram( Program input1, Program input2, const char* options = nullptr, void(CL_CALLBACK* notifyFptr)(cl_program, void*) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error_local = CL_SUCCESS; cl_program programs[2] = {input1(), input2()}; Context ctx = input1.getInfo(&error_local); if (error_local != CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } cl_program prog = ::clLinkProgram( ctx(), 0, nullptr, options, 2, programs, notifyFptr, data, &error_local); detail::errHandler(error_local, __COMPILE_PROGRAM_ERR); if (err != nullptr) { *err = error_local; } return Program(prog); } inline Program linkProgram( VECTOR_CLASS inputPrograms, const char* options = nullptr, void(CL_CALLBACK* notifyFptr)(cl_program, void*) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error_local = CL_SUCCESS; cl_program* programs = (cl_program*)alloca(inputPrograms.size() * sizeof(cl_program)); if (programs != nullptr) { for (unsigned int i = 0; i < inputPrograms.size(); i++) { programs[i] = inputPrograms[i](); } } Context ctx; if (inputPrograms.size() > 0) { ctx = inputPrograms[0].getInfo(&error_local); if (error_local != CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } } cl_program prog = ::clLinkProgram( ctx(), 0, nullptr, options, (cl_uint)inputPrograms.size(), programs, notifyFptr, data, &error_local); detail::errHandler(error_local, __COMPILE_PROGRAM_ERR); if (err != nullptr) { *err = error_local; } return Program(prog); } #endif template <> inline VECTOR_CLASS cl::Program::getInfo(cl_int* err) const { VECTOR_CLASS< ::size_t> sizes = getInfo(); VECTOR_CLASS binaries; for (unsigned long & size : sizes) { char* ptr = nullptr; if (size != 0) ptr = new char[size]; binaries.push_back(ptr); } cl_int result = getInfo(CL_PROGRAM_BINARIES, &binaries); if (err != nullptr) { *err = result; } return binaries; } inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) { cl_int error; object_ = ::clCreateKernel(program(), name, &error); detail::errHandler(error, __CREATE_KERNEL_ERR); if (err != nullptr) { *err = error; } } /*! \class CommandQueue * \brief CommandQueue interface for cl_command_queue. */ class CommandQueue : public detail::Wrapper { private: #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED static std::atomic default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED static volatile int default_initialized_; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED static CommandQueue default_; static volatile cl_int default_error_; public: CommandQueue( cl_command_queue_properties properties, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } } else { Device device = context.getInfo()[0]; object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } } /*! * \brief Constructs a CommandQueue for an implementation defined device in the given context */ explicit CommandQueue( const Context& context, cl_command_queue_properties properties = 0, cl_int* err = nullptr) { cl_int error; VECTOR_CLASS devices; error = context.getInfo(CL_CONTEXT_DEVICES, &devices); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } return; } object_ = ::clCreateCommandQueue(context(), devices[0](), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } CommandQueue( const Context& context, const Device& device, cl_command_queue_properties properties = 0, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ CommandQueue(const CommandQueue& queue) : detail::Wrapper(queue) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ CommandQueue& operator=(const CommandQueue& queue) { detail::Wrapper::operator=(queue); return *this; } #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ CommandQueue(CommandQueue&& queue) CL_HPP_NOEXCEPT : detail::Wrapper(std::move(queue)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ CommandQueue& operator=(CommandQueue&& queue) { detail::Wrapper::operator=(std::move(queue)); return *this; } #endif // #if defined(CL_HPP_RVALUE_REFERENCES_SUPPORTED) static CommandQueue getDefault(cl_int* err = nullptr) { int state = detail::compare_exchange( &default_initialized_, __DEFAULT_BEING_INITIALIZED, __DEFAULT_NOT_INITIALIZED); if (state & __DEFAULT_INITIALIZED) { if (err != nullptr) { *err = default_error_; } return default_; } if (state & __DEFAULT_BEING_INITIALIZED) { // Assume writes will propagate eventually... while (default_initialized_ != __DEFAULT_INITIALIZED) { detail::fence(); } if (err != nullptr) { *err = default_error_; } return default_; } cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } } else { Device device = context.getInfo()[0]; default_ = CommandQueue(context, device, 0, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } detail::fence(); default_error_ = error; // Assume writes will propagate eventually... default_initialized_ = __DEFAULT_INITIALIZED; detail::fence(); if (err != nullptr) { *err = default_error_; } return default_; } CommandQueue() {} __CL_EXPLICIT_CONSTRUCTORS CommandQueue(const cl_command_queue& commandQueue) : detail::Wrapper(commandQueue) {} CommandQueue& operator=(const cl_command_queue& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_command_queue_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetCommandQueueInfo, object_, name, param), __GET_COMMAND_QUEUE_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_command_queue_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, ::size_t offset, ::size_t size, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBuffer( object_, buffer(), blocking, offset, size, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, ::size_t offset, ::size_t size, const void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBuffer( object_, buffer(), blocking, offset, size, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, ::size_t src_offset, ::size_t dst_offset, ::size_t size, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBuffer( object_, src(), dst(), src_offset, dst_offset, size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQEUE_COPY_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const size_t<3>& buffer_offset, const size_t<3>& host_offset, const size_t<3>& region, ::size_t buffer_row_pitch, ::size_t buffer_slice_pitch, ::size_t host_row_pitch, ::size_t host_slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBufferRect( object_, buffer(), blocking, (const ::size_t*)buffer_offset, (const ::size_t*)host_offset, (const ::size_t*)region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const size_t<3>& buffer_offset, const size_t<3>& host_offset, const size_t<3>& region, ::size_t buffer_row_pitch, ::size_t buffer_slice_pitch, ::size_t host_row_pitch, ::size_t host_slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBufferRect( object_, buffer(), blocking, (const ::size_t*)buffer_offset, (const ::size_t*)host_offset, (const ::size_t*)region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const size_t<3>& src_origin, const size_t<3>& dst_origin, const size_t<3>& region, ::size_t src_row_pitch, ::size_t src_slice_pitch, ::size_t dst_row_pitch, ::size_t dst_slice_pitch, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferRect( object_, src(), dst(), (const ::size_t*)src_origin, (const ::size_t*)dst_origin, (const ::size_t*)region, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQEUE_COPY_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if defined(CL_VERSION_1_2) /** * Enqueue a command to fill a buffer object with a pattern * of a given size. The pattern is specified a as vector. * \tparam PatternType The datatype of the pattern field. * The pattern type must be an accepted OpenCL data type. */ template cl_int enqueueFillBuffer( const Buffer& buffer, PatternType pattern, ::size_t offset, ::size_t size, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillBuffer( object_, buffer(), static_cast(&pattern), sizeof(PatternType), offset, size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_FILL_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if defined(CL_VERSION_1_2) cl_int enqueueReadImage( const Image& image, cl_bool blocking, const size_t<3>& origin, const size_t<3>& region, ::size_t row_pitch, ::size_t slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadImage( object_, image(), blocking, (const ::size_t*)origin, (const ::size_t*)region, row_pitch, slice_pitch, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const size_t<3>& origin, const size_t<3>& region, ::size_t row_pitch, ::size_t slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteImage( object_, image(), blocking, (const ::size_t*)origin, (const ::size_t*)region, row_pitch, slice_pitch, ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyImage( const Image& src, const Image& dst, const size_t<3>& src_origin, const size_t<3>& dst_origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImage( object_, src(), dst(), (const ::size_t*)src_origin, (const ::size_t*)dst_origin, (const ::size_t*)region, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if defined(CL_VERSION_1_2) /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA floating-point color value if * the image channel data type is not an unnormalized signed or * unsigned data type. */ cl_int enqueueFillImage( const Image& image, cl_float4 fillColor, const size_t<3>& origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), (const ::size_t*)origin, (const ::size_t*)region, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_FILL_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA signed integer color value if * the image channel data type is an unnormalized signed integer * type. */ cl_int enqueueFillImage( const Image& image, cl_int4 fillColor, const size_t<3>& origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), (const ::size_t*)origin, (const ::size_t*)region, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_FILL_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA unsigned integer color value if * the image channel data type is an unnormalized unsigned integer * type. */ cl_int enqueueFillImage( const Image& image, cl_uint4 fillColor, const size_t<3>& origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), (const ::size_t*)origin, (const ::size_t*)region, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_FILL_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if defined(CL_VERSION_1_2) cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const size_t<3>& src_origin, const size_t<3>& region, ::size_t dst_offset, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImageToBuffer( object_, src(), dst(), (const ::size_t*)src_origin, (const ::size_t*)region, dst_offset, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, ::size_t src_offset, const size_t<3>& dst_origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferToImage( object_, src(), dst(), src_offset, (const ::size_t*)dst_origin, (const ::size_t*)region, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, ::size_t offset, ::size_t size, const VECTOR_CLASS* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) const { cl_event tmp; cl_int error; void* result = ::clEnqueueMapBuffer( object_, buffer(), blocking, flags, offset, size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } if (event != nullptr && error == CL_SUCCESS) *event = tmp; return result; } void* enqueueMapImage( const Image& buffer, cl_bool blocking, cl_map_flags flags, const size_t<3>& origin, const size_t<3>& region, ::size_t* row_pitch, ::size_t* slice_pitch, const VECTOR_CLASS* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) const { cl_event tmp; cl_int error; void* result = ::clEnqueueMapImage( object_, buffer(), blocking, flags, (const ::size_t*)origin, (const ::size_t*)region, row_pitch, slice_pitch, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr, &error); detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); if (err != nullptr) { *err = error; } if (event != nullptr && error == CL_SUCCESS) *event = tmp; return result; } cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( object_, memory(), mapped_ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if defined(CL_VERSION_1_2) /** * Enqueues a marker command which waits for either a list of events to complete, * or all previously enqueued commands to complete. * * Enqueues a marker command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command returns an event which can be waited on, * i.e. this event can be waited on to insure that all events either in the event_wait_list * or all previously enqueued commands, queued before this command to command_queue, * have completed. */ cl_int enqueueMarkerWithWaitList( const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarkerWithWaitList( object_, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MARKER_WAIT_LIST_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * A synchronization point that enqueues a barrier operation. * * Enqueues a barrier command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command blocks command execution, that is, any * following commands enqueued after it do not execute until it completes. This command * returns an event which can be waited on, i.e. this event can be waited on to insure that * all events either in the event_wait_list or all previously enqueued commands, queued * before this command to command_queue, have completed. */ cl_int enqueueBarrierWithWaitList( const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueBarrierWithWaitList( object_, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_BARRIER_WAIT_LIST_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command to indicate with which device a set of memory objects * should be associated. */ cl_int enqueueMigrateMemObjects( const VECTOR_CLASS& memObjects, cl_mem_migration_flags flags, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_event tmp; cl_mem* localMemObjects = static_cast(alloca(memObjects.size() * sizeof(cl_mem))); for (int i = 0; i < (int)memObjects.size(); ++i) { localMemObjects[i] = memObjects[i](); } cl_int err = detail::errHandler( ::clEnqueueMigrateMemObjects( object_, (cl_uint)memObjects.size(), static_cast(localMemObjects), flags, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if defined(CL_VERSION_1_2) cl_int enqueueNDRangeKernel( const Kernel& kernel, const NDRange& offset, const NDRange& global, const NDRange& local = NullRange, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNDRangeKernel( object_, kernel(), (cl_uint)global.dimensions(), offset.dimensions() != 0 ? (const ::size_t*)offset : nullptr, (const ::size_t*)global, local.dimensions() != 0 ? (const ::size_t*)local : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_NDRANGE_KERNEL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueTask( const Kernel& kernel, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueTask( object_, kernel(), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_TASK_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueNativeKernel( void(CL_CALLBACK* userFptr)(void*), std::pair args, const VECTOR_CLASS* mem_objects = nullptr, const VECTOR_CLASS* mem_locs = nullptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_mem* mems = (mem_objects != nullptr && mem_objects->size() > 0) ? (cl_mem*)alloca(mem_objects->size() * sizeof(cl_mem)) : nullptr; if (mems != nullptr) { for (unsigned int i = 0; i < mem_objects->size(); i++) { mems[i] = ((*mem_objects)[i])(); } } cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNativeKernel( object_, userFptr, args.first, args.second, (mem_objects != nullptr) ? (cl_uint)mem_objects->size() : 0, mems, (mem_locs != nullptr && mem_locs->size() > 0) ? (const void**)&mem_locs->front() : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_NATIVE_KERNEL); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) || (defined(CL_VERSION_1_1) && !defined(CL_VERSION_1_2)) CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueMarker(Event* event = nullptr) const CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarker( object_, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MARKER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueWaitForEvents(const VECTOR_CLASS& events) const CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueWaitForEvents( object_, (cl_uint)events.size(), events.size() > 0 ? (const cl_event*)&events.front() : nullptr), __ENQUEUE_WAIT_FOR_EVENTS_ERR); } #endif // #if defined(CL_VERSION_1_1) cl_int enqueueAcquireGLObjects( const VECTOR_CLASS* mem_objects = nullptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueAcquireGLObjects( object_, (mem_objects != nullptr) ? (cl_uint)mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem*)&mem_objects->front() : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_ACQUIRE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseGLObjects( const VECTOR_CLASS* mem_objects = nullptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReleaseGLObjects( object_, (mem_objects != nullptr) ? (cl_uint)mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem*)&mem_objects->front() : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_RELEASE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if defined(USE_DX_INTEROP) typedef CL_API_ENTRY cl_int(CL_API_CALL* PFN_clEnqueueAcquireD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); typedef CL_API_ENTRY cl_int(CL_API_CALL* PFN_clEnqueueReleaseD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); cl_int enqueueAcquireD3D10Objects( const VECTOR_CLASS* mem_objects = nullptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = nullptr; #if defined(CL_VERSION_1_2) cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); __INIT_CL_EXT_FCN_PTR_PLATFORM(platform, clEnqueueAcquireD3D10ObjectsKHR); #endif #if defined(CL_VERSION_1_1) __INIT_CL_EXT_FCN_PTR(clEnqueueAcquireD3D10ObjectsKHR); #endif cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueAcquireD3D10ObjectsKHR( object_, (mem_objects != nullptr) ? (cl_uint)mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem*)&mem_objects->front() : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_ACQUIRE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseD3D10Objects( const VECTOR_CLASS* mem_objects = nullptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) const { static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = nullptr; #if defined(CL_VERSION_1_2) cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); __INIT_CL_EXT_FCN_PTR_PLATFORM(platform, clEnqueueReleaseD3D10ObjectsKHR); #endif // #if defined(CL_VERSION_1_2) #if defined(CL_VERSION_1_1) __INIT_CL_EXT_FCN_PTR(clEnqueueReleaseD3D10ObjectsKHR); #endif // #if defined(CL_VERSION_1_1) cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueReleaseD3D10ObjectsKHR( object_, (mem_objects != nullptr) ? (cl_uint)mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem*)&mem_objects->front() : nullptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_RELEASE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) || (defined(CL_VERSION_1_1) && !defined(CL_VERSION_1_2)) CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueBarrier() const CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueBarrier(object_), __ENQUEUE_BARRIER_ERR); } #endif // #if defined(CL_VERSION_1_1) cl_int flush() const { return detail::errHandler(::clFlush(object_), __FLUSH_ERR); } cl_int finish() const { return detail::errHandler(::clFinish(object_), __FINISH_ERR); } }; #ifdef _WIN32 #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) std::atomic CommandQueue::default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) volatile int CommandQueue::default_initialized_ = __DEFAULT_NOT_INITIALIZED; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED __declspec(selectany) CommandQueue CommandQueue::default_; __declspec(selectany) volatile cl_int CommandQueue::default_error_ = CL_SUCCESS; #else // !_WIN32 #ifdef CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) std::atomic CommandQueue::default_initialized_; #else // !CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) volatile int CommandQueue::default_initialized_ = __DEFAULT_NOT_INITIALIZED; #endif // !CL_HPP_CPP11_ATOMICS_SUPPORTED __attribute__((weak)) CommandQueue CommandQueue::default_; __attribute__((weak)) volatile cl_int CommandQueue::default_error_ = CL_SUCCESS; #endif // !_WIN32 template Buffer::Buffer( const Context& context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if (readOnly) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if (useHostPtr) { flags |= CL_MEM_USE_HOST_PTR; } ::size_t size = sizeof(DataType) * (endIterator - startIterator); if (useHostPtr) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, nullptr, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if (!useHostPtr) { CommandQueue queue(context, 0, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } template Buffer::Buffer( const CommandQueue& queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if (readOnly) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if (useHostPtr) { flags |= CL_MEM_USE_HOST_PTR; } ::size_t size = sizeof(DataType) * (endIterator - startIterator); Context context = queue.getInfo(); if (useHostPtr) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, nullptr, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if (!useHostPtr) { error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } inline cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, ::size_t offset, ::size_t size, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBuffer(buffer, blocking, offset, size, ptr, events, event); } inline cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, ::size_t offset, ::size_t size, const void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBuffer(buffer, blocking, offset, size, ptr, events, event); } inline void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, ::size_t offset, ::size_t size, const VECTOR_CLASS* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } void* result = ::clEnqueueMapBuffer( queue(), buffer(), blocking, flags, offset, size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (cl_event*)event, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } return result; } inline cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (error != CL_SUCCESS) { return error; } cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( queue(), memory(), mapped_ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } inline cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, ::size_t src_offset, ::size_t dst_offset, ::size_t size, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBuffer(src, dst, src_offset, dst_offset, size, events, event); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses default command queue. */ template inline cl_int copy(IteratorType startIterator, IteratorType endIterator, cl::Buffer& buffer) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, startIterator, endIterator, buffer); } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses default command queue. */ template inline cl_int copy(const cl::Buffer& buffer, IteratorType startIterator, IteratorType endIterator) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, buffer, startIterator, endIterator); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses specified queue. */ template inline cl_int copy(const CommandQueue& queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer& buffer) { typedef typename std::iterator_traits::value_type DataType; cl_int error; ::size_t length = endIterator - startIterator; ::size_t byteLength = length * sizeof(DataType); DataType* pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_WRITE, 0, byteLength, nullptr, nullptr, &error)); // if exceptions enabled, enqueueMapBuffer will throw if (error != CL_SUCCESS) { return error; } #if defined(_MSC_VER) std::copy( startIterator, endIterator, stdext::checked_array_iterator( pointer, length)); #else std::copy(startIterator, endIterator, pointer); #endif Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if (error != CL_SUCCESS) { return error; } endEvent.wait(); return CL_SUCCESS; } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses specified queue. */ template inline cl_int copy(const CommandQueue& queue, const cl::Buffer& buffer, IteratorType startIterator, IteratorType endIterator) { typedef typename std::iterator_traits::value_type DataType; cl_int error; ::size_t length = endIterator - startIterator; ::size_t byteLength = length * sizeof(DataType); DataType* pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_READ, 0, byteLength, nullptr, nullptr, &error)); // if exceptions enabled, enqueueMapBuffer will throw if (error != CL_SUCCESS) { return error; } std::copy(pointer, pointer + length, startIterator); Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if (error != CL_SUCCESS) { return error; } endEvent.wait(); return CL_SUCCESS; } #if defined(CL_VERSION_1_1) inline cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const size_t<3>& buffer_offset, const size_t<3>& host_offset, const size_t<3>& region, ::size_t buffer_row_pitch, ::size_t buffer_slice_pitch, ::size_t host_row_pitch, ::size_t host_slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const size_t<3>& buffer_offset, const size_t<3>& host_offset, const size_t<3>& region, ::size_t buffer_row_pitch, ::size_t buffer_slice_pitch, ::size_t host_row_pitch, ::size_t host_slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const size_t<3>& src_origin, const size_t<3>& dst_origin, const size_t<3>& region, ::size_t src_row_pitch, ::size_t src_slice_pitch, ::size_t dst_row_pitch, ::size_t dst_slice_pitch, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferRect( src, dst, src_origin, dst_origin, region, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, events, event); } #endif inline cl_int enqueueReadImage( const Image& image, cl_bool blocking, const size_t<3>& origin, const size_t<3>& region, ::size_t row_pitch, ::size_t slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const size_t<3>& origin, const size_t<3>& region, ::size_t row_pitch, ::size_t slice_pitch, void* ptr, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueCopyImage( const Image& src, const Image& dst, const size_t<3>& src_origin, const size_t<3>& dst_origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImage( src, dst, src_origin, dst_origin, region, events, event); } inline cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const size_t<3>& src_origin, const size_t<3>& region, ::size_t dst_offset, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImageToBuffer( src, dst, src_origin, region, dst_offset, events, event); } inline cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, ::size_t src_offset, const size_t<3>& dst_origin, const size_t<3>& region, const VECTOR_CLASS* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferToImage( src, dst, src_offset, dst_origin, region, events, event); } inline cl_int flush(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.flush(); } inline cl_int finish(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.finish(); } // Kernel Functor support // New interface as of September 2011 // Requires the C++11 std::tr1::function (note do not support TR1) // Visual Studio 2010 and GCC 4.2 struct EnqueueArgs { CommandQueue queue_; const NDRange offset_; const NDRange global_; const NDRange local_; VECTOR_CLASS events_; EnqueueArgs(NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { } EnqueueArgs(Event e, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(Event e, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(Event e, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(const VECTOR_CLASS& events, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(const VECTOR_CLASS& events, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(const VECTOR_CLASS& events, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue& queue, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(CommandQueue& queue, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(CommandQueue& queue, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { } EnqueueArgs(CommandQueue& queue, Event e, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(CommandQueue& queue, Event e, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue& queue, Event e, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue& queue, const VECTOR_CLASS& events, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(CommandQueue& queue, const VECTOR_CLASS& events, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue& queue, const VECTOR_CLASS& events, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local), events_(events) { } }; namespace detail { class NullType { }; template struct SetArg { static void set(Kernel kernel, T0 arg) { kernel.setArg(index, arg); } }; template struct SetArg { static void set(Kernel, NullType) { } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27, typename T28, typename T29, typename T30, typename T31> class KernelFunctorGlobal { private: Kernel kernel_; public: KernelFunctorGlobal( Kernel kernel) : kernel_(kernel) { } KernelFunctorGlobal( const Program& program, const STRING_CLASS name, cl_int* err = nullptr) : kernel_(program, name.c_str(), err) { } Event operator()( const EnqueueArgs& args, T0 t0, T1 t1 = NullType(), T2 t2 = NullType(), T3 t3 = NullType(), T4 t4 = NullType(), T5 t5 = NullType(), T6 t6 = NullType(), T7 t7 = NullType(), T8 t8 = NullType(), T9 t9 = NullType(), T10 t10 = NullType(), T11 t11 = NullType(), T12 t12 = NullType(), T13 t13 = NullType(), T14 t14 = NullType(), T15 t15 = NullType(), T16 t16 = NullType(), T17 t17 = NullType(), T18 t18 = NullType(), T19 t19 = NullType(), T20 t20 = NullType(), T21 t21 = NullType(), T22 t22 = NullType(), T23 t23 = NullType(), T24 t24 = NullType(), T25 t25 = NullType(), T26 t26 = NullType(), T27 t27 = NullType(), T28 t28 = NullType(), T29 t29 = NullType(), T30 t30 = NullType(), T31 t31 = NullType()) { Event event; SetArg<0, T0>::set(kernel_, t0); SetArg<1, T1>::set(kernel_, t1); SetArg<2, T2>::set(kernel_, t2); SetArg<3, T3>::set(kernel_, t3); SetArg<4, T4>::set(kernel_, t4); SetArg<5, T5>::set(kernel_, t5); SetArg<6, T6>::set(kernel_, t6); SetArg<7, T7>::set(kernel_, t7); SetArg<8, T8>::set(kernel_, t8); SetArg<9, T9>::set(kernel_, t9); SetArg<10, T10>::set(kernel_, t10); SetArg<11, T11>::set(kernel_, t11); SetArg<12, T12>::set(kernel_, t12); SetArg<13, T13>::set(kernel_, t13); SetArg<14, T14>::set(kernel_, t14); SetArg<15, T15>::set(kernel_, t15); SetArg<16, T16>::set(kernel_, t16); SetArg<17, T17>::set(kernel_, t17); SetArg<18, T18>::set(kernel_, t18); SetArg<19, T19>::set(kernel_, t19); SetArg<20, T20>::set(kernel_, t20); SetArg<21, T21>::set(kernel_, t21); SetArg<22, T22>::set(kernel_, t22); SetArg<23, T23>::set(kernel_, t23); SetArg<24, T24>::set(kernel_, t24); SetArg<25, T25>::set(kernel_, t25); SetArg<26, T26>::set(kernel_, t26); SetArg<27, T27>::set(kernel_, t27); SetArg<28, T28>::set(kernel_, t28); SetArg<29, T29>::set(kernel_, t29); SetArg<30, T30>::set(kernel_, t30); SetArg<31, T31>::set(kernel_, t31); args.queue_.enqueueNDRangeKernel( kernel_, args.offset_, args.global_, args.local_, &args.events_, &event); return event; } }; //------------------------------------------------------------------------------------------------------ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27, typename T28, typename T29, typename T30, typename T31> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 32)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26, T27 arg27, T28 arg28, T29 arg29, T30 arg30, T31 arg31) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27, typename T28, typename T29, typename T30> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 31)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26, T27 arg27, T28 arg28, T29 arg29, T30 arg30) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27, typename T28, typename T29> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 30)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26, T27 arg27, T28 arg28, T29 arg29) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27, typename T28> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 29)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26, T27 arg27, T28 arg28) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26, typename T27> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 28)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26, T27 arg27) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25, typename T26> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 27)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25, T26 arg26) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24, typename T25> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 26)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24, T25 arg25) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23, typename T24> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 25)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23, T24 arg24) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22, typename T23> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 24)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22, T23 arg23) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21, typename T22> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 23)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21, T22 arg22) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename T21> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 22)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 21)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 20)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 19)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 18)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 17)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 16)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 15)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 14)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 13)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 12)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 11)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 10)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 9)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7, T8); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 8)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6, T7); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 7)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5, T6); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 6)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4, T5); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4, arg5); } }; template < typename T0, typename T1, typename T2, typename T3, typename T4> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 5)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3, T4); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3, arg4); } }; template < typename T0, typename T1, typename T2, typename T3> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 4)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2, T3); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { return functor_( enqueueArgs, arg0, arg1, arg2, arg3); } }; template < typename T0, typename T1, typename T2> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, T2, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 3)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1, T2); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1, T2 arg2) { return functor_( enqueueArgs, arg0, arg1, arg2); } }; template < typename T0, typename T1> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, T1, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 2)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0, T1); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0, T1 arg1) { return functor_( enqueueArgs, arg0, arg1); } }; template < typename T0> struct functionImplementation_ { typedef detail::KernelFunctorGlobal< T0, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType> FunctorType; FunctorType functor_; functionImplementation_(const FunctorType& functor) : functor_(functor) { #if (defined(_WIN32) && defined(_VARIADIC_MAX) && (_VARIADIC_MAX < 1)) // Fail variadic expansion for dev11 static_assert(0, "Visual Studio has a hard limit of argument count for a std::function expansion. Please define _VARIADIC_MAX to be 10. If you need more arguments than that VC12 and below cannot support it."); #endif } //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, T0); Event operator()( const EnqueueArgs& enqueueArgs, T0 arg0) { return functor_( enqueueArgs, arg0); } }; } // namespace detail //---------------------------------------------------------------------------------------------- template < typename T0, typename T1 = detail::NullType, typename T2 = detail::NullType, typename T3 = detail::NullType, typename T4 = detail::NullType, typename T5 = detail::NullType, typename T6 = detail::NullType, typename T7 = detail::NullType, typename T8 = detail::NullType, typename T9 = detail::NullType, typename T10 = detail::NullType, typename T11 = detail::NullType, typename T12 = detail::NullType, typename T13 = detail::NullType, typename T14 = detail::NullType, typename T15 = detail::NullType, typename T16 = detail::NullType, typename T17 = detail::NullType, typename T18 = detail::NullType, typename T19 = detail::NullType, typename T20 = detail::NullType, typename T21 = detail::NullType, typename T22 = detail::NullType, typename T23 = detail::NullType, typename T24 = detail::NullType, typename T25 = detail::NullType, typename T26 = detail::NullType, typename T27 = detail::NullType, typename T28 = detail::NullType, typename T29 = detail::NullType, typename T30 = detail::NullType, typename T31 = detail::NullType> struct make_kernel : public detail::functionImplementation_< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31> { public: typedef detail::KernelFunctorGlobal< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31> FunctorType; make_kernel( const Program& program, const STRING_CLASS name, cl_int* err = nullptr) : detail::functionImplementation_( FunctorType(program, name, err)) { } make_kernel( const Kernel kernel) : detail::functionImplementation_( FunctorType(kernel)) { } }; //---------------------------------------------------------------------------------------------------------------------- #undef __ERR_STR #if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) #undef __GET_DEVICE_INFO_ERR #undef __GET_PLATFORM_INFO_ERR #undef __GET_DEVICE_IDS_ERR #undef __GET_CONTEXT_INFO_ERR #undef __GET_EVENT_INFO_ERR #undef __GET_EVENT_PROFILE_INFO_ERR #undef __GET_MEM_OBJECT_INFO_ERR #undef __GET_IMAGE_INFO_ERR #undef __GET_SAMPLER_INFO_ERR #undef __GET_KERNEL_INFO_ERR #undef __GET_KERNEL_ARG_INFO_ERR #undef __GET_KERNEL_WORK_GROUP_INFO_ERR #undef __GET_PROGRAM_INFO_ERR #undef __GET_PROGRAM_BUILD_INFO_ERR #undef __GET_COMMAND_QUEUE_INFO_ERR #undef __CREATE_CONTEXT_ERR #undef __CREATE_CONTEXT_FROM_TYPE_ERR #undef __GET_SUPPORTED_IMAGE_FORMATS_ERR #undef __CREATE_BUFFER_ERR #undef __CREATE_SUBBUFFER_ERR #undef __CREATE_IMAGE2D_ERR #undef __CREATE_IMAGE3D_ERR #undef __CREATE_SAMPLER_ERR #undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR #undef __CREATE_USER_EVENT_ERR #undef __SET_USER_EVENT_STATUS_ERR #undef __SET_EVENT_CALLBACK_ERR #undef __SET_PRINTF_CALLBACK_ERR #undef __WAIT_FOR_EVENTS_ERR #undef __CREATE_KERNEL_ERR #undef __SET_KERNEL_ARGS_ERR #undef __CREATE_PROGRAM_WITH_SOURCE_ERR #undef __CREATE_PROGRAM_WITH_BINARY_ERR #undef __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR #undef __BUILD_PROGRAM_ERR #undef __CREATE_KERNELS_IN_PROGRAM_ERR #undef __CREATE_COMMAND_QUEUE_ERR #undef __SET_COMMAND_QUEUE_PROPERTY_ERR #undef __ENQUEUE_READ_BUFFER_ERR #undef __ENQUEUE_WRITE_BUFFER_ERR #undef __ENQUEUE_READ_BUFFER_RECT_ERR #undef __ENQUEUE_WRITE_BUFFER_RECT_ERR #undef __ENQEUE_COPY_BUFFER_ERR #undef __ENQEUE_COPY_BUFFER_RECT_ERR #undef __ENQUEUE_READ_IMAGE_ERR #undef __ENQUEUE_WRITE_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR #undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR #undef __ENQUEUE_MAP_BUFFER_ERR #undef __ENQUEUE_MAP_IMAGE_ERR #undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR #undef __ENQUEUE_NDRANGE_KERNEL_ERR #undef __ENQUEUE_TASK_ERR #undef __ENQUEUE_NATIVE_KERNEL #undef __CL_EXPLICIT_CONSTRUCTORS #undef __UNLOAD_COMPILER_ERR #endif //__CL_USER_OVERRIDE_ERROR_STRINGS #undef __CL_FUNCTION_TYPE // Extensions /** * Deprecated APIs for 1.2 */ #if defined(CL_VERSION_1_1) #undef __INIT_CL_EXT_FCN_PTR #endif // #if defined(CL_VERSION_1_1) #undef __CREATE_SUB_DEVICES #if defined(USE_CL_DEVICE_FISSION) #undef __PARAM_NAME_DEVICE_FISSION #endif // USE_CL_DEVICE_FISSION #undef __DEFAULT_NOT_INITIALIZED #undef __DEFAULT_BEING_INITIALIZED #undef __DEFAULT_INITIALIZED #undef CL_HPP_RVALUE_REFERENCES_SUPPORTED #undef CL_HPP_NOEXCEPT } // namespace cl #endif // CL_HPP_ src/algorithms/libs/opencl/clFFT.h000066400000000000000000000120011352176506000173350ustar00rootroot00000000000000 // // File: clFFT.h // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software. // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CLFFT_H #define __CLFFT_H #ifdef __cplusplus extern "C" { #endif #include #ifdef __APPLE__ #define CL_SILENCE_DEPRECATION #include #else #include #endif // XForm type typedef enum { clFFT_Forward = -1, clFFT_Inverse = 1 } clFFT_Direction; // XForm dimension typedef enum { clFFT_1D = 0, clFFT_2D = 1, clFFT_3D = 3 } clFFT_Dimension; // XForm Data type typedef enum { clFFT_SplitComplexFormat = 0, clFFT_InterleavedComplexFormat = 1 } clFFT_DataFormat; typedef struct { unsigned int x; unsigned int y; unsigned int z; } clFFT_Dim3; typedef struct { float *real; float *imag; } clFFT_SplitComplex; typedef struct { float real; float imag; } clFFT_Complex; typedef void *clFFT_Plan; clFFT_Plan clFFT_CreatePlan(cl_context context, clFFT_Dim3 n, clFFT_Dimension dim, clFFT_DataFormat dataFormat, cl_int *error_code); void clFFT_DestroyPlan(clFFT_Plan plan); cl_int clFFT_ExecuteInterleaved(cl_command_queue queue, clFFT_Plan plan, cl_int batchSize, clFFT_Direction dir, cl_mem data_in, cl_mem data_out, cl_int num_events, cl_event *event_list, cl_event *event); cl_int clFFT_ExecutePlannar(cl_command_queue queue, clFFT_Plan plan, cl_int batchSize, clFFT_Direction dir, cl_mem data_in_real, cl_mem data_in_imag, cl_mem data_out_real, cl_mem data_out_imag, cl_int num_events, cl_event *event_list, cl_event *event); cl_int clFFT_1DTwistInterleaved(clFFT_Plan Plan, cl_command_queue queue, cl_mem array, size_t numRows, size_t numCols, size_t startRow, size_t rowsToProcess, clFFT_Direction dir); cl_int clFFT_1DTwistPlannar(clFFT_Plan Plan, cl_command_queue queue, cl_mem array_real, cl_mem array_imag, size_t numRows, size_t numCols, size_t startRow, size_t rowsToProcess, clFFT_Direction dir); void clFFT_DumpPlan(clFFT_Plan plan, FILE *file); #ifdef __cplusplus } #endif #endif src/algorithms/libs/opencl/fft_base_kernels.h000066400000000000000000000313071352176506000217050ustar00rootroot00000000000000 // // File: fft_base_kernels.h // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software. // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CL_FFT_BASE_KERNELS_ #define __CL_FFT_BASE_KERNELS_ #include using namespace std; static string baseKernels = string( "#ifndef M_PI\n" "#define M_PI 0x1.921fb54442d18p+1\n" "#endif\n" "#define complexMul(a,b) ((float2)(mad(-(a).y, (b).y, (a).x * (b).x), mad((a).y, (b).x, (a).x * (b).y)))\n" "#define conj(a) ((float2)((a).x, -(a).y))\n" "#define conjTransp(a) ((float2)(-(a).y, (a).x))\n" "\n" "#define fftKernel2(a,dir) \\\n" "{ \\\n" " float2 c = (a)[0]; \\\n" " (a)[0] = c + (a)[1]; \\\n" " (a)[1] = c - (a)[1]; \\\n" "}\n" "\n" "#define fftKernel2S(d1,d2,dir) \\\n" "{ \\\n" " float2 c = (d1); \\\n" " (d1) = c + (d2); \\\n" " (d2) = c - (d2); \\\n" "}\n" "\n" "#define fftKernel4(a,dir) \\\n" "{ \\\n" " fftKernel2S((a)[0], (a)[2], dir); \\\n" " fftKernel2S((a)[1], (a)[3], dir); \\\n" " fftKernel2S((a)[0], (a)[1], dir); \\\n" " (a)[3] = (float2)(dir)*(conjTransp((a)[3])); \\\n" " fftKernel2S((a)[2], (a)[3], dir); \\\n" " float2 c = (a)[1]; \\\n" " (a)[1] = (a)[2]; \\\n" " (a)[2] = c; \\\n" "}\n" "\n" "#define fftKernel4s(a0,a1,a2,a3,dir) \\\n" "{ \\\n" " fftKernel2S((a0), (a2), dir); \\\n" " fftKernel2S((a1), (a3), dir); \\\n" " fftKernel2S((a0), (a1), dir); \\\n" " (a3) = (float2)(dir)*(conjTransp((a3))); \\\n" " fftKernel2S((a2), (a3), dir); \\\n" " float2 c = (a1); \\\n" " (a1) = (a2); \\\n" " (a2) = c; \\\n" "}\n" "\n" "#define bitreverse8(a) \\\n" "{ \\\n" " float2 c; \\\n" " c = (a)[1]; \\\n" " (a)[1] = (a)[4]; \\\n" " (a)[4] = c; \\\n" " c = (a)[3]; \\\n" " (a)[3] = (a)[6]; \\\n" " (a)[6] = c; \\\n" "}\n" "\n" "#define fftKernel8(a,dir) \\\n" "{ \\\n" " const float2 w1 = (float2)(0x1.6a09e6p-1f, dir*0x1.6a09e6p-1f); \\\n" " const float2 w3 = (float2)(-0x1.6a09e6p-1f, dir*0x1.6a09e6p-1f); \\\n" " float2 c; \\\n" " fftKernel2S((a)[0], (a)[4], dir); \\\n" " fftKernel2S((a)[1], (a)[5], dir); \\\n" " fftKernel2S((a)[2], (a)[6], dir); \\\n" " fftKernel2S((a)[3], (a)[7], dir); \\\n" " (a)[5] = complexMul(w1, (a)[5]); \\\n" " (a)[6] = (float2)(dir)*(conjTransp((a)[6])); \\\n" " (a)[7] = complexMul(w3, (a)[7]); \\\n" " fftKernel2S((a)[0], (a)[2], dir); \\\n" " fftKernel2S((a)[1], (a)[3], dir); \\\n" " fftKernel2S((a)[4], (a)[6], dir); \\\n" " fftKernel2S((a)[5], (a)[7], dir); \\\n" " (a)[3] = (float2)(dir)*(conjTransp((a)[3])); \\\n" " (a)[7] = (float2)(dir)*(conjTransp((a)[7])); \\\n" " fftKernel2S((a)[0], (a)[1], dir); \\\n" " fftKernel2S((a)[2], (a)[3], dir); \\\n" " fftKernel2S((a)[4], (a)[5], dir); \\\n" " fftKernel2S((a)[6], (a)[7], dir); \\\n" " bitreverse8((a)); \\\n" "}\n" "\n" "#define bitreverse4x4(a) \\\n" "{ \\\n" " float2 c; \\\n" " c = (a)[1]; (a)[1] = (a)[4]; (a)[4] = c; \\\n" " c = (a)[2]; (a)[2] = (a)[8]; (a)[8] = c; \\\n" " c = (a)[3]; (a)[3] = (a)[12]; (a)[12] = c; \\\n" " c = (a)[6]; (a)[6] = (a)[9]; (a)[9] = c; \\\n" " c = (a)[7]; (a)[7] = (a)[13]; (a)[13] = c; \\\n" " c = (a)[11]; (a)[11] = (a)[14]; (a)[14] = c; \\\n" "}\n" "\n" "#define fftKernel16(a,dir) \\\n" "{ \\\n" " const float w0 = 0x1.d906bcp-1f; \\\n" " const float w1 = 0x1.87de2ap-2f; \\\n" " const float w2 = 0x1.6a09e6p-1f; \\\n" " fftKernel4s((a)[0], (a)[4], (a)[8], (a)[12], dir); \\\n" " fftKernel4s((a)[1], (a)[5], (a)[9], (a)[13], dir); \\\n" " fftKernel4s((a)[2], (a)[6], (a)[10], (a)[14], dir); \\\n" " fftKernel4s((a)[3], (a)[7], (a)[11], (a)[15], dir); \\\n" " (a)[5] = complexMul((a)[5], (float2)(w0, dir*w1)); \\\n" " (a)[6] = complexMul((a)[6], (float2)(w2, dir*w2)); \\\n" " (a)[7] = complexMul((a)[7], (float2)(w1, dir*w0)); \\\n" " (a)[9] = complexMul((a)[9], (float2)(w2, dir*w2)); \\\n" " (a)[10] = (float2)(dir)*(conjTransp((a)[10])); \\\n" " (a)[11] = complexMul((a)[11], (float2)(-w2, dir*w2)); \\\n" " (a)[13] = complexMul((a)[13], (float2)(w1, dir*w0)); \\\n" " (a)[14] = complexMul((a)[14], (float2)(-w2, dir*w2)); \\\n" " (a)[15] = complexMul((a)[15], (float2)(-w0, dir*-w1)); \\\n" " fftKernel4((a), dir); \\\n" " fftKernel4((a) + 4, dir); \\\n" " fftKernel4((a) + 8, dir); \\\n" " fftKernel4((a) + 12, dir); \\\n" " bitreverse4x4((a)); \\\n" "}\n" "\n" "#define bitreverse32(a) \\\n" "{ \\\n" " float2 c1, c2; \\\n" " c1 = (a)[2]; (a)[2] = (a)[1]; c2 = (a)[4]; (a)[4] = c1; c1 = (a)[8]; (a)[8] = c2; c2 = (a)[16]; (a)[16] = c1; (a)[1] = c2; \\\n" " c1 = (a)[6]; (a)[6] = (a)[3]; c2 = (a)[12]; (a)[12] = c1; c1 = (a)[24]; (a)[24] = c2; c2 = (a)[17]; (a)[17] = c1; (a)[3] = c2; \\\n" " c1 = (a)[10]; (a)[10] = (a)[5]; c2 = (a)[20]; (a)[20] = c1; c1 = (a)[9]; (a)[9] = c2; c2 = (a)[18]; (a)[18] = c1; (a)[5] = c2; \\\n" " c1 = (a)[14]; (a)[14] = (a)[7]; c2 = (a)[28]; (a)[28] = c1; c1 = (a)[25]; (a)[25] = c2; c2 = (a)[19]; (a)[19] = c1; (a)[7] = c2; \\\n" " c1 = (a)[22]; (a)[22] = (a)[11]; c2 = (a)[13]; (a)[13] = c1; c1 = (a)[26]; (a)[26] = c2; c2 = (a)[21]; (a)[21] = c1; (a)[11] = c2; \\\n" " c1 = (a)[30]; (a)[30] = (a)[15]; c2 = (a)[29]; (a)[29] = c1; c1 = (a)[27]; (a)[27] = c2; c2 = (a)[23]; (a)[23] = c1; (a)[15] = c2; \\\n" "}\n" "\n" "#define fftKernel32(a,dir) \\\n" "{ \\\n" " fftKernel2S((a)[0], (a)[16], dir); \\\n" " fftKernel2S((a)[1], (a)[17], dir); \\\n" " fftKernel2S((a)[2], (a)[18], dir); \\\n" " fftKernel2S((a)[3], (a)[19], dir); \\\n" " fftKernel2S((a)[4], (a)[20], dir); \\\n" " fftKernel2S((a)[5], (a)[21], dir); \\\n" " fftKernel2S((a)[6], (a)[22], dir); \\\n" " fftKernel2S((a)[7], (a)[23], dir); \\\n" " fftKernel2S((a)[8], (a)[24], dir); \\\n" " fftKernel2S((a)[9], (a)[25], dir); \\\n" " fftKernel2S((a)[10], (a)[26], dir); \\\n" " fftKernel2S((a)[11], (a)[27], dir); \\\n" " fftKernel2S((a)[12], (a)[28], dir); \\\n" " fftKernel2S((a)[13], (a)[29], dir); \\\n" " fftKernel2S((a)[14], (a)[30], dir); \\\n" " fftKernel2S((a)[15], (a)[31], dir); \\\n" " (a)[17] = complexMul((a)[17], (float2)(0x1.f6297cp-1f, dir*0x1.8f8b84p-3f)); \\\n" " (a)[18] = complexMul((a)[18], (float2)(0x1.d906bcp-1f, dir*0x1.87de2ap-2f)); \\\n" " (a)[19] = complexMul((a)[19], (float2)(0x1.a9b662p-1f, dir*0x1.1c73b4p-1f)); \\\n" " (a)[20] = complexMul((a)[20], (float2)(0x1.6a09e6p-1f, dir*0x1.6a09e6p-1f)); \\\n" " (a)[21] = complexMul((a)[21], (float2)(0x1.1c73b4p-1f, dir*0x1.a9b662p-1f)); \\\n" " (a)[22] = complexMul((a)[22], (float2)(0x1.87de2ap-2f, dir*0x1.d906bcp-1f)); \\\n" " (a)[23] = complexMul((a)[23], (float2)(0x1.8f8b84p-3f, dir*0x1.f6297cp-1f)); \\\n" " (a)[24] = complexMul((a)[24], (float2)(0x0p+0f, dir*0x1p+0f)); \\\n" " (a)[25] = complexMul((a)[25], (float2)(-0x1.8f8b84p-3f, dir*0x1.f6297cp-1f)); \\\n" " (a)[26] = complexMul((a)[26], (float2)(-0x1.87de2ap-2f, dir*0x1.d906bcp-1f)); \\\n" " (a)[27] = complexMul((a)[27], (float2)(-0x1.1c73b4p-1f, dir*0x1.a9b662p-1f)); \\\n" " (a)[28] = complexMul((a)[28], (float2)(-0x1.6a09e6p-1f, dir*0x1.6a09e6p-1f)); \\\n" " (a)[29] = complexMul((a)[29], (float2)(-0x1.a9b662p-1f, dir*0x1.1c73b4p-1f)); \\\n" " (a)[30] = complexMul((a)[30], (float2)(-0x1.d906bcp-1f, dir*0x1.87de2ap-2f)); \\\n" " (a)[31] = complexMul((a)[31], (float2)(-0x1.f6297cp-1f, dir*0x1.8f8b84p-3f)); \\\n" " fftKernel16((a), dir); \\\n" " fftKernel16((a) + 16, dir); \\\n" " bitreverse32((a)); \\\n" "}\n\n"); static string twistKernelInterleaved = string( "__kernel void \\\n" "clFFT_1DTwistInterleaved(__global float2 *in, unsigned int startRow, unsigned int numCols, unsigned int N, unsigned int numRowsToProcess, int dir) \\\n" "{ \\\n" " float2 a, w; \\\n" " float ang; \\\n" " unsigned int j; \\\n" " unsigned int i = get_global_id(0); \\\n" " unsigned int startIndex = i; \\\n" " \\\n" " if(i < numCols) \\\n" " { \\\n" " for(j = 0; j < numRowsToProcess; j++) \\\n" " { \\\n" " a = in[startIndex]; \\\n" " ang = 2.0f * M_PI * dir * i * (startRow + j) / N; \\\n" " w = (float2)(native_cos(ang), native_sin(ang)); \\\n" " a = complexMul(a, w); \\\n" " in[startIndex] = a; \\\n" " startIndex += numCols; \\\n" " } \\\n" " } \\\n" "} \\\n"); static string twistKernelPlannar = string( "__kernel void \\\n" "clFFT_1DTwistSplit(__global float *in_real, __global float *in_imag , unsigned int startRow, unsigned int numCols, unsigned int N, unsigned int numRowsToProcess, int dir) \\\n" "{ \\\n" " float2 a, w; \\\n" " float ang; \\\n" " unsigned int j; \\\n" " unsigned int i = get_global_id(0); \\\n" " unsigned int startIndex = i; \\\n" " \\\n" " if(i < numCols) \\\n" " { \\\n" " for(j = 0; j < numRowsToProcess; j++) \\\n" " { \\\n" " a = (float2)(in_real[startIndex], in_imag[startIndex]); \\\n" " ang = 2.0f * M_PI * dir * i * (startRow + j) / N; \\\n" " w = (float2)(native_cos(ang), native_sin(ang)); \\\n" " a = complexMul(a, w); \\\n" " in_real[startIndex] = a.x; \\\n" " in_imag[startIndex] = a.y; \\\n" " startIndex += numCols; \\\n" " } \\\n" " } \\\n" "} \\\n"); #endif src/algorithms/libs/opencl/fft_execute.cc000066400000000000000000000400151352176506000210440ustar00rootroot00000000000000 // // File: fft_execute.cpp // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software.¬ // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #include "clFFT.h" #include "fft_internal.h" #include #include #include #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) < (b)) ? (a) : (b)) static cl_int allocateTemporaryBufferInterleaved(cl_fft_plan *plan, cl_uint batchSize) { cl_int err = CL_SUCCESS; if (plan->temp_buffer_needed && plan->last_batch_size != batchSize) { plan->last_batch_size = batchSize; size_t tmpLength = plan->n.x * plan->n.y * plan->n.z * batchSize * 2 * sizeof(cl_float); if (plan->tempmemobj) clReleaseMemObject(plan->tempmemobj); plan->tempmemobj = clCreateBuffer(plan->context, CL_MEM_READ_WRITE, tmpLength, nullptr, &err); } return err; } static cl_int allocateTemporaryBufferPlannar(cl_fft_plan *plan, cl_uint batchSize) { cl_int err = CL_SUCCESS; cl_int terr; if (plan->temp_buffer_needed && plan->last_batch_size != batchSize) { plan->last_batch_size = batchSize; size_t tmpLength = plan->n.x * plan->n.y * plan->n.z * batchSize * sizeof(cl_float); if (plan->tempmemobj_real) clReleaseMemObject(plan->tempmemobj_real); if (plan->tempmemobj_imag) clReleaseMemObject(plan->tempmemobj_imag); plan->tempmemobj_real = clCreateBuffer(plan->context, CL_MEM_READ_WRITE, tmpLength, nullptr, &err); plan->tempmemobj_imag = clCreateBuffer(plan->context, CL_MEM_READ_WRITE, tmpLength, nullptr, &terr); err |= terr; } return err; } void getKernelWorkDimensions(cl_fft_plan *plan, cl_fft_kernel_info *kernelInfo, cl_int *batchSize, size_t *gWorkItems, size_t *lWorkItems) { *lWorkItems = kernelInfo->num_workitems_per_workgroup; int numWorkGroups = kernelInfo->num_workgroups; int numXFormsPerWG = kernelInfo->num_xforms_per_workgroup; switch (kernelInfo->dir) { case cl_fft_kernel_x: *batchSize *= (plan->n.y * plan->n.z); numWorkGroups = (*batchSize % numXFormsPerWG) ? (*batchSize / numXFormsPerWG + 1) : (*batchSize / numXFormsPerWG); numWorkGroups *= kernelInfo->num_workgroups; break; case cl_fft_kernel_y: *batchSize *= plan->n.z; numWorkGroups *= *batchSize; break; case cl_fft_kernel_z: numWorkGroups *= *batchSize; break; } *gWorkItems = numWorkGroups * *lWorkItems; } cl_int clFFT_ExecuteInterleaved(cl_command_queue queue, clFFT_Plan Plan, cl_int batchSize, clFFT_Direction dir, cl_mem data_in, cl_mem data_out, cl_int num_events, cl_event *event_list, cl_event *event) { int s; auto *plan = (cl_fft_plan *)Plan; if (plan->format != clFFT_InterleavedComplexFormat) return CL_INVALID_VALUE; cl_int err; size_t gWorkItems, lWorkItems; int inPlaceDone; cl_int isInPlace = data_in == data_out ? 1 : 0; if ((err = allocateTemporaryBufferInterleaved(plan, batchSize)) != CL_SUCCESS) return err; cl_mem memObj[3]; memObj[0] = data_in; memObj[1] = data_out; memObj[2] = plan->tempmemobj; cl_fft_kernel_info *kernelInfo = plan->kernel_info; int numKernels = plan->num_kernels; int numKernelsOdd = numKernels & 1; int currRead = 0; int currWrite = 1; // at least one external dram shuffle (transpose) required if (plan->temp_buffer_needed) { // in-place transform if (isInPlace) { inPlaceDone = 0; currRead = 1; currWrite = 2; } else { currWrite = (numKernels & 1) ? 1 : 2; } while (kernelInfo) { if (isInPlace && numKernelsOdd && !inPlaceDone && kernelInfo->in_place_possible) { currWrite = currRead; inPlaceDone = 1; } s = batchSize; getKernelWorkDimensions(plan, kernelInfo, &s, &gWorkItems, &lWorkItems); err |= clSetKernelArg(kernelInfo->kernel, 0, sizeof(cl_mem), &memObj[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 1, sizeof(cl_mem), &memObj[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 2, sizeof(cl_int), &dir); err |= clSetKernelArg(kernelInfo->kernel, 3, sizeof(cl_int), &s); err |= clEnqueueNDRangeKernel(queue, kernelInfo->kernel, 1, nullptr, &gWorkItems, &lWorkItems, 0, nullptr, nullptr); if (err) return err; currRead = (currWrite == 1) ? 1 : 2; currWrite = (currWrite == 1) ? 2 : 1; kernelInfo = kernelInfo->next; } } // no dram shuffle (transpose required) transform // all kernels can execute in-place. else { while (kernelInfo) { s = batchSize; getKernelWorkDimensions(plan, kernelInfo, &s, &gWorkItems, &lWorkItems); err |= clSetKernelArg(kernelInfo->kernel, 0, sizeof(cl_mem), &memObj[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 1, sizeof(cl_mem), &memObj[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 2, sizeof(cl_int), &dir); err |= clSetKernelArg(kernelInfo->kernel, 3, sizeof(cl_int), &s); err |= clEnqueueNDRangeKernel(queue, kernelInfo->kernel, 1, nullptr, &gWorkItems, &lWorkItems, 0, nullptr, nullptr); if (err) return err; currRead = 1; currWrite = 1; kernelInfo = kernelInfo->next; } } return err; } cl_int clFFT_ExecutePlannar(cl_command_queue queue, clFFT_Plan Plan, cl_int batchSize, clFFT_Direction dir, cl_mem data_in_real, cl_mem data_in_imag, cl_mem data_out_real, cl_mem data_out_imag, cl_int num_events, cl_event *event_list, cl_event *event) { int s; auto *plan = (cl_fft_plan *)Plan; if (plan->format != clFFT_SplitComplexFormat) return CL_INVALID_VALUE; cl_int err; size_t gWorkItems, lWorkItems; int inPlaceDone; cl_int isInPlace = ((data_in_real == data_out_real) && (data_in_imag == data_out_imag)) ? 1 : 0; if ((err = allocateTemporaryBufferPlannar(plan, batchSize)) != CL_SUCCESS) return err; cl_mem memObj_real[3]; cl_mem memObj_imag[3]; memObj_real[0] = data_in_real; memObj_real[1] = data_out_real; memObj_real[2] = plan->tempmemobj_real; memObj_imag[0] = data_in_imag; memObj_imag[1] = data_out_imag; memObj_imag[2] = plan->tempmemobj_imag; cl_fft_kernel_info *kernelInfo = plan->kernel_info; int numKernels = plan->num_kernels; int numKernelsOdd = numKernels & 1; int currRead = 0; int currWrite = 1; // at least one external dram shuffle (transpose) required if (plan->temp_buffer_needed) { // in-place transform if (isInPlace) { inPlaceDone = 0; currRead = 1; currWrite = 2; } else { currWrite = (numKernels & 1) ? 1 : 2; } while (kernelInfo) { if (isInPlace && numKernelsOdd && !inPlaceDone && kernelInfo->in_place_possible) { currWrite = currRead; inPlaceDone = 1; } s = batchSize; getKernelWorkDimensions(plan, kernelInfo, &s, &gWorkItems, &lWorkItems); err |= clSetKernelArg(kernelInfo->kernel, 0, sizeof(cl_mem), &memObj_real[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 1, sizeof(cl_mem), &memObj_imag[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 2, sizeof(cl_mem), &memObj_real[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 3, sizeof(cl_mem), &memObj_imag[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 4, sizeof(cl_int), &dir); err |= clSetKernelArg(kernelInfo->kernel, 5, sizeof(cl_int), &s); err |= clEnqueueNDRangeKernel(queue, kernelInfo->kernel, 1, nullptr, &gWorkItems, &lWorkItems, 0, nullptr, nullptr); if (err) return err; currRead = (currWrite == 1) ? 1 : 2; currWrite = (currWrite == 1) ? 2 : 1; kernelInfo = kernelInfo->next; } } // no dram shuffle (transpose required) transform else { while (kernelInfo) { s = batchSize; getKernelWorkDimensions(plan, kernelInfo, &s, &gWorkItems, &lWorkItems); err |= clSetKernelArg(kernelInfo->kernel, 0, sizeof(cl_mem), &memObj_real[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 1, sizeof(cl_mem), &memObj_imag[currRead]); err |= clSetKernelArg(kernelInfo->kernel, 2, sizeof(cl_mem), &memObj_real[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 3, sizeof(cl_mem), &memObj_imag[currWrite]); err |= clSetKernelArg(kernelInfo->kernel, 4, sizeof(cl_int), &dir); err |= clSetKernelArg(kernelInfo->kernel, 5, sizeof(cl_int), &s); err |= clEnqueueNDRangeKernel(queue, kernelInfo->kernel, 1, nullptr, &gWorkItems, &lWorkItems, 0, nullptr, nullptr); if (err) return err; currRead = 1; currWrite = 1; kernelInfo = kernelInfo->next; } } return err; } cl_int clFFT_1DTwistInterleaved(clFFT_Plan Plan, cl_command_queue queue, cl_mem array, unsigned numRows, unsigned numCols, unsigned startRow, unsigned rowsToProcess, clFFT_Direction dir) { auto *plan = (cl_fft_plan *)Plan; unsigned int N = numRows * numCols; unsigned int nCols = numCols; unsigned int sRow = startRow; unsigned int rToProcess = rowsToProcess; int d = dir; int err = 0; cl_device_id device_id; err = clGetCommandQueueInfo(queue, CL_QUEUE_DEVICE, sizeof(cl_device_id), &device_id, nullptr); if (err) return err; size_t gSize; err = clGetKernelWorkGroupInfo(plan->twist_kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &gSize, nullptr); if (err) return err; gSize = min(128, gSize); size_t numGlobalThreads[1] = {max(numCols / gSize, 1) * gSize}; size_t numLocalThreads[1] = {gSize}; err |= clSetKernelArg(plan->twist_kernel, 0, sizeof(cl_mem), &array); err |= clSetKernelArg(plan->twist_kernel, 1, sizeof(unsigned int), &sRow); err |= clSetKernelArg(plan->twist_kernel, 2, sizeof(unsigned int), &nCols); err |= clSetKernelArg(plan->twist_kernel, 3, sizeof(unsigned int), &N); err |= clSetKernelArg(plan->twist_kernel, 4, sizeof(unsigned int), &rToProcess); err |= clSetKernelArg(plan->twist_kernel, 5, sizeof(int), &d); err |= clEnqueueNDRangeKernel(queue, plan->twist_kernel, 1, nullptr, numGlobalThreads, numLocalThreads, 0, nullptr, nullptr); return err; } cl_int clFFT_1DTwistPlannar(clFFT_Plan Plan, cl_command_queue queue, cl_mem array_real, cl_mem array_imag, unsigned numRows, unsigned numCols, unsigned startRow, unsigned rowsToProcess, clFFT_Direction dir) { auto *plan = (cl_fft_plan *)Plan; unsigned int N = numRows * numCols; unsigned int nCols = numCols; unsigned int sRow = startRow; unsigned int rToProcess = rowsToProcess; int d = dir; int err = 0; cl_device_id device_id; err = clGetCommandQueueInfo(queue, CL_QUEUE_DEVICE, sizeof(cl_device_id), &device_id, nullptr); if (err) return err; size_t gSize; err = clGetKernelWorkGroupInfo(plan->twist_kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &gSize, nullptr); if (err) return err; gSize = min(128, gSize); size_t numGlobalThreads[1] = {max(numCols / gSize, 1) * gSize}; size_t numLocalThreads[1] = {gSize}; err |= clSetKernelArg(plan->twist_kernel, 0, sizeof(cl_mem), &array_real); err |= clSetKernelArg(plan->twist_kernel, 1, sizeof(cl_mem), &array_imag); err |= clSetKernelArg(plan->twist_kernel, 2, sizeof(unsigned int), &sRow); err |= clSetKernelArg(plan->twist_kernel, 3, sizeof(unsigned int), &nCols); err |= clSetKernelArg(plan->twist_kernel, 4, sizeof(unsigned int), &N); err |= clSetKernelArg(plan->twist_kernel, 5, sizeof(unsigned int), &rToProcess); err |= clSetKernelArg(plan->twist_kernel, 6, sizeof(int), &d); err |= clEnqueueNDRangeKernel(queue, plan->twist_kernel, 1, nullptr, numGlobalThreads, numLocalThreads, 0, nullptr, nullptr); return err; } src/algorithms/libs/opencl/fft_internal.h000066400000000000000000000156571352176506000210760ustar00rootroot00000000000000 // // File: fft_internal.h // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software. // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CLFFT_INTERNAL_H #define __CLFFT_INTERNAL_H #include "clFFT.h" #include #include #include using namespace std; typedef enum kernel_dir_t { cl_fft_kernel_x, cl_fft_kernel_y, cl_fft_kernel_z } cl_fft_kernel_dir; typedef struct kernel_info_t { cl_kernel kernel; char *kernel_name; unsigned lmem_size; unsigned num_workgroups; unsigned num_xforms_per_workgroup; unsigned num_workitems_per_workgroup; cl_fft_kernel_dir dir; int in_place_possible; kernel_info_t *next; } cl_fft_kernel_info; typedef struct { // context in which fft resources are created and kernels are executed cl_context context; // size of signal clFFT_Dim3 n; // dimension of transform ... must be either 1D, 2D or 3D clFFT_Dimension dim; // data format ... must be either interleaved or plannar clFFT_DataFormat format; // string containing kernel source. Generated at runtime based on // n, dim, format and other parameters string *kernel_string; // CL program containing source and kernel this particular // n, dim, data format cl_program program; // linked list of kernels which needs to be executed for this fft cl_fft_kernel_info *kernel_info; // number of kernels int num_kernels; // twist kernel for virtualizing fft of very large sizes that do not // fit in GPU global memory cl_kernel twist_kernel; // flag indicating if temporary intermediate buffer is needed or not. // this depends on fft kernels being executed and if transform is // in-place or out-of-place. e.g. Local memory fft (say 1D 1024 ... // one that does not require global transpose do not need temporary buffer) // 2D 1024x1024 out-of-place fft however do require intermediate buffer. // If temp buffer is needed, its allocation is lazy i.e. its not allocated // until its needed cl_int temp_buffer_needed; // Batch size is runtime parameter and size of temporary buffer (if needed) // depends on batch size. Allocation of temporary buffer is lazy i.e. its // only created when needed. Once its created at first call of clFFT_Executexxx // it is not allocated next time if next time clFFT_Executexxx is called with // batch size different than the first call. last_batch_size caches the last // batch size with which this plan is used so that we dont keep allocating/deallocating // temp buffer if same batch size is used again and again. unsigned last_batch_size; // temporary buffer for interleaved plan cl_mem tempmemobj; // temporary buffer for planner plan. Only one of tempmemobj or // (tempmemobj_real, tempmemobj_imag) pair is valid (allocated) depending // data format of plan (plannar or interleaved) cl_mem tempmemobj_real, tempmemobj_imag; // Maximum size of signal for which local memory transposed based // fft is sufficient i.e. no global mem transpose (communication) // is needed unsigned max_localmem_fft_size; // Maximum work items per work group allowed. This, along with max_radix below controls // maximum local memory being used by fft kernels of this plan. Set to 256 by default unsigned max_work_item_per_workgroup; // Maximum base radix for local memory fft ... this controls the maximum register // space used by work items. Currently defaults to 16 unsigned max_radix; // Device depended parameter that tells how many work-items need to be read consecutive // values to make sure global memory access by work-items of a work-group result in // coalesced memory access to utilize full bandwidth e.g. on NVidia tesla, this is 16 unsigned min_mem_coalesce_width; // Number of local memory banks. This is used to geneate kernel with local memory // transposes with appropriate padding to avoid bank conflicts to local memory // e.g. on NVidia it is 16. unsigned num_local_mem_banks; } cl_fft_plan; void FFT1D(cl_fft_plan *plan, cl_fft_kernel_dir dir); #endif src/algorithms/libs/opencl/fft_kernelstring.cc000066400000000000000000001672441352176506000221270ustar00rootroot00000000000000 // // File: fft_kernelstring.cpp // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software. // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #include "clFFT.h" #include "fft_internal.h" #include #include #include #include #include #include #include #include using namespace std; #define max(A, B) ((A) > (B) ? (A) : (B)) #define min(A, B) ((A) < (B) ? (A) : (B)) static string num2str(int num) { char temp[200]; sprintf(temp, "%d", num); return string(temp); } // For any n, this function decomposes n into factors for loacal memory tranpose // based fft. Factors (radices) are sorted such that the first one (radixArray[0]) // is the largest. This base radix determines the number of registers used by each // work item and product of remaining radices determine the size of work group needed. // To make things concrete with and example, suppose n = 1024. It is decomposed into // 1024 = 16 x 16 x 4. Hence kernel uses float2 a[16], for local in-register fft and // needs 16 x 4 = 64 work items per work group. So kernel first performance 64 length // 16 ffts (64 work items working in parallel) following by transpose using local // memory followed by again 64 length 16 ffts followed by transpose using local memory // followed by 256 length 4 ffts. For the last step since with size of work group is // 64 and each work item can array for 16 values, 64 work items can compute 256 length // 4 ffts by each work item computing 4 length 4 ffts. // Similarly for n = 2048 = 8 x 8 x 8 x 4, each work group has 8 x 8 x 4 = 256 work // iterms which each computes 256 (in-parallel) length 8 ffts in-register, followed // by transpose using local memory, followed by 256 length 8 in-register ffts, followed // by transpose using local memory, followed by 256 length 8 in-register ffts, followed // by transpose using local memory, followed by 512 length 4 in-register ffts. Again, // for the last step, each work item computes two length 4 in-register ffts and thus // 256 work items are needed to compute all 512 ffts. // For n = 32 = 8 x 4, 4 work items first compute 4 in-register // lenth 8 ffts, followed by transpose using local memory followed by 8 in-register // length 4 ffts, where each work item computes two length 4 ffts thus 4 work items // can compute 8 length 4 ffts. However if work group size of say 64 is choosen, // each work group can compute 64/ 4 = 16 size 32 ffts (batched transform). // Users can play with these parameters to figure what gives best performance on // their particular device i.e. some device have less register space thus using // smaller base radix can avoid spilling ... some has small local memory thus // using smaller work group size may be required etc static void getRadixArray(unsigned int n, unsigned int *radixArray, unsigned int *numRadices, unsigned int maxRadix) { if (maxRadix > 1) { maxRadix = min(n, maxRadix); unsigned int cnt = 0; while (n > maxRadix) { radixArray[cnt++] = maxRadix; n /= maxRadix; } radixArray[cnt++] = n; *numRadices = cnt; return; } switch (n) { case 2: *numRadices = 1; radixArray[0] = 2; break; case 4: *numRadices = 1; radixArray[0] = 4; break; case 8: *numRadices = 1; radixArray[0] = 8; break; case 16: *numRadices = 2; radixArray[0] = 8; radixArray[1] = 2; break; case 32: *numRadices = 2; radixArray[0] = 8; radixArray[1] = 4; break; case 64: *numRadices = 2; radixArray[0] = 8; radixArray[1] = 8; break; case 128: *numRadices = 3; radixArray[0] = 8; radixArray[1] = 4; radixArray[2] = 4; break; case 256: *numRadices = 4; radixArray[0] = 4; radixArray[1] = 4; radixArray[2] = 4; radixArray[3] = 4; break; case 512: *numRadices = 3; radixArray[0] = 8; radixArray[1] = 8; radixArray[2] = 8; break; case 1024: *numRadices = 3; radixArray[0] = 16; radixArray[1] = 16; radixArray[2] = 4; break; case 2048: *numRadices = 4; radixArray[0] = 8; radixArray[1] = 8; radixArray[2] = 8; radixArray[3] = 4; break; default: *numRadices = 0; return; } } static void insertHeader(string &kernelString, string &kernelName, clFFT_DataFormat dataFormat) { if (dataFormat == clFFT_SplitComplexFormat) kernelString += string("__kernel void ") + kernelName + string("(__global float *in_real, __global float *in_imag, __global float *out_real, __global float *out_imag, int dir, int S)\n"); else kernelString += string("__kernel void ") + kernelName + string("(__global float2 *in, __global float2 *out, int dir, int S)\n"); } static void insertVariables(string &kStream, int maxRadix) { kStream += string(" int i, j, r, indexIn, indexOut, index, tid, bNum, xNum, k, l;\n"); kStream += string(" int s, ii, jj, offset;\n"); kStream += string(" float2 w;\n"); kStream += string(" float ang, angf, ang1;\n"); kStream += string(" __local float *lMemStore, *lMemLoad;\n"); kStream += string(" float2 a[") + num2str(maxRadix) + string("];\n"); kStream += string(" int lId = get_local_id( 0 );\n"); kStream += string(" int groupId = get_group_id( 0 );\n"); } static void formattedLoad(string &kernelString, int aIndex, int gIndex, clFFT_DataFormat dataFormat) { if (dataFormat == clFFT_InterleavedComplexFormat) kernelString += string(" a[") + num2str(aIndex) + string("] = in[") + num2str(gIndex) + string("];\n"); else { kernelString += string(" a[") + num2str(aIndex) + string("].x = in_real[") + num2str(gIndex) + string("];\n"); kernelString += string(" a[") + num2str(aIndex) + string("].y = in_imag[") + num2str(gIndex) + string("];\n"); } } static void formattedStore(string &kernelString, int aIndex, int gIndex, clFFT_DataFormat dataFormat) { if (dataFormat == clFFT_InterleavedComplexFormat) kernelString += string(" out[") + num2str(gIndex) + string("] = a[") + num2str(aIndex) + string("];\n"); else { kernelString += string(" out_real[") + num2str(gIndex) + string("] = a[") + num2str(aIndex) + string("].x;\n"); kernelString += string(" out_imag[") + num2str(gIndex) + string("] = a[") + num2str(aIndex) + string("].y;\n"); } } static int insertGlobalLoadsAndTranspose(string &kernelString, int N, int numWorkItemsPerXForm, int numXFormsPerWG, int R0, int mem_coalesce_width, clFFT_DataFormat dataFormat) { int log2NumWorkItemsPerXForm = (int)log2(numWorkItemsPerXForm); int groupSize = numWorkItemsPerXForm * numXFormsPerWG; int i, j; int lMemSize = 0; if (numXFormsPerWG > 1) kernelString += string(" s = S & ") + num2str(numXFormsPerWG - 1) + string(";\n"); if (numWorkItemsPerXForm >= mem_coalesce_width) { if (numXFormsPerWG > 1) { kernelString += string(" ii = lId & ") + num2str(numWorkItemsPerXForm - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str(log2NumWorkItemsPerXForm) + string(";\n"); kernelString += string(" if( !s || (groupId < get_num_groups(0)-1) || (jj < s) ) {\n"); kernelString += string(" offset = mad24( mad24(groupId, ") + num2str(numXFormsPerWG) + string(", jj), ") + num2str(N) + string(", ii );\n"); if (dataFormat == clFFT_InterleavedComplexFormat) { kernelString += string(" in += offset;\n"); kernelString += string(" out += offset;\n"); } else { kernelString += string(" in_real += offset;\n"); kernelString += string(" in_imag += offset;\n"); kernelString += string(" out_real += offset;\n"); kernelString += string(" out_imag += offset;\n"); } for (i = 0; i < R0; i++) formattedLoad(kernelString, i, i * numWorkItemsPerXForm, dataFormat); kernelString += string(" }\n"); } else { kernelString += string(" ii = lId;\n"); kernelString += string(" jj = 0;\n"); kernelString += string(" offset = mad24(groupId, ") + num2str(N) + string(", ii);\n"); if (dataFormat == clFFT_InterleavedComplexFormat) { kernelString += string(" in += offset;\n"); kernelString += string(" out += offset;\n"); } else { kernelString += string(" in_real += offset;\n"); kernelString += string(" in_imag += offset;\n"); kernelString += string(" out_real += offset;\n"); kernelString += string(" out_imag += offset;\n"); } for (i = 0; i < R0; i++) formattedLoad(kernelString, i, i * numWorkItemsPerXForm, dataFormat); } } else if (N >= mem_coalesce_width) { int numInnerIter = N / mem_coalesce_width; int numOuterIter = numXFormsPerWG / (groupSize / mem_coalesce_width); kernelString += string(" ii = lId & ") + num2str(mem_coalesce_width - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str((int)log2(mem_coalesce_width)) + string(";\n"); kernelString += string(" lMemStore = sMem + mad24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); kernelString += string(" offset = mad24( groupId, ") + num2str(numXFormsPerWG) + string(", jj);\n"); kernelString += string(" offset = mad24( offset, ") + num2str(N) + string(", ii );\n"); if (dataFormat == clFFT_InterleavedComplexFormat) { kernelString += string(" in += offset;\n"); kernelString += string(" out += offset;\n"); } else { kernelString += string(" in_real += offset;\n"); kernelString += string(" in_imag += offset;\n"); kernelString += string(" out_real += offset;\n"); kernelString += string(" out_imag += offset;\n"); } kernelString += string("if((groupId == get_num_groups(0)-1) && s) {\n"); for (i = 0; i < numOuterIter; i++) { kernelString += string(" if( jj < s ) {\n"); for (j = 0; j < numInnerIter; j++) formattedLoad(kernelString, i * numInnerIter + j, j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * N, dataFormat); kernelString += string(" }\n"); if (i != numOuterIter - 1) kernelString += string(" jj += ") + num2str(groupSize / mem_coalesce_width) + string(";\n"); } kernelString += string("}\n "); kernelString += string("else {\n"); for (i = 0; i < numOuterIter; i++) { for (j = 0; j < numInnerIter; j++) formattedLoad(kernelString, i * numInnerIter + j, j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * N, dataFormat); } kernelString += string("}\n"); kernelString += string(" ii = lId & ") + num2str(numWorkItemsPerXForm - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str(log2NumWorkItemsPerXForm) + string(";\n"); kernelString += string(" lMemLoad = sMem + mad24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(", ii);\n"); for (i = 0; i < numOuterIter; i++) { for (j = 0; j < numInnerIter; j++) { kernelString += string(" lMemStore[") + num2str(j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * (N + numWorkItemsPerXForm)) + string("] = a[") + num2str(i * numInnerIter + j) + string("].x;\n"); } } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < R0; i++) kernelString += string(" a[") + num2str(i) + string("].x = lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < numOuterIter; i++) { for (j = 0; j < numInnerIter; j++) { kernelString += string(" lMemStore[") + num2str(j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * (N + numWorkItemsPerXForm)) + string("] = a[") + num2str(i * numInnerIter + j) + string("].y;\n"); } } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < R0; i++) kernelString += string(" a[") + num2str(i) + string("].y = lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); lMemSize = (N + numWorkItemsPerXForm) * numXFormsPerWG; } else { kernelString += string(" offset = mad24( groupId, ") + num2str(N * numXFormsPerWG) + string(", lId );\n"); if (dataFormat == clFFT_InterleavedComplexFormat) { kernelString += string(" in += offset;\n"); kernelString += string(" out += offset;\n"); } else { kernelString += string(" in_real += offset;\n"); kernelString += string(" in_imag += offset;\n"); kernelString += string(" out_real += offset;\n"); kernelString += string(" out_imag += offset;\n"); } kernelString += string(" ii = lId & ") + num2str(N - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str((int)log2(N)) + string(";\n"); kernelString += string(" lMemStore = sMem + mad24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); kernelString += string("if((groupId == get_num_groups(0)-1) && s) {\n"); for (i = 0; i < R0; i++) { kernelString += string(" if(jj < s )\n"); formattedLoad(kernelString, i, i * groupSize, dataFormat); if (i != R0 - 1) kernelString += string(" jj += ") + num2str(groupSize / N) + string(";\n"); } kernelString += string("}\n"); kernelString += string("else {\n"); for (i = 0; i < R0; i++) { formattedLoad(kernelString, i, i * groupSize, dataFormat); } kernelString += string("}\n"); if (numWorkItemsPerXForm > 1) { kernelString += string(" ii = lId & ") + num2str(numWorkItemsPerXForm - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str(log2NumWorkItemsPerXForm) + string(";\n"); kernelString += string(" lMemLoad = sMem + mad24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); } else { kernelString += string(" ii = 0;\n"); kernelString += string(" jj = lId;\n"); kernelString += string(" lMemLoad = sMem + mul24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(");\n"); } for (i = 0; i < R0; i++) kernelString += string(" lMemStore[") + num2str(i * (groupSize / N) * (N + numWorkItemsPerXForm)) + string("] = a[") + num2str(i) + string("].x;\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < R0; i++) kernelString += string(" a[") + num2str(i) + string("].x = lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < R0; i++) kernelString += string(" lMemStore[") + num2str(i * (groupSize / N) * (N + numWorkItemsPerXForm)) + string("] = a[") + num2str(i) + string("].y;\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < R0; i++) kernelString += string(" a[") + num2str(i) + string("].y = lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); lMemSize = (N + numWorkItemsPerXForm) * numXFormsPerWG; } return lMemSize; } static int insertGlobalStoresAndTranspose(string &kernelString, int N, int maxRadix, int Nr, int numWorkItemsPerXForm, int numXFormsPerWG, int mem_coalesce_width, clFFT_DataFormat dataFormat) { int groupSize = numWorkItemsPerXForm * numXFormsPerWG; int i, j, k, ind; int lMemSize = 0; int numIter = maxRadix / Nr; string indent = string(""); if (numWorkItemsPerXForm >= mem_coalesce_width) { if (numXFormsPerWG > 1) { kernelString += string(" if( !s || (groupId < get_num_groups(0)-1) || (jj < s) ) {\n"); indent = string(" "); } for (i = 0; i < maxRadix; i++) { j = i % numIter; k = i / numIter; ind = j * Nr + k; formattedStore(kernelString, ind, i * numWorkItemsPerXForm, dataFormat); } if (numXFormsPerWG > 1) kernelString += string(" }\n"); } else if (N >= mem_coalesce_width) { int numInnerIter = N / mem_coalesce_width; int numOuterIter = numXFormsPerWG / (groupSize / mem_coalesce_width); kernelString += string(" lMemLoad = sMem + mad24( jj, ") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); kernelString += string(" ii = lId & ") + num2str(mem_coalesce_width - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str((int)log2(mem_coalesce_width)) + string(";\n"); kernelString += string(" lMemStore = sMem + mad24( jj,") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); for (i = 0; i < maxRadix; i++) { j = i % numIter; k = i / numIter; ind = j * Nr + k; kernelString += string(" lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("] = a[") + num2str(ind) + string("].x;\n"); } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < numOuterIter; i++) for (j = 0; j < numInnerIter; j++) kernelString += string(" a[") + num2str(i * numInnerIter + j) + string("].x = lMemStore[") + num2str(j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * (N + numWorkItemsPerXForm)) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < maxRadix; i++) { j = i % numIter; k = i / numIter; ind = j * Nr + k; kernelString += string(" lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("] = a[") + num2str(ind) + string("].y;\n"); } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < numOuterIter; i++) for (j = 0; j < numInnerIter; j++) kernelString += string(" a[") + num2str(i * numInnerIter + j) + string("].y = lMemStore[") + num2str(j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * (N + numWorkItemsPerXForm)) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); kernelString += string("if((groupId == get_num_groups(0)-1) && s) {\n"); for (i = 0; i < numOuterIter; i++) { kernelString += string(" if( jj < s ) {\n"); for (j = 0; j < numInnerIter; j++) formattedStore(kernelString, i * numInnerIter + j, j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * N, dataFormat); kernelString += string(" }\n"); if (i != numOuterIter - 1) kernelString += string(" jj += ") + num2str(groupSize / mem_coalesce_width) + string(";\n"); } kernelString += string("}\n"); kernelString += string("else {\n"); for (i = 0; i < numOuterIter; i++) { for (j = 0; j < numInnerIter; j++) formattedStore(kernelString, i * numInnerIter + j, j * mem_coalesce_width + i * (groupSize / mem_coalesce_width) * N, dataFormat); } kernelString += string("}\n"); lMemSize = (N + numWorkItemsPerXForm) * numXFormsPerWG; } else { kernelString += string(" lMemLoad = sMem + mad24( jj,") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); kernelString += string(" ii = lId & ") + num2str(N - 1) + string(";\n"); kernelString += string(" jj = lId >> ") + num2str((int)log2(N)) + string(";\n"); kernelString += string(" lMemStore = sMem + mad24( jj,") + num2str(N + numWorkItemsPerXForm) + string(", ii );\n"); for (i = 0; i < maxRadix; i++) { j = i % numIter; k = i / numIter; ind = j * Nr + k; kernelString += string(" lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("] = a[") + num2str(ind) + string("].x;\n"); } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < maxRadix; i++) kernelString += string(" a[") + num2str(i) + string("].x = lMemStore[") + num2str(i * (groupSize / N) * (N + numWorkItemsPerXForm)) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < maxRadix; i++) { j = i % numIter; k = i / numIter; ind = j * Nr + k; kernelString += string(" lMemLoad[") + num2str(i * numWorkItemsPerXForm) + string("] = a[") + num2str(ind) + string("].y;\n"); } kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); for (i = 0; i < maxRadix; i++) kernelString += string(" a[") + num2str(i) + string("].y = lMemStore[") + num2str(i * (groupSize / N) * (N + numWorkItemsPerXForm)) + string("];\n"); kernelString += string(" barrier( CLK_LOCAL_MEM_FENCE );\n"); kernelString += string("if((groupId == get_num_groups(0)-1) && s) {\n"); for (i = 0; i < maxRadix; i++) { kernelString += string(" if(jj < s ) {\n"); formattedStore(kernelString, i, i * groupSize, dataFormat); kernelString += string(" }\n"); if (i != maxRadix - 1) kernelString += string(" jj +=") + num2str(groupSize / N) + string(";\n"); } kernelString += string("}\n"); kernelString += string("else {\n"); for (i = 0; i < maxRadix; i++) { formattedStore(kernelString, i, i * groupSize, dataFormat); } kernelString += string("}\n"); lMemSize = (N + numWorkItemsPerXForm) * numXFormsPerWG; } return lMemSize; } static void insertfftKernel(string &kernelString, int Nr, int numIter) { int i; for (i = 0; i < numIter; i++) { kernelString += string(" fftKernel") + num2str(Nr) + string("(a+") + num2str(i * Nr) + string(", dir);\n"); } } static void insertTwiddleKernel(string &kernelString, int Nr, int numIter, int Nprev, int len, int numWorkItemsPerXForm) { int z, k; int logNPrev = (int)log2(Nprev); for (z = 0; z < numIter; z++) { if (z == 0) { if (Nprev > 1) kernelString += string(" angf = (float) (ii >> ") + num2str(logNPrev) + string(");\n"); else kernelString += string(" angf = (float) ii;\n"); } else { if (Nprev > 1) kernelString += string(" angf = (float) ((") + num2str(z * numWorkItemsPerXForm) + string(" + ii) >>") + num2str(logNPrev) + string(");\n"); else kernelString += string(" angf = (float) (") + num2str(z * numWorkItemsPerXForm) + string(" + ii);\n"); } for (k = 1; k < Nr; k++) { int ind = z * Nr + k; //float fac = (float) (2.0 * M_PI * (double) k / (double) len); kernelString += string(" ang = dir * ( 2.0f * M_PI * ") + num2str(k) + string(".0f / ") + num2str(len) + string(".0f )") + string(" * angf;\n"); kernelString += string(" w = (float2)(native_cos(ang), native_sin(ang));\n"); kernelString += string(" a[") + num2str(ind) + string("] = complexMul(a[") + num2str(ind) + string("], w);\n"); } } } static int getPadding(int numWorkItemsPerXForm, int Nprev, int numWorkItemsReq, int numXFormsPerWG, int Nr, int numBanks, int *offset, int *midPad) { if ((numWorkItemsPerXForm <= Nprev) || (Nprev >= numBanks)) *offset = 0; else { int numRowsReq = ((numWorkItemsPerXForm < numBanks) ? numWorkItemsPerXForm : numBanks) / Nprev; int numColsReq = 1; if (numRowsReq > Nr) numColsReq = numRowsReq / Nr; numColsReq = Nprev * numColsReq; *offset = numColsReq; } if (numWorkItemsPerXForm >= numBanks || numXFormsPerWG == 1) *midPad = 0; else { int bankNum = ((numWorkItemsReq + *offset) * Nr) & (numBanks - 1); if (bankNum >= numWorkItemsPerXForm) *midPad = 0; else *midPad = numWorkItemsPerXForm - bankNum; } int lMemSize = (numWorkItemsReq + *offset) * Nr * numXFormsPerWG + *midPad * (numXFormsPerWG - 1); return lMemSize; } static void insertLocalStores(string &kernelString, int numIter, int Nr, int numWorkItemsPerXForm, int numWorkItemsReq, int offset, string &comp) { int z, k; for (z = 0; z < numIter; z++) { for (k = 0; k < Nr; k++) { int index = k * (numWorkItemsReq + offset) + z * numWorkItemsPerXForm; kernelString += string(" lMemStore[") + num2str(index) + string("] = a[") + num2str(z * Nr + k) + string("].") + comp + string(";\n"); } } kernelString += string(" barrier(CLK_LOCAL_MEM_FENCE);\n"); } static void insertLocalLoads(string &kernelString, int n, int Nr, int Nrn, int Nprev, int Ncurr, int numWorkItemsPerXForm, int numWorkItemsReq, int offset, string &comp) { int numWorkItemsReqN = n / Nrn; int interBlockHNum = max(Nprev / numWorkItemsPerXForm, 1); int interBlockHStride = numWorkItemsPerXForm; int vertWidth = max(numWorkItemsPerXForm / Nprev, 1); vertWidth = min(vertWidth, Nr); int vertNum = Nr / vertWidth; int vertStride = (n / Nr + offset) * vertWidth; int iter = max(numWorkItemsReqN / numWorkItemsPerXForm, 1); int intraBlockHStride = (numWorkItemsPerXForm / (Nprev * Nr)) > 1 ? (numWorkItemsPerXForm / (Nprev * Nr)) : 1; intraBlockHStride *= Nprev; int stride = numWorkItemsReq / Nrn; int i; for (i = 0; i < iter; i++) { int ii = i / (interBlockHNum * vertNum); int zz = i % (interBlockHNum * vertNum); int jj = zz % interBlockHNum; int kk = zz / interBlockHNum; int z; for (z = 0; z < Nrn; z++) { int st = kk * vertStride + jj * interBlockHStride + ii * intraBlockHStride + z * stride; kernelString += string(" a[") + num2str(i * Nrn + z) + string("].") + comp + string(" = lMemLoad[") + num2str(st) + string("];\n"); } } kernelString += string(" barrier(CLK_LOCAL_MEM_FENCE);\n"); } static void insertLocalLoadIndexArithmatic(string &kernelString, int Nprev, int Nr, int numWorkItemsReq, int numWorkItemsPerXForm, int numXFormsPerWG, int offset, int midPad) { int Ncurr = Nprev * Nr; int logNcurr = (int)log2(Ncurr); int logNprev = (int)log2(Nprev); int incr = (numWorkItemsReq + offset) * Nr + midPad; if (Ncurr < numWorkItemsPerXForm) { if (Nprev == 1) kernelString += string(" j = ii & ") + num2str(Ncurr - 1) + string(";\n"); else kernelString += string(" j = (ii & ") + num2str(Ncurr - 1) + string(") >> ") + num2str(logNprev) + string(";\n"); if (Nprev == 1) kernelString += string(" i = ii >> ") + num2str(logNcurr) + string(";\n"); else kernelString += string(" i = mad24(ii >> ") + num2str(logNcurr) + string(", ") + num2str(Nprev) + string(", ii & ") + num2str(Nprev - 1) + string(");\n"); } else { if (Nprev == 1) kernelString += string(" j = ii;\n"); else kernelString += string(" j = ii >> ") + num2str(logNprev) + string(";\n"); if (Nprev == 1) kernelString += string(" i = 0;\n"); else kernelString += string(" i = ii & ") + num2str(Nprev - 1) + string(";\n"); } if (numXFormsPerWG > 1) kernelString += string(" i = mad24(jj, ") + num2str(incr) + string(", i);\n"); kernelString += string(" lMemLoad = sMem + mad24(j, ") + num2str(numWorkItemsReq + offset) + string(", i);\n"); } static void insertLocalStoreIndexArithmatic(string &kernelString, int numWorkItemsReq, int numXFormsPerWG, int Nr, int offset, int midPad) { if (numXFormsPerWG == 1) { kernelString += string(" lMemStore = sMem + ii;\n"); } else { kernelString += string(" lMemStore = sMem + mad24(jj, ") + num2str((numWorkItemsReq + offset) * Nr + midPad) + string(", ii);\n"); } } static void createLocalMemfftKernelString(cl_fft_plan *plan) { unsigned int radixArray[10]; unsigned int numRadix; unsigned int n = plan->n.x; assert(n <= plan->max_work_item_per_workgroup * plan->max_radix && "signal lenght too big for local mem fft\n"); getRadixArray(n, radixArray, &numRadix, 0); assert(numRadix > 0 && "no radix array supplied\n"); if (n / radixArray[0] > plan->max_work_item_per_workgroup) getRadixArray(n, radixArray, &numRadix, plan->max_radix); assert(radixArray[0] <= plan->max_radix && "max radix choosen is greater than allowed\n"); assert(n / radixArray[0] <= plan->max_work_item_per_workgroup && "required work items per xform greater than maximum work items allowed per work group for local mem fft\n"); unsigned int tmpLen = 1; unsigned int i; for (i = 0; i < numRadix; i++) { assert(radixArray[i] && !((radixArray[i] - 1) & radixArray[i])); tmpLen *= radixArray[i]; } assert(tmpLen == n && "product of radices choosen doesnt match the length of signal\n"); int offset, midPad; string localString(""), kernelName(""); clFFT_DataFormat dataFormat = plan->format; string *kernelString = plan->kernel_string; cl_fft_kernel_info **kInfo = &plan->kernel_info; int kCount = 0; while (*kInfo) { kInfo = &(*kInfo)->next; kCount++; } kernelName = string("fft") + num2str(kCount); *kInfo = (cl_fft_kernel_info *)malloc(sizeof(cl_fft_kernel_info)); (*kInfo)->kernel = nullptr; (*kInfo)->lmem_size = 0; (*kInfo)->num_workgroups = 0; (*kInfo)->num_workitems_per_workgroup = 0; (*kInfo)->dir = cl_fft_kernel_x; (*kInfo)->in_place_possible = 1; (*kInfo)->next = nullptr; (*kInfo)->kernel_name = (char *)malloc(sizeof(char) * (kernelName.size() + 1)); strcpy((*kInfo)->kernel_name, kernelName.c_str()); unsigned int numWorkItemsPerXForm = n / radixArray[0]; unsigned int numWorkItemsPerWG = numWorkItemsPerXForm <= 64 ? 64 : numWorkItemsPerXForm; assert(numWorkItemsPerWG <= plan->max_work_item_per_workgroup); int numXFormsPerWG = numWorkItemsPerWG / numWorkItemsPerXForm; (*kInfo)->num_workgroups = 1; (*kInfo)->num_xforms_per_workgroup = numXFormsPerWG; (*kInfo)->num_workitems_per_workgroup = numWorkItemsPerWG; unsigned int *N = radixArray; unsigned int maxRadix = N[0]; unsigned int lMemSize = 0; insertVariables(localString, maxRadix); lMemSize = insertGlobalLoadsAndTranspose(localString, n, numWorkItemsPerXForm, numXFormsPerWG, maxRadix, plan->min_mem_coalesce_width, dataFormat); (*kInfo)->lmem_size = (lMemSize > (*kInfo)->lmem_size) ? lMemSize : (*kInfo)->lmem_size; string xcomp = string("x"); string ycomp = string("y"); unsigned int Nprev = 1; unsigned int len = n; unsigned int r; for (r = 0; r < numRadix; r++) { int numIter = N[0] / N[r]; int numWorkItemsReq = n / N[r]; int Ncurr = Nprev * N[r]; insertfftKernel(localString, N[r], numIter); if (r < (numRadix - 1)) { insertTwiddleKernel(localString, N[r], numIter, Nprev, len, numWorkItemsPerXForm); lMemSize = getPadding(numWorkItemsPerXForm, Nprev, numWorkItemsReq, numXFormsPerWG, N[r], plan->num_local_mem_banks, &offset, &midPad); (*kInfo)->lmem_size = (lMemSize > (*kInfo)->lmem_size) ? lMemSize : (*kInfo)->lmem_size; insertLocalStoreIndexArithmatic(localString, numWorkItemsReq, numXFormsPerWG, N[r], offset, midPad); insertLocalLoadIndexArithmatic(localString, Nprev, N[r], numWorkItemsReq, numWorkItemsPerXForm, numXFormsPerWG, offset, midPad); insertLocalStores(localString, numIter, N[r], numWorkItemsPerXForm, numWorkItemsReq, offset, xcomp); insertLocalLoads(localString, n, N[r], N[r + 1], Nprev, Ncurr, numWorkItemsPerXForm, numWorkItemsReq, offset, xcomp); insertLocalStores(localString, numIter, N[r], numWorkItemsPerXForm, numWorkItemsReq, offset, ycomp); insertLocalLoads(localString, n, N[r], N[r + 1], Nprev, Ncurr, numWorkItemsPerXForm, numWorkItemsReq, offset, ycomp); Nprev = Ncurr; len = len / N[r]; } } lMemSize = insertGlobalStoresAndTranspose(localString, n, maxRadix, N[numRadix - 1], numWorkItemsPerXForm, numXFormsPerWG, plan->min_mem_coalesce_width, dataFormat); (*kInfo)->lmem_size = (lMemSize > (*kInfo)->lmem_size) ? lMemSize : (*kInfo)->lmem_size; insertHeader(*kernelString, kernelName, dataFormat); *kernelString += string("{\n"); if ((*kInfo)->lmem_size) *kernelString += string(" __local float sMem[") + num2str((*kInfo)->lmem_size) + string("];\n"); *kernelString += localString; *kernelString += string("}\n"); } // For n larger than what can be computed using local memory fft, global transposes // multiple kernel launces is needed. For these sizes, n can be decomposed using // much larger base radices i.e. say n = 262144 = 128 x 64 x 32. Thus three kernel // launches will be needed, first computing 64 x 32, length 128 ffts, second computing // 128 x 32 length 64 ffts, and finally a kernel computing 128 x 64 length 32 ffts. // Each of these base radices can futher be divided into factors so that each of these // base ffts can be computed within one kernel launch using in-register ffts and local // memory transposes i.e for the first kernel above which computes 64 x 32 ffts on length // 128, 128 can be decomposed into 128 = 16 x 8 i.e. 8 work items can compute 8 length // 16 ffts followed by transpose using local memory followed by each of these eight // work items computing 2 length 8 ffts thus computing 16 length 8 ffts in total. This // means only 8 work items are needed for computing one length 128 fft. If we choose // work group size of say 64, we can compute 64/8 = 8 length 128 ffts within one // work group. Since we need to compute 64 x 32 length 128 ffts in first kernel, this // means we need to launch 64 x 32 / 8 = 256 work groups with 64 work items in each // work group where each work group is computing 8 length 128 ffts where each length // 128 fft is computed by 8 work items. Same logic can be applied to other two kernels // in this example. Users can play with difference base radices and difference // decompositions of base radices to generates different kernels and see which gives // best performance. Following function is just fixed to use 128 as base radix void getGlobalRadixInfo(int n, int *radix, int *R1, int *R2, int *numRadices) { int baseRadix = min(n, 128); int numR = 0; int N = n; while (N > baseRadix) { N /= baseRadix; numR++; } for (int i = 0; i < numR; i++) radix[i] = baseRadix; radix[numR] = N; numR++; *numRadices = numR; for (int i = 0; i < numR; i++) { int B = radix[i]; if (B <= 8) { R1[i] = B; R2[i] = 1; continue; } int r1 = 2; int r2 = B / r1; while (r2 > r1) { r1 *= 2; r2 = B / r1; } R1[i] = r1; R2[i] = r2; } } static void createGlobalFFTKernelString(cl_fft_plan *plan, int n, int BS, cl_fft_kernel_dir dir, int vertBS) { int i, j, k, t; int radixArr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int R1Arr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int R2Arr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int radix, R1, R2; int numRadices; int maxThreadsPerBlock = plan->max_work_item_per_workgroup; int maxArrayLen = plan->max_radix; int batchSize = plan->min_mem_coalesce_width; clFFT_DataFormat dataFormat = plan->format; int vertical = (dir == cl_fft_kernel_x) ? 0 : 1; getGlobalRadixInfo(n, radixArr, R1Arr, R2Arr, &numRadices); int numPasses = numRadices; string localString(""), kernelName(""); string *kernelString = plan->kernel_string; cl_fft_kernel_info **kInfo = &plan->kernel_info; int kCount = 0; while (*kInfo) { kInfo = &(*kInfo)->next; kCount++; } int N = n; int m = (int)log2(n); int Rinit = vertical ? BS : 1; batchSize = vertical ? min(BS, batchSize) : batchSize; int passNum; for (passNum = 0; passNum < numPasses; passNum++) { localString.clear(); kernelName.clear(); radix = radixArr[passNum]; R1 = R1Arr[passNum]; R2 = R2Arr[passNum]; int strideI = Rinit; for (i = 0; i < numPasses; i++) if (i != passNum) strideI *= radixArr[i]; int strideO = Rinit; for (i = 0; i < passNum; i++) strideO *= radixArr[i]; int threadsPerXForm = R2; batchSize = R2 == 1 ? plan->max_work_item_per_workgroup : batchSize; batchSize = min(batchSize, strideI); int threadsPerBlock = batchSize * threadsPerXForm; threadsPerBlock = min(threadsPerBlock, maxThreadsPerBlock); batchSize = threadsPerBlock / threadsPerXForm; assert(R2 <= R1); assert(R1 * R2 == radix); assert(R1 <= maxArrayLen); assert(threadsPerBlock <= maxThreadsPerBlock); int numIter = R1 / R2; int gInInc = threadsPerBlock / batchSize; int lgStrideO = (int)log2(strideO); int numBlocksPerXForm = strideI / batchSize; int numBlocks = numBlocksPerXForm; if (!vertical) numBlocks *= BS; else numBlocks *= vertBS; kernelName = string("fft") + num2str(kCount); *kInfo = (cl_fft_kernel_info *)malloc(sizeof(cl_fft_kernel_info)); (*kInfo)->kernel = nullptr; if (R2 == 1) (*kInfo)->lmem_size = 0; else { if (strideO == 1) (*kInfo)->lmem_size = (radix + 1) * batchSize; else (*kInfo)->lmem_size = threadsPerBlock * R1; } (*kInfo)->num_workgroups = numBlocks; (*kInfo)->num_xforms_per_workgroup = 1; (*kInfo)->num_workitems_per_workgroup = threadsPerBlock; (*kInfo)->dir = dir; if ((passNum == (numPasses - 1)) && (numPasses & 1)) (*kInfo)->in_place_possible = 1; else (*kInfo)->in_place_possible = 0; (*kInfo)->next = nullptr; (*kInfo)->kernel_name = (char *)malloc(sizeof(char) * (kernelName.size() + 1)); strcpy((*kInfo)->kernel_name, kernelName.c_str()); insertVariables(localString, R1); if (vertical) { localString += string("xNum = groupId >> ") + num2str((int)log2(numBlocksPerXForm)) + string(";\n"); localString += string("groupId = groupId & ") + num2str(numBlocksPerXForm - 1) + string(";\n"); localString += string("indexIn = mad24(groupId, ") + num2str(batchSize) + string(", xNum << ") + num2str((int)log2(n * BS)) + string(");\n"); localString += string("tid = mul24(groupId, ") + num2str(batchSize) + string(");\n"); localString += string("i = tid >> ") + num2str(lgStrideO) + string(";\n"); localString += string("j = tid & ") + num2str(strideO - 1) + string(";\n"); int stride = radix * Rinit; for (i = 0; i < passNum; i++) stride *= radixArr[i]; localString += string("indexOut = mad24(i, ") + num2str(stride) + string(", j + ") + string("(xNum << ") + num2str((int)log2(n * BS)) + string("));\n"); localString += string("bNum = groupId;\n"); } else { int lgNumBlocksPerXForm = (int)log2(numBlocksPerXForm); localString += string("bNum = groupId & ") + num2str(numBlocksPerXForm - 1) + string(";\n"); localString += string("xNum = groupId >> ") + num2str(lgNumBlocksPerXForm) + string(";\n"); localString += string("indexIn = mul24(bNum, ") + num2str(batchSize) + string(");\n"); localString += string("tid = indexIn;\n"); localString += string("i = tid >> ") + num2str(lgStrideO) + string(";\n"); localString += string("j = tid & ") + num2str(strideO - 1) + string(";\n"); int stride = radix * Rinit; for (i = 0; i < passNum; i++) stride *= radixArr[i]; localString += string("indexOut = mad24(i, ") + num2str(stride) + string(", j);\n"); localString += string("indexIn += (xNum << ") + num2str(m) + string(");\n"); localString += string("indexOut += (xNum << ") + num2str(m) + string(");\n"); } // Load Data int lgBatchSize = (int)log2(batchSize); localString += string("tid = lId;\n"); localString += string("i = tid & ") + num2str(batchSize - 1) + string(";\n"); localString += string("j = tid >> ") + num2str(lgBatchSize) + string(";\n"); localString += string("indexIn += mad24(j, ") + num2str(strideI) + string(", i);\n"); if (dataFormat == clFFT_SplitComplexFormat) { localString += string("in_real += indexIn;\n"); localString += string("in_imag += indexIn;\n"); for (j = 0; j < R1; j++) localString += string("a[") + num2str(j) + string("].x = in_real[") + num2str(j * gInInc * strideI) + string("];\n"); for (j = 0; j < R1; j++) localString += string("a[") + num2str(j) + string("].y = in_imag[") + num2str(j * gInInc * strideI) + string("];\n"); } else { localString += string("in += indexIn;\n"); for (j = 0; j < R1; j++) localString += string("a[") + num2str(j) + string("] = in[") + num2str(j * gInInc * strideI) + string("];\n"); } localString += string("fftKernel") + num2str(R1) + string("(a, dir);\n"); if (R2 > 1) { // twiddle for (k = 1; k < R1; k++) { localString += string("ang = dir*(2.0f*M_PI*") + num2str(k) + string("/") + num2str(radix) + string(")*j;\n"); localString += string("w = (float2)(native_cos(ang), native_sin(ang));\n"); localString += string("a[") + num2str(k) + string("] = complexMul(a[") + num2str(k) + string("], w);\n"); } // shuffle numIter = R1 / R2; localString += string("indexIn = mad24(j, ") + num2str(threadsPerBlock * numIter) + string(", i);\n"); localString += string("lMemStore = sMem + tid;\n"); localString += string("lMemLoad = sMem + indexIn;\n"); for (k = 0; k < R1; k++) localString += string("lMemStore[") + num2str(k * threadsPerBlock) + string("] = a[") + num2str(k) + string("].x;\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); for (k = 0; k < numIter; k++) for (t = 0; t < R2; t++) localString += string("a[") + num2str(k * R2 + t) + string("].x = lMemLoad[") + num2str(t * batchSize + k * threadsPerBlock) + string("];\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); for (k = 0; k < R1; k++) localString += string("lMemStore[") + num2str(k * threadsPerBlock) + string("] = a[") + num2str(k) + string("].y;\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); for (k = 0; k < numIter; k++) for (t = 0; t < R2; t++) localString += string("a[") + num2str(k * R2 + t) + string("].y = lMemLoad[") + num2str(t * batchSize + k * threadsPerBlock) + string("];\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); for (j = 0; j < numIter; j++) localString += string("fftKernel") + num2str(R2) + string("(a + ") + num2str(j * R2) + string(", dir);\n"); } // twiddle if (passNum < (numPasses - 1)) { localString += string("l = ((bNum << ") + num2str(lgBatchSize) + string(") + i) >> ") + num2str(lgStrideO) + string(";\n"); localString += string("k = j << ") + num2str((int)log2(R1 / R2)) + string(";\n"); localString += string("ang1 = dir*(2.0f*M_PI/") + num2str(N) + string(")*l;\n"); for (t = 0; t < R1; t++) { localString += string("ang = ang1*(k + ") + num2str((t % R2) * R1 + (t / R2)) + string(");\n"); localString += string("w = (float2)(native_cos(ang), native_sin(ang));\n"); localString += string("a[") + num2str(t) + string("] = complexMul(a[") + num2str(t) + string("], w);\n"); } } // Store Data if (strideO == 1) { localString += string("lMemStore = sMem + mad24(i, ") + num2str(radix + 1) + string(", j << ") + num2str((int)log2(R1 / R2)) + string(");\n"); localString += string("lMemLoad = sMem + mad24(tid >> ") + num2str((int)log2(radix)) + string(", ") + num2str(radix + 1) + string(", tid & ") + num2str(radix - 1) + string(");\n"); for (i = 0; i < R1 / R2; i++) for (j = 0; j < R2; j++) localString += string("lMemStore[ ") + num2str(i + j * R1) + string("] = a[") + num2str(i * R2 + j) + string("].x;\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); if (threadsPerBlock >= radix) { for (i = 0; i < R1; i++) localString += string("a[") + num2str(i) + string("].x = lMemLoad[") + num2str(i * (radix + 1) * (threadsPerBlock / radix)) + string("];\n"); } else { int innerIter = radix / threadsPerBlock; int outerIter = R1 / innerIter; for (i = 0; i < outerIter; i++) for (j = 0; j < innerIter; j++) localString += string("a[") + num2str(i * innerIter + j) + string("].x = lMemLoad[") + num2str(j * threadsPerBlock + i * (radix + 1)) + string("];\n"); } localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); for (i = 0; i < R1 / R2; i++) for (j = 0; j < R2; j++) localString += string("lMemStore[ ") + num2str(i + j * R1) + string("] = a[") + num2str(i * R2 + j) + string("].y;\n"); localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); if (threadsPerBlock >= radix) { for (i = 0; i < R1; i++) localString += string("a[") + num2str(i) + string("].y = lMemLoad[") + num2str(i * (radix + 1) * (threadsPerBlock / radix)) + string("];\n"); } else { int innerIter = radix / threadsPerBlock; int outerIter = R1 / innerIter; for (i = 0; i < outerIter; i++) for (j = 0; j < innerIter; j++) localString += string("a[") + num2str(i * innerIter + j) + string("].y = lMemLoad[") + num2str(j * threadsPerBlock + i * (radix + 1)) + string("];\n"); } localString += string("barrier(CLK_LOCAL_MEM_FENCE);\n"); localString += string("indexOut += tid;\n"); if (dataFormat == clFFT_SplitComplexFormat) { localString += string("out_real += indexOut;\n"); localString += string("out_imag += indexOut;\n"); for (k = 0; k < R1; k++) localString += string("out_real[") + num2str(k * threadsPerBlock) + string("] = a[") + num2str(k) + string("].x;\n"); for (k = 0; k < R1; k++) localString += string("out_imag[") + num2str(k * threadsPerBlock) + string("] = a[") + num2str(k) + string("].y;\n"); } else { localString += string("out += indexOut;\n"); for (k = 0; k < R1; k++) localString += string("out[") + num2str(k * threadsPerBlock) + string("] = a[") + num2str(k) + string("];\n"); } } else { localString += string("indexOut += mad24(j, ") + num2str(numIter * strideO) + string(", i);\n"); if (dataFormat == clFFT_SplitComplexFormat) { localString += string("out_real += indexOut;\n"); localString += string("out_imag += indexOut;\n"); for (k = 0; k < R1; k++) localString += string("out_real[") + num2str(((k % R2) * R1 + (k / R2)) * strideO) + string("] = a[") + num2str(k) + string("].x;\n"); for (k = 0; k < R1; k++) localString += string("out_imag[") + num2str(((k % R2) * R1 + (k / R2)) * strideO) + string("] = a[") + num2str(k) + string("].y;\n"); } else { localString += string("out += indexOut;\n"); for (k = 0; k < R1; k++) localString += string("out[") + num2str(((k % R2) * R1 + (k / R2)) * strideO) + string("] = a[") + num2str(k) + string("];\n"); } } insertHeader(*kernelString, kernelName, dataFormat); *kernelString += string("{\n"); if ((*kInfo)->lmem_size) *kernelString += string(" __local float sMem[") + num2str((*kInfo)->lmem_size) + string("];\n"); *kernelString += localString; *kernelString += string("}\n"); N /= radix; kInfo = &(*kInfo)->next; kCount++; } } void FFT1D(cl_fft_plan *plan, cl_fft_kernel_dir dir) { unsigned int radixArray[10]; unsigned int numRadix; switch (dir) { case cl_fft_kernel_x: if (plan->n.x > plan->max_localmem_fft_size) { createGlobalFFTKernelString(plan, plan->n.x, 1, cl_fft_kernel_x, 1); } else if (plan->n.x > 1) { getRadixArray(plan->n.x, radixArray, &numRadix, 0); if (plan->n.x / radixArray[0] <= plan->max_work_item_per_workgroup) { createLocalMemfftKernelString(plan); } else { getRadixArray(plan->n.x, radixArray, &numRadix, plan->max_radix); if (plan->n.x / radixArray[0] <= plan->max_work_item_per_workgroup) createLocalMemfftKernelString(plan); else createGlobalFFTKernelString(plan, plan->n.x, 1, cl_fft_kernel_x, 1); } } break; case cl_fft_kernel_y: if (plan->n.y > 1) createGlobalFFTKernelString(plan, plan->n.y, plan->n.x, cl_fft_kernel_y, 1); break; case cl_fft_kernel_z: if (plan->n.z > 1) createGlobalFFTKernelString(plan, plan->n.z, plan->n.x * plan->n.y, cl_fft_kernel_z, 1); default: return; } } src/algorithms/libs/opencl/fft_setup.cc000066400000000000000000000320331352176506000205430ustar00rootroot00000000000000 // // File: fft_setup.cpp // // Version: <1.0> // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") // in consideration of your agreement to the following terms, and your use, // installation, modification or redistribution of this Apple software // constitutes acceptance of these terms. If you do not agree with these // terms, please do not use, install, modify or redistribute this Apple // software. // // In consideration of your agreement to abide by the following terms, and // subject to these terms, Apple grants you a personal, non - exclusive // license, under Apple's copyrights in this original Apple software ( the // "Apple Software" ), to use, reproduce, modify and redistribute the Apple // Software, with or without modifications, in source and / or binary forms; // provided that if you redistribute the Apple Software in its entirety and // without modifications, you must retain this notice and the following text // and disclaimers in all such redistributions of the Apple Software. Neither // the name, trademarks, service marks or logos of Apple Inc. may be used to // endorse or promote products derived from the Apple Software without specific // prior written permission from Apple. Except as expressly stated in this // notice, no other rights or licenses, express or implied, are granted by // Apple herein, including but not limited to any patent rights that may be // infringed by your derivative works or by other works in which the Apple // Software may be incorporated. // // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION // ALONE OR IN COMBINATION WITH YOUR PRODUCTS. // // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright ( C ) 2008 Apple Inc. All Rights Reserved. // //////////////////////////////////////////////////////////////////////////////////////////////////// #include "fft_base_kernels.h" #include "fft_internal.h" #include #include #include #include #include #include #include #include using namespace std; extern void getKernelWorkDimensions(cl_fft_plan *plan, cl_fft_kernel_info *kernelInfo, cl_int *batchSize, size_t *gWorkItems, size_t *lWorkItems); static void getBlockConfigAndKernelString(cl_fft_plan *plan) { plan->temp_buffer_needed = 0; *plan->kernel_string += baseKernels; if (plan->format == clFFT_SplitComplexFormat) *plan->kernel_string += twistKernelPlannar; else *plan->kernel_string += twistKernelInterleaved; switch (plan->dim) { case clFFT_1D: FFT1D(plan, cl_fft_kernel_x); break; case clFFT_2D: FFT1D(plan, cl_fft_kernel_x); FFT1D(plan, cl_fft_kernel_y); break; case clFFT_3D: FFT1D(plan, cl_fft_kernel_x); FFT1D(plan, cl_fft_kernel_y); FFT1D(plan, cl_fft_kernel_z); break; default: return; } plan->temp_buffer_needed = 0; cl_fft_kernel_info *kInfo = plan->kernel_info; while (kInfo) { plan->temp_buffer_needed |= !kInfo->in_place_possible; kInfo = kInfo->next; } } static void deleteKernelInfo(cl_fft_kernel_info *kInfo) { if (kInfo) { if (kInfo->kernel_name) free(kInfo->kernel_name); if (kInfo->kernel) clReleaseKernel(kInfo->kernel); free(kInfo); } } static void destroy_plan(cl_fft_plan *Plan) { cl_fft_kernel_info *kernel_info = Plan->kernel_info; while (kernel_info) { cl_fft_kernel_info *tmp = kernel_info->next; deleteKernelInfo(kernel_info); kernel_info = tmp; } Plan->kernel_info = nullptr; if (Plan->kernel_string) { delete Plan->kernel_string; Plan->kernel_string = nullptr; } if (Plan->twist_kernel) { clReleaseKernel(Plan->twist_kernel); Plan->twist_kernel = nullptr; } if (Plan->program) { clReleaseProgram(Plan->program); Plan->program = nullptr; } if (Plan->tempmemobj) { clReleaseMemObject(Plan->tempmemobj); Plan->tempmemobj = nullptr; } if (Plan->tempmemobj_real) { clReleaseMemObject(Plan->tempmemobj_real); Plan->tempmemobj_real = nullptr; } if (Plan->tempmemobj_imag) { clReleaseMemObject(Plan->tempmemobj_imag); Plan->tempmemobj_imag = nullptr; } } static int createKernelList(cl_fft_plan *plan) { cl_program program = plan->program; cl_fft_kernel_info *kernel_info = plan->kernel_info; cl_int err; while (kernel_info) { kernel_info->kernel = clCreateKernel(program, kernel_info->kernel_name, &err); if (!kernel_info->kernel || err != CL_SUCCESS) return err; kernel_info = kernel_info->next; } if (plan->format == clFFT_SplitComplexFormat) plan->twist_kernel = clCreateKernel(program, "clFFT_1DTwistSplit", &err); else plan->twist_kernel = clCreateKernel(program, "clFFT_1DTwistInterleaved", &err); if (!plan->twist_kernel || err) return err; return CL_SUCCESS; } int getMaxKernelWorkGroupSize(cl_fft_plan *plan, unsigned int *max_wg_size, unsigned int num_devices, cl_device_id *devices) { int reg_needed = 0; *max_wg_size = std::numeric_limits::max(); int err; unsigned wg_size; unsigned int i; for (i = 0; i < num_devices; i++) { cl_fft_kernel_info *kInfo = plan->kernel_info; while (kInfo) { err = clGetKernelWorkGroupInfo(kInfo->kernel, devices[i], CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &wg_size, nullptr); if (err != CL_SUCCESS) return -1; if (wg_size < kInfo->num_workitems_per_workgroup) reg_needed |= 1; if (*max_wg_size > wg_size) *max_wg_size = wg_size; kInfo = kInfo->next; } } return reg_needed; } #define ERR_MACRO(err) \ { \ if (err != CL_SUCCESS) \ { \ if (error_code) \ *error_code = err; \ clFFT_DestroyPlan((clFFT_Plan)plan); \ return (clFFT_Plan)NULL; \ } \ } clFFT_Plan clFFT_CreatePlan(cl_context context, clFFT_Dim3 n, clFFT_Dimension dim, clFFT_DataFormat dataFormat, cl_int *error_code) { int i; cl_int err; int isPow2 = 1; cl_fft_plan *plan = nullptr; ostringstream kString; int num_devices; int gpu_found = 0; cl_device_id devices[16]; size_t ret_size; cl_device_type device_type; if (!context) ERR_MACRO(CL_INVALID_VALUE); isPow2 |= n.x && !((n.x - 1) & n.x); isPow2 |= n.y && !((n.y - 1) & n.y); isPow2 |= n.z && !((n.z - 1) & n.z); if (!isPow2) ERR_MACRO(CL_INVALID_VALUE); if ((dim == clFFT_1D && (n.y != 1 || n.z != 1)) || (dim == clFFT_2D && n.z != 1)) ERR_MACRO(CL_INVALID_VALUE); plan = (cl_fft_plan *)malloc(sizeof(cl_fft_plan)); if (!plan) ERR_MACRO(CL_OUT_OF_RESOURCES); plan->context = context; clRetainContext(context); plan->n = n; plan->dim = dim; plan->format = dataFormat; plan->kernel_info = nullptr; plan->num_kernels = 0; plan->twist_kernel = nullptr; plan->program = nullptr; plan->temp_buffer_needed = 0; plan->last_batch_size = 0; plan->tempmemobj = nullptr; plan->tempmemobj_real = nullptr; plan->tempmemobj_imag = nullptr; plan->max_localmem_fft_size = 2048; plan->max_work_item_per_workgroup = 256; plan->max_radix = 16; plan->min_mem_coalesce_width = 16; plan->num_local_mem_banks = 16; patch_kernel_source: plan->kernel_string = new string(""); if (!plan->kernel_string) ERR_MACRO(CL_OUT_OF_RESOURCES); getBlockConfigAndKernelString(plan); const char *source_str = plan->kernel_string->c_str(); plan->program = clCreateProgramWithSource(context, 1, (const char **)&source_str, nullptr, &err); ERR_MACRO(err); err = clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(devices), devices, &ret_size); ERR_MACRO(err); num_devices = (int)(ret_size / sizeof(cl_device_id)); for (i = 0; i < num_devices; i++) { err = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(device_type), &device_type, nullptr); ERR_MACRO(err); if (device_type == CL_DEVICE_TYPE_GPU) { gpu_found = 1; err = clBuildProgram(plan->program, 1, &devices[i], "-cl-mad-enable", nullptr, nullptr); if (err != CL_SUCCESS) { char *build_log; char devicename[200]; size_t log_size; err = clGetProgramBuildInfo(plan->program, devices[i], CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size); ERR_MACRO(err); build_log = (char *)malloc(log_size + 1); err = clGetProgramBuildInfo(plan->program, devices[i], CL_PROGRAM_BUILD_LOG, log_size, build_log, nullptr); ERR_MACRO(err); err = clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(devicename), devicename, nullptr); ERR_MACRO(err); fprintf(stdout, "FFT program build log on device %s\n", devicename); fprintf(stdout, "%s\n", build_log); free(build_log); ERR_MACRO(err); } } } if (!gpu_found) ERR_MACRO(CL_INVALID_CONTEXT); err = createKernelList(plan); ERR_MACRO(err); // we created program and kernels based on "some max work group size (default 256)" ... this work group size // may be larger than what kernel may execute with ... if thats the case we need to regenerate the kernel source // setting this as limit i.e max group size and rebuild. unsigned int max_kernel_wg_size; int patching_req = getMaxKernelWorkGroupSize(plan, &max_kernel_wg_size, num_devices, devices); if (patching_req == -1) { ERR_MACRO(err); } if (patching_req) { destroy_plan(plan); plan->max_work_item_per_workgroup = max_kernel_wg_size; goto patch_kernel_source; } cl_fft_kernel_info *kInfo = plan->kernel_info; while (kInfo) { plan->num_kernels++; kInfo = kInfo->next; } if (error_code) *error_code = CL_SUCCESS; return (clFFT_Plan)plan; } void clFFT_DestroyPlan(clFFT_Plan plan) { auto *Plan = (cl_fft_plan *)plan; if (Plan) { destroy_plan(Plan); clReleaseContext(Plan->context); free(Plan); } } void clFFT_DumpPlan(clFFT_Plan Plan, FILE *file) { size_t gDim, lDim; FILE *out; if (!file) out = stdout; else out = file; auto *plan = (cl_fft_plan *)Plan; cl_fft_kernel_info *kInfo = plan->kernel_info; while (kInfo) { cl_int s = 1; getKernelWorkDimensions(plan, kInfo, &s, &gDim, &lDim); fprintf(out, "Run kernel %s with global dim = {%zd*BatchSize}, local dim={%zd}\n", kInfo->kernel_name, gDim, lDim); kInfo = kInfo->next; } fprintf(out, "%s\n", plan->kernel_string->c_str()); } src/algorithms/libs/pass_through.cc000066400000000000000000000140301352176506000177670ustar00rootroot00000000000000/*! * \file pass_through.cc * \brief Implementation of a block that just puts its input in its * output. * \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "pass_through.h" #include "configuration_interface.h" #include #include #include #include // for int8_t #include // for operator<< Pass_Through::Pass_Through(ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams) : role_(role), in_streams_(in_streams), out_streams_(out_streams) { std::string default_item_type = "gr_complex"; std::string input_type = configuration->property(role + ".input_item_type", default_item_type); std::string output_type = configuration->property(role + ".output_item_type", default_item_type); if (input_type != output_type) { LOG(WARNING) << "input_item_type and output_item_type are different in a Pass_Through implementation! Taking " << input_type << ", but item_size will supersede it."; } item_type_ = configuration->property(role + ".item_type", input_type); inverted_spectrum = configuration->property(role + ".inverted_spectrum", false); if (item_type_ == "float") { item_size_ = sizeof(float); } else if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); if (inverted_spectrum) { conjugate_cc_ = make_conjugate_cc(); } } else if (item_type_ == "short") { item_size_ = sizeof(int16_t); } else if (item_type_ == "ishort") { item_size_ = sizeof(int16_t); } else if (item_type_ == "cshort") { item_size_ = sizeof(lv_16sc_t); if (inverted_spectrum) { conjugate_sc_ = make_conjugate_sc(); } } else if (item_type_ == "byte") { item_size_ = sizeof(int8_t); } else if (item_type_ == "ibyte") { item_size_ = sizeof(int8_t); } else if (item_type_ == "cbyte") { item_size_ = sizeof(lv_8sc_t); if (inverted_spectrum) { conjugate_ic_ = make_conjugate_ic(); } } else { LOG(WARNING) << item_type_ << " unrecognized item type. Using float"; item_size_ = sizeof(float); } kludge_copy_ = gr::blocks::copy::make(item_size_); uint64_t max_source_buffer_samples = configuration->property("GNSS-SDR.max_source_buffer_samples", 0); if (max_source_buffer_samples > 0) { kludge_copy_->set_max_output_buffer(max_source_buffer_samples); LOG(INFO) << "Set signal conditioner max output buffer to " << max_source_buffer_samples; } DLOG(INFO) << "kludge_copy(" << kludge_copy_->unique_id() << ")"; if (in_streams_ > 1) { LOG(ERROR) << "This implementation only supports one input stream"; LOG(ERROR) << in_streams_; } if (out_streams_ > 1) { LOG(ERROR) << "This implementation only supports one output stream"; LOG(ERROR) << out_streams_; } } void Pass_Through::connect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; DLOG(INFO) << "nothing to connect internally"; } void Pass_Through::disconnect(gr::top_block_sptr top_block) { if (top_block) { /* top_block is not null */ }; // Nothing to disconnect } gr::basic_block_sptr Pass_Through::get_left_block() { if (inverted_spectrum) { if (item_type_ == "gr_complex") { return conjugate_cc_; } if (item_type_ == "cshort") { return conjugate_sc_; } if (item_type_ == "cbyte") { return conjugate_ic_; } LOG(WARNING) << "Setting inverted_spectrum to true with item_type " << item_type_ << " is not defined and has no effect."; } return kludge_copy_; } gr::basic_block_sptr Pass_Through::get_right_block() { if (inverted_spectrum) { if (item_type_ == "gr_complex") { return conjugate_cc_; } if (item_type_ == "cshort") { return conjugate_sc_; } if (item_type_ == "cbyte") { return conjugate_ic_; } DLOG(WARNING) << "Setting inverted_spectrum to true with item_type " << item_type_ << " is not defined and has no effect."; } return kludge_copy_; } src/algorithms/libs/pass_through.h000066400000000000000000000053461352176506000176430ustar00rootroot00000000000000/*! * \file pass_through.h * \brief Interface of a block that just puts its input in its * output. * \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com * * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_PASS_THROUGH_H_ #define GNSS_SDR_PASS_THROUGH_H_ #include "conjugate_cc.h" #include "conjugate_ic.h" #include "conjugate_sc.h" #include "gnss_block_interface.h" #include #include #include #include class ConfigurationInterface; /*! * \brief This class implements a block that connects input and output (does nothing) */ class Pass_Through : public GNSSBlockInterface { public: Pass_Through(ConfigurationInterface* configuration, const std::string& role, unsigned int in_stream, unsigned int out_stream); ~Pass_Through() = default; inline std::string role() override { return role_; } //! returns "Pass_Through" inline std::string implementation() override { return "Pass_Through"; } inline std::string item_type() const { return item_type_; } inline size_t item_size() override { return item_size_; } void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; private: std::string item_type_; std::string role_; unsigned int in_streams_; unsigned int out_streams_; gr::blocks::copy::sptr kludge_copy_; size_t item_size_; conjugate_cc_sptr conjugate_cc_; conjugate_sc_sptr conjugate_sc_; conjugate_ic_sptr conjugate_ic_; bool inverted_spectrum; }; #endif /*GNSS_SDR_PASS_THROUGH_H_*/ src/algorithms/libs/rtklib/000077500000000000000000000000001352176506000162435ustar00rootroot00000000000000src/algorithms/libs/rtklib/CMakeLists.txt000066400000000000000000000037541352176506000210140ustar00rootroot00000000000000# Copyright (C) 2012-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # set(RTKLIB_LIB_SOURCES rtklib_rtkcmn.cc rtklib_ephemeris.cc rtklib_preceph.cc rtklib_sbas.cc rtklib_ionex.cc rtklib_pntpos.cc rtklib_ppp.cc rtklib_tides.cc rtklib_lambda.cc rtklib_rtkpos.cc rtklib_conversions.cc rtklib_stream.cc rtklib_rtksvr.cc rtklib_solution.cc rtklib_rtcm.cc rtklib_rtcm2.cc rtklib_rtcm3.cc ) set(RTKLIB_LIB_HEADERS rtklib_rtkcmn.h rtklib_ephemeris.h rtklib_preceph.h rtklib_sbas.h rtklib_ionex.h rtklib_pntpos.h rtklib_ppp.h rtklib_tides.h rtklib_lambda.h rtklib_rtkpos.h rtklib_conversions.h rtklib_stream.h rtklib_rtksvr.h rtklib_solution.h rtklib_rtcm.h rtklib_rtcm2.h rtklib_rtcm3.h rtklib.h ) list(SORT RTKLIB_LIB_HEADERS) list(SORT RTKLIB_LIB_SOURCES) source_group(Headers FILES ${RTKLIB_LIB_HEADERS}) add_library(algorithms_libs_rtklib ${RTKLIB_LIB_SOURCES} ${RTKLIB_LIB_HEADERS}) target_link_libraries(algorithms_libs_rtklib PRIVATE core_system_parameters Gflags::gflags Glog::glog ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ) set_property(TARGET algorithms_libs_rtklib APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) src/algorithms/libs/rtklib/rtklib.h000066400000000000000000001725131352176506000177140ustar00rootroot00000000000000/*! * \file rtklib.h * \brief main header file for the rtklib library * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_H_ #define GNSS_SDR_RTKLIB_H_ #include "MATH_CONSTANTS.h" #include "gnss_frequencies.h" #include "gnss_obs_codes.h" #include #include #include #include #include #include #include /* macros --------------------------------------------------------------------*/ #define dev_t int #define socket_t int #define closesocket close #define lock_t pthread_mutex_t #define initlock(f) pthread_mutex_init(f, NULL) #define rtk_lock(f) pthread_mutex_lock(f) #define rtk_unlock(f) pthread_mutex_unlock(f) #define VER_RTKLIB "2.4.2" #define NTRIP_AGENT "RTKLIB/" VER_RTKLIB #define NTRIP_CLI_PORT 2101 /* default ntrip-client connection port */ #define NTRIP_SVR_PORT 80 /* default ntrip-server connection port */ #define NTRIP_MAXRSP 32768 /* max size of ntrip response */ #define NTRIP_MAXSTR 256 /* max length of mountpoint string */ #define NTRIP_RSP_OK_CLI "ICY 200 OK\r\n" /* ntrip response: client */ #define NTRIP_RSP_OK_SVR "OK\r\n" /* ntrip response: server */ #define NTRIP_RSP_SRCTBL "SOURCETABLE 200 OK\r\n" /* ntrip response: source table */ #define NTRIP_RSP_TBLEND "ENDSOURCETABLE" #define NTRIP_RSP_HTTP "HTTP/" /* ntrip response: http */ #define NTRIP_RSP_ERROR "ERROR" /* ntrip response: error */ #define FTP_CMD "wget" /* ftp/http command */ const int TINTACT = 200; //!< period for stream active (ms) const int SERIBUFFSIZE = 4096; //!< serial buffer size (bytes) const int TIMETAGH_LEN = 64; //!< time tag file header length const int MAXCLI = 32; //!< max client connection for tcp svr const int MAXSTATMSG = 32; //!< max length of status message const int FTP_TIMEOUT = 30; //!< ftp/http timeout (s) const int MAXRAWLEN = 4096; //!< max length of receiver raw message const int MAXSOLBUF = 256; //!< max number of solution buffer const int MAXSBSMSG = 32; //!< max number of SBAS msg in RTK server const int MAXOBSBUF = 128; //!< max number of observation data buffer const int FILEPATHSEP = '/'; const double RE_WGS84 = 6378137.0; //!< earth semimajor axis (WGS84) (m) const double FE_WGS84 = (1.0 / 298.257223563); //!< earth flattening (WGS84) const double HION = 350000.0; //!< ionosphere height (m) const double PRN_HWBIAS = 1e-6; //!< process noise of h/w bias (m/MHz/sqrt(s)) const double INT_SWAP_STAT = 86400.0; //!< swap interval of solution status file (s) const double INT_SWAP_TRAC = 86400.0; //!< swap interval of trace file (s) const unsigned int POLYCRC32 = 0xEDB88320u; //!< CRC32 polynomial const unsigned int POLYCRC24Q = 0x1864CFBu; //!< CRC24Q polynomial const int PMODE_SINGLE = 0; //!< positioning mode: single const int PMODE_DGPS = 1; //!< positioning mode: DGPS/DGNSS const int PMODE_KINEMA = 2; //!< positioning mode: kinematic const int PMODE_STATIC = 3; //!< positioning mode: static const int PMODE_MOVEB = 4; //!< positioning mode: moving-base const int PMODE_FIXED = 5; //!< positioning mode: fixed const int PMODE_PPP_KINEMA = 6; //!< positioning mode: PPP-kinemaric const int PMODE_PPP_STATIC = 7; //!< positioning mode: PPP-static const int PMODE_PPP_FIXED = 8; //!< positioning mode: PPP-fixed const int SOLF_LLH = 0; //!< solution format: lat/lon/height const int SOLF_XYZ = 1; //!< solution format: x/y/z-ecef const int SOLF_ENU = 2; //!< solution format: e/n/u-baseline const int SOLF_NMEA = 3; //!< solution format: NMEA-183 const int SOLF_STAT = 4; //!< solution format: solution status const int SOLF_GSIF = 5; //!< solution format: GSI F1/F2 const int SOLQ_NONE = 0; //!< solution status: no solution const int SOLQ_FIX = 1; //!< solution status: fix const int SOLQ_FLOAT = 2; //!< solution status: float const int SOLQ_SBAS = 3; //!< solution status: SBAS const int SOLQ_DGPS = 4; //!< solution status: DGPS/DGNSS const int SOLQ_SINGLE = 5; //!< solution status: single const int SOLQ_PPP = 6; //!< solution status: PPP const int SOLQ_DR = 7; //!< solution status: dead reckoning const int MAXSOLQ = 7; //!< max number of solution status const int TIMES_GPST = 0; //!< time system: gps time const int TIMES_UTC = 1; //!< time system: utc const int TIMES_JST = 2; //!< time system: jst const double ERR_SAAS = 0.3; //!< saastamoinen model error std (m) const double ERR_BRDCI = 0.5; //!< broadcast iono model error factor const double ERR_CBIAS = 0.3; //!< code bias error std (m) const double REL_HUMI = 0.7; //!< relative humidity for saastamoinen model const double GAP_RESION = 120; //!< default gap to reset ionos parameters (ep) const int MAXFREQ = 7; //!< max NFREQ const int MAXLEAPS = 64; //!< max number of leap seconds table const double DTTOL = 0.005; //!< tolerance of time difference (s) const int NFREQ = 3; //!< number of carrier frequencies const int NFREQGLO = 2; //!< number of carrier frequencies of GLONASS const int NEXOBS = 0; //!< number of extended obs codes const int MAXANT = 64; //!< max length of station name/antenna type const int MINPRNGPS = 1; //!< min satellite PRN number of GPS const int MAXPRNGPS = 32; //!< max satellite PRN number of GPS const int NSATGPS = (MAXPRNGPS - MINPRNGPS + 1); //!< number of GPS satellites const int NSYSGPS = 1; const int SYS_NONE = 0x00; //!< navigation system: none const int SYS_GPS = 0x01; //!< navigation system: GPS const int SYS_SBS = 0x02; //!< navigation system: SBAS const int SYS_GLO = 0x04; //!< navigation system: GLONASS const int SYS_GAL = 0x08; //!< navigation system: Galileo const int SYS_QZS = 0x10; //!< navigation system: QZSS const int SYS_BDS = 0x20; //!< navigation system: BeiDou const int SYS_IRN = 0x40; //!< navigation system: IRNS const int SYS_LEO = 0x80; //!< navigation system: LEO const int SYS_ALL = 0xFF; //!< navigation system: all #define ENAGLO #ifdef ENAGLO const int MINPRNGLO = 1; //!< min satellite slot number of GLONASS const int MAXPRNGLO = 27; //!< max satellite slot number of GLONASS const int NSATGLO = (MAXPRNGLO - MINPRNGLO + 1); //!< number of GLONASS satellites const int NSYSGLO = 1; #else const int MINPRNGLO = 0; const int MAXPRNGLO = 0; const int NSATGLO = 0; const int NSYSGLO = 0; #endif const int MINPRNGAL = 1; //!< min satellite PRN number of Galileo const int MAXPRNGAL = 36; //!< max satellite PRN number of Galileo const int NSATGAL = (MAXPRNGAL - MINPRNGAL + 1); //!< number of Galileo satellites const int NSYSGAL = 1; #ifdef ENAQZS const int MINPRNQZS = 193; //!< min satellite PRN number of QZSS const int MAXPRNQZS = 199; //!< max satellite PRN number of QZSS const int MINPRNQZS_S = 183; //!< min satellite PRN number of QZSS SAIF const int MAXPRNQZS_S = 189; //!< max satellite PRN number of QZSS SAIF const int NSATQZS = (MAXPRNQZS - MINPRNQZS + 1); //!< number of QZSS satellites const int NSYSQZS = 1; #else const int MINPRNQZS = 0; const int MAXPRNQZS = 0; const int MINPRNQZS_S = 0; const int MAXPRNQZS_S = 0; const int NSATQZS = 0; const int NSYSQZS = 0; #endif #define ENABDS #ifdef ENABDS const int MINPRNBDS = 1; //!< min satellite sat number of BeiDou const int MAXPRNBDS = 37; //!< max satellite sat number of BeiDou const int NSATBDS = (MAXPRNBDS - MINPRNBDS + 1); //!< number of BeiDou satellites const int NSYSBDS = 1; #else const int MINPRNBDS = 0; const int MAXPRNBDS = 0; const int NSATBDS = 0; const int NSYSBDS = 0; #endif #ifdef ENAIRN const int MINPRNIRN = 1; //!< min satellite sat number of IRNSS const int MAXPRNIRN = 7; //!< max satellite sat number of IRNSS const int NSATIRN = (MAXPRNIRN - MINPRNIRN + 1); //!< number of IRNSS satellites const int NSYSIRN = 1; #else const int MINPRNIRN = 0; const int MAXPRNIRN = 0; const int NSATIRN = 0; const int NSYSIRN = 0; #endif #ifdef ENALEO const int MINPRNLEO = 1; //!< min satellite sat number of LEO const int NSATLEO = 10; //!< max satellite sat number of LEO const int NSATLEO = (MAXPRNLEO - MINPRNLEO + 1); //!< number of LEO satellites const int NSYSLEO = 1; #else const int MINPRNLEO = 0; const int MAXPRNLEO = 0; const int NSATLEO = 0; const int NSYSLEO = 0; #endif const int NSYS = (NSYSGPS + NSYSGLO + NSYSGAL + NSYSQZS + NSYSBDS + NSYSIRN + NSYSLEO); //!< number of systems const int MINPRNSBS = 120; //!< min satellite PRN number of SBAS const int MAXPRNSBS = 142; //!< max satellite PRN number of SBAS const int NSATSBS = (MAXPRNSBS - MINPRNSBS + 1); //!< number of SBAS satellites const int MAXSAT = (NSATGPS + NSATGLO + NSATGAL + NSATQZS + NSATBDS + NSATIRN + NSATSBS + NSATLEO); const int MAXSTA = 255; #ifndef MAXOBS const int MAXOBS = 64; //!< max number of obs in an epoch #endif const int MAXRCV = 64; //!< max receiver number (1 to MAXRCV) const int MAXOBSTYPE = 64; //!< max number of obs type in RINEX const double MAXDTOE = 7200.0; //!< max time difference to GPS Toe (s) const double MAXDTOE_QZS = 7200.0; //!< max time difference to QZSS Toe (s) const double MAXDTOE_GAL = 10800.0; //!< max time difference to Galileo Toe (s) const double MAXDTOE_BDS = 21600.0; //!< max time difference to BeiDou Toe (s) const double MAXDTOE_GLO = 1800.0; //!< max time difference to GLONASS Toe (s) const double MAXDTOE_SBS = 360.0; //!< max time difference to SBAS Toe (s) const double MAXDTOE_S = 86400.0; //!< max time difference to ephem toe (s) for other const double MAXGDOP = 300.0; //!< max GDOP const int MAXSBSURA = 8; //!< max URA of SBAS satellite const int MAXBAND = 10; //!< max SBAS band of IGP const int MAXNIGP = 201; //!< max number of IGP in SBAS band const int MAXNGEO = 4; //!< max number of GEO satellites const int MAXSOLMSG = 8191; //!< max length of solution message const int MAXERRMSG = 4096; //!< max length of error/warning message const int IONOOPT_OFF = 0; //!< ionosphere option: correction off const int IONOOPT_BRDC = 1; //!< ionosphere option: broadcast model const int IONOOPT_SBAS = 2; //!< ionosphere option: SBAS model const int IONOOPT_IFLC = 3; //!< ionosphere option: L1/L2 or L1/L5 iono-free LC const int IONOOPT_EST = 4; //!< ionosphere option: estimation const int IONOOPT_TEC = 5; //!< ionosphere option: IONEX TEC model const int IONOOPT_QZS = 6; //!< ionosphere option: QZSS broadcast model const int IONOOPT_LEX = 7; //!< ionosphere option: QZSS LEX ionospehre const int IONOOPT_STEC = 8; //!< ionosphere option: SLANT TEC model const int TROPOPT_OFF = 0; //!< troposphere option: correction off const int TROPOPT_SAAS = 1; //!< troposphere option: Saastamoinen model const int TROPOPT_SBAS = 2; //!< troposphere option: SBAS model const int TROPOPT_EST = 3; //!< troposphere option: ZTD estimation const int TROPOPT_ESTG = 4; //!< troposphere option: ZTD+grad estimation const int TROPOPT_COR = 5; //!< troposphere option: ZTD correction const int TROPOPT_CORG = 6; //!< troposphere option: ZTD+grad correction const int EPHOPT_BRDC = 0; //!< ephemeris option: broadcast ephemeris const int EPHOPT_PREC = 1; //!< ephemeris option: precise ephemeris const int EPHOPT_SBAS = 2; //!< ephemeris option: broadcast + SBAS const int EPHOPT_SSRAPC = 3; //!< ephemeris option: broadcast + SSR_APC const int EPHOPT_SSRCOM = 4; //!< ephemeris option: broadcast + SSR_COM const int EPHOPT_LEX = 5; //!< ephemeris option: QZSS LEX ephemeris const double EFACT_GPS = 1.0; //!< error factor: GPS const double EFACT_GLO = 1.5; //!< error factor: GLONASS const double EFACT_GAL = 1.0; //!< error factor: Galileo const double EFACT_QZS = 1.0; //!< error factor: QZSS const double EFACT_BDS = 1.0; //!< error factor: BeiDou const double EFACT_IRN = 1.5; //!< error factor: IRNSS const double EFACT_SBS = 3.0; //!< error factor: SBAS const int MAXEXFILE = 1024; //!< max number of expanded files const double MAXSBSAGEF = 30.0; //!< max age of SBAS fast correction (s) const double MAXSBSAGEL = 1800.0; //!< max age of SBAS long term corr (s) const int ARMODE_OFF = 0; //!< AR mode: off const int ARMODE_CONT = 1; //!< AR mode: continuous const int ARMODE_INST = 2; //!< AR mode: instantaneous const int ARMODE_FIXHOLD = 3; //!< AR mode: fix and hold const int ARMODE_PPPAR = 4; //!< AR mode: PPP-AR const int ARMODE_PPPAR_ILS = 5; //!< AR mode: AR mode: PPP-AR ILS const int ARMODE_WLNL = 6; const int ARMODE_TCAR = 7; const int POSOPT_RINEX = 3; //!< pos option: rinex header pos const int MAXSTRPATH = 1024; //!< max length of stream path const int MAXSTRMSG = 1024; //!< max length of stream message using fatalfunc_t = void(const char *); //!< fatal callback function type #define STR_MODE_R 0x1 /* stream mode: read */ #define STR_MODE_W 0x2 /* stream mode: write */ #define STR_MODE_RW 0x3 /* stream mode: read/write */ #define STR_NONE 0 /* stream type: none */ #define STR_SERIAL 1 /* stream type: serial */ #define STR_FILE 2 /* stream type: file */ #define STR_TCPSVR 3 /* stream type: TCP server */ #define STR_TCPCLI 4 /* stream type: TCP client */ #define STR_UDP 5 /* stream type: UDP stream */ #define STR_NTRIPSVR 6 /* stream type: NTRIP server */ #define STR_NTRIPCLI 7 /* stream type: NTRIP client */ #define STR_FTP 8 /* stream type: ftp */ #define STR_HTTP 9 /* stream type: http */ #define NP_PPP(opt) ((opt)->dynamics ? 9 : 3) /* number of pos solution */ #define IC_PPP(s, opt) (NP_PPP(opt) + (s)) /* state index of clocks (s=0:gps,1:glo) */ #define IT_PPP(opt) (IC_PPP(0, opt) + NSYS) /* state index of tropos */ #define NR_PPP(opt) (IT_PPP(opt) + ((opt)->tropopt < TROPOPT_EST ? 0 : ((opt)->tropopt == TROPOPT_EST ? 1 : 3))) /* number of solutions */ #define IB_PPP(s, opt) (NR_PPP(opt) + (s)-1) /* state index of phase bias */ #define NX_PPP(opt) (IB_PPP(MAXSAT, opt) + 1) /* number of estimated states */ #define NF_RTK(opt) ((opt)->ionoopt == IONOOPT_IFLC ? 1 : (opt)->nf) #define NP_RTK(opt) ((opt)->dynamics == 0 ? 3 : 9) #define NI_RTK(opt) ((opt)->ionoopt != IONOOPT_EST ? 0 : MAXSAT) #define NT_RTK(opt) ((opt)->tropopt < TROPOPT_EST ? 0 : ((opt)->tropopt < TROPOPT_ESTG ? 2 : 6)) #define NL_RTK(opt) ((opt)->glomodear != 2 ? 0 : NFREQGLO) #define NB_RTK(opt) ((opt)->mode <= PMODE_DGPS ? 0 : MAXSAT * NF_RTK(opt)) #define NR_RTK(opt) (NP_RTK(opt) + NI_RTK(opt) + NT_RTK(opt) + NL_RTK(opt)) #define NX_RTK(opt) (NR_RTK(opt) + NB_RTK(opt)) typedef struct { /* time struct */ time_t time; /* time (s) expressed by standard time_t */ double sec; /* fraction of second under 1 s */ } gtime_t; typedef struct { /* observation data record */ gtime_t time; /* receiver sampling time (GPST) */ unsigned char sat, rcv; /* satellite/receiver number */ unsigned char SNR[NFREQ + NEXOBS]; /* signal strength (0.25 dBHz) */ unsigned char LLI[NFREQ + NEXOBS]; /* loss of lock indicator */ unsigned char code[NFREQ + NEXOBS]; /* code indicator (CODE_???) */ double L[NFREQ + NEXOBS]; /* observation data carrier-phase (cycle) */ double P[NFREQ + NEXOBS]; /* observation data pseudorange (m) */ float D[NFREQ + NEXOBS]; /* observation data doppler frequency (Hz) */ } obsd_t; typedef struct { /* observation data */ int n, nmax; /* number of obervation data/allocated */ obsd_t *data; /* observation data records */ } obs_t; typedef struct { /* earth rotation parameter data type */ double mjd; /* mjd (days) */ double xp, yp; /* pole offset (rad) */ double xpr, ypr; /* pole offset rate (rad/day) */ double ut1_utc; /* ut1-utc (s) */ double lod; /* length of day (s/day) */ } erpd_t; typedef struct { /* earth rotation parameter type */ int n, nmax; /* number and max number of data */ erpd_t *data; /* earth rotation parameter data */ } erp_t; typedef struct { /* antenna parameter type */ int sat; /* satellite number (0:receiver) */ char type[MAXANT]; /* antenna type */ char code[MAXANT]; /* serial number or satellite code */ gtime_t ts, te; /* valid time start and end */ double off[NFREQ][3]; /* phase center offset e/n/u or x/y/z (m) */ double var[NFREQ][19]; /* phase center variation (m) */ /* el=90,85,...,0 or nadir=0,1,2,3,... (deg) */ } pcv_t; typedef struct { /* antenna parameters type */ int n, nmax; /* number of data/allocated */ pcv_t *pcv; /* antenna parameters data */ } pcvs_t; typedef struct { /* almanac type */ int sat; /* satellite number */ int svh; /* sv health (0:ok) */ int svconf; /* as and sv config */ int week; /* GPS/QZS: gps week, GAL: galileo week */ gtime_t toa; /* Toa */ /* SV orbit parameters */ double A, e, i0, OMG0, omg, M0, OMGd; double toas; /* Toa (s) in week */ double f0, f1; /* SV clock parameters (af0,af1) */ } alm_t; typedef struct { /* GPS/QZS/GAL broadcast ephemeris type */ int sat; /* satellite number */ int iode, iodc; /* IODE,IODC */ int sva; /* SV accuracy (URA index) */ int svh; /* SV health (0:ok) */ int week; /* GPS/QZS: gps week, GAL: galileo week */ int code; /* GPS/QZS: code on L2, GAL/BDS: data sources */ int flag; /* GPS/QZS: L2 P data flag, BDS: nav type */ gtime_t toe, toc, ttr; /* Toe,Toc,T_trans */ /* SV orbit parameters */ double A, e, i0, OMG0, omg, M0, deln, OMGd, idot; double crc, crs, cuc, cus, cic, cis; double toes; /* Toe (s) in week */ double fit; /* fit interval (h) */ double f0, f1, f2; /* SV clock parameters (af0,af1,af2) */ double tgd[4]; /* group delay parameters */ /* GPS/QZS:tgd[0]=TGD */ /* GAL :tgd[0]=BGD E5a/E1,tgd[1]=BGD E5b/E1 */ /* BDS :tgd[0]=BGD1,tgd[1]=BGD2 */ double isc[4]; /* GPS :isc[0]=ISCL1, isc[1]=ISCL2, isc[2]=ISCL5I, isc[3]=ISCL5Q */ double Adot, ndot; /* Adot,ndot for CNAV */ } eph_t; typedef struct { /* GLONASS broadcast ephemeris type */ int sat; /* satellite number */ int iode; /* IODE (0-6 bit of tb field) */ int frq; /* satellite frequency number */ int svh, sva, age; /* satellite health, accuracy, age of operation */ gtime_t toe; /* epoch of epherides (gpst) */ gtime_t tof; /* message frame time (gpst) */ double pos[3]; /* satellite position (ecef) (m) */ double vel[3]; /* satellite velocity (ecef) (m/s) */ double acc[3]; /* satellite acceleration (ecef) (m/s^2) */ double taun, gamn; /* SV clock bias (s)/relative freq bias */ double dtaun; /* delay between L1 and L2 (s) */ } geph_t; typedef struct { /* precise ephemeris type */ gtime_t time; /* time (GPST) */ int index; /* ephemeris index for multiple files */ double pos[MAXSAT][4]; /* satellite position/clock (ecef) (m|s) */ float std[MAXSAT][4]; /* satellite position/clock std (m|s) */ double vel[MAXSAT][4]; /* satellite velocity/clk-rate (m/s|s/s) */ float vst[MAXSAT][4]; /* satellite velocity/clk-rate std (m/s|s/s) */ float cov[MAXSAT][3]; /* satellite position covariance (m^2) */ float vco[MAXSAT][3]; /* satellite velocity covariance (m^2) */ } peph_t; typedef struct { /* precise clock type */ gtime_t time; /* time (GPST) */ int index; /* clock index for multiple files */ double clk[MAXSAT][1]; /* satellite clock (s) */ float std[MAXSAT][1]; /* satellite clock std (s) */ } pclk_t; typedef struct { /* SBAS ephemeris type */ int sat; /* satellite number */ gtime_t t0; /* reference epoch time (GPST) */ gtime_t tof; /* time of message frame (GPST) */ int sva; /* SV accuracy (URA index) */ int svh; /* SV health (0:ok) */ double pos[3]; /* satellite position (m) (ecef) */ double vel[3]; /* satellite velocity (m/s) (ecef) */ double acc[3]; /* satellite acceleration (m/s^2) (ecef) */ double af0, af1; /* satellite clock-offset/drift (s,s/s) */ } seph_t; typedef struct { /* norad two line element data type */ char name[32]; /* common name */ char alias[32]; /* alias name */ char satno[16]; /* satellite catalog number */ char satclass; /* classification */ char desig[16]; /* international designator */ gtime_t epoch; /* element set epoch (UTC) */ double ndot; /* 1st derivative of mean motion */ double nddot; /* 2st derivative of mean motion */ double bstar; /* B* drag term */ int etype; /* element set type */ int eleno; /* element number */ double inc; /* orbit inclination (deg) */ double OMG; /* right ascension of ascending node (deg) */ double ecc; /* eccentricity */ double omg; /* argument of perigee (deg) */ double M; /* mean anomaly (deg) */ double n; /* mean motion (rev/day) */ int rev; /* revolution number at epoch */ } tled_t; typedef struct { /* norad two line element type */ int n, nmax; /* number/max number of two line element data */ tled_t *data; /* norad two line element data */ } tle_t; typedef struct { /* TEC grid type */ gtime_t time; /* epoch time (GPST) */ int ndata[3]; /* TEC grid data size {nlat,nlon,nhgt} */ double rb; /* earth radius (km) */ double lats[3]; /* latitude start/interval (deg) */ double lons[3]; /* longitude start/interval (deg) */ double hgts[3]; /* heights start/interval (km) */ double *data; /* TEC grid data (tecu) */ float *rms; /* RMS values (tecu) */ } tec_t; typedef struct { /* satellite fcb data type */ gtime_t ts, te; /* start/end time (GPST) */ double bias[MAXSAT][3]; /* fcb value (cyc) */ double std[MAXSAT][3]; /* fcb std-dev (cyc) */ } fcbd_t; typedef struct { /* SBAS message type */ int week, tow; /* reception time */ int prn; /* SBAS satellite PRN number */ unsigned char msg[29]; /* SBAS message (226bit) padded by 0 */ } sbsmsg_t; typedef struct { /* SBAS messages type */ int n, nmax; /* number of SBAS messages/allocated */ sbsmsg_t *msgs; /* SBAS messages */ } sbs_t; typedef struct { /* SBAS fast correction type */ gtime_t t0; /* time of applicability (TOF) */ double prc; /* pseudorange correction (PRC) (m) */ double rrc; /* range-rate correction (RRC) (m/s) */ double dt; /* range-rate correction delta-time (s) */ int iodf; /* IODF (issue of date fast corr) */ short udre; /* UDRE+1 */ short ai; /* degradation factor indicator */ } sbsfcorr_t; typedef struct { /* SBAS long term satellite error correction type */ gtime_t t0; /* correction time */ int iode; /* IODE (issue of date ephemeris) */ double dpos[3]; /* delta position (m) (ecef) */ double dvel[3]; /* delta velocity (m/s) (ecef) */ double daf0, daf1; /* delta clock-offset/drift (s,s/s) */ } sbslcorr_t; typedef struct { /* SBAS satellite correction type */ int sat; /* satellite number */ sbsfcorr_t fcorr; /* fast correction */ sbslcorr_t lcorr; /* long term correction */ } sbssatp_t; typedef struct { /* SBAS satellite corrections type */ int iodp; /* IODP (issue of date mask) */ int nsat; /* number of satellites */ int tlat; /* system latency (s) */ sbssatp_t sat[MAXSAT]; /* satellite correction */ } sbssat_t; typedef struct { /* SBAS ionospheric correction type */ gtime_t t0; /* correction time */ short lat, lon; /* latitude/longitude (deg) */ short give; /* GIVI+1 */ float delay; /* vertical delay estimate (m) */ } sbsigp_t; typedef struct { /* IGP band type */ short x; /* longitude/latitude (deg) */ const short *y; /* latitudes/longitudes (deg) */ unsigned char bits; /* IGP mask start bit */ unsigned char bite; /* IGP mask end bit */ } sbsigpband_t; typedef struct { /* SBAS ionospheric corrections type */ int iodi; /* IODI (issue of date ionos corr) */ int nigp; /* number of igps */ sbsigp_t igp[MAXNIGP]; /* ionospheric correction */ } sbsion_t; typedef struct { /* DGPS/GNSS correction type */ gtime_t t0; /* correction time */ double prc; /* pseudorange correction (PRC) (m) */ double rrc; /* range rate correction (RRC) (m/s) */ int iod; /* issue of data (IOD) */ double udre; /* UDRE */ } dgps_t; typedef struct { /* SSR correction type */ gtime_t t0[6]; /* epoch time (GPST) {eph,clk,hrclk,ura,bias,pbias} */ double udi[6]; /* SSR update interval (s) */ int iod[6]; /* iod ssr {eph,clk,hrclk,ura,bias,pbias} */ int iode; /* issue of data */ int iodcrc; /* issue of data crc for beidou/sbas */ int ura; /* URA indicator */ int refd; /* sat ref datum (0:ITRF,1:regional) */ double deph[3]; /* delta orbit {radial,along,cross} (m) */ double ddeph[3]; /* dot delta orbit {radial,along,cross} (m/s) */ double dclk[3]; /* delta clock {c0,c1,c2} (m,m/s,m/s^2) */ double hrclk; /* high-rate clock corection (m) */ float cbias[MAXCODE]; /* code biases (m) */ double pbias[MAXCODE]; /* phase biases (m) */ float stdpb[MAXCODE]; /* std-dev of phase biases (m) */ double yaw_ang, yaw_rate; /* yaw angle and yaw rate (deg,deg/s) */ unsigned char update; /* update flag (0:no update,1:update) */ } ssr_t; typedef struct { /* QZSS LEX message type */ int prn; /* satellite PRN number */ int type; /* message type */ int alert; /* alert flag */ unsigned char stat; /* signal tracking status */ unsigned char snr; /* signal C/N0 (0.25 dBHz) */ unsigned int ttt; /* tracking time (ms) */ unsigned char msg[212]; /* LEX message data part 1695 bits */ } lexmsg_t; typedef struct { /* QZSS LEX messages type */ int n, nmax; /* number of LEX messages and allocated */ lexmsg_t *msgs; /* LEX messages */ } lex_t; typedef struct { /* QZSS LEX ephemeris type */ gtime_t toe; /* epoch time (GPST) */ gtime_t tof; /* message frame time (GPST) */ int sat; /* satellite number */ unsigned char health; /* signal health (L1,L2,L1C,L5,LEX) */ unsigned char ura; /* URA index */ double pos[3]; /* satellite position (m) */ double vel[3]; /* satellite velocity (m/s) */ double acc[3]; /* satellite acceleration (m/s2) */ double jerk[3]; /* satellite jerk (m/s3) */ double af0, af1; /* satellite clock bias and drift (s,s/s) */ double tgd; /* TGD */ double isc[8]; /* ISC */ } lexeph_t; typedef struct { /* QZSS LEX ionosphere correction type */ gtime_t t0; /* epoch time (GPST) */ double tspan; /* valid time span (s) */ double pos0[2]; /* reference position {lat,lon} (rad) */ double coef[3][2]; /* coefficients lat x lon (3 x 2) */ } lexion_t; typedef struct { /* stec data type */ gtime_t time; /* time (GPST) */ unsigned char sat; /* satellite number */ double ion; /* slant ionos delay (m) */ float std; /* std-dev (m) */ float azel[2]; /* azimuth/elevation (rad) */ unsigned char flag; /* fix flag */ } stec_t; typedef struct { /* trop data type */ gtime_t time; /* time (GPST) */ double trp[3]; /* zenith tropos delay/gradient (m) */ float std[3]; /* std-dev (m) */ } trop_t; typedef struct { /* ppp corrections type */ int nsta; /* number of stations */ char stas[MAXSTA][8]; /* station names */ double rr[MAXSTA][3]; /* station ecef positions (m) */ int ns[MAXSTA], nsmax[MAXSTA]; /* number of stec data */ int nt[MAXSTA], ntmax[MAXSTA]; /* number of trop data */ stec_t *stec[MAXSTA]; /* stec data */ trop_t *trop[MAXSTA]; /* trop data */ } pppcorr_t; typedef struct { /* navigation data type */ 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 */ erp_t erp; /* earth rotation parameters */ 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) */ double lam[MAXSAT][NFREQ]; /* carrier wave lengths (m) */ 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 */ pcv_t pcvs[MAXSAT]; /* satellite antenna pcv */ sbssat_t sbssat; /* SBAS satellite corrections */ sbsion_t sbsion[MAXBAND + 1]; /* SBAS ionosphere corrections */ 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 */ } nav_t; typedef struct { /* station parameter type */ char name[MAXANT]; /* marker name */ char marker[MAXANT]; /* marker number */ char antdes[MAXANT]; /* antenna descriptor */ char antsno[MAXANT]; /* antenna serial number */ char rectype[MAXANT]; /* receiver type descriptor */ char recver[MAXANT]; /* receiver firmware version */ char recsno[MAXANT]; /* receiver serial number */ int antsetup; /* antenna setup id */ int itrf; /* ITRF realization year */ int deltype; /* antenna delta type (0:enu,1:xyz) */ double pos[3]; /* station position (ecef) (m) */ double del[3]; /* antenna position delta (e/n/u or x/y/z) (m) */ double hgt; /* antenna height (m) */ } sta_t; typedef struct { /* solution type */ gtime_t time; /* time (GPST) */ double rr[6]; /* position/velocity (m|m/s) */ /* {x,y,z,vx,vy,vz} or {e,n,u,ve,vn,vu} */ float qr[6]; /* position variance/covariance (m^2) */ /* {c_xx,c_yy,c_zz,c_xy,c_yz,c_zx} or */ /* {c_ee,c_nn,c_uu,c_en,c_nu,c_ue} */ double dtr[6]; /* receiver clock bias to time systems (s) */ unsigned char type; /* type (0:xyz-ecef,1:enu-baseline) */ unsigned char stat; /* solution status (SOLQ_???) */ unsigned char ns; /* number of valid satellites */ float age; /* age of differential (s) */ float ratio; /* AR ratio factor for validation */ float thres; /* AR ratio threshold for validation */ } sol_t; typedef struct { /* solution buffer type */ int n, nmax; /* number of solution/max number of buffer */ int cyclic; /* cyclic buffer flag */ int start, end; /* start/end index */ gtime_t time; /* current solution time */ sol_t *data; /* solution data */ double rb[3]; /* reference position {x,y,z} (ecef) (m) */ unsigned char buff[MAXSOLMSG + 1]; /* message buffer */ int nb; /* number of byte in message buffer */ } solbuf_t; typedef struct { /* solution status type */ gtime_t time; /* time (GPST) */ unsigned char sat; /* satellite number */ unsigned char frq; /* frequency (1:L1,2:L2,...) */ float az, el; /* azimuth/elevation angle (rad) */ float resp; /* pseudorange residual (m) */ float resc; /* carrier-phase residual (m) */ unsigned char flag; /* flags: (vsat<<5)+(slip<<3)+fix */ unsigned char snr; /* signal strength (0.25 dBHz) */ unsigned short lock; /* lock counter */ unsigned short outc; /* outage counter */ unsigned short slipc; /* slip counter */ unsigned short rejc; /* reject counter */ } solstat_t; typedef struct { /* solution status buffer type */ int n, nmax; /* number of solution/max number of buffer */ solstat_t *data; /* solution status data */ } solstatbuf_t; typedef struct { /* RTCM control struct type */ int staid; /* station id */ int stah; /* station health */ int seqno; /* sequence number for rtcm 2 or iods msm */ int outtype; /* output message type */ gtime_t time; /* message time */ gtime_t time_s; /* message start time */ obs_t obs; /* observation data (uncorrected) */ nav_t nav; /* satellite ephemerides */ sta_t sta; /* station parameters */ dgps_t *dgps; /* output of dgps corrections */ ssr_t ssr[MAXSAT]; /* output of ssr corrections */ char msg[128]; /* special message */ char msgtype[256]; /* last message type */ char msmtype[6][128]; /* msm signal types */ int obsflag; /* obs data complete flag (1:ok,0:not complete) */ int ephsat; /* update satellite of ephemeris */ double cp[MAXSAT][NFREQ + NEXOBS]; /* carrier-phase measurement */ unsigned short lock[MAXSAT][NFREQ + NEXOBS]; /* lock time */ unsigned short loss[MAXSAT][NFREQ + NEXOBS]; /* loss of lock count */ gtime_t lltime[MAXSAT][NFREQ + NEXOBS]; /* last lock time */ int nbyte; /* number of bytes in message buffer */ int nbit; /* number of bits in word buffer */ int len; /* message length (bytes) */ unsigned char buff[1200]; /* message buffer */ unsigned int word; /* word buffer for rtcm 2 */ unsigned int nmsg2[100]; /* message count of RTCM 2 (1-99:1-99,0:other) */ unsigned int nmsg3[400]; /* message count of RTCM 3 (1-299:1001-1299,300-399:2000-2099,0:other) */ char opt[256]; /* RTCM dependent options */ } rtcm_t; typedef struct { /* download url type */ char type[32]; /* data type */ char path[1024]; /* url path */ char dir[1024]; /* local directory */ double tint; /* time interval (s) */ } url_t; typedef struct { /* option type */ const char *name; /* option name */ int format; /* option format (0:int,1:double,2:string,3:enum) */ void *var; /* pointer to option variable */ const char *comment; /* option comment/enum labels/unit */ } opt_t; typedef struct { /* extended receiver error model */ int ena[4]; /* model enabled */ double cerr[4][NFREQ * 2]; /* code errors (m) */ double perr[4][NFREQ * 2]; /* carrier-phase errors (m) */ double gpsglob[NFREQ]; /* gps-glonass h/w bias (m) */ double gloicb[NFREQ]; /* glonass interchannel bias (m/fn) */ } exterr_t; typedef struct { /* SNR mask type */ int ena[2]; /* enable flag {rover,base} */ double mask[NFREQ][9]; /* mask (dBHz) at 5,10,...85 deg */ } snrmask_t; typedef struct { /* processing options type */ int mode; /* positioning mode (PMODE_???) */ int soltype; /* solution type (0:forward,1:backward,2:combined) */ int nf; /* number of frequencies (1:L1,2:L1+L2,3:L1+L2+L5) */ int navsys; /* navigation system */ double elmin; /* elevation mask angle (rad) */ snrmask_t snrmask; /* SNR mask */ int sateph; /* satellite ephemeris/clock (EPHOPT_???) */ int modear; /* AR mode (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */ int glomodear; /* GLONASS AR mode (0:off,1:on,2:auto cal,3:ext cal) */ int bdsmodear; /* BeiDou AR mode (0:off,1:on) */ int maxout; /* obs outage count to reset bias */ int minlock; /* min lock count to fix ambiguity */ int minfix; /* min fix count to hold ambiguity */ int armaxiter; /* max iteration to resolve ambiguity */ int ionoopt; /* ionosphere option (IONOOPT_???) */ int tropopt; /* troposphere option (TROPOPT_???) */ int dynamics; /* dynamics model (0:none,1:velociy,2:accel) */ int tidecorr; /* earth tide correction (0:off,1:solid,2:solid+otl+pole) */ int niter; /* number of filter iteration */ int codesmooth; /* code smoothing window size (0:none) */ int intpref; /* interpolate reference obs (for post mission) */ int sbascorr; /* SBAS correction options */ int sbassatsel; /* SBAS satellite selection (0:all) */ int rovpos; /* rover position for fixed mode */ int refpos; /* base position for relative mode */ /* (0:pos in prcopt, 1:average of single pos, */ /* 2:read from file, 3:rinex header, 4:rtcm pos) */ double eratio[NFREQ]; /* code/phase error ratio */ double err[5]; /* measurement error factor */ /* [0]:reserved */ /* [1-3]:error factor a/b/c of phase (m) */ /* [4]:doppler frequency (hz) */ double std[3]; /* initial-state std [0]bias,[1]iono [2]trop */ double prn[6]; /* process-noise std [0]bias,[1]iono [2]trop [3]acch [4]accv [5] pos */ double sclkstab; /* satellite clock stability (sec/sec) */ double thresar[8]; /* AR validation threshold */ double elmaskar; /* elevation mask of AR for rising satellite (deg) */ double elmaskhold; /* elevation mask to hold ambiguity (deg) */ double thresslip; /* slip threshold of geometry-free phase (m) */ double maxtdiff; /* max difference of time (sec) */ double maxinno; /* reject threshold of innovation (m) */ double maxgdop; /* reject threshold of gdop */ double baseline[2]; /* baseline length constraint {const,sigma} (m) */ double ru[3]; /* rover position for fixed mode {x,y,z} (ecef) (m) */ double rb[3]; /* base position for relative mode {x,y,z} (ecef) (m) */ char anttype[2][MAXANT]; /* antenna types {rover,base} */ double antdel[2][3]; /* antenna delta {{rov_e,rov_n,rov_u},{ref_e,ref_n,ref_u}} */ pcv_t pcvr[2]; /* receiver antenna parameters {rov,base} */ unsigned char exsats[MAXSAT]; /* excluded satellites (1:excluded,2:included) */ int maxaveep; /* max averaging epoches */ int initrst; /* initialize by restart */ int outsingle; /* output single by dgps/float/fix/ppp outage */ char rnxopt[2][256]; /* rinex options {rover,base} */ int posopt[6]; /* positioning options */ int syncsol; /* solution sync mode (0:off,1:on) */ double odisp[2][6 * 11]; /* ocean tide loading parameters {rov,base} */ exterr_t exterr; /* extended receiver error model */ int freqopt; /* disable L2-AR */ char pppopt[256]; /* ppp option */ } prcopt_t; typedef struct { /* solution options type */ int posf; /* solution format (SOLF_???) */ int times; /* time system (TIMES_???) */ int timef; /* time format (0:sssss.s,1:yyyy/mm/dd hh:mm:ss.s) */ int timeu; /* time digits under decimal point */ int degf; /* latitude/longitude format (0:ddd.ddd,1:ddd mm ss) */ int outhead; /* output header (0:no,1:yes) */ int outopt; /* output processing options (0:no,1:yes) */ int datum; /* datum (0:WGS84,1:Tokyo) */ int height; /* height (0:ellipsoidal,1:geodetic) */ int geoid; /* geoid model (0:EGM96,1:JGD2000) */ int solstatic; /* solution of static mode (0:all,1:single) */ int sstat; /* solution statistics level (0:off,1:states,2:residuals) */ int trace; /* debug trace level (0:off,1-5:debug) */ double nmeaintv[2]; /* nmea output interval (s) (<0:no,0:all) */ /* nmeaintv[0]:gprmc,gpgga,nmeaintv[1]:gpgsv */ char sep[64]; /* field separator */ char prog[64]; /* program name */ double maxsolstd; /* max std-dev for solution output (m) (0:all) */ } solopt_t; typedef struct { /* satellite status type */ unsigned char sys; /* navigation system */ unsigned char vs; /* valid satellite flag single */ double azel[2]; /* azimuth/elevation angles {az,el} (rad) */ double resp[NFREQ]; /* residuals of pseudorange (m) */ double resc[NFREQ]; /* residuals of carrier-phase (m) */ unsigned char vsat[NFREQ]; /* valid satellite flag */ unsigned char snr[NFREQ]; /* signal strength (0.25 dBHz) */ unsigned char fix[NFREQ]; /* ambiguity fix flag (1:fix,2:float,3:hold) */ unsigned char slip[NFREQ]; /* cycle-slip flag */ unsigned char half[NFREQ]; /* half-cycle valid flag */ int lock[NFREQ]; /* lock counter of phase */ unsigned int outc[NFREQ]; /* obs outage counter of phase */ unsigned int slipc[NFREQ]; /* cycle-slip counter */ unsigned int rejc[NFREQ]; /* reject counter */ double gf; /* geometry-free phase L1-L2 (m) */ double gf2; /* geometry-free phase L1-L5 (m) */ double mw; /* MW-LC (m) */ double phw; /* phase windup (cycle) */ gtime_t pt[2][NFREQ]; /* previous carrier-phase time */ double ph[2][NFREQ]; /* previous carrier-phase observable (cycle) */ } ssat_t; typedef struct { /* ambiguity control type */ gtime_t epoch[4]; /* last epoch */ int n[4]; /* number of epochs */ double LC[4]; /* linear combination average */ double LCv[4]; /* linear combination variance */ int fixcnt; /* fix count */ char flags[MAXSAT]; /* fix flags */ } ambc_t; typedef struct { /* RTK control/result type */ sol_t sol; /* RTK solution */ double rb[6]; /* base position/velocity (ecef) (m|m/s) */ int nx, na; /* number of float states/fixed states */ double tt; /* time difference between current and previous (s) */ double *x, *P; /* float states and their covariance */ double *xa, *Pa; /* fixed states and their covariance */ int nfix; /* number of continuous fixes of ambiguity */ ambc_t ambc[MAXSAT]; /* ambiguity control */ ssat_t ssat[MAXSAT]; /* satellite status */ int neb; /* bytes in error message buffer */ char errbuf[MAXERRMSG]; /* error message buffer */ prcopt_t opt; /* processing options */ } rtk_t; typedef struct half_cyc_tag { /* half-cycle correction list type */ unsigned char sat; /* satellite number */ unsigned char freq; /* frequency number (0:L1,1:L2,2:L5) */ unsigned char valid; /* half-cycle valid flag */ char corr; /* half-cycle corrected (x 0.5 cyc) */ gtime_t ts, te; /* time start, time end */ struct half_cyc_tag *next; /* pointer to next correction */ } half_cyc_t; typedef struct { /* stream type */ int type; /* type (STR_???) */ int mode; /* mode (STR_MODE_?) */ int state; /* state (-1:error,0:close,1:open) */ unsigned int inb, inr; /* input bytes/rate */ unsigned int outb, outr; /* output bytes/rate */ unsigned int tick, tact; /* tick/active tick */ unsigned int inbt, outbt; /* input/output bytes at tick */ lock_t lock; /* lock flag */ void *port; /* type dependent port control struct */ char path[MAXSTRPATH]; /* stream path */ char msg[MAXSTRMSG]; /* stream message */ } stream_t; typedef struct { /* serial control type */ dev_t dev; /* serial device */ int error; /* error state */ } serial_t; typedef struct { /* file control type */ FILE *fp; /* file pointer */ FILE *fp_tag; /* file pointer of tag file */ FILE *fp_tmp; /* temporary file pointer for swap */ FILE *fp_tag_tmp; /* temporary file pointer of tag file for swap */ char path[MAXSTRPATH]; /* file path */ char openpath[MAXSTRPATH]; /* open file path */ int mode; /* file mode */ int timetag; /* time tag flag (0:off,1:on) */ int repmode; /* replay mode (0:master,1:slave) */ int offset; /* time offset (ms) for slave */ gtime_t time; /* start time */ gtime_t wtime; /* write time */ unsigned int tick; /* start tick */ unsigned int tick_f; /* start tick in file */ unsigned int fpos; /* current file position */ double start; /* start offset (s) */ double speed; /* replay speed (time factor) */ double swapintv; /* swap interval (hr) (0: no swap) */ lock_t lock; /* lock flag */ } file_t; typedef struct { /* tcp control type */ int state; /* state (0:close,1:wait,2:connect) */ char saddr[256]; /* address string */ int port; /* port */ struct sockaddr_in addr; /* address resolved */ socket_t sock; /* socket descriptor */ int tcon; /* reconnect time (ms) (-1:never,0:now) */ unsigned int tact; /* data active tick */ unsigned int tdis; /* disconnect tick */ } tcp_t; typedef struct { /* tcp server type */ tcp_t svr; /* tcp server control */ tcp_t cli[MAXCLI]; /* tcp client controls */ } tcpsvr_t; typedef struct { /* tcp cilent type */ tcp_t svr; /* tcp server control */ int toinact; /* inactive timeout (ms) (0:no timeout) */ int tirecon; /* reconnect interval (ms) (0:no reconnect) */ } tcpcli_t; typedef struct { /* ntrip control type */ int state; /* state (0:close,1:wait,2:connect) */ int type; /* type (0:server,1:client) */ int nb; /* response buffer size */ char url[256]; /* url for proxy */ char mntpnt[256]; /* mountpoint */ char user[256]; /* user */ char passwd[256]; /* password */ char str[NTRIP_MAXSTR]; /* mountpoint string for server */ unsigned char buff[NTRIP_MAXRSP]; /* response buffer */ tcpcli_t *tcp; /* tcp client */ } ntrip_t; typedef struct { /* ftp download control type */ int state; /* state (0:close,1:download,2:complete,3:error) */ int proto; /* protocol (0:ftp,1:http) */ int error; /* error code (0:no error,1-10:wget error, */ /* 11:no temp dir,12:uncompact error) */ char addr[1024]; /* download address */ char file[1024]; /* download file path */ char user[256]; /* user for ftp */ char passwd[256]; /* password for ftp */ char local[1024]; /* local file path */ int topts[4]; /* time options {poff,tint,toff,tretry} (s) */ gtime_t tnext; /* next retry time (gpst) */ pthread_t thread; /* download thread */ } ftp_t; typedef struct { /* receiver raw data control type */ gtime_t time; /* message time */ gtime_t tobs; /* observation data time */ obs_t obs; /* observation data */ obs_t obuf; /* observation data buffer */ nav_t nav; /* satellite ephemerides */ sta_t sta; /* station parameters */ int ephsat; /* sat number of update ephemeris (0:no satellite) */ sbsmsg_t sbsmsg; /* SBAS message */ char msgtype[256]; /* last message type */ unsigned char subfrm[MAXSAT][380]; /* subframe buffer */ lexmsg_t lexmsg; /* LEX message */ double lockt[MAXSAT][NFREQ + NEXOBS]; /* lock time (s) */ double icpp[MAXSAT], off[MAXSAT], icpc; /* carrier params for ss2 */ double prCA[MAXSAT], dpCA[MAXSAT]; /* L1/CA pseudrange/doppler for javad */ unsigned char halfc[MAXSAT][NFREQ + NEXOBS]; /* half-cycle add flag */ char freqn[MAXOBS]; /* frequency number for javad */ int nbyte; /* number of bytes in message buffer */ int len; /* message length (bytes) */ int iod; /* issue of data */ int tod; /* time of day (ms) */ int tbase; /* time base (0:gpst,1:utc(usno),2:glonass,3:utc(su) */ int flag; /* general purpose flag */ int outtype; /* output message type */ unsigned char buff[MAXRAWLEN]; /* message buffer */ char opt[256]; /* receiver dependent options */ double receive_time; /* RT17: Reiceve time of week for week rollover detection */ unsigned int plen; /* RT17: Total size of packet to be read */ unsigned int pbyte; /* RT17: How many packet bytes have been read so far */ unsigned int page; /* RT17: Last page number */ unsigned int reply; /* RT17: Current reply number */ int week; /* RT17: week number */ unsigned char pbuff[255 + 4 + 2]; /* RT17: Packet buffer */ } raw_t; typedef struct { /* RTK server type */ int state; /* server state (0:stop,1:running) */ int cycle; /* processing cycle (ms) */ int nmeacycle; /* NMEA request cycle (ms) (0:no req) */ int nmeareq; /* NMEA request (0:no,1:nmeapos,2:single sol) */ double nmeapos[3]; /* NMEA request position (ecef) (m) */ int buffsize; /* input buffer size (bytes) */ int format[3]; /* input format {rov,base,corr} */ solopt_t solopt[2]; /* output solution options {sol1,sol2} */ int navsel; /* ephemeris select (0:all,1:rover,2:base,3:corr) */ int nsbs; /* number of sbas message */ int nsol; /* number of solution buffer */ rtk_t rtk; /* RTK control/result struct */ int nb[3]; /* bytes in input buffers {rov,base} */ int nsb[2]; /* bytes in soulution buffers */ int npb[3]; /* bytes in input peek buffers */ unsigned char *buff[3]; /* input buffers {rov,base,corr} */ unsigned char *sbuf[2]; /* output buffers {sol1,sol2} */ unsigned char *pbuf[3]; /* peek buffers {rov,base,corr} */ sol_t solbuf[MAXSOLBUF]; /* solution buffer */ unsigned int nmsg[3][10]; /* input message counts */ raw_t raw[3]; /* receiver raw control {rov,base,corr} */ rtcm_t rtcm[3]; /* RTCM control {rov,base,corr} */ gtime_t ftime[3]; /* download time {rov,base,corr} */ char files[3][MAXSTRPATH]; /* download paths {rov,base,corr} */ obs_t obs[3][MAXOBSBUF]; /* observation data {rov,base,corr} */ nav_t nav; /* navigation data */ sbsmsg_t sbsmsg[MAXSBSMSG]; /* SBAS message buffer */ stream_t stream[8]; /* streams {rov,base,corr,sol1,sol2,logr,logb,logc} */ stream_t *moni; /* monitor stream */ unsigned int tick; /* start tick */ pthread_t thread; /* server thread */ int cputime; /* CPU time (ms) for a processing cycle */ int prcout; /* missing observation data count */ lock_t lock; /* lock flag */ } rtksvr_t; typedef struct { /* multi-signal-message header type */ unsigned char iod; /* issue of data station */ unsigned char time_s; /* cumulative session transmitting time */ unsigned char clk_str; /* clock steering indicator */ unsigned char clk_ext; /* external clock indicator */ unsigned char smooth; /* divergence free smoothing indicator */ unsigned char tint_s; /* soothing interval */ unsigned char nsat, nsig; /* number of satellites/signals */ unsigned char sats[64]; /* satellites */ unsigned char sigs[32]; /* signals */ unsigned char cellmask[64]; /* cell mask */ } msm_h_t; const double CHISQR[100] = {/* chi-sqr(n) (alpha=0.001) */ 10.8, 13.8, 16.3, 18.5, 20.5, 22.5, 24.3, 26.1, 27.9, 29.6, 31.3, 32.9, 34.5, 36.1, 37.7, 39.3, 40.8, 42.3, 43.8, 45.3, 46.8, 48.3, 49.7, 51.2, 52.6, 54.1, 55.5, 56.9, 58.3, 59.7, 61.1, 62.5, 63.9, 65.2, 66.6, 68.0, 69.3, 70.7, 72.1, 73.4, 74.7, 76.0, 77.3, 78.6, 80.0, 81.3, 82.6, 84.0, 85.4, 86.7, 88.0, 89.3, 90.6, 91.9, 93.3, 94.7, 96.0, 97.4, 98.7, 100, 101, 102, 103, 104, 105, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118, 119, 120, 122, 123, 125, 126, 127, 128, 129, 131, 132, 133, 134, 135, 137, 138, 139, 140, 142, 143, 144, 145, 147, 148, 149}; const double LAM_CARR[MAXFREQ] = {/* carrier wave length (m) */ SPEED_OF_LIGHT / FREQ1, SPEED_OF_LIGHT / FREQ2, SPEED_OF_LIGHT / FREQ5, SPEED_OF_LIGHT / FREQ6, SPEED_OF_LIGHT / FREQ7, SPEED_OF_LIGHT / FREQ8, SPEED_OF_LIGHT / FREQ9}; const int STRFMT_RTCM2 = 0; /* stream format: RTCM 2 */ const int STRFMT_RTCM3 = 1; /* stream format: RTCM 3 */ const int STRFMT_SP3 = 16; /* stream format: SP3 */ const int STRFMT_RNXCLK = 17; /* stream format: RINEX CLK */ const int STRFMT_SBAS = 18; /* stream format: SBAS messages */ const int STRFMT_NMEA = 19; /* stream format: NMEA 0183 */ //const solopt_t solopt_default; /* default solution output options */ const int MAXSTRRTK = 8; /* max number of stream in RTK server */ #endif src/algorithms/libs/rtklib/rtklib_conversions.cc000066400000000000000000000450701352176506000224770ustar00rootroot00000000000000/*! * \file rtklib_conversions.cc * \brief GNSS-SDR to RTKLIB data structures conversion functions * \author 2017, Javier Arribas * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "rtklib_conversions.h" #include "MATH_CONSTANTS.h" // for PI, PI_2 #include "beidou_dnav_ephemeris.h" // for Beidou_Dnav_Ephemeris #include "galileo_almanac.h" // for Galileo_Almanac #include "galileo_ephemeris.h" // for Galileo_Ephemeris #include "glonass_gnav_ephemeris.h" // for Glonass_Gnav_Ephemeris #include "glonass_gnav_utc_model.h" // for Glonass_Gnav_Utc_Model #include "gnss_obs_codes.h" // for CODE_L1C, CODE_L2S, CODE_L5X #include "gnss_synchro.h" // for Gnss_Synchro #include "gps_almanac.h" // for Gps_Almanac #include "gps_cnav_ephemeris.h" // for Gps_CNAV_Ephemeris #include "gps_ephemeris.h" // for Gps_Ephemeris #include "rtklib_rtkcmn.h" #include #include #include obsd_t insert_obs_to_rtklib(obsd_t& rtklib_obs, const Gnss_Synchro& gnss_synchro, int week, int band) { // Get signal type info to adjust code type based on constellation std::string sig_ = gnss_synchro.Signal; rtklib_obs.D[band] = gnss_synchro.Carrier_Doppler_hz; rtklib_obs.P[band] = gnss_synchro.Pseudorange_m; rtklib_obs.L[band] = gnss_synchro.Carrier_phase_rads / PI_2; switch (band) { case 0: rtklib_obs.code[band] = static_cast(CODE_L1C); break; case 1: rtklib_obs.code[band] = static_cast(CODE_L2S); break; case 2: rtklib_obs.code[band] = static_cast(CODE_L5X); break; } double CN0_dB_Hz_est = gnss_synchro.CN0_dB_hz; if (CN0_dB_Hz_est > 63.75) { CN0_dB_Hz_est = 63.75; } if (CN0_dB_Hz_est < 0.0) { CN0_dB_Hz_est = 0.0; } auto CN0_dB_Hz = static_cast(std::round(CN0_dB_Hz_est / 0.25)); rtklib_obs.SNR[band] = CN0_dB_Hz; //Galileo is the third satellite system for RTKLIB, so, add the required offset to discriminate Galileo ephemeris switch (gnss_synchro.System) { case 'G': rtklib_obs.sat = gnss_synchro.PRN; break; case 'E': rtklib_obs.sat = gnss_synchro.PRN + NSATGPS + NSATGLO; break; case 'R': rtklib_obs.sat = gnss_synchro.PRN + NSATGPS; break; case 'C': rtklib_obs.sat = gnss_synchro.PRN + NSATGPS + NSATGLO + NSATGAL + NSATQZS; // Update signal code if (sig_ == "B1") { rtklib_obs.code[band] = static_cast(CODE_L2I); } else if (sig_ == "B3") { rtklib_obs.code[band] = static_cast(CODE_L6I); } break; default: rtklib_obs.sat = gnss_synchro.PRN; } // Mote that BeiDou week numbers do not need adjustment for foreseeable future. Consider change // to more elegant solution // if(gnss_synchro.System == 'C') // { // rtklib_obs.time = bdt2gpst(bdt2time(week, gnss_synchro.RX_time)); // } // else // { // rtklib_obs.time = gpst2time(adjgpsweek(week), gnss_synchro.RX_time); // } // rtklib_obs.time = gpst2time(adjgpsweek(week), gnss_synchro.RX_time); //account for the TOW crossover transitory in the first 18 seconds where the week is not yet updated! if (gnss_synchro.RX_time < 18.0) { //p_time += boost::posix_time::seconds(604800); rtklib_obs.time = timeadd(rtklib_obs.time, 604800); } rtklib_obs.rcv = 1; return rtklib_obs; } geph_t eph_to_rtklib(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& gnav_clock_model) { double week, sec; int adj_week; geph_t rtklib_sat = {0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, 0.0, 0.0}; rtklib_sat.sat = glonass_gnav_eph.i_satellite_slot_number + NSATGPS; /* satellite number */ rtklib_sat.iode = static_cast(glonass_gnav_eph.d_t_b); /* IODE (0-6 bit of tb field) */ rtklib_sat.frq = glonass_gnav_eph.i_satellite_freq_channel; /* satellite frequency number */ rtklib_sat.svh = glonass_gnav_eph.d_l3rd_n; /* satellite health*/ rtklib_sat.sva = static_cast(glonass_gnav_eph.d_F_T); /* satellite accuracy*/ rtklib_sat.age = static_cast(glonass_gnav_eph.d_E_n); /* satellite age*/ rtklib_sat.pos[0] = glonass_gnav_eph.d_Xn * 1000; /* satellite position (ecef) (m) */ rtklib_sat.pos[1] = glonass_gnav_eph.d_Yn * 1000; /* satellite position (ecef) (m) */ rtklib_sat.pos[2] = glonass_gnav_eph.d_Zn * 1000; /* satellite position (ecef) (m) */ rtklib_sat.vel[0] = glonass_gnav_eph.d_VXn * 1000; /* satellite velocity (ecef) (m/s) */ rtklib_sat.vel[1] = glonass_gnav_eph.d_VYn * 1000; /* satellite velocity (ecef) (m/s) */ rtklib_sat.vel[2] = glonass_gnav_eph.d_VZn * 1000; /* satellite velocity (ecef) (m/s) */ rtklib_sat.acc[0] = glonass_gnav_eph.d_AXn * 1000; /* satellite acceleration (ecef) (m/s^2) */ rtklib_sat.acc[1] = glonass_gnav_eph.d_AYn * 1000; /* satellite acceleration (ecef) (m/s^2) */ rtklib_sat.acc[2] = glonass_gnav_eph.d_AZn * 1000; /* satellite acceleration (ecef) (m/s^2) */ rtklib_sat.taun = glonass_gnav_eph.d_tau_n; /* SV clock bias (s) */ rtklib_sat.gamn = glonass_gnav_eph.d_gamma_n; /* SV relative freq bias */ rtklib_sat.age = static_cast(glonass_gnav_eph.d_Delta_tau_n); /* delay between L1 and L2 (s) */ // Time expressed in GPS Time but using RTKLib format glonass_gnav_eph.glot_to_gpst(glonass_gnav_eph.d_t_b, gnav_clock_model.d_tau_c, gnav_clock_model.d_tau_gps, &week, &sec); adj_week = adjgpsweek(static_cast(week)); rtklib_sat.toe = gpst2time(adj_week, sec); // Time expressed in GPS Time but using RTKLib format glonass_gnav_eph.glot_to_gpst(glonass_gnav_eph.d_t_k, gnav_clock_model.d_tau_c, gnav_clock_model.d_tau_gps, &week, &sec); adj_week = adjgpsweek(static_cast(week)); rtklib_sat.tof = gpst2time(adj_week, sec); return rtklib_sat; } eph_t eph_to_rtklib(const Galileo_Ephemeris& gal_eph) { eph_t rtklib_sat = {0, 0, 0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {}, {}, 0.0, 0.0}; //Galileo is the third satellite system for RTKLIB, so, add the required offset to discriminate Galileo ephemeris rtklib_sat.sat = gal_eph.i_satellite_PRN + NSATGPS + NSATGLO; rtklib_sat.A = gal_eph.A_1 * gal_eph.A_1; rtklib_sat.M0 = gal_eph.M0_1; rtklib_sat.deln = gal_eph.delta_n_3; rtklib_sat.OMG0 = gal_eph.OMEGA_0_2; rtklib_sat.OMGd = gal_eph.OMEGA_dot_3; rtklib_sat.omg = gal_eph.omega_2; rtklib_sat.i0 = gal_eph.i_0_2; rtklib_sat.idot = gal_eph.iDot_2; rtklib_sat.e = gal_eph.e_1; rtklib_sat.Adot = 0; //only in CNAV; rtklib_sat.ndot = 0; //only in CNAV; rtklib_sat.week = adjgpsweek(gal_eph.WN_5); /* week of tow */ rtklib_sat.cic = gal_eph.C_ic_4; rtklib_sat.cis = gal_eph.C_is_4; rtklib_sat.cuc = gal_eph.C_uc_3; rtklib_sat.cus = gal_eph.C_us_3; rtklib_sat.crc = gal_eph.C_rc_3; rtklib_sat.crs = gal_eph.C_rs_3; rtklib_sat.f0 = gal_eph.af0_4; rtklib_sat.f1 = gal_eph.af1_4; rtklib_sat.f2 = gal_eph.af2_4; rtklib_sat.tgd[0] = gal_eph.BGD_E1E5a_5; rtklib_sat.tgd[1] = gal_eph.BGD_E1E5b_5; rtklib_sat.tgd[2] = 0; rtklib_sat.tgd[3] = 0; rtklib_sat.toes = gal_eph.t0e_1; rtklib_sat.toc = gpst2time(rtklib_sat.week, gal_eph.t0c_4); rtklib_sat.ttr = gpst2time(rtklib_sat.week, gal_eph.TOW_5); /* adjustment for week handover */ double tow, toc; tow = time2gpst(rtklib_sat.ttr, &rtklib_sat.week); toc = time2gpst(rtklib_sat.toc, nullptr); if (rtklib_sat.toes < tow - 302400.0) { rtklib_sat.week++; tow -= 604800.0; } else if (rtklib_sat.toes > tow + 302400.0) { rtklib_sat.week--; tow += 604800.0; } rtklib_sat.toe = gpst2time(rtklib_sat.week, rtklib_sat.toes); rtklib_sat.toc = gpst2time(rtklib_sat.week, toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, tow); return rtklib_sat; } eph_t eph_to_rtklib(const Gps_Ephemeris& gps_eph) { eph_t rtklib_sat = {0, 0, 0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {}, {}, 0.0, 0.0}; rtklib_sat.sat = gps_eph.i_satellite_PRN; rtklib_sat.A = gps_eph.d_sqrt_A * gps_eph.d_sqrt_A; rtklib_sat.M0 = gps_eph.d_M_0; rtklib_sat.deln = gps_eph.d_Delta_n; rtklib_sat.OMG0 = gps_eph.d_OMEGA0; rtklib_sat.OMGd = gps_eph.d_OMEGA_DOT; rtklib_sat.omg = gps_eph.d_OMEGA; rtklib_sat.i0 = gps_eph.d_i_0; rtklib_sat.idot = gps_eph.d_IDOT; rtklib_sat.e = gps_eph.d_e_eccentricity; rtklib_sat.Adot = 0; //only in CNAV; rtklib_sat.ndot = 0; //only in CNAV; rtklib_sat.week = adjgpsweek(gps_eph.i_GPS_week); /* week of tow */ rtklib_sat.cic = gps_eph.d_Cic; rtklib_sat.cis = gps_eph.d_Cis; rtklib_sat.cuc = gps_eph.d_Cuc; rtklib_sat.cus = gps_eph.d_Cus; rtklib_sat.crc = gps_eph.d_Crc; rtklib_sat.crs = gps_eph.d_Crs; rtklib_sat.f0 = gps_eph.d_A_f0; rtklib_sat.f1 = gps_eph.d_A_f1; rtklib_sat.f2 = gps_eph.d_A_f2; rtklib_sat.tgd[0] = gps_eph.d_TGD; rtklib_sat.tgd[1] = 0.0; rtklib_sat.tgd[2] = 0.0; rtklib_sat.tgd[3] = 0.0; rtklib_sat.toes = gps_eph.d_Toe; rtklib_sat.toc = gpst2time(rtklib_sat.week, gps_eph.d_Toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, gps_eph.d_TOW); /* adjustment for week handover */ double tow, toc; tow = time2gpst(rtklib_sat.ttr, &rtklib_sat.week); toc = time2gpst(rtklib_sat.toc, nullptr); if (rtklib_sat.toes < tow - 302400.0) { rtklib_sat.week++; tow -= 604800.0; } else if (rtklib_sat.toes > tow + 302400.0) { rtklib_sat.week--; tow += 604800.0; } rtklib_sat.toe = gpst2time(rtklib_sat.week, rtklib_sat.toes); rtklib_sat.toc = gpst2time(rtklib_sat.week, toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, tow); return rtklib_sat; } eph_t eph_to_rtklib(const Beidou_Dnav_Ephemeris& bei_eph) { eph_t rtklib_sat = {0, 0, 0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {}, {}, 0.0, 0.0}; rtklib_sat.sat = bei_eph.i_satellite_PRN + NSATGPS + NSATGLO + NSATGAL + NSATQZS; rtklib_sat.A = bei_eph.d_sqrt_A * bei_eph.d_sqrt_A; rtklib_sat.M0 = bei_eph.d_M_0; rtklib_sat.deln = bei_eph.d_Delta_n; rtklib_sat.OMG0 = bei_eph.d_OMEGA0; rtklib_sat.OMGd = bei_eph.d_OMEGA_DOT; rtklib_sat.omg = bei_eph.d_OMEGA; rtklib_sat.i0 = bei_eph.d_i_0; rtklib_sat.idot = bei_eph.d_IDOT; rtklib_sat.e = bei_eph.d_eccentricity; rtklib_sat.Adot = 0; //only in CNAV; rtklib_sat.ndot = 0; //only in CNAV; rtklib_sat.svh = bei_eph.i_SV_health; rtklib_sat.sva = bei_eph.i_SV_accuracy; rtklib_sat.code = bei_eph.i_sig_type; /*B1I data*/ rtklib_sat.flag = bei_eph.i_nav_type; /*MEO/IGSO satellite*/ rtklib_sat.iode = static_cast(bei_eph.d_AODE); /* AODE */ rtklib_sat.iodc = static_cast(bei_eph.d_AODC); /* AODC */ rtklib_sat.week = bei_eph.i_BEIDOU_week; /* week of tow */ rtklib_sat.cic = bei_eph.d_Cic; rtklib_sat.cis = bei_eph.d_Cis; rtklib_sat.cuc = bei_eph.d_Cuc; rtklib_sat.cus = bei_eph.d_Cus; rtklib_sat.crc = bei_eph.d_Crc; rtklib_sat.crs = bei_eph.d_Crs; rtklib_sat.f0 = bei_eph.d_A_f0; rtklib_sat.f1 = bei_eph.d_A_f1; rtklib_sat.f2 = bei_eph.d_A_f2; rtklib_sat.tgd[0] = bei_eph.d_TGD1; rtklib_sat.tgd[1] = bei_eph.d_TGD2; rtklib_sat.tgd[2] = 0.0; rtklib_sat.tgd[3] = 0.0; rtklib_sat.toes = bei_eph.d_Toe; rtklib_sat.toe = bdt2gpst(bdt2time(rtklib_sat.week, bei_eph.d_Toe)); rtklib_sat.toc = bdt2gpst(bdt2time(rtklib_sat.week, bei_eph.d_Toc)); rtklib_sat.ttr = bdt2gpst(bdt2time(rtklib_sat.week, bei_eph.d_TOW)); /* adjustment for week handover */ double tow, toc, toe; tow = time2gpst(rtklib_sat.ttr, &rtklib_sat.week); toc = time2gpst(rtklib_sat.toc, nullptr); toe = time2gpst(rtklib_sat.toe, nullptr); if (rtklib_sat.toes < tow - 302400.0) { rtklib_sat.week++; tow -= 604800.0; } else if (rtklib_sat.toes > tow + 302400.0) { rtklib_sat.week--; tow += 604800.0; } rtklib_sat.toe = gpst2time(rtklib_sat.week, toe); rtklib_sat.toc = gpst2time(rtklib_sat.week, toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, tow); return rtklib_sat; } eph_t eph_to_rtklib(const Gps_CNAV_Ephemeris& gps_cnav_eph) { eph_t rtklib_sat = {0, 0, 0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {}, {}, 0.0, 0.0}; rtklib_sat.sat = gps_cnav_eph.i_satellite_PRN; const double A_REF = 26559710.0; // See IS-GPS-200H, pp. 170 rtklib_sat.A = A_REF + gps_cnav_eph.d_DELTA_A; rtklib_sat.M0 = gps_cnav_eph.d_M_0; rtklib_sat.deln = gps_cnav_eph.d_Delta_n; rtklib_sat.OMG0 = gps_cnav_eph.d_OMEGA0; // Compute the angle between the ascending node and the Greenwich meridian const double OMEGA_DOT_REF = -2.6e-9; // semicircles / s, see IS-GPS-200H pp. 164 double d_OMEGA_DOT = OMEGA_DOT_REF * PI + gps_cnav_eph.d_DELTA_OMEGA_DOT; rtklib_sat.OMGd = d_OMEGA_DOT; rtklib_sat.omg = gps_cnav_eph.d_OMEGA; rtklib_sat.i0 = gps_cnav_eph.d_i_0; rtklib_sat.idot = gps_cnav_eph.d_IDOT; rtklib_sat.e = gps_cnav_eph.d_e_eccentricity; rtklib_sat.Adot = gps_cnav_eph.d_A_DOT; //only in CNAV; rtklib_sat.ndot = gps_cnav_eph.d_DELTA_DOT_N; //only in CNAV; rtklib_sat.week = adjgpsweek(gps_cnav_eph.i_GPS_week); /* week of tow */ rtklib_sat.cic = gps_cnav_eph.d_Cic; rtklib_sat.cis = gps_cnav_eph.d_Cis; rtklib_sat.cuc = gps_cnav_eph.d_Cuc; rtklib_sat.cus = gps_cnav_eph.d_Cus; rtklib_sat.crc = gps_cnav_eph.d_Crc; rtklib_sat.crs = gps_cnav_eph.d_Crs; rtklib_sat.f0 = gps_cnav_eph.d_A_f0; rtklib_sat.f1 = gps_cnav_eph.d_A_f1; rtklib_sat.f2 = gps_cnav_eph.d_A_f2; rtklib_sat.tgd[0] = gps_cnav_eph.d_TGD; rtklib_sat.tgd[1] = 0.0; rtklib_sat.tgd[2] = 0.0; rtklib_sat.tgd[3] = 0.0; rtklib_sat.isc[0] = gps_cnav_eph.d_ISCL1; rtklib_sat.isc[1] = gps_cnav_eph.d_ISCL2; rtklib_sat.isc[2] = gps_cnav_eph.d_ISCL5I; rtklib_sat.isc[3] = gps_cnav_eph.d_ISCL5Q; rtklib_sat.toes = gps_cnav_eph.d_Toe1; rtklib_sat.toc = gpst2time(rtklib_sat.week, gps_cnav_eph.d_Toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, gps_cnav_eph.d_TOW); /* adjustment for week handover */ double tow, toc; tow = time2gpst(rtklib_sat.ttr, &rtklib_sat.week); toc = time2gpst(rtklib_sat.toc, nullptr); if (rtklib_sat.toes < tow - 302400.0) { rtklib_sat.week++; tow -= 604800.0; } else if (rtklib_sat.toes > tow + 302400.0) { rtklib_sat.week--; tow += 604800.0; } rtklib_sat.toe = gpst2time(rtklib_sat.week, rtklib_sat.toes); rtklib_sat.toc = gpst2time(rtklib_sat.week, toc); rtklib_sat.ttr = gpst2time(rtklib_sat.week, tow); return rtklib_sat; } alm_t alm_to_rtklib(const Gps_Almanac& gps_alm) { alm_t rtklib_alm; rtklib_alm = {0, 0, 0, 0, {0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rtklib_alm.sat = gps_alm.i_satellite_PRN; rtklib_alm.svh = gps_alm.i_SV_health; rtklib_alm.svconf = gps_alm.i_AS_status; rtklib_alm.week = gps_alm.i_WNa; gtime_t toa; toa.time = gps_alm.i_Toa; toa.sec = 0.0; rtklib_alm.toa = toa; rtklib_alm.A = gps_alm.d_sqrt_A * gps_alm.d_sqrt_A; rtklib_alm.e = gps_alm.d_e_eccentricity; rtklib_alm.i0 = (gps_alm.d_Delta_i + 0.3) * PI; rtklib_alm.OMG0 = gps_alm.d_OMEGA0 * PI; rtklib_alm.OMGd = gps_alm.d_OMEGA_DOT * PI; rtklib_alm.omg = gps_alm.d_OMEGA * PI; rtklib_alm.M0 = gps_alm.d_M_0 * PI; rtklib_alm.f0 = gps_alm.d_A_f0; rtklib_alm.f1 = gps_alm.d_A_f1; rtklib_alm.toas = gps_alm.i_Toa; return rtklib_alm; } alm_t alm_to_rtklib(const Galileo_Almanac& gal_alm) { alm_t rtklib_alm; rtklib_alm = {0, 0, 0, 0, {0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rtklib_alm.sat = gal_alm.i_satellite_PRN + NSATGPS + NSATGLO; rtklib_alm.svh = gal_alm.E1B_HS; rtklib_alm.svconf = gal_alm.E1B_HS; rtklib_alm.week = gal_alm.i_WNa; gtime_t toa; toa.time = gal_alm.i_Toa; toa.sec = 0.0; rtklib_alm.toa = toa; rtklib_alm.A = 5440.588203494 + gal_alm.d_Delta_sqrt_A; rtklib_alm.A = rtklib_alm.A * rtklib_alm.A; rtklib_alm.e = gal_alm.d_e_eccentricity; rtklib_alm.i0 = (gal_alm.d_Delta_i + 56.0 / 180.0) * PI; rtklib_alm.OMG0 = gal_alm.d_OMEGA0 * PI; rtklib_alm.OMGd = gal_alm.d_OMEGA_DOT * PI; rtklib_alm.omg = gal_alm.d_OMEGA * PI; rtklib_alm.M0 = gal_alm.d_M_0 * PI; rtklib_alm.f0 = gal_alm.d_A_f0; rtklib_alm.f1 = gal_alm.d_A_f1; rtklib_alm.toas = gal_alm.i_Toa; return rtklib_alm; } src/algorithms/libs/rtklib/rtklib_conversions.h000066400000000000000000000043151352176506000223360ustar00rootroot00000000000000/*! * \file rtklib_conversions.h * \brief GNSS-SDR to RTKLIB data structures conversion functions * \author 2017, Javier Arribas * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RTKLIB_CONVERSIONS_H_ #define GNSS_SDR_RTKLIB_CONVERSIONS_H_ #include "rtklib.h" class Beidou_Dnav_Ephemeris; class Galileo_Almanac; class Galileo_Ephemeris; class Glonass_Gnav_Ephemeris; class Glonass_Gnav_Utc_Model; class Gnss_Synchro; class Gps_Almanac; class Gps_CNAV_Ephemeris; class Gps_Ephemeris; eph_t eph_to_rtklib(const Galileo_Ephemeris& gal_eph); eph_t eph_to_rtklib(const Gps_Ephemeris& gps_eph); eph_t eph_to_rtklib(const Gps_CNAV_Ephemeris& gps_cnav_eph); eph_t eph_to_rtklib(const Beidou_Dnav_Ephemeris& bei_eph); alm_t alm_to_rtklib(const Gps_Almanac& gps_alm); alm_t alm_to_rtklib(const Galileo_Almanac& gal_alm); /*! * \brief Transforms a Glonass_Gnav_Ephemeris to its RTKLIB counterpart * \param glonass_gnav_eph GLONASS GNAV Ephemeris structure * \return Ephemeris structure for RTKLIB parsing */ geph_t eph_to_rtklib(const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const Glonass_Gnav_Utc_Model& gnav_clock_model); obsd_t insert_obs_to_rtklib(obsd_t& rtklib_obs, const Gnss_Synchro& gnss_synchro, int week, int band); #endif /* GNSS_SDR_RTKLIB_CONVERSIONS_H_ */ src/algorithms/libs/rtklib/rtklib_ephemeris.cc000066400000000000000000001042471352176506000221120ustar00rootroot00000000000000/*! * \file rtklib_ephemeris.cc * \brief satellite ephemeris and clock functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_ephemeris.h" #include "rtklib_preceph.h" #include "rtklib_rtkcmn.h" #include "rtklib_sbas.h" /* constants ------------------------------------------------------*/ const double RE_GLO = 6378136.0; /* radius of earth (m) ref [2] */ const double MU_GPS = 3.9860050e14; /* gravitational constant ref [1] */ const double MU_GLO = 3.9860044e14; /* gravitational constant ref [2] */ const double MU_GAL = 3.986004418e14; /* earth gravitational constant ref [7] */ const double MU_BDS = 3.986004418e14; /* earth gravitational constant ref [9] */ const double J2_GLO = 1.0826257e-3; /* 2nd zonal harmonic of geopot ref [2] */ const double OMGE_GLO = 7.292115e-5; /* earth angular velocity (rad/s) ref [2] */ const double OMGE_GAL = 7.2921151467e-5; /* earth angular velocity (rad/s) ref [7] */ const double OMGE_BDS = 7.292115e-5; /* earth angular velocity (rad/s) ref [9] */ const double SIN_5 = -0.0871557427476582; /* sin(-5.0 deg) */ const double COS_5 = 0.9961946980917456; /* cos(-5.0 deg) */ const double ERREPH_GLO = 5.0; /* error of glonass ephemeris (m) */ const double TSTEP = 60.0; /* integration step glonass ephemeris (s) */ const double RTOL_KEPLER = 1e-13; /* relative tolerance for Kepler equation */ const double DEFURASSR = 0.15; /* default accuracy of ssr corr (m) */ const double MAXECORSSR = 10.0; /* max orbit correction of ssr (m) */ const double MAXCCORSSR = 1e-6 * SPEED_OF_LIGHT; /* max clock correction of ssr (m) */ const double MAXAGESSR = 90.0; /* max age of ssr orbit and clock (s) */ const double MAXAGESSR_HRCLK = 10.0; /* max age of ssr high-rate clock (s) */ const double STD_BRDCCLK = 30.0; /* error of broadcast clock (m) */ const int MAX_ITER_KEPLER = 30; /* max number of iteration of Kelpler */ /* variance by ura ephemeris (ref [1] 20.3.3.3.1.1) --------------------------*/ double var_uraeph(int ura) { const double ura_value[] = { 2.4, 3.4, 4.85, 6.85, 9.65, 13.65, 24.0, 48.0, 96.0, 192.0, 384.0, 768.0, 1536.0, 3072.0, 6144.0}; return ura < 0 || 14 < ura ? std::pow(6144.0, 2.0) : std::pow(ura_value[ura], 2.0); } /* variance by ura ssr (ref [4]) ---------------------------------------------*/ double var_urassr(int ura) { double std_; if (ura <= 0) { return std::pow(DEFURASSR, 2.0); } if (ura >= 63) { return std::pow(5.4665, 2.0); } std_ = (std::pow((ura >> 3) & 7, 2.0) * (1.0 + (ura & 7) / 4.0) - 1.0) * 1e-3; return std::pow(std_, 2.0); } /* almanac to satellite position and clock bias -------------------------------- * compute satellite position and clock bias with almanac (gps, galileo, qzss) * args : gtime_t time I time (gpst) * alm_t *alm I almanac * double *rs O satellite position (ecef) {x,y,z} (m) * double *dts O satellite clock bias (s) * return : none * notes : see ref [1],[7],[8] *-----------------------------------------------------------------------------*/ void alm2pos(gtime_t time, const alm_t *alm, double *rs, double *dts) { double tk, M, E, Ek, sinE, cosE, u, r, i, O, x, y, sinO, cosO, cosi, mu; int n; trace(4, "alm2pos : time=%s sat=%2d\n", time_str(time, 3), alm->sat); tk = timediffweekcrossover(time, alm->toa); if (alm->A <= 0.0) { rs[0] = rs[1] = rs[2] = *dts = 0.0; return; } mu = satsys(alm->sat, nullptr) == SYS_GAL ? MU_GAL : MU_GPS; M = alm->M0 + sqrt(mu / (alm->A * alm->A * alm->A)) * tk; for (n = 0, E = M, Ek = 0.0; fabs(E - Ek) > RTOL_KEPLER && n < MAX_ITER_KEPLER; n++) { Ek = E; E -= (E - alm->e * sin(E) - M) / (1.0 - alm->e * cos(E)); } if (n >= MAX_ITER_KEPLER) { trace(2, "alm2pos: kepler iteration overflow sat=%2d\n", alm->sat); return; } sinE = sin(E); cosE = cos(E); u = atan2(sqrt(1.0 - alm->e * alm->e) * sinE, cosE - alm->e) + alm->omg; r = alm->A * (1.0 - alm->e * cosE); i = alm->i0; O = alm->OMG0 + (alm->OMGd - DEFAULT_OMEGA_EARTH_DOT) * tk - DEFAULT_OMEGA_EARTH_DOT * alm->toas; x = r * cos(u); y = r * sin(u); sinO = sin(O); cosO = cos(O); cosi = cos(i); rs[0] = x * cosO - y * cosi * sinO; rs[1] = x * sinO + y * cosi * cosO; rs[2] = y * sin(i); *dts = alm->f0 + alm->f1 * tk; } /* broadcast ephemeris to satellite clock bias --------------------------------- * compute satellite clock bias with broadcast ephemeris (gps, galileo, qzss) * args : gtime_t time I time by satellite clock (gpst) * eph_t *eph I broadcast ephemeris * return : satellite clock bias (s) without relativeity correction * notes : see ref [1],[7],[8] * satellite clock does not include relativity correction and tdg *-----------------------------------------------------------------------------*/ double eph2clk(gtime_t time, const eph_t *eph) { double t; int i; trace(4, "eph2clk : time=%s sat=%2d\n", time_str(time, 3), eph->sat); t = timediffweekcrossover(time, eph->toc); for (i = 0; i < 2; i++) { t -= eph->f0 + eph->f1 * t + eph->f2 * t * t; } return eph->f0 + eph->f1 * t + eph->f2 * t * t; } /* broadcast ephemeris to satellite position and clock bias -------------------- * compute satellite position and clock bias with broadcast ephemeris (gps, * galileo, qzss) * args : gtime_t time I time (gpst) * eph_t *eph I broadcast ephemeris * double *rs O satellite position (ecef) {x,y,z} (m) * double *dts O satellite clock bias (s) * double *var O satellite position and clock variance (m^2) * return : none * notes : see ref [1],[7],[8] * satellite clock includes relativity correction without code bias * (tgd or bgd) *-----------------------------------------------------------------------------*/ void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts, double *var) { double tk, M, E, Ek, sinE, cosE, u, r, i, O, sin2u, cos2u, x, y, sinO, cosO, cosi, mu, omge; double xg, yg, zg, sino, coso; int n, sys, prn; trace(4, "eph2pos : time=%s sat=%2d\n", time_str(time, 3), eph->sat); if (eph->A <= 0.0) { rs[0] = rs[1] = rs[2] = *dts = *var = 0.0; return; } tk = timediffweekcrossover(time, eph->toe); switch ((sys = satsys(eph->sat, &prn))) { case SYS_GAL: mu = MU_GAL; omge = OMGE_GAL; break; case SYS_BDS: mu = MU_BDS; omge = OMGE_BDS; break; default: mu = MU_GPS; omge = DEFAULT_OMEGA_EARTH_DOT; break; } M = eph->M0 + (sqrt(mu / (eph->A * eph->A * eph->A)) + eph->deln) * tk; for (n = 0, E = M, Ek = 0.0; fabs(E - Ek) > RTOL_KEPLER && n < MAX_ITER_KEPLER; n++) { Ek = E; E -= (E - eph->e * sin(E) - M) / (1.0 - eph->e * cos(E)); } if (n >= MAX_ITER_KEPLER) { trace(2, "eph2pos: kepler iteration overflow sat=%2d\n", eph->sat); return; } sinE = sin(E); cosE = cos(E); trace(4, "kepler: sat=%2d e=%8.5f n=%2d del=%10.3e\n", eph->sat, eph->e, n, E - Ek); u = atan2(sqrt(1.0 - eph->e * eph->e) * sinE, cosE - eph->e) + eph->omg; r = eph->A * (1.0 - eph->e * cosE); i = eph->i0 + eph->idot * tk; sin2u = sin(2.0 * u); cos2u = cos(2.0 * u); u += eph->cus * sin2u + eph->cuc * cos2u; r += eph->crs * sin2u + eph->crc * cos2u; i += eph->cis * sin2u + eph->cic * cos2u; x = r * cos(u); y = r * sin(u); cosi = cos(i); /* beidou geo satellite (ref [9]) */ if (sys == SYS_BDS && prn <= 5) { O = eph->OMG0 + eph->OMGd * tk - omge * eph->toes; sinO = sin(O); cosO = cos(O); xg = x * cosO - y * cosi * sinO; yg = x * sinO + y * cosi * cosO; zg = y * sin(i); sino = sin(omge * tk); coso = cos(omge * tk); rs[0] = xg * coso + yg * sino * COS_5 + zg * sino * SIN_5; rs[1] = -xg * sino + yg * coso * COS_5 + zg * coso * SIN_5; rs[2] = -yg * SIN_5 + zg * COS_5; } else { O = eph->OMG0 + (eph->OMGd - omge) * tk - omge * eph->toes; sinO = sin(O); cosO = cos(O); rs[0] = x * cosO - y * cosi * sinO; rs[1] = x * sinO + y * cosi * cosO; rs[2] = y * sin(i); } tk = timediffweekcrossover(time, eph->toc); *dts = eph->f0 + eph->f1 * tk + eph->f2 * tk * tk; /* relativity correction */ *dts -= 2.0 * sqrt(mu * eph->A) * eph->e * sinE / std::pow(SPEED_OF_LIGHT, 2.0); /* position and clock error variance */ *var = var_uraeph(eph->sva); } /* glonass orbit differential equations --------------------------------------*/ void deq(const double *x, double *xdot, const double *acc) { double a, b, c, r2 = dot(x, x, 3), r3 = r2 * sqrt(r2), omg2 = std::pow(OMGE_GLO, 2.0); if (r2 <= 0.0) { xdot[0] = xdot[1] = xdot[2] = xdot[3] = xdot[4] = xdot[5] = 0.0; return; } /* ref [2] A.3.1.2 with bug fix for xdot[4],xdot[5] */ a = 1.5 * J2_GLO * MU_GLO * std::pow(RE_GLO, 2.0) / r2 / r3; /* 3/2*J2*mu*Ae^2/r^5 */ b = 5.0 * x[2] * x[2] / r2; /* 5*z^2/r^2 */ c = -MU_GLO / r3 - a * (1.0 - b); /* -mu/r^3-a(1-b) */ xdot[0] = x[3]; xdot[1] = x[4]; xdot[2] = x[5]; xdot[3] = (c + omg2) * x[0] + 2.0 * OMGE_GLO * x[4] + acc[0]; xdot[4] = (c + omg2) * x[1] - 2.0 * OMGE_GLO * x[3] + acc[1]; xdot[5] = (c - 2.0 * a) * x[2] + acc[2]; } /* glonass position and velocity by numerical integration --------------------*/ void glorbit(double t, double *x, const double *acc) { double k1[6], k2[6], k3[6], k4[6], w[6]; int i; deq(x, k1, acc); for (i = 0; i < 6; i++) { w[i] = x[i] + k1[i] * t / 2.0; } deq(w, k2, acc); for (i = 0; i < 6; i++) { w[i] = x[i] + k2[i] * t / 2.0; } deq(w, k3, acc); for (i = 0; i < 6; i++) { w[i] = x[i] + k3[i] * t; } deq(w, k4, acc); for (i = 0; i < 6; i++) { x[i] += (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) * t / 6.0; } } /* glonass ephemeris to satellite clock bias ----------------------------------- * compute satellite clock bias with glonass ephemeris * args : gtime_t time I time by satellite clock (gpst) * geph_t *geph I glonass ephemeris * return : satellite clock bias (s) * notes : see ref [2] *-----------------------------------------------------------------------------*/ double geph2clk(gtime_t time, const geph_t *geph) { double t; int i; trace(4, "geph2clk: time=%s sat=%2d\n", time_str(time, 3), geph->sat); t = timediff(time, geph->toe); for (i = 0; i < 2; i++) { t -= -geph->taun + geph->gamn * t; } return -geph->taun + geph->gamn * t; } /* glonass ephemeris to satellite position and clock bias ---------------------- * compute satellite position and clock bias with glonass ephemeris * args : gtime_t time I time (gpst) * geph_t *geph I glonass ephemeris * double *rs O satellite position {x,y,z} (ecef) (m) * double *dts O satellite clock bias (s) * double *var O satellite position and clock variance (m^2) * return : none * notes : see ref [2] *-----------------------------------------------------------------------------*/ void geph2pos(gtime_t time, const geph_t *geph, double *rs, double *dts, double *var) { double t, tt, x[6]; int i; trace(4, "geph2pos: time=%s sat=%2d\n", time_str(time, 3), geph->sat); t = timediff(time, geph->toe); *dts = -geph->taun + geph->gamn * t; for (i = 0; i < 3; i++) { x[i] = geph->pos[i]; x[i + 3] = geph->vel[i]; } for (tt = t < 0.0 ? -TSTEP : TSTEP; fabs(t) > 1e-9; t -= tt) { if (fabs(t) < TSTEP) { tt = t; } glorbit(tt, x, geph->acc); } for (i = 0; i < 3; i++) { rs[i] = x[i]; } *var = std::pow(ERREPH_GLO, 2.0); } /* sbas ephemeris to satellite clock bias -------------------------------------- * compute satellite clock bias with sbas ephemeris * args : gtime_t time I time by satellite clock (gpst) * seph_t *seph I sbas ephemeris * return : satellite clock bias (s) * notes : see ref [3] *-----------------------------------------------------------------------------*/ double seph2clk(gtime_t time, const seph_t *seph) { double t; int i; trace(4, "seph2clk: time=%s sat=%2d\n", time_str(time, 3), seph->sat); t = timediffweekcrossover(time, seph->t0); for (i = 0; i < 2; i++) { t -= seph->af0 + seph->af1 * t; } return seph->af0 + seph->af1 * t; } /* sbas ephemeris to satellite position and clock bias ------------------------- * compute satellite position and clock bias with sbas ephemeris * args : gtime_t time I time (gpst) * seph_t *seph I sbas ephemeris * double *rs O satellite position {x,y,z} (ecef) (m) * double *dts O satellite clock bias (s) * double *var O satellite position and clock variance (m^2) * return : none * notes : see ref [3] *-----------------------------------------------------------------------------*/ void seph2pos(gtime_t time, const seph_t *seph, double *rs, double *dts, double *var) { double t; int i; trace(4, "seph2pos: time=%s sat=%2d\n", time_str(time, 3), seph->sat); t = timediffweekcrossover(time, seph->t0); for (i = 0; i < 3; i++) { rs[i] = seph->pos[i] + seph->vel[i] * t + seph->acc[i] * t * t / 2.0; } *dts = seph->af0 + seph->af1 * t; *var = var_uraeph(seph->sva); } /* select ephemeris --------------------------------------------------------*/ eph_t *seleph(gtime_t time, int sat, int iode, const nav_t *nav) { double t, tmax, tmin; int i, j = -1; trace(4, "seleph : time=%s sat=%2d iode=%d\n", time_str(time, 3), sat, iode); switch (satsys(sat, nullptr)) { case SYS_QZS: tmax = MAXDTOE_QZS + 1.0; break; case SYS_GAL: tmax = MAXDTOE_GAL + 1.0; break; case SYS_BDS: tmax = MAXDTOE_BDS + 1.0; break; default: tmax = MAXDTOE + 1.0; break; } tmin = tmax + 1.0; for (i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } if (iode >= 0 && nav->eph[i].iode != iode) { continue; } if ((t = fabs(timediffweekcrossover(nav->eph[i].toe, time))) > tmax) { continue; } if (iode >= 0) { return nav->eph + i; } if (t <= tmin) { j = i; tmin = t; } /* toe closest to time */ } if (iode >= 0 || j < 0) { trace(3, "no broadcast ephemeris: %s sat=%2d iode=%3d\n", time_str(time, 0), sat, iode); return nullptr; } return nav->eph + j; } /* select glonass ephemeris ------------------------------------------------*/ geph_t *selgeph(gtime_t time, int sat, int iode, const nav_t *nav) { double t, tmax = MAXDTOE_GLO, tmin = tmax + 1.0; int i, j = -1; trace(4, "selgeph : time=%s sat=%2d iode=%2d\n", time_str(time, 3), sat, iode); for (i = 0; i < nav->ng; i++) { if (nav->geph[i].sat != sat) { continue; } if (iode >= 0 && nav->geph[i].iode != iode) { continue; } if ((t = fabs(timediff(nav->geph[i].toe, time))) > tmax) { continue; } if (iode >= 0) { return nav->geph + i; } if (t <= tmin) { j = i; tmin = t; } /* toe closest to time */ } if (iode >= 0 || j < 0) { trace(3, "no glonass ephemeris : %s sat=%2d iode=%2d\n", time_str(time, 0), sat, iode); return nullptr; } return nav->geph + j; } /* select sbas ephemeris ---------------------------------------------------*/ seph_t *selseph(gtime_t time, int sat, const nav_t *nav) { double t, tmax = MAXDTOE_SBS, tmin = tmax + 1.0; int i, j = -1; trace(4, "selseph : time=%s sat=%2d\n", time_str(time, 3), sat); for (i = 0; i < nav->ns; i++) { if (nav->seph[i].sat != sat) { continue; } if ((t = fabs(timediffweekcrossover(nav->seph[i].t0, time))) > tmax) { continue; } if (t <= tmin) { j = i; tmin = t; } /* toe closest to time */ } if (j < 0) { trace(3, "no sbas ephemeris : %s sat=%2d\n", time_str(time, 0), sat); return nullptr; } return nav->seph + j; } /* satellite clock with broadcast ephemeris ----------------------------------*/ int ephclk(gtime_t time, gtime_t teph, int sat, const nav_t *nav, double *dts) { eph_t *eph; geph_t *geph; seph_t *seph; int sys; trace(4, "ephclk : time=%s sat=%2d\n", time_str(time, 3), sat); sys = satsys(sat, nullptr); if (sys == SYS_GPS || sys == SYS_GAL || sys == SYS_QZS || sys == SYS_BDS) { if (!(eph = seleph(teph, sat, -1, nav))) { return 0; } *dts = eph2clk(time, eph); } else if (sys == SYS_GLO) { if (!(geph = selgeph(teph, sat, -1, nav))) { return 0; } *dts = geph2clk(time, geph); } else if (sys == SYS_SBS) { if (!(seph = selseph(teph, sat, nav))) { return 0; } *dts = seph2clk(time, seph); } else { return 0; } return 1; } /* satellite position and clock by broadcast ephemeris -----------------------*/ int ephpos(gtime_t time, gtime_t teph, int sat, const nav_t *nav, int iode, double *rs, double *dts, double *var, int *svh) { eph_t *eph; geph_t *geph; seph_t *seph; double rst[3], dtst[1], tt = 1e-3; int i, sys; trace(4, "ephpos : time=%s sat=%2d iode=%d\n", time_str(time, 3), sat, iode); sys = satsys(sat, nullptr); *svh = -1; if (sys == SYS_GPS || sys == SYS_GAL || sys == SYS_QZS || sys == SYS_BDS) { if (!(eph = seleph(teph, sat, iode, nav))) { return 0; } eph2pos(time, eph, rs, dts, var); time = timeadd(time, tt); eph2pos(time, eph, rst, dtst, var); *svh = eph->svh; } else if (sys == SYS_GLO) { if (!(geph = selgeph(teph, sat, iode, nav))) { return 0; } geph2pos(time, geph, rs, dts, var); time = timeadd(time, tt); geph2pos(time, geph, rst, dtst, var); *svh = geph->svh; } else if (sys == SYS_SBS) { if (!(seph = selseph(teph, sat, nav))) { return 0; } seph2pos(time, seph, rs, dts, var); time = timeadd(time, tt); seph2pos(time, seph, rst, dtst, var); *svh = seph->svh; } else { return 0; } /* satellite velocity and clock drift by differential approx */ for (i = 0; i < 3; i++) { rs[i + 3] = (rst[i] - rs[i]) / tt; } dts[1] = (dtst[0] - dts[0]) / tt; return 1; } /* satellite position and clock with sbas correction -------------------------*/ int satpos_sbas(gtime_t time, gtime_t teph, int sat, const nav_t *nav, double *rs, double *dts, double *var, int *svh) { const sbssatp_t *sbs; int i; trace(4, "satpos_sbas: time=%s sat=%2d\n", time_str(time, 3), sat); /* search sbas satellite correciton */ for (i = 0; i < nav->sbssat.nsat; i++) { sbs = nav->sbssat.sat + i; if (sbs->sat == sat) { break; } } if (i >= nav->sbssat.nsat) { trace(2, "no sbas correction for orbit: %s sat=%2d\n", time_str(time, 0), sat); ephpos(time, teph, sat, nav, -1, rs, dts, var, svh); *svh = -1; return 0; } /* satellite position and clock by broadcast ephemeris */ if (!ephpos(time, teph, sat, nav, sbs->lcorr.iode, rs, dts, var, svh)) { return 0; } /* sbas satellite correction (long term and fast) */ if (sbssatcorr(time, sat, nav, rs, dts, var)) { return 1; } *svh = -1; return 0; } /* satellite position and clock with ssr correction --------------------------*/ int satpos_ssr(gtime_t time, gtime_t teph, int sat, const nav_t *nav, int opt, double *rs, double *dts, double *var, int *svh) { const ssr_t *ssr; eph_t *eph; double t1, t2, t3, er[3], ea[3], ec[3], rc[3], deph[3], dclk, dant[3] = {0}, tk; int i, sys; trace(4, "satpos_ssr: time=%s sat=%2d\n", time_str(time, 3), sat); ssr = nav->ssr + sat - 1; if (!ssr->t0[0].time) { trace(2, "no ssr orbit correction: %s sat=%2d\n", time_str(time, 0), sat); return 0; } if (!ssr->t0[1].time) { trace(2, "no ssr clock correction: %s sat=%2d\n", time_str(time, 0), sat); return 0; } /* inconsistency between orbit and clock correction */ if (ssr->iod[0] != ssr->iod[1]) { trace(2, "inconsist ssr correction: %s sat=%2d iod=%d %d\n", time_str(time, 0), sat, ssr->iod[0], ssr->iod[1]); *svh = -1; return 0; } t1 = timediffweekcrossover(time, ssr->t0[0]); t2 = timediffweekcrossover(time, ssr->t0[1]); t3 = timediffweekcrossover(time, ssr->t0[2]); /* ssr orbit and clock correction (ref [4]) */ if (fabs(t1) > MAXAGESSR || fabs(t2) > MAXAGESSR) { trace(2, "age of ssr error: %s sat=%2d t=%.0f %.0f\n", time_str(time, 0), sat, t1, t2); *svh = -1; return 0; } if (ssr->udi[0] >= 1.0) { t1 -= ssr->udi[0] / 2.0; } if (ssr->udi[1] >= 1.0) { t2 -= ssr->udi[0] / 2.0; } for (i = 0; i < 3; i++) { deph[i] = ssr->deph[i] + ssr->ddeph[i] * t1; } dclk = ssr->dclk[0] + ssr->dclk[1] * t2 + ssr->dclk[2] * t2 * t2; /* ssr highrate clock correction (ref [4]) */ if (ssr->iod[0] == ssr->iod[2] && ssr->t0[2].time && fabs(t3) < MAXAGESSR_HRCLK) { dclk += ssr->hrclk; } if (norm_rtk(deph, 3) > MAXECORSSR || fabs(dclk) > MAXCCORSSR) { trace(3, "invalid ssr correction: %s deph=%.1f dclk=%.1f\n", time_str(time, 0), norm_rtk(deph, 3), dclk); *svh = -1; return 0; } /* satellite position and clock by broadcast ephemeris */ if (!ephpos(time, teph, sat, nav, ssr->iode, rs, dts, var, svh)) { return 0; } /* satellite clock for gps, galileo and qzss */ sys = satsys(sat, nullptr); if (sys == SYS_GPS || sys == SYS_GAL || sys == SYS_QZS || sys == SYS_BDS) { if (!(eph = seleph(teph, sat, ssr->iode, nav))) { return 0; } /* satellite clock by clock parameters */ tk = timediffweekcrossover(time, eph->toc); dts[0] = eph->f0 + eph->f1 * tk + eph->f2 * tk * tk; dts[1] = eph->f1 + 2.0 * eph->f2 * tk; /* relativity correction */ dts[0] -= 2.0 * dot(rs, rs + 3, 3) / SPEED_OF_LIGHT / SPEED_OF_LIGHT; } /* radial-along-cross directions in ecef */ if (!normv3(rs + 3, ea)) { return 0; } cross3(rs, rs + 3, rc); if (!normv3(rc, ec)) { *svh = -1; return 0; } cross3(ea, ec, er); /* satellite antenna offset correction */ if (opt) { satantoff(time, rs, sat, nav, dant); } for (i = 0; i < 3; i++) { rs[i] += -(er[i] * deph[0] + ea[i] * deph[1] + ec[i] * deph[2]) + dant[i]; } /* t_corr = t_sv - (dts(brdc) + dclk(ssr) / SPEED_OF_LIGHT) (ref [10] eq.3.12-7) */ dts[0] += dclk / SPEED_OF_LIGHT; /* variance by ssr ura */ *var = var_urassr(ssr->ura); trace(5, "satpos_ssr: %s sat=%2d deph=%6.3f %6.3f %6.3f er=%6.3f %6.3f %6.3f dclk=%6.3f var=%6.3f\n", time_str(time, 2), sat, deph[0], deph[1], deph[2], er[0], er[1], er[2], dclk, *var); return 1; } /* satellite position and clock ------------------------------------------------ * compute satellite position, velocity and clock * args : gtime_t time I time (gpst) * gtime_t teph I time to select ephemeris (gpst) * int sat I satellite number * nav_t *nav I navigation data * int ephopt I ephemeris option (EPHOPT_???) * double *rs O sat position and velocity (ecef) * {x,y,z,vx,vy,vz} (m|m/s) * double *dts O sat clock {bias,drift} (s|s/s) * double *var O sat position and clock error variance (m^2) * int *svh O sat health flag (-1:correction not available) * return : status (1:ok,0:error) * notes : satellite position is referenced to antenna phase center * satellite clock does not include code bias correction (tgd or bgd) *-----------------------------------------------------------------------------*/ int satpos(gtime_t time, gtime_t teph, int sat, int ephopt, const nav_t *nav, double *rs, double *dts, double *var, int *svh) { trace(4, "satpos : time=%s sat=%2d ephopt=%d\n", time_str(time, 3), sat, ephopt); *svh = 0; switch (ephopt) { case EPHOPT_BRDC: return ephpos(time, teph, sat, nav, -1, rs, dts, var, svh); case EPHOPT_SBAS: return satpos_sbas(time, teph, sat, nav, rs, dts, var, svh); case EPHOPT_SSRAPC: return satpos_ssr(time, teph, sat, nav, 0, rs, dts, var, svh); case EPHOPT_SSRCOM: return satpos_ssr(time, teph, sat, nav, 1, rs, dts, var, svh); case EPHOPT_PREC: if (!peph2pos(time, sat, nav, 1, rs, dts, var)) { break; } else { return 1; } //TODO: enable lex //case EPHOPT_LEX : // if (!lexeph2pos(time, sat, nav, rs, dts, var)) break; else return 1; } *svh = -1; return 0; } /* satellite positions and clocks ---------------------------------------------- * compute satellite positions, velocities and clocks * args : gtime_t teph I time to select ephemeris (gpst) * obsd_t *obs I observation data * int n I number of observation data * nav_t *nav I navigation data * int ephopt I ephemeris option (EPHOPT_???) * double *rs O satellite positions and velocities (ecef) * double *dts O satellite clocks * double *var O sat position and clock error variances (m^2) * int *svh O sat health flag (-1:correction not available) * return : none * notes : rs [(0:2)+i*6]= obs[i] sat position {x,y,z} (m) * rs [(3:5)+i*6]= obs[i] sat velocity {vx,vy,vz} (m/s) * dts[(0:1)+i*2]= obs[i] sat clock {bias,drift} (s|s/s) * var[i] = obs[i] sat position and clock error variance (m^2) * svh[i] = obs[i] sat health flag * if no navigation data, set 0 to rs[], dts[], var[] and svh[] * satellite position and clock are values at signal transmission time * satellite position is referenced to antenna phase center * satellite clock does not include code bias correction (tgd or bgd) * any pseudorange and broadcast ephemeris are always needed to get * signal transmission time *-----------------------------------------------------------------------------*/ void satposs(gtime_t teph, const obsd_t *obs, int n, const nav_t *nav, int ephopt, double *rs, double *dts, double *var, int *svh) { gtime_t time[MAXOBS] = {}; double dt, pr; int i, j; trace(3, "satposs : teph=%s n=%d ephopt=%d\n", time_str(teph, 3), n, ephopt); for (i = 0; i < n && i < MAXOBS; i++) { for (j = 0; j < 6; j++) { rs[j + i * 6] = 0.0; } for (j = 0; j < 2; j++) { dts[j + i * 2] = 0.0; } var[i] = 0.0; svh[i] = 0; /* search any pseudorange */ for (j = 0, pr = 0.0; j < NFREQ; j++) { if ((pr = obs[i].P[j]) != 0.0) { break; } } if (j >= NFREQ) { trace(2, "no pseudorange %s sat=%2d\n", time_str(obs[i].time, 3), obs[i].sat); continue; } /* transmission time by satellite clock */ time[i] = timeadd(obs[i].time, -pr / SPEED_OF_LIGHT); /* satellite clock bias by broadcast ephemeris */ if (!ephclk(time[i], teph, obs[i].sat, nav, &dt)) { trace(3, "no broadcast clock %s sat=%2d\n", time_str(time[i], 3), obs[i].sat); continue; } time[i] = timeadd(time[i], -dt); /* satellite position and clock at transmission time */ if (!satpos(time[i], teph, obs[i].sat, ephopt, nav, rs + i * 6, dts + i * 2, var + i, svh + i)) { trace(3, "no ephemeris %s sat=%2d\n", time_str(time[i], 3), obs[i].sat); continue; } /* if no precise clock available, use broadcast clock instead */ if (dts[i * 2] == 0.0) { if (!ephclk(time[i], teph, obs[i].sat, nav, dts + i * 2)) { continue; } dts[1 + i * 2] = 0.0; *var = std::pow(STD_BRDCCLK, 2.0); } } for (i = 0; i < n && i < MAXOBS; i++) { trace(4, "%s sat=%2d rs=%13.3f %13.3f %13.3f dts=%12.3f var=%7.3f svh=%02X\n", time_str(time[i], 6), obs[i].sat, rs[i * 6], rs[1 + i * 6], rs[2 + i * 6], dts[i * 2] * 1e9, var[i], svh[i]); } } src/algorithms/libs/rtklib/rtklib_ephemeris.h000066400000000000000000000102211352176506000217400ustar00rootroot00000000000000/*! * \file rtklib_ephemeris.h * \brief satellite ephemeris and clock functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_EPHEMERIS_H_ #define GNSS_SDR_RTKLIB_EPHEMERIS_H_ #include "rtklib.h" double var_uraeph(int ura); double var_urassr(int ura); void alm2pos(gtime_t time, const alm_t *alm, double *rs, double *dts); double eph2clk(gtime_t time, const eph_t *eph); void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts, double *var); void deq(const double *x, double *xdot, const double *acc); void glorbit(double t, double *x, const double *acc); double geph2clk(gtime_t time, const geph_t *geph); void geph2pos(gtime_t time, const geph_t *geph, double *rs, double *dts, double *var); double seph2clk(gtime_t time, const seph_t *seph); void seph2pos(gtime_t time, const seph_t *seph, double *rs, double *dts, double *var); eph_t *seleph(gtime_t time, int sat, int iode, const nav_t *nav); geph_t *selgeph(gtime_t time, int sat, int iode, const nav_t *nav); seph_t *selseph(gtime_t time, int sat, const nav_t *nav); int ephclk(gtime_t time, gtime_t teph, int sat, const nav_t *nav, double *dts); //satellite position and clock by broadcast ephemeris int ephpos(gtime_t time, gtime_t teph, int sat, const nav_t *nav, int iode, double *rs, double *dts, double *var, int *svh); int satpos_sbas(gtime_t time, gtime_t teph, int sat, const nav_t *nav, double *rs, double *dts, double *var, int *svh); int satpos_ssr(gtime_t time, gtime_t teph, int sat, const nav_t *nav, int opt, double *rs, double *dts, double *var, int *svh); int satpos(gtime_t time, gtime_t teph, int sat, int ephopt, const nav_t *nav, double *rs, double *dts, double *var, int *svh); void satposs(gtime_t teph, const obsd_t *obs, int n, const nav_t *nav, int ephopt, double *rs, double *dts, double *var, int *svh); #endif /* GNSS_SDR_RTKLIB_EPHEMERIS_H_ */ src/algorithms/libs/rtklib/rtklib_ionex.cc000066400000000000000000000554501352176506000212540ustar00rootroot00000000000000/*! * \file rtklib_ionex.cc * \brief ionex functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * References: * [1] S.Schear, W.Gurtner and J.Feltens, IONEX: The IONosphere Map EXchange * Format Version 1, February 25, 1998 * [2] S.Schaer, R.Markus, B.Gerhard and A.S.Timon, Daily Global Ionosphere * Maps based on GPS Carrier Phase Data Routinely produced by CODE * Analysis Center, Proceeding of the IGS Analysis Center Workshop, 1996 * *----------------------------------------------------------------------------*/ #include "rtklib_ionex.h" #include "rtklib_rtkcmn.h" #include /* get index -----------------------------------------------------------------*/ int getindex(double value, const double *range) { if (range[2] == 0.0) { return 0; } if (range[1] > 0.0 && (value < range[0] || range[1] < value)) { return -1; } if (range[1] < 0.0 && (value < range[1] || range[0] < value)) { return -1; } return static_cast(floor((value - range[0]) / range[2] + 0.5)); } /* get number of items -------------------------------------------------------*/ int nitem(const double *range) { return getindex(range[1], range) + 1; } /* data index (i:lat,j:lon,k:hgt) --------------------------------------------*/ int dataindex(int i, int j, int k, const int *ndata) { if (i < 0 || ndata[0] <= i || j < 0 || ndata[1] <= j || k < 0 || ndata[2] <= k) { return -1; } return i + ndata[0] * (j + ndata[1] * k); } /* add tec data to navigation data -------------------------------------------*/ tec_t *addtec(const double *lats, const double *lons, const double *hgts, double rb, nav_t *nav) { tec_t *p, *nav_tec; gtime_t time0 = {0, 0}; int i, n, ndata[3]; trace(3, "addtec :\n"); ndata[0] = nitem(lats); ndata[1] = nitem(lons); ndata[2] = nitem(hgts); if (ndata[0] <= 1 || ndata[1] <= 1 || ndata[2] <= 0) { return nullptr; } if (nav->nt >= nav->ntmax) { nav->ntmax += 256; if (!(nav_tec = static_cast(realloc(nav->tec, sizeof(tec_t) * nav->ntmax)))) { trace(1, "readionex malloc error ntmax=%d\n", nav->ntmax); free(nav->tec); nav->tec = nullptr; nav->nt = nav->ntmax = 0; return nullptr; } nav->tec = nav_tec; } p = nav->tec + nav->nt; p->time = time0; p->rb = rb; for (i = 0; i < 3; i++) { p->ndata[i] = ndata[i]; p->lats[i] = lats[i]; p->lons[i] = lons[i]; p->hgts[i] = hgts[i]; } n = ndata[0] * ndata[1] * ndata[2]; if (!(p->data = static_cast(malloc(sizeof(double) * n))) || !(p->rms = static_cast(malloc(sizeof(float) * n)))) { return nullptr; } for (i = 0; i < n; i++) { p->data[i] = 0.0; p->rms[i] = 0.0F; } nav->nt++; return p; } /* read ionex dcb aux data ----------------------------------------------------*/ void readionexdcb(FILE *fp, double *dcb, double *rms) { int i, sat; char buff[1024], id[32], *label; trace(3, "readionexdcb:\n"); for (i = 0; i < MAXSAT; i++) { dcb[i] = rms[i] = 0.0; } while (fgets(buff, sizeof(buff), fp)) { if (strlen(buff) < 60) { continue; } label = buff + 60; if (strstr(label, "PRN / BIAS / RMS") == label) { strncpy(id, buff + 3, 3); id[3] = '\0'; if (!(sat = satid2no(id))) { trace(2, "ionex invalid satellite: %s\n", id); continue; } dcb[sat - 1] = str2num(buff, 6, 10); rms[sat - 1] = str2num(buff, 16, 10); } else if (strstr(label, "END OF AUX DATA") == label) { break; } } } /* read ionex header ---------------------------------------------------------*/ double readionexh(FILE *fp, double *lats, double *lons, double *hgts, double *rb, double *nexp, double *dcb, double *rms) { double ver = 0.0; char buff[1024], *label; trace(3, "readionexh:\n"); while (fgets(buff, sizeof(buff), fp)) { if (strlen(buff) < 60) { continue; } label = buff + 60; if (strstr(label, "IONEX VERSION / TYPE") == label) { if (buff[20] == 'I') { ver = str2num(buff, 0, 8); } } else if (strstr(label, "BASE RADIUS") == label) { *rb = str2num(buff, 0, 8); } else if (strstr(label, "HGT1 / HGT2 / DHGT") == label) { hgts[0] = str2num(buff, 2, 6); hgts[1] = str2num(buff, 8, 6); hgts[2] = str2num(buff, 14, 6); } else if (strstr(label, "LAT1 / LAT2 / DLAT") == label) { lats[0] = str2num(buff, 2, 6); lats[1] = str2num(buff, 8, 6); lats[2] = str2num(buff, 14, 6); } else if (strstr(label, "LON1 / LON2 / DLON") == label) { lons[0] = str2num(buff, 2, 6); lons[1] = str2num(buff, 8, 6); lons[2] = str2num(buff, 14, 6); } else if (strstr(label, "EXPONENT") == label) { *nexp = str2num(buff, 0, 6); } else if (strstr(label, "START OF AUX DATA") == label && strstr(buff, "DIFFERENTIAL CODE BIASES")) { readionexdcb(fp, dcb, rms); } else if (strstr(label, "END OF HEADER") == label) { return ver; } } return 0.0; } /* read ionex body -----------------------------------------------------------*/ int readionexb(FILE *fp, const double *lats, const double *lons, const double *hgts, double rb, double nexp, nav_t *nav) { tec_t *p = nullptr; gtime_t time = {0, 0}; double lat, lon[3], hgt, x; int i, j, k, n, m, index, type = 0; char buff[1024], *label = buff + 60; trace(3, "readionexb:\n"); while (fgets(buff, sizeof(buff), fp)) { if (strlen(buff) < 60) { continue; } if (strstr(label, "START OF TEC MAP") == label) { if ((p = addtec(lats, lons, hgts, rb, nav))) { type = 1; } } else if (strstr(label, "END OF TEC MAP") == label) { type = 0; p = nullptr; } else if (strstr(label, "START OF RMS MAP") == label) { type = 2; p = nullptr; } else if (strstr(label, "END OF RMS MAP") == label) { type = 0; p = nullptr; } else if (strstr(label, "EPOCH OF CURRENT MAP") == label) { if (str2time(buff, 0, 36, &time)) { trace(2, "ionex epoch invalid: %-36.36s\n", buff); continue; } if (type == 2) { for (i = nav->nt - 1; i >= 0; i--) { if (fabs(timediff(time, nav->tec[i].time)) >= 1.0) { continue; } p = nav->tec + i; break; } } else if (p) { p->time = time; } } else if (strstr(label, "LAT/LON1/LON2/DLON/H") == label && p) { lat = str2num(buff, 2, 6); lon[0] = str2num(buff, 8, 6); lon[1] = str2num(buff, 14, 6); lon[2] = str2num(buff, 20, 6); hgt = str2num(buff, 26, 6); i = getindex(lat, p->lats); k = getindex(hgt, p->hgts); n = nitem(lon); for (m = 0; m < n; m++) { if (m % 16 == 0 && !fgets(buff, sizeof(buff), fp)) { break; } j = getindex(lon[0] + lon[2] * m, p->lons); if ((index = dataindex(i, j, k, p->ndata)) < 0) { continue; } if ((x = str2num(buff, m % 16 * 5, 5)) == 9999.0) { continue; } if (type == 1) { p->data[index] = x * std::pow(10.0, nexp); } else { p->rms[index] = static_cast(x * std::pow(10.0, nexp)); } } } } return 1; } /* combine tec grid data -----------------------------------------------------*/ void combtec(nav_t *nav) { tec_t tmp; int i, j, n = 0; trace(3, "combtec : nav->nt=%d\n", nav->nt); for (i = 0; i < nav->nt - 1; i++) { for (j = i + 1; j < nav->nt; j++) { if (timediff(nav->tec[j].time, nav->tec[i].time) < 0.0) { tmp = nav->tec[i]; nav->tec[i] = nav->tec[j]; nav->tec[j] = tmp; } } } for (i = 0; i < nav->nt; i++) { if (i > 0 && timediff(nav->tec[i].time, nav->tec[n - 1].time) == 0.0) { free(nav->tec[n - 1].data); free(nav->tec[n - 1].rms); nav->tec[n - 1] = nav->tec[i]; continue; } nav->tec[n++] = nav->tec[i]; } nav->nt = n; trace(4, "combtec : nav->nt=%d\n", nav->nt); } /* read ionex tec grid file ---------------------------------------------------- * read ionex ionospheric tec grid file * args : char *file I ionex tec grid file * (wind-card * is expanded) * nav_t *nav IO navigation data * nav->nt, nav->ntmax and nav->tec are modified * int opt I read option (1: no clear of tec data,0:clear) * return : none * notes : see ref [1] *-----------------------------------------------------------------------------*/ void readtec(const char *file, nav_t *nav, int opt) { FILE *fp; double lats[3] = {0}, lons[3] = {0}, hgts[3] = {0}, rb = 0.0, nexp = -1.0; double dcb[MAXSAT] = {0}, rms[MAXSAT] = {0}; int i, n; char *efiles[MAXEXFILE]; trace(3, "readtec : file=%s\n", file); /* clear of tec grid data option */ if (!opt) { free(nav->tec); nav->tec = nullptr; nav->nt = nav->ntmax = 0; } for (i = 0; i < MAXEXFILE; i++) { if (!(efiles[i] = static_cast(malloc(1024)))) { for (i--; i >= 0; i--) { free(efiles[i]); } return; } } /* expand wild card in file path */ n = expath(file, efiles, MAXEXFILE); for (i = 0; i < n; i++) { if (!(fp = fopen(efiles[i], "re"))) { trace(2, "ionex file open error %s\n", efiles[i]); continue; } /* read ionex header */ if (readionexh(fp, lats, lons, hgts, &rb, &nexp, dcb, rms) <= 0.0) { trace(2, "ionex file format error %s\n", efiles[i]); fclose(fp); continue; } /* read ionex body */ readionexb(fp, lats, lons, hgts, rb, nexp, nav); fclose(fp); } for (i = 0; i < MAXEXFILE; i++) { free(efiles[i]); } /* combine tec grid data */ if (nav->nt > 0) { combtec(nav); } /* P1-P2 dcb */ for (i = 0; i < MAXSAT; i++) { nav->cbias[i][0] = SPEED_OF_LIGHT * dcb[i] * 1e-9; /* ns->m */ } } /* interpolate tec grid data -------------------------------------------------*/ int interptec(const tec_t *tec, int k, const double *posp, double *value, double *rms) { double dlat, dlon, a, b, d[4] = {0}, r[4] = {0}; int i, j, n, index; trace(3, "interptec: k=%d posp=%.2f %.2f\n", k, posp[0] * R2D, posp[1] * R2D); *value = *rms = 0.0; if (tec->lats[2] == 0.0 || tec->lons[2] == 0.0) { return 0; } dlat = posp[0] * R2D - tec->lats[0]; dlon = posp[1] * R2D - tec->lons[0]; if (tec->lons[2] > 0.0) { dlon -= floor(dlon / 360) * 360.0; /* 0 <= dlon<360 */ } else { dlon += floor(-dlon / 360) * 360.0; /* -360lats[2]; b = dlon / tec->lons[2]; i = static_cast(floor(a)); a -= i; j = static_cast(floor(b)); b -= j; /* get gridded tec data */ for (n = 0; n < 4; n++) { if ((index = dataindex(i + (n % 2), j + (n < 2 ? 0 : 1), k, tec->ndata)) < 0) { continue; } d[n] = tec->data[index]; r[n] = tec->rms[index]; } if (d[0] > 0.0 && d[1] > 0.0 && d[2] > 0.0 && d[3] > 0.0) { /* bilinear interpolation (inside of grid) */ *value = (1.0 - a) * (1.0 - b) * d[0] + a * (1.0 - b) * d[1] + (1.0 - a) * b * d[2] + a * b * d[3]; *rms = (1.0 - a) * (1.0 - b) * r[0] + a * (1.0 - b) * r[1] + (1.0 - a) * b * r[2] + a * b * r[3]; } /* nearest-neighbour extrapolation (outside of grid) */ else if (a <= 0.5 && b <= 0.5 && d[0] > 0.0) { *value = d[0]; *rms = r[0]; } else if (a > 0.5 && b <= 0.5 && d[1] > 0.0) { *value = d[1]; *rms = r[1]; } else if (a <= 0.5 && b > 0.5 && d[2] > 0.0) { *value = d[2]; *rms = r[2]; } else if (a > 0.5 && b > 0.5 && d[3] > 0.0) { *value = d[3]; *rms = r[3]; } else { i = 0; for (n = 0; n < 4; n++) { if (d[n] > 0.0) { i++; *value += d[n]; *rms += r[n]; } } if (i == 0) { return 0; } *value /= i; *rms /= i; } return 1; } /* ionosphere delay by tec grid data -----------------------------------------*/ int iondelay(gtime_t time, const tec_t *tec, const double *pos, const double *azel, int opt, double *delay, double *var) { const double fact = 40.30E16 / FREQ1 / FREQ1; /* tecu->L1 iono (m) */ double fs, posp[3] = {0}, vtec, rms, hion, rp; int i; trace(3, "iondelay: time=%s pos=%.1f %.1f azel=%.1f %.1f\n", time_str(time, 0), pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); *delay = *var = 0.0; for (i = 0; i < tec->ndata[2]; i++) { /* for a layer */ hion = tec->hgts[0] + tec->hgts[2] * i; /* ionospheric pierce point position */ fs = ionppp(pos, azel, tec->rb, hion, posp); if (opt & 2) { /* modified single layer mapping function (M-SLM) ref [2] */ rp = tec->rb / (tec->rb + hion) * sin(0.9782 * (PI / 2.0 - azel[1])); fs = 1.0 / sqrt(1.0 - rp * rp); } if (opt & 1) { /* earth rotation correction (sun-fixed coordinate) */ posp[1] += 2.0 * PI * timediff(time, tec->time) / 86400.0; } /* interpolate tec grid data */ if (!interptec(tec, i, posp, &vtec, &rms)) { return 0; } *delay += fact * fs * vtec; *var += fact * fact * fs * fs * rms * rms; } trace(4, "iondelay: delay=%7.2f std=%6.2f\n", *delay, sqrt(*var)); return 1; } /* ionosphere model by tec grid data ------------------------------------------- * compute ionospheric delay by tec grid data * args : gtime_t time I time (gpst) * nav_t *nav I navigation data * double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * int opt I model option * bit0: 0:earth-fixed,1:sun-fixed * bit1: 0:single-layer,1:modified single-layer * double *delay O ionospheric delay (L1) (m) * double *var O ionospheric dealy (L1) variance (m^2) * return : status (1:ok,0:error) * notes : before calling the function, read tec grid data by calling readtec() * return ok with delay=0 and var=VAR_NOTEC if elnt; i++) { if (timediff(nav->tec[i].time, time) > 0.0) { break; } } if (i == 0 || i >= nav->nt) { trace(2, "%s: tec grid out of period\n", time_str(time, 0)); return 0; } if ((tt = timediff(nav->tec[i].time, nav->tec[i - 1].time)) == 0.0) { trace(2, "tec grid time interval error\n"); return 0; } /* ionospheric delay by tec grid data */ stat[0] = iondelay(time, nav->tec + i - 1, pos, azel, opt, dels, vars); stat[1] = iondelay(time, nav->tec + i, pos, azel, opt, dels + 1, vars + 1); if (!stat[0] && !stat[1]) { trace(2, "%s: tec grid out of area pos=%6.2f %7.2f azel=%6.1f %5.1f\n", time_str(time, 0), pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); return 0; } if (stat[0] && stat[1]) { /* linear interpolation by time */ a = timediff(time, nav->tec[i - 1].time) / tt; *delay = dels[0] * (1.0 - a) + dels[1] * a; *var = vars[0] * (1.0 - a) + vars[1] * a; } else if (stat[0]) { /* nearest-neighbour extrapolation by time */ *delay = dels[0]; *var = vars[0]; } else { *delay = dels[1]; *var = vars[1]; } trace(3, "iontec : delay=%5.2f std=%5.2f\n", *delay, sqrt(*var)); return 1; } src/algorithms/libs/rtklib/rtklib_ionex.h000066400000000000000000000100011352176506000210750ustar00rootroot00000000000000/*! * \file rtklib_ionex.h * \brief ionex functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * References: * [1] S.Schear, W.Gurtner and J.Feltens, IONEX: The IONosphere Map EXchange * Format Version 1, February 25, 1998 * [2] S.Schaer, R.Markus, B.Gerhard and A.S.Timon, Daily Global Ionosphere * Maps based on GPS Carrier Phase Data Routinely produced by CODE * Analysis Center, Proceeding of the IGS Analysis Center Workshop, 1996 * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_IONEX_H_ #define GNSS_SDR_RTKLIB_IONEX_H_ #include "rtklib.h" const double VAR_NOTEC = 30.0 * 30.0; /* variance of no tec */ const double MIN_EL = 0.0; /* min elevation angle (rad) */ const double MIN_HGT = -1000.0; /* min user height (m) */ int getindex(double value, const double *range); int nitem(const double *range); int dataindex(int i, int j, int k, const int *ndata); tec_t *addtec(const double *lats, const double *lons, const double *hgts, double rb, nav_t *nav); void readionexdcb(FILE *fp, double *dcb, double *rms); double readionexh(FILE *fp, double *lats, double *lons, double *hgts, double *rb, double *nexp, double *dcb, double *rms); int readionexb(FILE *fp, const double *lats, const double *lons, const double *hgts, double rb, double nexp, nav_t *nav); void combtec(nav_t *nav); void readtec(const char *file, nav_t *nav, int opt); int interptec(const tec_t *tec, int k, const double *posp, double *value, double *rms); int iondelay(gtime_t time, const tec_t *tec, const double *pos, const double *azel, int opt, double *delay, double *var); int iontec(gtime_t time, const nav_t *nav, const double *pos, const double *azel, int opt, double *delay, double *var); #endif /* GNSS_SDR_RTKLIB_IONEX_H_ */ src/algorithms/libs/rtklib/rtklib_lambda.cc000066400000000000000000000325521352176506000213500ustar00rootroot00000000000000/*! * \file rtklib_lambda.cc * \brief Integer ambiguity resolution * \authors
    *
  • 2007-2008, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2008, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_lambda.h" #include "rtklib_rtkcmn.h" #include /* LD factorization (Q=L'*diag(D)*L) -----------------------------------------*/ int LD(int n, const double *Q, double *L, double *D) { int i, j, k, info = 0; double a, *A = mat(n, n); memcpy(A, Q, sizeof(double) * n * n); for (i = n - 1; i >= 0; i--) { if ((D[i] = A[i + i * n]) <= 0.0) { info = -1; break; } a = sqrt(D[i]); for (j = 0; j <= i; j++) { L[i + j * n] = A[i + j * n] / a; } for (j = 0; j <= i - 1; j++) { for (k = 0; k <= j; k++) { A[j + k * n] -= L[i + k * n] * L[i + j * n]; } } for (j = 0; j <= i; j++) { L[i + j * n] /= L[i + i * n]; } } free(A); if (info) { fprintf(stderr, "%s : LD factorization error\n", __FILE__); } return info; } /* integer gauss transformation ----------------------------------------------*/ void gauss(int n, double *L, double *Z, int i, int j) { int k, mu; if ((mu = static_cast ROUND_LAMBDA(L[i + j * n])) != 0) { for (k = i; k < n; k++) { L[k + n * j] -= static_cast(mu) * L[k + i * n]; } for (k = 0; k < n; k++) { Z[k + n * j] -= static_cast(mu) * Z[k + i * n]; } } } /* permutations --------------------------------------------------------------*/ void perm(int n, double *L, double *D, int j, double del, double *Z) { int k; double eta, lam, a0, a1; eta = D[j] / del; lam = D[j + 1] * L[j + 1 + j * n] / del; D[j] = eta * D[j + 1]; D[j + 1] = del; for (k = 0; k <= j - 1; k++) { a0 = L[j + k * n]; a1 = L[j + 1 + k * n]; L[j + k * n] = -L[j + 1 + j * n] * a0 + a1; L[j + 1 + k * n] = eta * a0 + lam * a1; } L[j + 1 + j * n] = lam; for (k = j + 2; k < n; k++) { SWAP_LAMBDA(L[k + j * n], L[k + (j + 1) * n]); } for (k = 0; k < n; k++) { SWAP_LAMBDA(Z[k + j * n], Z[k + (j + 1) * n]); } } /* lambda reduction (z=Z'*a, Qz=Z'*Q*Z=L'*diag(D)*L) (ref.[1]) ---------------*/ void reduction(int n, double *L, double *D, double *Z) { int i, j, k; double del; j = n - 2; k = n - 2; while (j >= 0) { if (j <= k) { for (i = j + 1; i < n; i++) { gauss(n, L, Z, i, j); } } del = D[j] + L[j + 1 + j * n] * L[j + 1 + j * n] * D[j + 1]; if (del + 1E-6 < D[j + 1]) { /* compared considering numerical error */ perm(n, L, D, j, del, Z); k = j; j = n - 2; } else { j--; } } } /* modified lambda (mlambda) search (ref. [2]) -------------------------------*/ int search(int n, int m, const double *L, const double *D, const double *zs, double *zn, double *s) { int i, j, k, c, nn = 0, imax = 0; double newdist, maxdist = 1E99, y; double *S = zeros(n, n), *dist = mat(n, 1), *zb = mat(n, 1), *z = mat(n, 1), *step = mat(n, 1); k = n - 1; dist[k] = 0.0; zb[k] = zs[k]; z[k] = ROUND_LAMBDA(zb[k]); y = zb[k] - z[k]; step[k] = SGN_LAMBDA(y); for (c = 0; c < LOOPMAX; c++) { newdist = dist[k] + y * y / D[k]; if (newdist < maxdist) { if (k != 0) { dist[--k] = newdist; for (i = 0; i <= k; i++) { S[k + i * n] = S[k + 1 + i * n] + (z[k + 1] - zb[k + 1]) * L[k + 1 + i * n]; } zb[k] = zs[k] + S[k + k * n]; z[k] = ROUND_LAMBDA(zb[k]); y = zb[k] - z[k]; step[k] = SGN_LAMBDA(y); } else { if (nn < m) { if (nn == 0 || newdist > s[imax]) { imax = nn; } for (i = 0; i < n; i++) { zn[i + nn * n] = z[i]; } s[nn++] = newdist; } else { if (newdist < s[imax]) { for (i = 0; i < n; i++) { zn[i + imax * n] = z[i]; } s[imax] = newdist; for (i = imax = 0; i < m; i++) { if (s[imax] < s[i]) { imax = i; } } } maxdist = s[imax]; } z[0] += step[0]; y = zb[0] - z[0]; step[0] = -step[0] - SGN_LAMBDA(step[0]); } } else { if (k == n - 1) { break; } k++; z[k] += step[k]; y = zb[k] - z[k]; step[k] = -step[k] - SGN_LAMBDA(step[k]); } } for (i = 0; i < m - 1; i++) { /* sort by s */ for (j = i + 1; j < m; j++) { if (s[i] < s[j]) { continue; } SWAP_LAMBDA(s[i], s[j]); for (k = 0; k < n; k++) { SWAP_LAMBDA(zn[k + i * n], zn[k + j * n]); } } } free(S); free(dist); free(zb); free(z); free(step); if (c >= LOOPMAX) { fprintf(stderr, "%s : search loop count overflow\n", __FILE__); return -1; } return 0; } /* lambda/mlambda integer least-square estimation ------------------------------ * integer least-square estimation. reduction is performed by lambda (ref.[1]), * and search by mlambda (ref.[2]). * args : int n I number of float parameters * int m I number of fixed solutions * double *a I float parameters (n x 1) * double *Q I covariance matrix of float parameters (n x n) * double *F O fixed solutions (n x m) * double *s O sum of squared residulas of fixed solutions (1 x m) * return : status (0:ok,other:error) * notes : matrix stored by column-major order (fortran convention) *-----------------------------------------------------------------------------*/ int lambda(int n, int m, const double *a, const double *Q, double *F, double *s) { int info; double *L, *D, *Z, *z, *E; if (n <= 0 || m <= 0) { return -1; } L = zeros(n, n); D = mat(n, 1); Z = eye(n); z = mat(n, 1); E = mat(n, m); /* LD factorization */ if (!(info = LD(n, Q, L, D))) { /* lambda reduction */ reduction(n, L, D, Z); matmul("TN", n, 1, n, 1.0, Z, a, 0.0, z); /* z=Z'*a */ /* mlambda search */ if (!(info = search(n, m, L, D, z, E, s))) { info = solve("T", Z, E, n, m, F); /* F=Z'\E */ } } free(L); free(D); free(Z); free(z); free(E); return info; } /* lambda reduction ------------------------------------------------------------ * reduction by lambda (ref [1]) for integer least square * args : int n I number of float parameters * double *Q I covariance matrix of float parameters (n x n) * double *Z O lambda reduction matrix (n x n) * return : status (0:ok,other:error) *-----------------------------------------------------------------------------*/ int lambda_reduction(int n, const double *Q, double *Z) { double *L, *D; int i, j, info; if (n <= 0) { return -1; } L = zeros(n, n); D = mat(n, 1); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { Z[i + j * n] = i == j ? 1.0 : 0.0; } } /* LD factorization */ if ((info = LD(n, Q, L, D))) { free(L); free(D); return info; } /* lambda reduction */ reduction(n, L, D, Z); free(L); free(D); return 0; } /* mlambda search -------------------------------------------------------------- * search by mlambda (ref [2]) for integer least square * args : int n I number of float parameters * int m I number of fixed solutions * double *a I float parameters (n x 1) * double *Q I covariance matrix of float parameters (n x n) * double *F O fixed solutions (n x m) * double *s O sum of squared residulas of fixed solutions (1 x m) * return : status (0:ok,other:error) *-----------------------------------------------------------------------------*/ int lambda_search(int n, int m, const double *a, const double *Q, double *F, double *s) { double *L, *D; int info; if (n <= 0 || m <= 0) { return -1; } L = zeros(n, n); D = mat(n, 1); /* LD factorization */ if ((info = LD(n, Q, L, D))) { free(L); free(D); return info; } /* mlambda search */ info = search(n, m, L, D, a, F, s); free(L); free(D); return info; } src/algorithms/libs/rtklib/rtklib_lambda.h000066400000000000000000000075371352176506000212170ustar00rootroot00000000000000/*! * \file rtklib_lambda.h * \brief Integer ambiguity resolution * \authors
    *
  • 2007-2008, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2008, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * References: * [1] P.J.G.Teunissen, The least-square ambiguity decorrelation adjustment: * a method for fast GPS ambiguity estimation, J.Geodesy, Vol.70, 65-82, * 1995 * [2] X.-W.Chang, X.Yang, T.Zhou, MLAMBDA: A modified LAMBDA method for * integer least-squares estimation, J.Geodesy, Vol.79, 552-565, 2005 * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_LAMBDA_H_ #define GNSS_SDR_RTKLIB_LAMBDA_H_ #include "rtklib.h" /* constants/macros ----------------------------------------------------------*/ const int LOOPMAX = 10000; /* maximum count of search loop */ #define SGN_LAMBDA(x) ((x) <= 0.0 ? -1.0 : 1.0) #define ROUND_LAMBDA(x) (floor((x) + 0.5)) #define SWAP_LAMBDA(x, y) \ do \ { \ double tmp_; \ tmp_ = x; \ x = y; \ y = tmp_; \ } \ while (0) int LD(int n, const double *Q, double *L, double *D); void gauss(int n, double *L, double *Z, int i, int j); void perm(int n, double *L, double *D, int j, double del, double *Z); void reduction(int n, double *L, double *D, double *Z); int search(int n, int m, const double *L, const double *D, const double *zs, double *zn, double *s); int lambda(int n, int m, const double *a, const double *Q, double *F, double *s); int lambda_reduction(int n, const double *Q, double *Z); int lambda_search(int n, int m, const double *a, const double *Q, double *F, double *s); #endif src/algorithms/libs/rtklib/rtklib_pntpos.cc000066400000000000000000001025471352176506000214550ustar00rootroot00000000000000/*! * \file rtklib_pntpos.cc * \brief standard code-based positioning * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_pntpos.h" #include "rtklib_ephemeris.h" #include "rtklib_ionex.h" #include "rtklib_sbas.h" #include /* pseudorange measurement error variance ------------------------------------*/ double varerr(const prcopt_t *opt, double el, int sys) { double fact, varr; fact = sys == SYS_GLO ? EFACT_GLO : (sys == SYS_SBS ? EFACT_SBS : EFACT_GPS); varr = std::pow(opt->err[0], 2.0) * (std::pow(opt->err[1], 2.0) + std::pow(opt->err[2], 2.0) / sin(el)); if (opt->ionoopt == IONOOPT_IFLC) { varr *= std::pow(2, 3.0); /* iono-free */ } return std::pow(fact, 2.0) * varr; } /* get tgd parameter (m) -----------------------------------------------------*/ double gettgd(int sat, const nav_t *nav) { int i; for (i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].tgd[0]; } return 0.0; } /* get isc parameter (m) -----------------------------------------------------*/ double getiscl1(int sat, const nav_t *nav) { for (int i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].isc[0]; } return 0.0; } double getiscl2(int sat, const nav_t *nav) { for (int i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].isc[1]; } return 0.0; } double getiscl5i(int sat, const nav_t *nav) { for (int i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].isc[2]; } return 0.0; } double getiscl5q(int sat, const nav_t *nav) { for (int i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].isc[3]; } return 0.0; } /* psendorange with code bias correction -------------------------------------*/ double prange(const obsd_t *obs, const nav_t *nav, const double *azel, int iter, const prcopt_t *opt, double *var) { const double *lam = nav->lam[obs->sat - 1]; double PC = 0.0; double P1 = 0.0; double P2 = 0.0; double P1_P2 = 0.0; double P1_C1 = 0.0; double P2_C2 = 0.0; // Intersignal corrections (m). See GPS IS-200 CNAV message //double ISCl1 = 0.0; double ISCl2 = 0.0; double ISCl5i = 0.0; //double ISCl5q = 0.0; double gamma_ = 0.0; int i = 0; int j = 1; int sys = satsys(obs->sat, nullptr); *var = 0.0; if (sys == SYS_NONE) { trace(4, "prange: satsys NULL\n"); return 0.0; } /* L1-L2 for GPS/GLO/QZS, L1-L5 for GAL/SBS/BDS */ if (sys == SYS_GAL or sys == SYS_SBS or sys == SYS_BDS) { j = 2; } else if (sys == SYS_GPS or sys == SYS_GLO) { if (obs->code[1] != CODE_NONE) { j = 1; } else if (obs->code[2] != CODE_NONE) { j = 2; } } if (lam[i] == 0.0 or lam[j] == 0.0) { trace(4, "prange: NFREQ<2||lam[i]==0.0||lam[j]==0.0\n"); printf("i: %d j:%d, lam[i]: %f lam[j] %f\n", i, j, lam[i], lam[j]); return 0.0; } /* test snr mask */ if (iter > 0) { if (testsnr(0, i, azel[1], obs->SNR[i] * 0.25, &opt->snrmask)) { trace(4, "snr mask: %s sat=%2d el=%.1f snr=%.1f\n", time_str(obs->time, 0), obs->sat, azel[1] * R2D, obs->SNR[i] * 0.25); return 0.0; } if (opt->ionoopt == IONOOPT_IFLC) { if (testsnr(0, j, azel[1], obs->SNR[j] * 0.25, &opt->snrmask)) { trace(4, "prange: testsnr error\n"); return 0.0; } } } /* fL1^2 / fL2(orL5)^2 . See IS-GPS-200, p. 103 and Galileo ICD p. 48 */ if (sys == SYS_GPS or sys == SYS_GAL or sys == SYS_GLO or sys == SYS_BDS) { gamma_ = std::pow(lam[j], 2.0) / std::pow(lam[i], 2.0); } P1 = obs->P[i]; P2 = obs->P[j]; P1_P2 = nav->cbias[obs->sat - 1][0]; P1_C1 = nav->cbias[obs->sat - 1][1]; P2_C2 = nav->cbias[obs->sat - 1][2]; /* if no P1-P2 DCB, use TGD instead */ if (P1_P2 == 0.0) { P1_P2 = gettgd(obs->sat, nav); } if (sys == SYS_GPS) { // ISCl1 = getiscl1(obs->sat, nav); ISCl2 = getiscl2(obs->sat, nav); ISCl5i = getiscl5i(obs->sat, nav); // ISCl5q = getiscl5q(obs->sat, nav); } //CHECK IF IT IS STILL NEEDED if (opt->ionoopt == IONOOPT_IFLC) { /* dual-frequency */ if (P1 == 0.0 || P2 == 0.0) { return 0.0; } if (obs->code[i] == CODE_L1C) { P1 += P1_C1; } /* C1->P1 */ if (obs->code[j] == CODE_L2C) { P2 += P2_C2; } /* C2->P2 */ /* iono-free combination */ PC = (gamma_ * P1 - P2) / (gamma_ - 1.0); } //////////////////////////////////////////// else { /* single-frequency */ if (obs->code[i] == CODE_NONE and obs->code[j] == CODE_NONE) { return 0.0; } if (obs->code[i] != CODE_NONE and obs->code[j] == CODE_NONE) { P1 += P1_C1; /* C1->P1 */ PC = P1 - P1_P2; } else if (obs->code[i] == CODE_NONE and obs->code[j] != CODE_NONE) { if (sys == SYS_GPS) { P2 += P2_C2; /* C2->P2 */ //PC = P2 - gamma_ * P1_P2 / (1.0 - gamma_); if (obs->code[j] == CODE_L2S) //L2 single freq. { PC = P2 + P1_P2 - ISCl2; } else if (obs->code[j] == CODE_L5X) //L5 single freq. { PC = P2 + P1_P2 - ISCl5i; } } if (sys == SYS_BDS) { P2 += P2_C2; /* C2->P2 */ PC = P2; // no tgd corrections for B3I } else if (sys == SYS_GAL or sys == SYS_GLO or sys == SYS_BDS) // Gal. E5a single freq. { P2 += P2_C2; /* C2->P2 */ PC = P2 - gamma_ * P1_P2 / (1.0 - gamma_); } } /* dual-frequency */ else if (sys == SYS_GPS) { if (obs->code[j] == CODE_L2S) /* L1 + L2 */ { //By the moment, GPS L2 pseudoranges are not used //PC = (P2 + ISCl2 - gamma_ * (P1 + ISCl1)) / (1.0 - gamma_) - P1_P2; P1 += P1_C1; /* C1->P1 */ PC = P1 + P1_P2; } else if (obs->code[j] == CODE_L5X) /* L1 + L5 */ { //By the moment, GPS L5 pseudoranges are not used //PC = (P2 + ISCl5i - gamma_ * (P1 + ISCl5i)) / (1.0 - gamma_) - P1_P2; P1 += P1_C1; /* C1->P1 */ PC = P1 + P1_P2; } } else if (sys == SYS_GAL or sys == SYS_GLO or sys == SYS_BDS) /* E1 + E5a */ { P1 += P1_C1; P2 += P2_C2; PC = (gamma_ * P1 - P2) / (gamma_ - 1.0); } } if (opt->sateph == EPHOPT_SBAS) { PC -= P1_C1; } /* sbas clock based C1 */ *var = std::pow(ERR_CBIAS, 2.0); return PC; } /* ionospheric correction ------------------------------------------------------ * compute ionospheric correction * args : gtime_t time I time * nav_t *nav I navigation data * int sat I satellite number * double *pos I receiver position {lat,lon,h} (rad|m) * double *azel I azimuth/elevation angle {az,el} (rad) * int ionoopt I ionospheric correction option (IONOOPT_???) * double *ion O ionospheric delay (L1) (m) * double *var O ionospheric delay (L1) variance (m^2) * return : status(1:ok,0:error) *-----------------------------------------------------------------------------*/ int ionocorr(gtime_t time, const nav_t *nav, int sat, const double *pos, const double *azel, int ionoopt, double *ion, double *var) { trace(4, "ionocorr: time=%s opt=%d sat=%2d pos=%.3f %.3f azel=%.3f %.3f\n", time_str(time, 3), ionoopt, sat, pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); /* broadcast model */ if (ionoopt == IONOOPT_BRDC) { *ion = ionmodel(time, nav->ion_gps, pos, azel); *var = std::pow(*ion * ERR_BRDCI, 2.0); return 1; } /* sbas ionosphere model */ if (ionoopt == IONOOPT_SBAS) { return sbsioncorr(time, nav, pos, azel, ion, var); } /* ionex tec model */ if (ionoopt == IONOOPT_TEC) { return iontec(time, nav, pos, azel, 1, ion, var); } /* qzss broadcast model */ if (ionoopt == IONOOPT_QZS && norm_rtk(nav->ion_qzs, 8) > 0.0) { *ion = ionmodel(time, nav->ion_qzs, pos, azel); *var = std::pow(*ion * ERR_BRDCI, 2.0); return 1; } /* lex ionosphere model */ //if (ionoopt == IONOOPT_LEX) { // return lexioncorr(time, nav, pos, azel, ion, var); //} *ion = 0.0; *var = ionoopt == IONOOPT_OFF ? std::pow(ERR_ION, 2.0) : 0.0; return 1; } /* tropospheric correction ----------------------------------------------------- * compute tropospheric correction * args : gtime_t time I time * nav_t *nav I navigation data * double *pos I receiver position {lat,lon,h} (rad|m) * double *azel I azimuth/elevation angle {az,el} (rad) * int tropopt I tropospheric correction option (TROPOPT_???) * double *trp O tropospheric delay (m) * double *var O tropospheric delay variance (m^2) * return : status(1:ok,0:error) *-----------------------------------------------------------------------------*/ int tropcorr(gtime_t time, const nav_t *nav __attribute__((unused)), const double *pos, const double *azel, int tropopt, double *trp, double *var) { trace(4, "tropcorr: time=%s opt=%d pos=%.3f %.3f azel=%.3f %.3f\n", time_str(time, 3), tropopt, pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); /* saastamoinen model */ if (tropopt == TROPOPT_SAAS || tropopt == TROPOPT_EST || tropopt == TROPOPT_ESTG) { *trp = tropmodel(time, pos, azel, REL_HUMI); *var = std::pow(ERR_SAAS / (sin(azel[1]) + 0.1), 2.0); return 1; } /* sbas troposphere model */ if (tropopt == TROPOPT_SBAS) { *trp = sbstropcorr(time, pos, azel, var); return 1; } /* no correction */ *trp = 0.0; *var = tropopt == TROPOPT_OFF ? std::pow(ERR_TROP, 2.0) : 0.0; return 1; } /* pseudorange residuals -----------------------------------------------------*/ int rescode(int iter, const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, const prcopt_t *opt, double *v, double *H, double *var, double *azel, int *vsat, double *resp, int *ns) { double r, dion, dtrp, vmeas, vion, vtrp, rr[3], pos[3], dtr, e[3], P, lam_L1; int i, j, nv = 0, sys, mask[4] = {0}; trace(3, "resprng : n=%d\n", n); for (i = 0; i < 3; i++) { rr[i] = x[i]; } dtr = x[3]; ecef2pos(rr, pos); for (i = *ns = 0; i < n && i < MAXOBS; i++) { vsat[i] = 0; azel[i * 2] = azel[1 + i * 2] = resp[i] = 0.0; if (!(sys = satsys(obs[i].sat, nullptr))) { continue; } /* reject duplicated observation data */ if (i < n - 1 && i < MAXOBS - 1 && obs[i].sat == obs[i + 1].sat) { trace(2, "duplicated observation data %s sat=%2d\n", time_str(obs[i].time, 3), obs[i].sat); i++; continue; } /* geometric distance/azimuth/elevation angle */ if ((r = geodist(rs + i * 6, rr, e)) <= 0.0) { trace(4, "geodist error\n"); continue; } double elaux = satazel(pos, e, azel + i * 2); if (elaux < opt->elmin) { trace(4, "satazel error. el = %lf , elmin = %lf\n", elaux, opt->elmin); continue; } /* psudorange with code bias correction */ if ((P = prange(obs + i, nav, azel + i * 2, iter, opt, &vmeas)) == 0.0) { trace(4, "prange error\n"); continue; } /* excluded satellite? */ if (satexclude(obs[i].sat, svh[i], opt)) { trace(4, "satexclude error\n"); continue; } /* ionospheric corrections */ if (!ionocorr(obs[i].time, nav, obs[i].sat, pos, azel + i * 2, iter > 0 ? opt->ionoopt : IONOOPT_BRDC, &dion, &vion)) { trace(4, "ionocorr error\n"); continue; } /* GPS-L1 -> L1/B1 */ if ((lam_L1 = nav->lam[obs[i].sat - 1][0]) > 0.0) { dion *= std::pow(lam_L1 / LAM_CARR[0], 2.0); } /* tropospheric corrections */ if (!tropcorr(obs[i].time, nav, pos, azel + i * 2, iter > 0 ? opt->tropopt : TROPOPT_SAAS, &dtrp, &vtrp)) { trace(4, "tropocorr error\n"); continue; } /* pseudorange residual */ v[nv] = P - (r + dtr - SPEED_OF_LIGHT * dts[i * 2] + dion + dtrp); /* design matrix */ for (j = 0; j < NX; j++) { H[j + nv * NX] = j < 3 ? -e[j] : (j == 3 ? 1.0 : 0.0); } /* time system and receiver bias offset correction */ if (sys == SYS_GLO) { v[nv] -= x[4]; H[4 + nv * NX] = 1.0; mask[1] = 1; } else if (sys == SYS_GAL) { v[nv] -= x[5]; H[5 + nv * NX] = 1.0; mask[2] = 1; } else if (sys == SYS_BDS) { v[nv] -= x[6]; H[6 + nv * NX] = 1.0; mask[3] = 1; } else { mask[0] = 1; } vsat[i] = 1; resp[i] = v[nv]; (*ns)++; /* error variance */ var[nv++] = varerr(opt, azel[1 + i * 2], sys) + vare[i] + vmeas + vion + vtrp; trace(4, "sat=%2d azel=%5.1f %4.1f res=%7.3f sig=%5.3f\n", obs[i].sat, azel[i * 2] * R2D, azel[1 + i * 2] * R2D, resp[i], sqrt(var[nv - 1])); } /* constraint to avoid rank-deficient */ for (i = 0; i < 4; i++) { if (mask[i]) { continue; } v[nv] = 0.0; for (j = 0; j < NX; j++) { H[j + nv * NX] = j == i + 3 ? 1.0 : 0.0; } var[nv++] = 0.01; } return nv; } /* validate solution ---------------------------------------------------------*/ int valsol(const double *azel, const int *vsat, int n, const prcopt_t *opt, const double *v, int nv, int nx, char *msg) { double azels[MAXOBS * 2] = {0}; double dop[4], vv; int i, ns; trace(3, "valsol : n=%d nv=%d\n", n, nv); /* chi-square validation of residuals */ vv = dot(v, v, nv); if (nv > nx && vv > CHISQR[nv - nx - 1]) { sprintf(msg, "chi-square error nv=%d vv=%.1f cs=%.1f", nv, vv, CHISQR[nv - nx - 1]); return 0; } /* large gdop check */ for (i = ns = 0; i < n; i++) { if (!vsat[i]) { continue; } azels[ns * 2] = azel[i * 2]; azels[1 + ns * 2] = azel[1 + i * 2]; ns++; } dops(ns, azels, opt->elmin, dop); if (dop[0] <= 0.0 || dop[0] > opt->maxgdop) { sprintf(msg, "gdop error nv=%d gdop=%.1f", nv, dop[0]); return 0; } return 1; } /* estimate receiver position ------------------------------------------------*/ int estpos(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_rtk(dx, NX) < 1e-4) { sol->type = 0; sol->time = timeadd(obs[0].time, -x[3] / SPEED_OF_LIGHT); sol->dtr[0] = x[3] / SPEED_OF_LIGHT; /* receiver clock bias (s) */ sol->dtr[1] = x[4] / SPEED_OF_LIGHT; /* glo-gps time offset (s) */ sol->dtr[2] = x[5] / SPEED_OF_LIGHT; /* gal-gps time offset (s) */ sol->dtr[3] = x[6] / SPEED_OF_LIGHT; /* 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] = static_cast(Q[j + j * NX]); } sol->qr[3] = static_cast(Q[1]); /* cov xy */ sol->qr[4] = static_cast(Q[2 + NX]); /* cov yz */ sol->qr[5] = static_cast(Q[2]); /* cov zx */ sol->ns = static_cast(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; } /* raim fde (failure detection and exclution) -------------------------------*/ int raim_fde(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) { obsd_t *obs_e; sol_t sol_e = {{0, 0}, {}, {}, {}, '0', '0', '0', 0.0, 0.0, 0.0}; char tstr[32], name[16], msg_e[128]; double *rs_e, *dts_e, *vare_e, *azel_e, *resp_e, rms_e, rms = 100.0; int i, j, k, nvsat, stat = 0, *svh_e, *vsat_e, sat = 0; trace(3, "raim_fde: %s n=%2d\n", time_str(obs[0].time, 0), n); if (!(obs_e = static_cast(malloc(sizeof(obsd_t) * n)))) { return 0; } rs_e = mat(6, n); dts_e = mat(2, n); vare_e = mat(1, n); azel_e = zeros(2, n); svh_e = imat(1, n); vsat_e = imat(1, n); resp_e = mat(1, n); for (i = 0; i < n; i++) { /* satellite exclution */ for (j = k = 0; j < n; j++) { if (j == i) { continue; } obs_e[k] = obs[j]; matcpy(rs_e + 6 * k, rs + 6 * j, 6, 1); matcpy(dts_e + 2 * k, dts + 2 * j, 2, 1); vare_e[k] = vare[j]; svh_e[k++] = svh[j]; } /* estimate receiver position without a satellite */ if (!estpos(obs_e, n - 1, rs_e, dts_e, vare_e, svh_e, nav, opt, &sol_e, azel_e, vsat_e, resp_e, msg_e)) { trace(3, "raim_fde: exsat=%2d (%s)\n", obs[i].sat, msg); continue; } for (j = nvsat = 0, rms_e = 0.0; j < n - 1; j++) { if (!vsat_e[j]) { continue; } rms_e += std::pow(resp_e[j], 2.0); nvsat++; } if (nvsat < 5) { trace(3, "raim_fde: exsat=%2d lack of satellites nvsat=%2d\n", obs[i].sat, nvsat); continue; } rms_e = sqrt(rms_e / nvsat); trace(3, "raim_fde: exsat=%2d rms=%8.3f\n", obs[i].sat, rms_e); if (rms_e > rms) { continue; } /* save result */ for (j = k = 0; j < n; j++) { if (j == i) { continue; } matcpy(azel + 2 * j, azel_e + 2 * k, 2, 1); vsat[j] = vsat_e[k]; resp[j] = resp_e[k++]; } stat = 1; *sol = sol_e; sat = obs[i].sat; rms = rms_e; vsat[i] = 0; strcpy(msg, msg_e); } if (stat) { time2str(obs[0].time, tstr, 2); satno2id(sat, name); trace(2, "%s: %s excluded by raim\n", tstr + 11, name); } free(obs_e); free(rs_e); free(dts_e); free(vare_e); free(azel_e); free(svh_e); free(vsat_e); free(resp_e); return stat; } /* doppler residuals ---------------------------------------------------------*/ int resdop(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const double *rr, const double *x, const double *azel, const int *vsat, double *v, double *H) { double lam, rate, pos[3], E[9], a[3], e[3], vs[3], cosel; int i, j, nv = 0; int band = 0; trace(3, "resdop : n=%d\n", n); ecef2pos(rr, pos); xyz2enu(pos, E); for (i = 0; i < n && i < MAXOBS; i++) { if (obs[i].code[0] != CODE_NONE) { band = 0; } if (obs[i].code[1] != CODE_NONE) { band = 1; } if (obs[i].code[2] != CODE_NONE) { band = 2; } lam = nav->lam[obs[i].sat - 1][band]; if (obs[i].D[band] == 0.0 || lam == 0.0 || !vsat[i] || norm_rtk(rs + 3 + i * 6, 3) <= 0.0) { continue; } /* line-of-sight vector in ecef */ cosel = cos(azel[1 + i * 2]); a[0] = sin(azel[i * 2]) * cosel; a[1] = cos(azel[i * 2]) * cosel; a[2] = sin(azel[1 + i * 2]); matmul("TN", 3, 1, 3, 1.0, E, a, 0.0, e); /* satellite velocity relative to receiver in ecef */ for (j = 0; j < 3; j++) { vs[j] = rs[j + 3 + i * 6] - x[j]; } /* range rate with earth rotation correction */ rate = dot(vs, e, 3) + DEFAULT_OMEGA_EARTH_DOT / SPEED_OF_LIGHT * (rs[4 + i * 6] * rr[0] + rs[1 + i * 6] * x[0] - rs[3 + i * 6] * rr[1] - rs[i * 6] * x[1]); /* doppler residual */ v[nv] = -lam * obs[i].D[band] - (rate + x[3] - SPEED_OF_LIGHT * dts[1 + i * 2]); /* design matrix */ for (j = 0; j < 4; j++) { H[j + nv * 4] = j < 3 ? -e[j] : 1.0; } nv++; } return nv; } /* estimate receiver velocity ------------------------------------------------*/ void estvel(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const prcopt_t *opt __attribute__((unused)), sol_t *sol, const double *azel, const int *vsat) { double x[4] = {0}, dx[4], Q[16], *v, *H; int i, j, nv; trace(3, "estvel : n=%d\n", n); v = mat(n, 1); H = mat(4, n); for (i = 0; i < MAXITR; i++) { /* doppler residuals */ if ((nv = resdop(obs, n, rs, dts, nav, sol->rr, x, azel, vsat, v, H)) < 4) { break; } /* least square estimation */ if (lsq(H, v, 4, nv, dx, Q)) { break; } for (j = 0; j < 4; j++) { x[j] += dx[j]; } if (norm_rtk(dx, 4) < 1e-6) { for (i = 0; i < 3; i++) { sol->rr[i + 3] = x[i]; } break; } } free(v); free(H); } /* single-point positioning ---------------------------------------------------- * 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 pntpos(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 positions, velocities and clocks */ satposs(sol->time, obs, n, nav, opt_.sateph, rs, dts, var, svh); /* estimate receiver position with pseudorange */ stat = estpos(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; } src/algorithms/libs/rtklib/rtklib_pntpos.h000066400000000000000000000174621352176506000213200ustar00rootroot00000000000000/*! * \file rtklib_pntpos.h * \brief standard code-based positioning * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_PNTPOS_H_ #define GNSS_SDR_RTKLIB_PNTPOS_H_ #include "rtklib.h" #include "rtklib_rtkcmn.h" /* constants -----------------------------------------------------------------*/ const int NX = 4 + 3; //!< # of estimated parameters const int MAXITR = 10; //!< max number of iteration for point pos const double ERR_ION = 5.0; //!< ionospheric delay std (m) const double ERR_TROP = 3.0; //!< tropspheric delay std (m) /* pseudorange measurement error variance ------------------------------------*/ double varerr(const prcopt_t *opt, double el, int sys); /* get tgd parameter (m) -----------------------------------------------------*/ double gettgd(int sat, const nav_t *nav); /* get isc parameter (m) -----------------------------------------------------*/ double getiscl1(int sat, const nav_t *nav); double getiscl2(int sat, const nav_t *nav); double getiscl5i(int sat, const nav_t *nav); double getiscl5q(int sat, const nav_t *nav); /* psendorange with code bias correction -------------------------------------*/ double prange(const obsd_t *obs, const nav_t *nav, const double *azel, int iter, const prcopt_t *opt, double *var); /* ionospheric correction ------------------------------------------------------ * compute ionospheric correction * args : gtime_t time I time * nav_t *nav I navigation data * int sat I satellite number * double *pos I receiver position {lat,lon,h} (rad|m) * double *azel I azimuth/elevation angle {az,el} (rad) * int ionoopt I ionospheric correction option (IONOOPT_???) * double *ion O ionospheric delay (L1) (m) * double *var O ionospheric delay (L1) variance (m^2) * return : status(1:ok,0:error) *-----------------------------------------------------------------------------*/ int ionocorr(gtime_t time, const nav_t *nav, int sat, const double *pos, const double *azel, int ionoopt, double *ion, double *var); /* tropospheric correction ----------------------------------------------------- * compute tropospheric correction * args : gtime_t time I time * nav_t *nav I navigation data * double *pos I receiver position {lat,lon,h} (rad|m) * double *azel I azimuth/elevation angle {az,el} (rad) * int tropopt I tropospheric correction option (TROPOPT_???) * double *trp O tropospheric delay (m) * double *var O tropospheric delay variance (m^2) * return : status(1:ok,0:error) *-----------------------------------------------------------------------------*/ int tropcorr(gtime_t time, const nav_t *nav, const double *pos, const double *azel, int tropopt, double *trp, double *var); /* pseudorange residuals -----------------------------------------------------*/ int rescode(int iter, const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, const prcopt_t *opt, double *v, double *H, double *var, double *azel, int *vsat, double *resp, int *ns); /* validate solution ---------------------------------------------------------*/ int valsol(const double *azel, const int *vsat, int n, const prcopt_t *opt, const double *v, int nv, int nx, char *msg); /* estimate receiver position ------------------------------------------------*/ int estpos(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); /* raim fde (failure detection and exclution) -------------------------------*/ int raim_fde(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); /* doppler residuals ---------------------------------------------------------*/ int resdop(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const double *rr, const double *x, const double *azel, const int *vsat, double *v, double *H); /* estimate receiver velocity ------------------------------------------------*/ void estvel(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const prcopt_t *opt, sol_t *sol, const double *azel, const int *vsat); /*! * \brief single-point positioning * 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 pntpos(const obsd_t *obs, int n, const nav_t *nav, const prcopt_t *opt, sol_t *sol, double *azel, ssat_t *ssat, char *msg); #endif /* GNSS_SDR_RTKLIB_PNTPOS_H_ */ src/algorithms/libs/rtklib/rtklib_ppp.cc000066400000000000000000001511341352176506000207250ustar00rootroot00000000000000/*! * \file rtklib_ppp.cc * \brief Precise Point Positioning * \authors
    *
  • 2007-2008, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2008, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_ppp.h" #include "rtklib_ephemeris.h" #include "rtklib_ionex.h" #include "rtklib_lambda.h" #include "rtklib_rtkcmn.h" #include "rtklib_sbas.h" #include "rtklib_tides.h" #include /* wave length of LC (m) -----------------------------------------------------*/ double lam_LC(int i, int j, int k) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; return SPEED_OF_LIGHT / (i * f1 + j * f2 + k * f5); } /* carrier-phase LC (m) ------------------------------------------------------*/ double L_LC(int i, int j, int k, const double *L) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; double L1, L2, L5; if ((i && !L[0]) || (j && !L[1]) || (k && !L[2])) { return 0.0; } L1 = SPEED_OF_LIGHT / f1 * L[0]; L2 = SPEED_OF_LIGHT / f2 * L[1]; L5 = SPEED_OF_LIGHT / f5 * L[2]; return (i * f1 * L1 + j * f2 * L2 + k * f5 * L5) / (i * f1 + j * f2 + k * f5); } /* pseudorange LC (m) --------------------------------------------------------*/ double P_LC(int i, int j, int k, const double *P) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; double P1, P2, P5; if ((i && !P[0]) || (j && !P[1]) || (k && !P[2])) { return 0.0; } P1 = P[0]; P2 = P[1]; P5 = P[2]; return (i * f1 * P1 + j * f2 * P2 + k * f5 * P5) / (i * f1 + j * f2 + k * f5); } /* noise variance of LC (m) --------------------------------------------------*/ double var_LC(int i, int j, int k, double sig) { const double f1 = FREQ1, f2 = FREQ2, f5 = FREQ5; return (std::pow(i * f1, 2.0) + std::pow(j * f2, 2.0) + std::pow(k * f5, 2.0)) / std::pow(i * f1 + j * f2 + k * f5, 2.0) * std::pow(sig, 2.0); } /* complementaty error function (ref [1] p.227-229) --------------------------*/ double p_gamma(double a, double x, double log_gamma_a) { double y, w; int i; if (x == 0.0) { return 0.0; } if (x >= a + 1.0) { return 1.0 - q_gamma(a, x, log_gamma_a); } y = w = exp(a * log(x) - x - log_gamma_a) / a; for (i = 1; i < 100; i++) { w *= x / (a + i); y += w; if (fabs(w) < 1E-15) { break; } } return y; } double q_gamma(double a, double x, double log_gamma_a) { double y, w, la = 1.0, lb = x + 1.0 - a, lc; int i; if (x < a + 1.0) { return 1.0 - p_gamma(a, x, log_gamma_a); } w = exp(-x + a * log(x) - log_gamma_a); y = w / lb; for (i = 2; i < 100; i++) { lc = ((i - 1 - a) * (lb - la) + (i + x) * lb) / i; la = lb; lb = lc; w *= (i - 1 - a) / i; y += w / la / lb; if (fabs(w / la / lb) < 1E-15) { break; } } return y; } double f_erfc(double x) { return x >= 0.0 ? q_gamma(0.5, x * x, LOG_PI / 2.0) : 1.0 + p_gamma(0.5, x * x, LOG_PI / 2.0); } /* confidence function of integer ambiguity ----------------------------------*/ double conffunc(int N, double B, double sig) { double x, p = 1.0; int i; x = fabs(B - N); for (i = 1; i < 8; i++) { p -= f_erfc((i - x) / (SQRT2 * sig)) - f_erfc((i + x) / (SQRT2 * sig)); } return p; } /* average LC ----------------------------------------------------------------*/ void average_LC(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav __attribute__((unused)), const double *azel) { ambc_t *amb; double LC1, LC2, LC3, var1, var2, var3, sig; int i, j, sat; for (i = 0; i < n; i++) { sat = obs[i].sat; if (azel[1 + 2 * i] < rtk->opt.elmin) { continue; } if (satsys(sat, nullptr) != SYS_GPS) { continue; } /* triple-freq carrier and code LC (m) */ LC1 = L_LC(1, -1, 0, obs[i].L) - P_LC(1, 1, 0, obs[i].P); LC2 = L_LC(0, 1, -1, obs[i].L) - P_LC(0, 1, 1, obs[i].P); LC3 = L_LC(1, -6, 5, obs[i].L) - P_LC(1, 1, 0, obs[i].P); sig = std::sqrt(std::pow(rtk->opt.err[1], 2.0) + std::pow(rtk->opt.err[2] / sin(azel[1 + 2 * i]), 2.0)); /* measurement noise variance (m) */ var1 = var_LC(1, 1, 0, sig * rtk->opt.eratio[0]); var2 = var_LC(0, 1, 1, sig * rtk->opt.eratio[0]); var3 = var_LC(1, 1, 0, sig * rtk->opt.eratio[0]); amb = rtk->ambc + sat - 1; if (rtk->ssat[sat - 1].slip[0] || rtk->ssat[sat - 1].slip[1] || rtk->ssat[sat - 1].slip[2] || amb->n[0] == 0.0 || fabs(timediff(amb->epoch[0], obs[0].time)) > MIN_ARC_GAP) { amb->n[0] = amb->n[1] = amb->n[2] = 0.0; amb->LC[0] = amb->LC[1] = amb->LC[2] = 0.0; amb->LCv[0] = amb->LCv[1] = amb->LCv[2] = 0.0; amb->fixcnt = 0; for (j = 0; j < MAXSAT; j++) { amb->flags[j] = 0; } } /* averaging */ if (LC1) { amb->n[0] += 1.0; amb->LC[0] += (LC1 - amb->LC[0]) / amb->n[0]; amb->LCv[0] += (var1 - amb->LCv[0]) / amb->n[0]; } if (LC2) { amb->n[1] += 1.0; amb->LC[1] += (LC2 - amb->LC[1]) / amb->n[1]; amb->LCv[1] += (var2 - amb->LCv[1]) / amb->n[1]; } if (LC3) { amb->n[2] += 1.0; amb->LC[2] += (LC3 - amb->LC[2]) / amb->n[2]; amb->LCv[2] += (var3 - amb->LCv[2]) / amb->n[2]; } amb->epoch[0] = obs[0].time; } } /* fix wide-lane ambiguity ---------------------------------------------------*/ int fix_amb_WL(rtk_t *rtk, const nav_t *nav, int sat1, int sat2, int *NW) { ambc_t *amb1, *amb2; double BW, vW, lam_WL = lam_LC(1, -1, 0); amb1 = rtk->ambc + sat1 - 1; amb2 = rtk->ambc + sat2 - 1; if (!amb1->n[0] || !amb2->n[0]) { return 0; } /* wide-lane ambiguity */ #ifndef REV_WL_FCB BW = (amb1->LC[0] - amb2->LC[0]) / lam_WL + nav->wlbias[sat1 - 1] - nav->wlbias[sat2 - 1]; #else BW = (amb1->LC[0] - amb2->LC[0]) / lam_WL - nav->wlbias[sat1 - 1] + nav->wlbias[sat2 - 1]; #endif *NW = ROUND_PPP(BW); /* variance of wide-lane ambiguity */ vW = (amb1->LCv[0] / amb1->n[0] + amb2->LCv[0] / amb2->n[0]) / std::pow(lam_WL, 2.0); /* validation of integer wide-lane ambigyity */ return fabs(*NW - BW) <= rtk->opt.thresar[2] && conffunc(*NW, BW, sqrt(vW)) >= rtk->opt.thresar[1]; } /* linear dependency check ---------------------------------------------------*/ int is_depend(int sat1, int sat2, int *flgs, int *max_flg) { int i; if (flgs[sat1 - 1] == 0 && flgs[sat2 - 1] == 0) { flgs[sat1 - 1] = flgs[sat2 - 1] = ++(*max_flg); } else if (flgs[sat1 - 1] == 0 && flgs[sat2 - 1] != 0) { flgs[sat1 - 1] = flgs[sat2 - 1]; } else if (flgs[sat1 - 1] != 0 && flgs[sat2 - 1] == 0) { flgs[sat2 - 1] = flgs[sat1 - 1]; } else if (flgs[sat1 - 1] > flgs[sat2 - 1]) { for (i = 0; i < MAXSAT; i++) { if (flgs[i] == flgs[sat2 - 1]) { flgs[i] = flgs[sat1 - 1]; } } } else if (flgs[sat1 - 1] < flgs[sat2 - 1]) { for (i = 0; i < MAXSAT; i++) { if (flgs[i] == flgs[sat1 - 1]) { flgs[i] = flgs[sat2 - 1]; } } } else { return 0; /* linear dependent */ } return 1; } /* select fixed ambiguities --------------------------------------------------*/ int sel_amb(int *sat1, int *sat2, double *N, double *var, int n) { int i, j, flgs[MAXSAT] = {0}, max_flg = 0; /* sort by variance */ for (i = 0; i < n; i++) { for (j = 1; j < n - i; j++) { if (var[j] >= var[j - 1]) { continue; } SWAP_I(sat1[j], sat1[j - 1]); SWAP_I(sat2[j], sat2[j - 1]); SWAP_D(N[j], N[j - 1]); SWAP_D(var[j], var[j - 1]); } } /* select linearly independent satellite pair */ for (i = j = 0; i < n; i++) { if (!is_depend(sat1[i], sat2[i], flgs, &max_flg)) { continue; } sat1[j] = sat1[i]; sat2[j] = sat2[i]; N[j] = N[i]; var[j++] = var[i]; } return j; } /* fixed solution ------------------------------------------------------------*/ int fix_sol(rtk_t *rtk, const int *sat1, const int *sat2, const double *NC, int n) { double *v, *H, *R; int i, j, k, info; if (n <= 0) { return 0; } v = zeros(n, 1); H = zeros(rtk->nx, n); R = zeros(n, n); /* constraints to fixed ambiguities */ for (i = 0; i < n; i++) { j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); v[i] = NC[i] - (rtk->x[j] - rtk->x[k]); H[j + i * rtk->nx] = 1.0; H[k + i * rtk->nx] = -1.0; R[i + i * n] = std::pow(CONST_AMB, 2.0); } /* update states with constraints */ if ((info = filter(rtk->x, rtk->P, H, v, R, rtk->nx, n))) { trace(1, "filter error (info=%d)\n", info); free(v); free(H); free(R); return 0; } /* set solution */ for (i = 0; i < rtk->na; i++) { rtk->xa[i] = rtk->x[i]; for (j = 0; j < rtk->na; j++) { rtk->Pa[i + j * rtk->na] = rtk->Pa[j + i * rtk->na] = rtk->P[i + j * rtk->nx]; } } /* set flags */ for (i = 0; i < n; i++) { rtk->ambc[sat1[i] - 1].flags[sat2[i] - 1] = 1; rtk->ambc[sat2[i] - 1].flags[sat1[i] - 1] = 1; } free(v); free(H); free(R); return 1; } /* fix narrow-lane ambiguity by rounding -------------------------------------*/ int fix_amb_ROUND(rtk_t *rtk, int *sat1, int *sat2, const int *NW, int n) { double C1, C2, B1, v1, BC, v, vc, *NC, *var, lam_NL = lam_LC(1, 1, 0), lam1, lam2; int i, j, k, m = 0, N1, stat; lam1 = LAM_CARR[0]; lam2 = LAM_CARR[1]; C1 = std::pow(lam2, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); C2 = -std::pow(lam1, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); NC = zeros(n, 1); var = zeros(n, 1); for (i = 0; i < n; i++) { j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); /* narrow-lane ambiguity */ B1 = (rtk->x[j] - rtk->x[k] + C2 * lam2 * NW[i]) / lam_NL; N1 = ROUND_PPP(B1); /* variance of narrow-lane ambiguity */ var[m] = rtk->P[j + j * rtk->nx] + rtk->P[k + k * rtk->nx] - 2.0 * rtk->P[j + k * rtk->nx]; v1 = var[m] / std::pow(lam_NL, 2.0); /* validation of narrow-lane ambiguity */ if (fabs(N1 - B1) > rtk->opt.thresar[2] || conffunc(N1, B1, sqrt(v1)) < rtk->opt.thresar[1]) { continue; } /* iono-free ambiguity (m) */ BC = C1 * lam1 * N1 + C2 * lam2 * (N1 - NW[i]); /* check residuals */ v = rtk->ssat[sat1[i] - 1].resc[0] - rtk->ssat[sat2[i] - 1].resc[0]; vc = v + (BC - (rtk->x[j] - rtk->x[k])); if (fabs(vc) > THRES_RES) { continue; } sat1[m] = sat1[i]; sat2[m] = sat2[i]; NC[m++] = BC; } /* select fixed ambiguities by dependency check */ m = sel_amb(sat1, sat2, NC, var, m); /* fixed solution */ stat = fix_sol(rtk, sat1, sat2, NC, m); free(NC); free(var); return stat && m >= 3; } /* fix narrow-lane ambiguity by ILS ------------------------------------------*/ int fix_amb_ILS(rtk_t *rtk, int *sat1, int *sat2, int *NW, int n) { double C1, C2, *B1, *N1, *NC, *D, *E, *Q, s[2], lam_NL = lam_LC(1, 1, 0), lam1, lam2; int i, j, k, m = 0, info, stat, flgs[MAXSAT] = {0}, max_flg = 0; lam1 = LAM_CARR[0]; lam2 = LAM_CARR[1]; C1 = std::pow(lam2, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); C2 = -std::pow(lam1, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); B1 = zeros(n, 1); N1 = zeros(n, 2); D = zeros(rtk->nx, n); E = mat(n, rtk->nx); Q = mat(n, n); NC = mat(n, 1); for (i = 0; i < n; i++) { /* check linear independency */ if (!is_depend(sat1[i], sat2[i], flgs, &max_flg)) { continue; } j = IB_PPP(sat1[i], &rtk->opt); k = IB_PPP(sat2[i], &rtk->opt); /* float narrow-lane ambiguity (cycle) */ B1[m] = (rtk->x[j] - rtk->x[k] + C2 * lam2 * NW[i]) / lam_NL; N1[m] = ROUND_PPP(B1[m]); /* validation of narrow-lane ambiguity */ if (fabs(N1[m] - B1[m]) > rtk->opt.thresar[2]) { continue; } /* narrow-lane ambiguity transformation matrix */ D[j + m * rtk->nx] = 1.0 / lam_NL; D[k + m * rtk->nx] = -1.0 / lam_NL; sat1[m] = sat1[i]; sat2[m] = sat2[i]; NW[m++] = NW[i]; } if (m < 3) { free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } /* covariance of narrow-lane ambiguities */ matmul("TN", m, rtk->nx, rtk->nx, 1.0, D, rtk->P, 0.0, E); matmul("NN", m, m, rtk->nx, 1.0, E, D, 0.0, Q); /* integer least square */ if ((info = lambda(m, 2, B1, Q, N1, s))) { trace(2, "lambda error: info=%d\n", info); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } if (s[0] <= 0.0) { free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } rtk->sol.ratio = static_cast(MIN_PPP(s[1] / s[0], 999.9)); /* varidation by ratio-test */ if (rtk->opt.thresar[0] > 0.0 && rtk->sol.ratio < rtk->opt.thresar[0]) { trace(2, "varidation error: n=%2d ratio=%8.3f\n", m, rtk->sol.ratio); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return 0; } trace(2, "varidation ok: %s n=%2d ratio=%8.3f\n", time_str(rtk->sol.time, 0), m, rtk->sol.ratio); /* narrow-lane to iono-free ambiguity */ for (i = 0; i < m; i++) { NC[i] = C1 * lam1 * N1[i] + C2 * lam2 * (N1[i] - NW[i]); } /* fixed solution */ stat = fix_sol(rtk, sat1, sat2, NC, m); free(B1); free(N1); free(D); free(E); free(Q); free(NC); return stat; } /* resolve integer ambiguity for ppp -----------------------------------------*/ int pppamb(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav, const double *azel) { double elmask; int i, j, m = 0, stat = 0, *NW, *sat1, *sat2; if (n <= 0 || rtk->opt.ionoopt != IONOOPT_IFLC || rtk->opt.nf < 2) { return 0; } trace(3, "pppamb: time=%s n=%d\n", time_str(obs[0].time, 0), n); elmask = rtk->opt.elmaskar > 0.0 ? rtk->opt.elmaskar : rtk->opt.elmin; sat1 = imat(n * n, 1); sat2 = imat(n * n, 1); NW = imat(n * n, 1); /* average LC */ average_LC(rtk, obs, n, nav, azel); /* fix wide-lane ambiguity */ for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (!rtk->ssat[obs[i].sat - 1].vsat[0] || !rtk->ssat[obs[j].sat - 1].vsat[0] || azel[1 + i * 2] < elmask || azel[1 + j * 2] < elmask) { continue; } #if 0 /* test already fixed */ if (rtk->ambc[obs[i].sat-1].flags[obs[j].sat-1] && rtk->ambc[obs[j].sat-1].flags[obs[i].sat-1]) continue; #endif sat1[m] = obs[i].sat; sat2[m] = obs[j].sat; if (fix_amb_WL(rtk, nav, sat1[m], sat2[m], NW + m)) { m++; } } } /* fix narrow-lane ambiguity */ if (rtk->opt.modear == ARMODE_PPPAR) { stat = fix_amb_ROUND(rtk, sat1, sat2, NW, m); } else if (rtk->opt.modear == ARMODE_PPPAR_ILS) { stat = fix_amb_ILS(rtk, sat1, sat2, NW, m); } free(sat1); free(sat2); free(NW); return stat; } void pppoutsolstat(rtk_t *rtk, int level, FILE *fp) { ssat_t *ssat; double tow, pos[3], vel[3], acc[3]; int i, j, week, nfreq = 1; char id[32]; if (level <= 0 || !fp) { return; } trace(3, "pppoutsolstat:\n"); tow = time2gpst(rtk->sol.time, &week); /* receiver position */ fprintf(fp, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->x[0], rtk->x[1], rtk->x[2], 0.0, 0.0, 0.0); /* receiver velocity and acceleration */ if (rtk->opt.dynamics) { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->x + 3, vel); ecef2enu(pos, rtk->x + 6, acc); fprintf(fp, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], acc[0], acc[1], acc[2], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } /* receiver clocks */ i = IC_PPP(0, &rtk->opt); fprintf(fp, "$CLK,%d,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f\n", week, tow, rtk->sol.stat, 1, rtk->x[i] * 1E9 / SPEED_OF_LIGHT, rtk->x[i + 1] * 1E9 / SPEED_OF_LIGHT, 0.0, 0.0); /* tropospheric parameters */ if (rtk->opt.tropopt == TROPOPT_EST || rtk->opt.tropopt == TROPOPT_ESTG) { i = IT_PPP(&rtk->opt); fprintf(fp, "$TROP,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, 1, rtk->x[i], 0.0); } if (rtk->opt.tropopt == TROPOPT_ESTG) { i = IT_PPP(&rtk->opt); fprintf(fp, "$TRPG,%d,%.3f,%d,%d,%.5f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, 1, rtk->x[i + 1], rtk->x[i + 2], 0.0, 0.0); } if (rtk->sol.stat == SOLQ_NONE || level <= 1) { return; } /* residuals and status */ for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) { continue; } satno2id(i + 1, id); for (j = 0; j < nfreq; j++) { fprintf(fp, "$SAT,%d,%.3f,%s,%d,%.1f,%.1f,%.4f,%.4f,%d,%.0f,%d,%d,%d,%d,%d,%d\n", week, tow, id, j + 1, ssat->azel[0] * R2D, ssat->azel[1] * R2D, ssat->resp[j], ssat->resc[j], ssat->vsat[j], ssat->snr[j] * 0.25, ssat->fix[j], ssat->slip[j] & 3, ssat->lock[j], ssat->outc[j], ssat->slipc[j], ssat->rejc[j]); } } } /* exclude meas of eclipsing satellite (block IIA) ---------------------------*/ void testeclipse(const obsd_t *obs, int n, const nav_t *nav, double *rs) { double rsun[3], esun[3], r, ang, erpv[5] = {0}, cosa; int i, j; const char *type; trace(3, "testeclipse:\n"); /* unit vector of sun direction (ecef) */ sunmoonpos(gpst2utc(obs[0].time), erpv, rsun, nullptr, nullptr); if (normv3(rsun, esun) == 0) { trace(1, "Error computing the norm"); } for (i = 0; i < n; i++) { type = nav->pcvs[obs[i].sat - 1].type; if ((r = norm_rtk(rs + i * 6, 3)) <= 0.0) { continue; } #if 1 /* only block IIA */ if (*type && !strstr(type, "BLOCK IIA")) { continue; } #endif /* sun-earth-satellite angle */ cosa = dot(rs + i * 6, esun, 3) / r; cosa = cosa < -1.0 ? -1.0 : (cosa > 1.0 ? 1.0 : cosa); ang = acos(cosa); /* test eclipse */ if (ang < PI / 2.0 || r * sin(ang) > RE_WGS84) { continue; } trace(2, "eclipsing sat excluded %s sat=%2d\n", time_str(obs[0].time, 0), obs[i].sat); for (j = 0; j < 3; j++) { rs[j + i * 6] = 0.0; } } } /* measurement error variance ------------------------------------------------*/ double varerr(int sat __attribute__((unused)), int sys, double el, int type, const prcopt_t *opt) { double a, b, a2, b2, fact = 1.0; double sinel = sin(el); int i = sys == SYS_GLO ? 1 : (sys == SYS_GAL ? 2 : 0); /* extended error model */ if (type == 1 && opt->exterr.ena[0]) { /* code */ a = opt->exterr.cerr[i][0]; b = opt->exterr.cerr[i][1]; if (opt->ionoopt == IONOOPT_IFLC) { a2 = opt->exterr.cerr[i][2]; b2 = opt->exterr.cerr[i][3]; a = std::sqrt(std::pow(2.55, 2.0) * a * a + std::pow(1.55, 2.0) * a2 * a2); b = std::sqrt(std::pow(2.55, 2.0) * b * b + std::pow(1.55, 2.0) * b2 * b2); } } else if (type == 0 && opt->exterr.ena[1]) { /* phase */ a = opt->exterr.perr[i][0]; b = opt->exterr.perr[i][1]; if (opt->ionoopt == IONOOPT_IFLC) { a2 = opt->exterr.perr[i][2]; b2 = opt->exterr.perr[i][3]; a = std::sqrt(std::pow(2.55, 2.0) * a * a + std::pow(1.55, 2.0) * a2 * a2); b = std::sqrt(std::pow(2.55, 2.0) * b * b + std::pow(1.55, 2.0) * b2 * b2); } } else { /* normal error model */ if (type == 1) { fact *= opt->eratio[0]; } fact *= sys == SYS_GLO ? EFACT_GLO : (sys == SYS_SBS ? EFACT_SBS : EFACT_GPS); if (opt->ionoopt == IONOOPT_IFLC) { fact *= 3.0; } a = fact * opt->err[1]; b = fact * opt->err[2]; } return a * a + b * b / sinel / sinel; } /* initialize state and covariance -------------------------------------------*/ void initx(rtk_t *rtk, double xi, double var, int i) { int j; rtk->x[i] = xi; for (j = 0; j < rtk->nx; j++) { rtk->P[i + j * rtk->nx] = rtk->P[j + i * rtk->nx] = i == j ? var : 0.0; } } /* dual-frequency iono-free measurements -------------------------------------*/ int ifmeas(const obsd_t *obs, const nav_t *nav, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var) { const double *lam = nav->lam[obs->sat - 1]; double c1, c2, L1, L2, P1, P2, P1_C1, P2_C2, gamma; int i = 0, j = 1, k; trace(4, "ifmeas :\n"); /* L1-L2 for GPS/GLO/QZS, L1-L5 for GAL/SBS */ if (NFREQ >= 3 && (satsys(obs->sat, nullptr) & (SYS_GAL | SYS_SBS))) { j = 2; } if (NFREQ < 2 || lam[i] == 0.0 || lam[j] == 0.0) { return 0; } /* test snr mask */ if (testsnr(0, i, azel[1], obs->SNR[i] * 0.25, &opt->snrmask) || testsnr(0, j, azel[1], obs->SNR[j] * 0.25, &opt->snrmask)) { return 0; } gamma = std::pow(lam[j], 2.0) / std::pow(lam[i], 2.0); /* f1^2/f2^2 */ c1 = gamma / (gamma - 1.0); /* f1^2/(f1^2-f2^2) */ c2 = -1.0 / (gamma - 1.0); /* -f2^2/(f1^2-f2^2) */ L1 = obs->L[i] * lam[i]; /* cycle -> m */ L2 = obs->L[j] * lam[j]; P1 = obs->P[i]; P2 = obs->P[j]; P1_C1 = nav->cbias[obs->sat - 1][1]; P2_C2 = nav->cbias[obs->sat - 1][2]; if (opt->sateph == EPHOPT_LEX) { P1_C1 = nav->lexeph[obs->sat - 1].isc[0] * SPEED_OF_LIGHT; /* ISC_L1C/A */ } if (L1 == 0.0 || L2 == 0.0 || P1 == 0.0 || P2 == 0.0) { return 0; } /* iono-free phase with windup correction */ meas[0] = c1 * L1 + c2 * L2 - (c1 * lam[i] + c2 * lam[j]) * phw; /* iono-free code with dcb correction */ if (obs->code[i] == CODE_L1C) { P1 += P1_C1; /* C1->P1 */ } if (obs->code[j] == CODE_L2C) { P2 += P2_C2; /* C2->P2 */ } meas[1] = c1 * P1 + c2 * P2; var[1] = std::pow(ERR_CBIAS, 2.0); if (opt->sateph == EPHOPT_SBAS) { meas[1] -= P1_C1; /* sbas clock based C1 */ } /* gps-glonass h/w bias correction for code */ if (opt->exterr.ena[3] && satsys(obs->sat, nullptr) == SYS_GLO) { meas[1] += c1 * opt->exterr.gpsglob[0] + c2 * opt->exterr.gpsglob[1]; } /* antenna phase center variation correction */ for (k = 0; k < 2; k++) { if (dants) { meas[k] -= c1 * dants[i] + c2 * dants[j]; } if (dantr) { meas[k] -= c1 * dantr[i] + c2 * dantr[j]; } } return 1; } /* get tgd parameter (m) -----------------------------------------------------*/ double gettgd_ppp(int sat, const nav_t *nav) { int i; for (i = 0; i < nav->n; i++) { if (nav->eph[i].sat != sat) { continue; } return SPEED_OF_LIGHT * nav->eph[i].tgd[0]; } return 0.0; } /* slant ionospheric delay ---------------------------------------------------*/ int corr_ion(gtime_t time, const nav_t *nav, int sat __attribute__((unused)), const double *pos, const double *azel, int ionoopt, double *ion, double *var, int *brk __attribute__((unused))) { #ifdef EXTSTEC double rate; #endif /* sbas ionosphere model */ if (ionoopt == IONOOPT_SBAS) { return sbsioncorr(time, nav, pos, azel, ion, var); } /* ionex tec model */ if (ionoopt == IONOOPT_TEC) { return iontec(time, nav, pos, azel, 1, ion, var); } #ifdef EXTSTEC /* slant tec model */ if (ionoopt == IONOOPT_STEC) { return stec_ion(time, nav, sat, pos, azel, ion, &rate, var, brk); } #endif /* broadcast model */ if (ionoopt == IONOOPT_BRDC) { *ion = ionmodel(time, nav->ion_gps, pos, azel); *var = std::pow(*ion * ERR_BRDCI, 2.0); return 1; } /* ionosphere model off */ *ion = 0.0; *var = VAR_IONO_OFF; return 1; } /* ionosphere and antenna corrected measurements -----------------------------*/ int corrmeas(const obsd_t *obs, const nav_t *nav, const double *pos, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var, int *brk) { const double *lam = nav->lam[obs->sat - 1]; double ion = 0.0, L1, P1, PC, P1_P2, P1_C1, vari, gamma; int i; trace(4, "corrmeas:\n"); meas[0] = meas[1] = var[0] = var[1] = 0.0; /* iono-free LC */ if (opt->ionoopt == IONOOPT_IFLC) { return ifmeas(obs, nav, azel, opt, dantr, dants, phw, meas, var); } if (lam[0] == 0.0 || obs->L[0] == 0.0 || obs->P[0] == 0.0) { return 0; } if (testsnr(0, 0, azel[1], obs->SNR[0] * 0.25, &opt->snrmask)) { return 0; } L1 = obs->L[0] * lam[0]; P1 = obs->P[0]; /* dcb correction */ gamma = std::pow(lam[1] / lam[0], 2.0); /* f1^2/f2^2 */ P1_P2 = nav->cbias[obs->sat - 1][0]; P1_C1 = nav->cbias[obs->sat - 1][1]; if (P1_P2 == 0.0 && (satsys(obs->sat, nullptr) & (SYS_GPS | SYS_GAL | SYS_QZS))) { P1_P2 = (1.0 - gamma) * gettgd_ppp(obs->sat, nav); } if (obs->code[0] == CODE_L1C) { P1 += P1_C1; /* C1->P1 */ } PC = P1 - P1_P2 / (1.0 - gamma); /* P1->PC */ /* slant ionospheric delay L1 (m) */ if (!corr_ion(obs->time, nav, obs->sat, pos, azel, opt->ionoopt, &ion, &vari, brk)) { trace(2, "iono correction error: time=%s sat=%2d ionoopt=%d\n", time_str(obs->time, 2), obs->sat, opt->ionoopt); return 0; } /* ionosphere and windup corrected phase and code */ meas[0] = L1 + ion - lam[0] * phw; meas[1] = PC - ion; var[0] += vari; var[1] += vari + std::pow(ERR_CBIAS, 2.0); /* antenna phase center variation correction */ for (i = 0; i < 2; i++) { if (dants) { meas[i] -= dants[0]; } if (dantr) { meas[i] -= dantr[0]; } } return 1; } /* L1/L2 geometry-free phase measurement -------------------------------------*/ double gfmeas(const obsd_t *obs, const nav_t *nav) { const double *lam = nav->lam[obs->sat - 1]; if (lam[0] == 0.0 || lam[1] == 0.0 || obs->L[0] == 0.0 || obs->L[1] == 0.0) { return 0.0; } return lam[0] * obs->L[0] - lam[1] * obs->L[1]; } /* temporal update of position -----------------------------------------------*/ void udpos_ppp(rtk_t *rtk) { int i; trace(3, "udpos_ppp:\n"); /* fixed mode */ if (rtk->opt.mode == PMODE_PPP_FIXED) { for (i = 0; i < 3; i++) { initx(rtk, rtk->opt.ru[i], 1E-8, i); } return; } /* initialize position for first epoch */ if (norm_rtk(rtk->x, 3) <= 0.0) { for (i = 0; i < 3; i++) { initx(rtk, rtk->sol.rr[i], VAR_POS_PPP, i); } } /* static ppp mode */ if (rtk->opt.mode == PMODE_PPP_STATIC) { return; } /* kinmatic mode without dynamics */ for (i = 0; i < 3; i++) { initx(rtk, rtk->sol.rr[i], VAR_POS_PPP, i); } } /* temporal update of clock --------------------------------------------------*/ void udclk_ppp(rtk_t *rtk) { double dtr; int i; trace(3, "udclk_ppp:\n"); /* initialize every epoch for clock (white noise) */ for (i = 0; i < NSYS; i++) { if (rtk->opt.sateph == EPHOPT_PREC) { /* time of prec ephemeris is based gpst */ /* negelect receiver inter-system bias */ dtr = rtk->sol.dtr[0]; } else { dtr = i == 0 ? rtk->sol.dtr[0] : rtk->sol.dtr[0] + rtk->sol.dtr[i]; } initx(rtk, SPEED_OF_LIGHT * dtr, VAR_CLK, IC_PPP(i, &rtk->opt)); } } /* temporal update of tropospheric parameters --------------------------------*/ void udtrop_ppp(rtk_t *rtk) { double pos[3], azel[] = {0.0, PI / 2.0}, ztd, var; int i = IT_PPP(&rtk->opt), j; trace(3, "udtrop_ppp:\n"); if (rtk->x[i] == 0.0) { ecef2pos(rtk->sol.rr, pos); ztd = sbstropcorr(rtk->sol.time, pos, azel, &var); initx(rtk, ztd, var, i); if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (j = 0; j < 2; j++) { initx(rtk, 1E-6, VAR_GRA_PPP, ++i); } } } else { rtk->P[i * (1 + rtk->nx)] += std::pow(rtk->opt.prn[2], 2.0) * fabs(rtk->tt); if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (j = 0; j < 2; j++) { rtk->P[++i * (1 + rtk->nx)] += std::pow(rtk->opt.prn[2] * 0.1, 2.0) * fabs(rtk->tt); } } } } /* detect cycle slip by LLI --------------------------------------------------*/ void detslp_ll(rtk_t *rtk, const obsd_t *obs, int n) { int i, j; trace(3, "detslp_ll: n=%d\n", n); for (i = 0; i < n && i < MAXOBS; i++) { for (j = 0; j < rtk->opt.nf; j++) { if (obs[i].L[j] == 0.0 || !(obs[i].LLI[j] & 3)) { continue; } trace(3, "detslp_ll: slip detected sat=%2d f=%d\n", obs[i].sat, j + 1); rtk->ssat[obs[i].sat - 1].slip[j] = 1; } } } /* detect cycle slip by geometry free phase jump -----------------------------*/ void detslp_gf(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { double g0, g1; int i, j; trace(3, "detslp_gf: n=%d\n", n); for (i = 0; i < n && i < MAXOBS; i++) { if ((g1 = gfmeas(obs + i, nav)) == 0.0) { continue; } g0 = rtk->ssat[obs[i].sat - 1].gf; rtk->ssat[obs[i].sat - 1].gf = g1; trace(4, "detslip_gf: sat=%2d gf0=%8.3f gf1=%8.3f\n", obs[i].sat, g0, g1); if (g0 != 0.0 && fabs(g1 - g0) > rtk->opt.thresslip) { trace(3, "detslip_gf: slip detected sat=%2d gf=%8.3f->%8.3f\n", obs[i].sat, g0, g1); for (j = 0; j < rtk->opt.nf; j++) { rtk->ssat[obs[i].sat - 1].slip[j] |= 1; } } } } /* temporal update of phase biases -------------------------------------------*/ void udbias_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { double meas[2], var[2], bias[MAXOBS] = {0}, offset = 0.0, pos[3] = {0}; int i, j, k, sat, brk = 0; trace(3, "udbias : n=%d\n", n); for (i = 0; i < MAXSAT; i++) { for (j = 0; j < rtk->opt.nf; j++) { rtk->ssat[i].slip[j] = 0; } } /* detect cycle slip by LLI */ detslp_ll(rtk, obs, n); /* detect cycle slip by geometry-free phase jump */ detslp_gf(rtk, obs, n, nav); /* reset phase-bias if expire obs outage counter */ for (i = 0; i < MAXSAT; i++) { if (++rtk->ssat[i].outc[0] > static_cast(rtk->opt.maxout)) { initx(rtk, 0.0, 0.0, IB_PPP(i + 1, &rtk->opt)); } } ecef2pos(rtk->sol.rr, pos); for (i = k = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; j = IB_PPP(sat, &rtk->opt); if (!corrmeas(obs + i, nav, pos, rtk->ssat[sat - 1].azel, &rtk->opt, nullptr, nullptr, 0.0, meas, var, &brk)) { continue; } if (brk) { rtk->ssat[sat - 1].slip[0] = 1; trace(2, "%s: sat=%2d correction break\n", time_str(obs[i].time, 0), sat); } bias[i] = meas[0] - meas[1]; if (rtk->x[j] == 0.0 || rtk->ssat[sat - 1].slip[0] || rtk->ssat[sat - 1].slip[1]) { continue; } offset += bias[i] - rtk->x[j]; k++; } /* correct phase-code jump to enssure phase-code coherency */ if (k >= 2 && fabs(offset / k) > 0.0005 * SPEED_OF_LIGHT) { for (i = 0; i < MAXSAT; i++) { j = IB_PPP(i + 1, &rtk->opt); if (rtk->x[j] != 0.0) { rtk->x[j] += offset / k; } } trace(2, "phase-code jump corrected: %s n=%2d dt=%12.9fs\n", time_str(rtk->sol.time, 0), k, offset / k / SPEED_OF_LIGHT); } for (i = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; j = IB_PPP(sat, &rtk->opt); rtk->P[j + j * rtk->nx] += std::pow(rtk->opt.prn[0], 2.0) * fabs(rtk->tt); if (rtk->x[j] != 0.0 && !rtk->ssat[sat - 1].slip[0] && !rtk->ssat[sat - 1].slip[1]) { continue; } if (bias[i] == 0.0) { continue; } /* reinitialize phase-bias if detecting cycle slip */ initx(rtk, bias[i], VAR_BIAS, IB_PPP(sat, &rtk->opt)); trace(5, "udbias_ppp: sat=%2d bias=%.3f\n", sat, meas[0] - meas[1]); } } /* temporal update of states --------------------------------------------------*/ void udstate_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { trace(3, "udstate_ppp: n=%d\n", n); /* temporal update of position */ udpos_ppp(rtk); /* temporal update of clock */ udclk_ppp(rtk); /* temporal update of tropospheric parameters */ if (rtk->opt.tropopt >= TROPOPT_EST) { udtrop_ppp(rtk); } /* temporal update of phase-bias */ udbias_ppp(rtk, obs, n, nav); } /* satellite antenna phase center variation ----------------------------------*/ void satantpcv(const double *rs, const double *rr, const pcv_t *pcv, double *dant) { double ru[3], rz[3], eu[3], ez[3], nadir, cosa; int i; for (i = 0; i < 3; i++) { ru[i] = rr[i] - rs[i]; rz[i] = -rs[i]; } if (!normv3(ru, eu) || !normv3(rz, ez)) { return; } cosa = dot(eu, ez, 3); cosa = cosa < -1.0 ? -1.0 : (cosa > 1.0 ? 1.0 : cosa); nadir = acos(cosa); antmodel_s(pcv, nadir, dant); } /* precise tropospheric model ------------------------------------------------*/ double prectrop(gtime_t time, const double *pos, const double *azel, const prcopt_t *opt, const double *x, double *dtdx, double *var) { const double zazel[] = {0.0, PI / 2.0}; double zhd, m_h, m_w, cotz, grad_n, grad_e; /* zenith hydrostatic delay */ zhd = tropmodel(time, pos, zazel, 0.0); /* mapping function */ m_h = tropmapf(time, pos, azel, &m_w); if ((opt->tropopt == TROPOPT_ESTG || opt->tropopt == TROPOPT_CORG) && azel[1] > 0.0) { /* m_w=m_0+m_0*cot(el)*(Gn*cos(az)+Ge*sin(az)): ref [6] */ cotz = 1.0 / tan(azel[1]); grad_n = m_w * cotz * cos(azel[0]); grad_e = m_w * cotz * sin(azel[0]); m_w += grad_n * x[1] + grad_e * x[2]; dtdx[1] = grad_n * (x[0] - zhd); dtdx[2] = grad_e * (x[0] - zhd); } dtdx[0] = m_w; *var = std::pow(0.01, 2.0); return m_h * zhd + m_w * (x[0] - zhd); } /* phase and code residuals --------------------------------------------------*/ int res_ppp(int iter __attribute__((unused)), const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, rtk_t *rtk, double *v, double *H, double *R, double *azel) { prcopt_t *opt = &rtk->opt; double r, rr[3], disp[3], pos[3], e[3], meas[2], dtdx[3], dantr[NFREQ] = {0}; double dants[NFREQ] = {0}, var[MAXOBS * 2], dtrp = 0.0, vart = 0.0, varm[2] = {0}; int i, j, k, sat, sys, nv = 0, nx = rtk->nx, brk, tideopt; trace(3, "res_ppp : n=%d nx=%d\n", n, nx); for (i = 0; i < MAXSAT; i++) { rtk->ssat[i].vsat[0] = 0; } for (i = 0; i < 3; i++) { rr[i] = x[i]; } /* earth tides correction */ if (opt->tidecorr) { tideopt = opt->tidecorr == 1 ? 1 : 7; /* 1:solid, 2:solid+otl+pole */ tidedisp(gpst2utc(obs[0].time), rr, tideopt, &nav->erp, opt->odisp[0], disp); for (i = 0; i < 3; i++) { rr[i] += disp[i]; } } ecef2pos(rr, pos); for (i = 0; i < n && i < MAXOBS; i++) { sat = obs[i].sat; if (!(sys = satsys(sat, nullptr)) || !rtk->ssat[sat - 1].vs) { continue; } /* geometric distance/azimuth/elevation angle */ if ((r = geodist(rs + i * 6, rr, e)) <= 0.0 || satazel(pos, e, azel + i * 2) < opt->elmin) { continue; } /* excluded satellite? */ if (satexclude(obs[i].sat, svh[i], opt)) { continue; } /* tropospheric delay correction */ if (opt->tropopt == TROPOPT_SAAS) { dtrp = tropmodel(obs[i].time, pos, azel + i * 2, REL_HUMI); vart = std::pow(ERR_SAAS, 2.0); } else if (opt->tropopt == TROPOPT_SBAS) { dtrp = sbstropcorr(obs[i].time, pos, azel + i * 2, &vart); } else if (opt->tropopt == TROPOPT_EST || opt->tropopt == TROPOPT_ESTG) { dtrp = prectrop(obs[i].time, pos, azel + i * 2, opt, x + IT_PPP(opt), dtdx, &vart); } else if (opt->tropopt == TROPOPT_COR || opt->tropopt == TROPOPT_CORG) { dtrp = prectrop(obs[i].time, pos, azel + i * 2, opt, x, dtdx, &vart); } /* satellite antenna model */ if (opt->posopt[0]) { satantpcv(rs + i * 6, rr, nav->pcvs + sat - 1, dants); } /* receiver antenna model */ antmodel(opt->pcvr, opt->antdel[0], azel + i * 2, opt->posopt[1], dantr); /* phase windup correction */ if (opt->posopt[2]) { windupcorr(rtk->sol.time, rs + i * 6, rr, &rtk->ssat[sat - 1].phw); } /* ionosphere and antenna phase corrected measurements */ if (!corrmeas(obs + i, nav, pos, azel + i * 2, &rtk->opt, dantr, dants, rtk->ssat[sat - 1].phw, meas, varm, &brk)) { continue; } /* satellite clock and tropospheric delay */ r += -SPEED_OF_LIGHT * dts[i * 2] + dtrp; trace(5, "sat=%2d azel=%6.1f %5.1f dtrp=%.3f dantr=%6.3f %6.3f dants=%6.3f %6.3f phw=%6.3f\n", sat, azel[i * 2] * R2D, azel[1 + i * 2] * R2D, dtrp, dantr[0], dantr[1], dants[0], dants[1], rtk->ssat[sat - 1].phw); for (j = 0; j < 2; j++) { /* for phase and code */ if (meas[j] == 0.0) { continue; } for (k = 0; k < nx; k++) { H[k + nx * nv] = 0.0; } v[nv] = meas[j] - r; for (k = 0; k < 3; k++) { H[k + nx * nv] = -e[k]; } if (sys != SYS_GLO) { v[nv] -= x[IC_PPP(0, opt)]; H[IC_PPP(0, opt) + nx * nv] = 1.0; } else { v[nv] -= x[IC_PPP(1, opt)]; H[IC_PPP(1, opt) + nx * nv] = 1.0; } if (opt->tropopt >= TROPOPT_EST) { for (k = 0; k < (opt->tropopt >= TROPOPT_ESTG ? 3 : 1); k++) { H[IT_PPP(opt) + k + nx * nv] = dtdx[k]; } } if (j == 0) { v[nv] -= x[IB_PPP(obs[i].sat, opt)]; H[IB_PPP(obs[i].sat, opt) + nx * nv] = 1.0; } var[nv] = varerr(obs[i].sat, sys, azel[1 + i * 2], j, opt) + varm[j] + vare[i] + vart; if (j == 0) { rtk->ssat[sat - 1].resc[0] = v[nv]; } else { rtk->ssat[sat - 1].resp[0] = v[nv]; } /* test innovation */ #if 0 if (opt->maxinno>0.0 && fabs(v[nv])>opt->maxinno) { #else if (opt->maxinno > 0.0 && fabs(v[nv]) > opt->maxinno && sys != SYS_GLO) { #endif trace(2, "ppp outlier rejected %s sat=%2d type=%d v=%.3f\n", time_str(obs[i].time, 0), sat, j, v[nv]); rtk->ssat[sat - 1].rejc[0]++; continue; } if (j == 0) { rtk->ssat[sat - 1].vsat[0] = 1; } nv++; } } for (i = 0; i < nv; i++) { for (j = 0; j < nv; j++) { R[i + j * nv] = i == j ? var[i] : 0.0; } } trace(5, "x=\n"); tracemat(5, x, 1, nx, 8, 3); trace(5, "v=\n"); tracemat(5, v, 1, nv, 8, 3); trace(5, "H=\n"); tracemat(5, H, nx, nv, 8, 3); trace(5, "R=\n"); tracemat(5, R, nv, nv, 8, 5); return nv; } /* number of estimated states ------------------------------------------------*/ int pppnx(const prcopt_t *opt) { return NX_PPP(opt); } /* precise point positioning -------------------------------------------------*/ void pppos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { const prcopt_t *opt = &rtk->opt; double *rs, *dts, *var, *v, *H, *R, *azel, *xp, *Pp; int i, nv, info, svh[MAXOBS], stat = SOLQ_SINGLE; trace(3, "pppos : nx=%d n=%d\n", rtk->nx, n); rs = mat(6, n); dts = mat(2, n); var = mat(1, n); azel = zeros(2, n); for (i = 0; i < MAXSAT; i++) { rtk->ssat[i].fix[0] = 0; } /* temporal update of states */ udstate_ppp(rtk, obs, n, nav); trace(4, "x(0)="); tracemat(4, rtk->x, 1, NR_PPP(opt), 13, 4); /* satellite positions and clocks */ satposs(obs[0].time, obs, n, nav, rtk->opt.sateph, rs, dts, var, svh); /* exclude measurements of eclipsing satellite */ if (rtk->opt.posopt[3]) { testeclipse(obs, n, nav, rs); } xp = mat(rtk->nx, 1); Pp = zeros(rtk->nx, rtk->nx); matcpy(xp, rtk->x, rtk->nx, 1); nv = n * rtk->opt.nf * 2; v = mat(nv, 1); H = mat(rtk->nx, nv); R = mat(nv, nv); for (i = 0; i < rtk->opt.niter; i++) { /* phase and code residuals */ if ((nv = res_ppp(i, obs, n, rs, dts, var, svh, nav, xp, rtk, v, H, R, azel)) <= 0) { break; } /* measurement update */ matcpy(Pp, rtk->P, rtk->nx, rtk->nx); if ((info = filter(xp, Pp, H, v, R, rtk->nx, nv))) { trace(2, "ppp filter error %s info=%d\n", time_str(rtk->sol.time, 0), info); break; } trace(4, "x(%d)=", i + 1); tracemat(4, xp, 1, NR_PPP(opt), 13, 4); stat = SOLQ_PPP; } if (stat == SOLQ_PPP) { /* postfit residuals */ res_ppp(1, obs, n, rs, dts, var, svh, nav, xp, rtk, v, H, R, azel); /* update state and covariance matrix */ matcpy(rtk->x, xp, rtk->nx, 1); matcpy(rtk->P, Pp, rtk->nx, rtk->nx); /* ambiguity resolution in ppp */ if (opt->modear == ARMODE_PPPAR || opt->modear == ARMODE_PPPAR_ILS) { if (pppamb(rtk, obs, n, nav, azel)) { stat = SOLQ_FIX; } } /* update solution status */ rtk->sol.ns = 0; for (i = 0; i < n && i < MAXOBS; i++) { if (!rtk->ssat[obs[i].sat - 1].vsat[0]) { continue; } rtk->ssat[obs[i].sat - 1].lock[0]++; rtk->ssat[obs[i].sat - 1].outc[0] = 0; rtk->ssat[obs[i].sat - 1].fix[0] = 4; rtk->sol.ns++; } rtk->sol.stat = stat; for (i = 0; i < 3; i++) { rtk->sol.rr[i] = rtk->x[i]; rtk->sol.qr[i] = static_cast(rtk->P[i + i * rtk->nx]); } rtk->sol.qr[3] = static_cast(rtk->P[1]); rtk->sol.qr[4] = static_cast(rtk->P[2 + rtk->nx]); rtk->sol.qr[5] = static_cast(rtk->P[2]); rtk->sol.dtr[0] = rtk->x[IC_PPP(0, opt)]; rtk->sol.dtr[1] = rtk->x[IC_PPP(1, opt)] - rtk->x[IC_PPP(0, opt)]; for (i = 0; i < n && i < MAXOBS; i++) { rtk->ssat[obs[i].sat - 1].snr[0] = MIN_PPP(obs[i].SNR[0], obs[i].SNR[1]); } for (i = 0; i < MAXSAT; i++) { if (rtk->ssat[i].slip[0] & 3) { rtk->ssat[i].slipc[0]++; } } } free(rs); free(dts); free(var); free(azel); free(xp); free(Pp); free(v); free(H); free(R); } src/algorithms/libs/rtklib/rtklib_ppp.h000066400000000000000000000152671352176506000205750ustar00rootroot00000000000000/*! * \file rtklib_ppp.h * \brief Precise Point Positioning * \authors
    *
  • 2007-2008, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2008, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_PPP_H_ #define GNSS_SDR_RTKLIB_PPP_H_ #include "rtklib.h" #define MIN_PPP(x, y) ((x) <= (y) ? (x) : (y)) #define ROUND_PPP(x) (int)floor((x) + 0.5) #define SWAP_I(x, y) \ do \ { \ int _z = x; \ x = y; \ y = _z; \ } \ while (0) #define SWAP_D(x, y) \ do \ { \ double _z = x; \ x = y; \ y = _z; \ } \ while (0) const double MIN_ARC_GAP = 300.0; /* min arc gap (s) */ const double CONST_AMB = 0.001; /* constraint to fixed ambiguity */ const double THRES_RES = 0.3; /* threshold of residuals test (m) */ const double LOG_PI = 1.14472988584940017; /* log(pi) */ const double SQRT2 = 1.41421356237309510; /* sqrt(2) */ const double VAR_POS_PPP = std::pow(100.0, 2.0); /* init variance receiver position (m^2) */ const double VAR_CLK = std::pow(100.0, 2.0); /* init variance receiver clock (m^2) */ const double VAR_ZTD = std::pow(0.3, 2.0); /* init variance ztd (m^2) */ const double VAR_GRA_PPP = std::pow(0.001, 2.0); /* init variance gradient (m^2) */ const double VAR_BIAS = std::pow(100.0, 2.0); /* init variance phase-bias (m^2) */ const double VAR_IONO_OFF = std::pow(10.0, 2.0); /* variance of iono-model-off */ /* functions originally included in RTKLIB/src/ppp_ar.c v2.4.2*/ double lam_LC(int i, int j, int k); double L_LC(int i, int j, int k, const double *L); double P_LC(int i, int j, int k, const double *P); double var_LC(int i, int j, int k, double sig); double q_gamma(double a, double x, double log_gamma_a); double p_gamma(double a, double x, double log_gamma_a); double f_erfc(double x); double conffunc(int N, double B, double sig); void average_LC(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav, const double *azel); int fix_amb_WL(rtk_t *rtk, const nav_t *nav, int sat1, int sat2, int *NW); int is_depend(int sat1, int sat2, int *flgs, int *max_flg); int sel_amb(int *sat1, int *sat2, double *N, double *var, int n); int fix_sol(rtk_t *rtk, const int *sat1, const int *sat2, const double *NC, int n); int fix_amb_ROUND(rtk_t *rtk, int *sat1, int *sat2, const int *NW, int n); int fix_amb_ILS(rtk_t *rtk, int *sat1, int *sat2, int *NW, int n); int pppamb(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav, const double *azel); /* functions originally included in RTKLIB/src/ppp.c v2.4.2 */ void pppoutsolstat(rtk_t *rtk, int level, FILE *fp); void testeclipse(const obsd_t *obs, int n, const nav_t *nav, double *rs); double varerr(int sat, int sys, double el, int type, const prcopt_t *opt); void initx(rtk_t *rtk, double xi, double var, int i); int ifmeas(const obsd_t *obs, const nav_t *nav, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var); double gettgd_ppp(int sat, const nav_t *nav); int corr_ion(gtime_t time, const nav_t *nav, int sat, const double *pos, const double *azel, int ionoopt, double *ion, double *var, int *brk); int corrmeas(const obsd_t *obs, const nav_t *nav, const double *pos, const double *azel, const prcopt_t *opt, const double *dantr, const double *dants, double phw, double *meas, double *var, int *brk); double gfmeas(const obsd_t *obs, const nav_t *nav); void udpos_ppp(rtk_t *rtk); void udclk_ppp(rtk_t *rtk); void udtrop_ppp(rtk_t *rtk); void detslp_ll(rtk_t *rtk, const obsd_t *obs, int n); void detslp_gf(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav); void udbias_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav); void udstate_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav); void satantpcv(const double *rs, const double *rr, const pcv_t *pcv, double *dant); double prectrop(gtime_t time, const double *pos, const double *azel, const prcopt_t *opt, const double *x, double *dtdx, double *var); int res_ppp(int iter, const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, rtk_t *rtk, double *v, double *H, double *R, double *azel); int pppnx(const prcopt_t *opt); void pppos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav); #endif src/algorithms/libs/rtklib/rtklib_preceph.cc000066400000000000000000001060571352176506000215600ustar00rootroot00000000000000/*! * \file rtklib_preceph.cc * \brief precise ephemeris and clock functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * References : * [1] S.Hilla, The Extended Standard Product 3 Orbit Format (SP3-c), * 12 February, 2007 * [2] J.Ray, W.Gurtner, RINEX Extensions to Handle Clock Information, * 27 August, 1998 * [3] D.D.McCarthy, IERS Technical Note 21, IERS Conventions 1996, July 1996 * [4] D.A.Vallado, Fundamentals of Astrodynamics and Applications 2nd ed, * Space Technology Library, 2004 * *----------------------------------------------------------------------------*/ #include "rtklib_preceph.h" #include "rtklib_rtkcmn.h" #include /* satellite code to satellite system ----------------------------------------*/ int code2sys(char code) { if (code == 'G' || code == ' ') { return SYS_GPS; } if (code == 'R') { return SYS_GLO; } if (code == 'E') { return SYS_GAL; /* extension to sp3-c */ } if (code == 'J') { return SYS_QZS; /* extension to sp3-c */ } if (code == 'C') { return SYS_BDS; /* extension to sp3-c */ } if (code == 'L') { return SYS_LEO; /* extension to sp3-c */ } return SYS_NONE; } /* read sp3 header -----------------------------------------------------------*/ int readsp3h(FILE *fp, gtime_t *time, char *type, int *sats, double *bfact, char *tsys) { int i, j, k = 0, ns = 0, sys, prn; char buff[1024]; trace(3, "readsp3h:\n"); for (i = 0; i < 22; i++) { if (!fgets(buff, sizeof(buff), fp)) { break; } if (i == 0) { *type = buff[2]; if (str2time(buff, 3, 28, time)) { return 0; } } else if (2 <= i && i <= 6) { if (i == 2) { ns = static_cast(str2num(buff, 4, 2)); } for (j = 0; j < 17 && k < ns; j++) { sys = code2sys(buff[9 + 3 * j]); prn = static_cast(str2num(buff, 10 + 3 * j, 2)); if (k < MAXSAT) { sats[k++] = satno(sys, prn); } } } else if (i == 12) { strncpy(tsys, buff + 9, 3); tsys[3] = '\0'; } else if (i == 14) { bfact[0] = str2num(buff, 3, 10); bfact[1] = str2num(buff, 14, 12); } } return ns; } /* add precise ephemeris -----------------------------------------------------*/ int addpeph(nav_t *nav, peph_t *peph) { peph_t *nav_peph; if (nav->ne >= nav->nemax) { nav->nemax += 256; if (!(nav_peph = static_cast(realloc(nav->peph, sizeof(peph_t) * nav->nemax)))) { trace(1, "readsp3b malloc error n=%d\n", nav->nemax); free(nav->peph); nav->peph = nullptr; nav->ne = nav->nemax = 0; return 0; } nav->peph = nav_peph; } nav->peph[nav->ne++] = *peph; return 1; } /* read sp3 body -------------------------------------------------------------*/ void readsp3b(FILE *fp, char type, int *sats __attribute__((unused)), int ns, const double *bfact, char *tsys, int index, int opt, nav_t *nav) { peph_t peph; gtime_t time; double val, std, base; int i, j, sat, sys, prn, n = ns * (type == 'P' ? 1 : 2), pred_o, pred_c, v; char buff[1024]; trace(3, "readsp3b: type=%c ns=%d index=%d opt=%d\n", type, ns, index, opt); while (fgets(buff, sizeof(buff), fp)) { if (!strncmp(buff, "EOF", 3)) { break; } if (buff[0] != '*' || str2time(buff, 3, 28, &time)) { trace(2, "sp3 invalid epoch %31.31s\n", buff); continue; } if (!strcmp(tsys, "UTC")) { time = utc2gpst(time); /* utc->gpst */ } peph.time = time; peph.index = index; for (i = 0; i < MAXSAT; i++) { for (j = 0; j < 4; j++) { peph.pos[i][j] = 0.0; peph.std[i][j] = 0.0F; peph.vel[i][j] = 0.0; peph.vst[i][j] = 0.0F; } for (j = 0; j < 3; j++) { peph.cov[i][j] = 0.0F; peph.vco[i][j] = 0.0F; } } for (i = pred_o = pred_c = v = 0; i < n && fgets(buff, sizeof(buff), fp); i++) { if (strlen(buff) < 4 || (buff[0] != 'P' && buff[0] != 'V')) { continue; } sys = buff[1] == ' ' ? SYS_GPS : code2sys(buff[1]); prn = static_cast(str2num(buff, 2, 2)); if (sys == SYS_SBS) { prn += 100; } else if (sys == SYS_QZS) { prn += 192; /* extension to sp3-c */ } if (!(sat = satno(sys, prn))) { continue; } if (buff[0] == 'P') { pred_c = strlen(buff) >= 76 && buff[75] == 'P'; pred_o = strlen(buff) >= 80 && buff[79] == 'P'; } for (j = 0; j < 4; j++) { /* read option for predicted value */ if (j < 3 && (opt & 1) && pred_o) { continue; } if (j < 3 && (opt & 2) && !pred_o) { continue; } if (j == 3 && (opt & 1) && pred_c) { continue; } if (j == 3 && (opt & 2) && !pred_c) { continue; } val = str2num(buff, 4 + j * 14, 14); std = str2num(buff, 61 + j * 3, j < 3 ? 2 : 3); if (buff[0] == 'P') { /* position */ if (val != 0.0 && fabs(val - 999999.999999) >= 1e-6) { peph.pos[sat - 1][j] = val * (j < 3 ? 1000.0 : 1e-6); v = 1; /* valid epoch */ } if ((base = bfact[j < 3 ? 0 : 1]) > 0.0 && std > 0.0) { peph.std[sat - 1][j] = static_cast(std::pow(base, std) * (j < 3 ? 1e-3 : 1e-12)); } } else if (v) { /* velocity */ if (val != 0.0 && fabs(val - 999999.999999) >= 1e-6) { peph.vel[sat - 1][j] = val * (j < 3 ? 0.1 : 1e-10); } if ((base = bfact[j < 3 ? 0 : 1]) > 0.0 && std > 0.0) { peph.vst[sat - 1][j] = static_cast(std::pow(base, std) * (j < 3 ? 1e-7 : 1e-16)); } } } } if (v) { if (!addpeph(nav, &peph)) { return; } } } } /* compare precise ephemeris -------------------------------------------------*/ int cmppeph(const void *p1, const void *p2) { auto *q1 = (peph_t *)p1, *q2 = (peph_t *)p2; double tt = timediff(q1->time, q2->time); return tt < -1e-9 ? -1 : (tt > 1e-9 ? 1 : q1->index - q2->index); } /* combine precise ephemeris -------------------------------------------------*/ void combpeph(nav_t *nav, int opt) { int i, j, k, m; trace(3, "combpeph: ne=%d\n", nav->ne); qsort(nav->peph, nav->ne, sizeof(peph_t), cmppeph); if (opt & 4) { return; } for (i = 0, j = 1; j < nav->ne; j++) { if (fabs(timediff(nav->peph[i].time, nav->peph[j].time)) < 1e-9) { for (k = 0; k < MAXSAT; k++) { if (norm_rtk(nav->peph[j].pos[k], 4) <= 0.0) { continue; } for (m = 0; m < 4; m++) { nav->peph[i].pos[k][m] = nav->peph[j].pos[k][m]; } for (m = 0; m < 4; m++) { nav->peph[i].std[k][m] = nav->peph[j].std[k][m]; } for (m = 0; m < 4; m++) { nav->peph[i].vel[k][m] = nav->peph[j].vel[k][m]; } for (m = 0; m < 4; m++) { nav->peph[i].vst[k][m] = nav->peph[j].vst[k][m]; } } } else if (++i < j) { nav->peph[i] = nav->peph[j]; } } nav->ne = i + 1; trace(4, "combpeph: ne=%d\n", nav->ne); } /* read sp3 precise ephemeris file --------------------------------------------- * read sp3 precise ephemeris/clock files and set them to navigation data * args : char *file I sp3-c precise ephemeris file * (wind-card * is expanded) * nav_t *nav IO navigation data * int opt I options (1: only observed + 2: only predicted + * 4: not combined) * return : none * notes : see ref [1] * precise ephemeris is appended and combined * nav->peph and nav->ne must by properly initialized before calling the * function * only files with extensions of .sp3, .SP3, .eph* and .EPH* are read *-----------------------------------------------------------------------------*/ void readsp3(const char *file, nav_t *nav, int opt) { FILE *fp; gtime_t time = {0, 0}; double bfact[2] = {}; int i, j, n, ns, sats[MAXSAT] = {}; char *efiles[MAXEXFILE], *ext, type = ' ', tsys[4] = ""; trace(3, "readpephs: file=%s\n", file); for (i = 0; i < MAXEXFILE; i++) { if (!(efiles[i] = static_cast(malloc(1024)))) { for (i--; i >= 0; i--) { free(efiles[i]); } return; } } /* expand wild card in file path */ n = expath(file, efiles, MAXEXFILE); for (i = j = 0; i < n; i++) { if (!(ext = strrchr(efiles[i], '.'))) { continue; } if (!strstr(ext + 1, "sp3") && !strstr(ext + 1, ".SP3") && !strstr(ext + 1, "eph") && !strstr(ext + 1, ".EPH")) { continue; } if (!(fp = fopen(efiles[i], "re"))) { trace(2, "sp3 file open error %s\n", efiles[i]); continue; } /* read sp3 header */ ns = readsp3h(fp, &time, &type, sats, bfact, tsys); /* read sp3 body */ readsp3b(fp, type, sats, ns, bfact, tsys, j++, opt, nav); fclose(fp); } for (i = 0; i < MAXEXFILE; i++) { free(efiles[i]); } /* combine precise ephemeris */ if (nav->ne > 0) { combpeph(nav, opt); } } /* read satellite antenna parameters ------------------------------------------- * read satellite antenna parameters * args : char *file I antenna parameter file * gtime_t time I time * nav_t *nav IO navigation data * return : status (1:ok,0:error) * notes : only support antex format for the antenna parameter file *-----------------------------------------------------------------------------*/ int readsap(const char *file, gtime_t time, nav_t *nav) { pcvs_t pcvs = {0, 0, (pcv_t *){nullptr}}; pcv_t pcv0 = {0, {}, {}, {0, 0}, {0, 0}, {{}, {}}, {{}, {}}}, *pcv; int i; trace(3, "readsap : file=%s time=%s\n", file, time_str(time, 0)); if (!readpcv(file, &pcvs)) { return 0; } for (i = 0; i < MAXSAT; i++) { pcv = searchpcv(i + 1, "", time, &pcvs); nav->pcvs[i] = pcv ? *pcv : pcv0; } free(pcv); return 1; } /* read dcb parameters file --------------------------------------------------*/ int readdcbf(const char *file, nav_t *nav, const sta_t *sta) { FILE *fp; double cbias; char buff[256], str1[32], str2[32] = ""; int i, j, sat, type = 0; trace(3, "readdcbf: file=%s\n", file); if (!(fp = fopen(file, "re"))) { trace(2, "dcb parameters file open error: %s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if (strstr(buff, "DIFFERENTIAL (P1-P2) CODE BIASES")) { type = 1; } else if (strstr(buff, "DIFFERENTIAL (P1-C1) CODE BIASES")) { type = 2; } else if (strstr(buff, "DIFFERENTIAL (P2-C2) CODE BIASES")) { type = 3; } if (!type || sscanf(buff, "%s %s", str1, str2) < 1) { continue; } if ((cbias = str2num(buff, 26, 9)) == 0.0) { continue; } if (sta && (!strcmp(str1, "G") || !strcmp(str1, "R"))) { /* receiver dcb */ for (i = 0; i < MAXRCV; i++) { if (!strcmp(sta[i].name, str2)) { break; } } if (i < MAXRCV) { j = !strcmp(str1, "G") ? 0 : 1; nav->rbias[i][j][type - 1] = cbias * 1e-9 * SPEED_OF_LIGHT; /* ns -> m */ } } else if ((sat = satid2no(str1))) { /* satellite dcb */ nav->cbias[sat - 1][type - 1] = cbias * 1e-9 * SPEED_OF_LIGHT; /* ns -> m */ } } fclose(fp); return 1; } /* read dcb parameters --------------------------------------------------------- * read differential code bias (dcb) parameters * args : char *file I dcb parameters file (wild-card * expanded) * nav_t *nav IO navigation data * sta_t *sta I station info data to import receiver dcb * (NULL: no use) * return : status (1:ok,0:error) * notes : currently only p1-c1 bias of code *.dcb file *-----------------------------------------------------------------------------*/ int readdcb(const char *file, nav_t *nav, const sta_t *sta) { int i, j, n; char *efiles[MAXEXFILE] = {}; trace(3, "readdcb : file=%s\n", file); for (i = 0; i < MAXSAT; i++) { for (j = 0; j < 3; j++) { nav->cbias[i][j] = 0.0; } } for (i = 0; i < MAXEXFILE; i++) { if (!(efiles[i] = static_cast(malloc(1024)))) { for (i--; i >= 0; i--) { free(efiles[i]); } return 0; } } n = expath(file, efiles, MAXEXFILE); for (i = 0; i < n; i++) { readdcbf(efiles[i], nav, sta); } for (i = 0; i < MAXEXFILE; i++) { free(efiles[i]); } return 1; } /* add satellite fcb ---------------------------------------------------------*/ int addfcb(nav_t *nav, gtime_t ts, gtime_t te, int sat, const double *bias, const double *std) { fcbd_t *nav_fcb; int i, j; if (nav->nf > 0 && fabs(timediff(ts, nav->fcb[nav->nf - 1].ts)) <= 1e-3) { for (i = 0; i < 3; i++) { nav->fcb[nav->nf - 1].bias[sat - 1][i] = bias[i]; nav->fcb[nav->nf - 1].std[sat - 1][i] = std[i]; } return 1; } if (nav->nf >= nav->nfmax) { nav->nfmax = nav->nfmax <= 0 ? 2048 : nav->nfmax * 2; if (!(nav_fcb = static_cast(realloc(nav->fcb, sizeof(fcbd_t) * nav->nfmax)))) { free(nav->fcb); nav->nf = nav->nfmax = 0; return 0; } nav->fcb = nav_fcb; } for (i = 0; i < MAXSAT; i++) { for (j = 0; j < 3; j++) { nav->fcb[nav->nf].bias[i][j] = nav->fcb[nav->nf].std[i][j] = 0.0; } } for (i = 0; i < 3; i++) { nav->fcb[nav->nf].bias[sat - 1][i] = bias[i]; nav->fcb[nav->nf].std[sat - 1][i] = std[i]; } nav->fcb[nav->nf].ts = ts; nav->fcb[nav->nf++].te = te; return 1; } /* read satellite fcb file ---------------------------------------------------*/ int readfcbf(const char *file, nav_t *nav) { FILE *fp; gtime_t ts, te; double ep1[6], ep2[6], bias[3] = {}, std[3] = {}; char buff[1024], str[32], *p; int sat; trace(3, "readfcbf: file=%s\n", file); if (!(fp = fopen(file, "re"))) { trace(2, "fcb parameters file open error: %s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if ((p = strchr(buff, '#'))) { *p = '\0'; } if (sscanf(buff, "%lf/%lf/%lf %lf:%lf:%lf %lf/%lf/%lf %lf:%lf:%lf %s" "%lf %lf %lf %lf %lf %lf", ep1, ep1 + 1, ep1 + 2, ep1 + 3, ep1 + 4, ep1 + 5, ep2, ep2 + 1, ep2 + 2, ep2 + 3, ep2 + 4, ep2 + 5, str, bias, std, bias + 1, std + 1, bias + 2, std + 2) < 17) { continue; } if (!(sat = satid2no(str))) { continue; } ts = epoch2time(ep1); te = epoch2time(ep2); if (!addfcb(nav, ts, te, sat, bias, std)) { fclose(fp); return 0; } } fclose(fp); return 1; } /* compare satellite fcb -----------------------------------------------------*/ int cmpfcb(const void *p1, const void *p2) { auto *q1 = (fcbd_t *)p1, *q2 = (fcbd_t *)p2; double tt = timediff(q1->ts, q2->ts); return tt < -1e-3 ? -1 : (tt > 1e-3 ? 1 : 0); } /* read satellite fcb data ----------------------------------------------------- * read satellite fractional cycle bias (dcb) parameters * args : char *file I fcb parameters file (wild-card * expanded) * nav_t *nav IO navigation data * return : status (1:ok,0:error) * notes : fcb data appended to navigation data *-----------------------------------------------------------------------------*/ int readfcb(const char *file, nav_t *nav) { char *efiles[MAXEXFILE] = {}; int i, n; trace(3, "readfcb : file=%s\n", file); for (i = 0; i < MAXEXFILE; i++) { if (!(efiles[i] = static_cast(malloc(1024)))) { for (i--; i >= 0; i--) { free(efiles[i]); } return 0; } } n = expath(file, efiles, MAXEXFILE); for (i = 0; i < n; i++) { readfcbf(efiles[i], nav); } for (i = 0; i < MAXEXFILE; i++) { free(efiles[i]); } if (nav->nf > 1) { qsort(nav->fcb, nav->nf, sizeof(fcbd_t), cmpfcb); } return 1; } /* polynomial interpolation by Neville's algorithm ---------------------------*/ double interppol(const double *x, double *y, int n) { int i, j; for (j = 1; j < n; j++) { for (i = 0; i < n - j; i++) { y[i] = (x[i + j] * y[i] - x[i] * y[i + 1]) / (x[i + j] - x[i]); } } return y[0]; } /* satellite position by precise ephemeris -----------------------------------*/ int pephpos(gtime_t time, int sat, const nav_t *nav, double *rs, double *dts, double *vare, double *varc) { double t[NMAX + 1], p[3][NMAX + 1], c[2], *pos, std = 0.0, s[3], sinl, cosl; int i, j, k, index; trace(4, "pephpos : time=%s sat=%2d\n", time_str(time, 3), sat); rs[0] = rs[1] = rs[2] = dts[0] = 0.0; if (nav->ne < NMAX + 1 || timediff(time, nav->peph[0].time) < -MAXDTE || timediff(time, nav->peph[nav->ne - 1].time) > MAXDTE) { trace(3, "no prec ephem %s sat=%2d\n", time_str(time, 0), sat); return 0; } /* binary search */ for (i = 0, j = nav->ne - 1; i < j;) { k = (i + j) / 2; if (timediff(nav->peph[k].time, time) < 0.0) { i = k + 1; } else { j = k; } } index = i <= 0 ? 0 : i - 1; /* polynomial interpolation for orbit */ i = index - (NMAX + 1) / 2; if (i < 0) { i = 0; } else if (i + NMAX >= nav->ne) { i = nav->ne - NMAX - 1; } for (j = 0; j <= NMAX; j++) { t[j] = timediff(nav->peph[i + j].time, time); if (norm_rtk(nav->peph[i + j].pos[sat - 1], 3) <= 0.0) { trace(3, "prec ephem outage %s sat=%2d\n", time_str(time, 0), sat); return 0; } } for (j = 0; j <= NMAX; j++) { pos = nav->peph[i + j].pos[sat - 1]; #if 0 p[0][j] = pos[0]; p[1][j] = pos[1]; #else /* correciton for earh rotation ver.2.4.0 */ sinl = sin(DEFAULT_OMEGA_EARTH_DOT * t[j]); cosl = cos(DEFAULT_OMEGA_EARTH_DOT * t[j]); p[0][j] = cosl * pos[0] - sinl * pos[1]; p[1][j] = sinl * pos[0] + cosl * pos[1]; #endif p[2][j] = pos[2]; } for (i = 0; i < 3; i++) { rs[i] = interppol(t, p[i], NMAX + 1); } if (vare) { for (i = 0; i < 3; i++) { s[i] = nav->peph[index].std[sat - 1][i]; } std = norm_rtk(s, 3); /* extrapolation error for orbit */ if (t[0] > 0.0) { std += EXTERR_EPH * std::pow(t[0], 2.0) / 2.0; } else if (t[NMAX] < 0.0) { std += EXTERR_EPH * std::pow(t[NMAX], 2.0) / 2.0; } *vare = std::pow(std, 2.0); } /* linear interpolation for clock */ t[0] = timediff(time, nav->peph[index].time); t[1] = timediff(time, nav->peph[index + 1].time); c[0] = nav->peph[index].pos[sat - 1][3]; c[1] = nav->peph[index + 1].pos[sat - 1][3]; if (t[0] <= 0.0) { if ((dts[0] = c[0]) != 0.0) { std = nav->peph[index].std[sat - 1][3] * SPEED_OF_LIGHT - EXTERR_CLK * t[0]; } } else if (t[1] >= 0.0) { if ((dts[0] = c[1]) != 0.0) { std = nav->peph[index + 1].std[sat - 1][3] * SPEED_OF_LIGHT + EXTERR_CLK * t[1]; } } else if (c[0] != 0.0 && c[1] != 0.0) { dts[0] = (c[1] * t[0] - c[0] * t[1]) / (t[0] - t[1]); i = t[0] < -t[1] ? 0 : 1; std = nav->peph[index + i].std[sat - 1][3] + EXTERR_CLK * fabs(t[i]); } else { dts[0] = 0.0; } if (varc) { *varc = std::pow(std, 2.0); } return 1; } /* satellite clock by precise clock ------------------------------------------*/ int pephclk(gtime_t time, int sat, const nav_t *nav, double *dts, double *varc) { double t[2], c[2], std; int i, j, k, index; trace(4, "pephclk : time=%s sat=%2d\n", time_str(time, 3), sat); if (nav->nc < 2 || timediff(time, nav->pclk[0].time) < -MAXDTE || timediff(time, nav->pclk[nav->nc - 1].time) > MAXDTE) { trace(3, "no prec clock %s sat=%2d\n", time_str(time, 0), sat); return 1; } /* binary search */ for (i = 0, j = nav->nc - 1; i < j;) { k = (i + j) / 2; if (timediff(nav->pclk[k].time, time) < 0.0) { i = k + 1; } else { j = k; } } index = i <= 0 ? 0 : i - 1; /* linear interpolation for clock */ t[0] = timediff(time, nav->pclk[index].time); t[1] = timediff(time, nav->pclk[index + 1].time); c[0] = nav->pclk[index].clk[sat - 1][0]; c[1] = nav->pclk[index + 1].clk[sat - 1][0]; if (t[0] <= 0.0) { if ((dts[0] = c[0]) == 0.0) { return 0; } std = nav->pclk[index].std[sat - 1][0] * SPEED_OF_LIGHT - EXTERR_CLK * t[0]; } else if (t[1] >= 0.0) { if ((dts[0] = c[1]) == 0.0) { return 0; } std = nav->pclk[index + 1].std[sat - 1][0] * SPEED_OF_LIGHT + EXTERR_CLK * t[1]; } else if (c[0] != 0.0 && c[1] != 0.0) { dts[0] = (c[1] * t[0] - c[0] * t[1]) / (t[0] - t[1]); i = t[0] < -t[1] ? 0 : 1; std = nav->pclk[index + i].std[sat - 1][0] * SPEED_OF_LIGHT + EXTERR_CLK * fabs(t[i]); } else { trace(3, "prec clock outage %s sat=%2d\n", time_str(time, 0), sat); return 0; } if (varc) { *varc = std::pow(std, 2.0); } return 1; } /* satellite antenna phase center offset --------------------------------------- * compute satellite antenna phase center offset in ecef * args : gtime_t time I time (gpst) * double *rs I satellite position and velocity (ecef) * {x,y,z,vx,vy,vz} (m|m/s) * int sat I satellite number * nav_t *nav I navigation data * double *dant I satellite antenna phase center offset (ecef) * {dx,dy,dz} (m) (iono-free LC value) * return : none *-----------------------------------------------------------------------------*/ void satantoff(gtime_t time, const double *rs, int sat, const nav_t *nav, double *dant) { const double *lam = nav->lam[sat - 1]; const pcv_t *pcv = nav->pcvs + sat - 1; double ex[3], ey[3], ez[3], es[3], r[3], rsun[3], gmst, erpv[5] = {}; double gamma, C1, C2, dant1, dant2; int i, j = 0, k = 1; trace(4, "satantoff: time=%s sat=%2d\n", time_str(time, 3), sat); /* sun position in ecef */ sunmoonpos(gpst2utc(time), erpv, rsun, nullptr, &gmst); /* unit vectors of satellite fixed coordinates */ for (i = 0; i < 3; i++) { r[i] = -rs[i]; } if (!normv3(r, ez)) { return; } for (i = 0; i < 3; i++) { r[i] = rsun[i] - rs[i]; } if (!normv3(r, es)) { return; } cross3(ez, es, r); if (!normv3(r, ey)) { return; } cross3(ey, ez, ex); if (NFREQ >= 3 && (satsys(sat, nullptr) & (SYS_GAL | SYS_SBS))) { k = 2; } if (NFREQ < 2 || lam[j] == 0.0 || lam[k] == 0.0) { return; } gamma = std::pow(lam[k], 2.0) / std::pow(lam[j], 2.0); C1 = gamma / (gamma - 1.0); C2 = -1.0 / (gamma - 1.0); /* iono-free LC */ for (i = 0; i < 3; i++) { dant1 = pcv->off[j][0] * ex[i] + pcv->off[j][1] * ey[i] + pcv->off[j][2] * ez[i]; dant2 = pcv->off[k][0] * ex[i] + pcv->off[k][1] * ey[i] + pcv->off[k][2] * ez[i]; dant[i] = C1 * dant1 + C2 * dant2; } } /* satellite position/clock by precise ephemeris/clock ------------------------- * compute satellite position/clock with precise ephemeris/clock * args : gtime_t time I time (gpst) * int sat I satellite number * nav_t *nav I navigation data * int opt I sat position option * (0: center of mass, 1: antenna phase center) * double *rs O sat position and velocity (ecef) * {x,y,z,vx,vy,vz} (m|m/s) * double *dts O sat clock {bias,drift} (s|s/s) * double *var IO sat position and clock error variance (m) * (NULL: no output) * return : status (1:ok,0:error or data outage) * notes : clock includes relativistic correction but does not contain code bias * before calling the function, nav->peph, nav->ne, nav->pclk and * nav->nc must be set by calling readsp3(), readrnx() or readrnxt() * if precise clocks are not set, clocks in sp3 are used instead *-----------------------------------------------------------------------------*/ int peph2pos(gtime_t time, int sat, const nav_t *nav, int opt, double *rs, double *dts, double *var) { double rss[3], rst[3], dtss[1], dtst[1], dant[3] = {}, vare = 0.0, varc = 0.0, tt = 1e-3; int i; trace(4, "peph2pos: time=%s sat=%2d opt=%d\n", time_str(time, 3), sat, opt); if (sat <= 0 || MAXSAT < sat) { return 0; } /* satellite position and clock bias */ if (!pephpos(time, sat, nav, rss, dtss, &vare, &varc) || !pephclk(time, sat, nav, dtss, &varc)) { return 0; } time = timeadd(time, tt); if (!pephpos(time, sat, nav, rst, dtst, nullptr, nullptr) || !pephclk(time, sat, nav, dtst, nullptr)) { return 0; } /* satellite antenna offset correction */ if (opt) { satantoff(time, rss, sat, nav, dant); } for (i = 0; i < 3; i++) { rs[i] = rss[i] + dant[i]; rs[i + 3] = (rst[i] - rss[i]) / tt; } /* relativistic effect correction */ if (dtss[0] != 0.0) { dts[0] = dtss[0] - 2.0 * dot(rs, rs + 3, 3) / SPEED_OF_LIGHT / SPEED_OF_LIGHT; dts[1] = (dtst[0] - dtss[0]) / tt; } else { /* no precise clock */ dts[0] = dts[1] = 0.0; } if (var) { *var = vare + varc; } return 1; } src/algorithms/libs/rtklib/rtklib_preceph.h000066400000000000000000000112641352176506000214150ustar00rootroot00000000000000/*! * \file rtklib_preceph.h * \brief precise ephemeris and clock functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * References : * [1] S.Hilla, The Extended Standard Product 3 Orbit Format (SP3-c), * 12 February, 2007 * [2] J.Ray, W.Gurtner, RINEX Extensions to Handle Clock Information, * 27 August, 1998 * [3] D.D.McCarthy, IERS Technical Note 21, IERS Conventions 1996, July 1996 * [4] D.A.Vallado, Fundamentals of Astrodynamics and Applications 2nd ed, * Space Technology Library, 2004 * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_PRECEPH_H_ #define GNSS_SDR_RTKLIB_PRECEPH_H_ #include "rtklib.h" const int NMAX = 10; /* order of polynomial interpolation */ const double MAXDTE = 900.0; /* max time difference to ephem time (s) */ const double EXTERR_CLK = 1e-3; /* extrapolation error for clock (m/s) */ const double EXTERR_EPH = 5e-7; /* extrapolation error for ephem (m/s^2) */ int code2sys(char code); int readsp3h(FILE *fp, gtime_t *time, char *type, int *sats, double *bfact, char *tsys); int addpeph(nav_t *nav, peph_t *peph); void readsp3b(FILE *fp, char type, int *sats, int ns, const double *bfact, char *tsys, int index, int opt, nav_t *nav); int cmppeph(const void *p1, const void *p2); void combpeph(nav_t *nav, int opt); void readsp3(const char *file, nav_t *nav, int opt); int readsap(const char *file, gtime_t time, nav_t *nav); int readdcbf(const char *file, nav_t *nav, const sta_t *sta); int readdcb(const char *file, nav_t *nav, const sta_t *sta); int addfcb(nav_t *nav, gtime_t ts, gtime_t te, int sat, const double *bias, const double *std); int readfcbf(const char *file, nav_t *nav); int readdcb(const char *file, nav_t *nav, const sta_t *sta); int addfcb(nav_t *nav, gtime_t ts, gtime_t te, int sat, const double *bias, const double *std); int readfcbf(const char *file, nav_t *nav); int cmpfcb(const void *p1, const void *p2); int readfcb(const char *file, nav_t *nav); double interppol(const double *x, double *y, int n); int pephpos(gtime_t time, int sat, const nav_t *nav, double *rs, double *dts, double *vare, double *varc); int pephclk(gtime_t time, int sat, const nav_t *nav, double *dts, double *varc); void satantoff(gtime_t time, const double *rs, int sat, const nav_t *nav, double *dant); int peph2pos(gtime_t time, int sat, const nav_t *nav, int opt, double *rs, double *dts, double *var); #endif // GNSS_SDR_RTKLIB_PRECEPH_H_ src/algorithms/libs/rtklib/rtklib_rtcm.cc000066400000000000000000000432351352176506000210750ustar00rootroot00000000000000/*! * \file rtklib_rtcm.cc * \brief RTCM functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_rtcm.h" #include "rtklib_rtkcmn.h" //extern int encode_rtcm3(rtcm_t *rtcm, int type, int sync); /* initialize rtcm control ----------------------------------------------------- * initialize rtcm control struct and reallocate memory for observation and * ephemeris buffer in rtcm control struct * args : rtcm_t *raw IO rtcm control struct * return : status (1:ok,0:memory allocation error) *-----------------------------------------------------------------------------*/ int init_rtcm(rtcm_t *rtcm) { gtime_t time0 = {0, 0.0}; obsd_t data0 = {{0, 0.0}, 0, 0, {0}, {0}, {0}, {0.0}, {0.0}, {0.0}}; eph_t eph0 = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; geph_t geph0 = {0, -1, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0.0}, {0.0}, {0.0}, 0.0, 0.0, 0.0}; ssr_t ssr0 = {{{0, 0.0}}, {0.0}, {0}, 0, 0, 0, 0, {0.0}, {0.0}, {0.0}, 0.0, {0.0}, {0.0}, {0.0}, 0.0, 0.0, '0'}; int i, j; trace(3, "init_rtcm:\n"); rtcm->staid = rtcm->stah = rtcm->seqno = rtcm->outtype = 0; rtcm->time = rtcm->time_s = time0; rtcm->sta.name[0] = rtcm->sta.marker[0] = '\0'; rtcm->sta.antdes[0] = rtcm->sta.antsno[0] = '\0'; rtcm->sta.rectype[0] = rtcm->sta.recver[0] = rtcm->sta.recsno[0] = '\0'; rtcm->sta.antsetup = rtcm->sta.itrf = rtcm->sta.deltype = 0; for (i = 0; i < 3; i++) { rtcm->sta.pos[i] = rtcm->sta.del[i] = 0.0; } rtcm->sta.hgt = 0.0; rtcm->dgps = nullptr; for (i = 0; i < MAXSAT; i++) { rtcm->ssr[i] = ssr0; } rtcm->msg[0] = rtcm->msgtype[0] = rtcm->opt[0] = '\0'; for (i = 0; i < 6; i++) { rtcm->msmtype[i][0] = '\0'; } rtcm->obsflag = rtcm->ephsat = 0; for (i = 0; i < MAXSAT; i++) { for (j = 0; j < NFREQ + NEXOBS; j++) { rtcm->cp[i][j] = 0.0; rtcm->lock[i][j] = rtcm->loss[i][j] = 0; rtcm->lltime[i][j] = time0; } } rtcm->nbyte = rtcm->nbit = rtcm->len = 0; rtcm->word = 0; for (i = 0; i < 100; i++) { rtcm->nmsg2[i] = 0; } for (i = 0; i < 300; i++) { rtcm->nmsg3[i] = 0; } rtcm->obs.data = nullptr; rtcm->nav.eph = nullptr; rtcm->nav.geph = nullptr; /* reallocate memory for observation and ephemris buffer */ if (!(rtcm->obs.data = static_cast(malloc(sizeof(obsd_t) * MAXOBS))) || !(rtcm->nav.eph = static_cast(malloc(sizeof(eph_t) * MAXSAT))) || !(rtcm->nav.geph = static_cast(malloc(sizeof(geph_t) * MAXPRNGLO)))) { free_rtcm(rtcm); return 0; } rtcm->obs.n = 0; rtcm->nav.n = MAXSAT; rtcm->nav.ng = MAXPRNGLO; for (i = 0; i < MAXOBS; i++) { rtcm->obs.data[i] = data0; } for (i = 0; i < MAXSAT; i++) { rtcm->nav.eph[i] = eph0; } for (i = 0; i < MAXPRNGLO; i++) { rtcm->nav.geph[i] = geph0; } return 1; } /* free rtcm control ---------------------------------------------------------- * free observation and ephemris buffer in rtcm control struct * args : rtcm_t *raw IO rtcm control struct * return : none *-----------------------------------------------------------------------------*/ void free_rtcm(rtcm_t *rtcm) { trace(3, "free_rtcm:\n"); /* free memory for observation and ephemeris buffer */ free(rtcm->obs.data); rtcm->obs.data = nullptr; rtcm->obs.n = 0; free(rtcm->nav.eph); rtcm->nav.eph = nullptr; rtcm->nav.n = 0; free(rtcm->nav.geph); rtcm->nav.geph = nullptr; rtcm->nav.ng = 0; } /* input rtcm 2 message from stream -------------------------------------------- * fetch next rtcm 2 message and input a message from byte stream * args : rtcm_t *rtcm IO rtcm control struct * unsigned char data I stream data (1 byte) * return : status (-1: error message, 0: no message, 1: input observation data, * 2: input ephemeris, 5: input station pos/ant parameters, * 6: input time parameter, 7: input dgps corrections, * 9: input special message) * notes : before firstly calling the function, time in rtcm control struct has * to be set to the approximate time within 1/2 hour in order to resolve * ambiguity of time in rtcm messages. * supported msgs RTCM ver.2: 1,3,9,14,16,17,18,19,22 * refer [1] for RTCM ver.2 *-----------------------------------------------------------------------------*/ int input_rtcm2(rtcm_t *rtcm, unsigned char data) { unsigned char preamb; int i; trace(5, "input_rtcm2: data=%02x\n", data); if ((data & 0xC0) != 0x40) { return 0; /* ignore if upper 2bit != 01 */ } for (i = 0; i < 6; i++, data >>= 1) { /* decode 6-of-8 form */ rtcm->word = (rtcm->word << 1) + (data & 1); /* synchronize frame */ if (rtcm->nbyte == 0) { preamb = static_cast(rtcm->word >> 22); if (rtcm->word & 0x40000000) { preamb ^= 0xFF; /* decode preamble */ } if (preamb != RTCM2PREAMB) { continue; } /* check parity */ if (!decode_word(rtcm->word, rtcm->buff)) { continue; } rtcm->nbyte = 3; rtcm->nbit = 0; continue; } if (++rtcm->nbit < 30) { continue; } rtcm->nbit = 0; /* check parity */ if (!decode_word(rtcm->word, rtcm->buff + rtcm->nbyte)) { trace(2, "rtcm2 partity error: i=%d word=%08x\n", i, rtcm->word); rtcm->nbyte = 0; rtcm->word &= 0x3; continue; } rtcm->nbyte += 3; if (rtcm->nbyte == 6) { rtcm->len = (rtcm->buff[5] >> 3) * 3 + 6; } if (rtcm->nbyte < rtcm->len) { continue; } rtcm->nbyte = 0; rtcm->word &= 0x3; /* decode rtcm2 message */ return decode_rtcm2(rtcm); } return 0; } /* input rtcm 3 message from stream -------------------------------------------- * fetch next rtcm 3 message and input a message from byte stream * args : rtcm_t *rtcm IO rtcm control struct * unsigned char data I stream data (1 byte) * return : status (-1: error message, 0: no message, 1: input observation data, * 2: input ephemeris, 5: input station pos/ant parameters, * 10: input ssr messages) * notes : before firstly calling the function, time in rtcm control struct has * to be set to the approximate time within 1/2 week in order to resolve * ambiguity of time in rtcm messages. * * to specify input options, set rtcm->opt to the following option * strings separated by spaces. * * -EPHALL : input all ephemerides * -STA=nnn : input only message with STAID=nnn * -GLss : select signal ss for GPS MSM (ss=1C,1P,...) * -RLss : select signal ss for GLO MSM (ss=1C,1P,...) * -ELss : select signal ss for GAL MSM (ss=1C,1B,...) * -JLss : select signal ss for QZS MSM (ss=1C,2C,...) * -CLss : select signal ss for BDS MSM (ss=2I,7I,...) * * supported RTCM 3 messages * (ref [2][3][4][5][6][7][8][9][10][11][12][13][14][15]) * * TYPE GPS GLOASS GALILEO QZSS BEIDOU SBAS * ---------------------------------------------------------------------- * OBS C-L1 : 1001~ 1009~ - - - - * F-L1 : 1002 1010 - - - - * C-L12 : 1003~ 1011~ - - - - * F-L12 : 1004 1012 - - - - * * NAV : 1019 1020 1045* 1044* 1047* - * - - 1046* - - - * * MSM 1 : 1071~ 1081~ 1091~ 1111*~ 1121*~ 1101*~ * 2 : 1072~ 1082~ 1092~ 1112*~ 1122*~ 1102*~ * 3 : 1073~ 1083~ 1093~ 1113*~ 1123*~ 1103*~ * 4 : 1074 1084 1094 1114* 1124* 1104* * 5 : 1075 1085 1095 1115* 1125* 1105* * 6 : 1076 1086 1096 1116* 1126* 1106* * 7 : 1077 1087 1097 1117* 1127* 1107* * * SSR OBT : 1057 1063 1240* 1246* 1258* - * CLK : 1058 1064 1241* 1247* 1259* - * BIAS : 1059 1065 1242* 1248* 1260* - * OBTCLK: 1060 1066 1243* 1249* 1261* - * URA : 1061 1067 1244* 1250* 1262* - * HRCLK : 1062 1068 1245* 1251* 1263* - * * ANT INFO : 1005 1006 1007 1008 1033 * ---------------------------------------------------------------------- * (* draft, ~ only encode) * * for MSM observation data with multiple signals for a frequency, * a signal is selected according to internal priority. to select * a specified signal, use the input options. * * rtcm3 message format: * +----------+--------+-----------+--------------------+----------+ * | preamble | 000000 | length | data message | parity | * +----------+--------+-----------+--------------------+----------+ * |<-- 8 --->|<- 6 -->|<-- 10 --->|<--- length x 8 --->|<-- 24 -->| * *-----------------------------------------------------------------------------*/ int input_rtcm3(rtcm_t *rtcm, unsigned char data) { trace(5, "input_rtcm3: data=%02x\n", data); /* synchronize frame */ if (rtcm->nbyte == 0) { if (data != RTCM3PREAMB) { return 0; } rtcm->buff[rtcm->nbyte++] = data; return 0; } rtcm->buff[rtcm->nbyte++] = data; if (rtcm->nbyte == 3) { rtcm->len = getbitu(rtcm->buff, 14, 10) + 3; /* length without parity */ } if (rtcm->nbyte < 3 || rtcm->nbyte < rtcm->len + 3) { return 0; } rtcm->nbyte = 0; /* check parity */ if (rtk_crc24q(rtcm->buff, rtcm->len) != getbitu(rtcm->buff, rtcm->len * 8, 24)) { trace(2, "rtcm3 parity error: len=%d\n", rtcm->len); return 0; } /* decode rtcm3 message */ return decode_rtcm3(rtcm); } /* input rtcm 2 message from file ---------------------------------------------- * fetch next rtcm 2 message and input a message from file * args : rtcm_t *rtcm IO rtcm control struct * FILE *fp I file pointer * return : status (-2: end of file, -1...10: same as above) * notes : same as above *-----------------------------------------------------------------------------*/ int input_rtcm2f(rtcm_t *rtcm, FILE *fp) { int i, data = 0, ret; trace(4, "input_rtcm2f: data=%02x\n", data); for (i = 0; i < 4096; i++) { if ((data = fgetc(fp)) == EOF) { return -2; } if ((ret = input_rtcm2(rtcm, static_cast(data)))) { return ret; } } return 0; /* return at every 4k bytes */ } /* input rtcm 3 message from file ---------------------------------------------- * fetch next rtcm 3 message and input a message from file * args : rtcm_t *rtcm IO rtcm control struct * FILE *fp I file pointer * return : status (-2: end of file, -1...10: same as above) * notes : same as above *-----------------------------------------------------------------------------*/ int input_rtcm3f(rtcm_t *rtcm, FILE *fp) { int i, data = 0, ret; trace(4, "input_rtcm3f: data=%02x\n", data); for (i = 0; i < 4096; i++) { if ((data = fgetc(fp)) == EOF) { return -2; } if ((ret = input_rtcm3(rtcm, static_cast(data)))) { return ret; } } return 0; /* return at every 4k bytes */ } /* generate rtcm 2 message ----------------------------------------------------- * generate rtcm 2 message * args : rtcm_t *rtcm IO rtcm control struct * int type I message type * int sync I sync flag (1:another message follows) * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ int gen_rtcm2(rtcm_t *rtcm, int type, int sync) { trace(4, "gen_rtcm2: type=%d sync=%d\n", type, sync); rtcm->nbit = rtcm->len = rtcm->nbyte = 0; /* not yet implemented */ return 0; } ///* generate rtcm 3 message ----------------------------------------------------- // * generate rtcm 3 message // * args : rtcm_t *rtcm IO rtcm control struct // * int type I message type // * int sync I sync flag (1:another message follows) // * return : status (1:ok,0:error) // *-----------------------------------------------------------------------------*/ //int gen_rtcm3(rtcm_t *rtcm, int type, int sync) //{ // unsigned int crc; // int i = 0; // // trace(4, "gen_rtcm3: type=%d sync=%d\n", type, sync); // // rtcm->nbit = rtcm->len = rtcm->nbyte = 0; // // /* set preamble and reserved */ // setbitu(rtcm->buff, i, 8, RTCM3PREAMB); i += 8; // setbitu(rtcm->buff, i, 6, 0 ); i += 6; // setbitu(rtcm->buff, i, 10, 0 ); i += 10; // // /* encode rtcm 3 message body */ // if (!encode_rtcm3(rtcm, type, sync)) return 0; // // /* padding to align 8 bit boundary */ // for (i = rtcm->nbit;i%8;i++) // { // setbitu(rtcm->buff, i, 1, 0); // } // /* message length (header+data) (bytes) */ // if ((rtcm->len = i/8) >= 3+1024) // { // trace(2, "generate rtcm 3 message length error len=%d\n", rtcm->len-3); // rtcm->nbit = rtcm->len = 0; // return 0; // } // /* message length without header and parity */ // setbitu(rtcm->buff, 14, 10, rtcm->len-3); // // /* crc-24q */ // crc = rtk_crc24q(rtcm->buff, rtcm->len); // setbitu(rtcm->buff, i, 24, crc); // // /* length total (bytes) */ // rtcm->nbyte = rtcm->len+3; // // return 1; //} src/algorithms/libs/rtklib/rtklib_rtcm.h000066400000000000000000000057401352176506000207360ustar00rootroot00000000000000/*! * \file rtklib_rtcm.h * \brief RTCM functions headers * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RTCM_H_ #define GNSS_SDR_RTKLIB_RTCM_H_ #include "rtklib.h" #include "rtklib_rtcm2.h" #include "rtklib_rtcm3.h" #define RTCM2PREAMB 0x66 /* rtcm ver.2 frame preamble */ #define RTCM3PREAMB 0xD3 /* rtcm ver.3 frame preamble */ int init_rtcm(rtcm_t *rtcm); void free_rtcm(rtcm_t *rtcm); int input_rtcm2(rtcm_t *rtcm, unsigned char data); int input_rtcm3(rtcm_t *rtcm, unsigned char data); int input_rtcm2f(rtcm_t *rtcm, FILE *fp); int input_rtcm3f(rtcm_t *rtcm, FILE *fp); int gen_rtcm2(rtcm_t *rtcm, int type, int sync); //int gen_rtcm3(rtcm_t *rtcm, int type, int sync); #endif src/algorithms/libs/rtklib/rtklib_rtcm2.cc000066400000000000000000000503451352176506000211570ustar00rootroot00000000000000/*! * \file rtklib_rtcm2.cc * \brief RTCM functions for v2 * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_rtcm2.h" #include "rtklib_rtkcmn.h" /* adjust hourly rollover of rtcm 2 time -------------------------------------*/ void adjhour(rtcm_t *rtcm, double zcnt) { double tow, hour, sec; int week; /* if no time, get cpu time */ if (rtcm->time.time == 0) { rtcm->time = utc2gpst(timeget()); } tow = time2gpst(rtcm->time, &week); hour = floor(tow / 3600.0); sec = tow - hour * 3600.0; if (zcnt < sec - 1800.0) { zcnt += 3600.0; } else if (zcnt > sec + 1800.0) { zcnt -= 3600.0; } rtcm->time = gpst2time(week, hour * 3600 + zcnt); } /* get observation data index ------------------------------------------------*/ int obsindex(obs_t *obs, gtime_t time, int sat) { int i, j; for (i = 0; i < obs->n; i++) { if (obs->data[i].sat == sat) { return i; /* field already exists */ } } if (i >= MAXOBS) { return -1; /* overflow */ } /* add new field */ obs->data[i].time = time; obs->data[i].sat = sat; for (j = 0; j < NFREQ; j++) { obs->data[i].L[j] = obs->data[i].P[j] = 0.0; obs->data[i].D[j] = 0.0; obs->data[i].SNR[j] = obs->data[i].LLI[j] = obs->data[i].code[j] = 0; } obs->n++; return i; } /* decode type 1/9: differential gps correction/partial correction set -------*/ int decode_type1(rtcm_t *rtcm) { int i = 48, fact, udre, prn, sat, iod; double prc, rrc; trace(4, "decode_type1: len=%d\n", rtcm->len); while (i + 40 <= rtcm->len * 8) { fact = getbitu(rtcm->buff, i, 1); i += 1; udre = getbitu(rtcm->buff, i, 2); i += 2; prn = getbitu(rtcm->buff, i, 5); i += 5; prc = getbits(rtcm->buff, i, 16); i += 16; rrc = getbits(rtcm->buff, i, 8); i += 8; iod = getbits(rtcm->buff, i, 8); i += 8; if (prn == 0) { prn = 32; } if (prc == 0x80000000 || rrc == 0xFFFF8000) { trace(2, "rtcm2 1 prc/rrc indicates satellite problem: prn=%d\n", prn); continue; } if (rtcm->dgps) { sat = satno(SYS_GPS, prn); rtcm->dgps[sat - 1].t0 = rtcm->time; rtcm->dgps[sat - 1].prc = prc * (fact ? 0.32 : 0.02); rtcm->dgps[sat - 1].rrc = rrc * (fact ? 0.032 : 0.002); rtcm->dgps[sat - 1].iod = iod; rtcm->dgps[sat - 1].udre = udre; } } return 7; } /* decode type 3: reference station parameter --------------------------------*/ int decode_type3(rtcm_t *rtcm) { int i = 48; trace(4, "decode_type3: len=%d\n", rtcm->len); if (i + 96 <= rtcm->len * 8) { rtcm->sta.pos[0] = getbits(rtcm->buff, i, 32) * 0.01; i += 32; rtcm->sta.pos[1] = getbits(rtcm->buff, i, 32) * 0.01; i += 32; rtcm->sta.pos[2] = getbits(rtcm->buff, i, 32) * 0.01; } else { trace(2, "rtcm2 3 length error: len=%d\n", rtcm->len); return -1; } return 5; } /* decode type 14: gps time of week ------------------------------------------*/ int decode_type14(rtcm_t *rtcm) { double zcnt; int i = 48, week, hour, leaps; trace(4, "decode_type14: len=%d\n", rtcm->len); zcnt = getbitu(rtcm->buff, 24, 13); if (i + 24 <= rtcm->len * 8) { week = getbitu(rtcm->buff, i, 10); i += 10; hour = getbitu(rtcm->buff, i, 8); i += 8; leaps = getbitu(rtcm->buff, i, 6); } else { trace(2, "rtcm2 14 length error: len=%d\n", rtcm->len); return -1; } week = adjgpsweek(week); rtcm->time = gpst2time(week, hour * 3600.0 + zcnt * 0.6); rtcm->nav.leaps = leaps; return 6; } /* decode type 16: gps special message ---------------------------------------*/ int decode_type16(rtcm_t *rtcm) { int i = 48, n = 0; trace(4, "decode_type16: len=%d\n", rtcm->len); while (i + 8 <= rtcm->len * 8 && n < 90) { rtcm->msg[n++] = getbitu(rtcm->buff, i, 8); i += 8; } rtcm->msg[n] = '\0'; trace(3, "rtcm2 16 message: %s\n", rtcm->msg); return 9; } /* decode type 17: gps ephemerides -------------------------------------------*/ int decode_type17(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; int i = 48, week, prn, sat; trace(4, "decode_type17: len=%d\n", rtcm->len); if (i + 480 <= rtcm->len * 8) { week = getbitu(rtcm->buff, i, 10); i += 10; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; eph.iode = getbitu(rtcm->buff, i, 8); i += 8; toc = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.f1 = getbits(rtcm->buff, i, 16) * TWO_N43; i += 16; eph.f2 = getbits(rtcm->buff, i, 8) * TWO_N55; i += 8; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16); i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 16); i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.iodc = getbitu(rtcm->buff, i, 10); i += 10; eph.f0 = getbits(rtcm->buff, i, 22) * TWO_N31; i += 22; prn = getbitu(rtcm->buff, i, 5); i += 5 + 3; eph.tgd[0] = getbits(rtcm->buff, i, 8) * TWO_N31; i += 8; eph.code = getbitu(rtcm->buff, i, 2); i += 2; eph.sva = getbitu(rtcm->buff, i, 4); i += 4; eph.svh = getbitu(rtcm->buff, i, 6); i += 6; eph.flag = getbitu(rtcm->buff, i, 1); } else { trace(2, "rtcm2 17 length error: len=%d\n", rtcm->len); return -1; } if (prn == 0) { prn = 32; } sat = satno(SYS_GPS, prn); eph.sat = sat; eph.week = adjgpsweek(week); eph.toe = gpst2time(eph.week, eph.toes); eph.toc = gpst2time(eph.week, toc); eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 18: rtk uncorrected carrier-phase -----------------------------*/ int decode_type18(rtcm_t *rtcm) { gtime_t time; double usec, cp, tt; int i = 48, index, freq, sync = 1, code, sys, prn, sat, loss; trace(4, "decode_type18: len=%d\n", rtcm->len); if (i + 24 <= rtcm->len * 8) { freq = getbitu(rtcm->buff, i, 2); i += 2 + 2; usec = getbitu(rtcm->buff, i, 20); i += 20; } else { trace(2, "rtcm2 18 length error: len=%d\n", rtcm->len); return -1; } if (freq & 0x1) { trace(2, "rtcm2 18 not supported frequency: freq=%d\n", freq); return -1; } freq >>= 1; while (i + 48 <= rtcm->len * 8 && rtcm->obs.n < MAXOBS) { sync = getbitu(rtcm->buff, i, 1); i += 1; code = getbitu(rtcm->buff, i, 1); i += 1; sys = getbitu(rtcm->buff, i, 1); i += 1; prn = getbitu(rtcm->buff, i, 5); i += 5 + 3; loss = getbitu(rtcm->buff, i, 5); i += 5; cp = getbits(rtcm->buff, i, 32); i += 32; if (prn == 0) { prn = 32; } if (!(sat = satno(sys ? SYS_GLO : SYS_GPS, prn))) { trace(2, "rtcm2 18 satellite number error: sys=%d prn=%d\n", sys, prn); continue; } time = timeadd(rtcm->time, usec * 1E-6); if (sys) { time = utc2gpst(time); /* convert glonass time to gpst */ } tt = timediff(rtcm->obs.data[0].time, time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex(&rtcm->obs, time, sat)) >= 0) { rtcm->obs.data[index].L[freq] = -cp / 256.0; rtcm->obs.data[index].LLI[freq] = rtcm->loss[sat - 1][freq] != loss; rtcm->obs.data[index].code[freq] = !freq ? (code ? CODE_L1P : CODE_L1C) : (code ? CODE_L2P : CODE_L2C); rtcm->loss[sat - 1][freq] = loss; } } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 19: rtk uncorrected pseudorange -------------------------------*/ int decode_type19(rtcm_t *rtcm) { gtime_t time; double usec, pr, tt; int i = 48, index, freq, sync = 1, code, sys, prn, sat; trace(4, "decode_type19: len=%d\n", rtcm->len); if (i + 24 <= rtcm->len * 8) { freq = getbitu(rtcm->buff, i, 2); i += 2 + 2; usec = getbitu(rtcm->buff, i, 20); i += 20; } else { trace(2, "rtcm2 19 length error: len=%d\n", rtcm->len); return -1; } if (freq & 0x1) { trace(2, "rtcm2 19 not supported frequency: freq=%d\n", freq); return -1; } freq >>= 1; while (i + 48 <= rtcm->len * 8 && rtcm->obs.n < MAXOBS) { sync = getbitu(rtcm->buff, i, 1); i += 1; code = getbitu(rtcm->buff, i, 1); i += 1; sys = getbitu(rtcm->buff, i, 1); i += 1; prn = getbitu(rtcm->buff, i, 5); i += 5 + 8; pr = getbitu(rtcm->buff, i, 32); i += 32; if (prn == 0) { prn = 32; } if (!(sat = satno(sys ? SYS_GLO : SYS_GPS, prn))) { trace(2, "rtcm2 19 satellite number error: sys=%d prn=%d\n", sys, prn); continue; } time = timeadd(rtcm->time, usec * 1E-6); if (sys) { time = utc2gpst(time); /* convert glonass time to gpst */ } tt = timediff(rtcm->obs.data[0].time, time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex(&rtcm->obs, time, sat)) >= 0) { rtcm->obs.data[index].P[freq] = pr * 0.02; rtcm->obs.data[index].code[freq] = !freq ? (code ? CODE_L1P : CODE_L1C) : (code ? CODE_L2P : CODE_L2C); } } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 22: extended reference station parameter ----------------------*/ int decode_type22(rtcm_t *rtcm) { double del[2][3] = {{0}}, hgt = 0.0; int i = 48, j, noh; trace(4, "decode_type22: len=%d\n", rtcm->len); if (i + 24 <= rtcm->len * 8) { del[0][0] = getbits(rtcm->buff, i, 8) / 25600.0; i += 8; del[0][1] = getbits(rtcm->buff, i, 8) / 25600.0; i += 8; del[0][2] = getbits(rtcm->buff, i, 8) / 25600.0; i += 8; } else { trace(2, "rtcm2 22 length error: len=%d\n", rtcm->len); return -1; } if (i + 24 <= rtcm->len * 8) { i += 5; noh = getbits(rtcm->buff, i, 1); i += 1; hgt = noh ? 0.0 : getbitu(rtcm->buff, i, 18) / 25600.0; i += 18; } if (i + 24 <= rtcm->len * 8) { del[1][0] = getbits(rtcm->buff, i, 8) / 1600.0; i += 8; del[1][1] = getbits(rtcm->buff, i, 8) / 1600.0; i += 8; del[1][2] = getbits(rtcm->buff, i, 8) / 1600.0; } rtcm->sta.deltype = 1; /* xyz */ for (j = 0; j < 3; j++) { rtcm->sta.del[j] = del[0][j]; } rtcm->sta.hgt = hgt; return 5; } /* decode type 23: antenna type definition record ----------------------------*/ int decode_type23(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 24: antenna reference point (arp) -----------------------------*/ int decode_type24(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 31: differential glonass correction ---------------------------*/ int decode_type31(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 32: differential glonass reference station parameters ---------*/ int decode_type32(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 34: glonass partial differential correction set ---------------*/ int decode_type34(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 36: glonass special message -----------------------------------*/ int decode_type36(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 37: gnss system time offset -----------------------------------*/ int decode_type37(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode type 59: proprietary message ---------------------------------------*/ int decode_type59(rtcm_t *rtcm __attribute((unused))) { return 0; } /* decode rtcm ver.2 message -------------------------------------------------*/ int decode_rtcm2(rtcm_t *rtcm) { double zcnt; int staid, seqno, stah, ret = 0, type = getbitu(rtcm->buff, 8, 6); trace(3, "decode_rtcm2: type=%2d len=%3d\n", type, rtcm->len); if ((zcnt = getbitu(rtcm->buff, 24, 13) * 0.6) >= 3600.0) { trace(2, "rtcm2 modified z-count error: zcnt=%.1f\n", zcnt); return -1; } adjhour(rtcm, zcnt); staid = getbitu(rtcm->buff, 14, 10); seqno = getbitu(rtcm->buff, 37, 3); stah = getbitu(rtcm->buff, 45, 3); if (seqno - rtcm->seqno != 1 && seqno - rtcm->seqno != -7) { trace(2, "rtcm2 message outage: seqno=%d->%d\n", rtcm->seqno, seqno); } rtcm->seqno = seqno; rtcm->stah = stah; if (rtcm->outtype) { sprintf(rtcm->msgtype, "RTCM %2d (%4d) zcnt=%7.1f staid=%3d seqno=%d", type, rtcm->len, zcnt, staid, seqno); } if (type == 3 || type == 22 || type == 23 || type == 24) { if (rtcm->staid != 0 && staid != rtcm->staid) { trace(2, "rtcm2 station id changed: %d->%d\n", rtcm->staid, staid); } rtcm->staid = staid; } if (rtcm->staid != 0 && staid != rtcm->staid) { trace(2, "rtcm2 station id invalid: %d %d\n", staid, rtcm->staid); return -1; } switch (type) { case 1: ret = decode_type1(rtcm); break; case 3: ret = decode_type3(rtcm); break; case 9: ret = decode_type1(rtcm); break; case 14: ret = decode_type14(rtcm); break; case 16: ret = decode_type16(rtcm); break; case 17: ret = decode_type17(rtcm); break; case 18: ret = decode_type18(rtcm); break; case 19: ret = decode_type19(rtcm); break; case 22: ret = decode_type22(rtcm); break; case 23: ret = decode_type23(rtcm); break; /* not supported */ case 24: ret = decode_type24(rtcm); break; /* not supported */ case 31: ret = decode_type31(rtcm); break; /* not supported */ case 32: ret = decode_type32(rtcm); break; /* not supported */ case 34: ret = decode_type34(rtcm); break; /* not supported */ case 36: ret = decode_type36(rtcm); break; /* not supported */ case 37: ret = decode_type37(rtcm); break; /* not supported */ case 59: ret = decode_type59(rtcm); break; /* not supported */ } if (ret >= 0) { if (1 <= type && type <= 99) { rtcm->nmsg2[type]++; } else { rtcm->nmsg2[0]++; } } return ret; } src/algorithms/libs/rtklib/rtklib_rtcm2.h000066400000000000000000000061541352176506000210200ustar00rootroot00000000000000/*! * \file rtklib_rtcm2.h * \brief RTCM v2 functions headers * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RTCM2_H_ #define GNSS_SDR_RTKLIB_RTCM2_H_ #include "rtklib.h" void adjhour(rtcm_t *rtcm, double zcnt); int obsindex(obs_t *obs, gtime_t time, int sat); int decode_type1(rtcm_t *rtcm); int decode_type3(rtcm_t *rtcm); int decode_type14(rtcm_t *rtcm); int decode_type16(rtcm_t *rtcm); int decode_type17(rtcm_t *rtcm); int decode_type18(rtcm_t *rtcm); int decode_type19(rtcm_t *rtcm); int decode_type22(rtcm_t *rtcm); int decode_type23(rtcm_t *rtcm); int decode_type24(rtcm_t *rtcm); int decode_type31(rtcm_t *rtcm); int decode_type32(rtcm_t *rtcm); int decode_type34(rtcm_t *rtcm); int decode_type36(rtcm_t *rtcm); int decode_type37(rtcm_t *rtcm); int decode_type59(rtcm_t *rtcm); int decode_rtcm2(rtcm_t *rtcm); #endif src/algorithms/libs/rtklib/rtklib_rtcm3.cc000066400000000000000000003566671352176506000212000ustar00rootroot00000000000000/*! * \file rtklib_rtcm3.cc * \brief RTCM functions for v3 * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_rtcm3.h" #include "rtklib_rtkcmn.h" #include /* msm signal id table -------------------------------------------------------*/ const char *msm_sig_gps[32] = { /* GPS: ref [13] table 3.5-87, ref [14][15] table 3.5-91 */ "", "1C", "1P", "1W", "1Y", "1M", "", "2C", "2P", "2W", "2Y", "2M", /* 1-12 */ "", "", "2S", "2L", "2X", "", "", "", "", "5I", "5Q", "5X", /* 13-24 */ "", "", "", "", "", "1S", "1L", "1X" /* 25-32 */ }; const char *msm_sig_glo[32] = { /* GLONASS: ref [13] table 3.5-93, ref [14][15] table 3.5-97 */ "", "1C", "1P", "", "", "", "", "2C", "2P", "", "3I", "3Q", "3X", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}; const char *msm_sig_gal[32] = { /* Galileo: ref [15] table 3.5-100 */ "", "1C", "1A", "1B", "1X", "1Z", "", "6C", "6A", "6B", "6X", "6Z", "", "7I", "7Q", "7X", "", "8I", "8Q", "8X", "", "5I", "5Q", "5X", "", "", "", "", "", "", "", ""}; const char *msm_sig_qzs[32] = { /* QZSS: ref [15] table 3.5-103 */ "", "1C", "", "", "", "", "", "", "6S", "6L", "6X", "", "", "", "2S", "2L", "2X", "", "", "", "", "5I", "5Q", "5X", "", "", "", "", "", "1S", "1L", "1X"}; const char *msm_sig_sbs[32] = { /* SBAS: ref [13] table 3.5-T+005 */ "", "1C", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "5I", "5Q", "5X", "", "", "", "", "", "", "", ""}; const char *msm_sig_cmp[32] = { /* BeiDou: ref [15] table 3.5-106 */ "", "1I", "1Q", "1X", "", "", "", "6I", "6Q", "6X", "", "", "", "7I", "7Q", "7X", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}; /* get sign-magnitude bits ---------------------------------------------------*/ double getbitg(const unsigned char *buff, int pos, int len) { double value = getbitu(buff, pos + 1, len - 1); return getbitu(buff, pos, 1) ? -value : value; } /* adjust weekly rollover of gps time ----------------------------------------*/ void adjweek(rtcm_t *rtcm, double tow) { double tow_p; int week; /* if no time, get cpu time */ if (rtcm->time.time == 0) { rtcm->time = utc2gpst(timeget()); } tow_p = time2gpst(rtcm->time, &week); if (tow < tow_p - 302400.0) { tow += 604800.0; } else if (tow > tow_p + 302400.0) { tow -= 604800.0; } rtcm->time = gpst2time(week, tow); } /* adjust weekly rollover of bdt time ----------------------------------------*/ int adjbdtweek(int week) { int w; (void)time2bdt(gpst2bdt(utc2gpst(timeget())), &w); if (w < 1) { w = 1; /* use 2006/1/1 if time is earlier than 2006/1/1 */ } return week + (w - week + 512) / 1024 * 1024; } /* adjust daily rollover of glonass time -------------------------------------*/ void adjday_glot(rtcm_t *rtcm, double tod) { gtime_t time; double tow, tod_p; int week; if (rtcm->time.time == 0) { rtcm->time = utc2gpst(timeget()); } time = timeadd(gpst2utc(rtcm->time), 10800.0); /* glonass time */ tow = time2gpst(time, &week); tod_p = fmod(tow, 86400.0); tow -= tod_p; if (tod < tod_p - 43200.0) { tod += 86400.0; } else if (tod > tod_p + 43200.0) { tod -= 86400.0; } time = gpst2time(week, tow + tod); rtcm->time = utc2gpst(timeadd(time, -10800.0)); } /* adjust carrier-phase rollover ---------------------------------------------*/ double adjcp(rtcm_t *rtcm, int sat, int freq, double cp) { if (rtcm->cp[sat - 1][freq] == 0.0) { ; } else if (cp < rtcm->cp[sat - 1][freq] - 750.0) { cp += 1500.0; } else if (cp > rtcm->cp[sat - 1][freq] + 750.0) { cp -= 1500.0; } rtcm->cp[sat - 1][freq] = cp; return cp; } /* loss-of-lock indicator ----------------------------------------------------*/ int lossoflock(rtcm_t *rtcm, int sat, int freq, int lock) { int lli = (!lock && !rtcm->lock[sat - 1][freq]) || lock < rtcm->lock[sat - 1][freq]; rtcm->lock[sat - 1][freq] = static_cast(lock); return lli; } /* s/n ratio -----------------------------------------------------------------*/ unsigned char snratio(double snr) { return static_cast(snr <= 0.0 || 255.5 <= snr ? 0.0 : snr * 4.0 + 0.5); } /* get observation data index ------------------------------------------------*/ int obsindex3(obs_t *obs, gtime_t time, int sat) { int i, j; for (i = 0; i < obs->n; i++) { if (obs->data[i].sat == sat) { return i; /* field already exists */ } } if (i >= MAXOBS) { return -1; /* overflow */ } /* add new field */ obs->data[i].time = time; obs->data[i].sat = sat; for (j = 0; j < NFREQ + NEXOBS; j++) { obs->data[i].L[j] = obs->data[i].P[j] = 0.0; obs->data[i].D[j] = 0.0; obs->data[i].SNR[j] = obs->data[i].LLI[j] = obs->data[i].code[j] = 0; } obs->n++; return i; } /* test station id consistency -----------------------------------------------*/ int test_staid(rtcm_t *rtcm, int staid) { char *p; int type, id; /* test station id option */ if ((p = strstr(rtcm->opt, "-STA=")) && sscanf(p, "-STA=%d", &id) == 1) { if (staid != id) { return 0; } } /* save station id */ if (rtcm->staid == 0 || rtcm->obsflag) { rtcm->staid = staid; } else if (staid != rtcm->staid) { type = getbitu(rtcm->buff, 24, 12); trace(2, "rtcm3 %d staid invalid id=%d %d\n", type, staid, rtcm->staid); /* reset station id if station id error */ rtcm->staid = 0; return 0; } return 1; } /* decode type 1001-1004 message header --------------------------------------*/ int decode_head1001(rtcm_t *rtcm, int *sync) { double tow; char *msg; int i = 24, staid, nsat, type; type = getbitu(rtcm->buff, i, 12); i += 12; if (i + 52 <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; tow = getbitu(rtcm->buff, i, 30) * 0.001; i += 30; *sync = getbitu(rtcm->buff, i, 1); i += 1; nsat = getbitu(rtcm->buff, i, 5); } else { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } adjweek(rtcm, tow); trace(4, "decode_head1001: time=%s nsat=%d sync=%d\n", time_str(rtcm->time, 2), nsat, *sync); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d %s nsat=%2d sync=%d", staid, time_str(rtcm->time, 2), nsat, *sync); } return nsat; } /* decode type 1001: L1-only gps rtk observation -----------------------------*/ int decode_type1001(rtcm_t *rtcm) { int sync; if (decode_head1001(rtcm, &sync) < 0) { return -1; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1002: extended L1-only gps rtk observables --------------------*/ int decode_type1002(rtcm_t *rtcm) { double pr1, cnr1, tt, cp1; int i = 24 + 64, j, index, nsat, sync, prn, code, sat, ppr1, lock1, amb, sys; if ((nsat = decode_head1001(rtcm, &sync)) < 0) { return -1; } for (j = 0; j < nsat && rtcm->obs.n < MAXOBS && i + 74 <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, 6); i += 6; code = getbitu(rtcm->buff, i, 1); i += 1; pr1 = getbitu(rtcm->buff, i, 24); i += 24; ppr1 = getbits(rtcm->buff, i, 20); i += 20; lock1 = getbitu(rtcm->buff, i, 7); i += 7; amb = getbitu(rtcm->buff, i, 8); i += 8; cnr1 = getbitu(rtcm->buff, i, 8); i += 8; if (prn < 40) { sys = SYS_GPS; } else { sys = SYS_SBS; prn += 80; } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1002 satellite number error: prn=%d\n", prn); continue; } tt = timediff(rtcm->obs.data[0].time, rtcm->time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex3(&rtcm->obs, rtcm->time, sat)) < 0) { continue; } pr1 = pr1 * 0.02 + amb * PRUNIT_GPS; if (ppr1 != static_cast(0xFFF80000)) { rtcm->obs.data[index].P[0] = pr1; cp1 = adjcp(rtcm, sat, 0, ppr1 * 0.0005 / LAM_CARR[0]); rtcm->obs.data[index].L[0] = pr1 / LAM_CARR[0] + cp1; } rtcm->obs.data[index].LLI[0] = lossoflock(rtcm, sat, 0, lock1); rtcm->obs.data[index].SNR[0] = snratio(cnr1 * 0.25); rtcm->obs.data[index].code[0] = code ? CODE_L1P : CODE_L1C; } return sync ? 0 : 1; } /* decode type 1003: L1&L2 gps rtk observables -------------------------------*/ int decode_type1003(rtcm_t *rtcm) { int sync; if (decode_head1001(rtcm, &sync) < 0) { return -1; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1004: extended L1&L2 gps rtk observables ----------------------*/ int decode_type1004(rtcm_t *rtcm) { const int L2codes[] = {CODE_L2X, CODE_L2P, CODE_L2D, CODE_L2W}; double pr1, cnr1, cnr2, tt, cp1, cp2; int i = 24 + 64, j, index, nsat, sync, prn, sat, code1, code2, pr21, ppr1, ppr2; int lock1, lock2, amb, sys; if ((nsat = decode_head1001(rtcm, &sync)) < 0) { return -1; } for (j = 0; j < nsat && rtcm->obs.n < MAXOBS && i + 125 <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, 6); i += 6; code1 = getbitu(rtcm->buff, i, 1); i += 1; pr1 = getbitu(rtcm->buff, i, 24); i += 24; ppr1 = getbits(rtcm->buff, i, 20); i += 20; lock1 = getbitu(rtcm->buff, i, 7); i += 7; amb = getbitu(rtcm->buff, i, 8); i += 8; cnr1 = getbitu(rtcm->buff, i, 8); i += 8; code2 = getbitu(rtcm->buff, i, 2); i += 2; pr21 = getbits(rtcm->buff, i, 14); i += 14; ppr2 = getbits(rtcm->buff, i, 20); i += 20; lock2 = getbitu(rtcm->buff, i, 7); i += 7; cnr2 = getbitu(rtcm->buff, i, 8); i += 8; if (prn < 40) { sys = SYS_GPS; } else { sys = SYS_SBS; prn += 80; } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1004 satellite number error: sys=%d prn=%d\n", sys, prn); continue; } tt = timediff(rtcm->obs.data[0].time, rtcm->time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex3(&rtcm->obs, rtcm->time, sat)) < 0) { continue; } pr1 = pr1 * 0.02 + amb * PRUNIT_GPS; if (ppr1 != static_cast(0xFFF80000)) { rtcm->obs.data[index].P[0] = pr1; cp1 = adjcp(rtcm, sat, 0, ppr1 * 0.0005 / LAM_CARR[0]); rtcm->obs.data[index].L[0] = pr1 / LAM_CARR[0] + cp1; } rtcm->obs.data[index].LLI[0] = lossoflock(rtcm, sat, 0, lock1); rtcm->obs.data[index].SNR[0] = snratio(cnr1 * 0.25); rtcm->obs.data[index].code[0] = code1 ? CODE_L1P : CODE_L1C; if (pr21 != static_cast(0xFFFFE000)) { rtcm->obs.data[index].P[1] = pr1 + pr21 * 0.02; } if (ppr2 != static_cast(0xFFF80000)) { cp2 = adjcp(rtcm, sat, 1, ppr2 * 0.0005 / LAM_CARR[1]); rtcm->obs.data[index].L[1] = pr1 / LAM_CARR[1] + cp2; } rtcm->obs.data[index].LLI[1] = lossoflock(rtcm, sat, 1, lock2); rtcm->obs.data[index].SNR[1] = snratio(cnr2 * 0.25); rtcm->obs.data[index].code[1] = L2codes[code2]; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* get signed 38bit field ----------------------------------------------------*/ double getbits_38(const unsigned char *buff, int pos) { return static_cast(getbits(buff, pos, 32)) * 64.0 + getbitu(buff, pos + 32, 6); } /* decode type 1005: stationary rtk reference station arp --------------------*/ int decode_type1005(rtcm_t *rtcm) { double rr[3], re[3], pos[3]; char *msg; int i = 24 + 12, j, staid, itrf; if (i + 140 == rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; itrf = getbitu(rtcm->buff, i, 6); i += 6 + 4; rr[0] = getbits_38(rtcm->buff, i); i += 38 + 2; rr[1] = getbits_38(rtcm->buff, i); i += 38 + 2; rr[2] = getbits_38(rtcm->buff, i); } else { trace(2, "rtcm3 1005 length error: len=%d\n", rtcm->len); return -1; } if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); for (j = 0; j < 3; j++) { re[j] = rr[j] * 0.0001; } ecef2pos(re, pos); sprintf(msg, " staid=%4d pos=%.8f %.8f %.3f", staid, pos[0] * R2D, pos[1] * R2D, pos[2]); } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } rtcm->sta.deltype = 0; /* xyz */ for (j = 0; j < 3; j++) { rtcm->sta.pos[j] = rr[j] * 0.0001; rtcm->sta.del[j] = 0.0; } rtcm->sta.hgt = 0.0; rtcm->sta.itrf = itrf; return 5; } /* decode type 1006: stationary rtk reference station arp with height --------*/ int decode_type1006(rtcm_t *rtcm) { double rr[3], re[3], pos[3], anth; char *msg; int i = 24 + 12, j, staid, itrf; if (i + 156 <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; itrf = getbitu(rtcm->buff, i, 6); i += 6 + 4; rr[0] = getbits_38(rtcm->buff, i); i += 38 + 2; rr[1] = getbits_38(rtcm->buff, i); i += 38 + 2; rr[2] = getbits_38(rtcm->buff, i); i += 38; anth = getbitu(rtcm->buff, i, 16); } else { trace(2, "rtcm3 1006 length error: len=%d\n", rtcm->len); return -1; } if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); for (j = 0; j < 3; j++) { re[j] = rr[j] * 0.0001; } ecef2pos(re, pos); sprintf(msg, " staid=%4d pos=%.8f %.8f %.3f anth=%.3f", staid, pos[0] * R2D, pos[1] * R2D, pos[2], anth); } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } rtcm->sta.deltype = 1; /* xyz */ for (j = 0; j < 3; j++) { rtcm->sta.pos[j] = rr[j] * 0.0001; rtcm->sta.del[j] = 0.0; } rtcm->sta.hgt = anth * 0.0001; rtcm->sta.itrf = itrf; return 5; } /* decode type 1007: antenna descriptor --------------------------------------*/ int decode_type1007(rtcm_t *rtcm) { char des[32] = ""; char *msg; int i = 24 + 12, j, staid, n, setup; n = getbitu(rtcm->buff, i + 12, 8); if (i + 28 + 8 * n <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12 + 8; for (j = 0; j < n && j < 31; j++) { des[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } setup = getbitu(rtcm->buff, i, 8); } else { trace(2, "rtcm3 1007 length error: len=%d\n", rtcm->len); return -1; } if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d", staid); } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } strncpy(rtcm->sta.antdes, des, n); rtcm->sta.antdes[n] = '\0'; rtcm->sta.antsetup = setup; rtcm->sta.antsno[0] = '\0'; return 5; } /* decode type 1008: antenna descriptor & serial number ----------------------*/ int decode_type1008(rtcm_t *rtcm) { char des[32] = "", sno[32] = ""; char *msg; int i = 24 + 12, j, staid, n, m, setup; n = getbitu(rtcm->buff, i + 12, 8); m = getbitu(rtcm->buff, i + 28 + 8 * n, 8); if (i + 36 + 8 * (n + m) <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12 + 8; for (j = 0; j < n && j < 31; j++) { des[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } setup = getbitu(rtcm->buff, i, 8); i += 8 + 8; for (j = 0; j < m && j < 31; j++) { sno[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } } else { trace(2, "rtcm3 1008 length error: len=%d\n", rtcm->len); return -1; } if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d", staid); } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } strncpy(rtcm->sta.antdes, des, n); rtcm->sta.antdes[n] = '\0'; rtcm->sta.antsetup = setup; strncpy(rtcm->sta.antsno, sno, m); rtcm->sta.antsno[m] = '\0'; return 5; } /* decode type 1009-1012 message header --------------------------------------*/ int decode_head1009(rtcm_t *rtcm, int *sync) { double tod; char *msg; int i = 24, staid, nsat, type; type = getbitu(rtcm->buff, i, 12); i += 12; if (i + 49 <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; tod = getbitu(rtcm->buff, i, 27) * 0.001; i += 27; /* sec in a day */ *sync = getbitu(rtcm->buff, i, 1); i += 1; nsat = getbitu(rtcm->buff, i, 5); } else { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } adjday_glot(rtcm, tod); trace(4, "decode_head1009: time=%s nsat=%d sync=%d\n", time_str(rtcm->time, 2), nsat, *sync); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d %s nsat=%2d sync=%d", staid, time_str(rtcm->time, 2), nsat, *sync); } return nsat; } /* decode type 1009: L1-only glonass rtk observables -------------------------*/ int decode_type1009(rtcm_t *rtcm) { int sync; if (decode_head1009(rtcm, &sync) < 0) { return -1; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1010: extended L1-only glonass rtk observables ----------------*/ int decode_type1010(rtcm_t *rtcm) { double pr1, cnr1, tt, cp1, lam1; int i = 24 + 61, j, index, nsat, sync, prn, sat, code, freq, ppr1, lock1, amb, sys = SYS_GLO; if ((nsat = decode_head1009(rtcm, &sync)) < 0) { return -1; } for (j = 0; j < nsat && rtcm->obs.n < MAXOBS && i + 79 <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, 6); i += 6; code = getbitu(rtcm->buff, i, 1); i += 1; freq = getbitu(rtcm->buff, i, 5); i += 5; pr1 = getbitu(rtcm->buff, i, 25); i += 25; ppr1 = getbits(rtcm->buff, i, 20); i += 20; lock1 = getbitu(rtcm->buff, i, 7); i += 7; amb = getbitu(rtcm->buff, i, 7); i += 7; cnr1 = getbitu(rtcm->buff, i, 8); i += 8; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1010 satellite number error: prn=%d\n", prn); continue; } tt = timediff(rtcm->obs.data[0].time, rtcm->time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex3(&rtcm->obs, rtcm->time, sat)) < 0) { continue; } pr1 = pr1 * 0.02 + amb * PRUNIT_GLO; if (ppr1 != static_cast(0xFFF80000)) { rtcm->obs.data[index].P[0] = pr1; lam1 = SPEED_OF_LIGHT / (FREQ1_GLO + DFRQ1_GLO * (freq - 7)); cp1 = adjcp(rtcm, sat, 0, ppr1 * 0.0005 / lam1); rtcm->obs.data[index].L[0] = pr1 / lam1 + cp1; } rtcm->obs.data[index].LLI[0] = lossoflock(rtcm, sat, 0, lock1); rtcm->obs.data[index].SNR[0] = snratio(cnr1 * 0.25); rtcm->obs.data[index].code[0] = code ? CODE_L1P : CODE_L1C; } return sync ? 0 : 1; } /* decode type 1011: L1&L2 glonass rtk observables ---------------------------*/ int decode_type1011(rtcm_t *rtcm) { int sync; if (decode_head1009(rtcm, &sync) < 0) { return -1; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1012: extended L1&L2 glonass rtk observables ------------------*/ int decode_type1012(rtcm_t *rtcm) { double pr1, cnr1, cnr2, tt, cp1, cp2, lam1, lam2; int i = 24 + 61, j, index, nsat, sync, prn, sat, freq, code1, code2, pr21, ppr1, ppr2; int lock1, lock2, amb, sys = SYS_GLO; if ((nsat = decode_head1009(rtcm, &sync)) < 0) { return -1; } for (j = 0; j < nsat && rtcm->obs.n < MAXOBS && i + 130 <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, 6); i += 6; code1 = getbitu(rtcm->buff, i, 1); i += 1; freq = getbitu(rtcm->buff, i, 5); i += 5; pr1 = getbitu(rtcm->buff, i, 25); i += 25; ppr1 = getbits(rtcm->buff, i, 20); i += 20; lock1 = getbitu(rtcm->buff, i, 7); i += 7; amb = getbitu(rtcm->buff, i, 7); i += 7; cnr1 = getbitu(rtcm->buff, i, 8); i += 8; code2 = getbitu(rtcm->buff, i, 2); i += 2; pr21 = getbits(rtcm->buff, i, 14); i += 14; ppr2 = getbits(rtcm->buff, i, 20); i += 20; lock2 = getbitu(rtcm->buff, i, 7); i += 7; cnr2 = getbitu(rtcm->buff, i, 8); i += 8; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1012 satellite number error: sys=%d prn=%d\n", sys, prn); continue; } tt = timediff(rtcm->obs.data[0].time, rtcm->time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } if ((index = obsindex3(&rtcm->obs, rtcm->time, sat)) < 0) { continue; } pr1 = pr1 * 0.02 + amb * PRUNIT_GLO; if (ppr1 != static_cast(0xFFF80000)) { lam1 = SPEED_OF_LIGHT / (FREQ1_GLO + DFRQ1_GLO * (freq - 7)); rtcm->obs.data[index].P[0] = pr1; cp1 = adjcp(rtcm, sat, 0, ppr1 * 0.0005 / lam1); rtcm->obs.data[index].L[0] = pr1 / lam1 + cp1; } rtcm->obs.data[index].LLI[0] = lossoflock(rtcm, sat, 0, lock1); rtcm->obs.data[index].SNR[0] = snratio(cnr1 * 0.25); rtcm->obs.data[index].code[0] = code1 ? CODE_L1P : CODE_L1C; if (pr21 != static_cast(0xFFFFE000)) { rtcm->obs.data[index].P[1] = pr1 + pr21 * 0.02; } if (ppr2 != static_cast(0xFFF80000)) { lam2 = SPEED_OF_LIGHT / (FREQ2_GLO + DFRQ2_GLO * (freq - 7)); cp2 = adjcp(rtcm, sat, 1, ppr2 * 0.0005 / lam2); rtcm->obs.data[index].L[1] = pr1 / lam2 + cp2; } rtcm->obs.data[index].LLI[1] = lossoflock(rtcm, sat, 1, lock2); rtcm->obs.data[index].SNR[1] = snratio(cnr2 * 0.25); rtcm->obs.data[index].code[1] = code2 ? CODE_L2P : CODE_L2C; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1013: system parameters ---------------------------------------*/ int decode_type1013(rtcm_t *rtcm __attribute__((unused))) { return 0; } /* decode type 1019: gps ephemerides -----------------------------------------*/ int decode_type1019(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, sys = SYS_GPS; if (i + 476 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; week = getbitu(rtcm->buff, i, 10); i += 10; eph.sva = getbitu(rtcm->buff, i, 4); i += 4; eph.code = getbitu(rtcm->buff, i, 2); i += 2; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; eph.iode = getbitu(rtcm->buff, i, 8); i += 8; toc = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.f2 = getbits(rtcm->buff, i, 8) * TWO_N55; i += 8; eph.f1 = getbits(rtcm->buff, i, 16) * TWO_N43; i += 16; eph.f0 = getbits(rtcm->buff, i, 22) * TWO_N31; i += 22; eph.iodc = getbitu(rtcm->buff, i, 10); i += 10; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.tgd[0] = getbits(rtcm->buff, i, 8) * TWO_N31; i += 8; eph.svh = getbitu(rtcm->buff, i, 6); i += 6; eph.flag = getbitu(rtcm->buff, i, 1); i += 1; eph.fit = getbitu(rtcm->buff, i, 1) ? 0.0 : 4.0; /* 0:4hr, 1:>4hr */ } else { trace(2, "rtcm3 1019 length error: len=%d\n", rtcm->len); return -1; } if (prn >= 40) { sys = SYS_SBS; prn += 80; } trace(4, "decode_type1019: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d iode=%3d iodc=%3d week=%d toe=%6.0f toc=%6.0f svh=%02X", prn, eph.iode, eph.iodc, week, eph.toes, toc, eph.svh); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1019 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = adjgpsweek(week); eph.toe = gpst2time(eph.week, eph.toes); eph.toc = gpst2time(eph.week, toc); eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; if (!strstr(rtcm->opt, "-EPHALL")) { if (eph.iode == rtcm->nav.eph[sat - 1].iode) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 1020: glonass ephemerides -------------------------------------*/ int decode_type1020(rtcm_t *rtcm) { geph_t geph = {0, -1, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0.0}, {0.0}, {0.0}, 0.0, 0.0, 0.0}; double tk_h, tk_m, tk_s, toe, tow, tod, tof; char *msg; int i = 24 + 12, prn, sat, week, tb, bn, sys = SYS_GLO; if (i + 348 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; geph.frq = getbitu(rtcm->buff, i, 5) - 7; i += 5 + 2 + 2; tk_h = getbitu(rtcm->buff, i, 5); i += 5; tk_m = getbitu(rtcm->buff, i, 6); i += 6; tk_s = getbitu(rtcm->buff, i, 1) * 30.0; i += 1; bn = getbitu(rtcm->buff, i, 1); i += 1 + 1; tb = getbitu(rtcm->buff, i, 7); i += 7; geph.vel[0] = getbitg(rtcm->buff, i, 24) * TWO_N20 * 1E3; i += 24; geph.pos[0] = getbitg(rtcm->buff, i, 27) * TWO_N11 * 1E3; i += 27; geph.acc[0] = getbitg(rtcm->buff, i, 5) * TWO_N30 * 1E3; i += 5; geph.vel[1] = getbitg(rtcm->buff, i, 24) * TWO_N20 * 1E3; i += 24; geph.pos[1] = getbitg(rtcm->buff, i, 27) * TWO_N11 * 1E3; i += 27; geph.acc[1] = getbitg(rtcm->buff, i, 5) * TWO_N30 * 1E3; i += 5; geph.vel[2] = getbitg(rtcm->buff, i, 24) * TWO_N20 * 1E3; i += 24; geph.pos[2] = getbitg(rtcm->buff, i, 27) * TWO_N11 * 1E3; i += 27; geph.acc[2] = getbitg(rtcm->buff, i, 5) * TWO_N30 * 1E3; i += 5 + 1; geph.gamn = getbitg(rtcm->buff, i, 11) * TWO_N40; i += 11 + 3; geph.taun = getbitg(rtcm->buff, i, 22) * TWO_N30; } else { trace(2, "rtcm3 1020 length error: len=%d\n", rtcm->len); return -1; } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1020 satellite number error: prn=%d\n", prn); return -1; } trace(4, "decode_type1020: prn=%d tk=%02.0f:%02.0f:%02.0f\n", prn, tk_h, tk_m, tk_s); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d tk=%02.0f:%02.0f:%02.0f frq=%2d bn=%d tb=%d", prn, tk_h, tk_m, tk_s, geph.frq, bn, tb); } geph.sat = sat; geph.svh = bn; geph.iode = tb & 0x7F; if (rtcm->time.time == 0) { rtcm->time = utc2gpst(timeget()); } tow = time2gpst(gpst2utc(rtcm->time), &week); tod = fmod(tow, 86400.0); tow -= tod; tof = tk_h * 3600.0 + tk_m * 60.0 + tk_s - 10800.0; /* lt->utc */ if (tof < tod - 43200.0) { tof += 86400.0; } else if (tof > tod + 43200.0) { tof -= 86400.0; } geph.tof = utc2gpst(gpst2time(week, tow + tof)); toe = tb * 900.0 - 10800.0; /* lt->utc */ if (toe < tod - 43200.0) { toe += 86400.0; } else if (toe > tod + 43200.0) { toe -= 86400.0; } geph.toe = utc2gpst(gpst2time(week, tow + toe)); /* utc->gpst */ if (!strstr(rtcm->opt, "-EPHALL")) { if (fabs(timediff(geph.toe, rtcm->nav.geph[prn - 1].toe)) < 1.0 && geph.svh == rtcm->nav.geph[prn - 1].svh) { return 0; /* unchanged */ } } rtcm->nav.geph[prn - 1] = geph; rtcm->ephsat = sat; return 2; } /* decode type 1021: helmert/abridged molodenski -----------------------------*/ int decode_type1021(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1021: not supported message\n"); return 0; } /* decode type 1022: moledenski-badekas transformation -----------------------*/ int decode_type1022(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1022: not supported message\n"); return 0; } /* decode type 1023: residual, ellipoidal grid representation ----------------*/ int decode_type1023(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1023: not supported message\n"); return 0; } /* decode type 1024: residual, plane grid representation ---------------------*/ int decode_type1024(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1024: not supported message\n"); return 0; } /* decode type 1025: projection (types except LCC2SP, OM) ---------------------*/ int decode_type1025(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1025: not supported message\n"); return 0; } /* decode type 1026: projection (LCC2SP - lambert conic conformal (2sp)) -----*/ int decode_type1026(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1026: not supported message\n"); return 0; } /* decode type 1027: projection (type OM - oblique mercator) -----------------*/ int decode_type1027(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1027: not supported message\n"); return 0; } /* decode type 1029: unicode text string -------------------------------------*/ int decode_type1029(rtcm_t *rtcm) { char *msg; int i = 24 + 12, j, staid, nchar; // mjd, tod, nchar, cunit; if (i + 60 <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; /* mjd = getbitu(rtcm->buff, i, 16); */ i += 16; /* tod = getbitu(rtcm->buff, i, 17); */ i += 17; nchar = getbitu(rtcm->buff, i, 7); i += 7; /* cunit = getbitu(rtcm->buff, i, 8); */ i += 8; } else { trace(2, "rtcm3 1029 length error: len=%d\n", rtcm->len); return -1; } if (i + nchar * 8 > rtcm->len * 8) { trace(2, "rtcm3 1029 length error: len=%d nchar=%d\n", rtcm->len, nchar); return -1; } for (j = 0; j < nchar && j < 126; j++) { rtcm->msg[j] = getbitu(rtcm->buff, i, 8); i += 8; } rtcm->msg[j] = '\0'; if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d text=%s", staid, rtcm->msg); } return 0; } /* decode type 1030: network rtk residual ------------------------------------*/ int decode_type1030(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1030: not supported message\n"); return 0; } /* decode type 1031: glonass network rtk residual ----------------------------*/ int decode_type1031(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1031: not supported message\n"); return 0; } /* decode type 1032: physical reference station position information ---------*/ int decode_type1032(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1032: not supported message\n"); return 0; } /* decode type 1033: receiver and antenna descriptor -------------------------*/ int decode_type1033(rtcm_t *rtcm) { char des[32] = "", sno[32] = "", rec[32] = "", ver[32] = "", rsn[32] = ""; char *msg; int i = 24 + 12, j, staid, n, m, n1, n2, n3, setup; n = getbitu(rtcm->buff, i + 12, 8); m = getbitu(rtcm->buff, i + 28 + 8 * n, 8); n1 = getbitu(rtcm->buff, i + 36 + 8 * (n + m), 8); n2 = getbitu(rtcm->buff, i + 44 + 8 * (n + m + n1), 8); n3 = getbitu(rtcm->buff, i + 52 + 8 * (n + m + n1 + n2), 8); if (i + 60 + 8 * (n + m + n1 + n2 + n3) <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12 + 8; for (j = 0; j < n && j < 31; j++) { des[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } setup = getbitu(rtcm->buff, i, 8); i += 8 + 8; for (j = 0; j < m && j < 31; j++) { sno[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } i += 8; for (j = 0; j < n1 && j < 31; j++) { rec[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } i += 8; for (j = 0; j < n2 && j < 31; j++) { ver[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } i += 8; for (j = 0; j < n3 && j < 31; j++) { rsn[j] = static_cast(getbitu(rtcm->buff, i, 8)); i += 8; } } else { trace(2, "rtcm3 1033 length error: len=%d\n", rtcm->len); return -1; } if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d", staid); } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } strncpy(rtcm->sta.antdes, des, n); rtcm->sta.antdes[n] = '\0'; rtcm->sta.antsetup = setup; strncpy(rtcm->sta.antsno, sno, m); rtcm->sta.antsno[m] = '\0'; strncpy(rtcm->sta.rectype, rec, n1); rtcm->sta.rectype[n1] = '\0'; strncpy(rtcm->sta.recver, ver, n2); rtcm->sta.recver[n2] = '\0'; strncpy(rtcm->sta.recsno, rsn, n3); rtcm->sta.recsno[n3] = '\0'; trace(3, "rtcm3 1033: ant=%s:%s rec=%s:%s:%s\n", des, sno, rec, ver, rsn); return 5; } /* decode type 1034: gps network fkp gradient --------------------------------*/ int decode_type1034(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1034: not supported message\n"); return 0; } /* decode type 1035: glonass network fkp gradient ----------------------------*/ int decode_type1035(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1035: not supported message\n"); return 0; } /* decode type 1037: glonass network rtk ionospheric correction difference ---*/ int decode_type1037(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1037: not supported message\n"); return 0; } /* decode type 1038: glonass network rtk geometic correction difference ------*/ int decode_type1038(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1038: not supported message\n"); return 0; } /* decode type 1039: glonass network rtk combined correction difference ------*/ int decode_type1039(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1039: not supported message\n"); return 0; } /* decode type 1044: qzss ephemerides (ref [15]) -----------------------------*/ int decode_type1044(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, sys = SYS_QZS; if (i + 473 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 4) + 192; i += 4; toc = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.f2 = getbits(rtcm->buff, i, 8) * TWO_N55; i += 8; eph.f1 = getbits(rtcm->buff, i, 16) * TWO_N43; i += 16; eph.f0 = getbits(rtcm->buff, i, 22) * TWO_N31; i += 22; eph.iode = getbitu(rtcm->buff, i, 8); i += 8; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; eph.code = getbitu(rtcm->buff, i, 2); i += 2; week = getbitu(rtcm->buff, i, 10); i += 10; eph.sva = getbitu(rtcm->buff, i, 4); i += 4; eph.svh = getbitu(rtcm->buff, i, 6); i += 6; eph.tgd[0] = getbits(rtcm->buff, i, 8) * TWO_N31; i += 8; eph.iodc = getbitu(rtcm->buff, i, 10); i += 10; eph.fit = getbitu(rtcm->buff, i, 1) ? 0.0 : 2.0; /* 0:2hr, 1:>2hr */ } else { trace(2, "rtcm3 1044 length error: len=%d\n", rtcm->len); return -1; } trace(4, "decode_type1044: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%3d iode=%3d iodc=%3d week=%d toe=%6.0f toc=%6.0f svh=%02X", prn, eph.iode, eph.iodc, week, eph.toes, toc, eph.svh); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1044 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = adjgpsweek(week); eph.toe = gpst2time(eph.week, eph.toes); eph.toc = gpst2time(eph.week, toc); eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; if (!strstr(rtcm->opt, "-EPHALL")) { if (eph.iode == rtcm->nav.eph[sat - 1].iode && eph.iodc == rtcm->nav.eph[sat - 1].iodc) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 1045: galileo satellite ephemerides (ref [15]) ----------------*/ int decode_type1045(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, e5a_hs, e5a_dvs, sys = SYS_GAL; if (i + 484 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; week = getbitu(rtcm->buff, i, 12); i += 12; /* gst-week */ eph.iode = getbitu(rtcm->buff, i, 10); i += 10; eph.sva = getbitu(rtcm->buff, i, 8); i += 8; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; toc = getbitu(rtcm->buff, i, 14) * 60.0; i += 14; eph.f2 = getbits(rtcm->buff, i, 6) * TWO_N59; i += 6; eph.f1 = getbits(rtcm->buff, i, 21) * TWO_N46; i += 21; eph.f0 = getbits(rtcm->buff, i, 31) * TWO_N34; i += 31; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 14) * 60.0; i += 14; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.tgd[0] = getbits(rtcm->buff, i, 10) * TWO_N32; i += 10; /* E5a/E1 */ e5a_hs = getbitu(rtcm->buff, i, 2); i += 2; /* OSHS */ e5a_dvs = getbitu(rtcm->buff, i, 1); i += 1; /* OSDVS */ //rsv = getbitu(rtcm->buff, i, 7); } else { trace(2, "rtcm3 1045 length error: len=%d\n", rtcm->len); return -1; } trace(4, "decode_type1045: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d iode=%3d week=%d toe=%6.0f toc=%6.0f hs=%d dvs=%d", prn, eph.iode, week, eph.toes, toc, e5a_hs, e5a_dvs); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1045 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = week + 1024; /* gal-week = gst-week + 1024 */ eph.toe = gpst2time(eph.week, eph.toes); eph.toc = gpst2time(eph.week, toc); eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; eph.svh = (e5a_hs << 4) + (e5a_dvs << 3); eph.code = 2; /* data source = f/nav e5a */ if (!strstr(rtcm->opt, "-EPHALL")) { if (eph.iode == rtcm->nav.eph[sat - 1].iode) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 1046: galileo satellite ephemerides (extension for IGS MGEX) --*/ int decode_type1046(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, e5a_hs, e5a_dvs, sys = SYS_GAL; if (i + 484 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; week = getbitu(rtcm->buff, i, 12); i += 12; /* gst-week */ eph.iode = getbitu(rtcm->buff, i, 10); i += 10; eph.sva = getbitu(rtcm->buff, i, 8); i += 8; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; toc = getbitu(rtcm->buff, i, 14) * 60.0; i += 14; eph.f2 = getbits(rtcm->buff, i, 6) * TWO_N59; i += 6; eph.f1 = getbits(rtcm->buff, i, 21) * TWO_N46; i += 21; eph.f0 = getbits(rtcm->buff, i, 31) * TWO_N34; i += 31; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 14) * 60.0; i += 14; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.tgd[0] = getbits(rtcm->buff, i, 10) * TWO_N32; i += 10; /* E5a/E1 */ e5a_hs = getbitu(rtcm->buff, i, 2); i += 2; /* OSHS */ e5a_dvs = getbitu(rtcm->buff, i, 1); i += 1; /* OSDVS */ //rsv = getbitu(rtcm->buff, i, 7); } else { trace(2, "rtcm3 1046 length error: len=%d\n", rtcm->len); return -1; } trace(4, "decode_type1046: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d iode=%3d week=%d toe=%6.0f toc=%6.0f hs=%d dvs=%d", prn, eph.iode, week, eph.toes, toc, e5a_hs, e5a_dvs); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1046 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = week + 1024; /* gal-week = gst-week + 1024 */ eph.toe = gpst2time(eph.week, eph.toes); eph.toc = gpst2time(eph.week, toc); eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; eph.svh = (e5a_hs << 4) + (e5a_dvs << 3); eph.code = 2; /* data source = f/nav e5a */ if (!strstr(rtcm->opt, "-EPHALL")) { if (eph.iode == rtcm->nav.eph[sat - 1].iode) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 1047: beidou ephemerides (tentative mt and format) ------------*/ int decode_type1047(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; ; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, sys = SYS_BDS; if (i + 476 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; week = getbitu(rtcm->buff, i, 10); i += 10; eph.sva = getbitu(rtcm->buff, i, 4); i += 4; eph.code = getbitu(rtcm->buff, i, 2); i += 2; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; eph.iode = getbitu(rtcm->buff, i, 8); i += 8; toc = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.f2 = getbits(rtcm->buff, i, 8) * TWO_N55; i += 8; eph.f1 = getbits(rtcm->buff, i, 16) * TWO_N43; i += 16; eph.f0 = getbits(rtcm->buff, i, 22) * TWO_N31; i += 22; eph.iodc = getbitu(rtcm->buff, i, 10); i += 10; eph.crs = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 16) * 16.0; i += 16; eph.cic = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 16) * TWO_N29; i += 16; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 16) * TWO_N5; i += 16; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.tgd[0] = getbits(rtcm->buff, i, 8) * TWO_N31; i += 8; eph.svh = getbitu(rtcm->buff, i, 6); i += 6; eph.flag = getbitu(rtcm->buff, i, 1); i += 1; eph.fit = getbitu(rtcm->buff, i, 1) ? 0.0 : 4.0; /* 0:4hr, 1:>4hr */ } else { trace(2, "rtcm3 1047 length error: len=%d\n", rtcm->len); return -1; } trace(4, "decode_type1047: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d iode=%3d iodc=%3d week=%d toe=%6.0f toc=%6.0f svh=%02X", prn, eph.iode, eph.iodc, week, eph.toes, toc, eph.svh); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 1047 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = adjbdtweek(week); eph.toe = bdt2gpst(bdt2time(eph.week, eph.toes)); /* bdt -> gpst */ eph.toc = bdt2gpst(bdt2time(eph.week, toc)); /* bdt -> gpst */ eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; if (!strstr(rtcm->opt, "-EPHALL")) { if (timediff(eph.toe, rtcm->nav.eph[sat - 1].toe) == 0.0 && eph.iode == rtcm->nav.eph[sat - 1].iode && eph.iodc == rtcm->nav.eph[sat - 1].iodc) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode type 63: beidou ephemerides (rtcm draft) ---------------------------*/ int decode_type63(rtcm_t *rtcm) { eph_t eph = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; double toc, sqrtA; char *msg; int i = 24 + 12, prn, sat, week, sys = SYS_BDS; if (i + 499 <= rtcm->len * 8) { prn = getbitu(rtcm->buff, i, 6); i += 6; week = getbitu(rtcm->buff, i, 13); i += 13; eph.sva = getbitu(rtcm->buff, i, 4); i += 4; eph.idot = getbits(rtcm->buff, i, 14) * TWO_N43 * SC2RAD; i += 14; eph.iode = getbitu(rtcm->buff, i, 5); i += 5; /* AODE */ toc = getbitu(rtcm->buff, i, 17) * 8.0; i += 17; eph.f2 = getbits(rtcm->buff, i, 11) * TWO_N55; i += 11; eph.f1 = getbits(rtcm->buff, i, 22) * TWO_N50; i += 22; eph.f0 = getbits(rtcm->buff, i, 24) * TWO_N33; i += 24; eph.iodc = getbitu(rtcm->buff, i, 5); i += 5; /* AODC */ eph.crs = getbits(rtcm->buff, i, 18) * TWO_N6; i += 18; eph.deln = getbits(rtcm->buff, i, 16) * TWO_N43 * SC2RAD; i += 16; eph.M0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cuc = getbits(rtcm->buff, i, 18) * TWO_N31; i += 18; eph.e = getbitu(rtcm->buff, i, 32) * TWO_N33; i += 32; eph.cus = getbits(rtcm->buff, i, 18) * TWO_N31; i += 18; sqrtA = getbitu(rtcm->buff, i, 32) * TWO_N19; i += 32; eph.toes = getbitu(rtcm->buff, i, 17) * 8.0; i += 17; eph.cic = getbits(rtcm->buff, i, 18) * TWO_N31; i += 18; eph.OMG0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.cis = getbits(rtcm->buff, i, 18) * TWO_N31; i += 18; eph.i0 = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.crc = getbits(rtcm->buff, i, 18) * TWO_N6; i += 18; eph.omg = getbits(rtcm->buff, i, 32) * TWO_N31 * SC2RAD; i += 32; eph.OMGd = getbits(rtcm->buff, i, 24) * TWO_N43 * SC2RAD; i += 24; eph.tgd[0] = getbits(rtcm->buff, i, 10) * 1E-10; i += 10; eph.tgd[1] = getbits(rtcm->buff, i, 10) * 1E-10; i += 10; eph.svh = getbitu(rtcm->buff, i, 1); i += 1; } else { trace(2, "rtcm3 63 length error: len=%d\n", rtcm->len); return -1; } trace(4, "decode_type63: prn=%d iode=%d toe=%.0f\n", prn, eph.iode, eph.toes); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " prn=%2d iode=%3d iodc=%3d week=%d toe=%6.0f toc=%6.0f svh=%02X", prn, eph.iode, eph.iodc, week, eph.toes, toc, eph.svh); } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 63 satellite number error: prn=%d\n", prn); return -1; } eph.sat = sat; eph.week = adjbdtweek(week); eph.toe = bdt2gpst(bdt2time(eph.week, eph.toes)); /* bdt -> gpst */ eph.toc = bdt2gpst(bdt2time(eph.week, toc)); /* bdt -> gpst */ eph.ttr = rtcm->time; eph.A = sqrtA * sqrtA; if (!strstr(rtcm->opt, "-EPHALL")) { if (timediff(eph.toe, rtcm->nav.eph[sat - 1].toe) == 0.0 && eph.iode == rtcm->nav.eph[sat - 1].iode && eph.iodc == rtcm->nav.eph[sat - 1].iodc) { return 0; /* unchanged */ } } rtcm->nav.eph[sat - 1] = eph; rtcm->ephsat = sat; return 2; } /* decode ssr 1, 4 message header ---------------------------------------------*/ int decode_ssr1_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *refd, int *hsize) { double tod, tow; char *msg; int i = 24 + 12, nsat, udi, provid = 0, solid = 0, ns = 6; #ifndef SSR_QZSS_DRAFT_V05 ns = sys == SYS_QZS ? 4 : 6; #endif if (i + (sys == SYS_GLO ? 53 : 50 + ns) > rtcm->len * 8) { return -1; } if (sys == SYS_GLO) { tod = getbitu(rtcm->buff, i, 17); i += 17; adjday_glot(rtcm, tod); } else { tow = getbitu(rtcm->buff, i, 20); i += 20; adjweek(rtcm, tow); } udi = getbitu(rtcm->buff, i, 4); i += 4; *sync = getbitu(rtcm->buff, i, 1); i += 1; *refd = getbitu(rtcm->buff, i, 1); i += 1; /* satellite ref datum */ *iod = getbitu(rtcm->buff, i, 4); i += 4; /* iod */ provid = getbitu(rtcm->buff, i, 16); i += 16; /* provider id */ solid = getbitu(rtcm->buff, i, 4); i += 4; /* solution id */ nsat = getbitu(rtcm->buff, i, ns); i += ns; *udint = SSRUDINT[udi]; trace(4, "decode_ssr1_head: time=%s sys=%d nsat=%d sync=%d iod=%d provid=%d solid=%d\n", time_str(rtcm->time, 2), sys, nsat, *sync, *iod, provid, solid); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " %s nsat=%2d iod=%2d udi=%2d sync=%d", time_str(rtcm->time, 2), nsat, *iod, udi, *sync); } *hsize = i; return nsat; } /* decode ssr 2, 3, 5, 6 message header -----------------------------------------*/ int decode_ssr2_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *hsize) { double tod, tow; char *msg; int i = 24 + 12, nsat, udi, provid = 0, solid = 0, ns = 6; #ifndef SSR_QZSS_DRAFT_V05 ns = sys == SYS_QZS ? 4 : 6; #endif if (i + (sys == SYS_GLO ? 52 : 49 + ns) > rtcm->len * 8) { return -1; } if (sys == SYS_GLO) { tod = getbitu(rtcm->buff, i, 17); i += 17; adjday_glot(rtcm, tod); } else { tow = getbitu(rtcm->buff, i, 20); i += 20; adjweek(rtcm, tow); } udi = getbitu(rtcm->buff, i, 4); i += 4; *sync = getbitu(rtcm->buff, i, 1); i += 1; *iod = getbitu(rtcm->buff, i, 4); i += 4; provid = getbitu(rtcm->buff, i, 16); i += 16; /* provider id */ solid = getbitu(rtcm->buff, i, 4); i += 4; /* solution id */ nsat = getbitu(rtcm->buff, i, ns); i += ns; *udint = SSRUDINT[udi]; trace(4, "decode_ssr2_head: time=%s sys=%d nsat=%d sync=%d iod=%d provid=%d solid=%d\n", time_str(rtcm->time, 2), sys, nsat, *sync, *iod, provid, solid); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " %s nsat=%2d iod=%2d udi=%2d sync=%d", time_str(rtcm->time, 2), nsat, *iod, udi, *sync); } *hsize = i; return nsat; } /* decode ssr 7 message header -----------------------------------------------*/ int decode_ssr7_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *dispe, int *mw, int *hsize) { double tod, tow; char *msg; int i = 24 + 12, nsat, udi, provid = 0, solid = 0, ns = 6; #ifndef SSR_QZSS_DRAFT_V05 ns = sys == SYS_QZS ? 4 : 6; #endif if (i + (sys == SYS_GLO ? 54 : 51 + ns) > rtcm->len * 8) { return -1; } if (sys == SYS_GLO) { tod = getbitu(rtcm->buff, i, 17); i += 17; adjday_glot(rtcm, tod); } else { tow = getbitu(rtcm->buff, i, 20); i += 20; adjweek(rtcm, tow); } udi = getbitu(rtcm->buff, i, 4); i += 4; *sync = getbitu(rtcm->buff, i, 1); i += 1; *iod = getbitu(rtcm->buff, i, 4); i += 4; provid = getbitu(rtcm->buff, i, 16); i += 16; /* provider id */ solid = getbitu(rtcm->buff, i, 4); i += 4; /* solution id */ *dispe = getbitu(rtcm->buff, i, 1); i += 1; /* dispersive bias consistency ind */ *mw = getbitu(rtcm->buff, i, 1); i += 1; /* MW consistency indicator */ nsat = getbitu(rtcm->buff, i, ns); i += ns; *udint = SSRUDINT[udi]; trace(4, "decode_ssr7_head: time=%s sys=%d nsat=%d sync=%d iod=%d provid=%d solid=%d\n", time_str(rtcm->time, 2), sys, nsat, *sync, *iod, provid, solid); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " %s nsat=%2d iod=%2d udi=%2d sync=%d", time_str(rtcm->time, 2), nsat, *iod, udi, *sync); } *hsize = i; return nsat; } /* decode ssr 1: orbit corrections -------------------------------------------*/ int decode_ssr1(rtcm_t *rtcm, int sys) { double udint, deph[3], ddeph[3]; int i, j, k, type, sync, iod, nsat, prn, sat, iode, iodcrc, refd = 0, np, ni, nj, offp; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr1_head(rtcm, sys, &sync, &iod, &udint, &refd, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; ni = 8; nj = 0; offp = 0; break; case SYS_GLO: np = 5; ni = 8; nj = 0; offp = 0; break; case SYS_GAL: np = 6; ni = 10; nj = 0; offp = 0; break; case SYS_QZS: np = 4; ni = 8; nj = 0; offp = 192; break; case SYS_BDS: np = 6; ni = 10; nj = 24; offp = 1; break; case SYS_SBS: np = 6; ni = 9; nj = 24; offp = 120; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 121 + np + ni + nj <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; iode = getbitu(rtcm->buff, i, ni); i += ni; iodcrc = getbitu(rtcm->buff, i, nj); i += nj; deph[0] = getbits(rtcm->buff, i, 22) * 1E-4; i += 22; deph[1] = getbits(rtcm->buff, i, 20) * 4E-4; i += 20; deph[2] = getbits(rtcm->buff, i, 20) * 4E-4; i += 20; ddeph[0] = getbits(rtcm->buff, i, 21) * 1E-6; i += 21; ddeph[1] = getbits(rtcm->buff, i, 19) * 4E-6; i += 19; ddeph[2] = getbits(rtcm->buff, i, 19) * 4E-6; i += 19; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[0] = rtcm->time; rtcm->ssr[sat - 1].udi[0] = udint; rtcm->ssr[sat - 1].iod[0] = iod; rtcm->ssr[sat - 1].iode = iode; /* sbas/bds: toe/t0 modulo */ rtcm->ssr[sat - 1].iodcrc = iodcrc; /* sbas/bds: iod crc */ rtcm->ssr[sat - 1].refd = refd; for (k = 0; k < 3; k++) { rtcm->ssr[sat - 1].deph[k] = deph[k]; rtcm->ssr[sat - 1].ddeph[k] = ddeph[k]; } rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 2: clock corrections -------------------------------------------*/ int decode_ssr2(rtcm_t *rtcm, int sys) { double udint, dclk[3]; int i, j, k, type, sync, iod, nsat, prn, sat, np, offp; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr2_head(rtcm, sys, &sync, &iod, &udint, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; offp = 0; break; case SYS_GLO: np = 5; offp = 0; break; case SYS_GAL: np = 6; offp = 0; break; case SYS_QZS: np = 4; offp = 192; break; case SYS_BDS: np = 6; offp = 1; break; case SYS_SBS: np = 6; offp = 120; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 70 + np <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; dclk[0] = getbits(rtcm->buff, i, 22) * 1E-4; i += 22; dclk[1] = getbits(rtcm->buff, i, 21) * 1E-6; i += 21; dclk[2] = getbits(rtcm->buff, i, 27) * 2E-8; i += 27; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[1] = rtcm->time; rtcm->ssr[sat - 1].udi[1] = udint; rtcm->ssr[sat - 1].iod[1] = iod; for (k = 0; k < 3; k++) { rtcm->ssr[sat - 1].dclk[k] = dclk[k]; } rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 3: satellite code biases ---------------------------------------*/ int decode_ssr3(rtcm_t *rtcm, int sys) { const int *codes; double udint, bias, cbias[MAXCODE]; int i, j, k, type, mode, sync, iod, nsat, prn, sat, nbias, np, offp, ncode; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr2_head(rtcm, sys, &sync, &iod, &udint, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; offp = 0; codes = CODES_GPS; ncode = 17; break; case SYS_GLO: np = 5; offp = 0; codes = CODES_GLO; ncode = 4; break; case SYS_GAL: np = 6; offp = 0; codes = CODES_GAL; ncode = 19; break; case SYS_QZS: np = 4; offp = 192; codes = CODES_QZS; ncode = 13; break; case SYS_BDS: np = 6; offp = 1; codes = CODES_BDS; ncode = 9; break; case SYS_SBS: np = 6; offp = 120; codes = CODES_SBS; ncode = 4; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 5 + np <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; nbias = getbitu(rtcm->buff, i, 5); i += 5; for (k = 0; k < MAXCODE; k++) { cbias[k] = 0.0; } for (k = 0; k < nbias && i + 19 <= rtcm->len * 8; k++) { mode = getbitu(rtcm->buff, i, 5); i += 5; bias = getbits(rtcm->buff, i, 14) * 0.01; i += 14; if (mode <= ncode) { cbias[codes[mode] - 1] = static_cast(bias); } else { trace(2, "rtcm3 %d not supported mode: mode=%d\n", type, mode); } } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[4] = rtcm->time; rtcm->ssr[sat - 1].udi[4] = udint; rtcm->ssr[sat - 1].iod[4] = iod; for (k = 0; k < MAXCODE; k++) { rtcm->ssr[sat - 1].cbias[k] = static_cast(cbias[k]); } rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 4: combined orbit and clock corrections ------------------------*/ int decode_ssr4(rtcm_t *rtcm, int sys) { double udint, deph[3], ddeph[3], dclk[3]; int i, j, k, type, nsat, sync, iod, prn, sat, iode, iodcrc, refd = 0, np, ni, nj, offp; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr1_head(rtcm, sys, &sync, &iod, &udint, &refd, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; ni = 8; nj = 0; offp = 0; break; case SYS_GLO: np = 5; ni = 8; nj = 0; offp = 0; break; case SYS_GAL: np = 6; ni = 10; nj = 0; offp = 0; break; case SYS_QZS: np = 4; ni = 8; nj = 0; offp = 192; break; case SYS_BDS: np = 6; ni = 10; nj = 24; offp = 1; break; case SYS_SBS: np = 6; ni = 9; nj = 24; offp = 120; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 191 + np + ni + nj <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; iode = getbitu(rtcm->buff, i, ni); i += ni; iodcrc = getbitu(rtcm->buff, i, nj); i += nj; deph[0] = getbits(rtcm->buff, i, 22) * 1E-4; i += 22; deph[1] = getbits(rtcm->buff, i, 20) * 4E-4; i += 20; deph[2] = getbits(rtcm->buff, i, 20) * 4E-4; i += 20; ddeph[0] = getbits(rtcm->buff, i, 21) * 1E-6; i += 21; ddeph[1] = getbits(rtcm->buff, i, 19) * 4E-6; i += 19; ddeph[2] = getbits(rtcm->buff, i, 19) * 4E-6; i += 19; dclk[0] = getbits(rtcm->buff, i, 22) * 1E-4; i += 22; dclk[1] = getbits(rtcm->buff, i, 21) * 1E-6; i += 21; dclk[2] = getbits(rtcm->buff, i, 27) * 2E-8; i += 27; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[0] = rtcm->ssr[sat - 1].t0[1] = rtcm->time; rtcm->ssr[sat - 1].udi[0] = rtcm->ssr[sat - 1].udi[1] = udint; rtcm->ssr[sat - 1].iod[0] = rtcm->ssr[sat - 1].iod[1] = iod; rtcm->ssr[sat - 1].iode = iode; rtcm->ssr[sat - 1].iodcrc = iodcrc; rtcm->ssr[sat - 1].refd = refd; for (k = 0; k < 3; k++) { rtcm->ssr[sat - 1].deph[k] = deph[k]; rtcm->ssr[sat - 1].ddeph[k] = ddeph[k]; rtcm->ssr[sat - 1].dclk[k] = dclk[k]; } rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 5: ura ---------------------------------------------------------*/ int decode_ssr5(rtcm_t *rtcm, int sys) { double udint; int i, j, type, nsat, sync, iod, prn, sat, ura, np, offp; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr2_head(rtcm, sys, &sync, &iod, &udint, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; offp = 0; break; case SYS_GLO: np = 5; offp = 0; break; case SYS_GAL: np = 6; offp = 0; break; case SYS_QZS: np = 4; offp = 192; break; case SYS_BDS: np = 6; offp = 1; break; case SYS_SBS: np = 6; offp = 120; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 6 + np <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; ura = getbitu(rtcm->buff, i, 6); i += 6; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[3] = rtcm->time; rtcm->ssr[sat - 1].udi[3] = udint; rtcm->ssr[sat - 1].iod[3] = iod; rtcm->ssr[sat - 1].ura = ura; rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 6: high rate clock correction ----------------------------------*/ int decode_ssr6(rtcm_t *rtcm, int sys) { double udint, hrclk; int i, j, type, nsat, sync, iod, prn, sat, np, offp; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr2_head(rtcm, sys, &sync, &iod, &udint, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; offp = 0; break; case SYS_GLO: np = 5; offp = 0; break; case SYS_GAL: np = 6; offp = 0; break; case SYS_QZS: np = 4; offp = 192; break; case SYS_BDS: np = 6; offp = 1; break; case SYS_SBS: np = 6; offp = 120; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 22 + np <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; hrclk = getbits(rtcm->buff, i, 22) * 1E-4; i += 22; if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[2] = rtcm->time; rtcm->ssr[sat - 1].udi[2] = udint; rtcm->ssr[sat - 1].iod[2] = iod; rtcm->ssr[sat - 1].hrclk = hrclk; rtcm->ssr[sat - 1].update = 1; } return sync ? 0 : 10; } /* decode ssr 7: phase bias --------------------------------------------------*/ int decode_ssr7(rtcm_t *rtcm, int sys) { const int *codes; double udint, bias, std, pbias[MAXCODE], stdpb[MAXCODE]; int i, j, k, type, mode, sync, iod, nsat, prn, sat, nbias, ncode, np, mw, offp; int dispe, yaw_ang, yaw_rate; type = getbitu(rtcm->buff, 24, 12); if ((nsat = decode_ssr7_head(rtcm, sys, &sync, &iod, &udint, &dispe, &mw, &i)) < 0) { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } switch (sys) { case SYS_GPS: np = 6; offp = 0; codes = CODES_GPS; ncode = 17; break; case SYS_GLO: np = 5; offp = 0; codes = CODES_GLO; ncode = 4; break; case SYS_GAL: np = 6; offp = 0; codes = CODES_GAL; ncode = 19; break; case SYS_QZS: np = 4; offp = 192; codes = CODES_QZS; ncode = 13; break; case SYS_BDS: np = 6; offp = 1; codes = CODES_BDS; ncode = 9; break; default: return sync ? 0 : 10; } for (j = 0; j < nsat && i + 5 + 17 + np <= rtcm->len * 8; j++) { prn = getbitu(rtcm->buff, i, np) + offp; i += np; nbias = getbitu(rtcm->buff, i, 5); i += 5; yaw_ang = getbitu(rtcm->buff, i, 9); i += 9; yaw_rate = getbits(rtcm->buff, i, 8); i += 8; for (k = 0; k < MAXCODE; k++) { pbias[k] = stdpb[k] = 0.0; } for (k = 0; k < nbias && i + 49 <= rtcm->len * 8; k++) { mode = getbitu(rtcm->buff, i, 5); i += 5; /* sii = getbitu(rtcm->buff, i, 1); */ i += 1; /* integer-indicator */ /* swl = getbitu(rtcm->buff, i, 2); */ i += 2; /* WL integer-indicator */ /* sdc = getbitu(rtcm->buff, i, 4); */ i += 4; /* discontinuity counter */ bias = getbits(rtcm->buff, i, 20); i += 20; /* phase bias (m) */ std = getbitu(rtcm->buff, i, 17); i += 17; /* phase bias std-dev (m) */ if (mode <= ncode) { pbias[codes[mode] - 1] = bias * 0.0001; /* (m) */ stdpb[codes[mode] - 1] = std * 0.0001; /* (m) */ } else { trace(2, "rtcm3 %d not supported mode: mode=%d\n", type, mode); } } if (!(sat = satno(sys, prn))) { trace(2, "rtcm3 %d satellite number error: prn=%d\n", type, prn); continue; } rtcm->ssr[sat - 1].t0[5] = rtcm->time; rtcm->ssr[sat - 1].udi[5] = udint; rtcm->ssr[sat - 1].iod[5] = iod; rtcm->ssr[sat - 1].yaw_ang = yaw_ang / 256.0 * 180.0; /* (deg) */ rtcm->ssr[sat - 1].yaw_rate = yaw_rate / 8192.0 * 180.0; /* (deg/s) */ for (k = 0; k < MAXCODE; k++) { rtcm->ssr[sat - 1].pbias[k] = pbias[k]; rtcm->ssr[sat - 1].stdpb[k] = static_cast(stdpb[k]); } } return 20; } /* get signal index ----------------------------------------------------------*/ void sigindex(int sys, const unsigned char *code, const int *freq, int n, const char *opt, int *ind) { int i, nex, pri, pri_h[8] = {0}, index[8] = {0}, ex[32] = {0}; /* test code priority */ for (i = 0; i < n; i++) { if (!code[i]) { continue; } if (freq[i] > NFREQ) { /* save as extended signal if freq > NFREQ */ ex[i] = 1; continue; } /* code priority */ pri = getcodepri(sys, code[i], opt); /* select highest priority signal */ if (pri > pri_h[freq[i] - 1]) { if (index[freq[i] - 1]) { ex[index[freq[i] - 1] - 1] = 1; } pri_h[freq[i] - 1] = pri; index[freq[i] - 1] = i + 1; } else { ex[i] = 1; } } /* signal index in obs data */ for (i = nex = 0; i < n; i++) { if (ex[i] == 0) { ind[i] = freq[i] - 1; } else if (nex < NEXOBS) { ind[i] = NFREQ + nex++; } else { /* no space in obs data */ trace(2, "rtcm msm: no space in obs data sys=%d code=%d\n", sys, code[i]); ind[i] = -1; } #if 0 trace(2, "sig pos: sys=%d code=%d ex=%d ind=%d\n", sys, code[i], ex[i], ind[i]); #endif } } /* save obs data in msm message ----------------------------------------------*/ void save_msm_obs(rtcm_t *rtcm, int sys, msm_h_t *h, const double *r, const double *pr, const double *cp, const double *rr, const double *rrf, const double *cnr, const int *lock, const int *ex, const int *half) { const char *sig[32]; double tt, wl; unsigned char code[32]; char *msm_type = (char *)"", *q = nullptr; int i, j, k, type, prn, sat, fn, index = 0, freq[32], ind[32]; type = getbitu(rtcm->buff, 24, 12); switch (sys) { case SYS_GPS: msm_type = q = rtcm->msmtype[0]; break; case SYS_GLO: msm_type = q = rtcm->msmtype[1]; break; case SYS_GAL: msm_type = q = rtcm->msmtype[2]; break; case SYS_QZS: msm_type = q = rtcm->msmtype[3]; break; case SYS_SBS: msm_type = q = rtcm->msmtype[4]; break; case SYS_BDS: msm_type = q = rtcm->msmtype[5]; break; } /* id to signal */ for (i = 0; i < h->nsig; i++) { switch (sys) { case SYS_GPS: sig[i] = msm_sig_gps[h->sigs[i] - 1]; break; case SYS_GLO: sig[i] = msm_sig_glo[h->sigs[i] - 1]; break; case SYS_GAL: sig[i] = msm_sig_gal[h->sigs[i] - 1]; break; case SYS_QZS: sig[i] = msm_sig_qzs[h->sigs[i] - 1]; break; case SYS_SBS: sig[i] = msm_sig_sbs[h->sigs[i] - 1]; break; case SYS_BDS: sig[i] = msm_sig_cmp[h->sigs[i] - 1]; break; default: sig[i] = ""; break; } /* signal to rinex obs type */ code[i] = obs2code(sig[i], freq + i); /* frequency index for beidou */ if (sys == SYS_BDS) { if (freq[i] == 5) { freq[i] = 2; /* B2 */ } else if (freq[i] == 4) { freq[i] = 3; /* B3 */ } } if (code[i] != CODE_NONE) { if (q) { q += sprintf(q, "L%s%s", sig[i], i < h->nsig - 1 ? ", " : ""); } } else { if (q) { q += sprintf(q, "(%d)%s", h->sigs[i], i < h->nsig - 1 ? ", " : ""); } trace(2, "rtcm3 %d: unknown signal id=%2d\n", type, h->sigs[i]); } } trace(3, "rtcm3 %d: signals=%s\n", type, msm_type); /* get signal index */ sigindex(sys, code, freq, h->nsig, rtcm->opt, ind); for (i = j = 0; i < h->nsat; i++) { prn = h->sats[i]; if (sys == SYS_QZS) { prn += MINPRNQZS - 1; } else if (sys == SYS_SBS) { prn += MINPRNSBS - 1; } if ((sat = satno(sys, prn))) { tt = timediff(rtcm->obs.data[0].time, rtcm->time); if (rtcm->obsflag || fabs(tt) > 1E-9) { rtcm->obs.n = rtcm->obsflag = 0; } index = obsindex3(&rtcm->obs, rtcm->time, sat); } else { trace(2, "rtcm3 %d satellite error: prn=%d\n", type, prn); } for (k = 0; k < h->nsig; k++) { if (!h->cellmask[k + i * h->nsig]) { continue; } if (sat && index >= 0 && ind[k] >= 0) { /* satellite carrier wave length */ wl = satwavelen(sat, freq[k] - 1, &rtcm->nav); /* glonass wave length by extended info */ if (sys == SYS_GLO && ex && ex[i] <= 13) { fn = ex[i] - 7; wl = SPEED_OF_LIGHT / ((freq[k] == 2 ? FREQ2_GLO : FREQ1_GLO) + (freq[k] == 2 ? DFRQ2_GLO : DFRQ1_GLO) * fn); } /* pseudorange (m) */ if (r[i] != 0.0 && pr[j] > -1E12) { rtcm->obs.data[index].P[ind[k]] = r[i] + pr[j]; } /* carrier-phase (cycle) */ if (r[i] != 0.0 && cp[j] > -1E12 && wl > 0.0) { rtcm->obs.data[index].L[ind[k]] = (r[i] + cp[j]) / wl; } /* doppler (hz) */ if (rr && rrf && rrf[j] > -1E12 && wl > 0.0) { rtcm->obs.data[index].D[ind[k]] = static_cast(-(rr[i] + rrf[j]) / wl); } rtcm->obs.data[index].LLI[ind[k]] = lossoflock(rtcm, sat, ind[k], lock[j]) + (half[j] ? 3 : 0); rtcm->obs.data[index].SNR[ind[k]] = static_cast(cnr[j] * 4.0); rtcm->obs.data[index].code[ind[k]] = code[k]; } j++; } } } /* decode type msm message header --------------------------------------------*/ int decode_msm_head(rtcm_t *rtcm, int sys, int *sync, int *iod, msm_h_t *h, int *hsize) { msm_h_t h0 = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; double tow, tod; char *msg; int i = 24, j, mask, staid, type, ncell = 0; type = getbitu(rtcm->buff, i, 12); i += 12; *h = h0; if (i + 157 <= rtcm->len * 8) { staid = getbitu(rtcm->buff, i, 12); i += 12; if (sys == SYS_GLO) { /* dow = getbitu(rtcm->buff, i, 3); */ i += 3; tod = getbitu(rtcm->buff, i, 27) * 0.001; i += 27; adjday_glot(rtcm, tod); } else if (sys == SYS_BDS) { tow = getbitu(rtcm->buff, i, 30) * 0.001; i += 30; tow += 14.0; /* BDT -> GPST */ adjweek(rtcm, tow); } else { tow = getbitu(rtcm->buff, i, 30) * 0.001; i += 30; adjweek(rtcm, tow); } *sync = getbitu(rtcm->buff, i, 1); i += 1; *iod = getbitu(rtcm->buff, i, 3); i += 3; h->time_s = getbitu(rtcm->buff, i, 7); i += 7; h->clk_str = getbitu(rtcm->buff, i, 2); i += 2; h->clk_ext = getbitu(rtcm->buff, i, 2); i += 2; h->smooth = getbitu(rtcm->buff, i, 1); i += 1; h->tint_s = getbitu(rtcm->buff, i, 3); i += 3; for (j = 1; j <= 64; j++) { mask = getbitu(rtcm->buff, i, 1); i += 1; if (mask) { h->sats[h->nsat++] = j; } } for (j = 1; j <= 32; j++) { mask = getbitu(rtcm->buff, i, 1); i += 1; if (mask) { h->sigs[h->nsig++] = j; } } } else { trace(2, "rtcm3 %d length error: len=%d\n", type, rtcm->len); return -1; } /* test station id */ if (!test_staid(rtcm, staid)) { return -1; } if (h->nsat * h->nsig > 64) { trace(2, "rtcm3 %d number of sats and sigs error: nsat=%d nsig=%d\n", type, h->nsat, h->nsig); return -1; } if (i + h->nsat * h->nsig > rtcm->len * 8) { trace(2, "rtcm3 %d length error: len=%d nsat=%d nsig=%d\n", type, rtcm->len, h->nsat, h->nsig); return -1; } for (j = 0; j < h->nsat * h->nsig; j++) { h->cellmask[j] = getbitu(rtcm->buff, i, 1); i += 1; if (h->cellmask[j]) { ncell++; } } *hsize = i; trace(4, "decode_head_msm: time=%s sys=%d staid=%d nsat=%d nsig=%d sync=%d iod=%d ncell=%d\n", time_str(rtcm->time, 2), sys, staid, h->nsat, h->nsig, *sync, *iod, ncell); if (rtcm->outtype) { msg = rtcm->msgtype + strlen(rtcm->msgtype); sprintf(msg, " staid=%4d %s nsat=%2d nsig=%2d iod=%2d ncell=%2d sync=%d", staid, time_str(rtcm->time, 2), h->nsat, h->nsig, *iod, ncell, *sync); } return ncell; } /* decode unsupported msm message --------------------------------------------*/ int decode_msm0(rtcm_t *rtcm, int sys) { msm_h_t h = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; int i, sync, iod; if (decode_msm_head(rtcm, sys, &sync, &iod, &h, &i) < 0) { return -1; } rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode msm 4: full pseudorange and phaserange plus cnr --------------------*/ int decode_msm4(rtcm_t *rtcm, int sys) { msm_h_t h = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; double r[64], pr[64], cp[64], cnr[64]; int i, j, type, sync, iod, ncell, rng, rng_m, prv, cpv, lock[64], half[64]; type = getbitu(rtcm->buff, 24, 12); /* decode msm header */ if ((ncell = decode_msm_head(rtcm, sys, &sync, &iod, &h, &i)) < 0) { return -1; } if (i + h.nsat * 18 + ncell * 48 > rtcm->len * 8) { trace(2, "rtcm3 %d length error: nsat=%d ncell=%d len=%d\n", type, h.nsat, ncell, rtcm->len); return -1; } for (j = 0; j < h.nsat; j++) { r[j] = 0.0; } for (j = 0; j < ncell; j++) { pr[j] = cp[j] = -1E16; } /* decode satellite data */ for (j = 0; j < h.nsat; j++) { /* range */ rng = getbitu(rtcm->buff, i, 8); i += 8; if (rng != 255) { r[j] = rng * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { rng_m = getbitu(rtcm->buff, i, 10); i += 10; if (r[j] != 0.0) { r[j] += rng_m * TWO_N10 * RANGE_MS; } } /* decode signal data */ for (j = 0; j < ncell; j++) { /* pseudorange */ prv = getbits(rtcm->buff, i, 15); i += 15; if (prv != -16384) { pr[j] = prv * TWO_N24 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* phaserange */ cpv = getbits(rtcm->buff, i, 22); i += 22; if (cpv != -2097152) { cp[j] = cpv * TWO_N29 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* lock time */ lock[j] = getbitu(rtcm->buff, i, 4); i += 4; } for (j = 0; j < ncell; j++) { /* half-cycle ambiguity */ half[j] = getbitu(rtcm->buff, i, 1); i += 1; } for (j = 0; j < ncell; j++) { /* cnr */ cnr[j] = getbitu(rtcm->buff, i, 6) * 1.0; i += 6; } /* save obs data in msm message */ save_msm_obs(rtcm, sys, &h, r, pr, cp, nullptr, nullptr, cnr, lock, nullptr, half); rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode msm 5: full pseudorange, phaserange, phaserangerate and cnr --------*/ int decode_msm5(rtcm_t *rtcm, int sys) { msm_h_t h = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; double r[64], rr[64], pr[64], cp[64], rrf[64], cnr[64]; int i, j, type, sync, iod, ncell, rng, rng_m, rate, prv, cpv, rrv, lock[64]; int ex[64], half[64]; type = getbitu(rtcm->buff, 24, 12); /* decode msm header */ if ((ncell = decode_msm_head(rtcm, sys, &sync, &iod, &h, &i)) < 0) { return -1; } if (i + h.nsat * 36 + ncell * 63 > rtcm->len * 8) { trace(2, "rtcm3 %d length error: nsat=%d ncell=%d len=%d\n", type, h.nsat, ncell, rtcm->len); return -1; } for (j = 0; j < h.nsat; j++) { r[j] = rr[j] = 0.0; ex[j] = 15; } for (j = 0; j < ncell; j++) { pr[j] = cp[j] = rrf[j] = -1E16; } /* decode satellite data */ for (j = 0; j < h.nsat; j++) { /* range */ rng = getbitu(rtcm->buff, i, 8); i += 8; if (rng != 255) { r[j] = rng * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { /* extended info */ ex[j] = getbitu(rtcm->buff, i, 4); i += 4; } for (j = 0; j < h.nsat; j++) { rng_m = getbitu(rtcm->buff, i, 10); i += 10; if (r[j] != 0.0) { r[j] += rng_m * TWO_N10 * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { /* phaserangerate */ rate = getbits(rtcm->buff, i, 14); i += 14; if (rate != -8192) { rr[j] = rate * 1.0; } } /* decode signal data */ for (j = 0; j < ncell; j++) { /* pseudorange */ prv = getbits(rtcm->buff, i, 15); i += 15; if (prv != -16384) { pr[j] = prv * TWO_N24 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* phaserange */ cpv = getbits(rtcm->buff, i, 22); i += 22; if (cpv != -2097152) { cp[j] = cpv * TWO_N29 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* lock time */ lock[j] = getbitu(rtcm->buff, i, 4); i += 4; } for (j = 0; j < ncell; j++) { /* half-cycle ambiguity */ half[j] = getbitu(rtcm->buff, i, 1); i += 1; } for (j = 0; j < ncell; j++) { /* cnr */ cnr[j] = getbitu(rtcm->buff, i, 6) * 1.0; i += 6; } for (j = 0; j < ncell; j++) { /* phaserangerate */ rrv = getbits(rtcm->buff, i, 15); i += 15; if (rrv != -16384) { rrf[j] = rrv * 0.0001; } } /* save obs data in msm message */ save_msm_obs(rtcm, sys, &h, r, pr, cp, rr, rrf, cnr, lock, ex, half); rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode msm 6: full pseudorange and phaserange plus cnr (high-res) ---------*/ int decode_msm6(rtcm_t *rtcm, int sys) { msm_h_t h = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; double r[64], pr[64], cp[64], cnr[64]; int i, j, type, sync, iod, ncell, rng, rng_m, prv, cpv, lock[64], half[64]; type = getbitu(rtcm->buff, 24, 12); /* decode msm header */ if ((ncell = decode_msm_head(rtcm, sys, &sync, &iod, &h, &i)) < 0) { return -1; } if (i + h.nsat * 18 + ncell * 65 > rtcm->len * 8) { trace(2, "rtcm3 %d length error: nsat=%d ncell=%d len=%d\n", type, h.nsat, ncell, rtcm->len); return -1; } for (j = 0; j < h.nsat; j++) { r[j] = 0.0; } for (j = 0; j < ncell; j++) { pr[j] = cp[j] = -1E16; } /* decode satellite data */ for (j = 0; j < h.nsat; j++) { /* range */ rng = getbitu(rtcm->buff, i, 8); i += 8; if (rng != 255) { r[j] = rng * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { rng_m = getbitu(rtcm->buff, i, 10); i += 10; if (r[j] != 0.0) { r[j] += rng_m * TWO_N10 * RANGE_MS; } } /* decode signal data */ for (j = 0; j < ncell; j++) { /* pseudorange */ prv = getbits(rtcm->buff, i, 20); i += 20; if (prv != -524288) { pr[j] = prv * TWO_N29 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* phaserange */ cpv = getbits(rtcm->buff, i, 24); i += 24; if (cpv != -8388608) { cp[j] = cpv * TWO_N31 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* lock time */ lock[j] = getbitu(rtcm->buff, i, 10); i += 10; } for (j = 0; j < ncell; j++) { /* half-cycle ambiguity */ half[j] = getbitu(rtcm->buff, i, 1); i += 1; } for (j = 0; j < ncell; j++) { /* cnr */ cnr[j] = getbitu(rtcm->buff, i, 10) * 0.0625; i += 10; } /* save obs data in msm message */ save_msm_obs(rtcm, sys, &h, r, pr, cp, nullptr, nullptr, cnr, lock, nullptr, half); rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode msm 7: full pseudorange, phaserange, phaserangerate and cnr (h-res) */ int decode_msm7(rtcm_t *rtcm, int sys) { msm_h_t h = {0, 0, 0, 0, 0, 0, 0, 0, {0}, {0}, {0}}; double r[64], rr[64], pr[64], cp[64], rrf[64], cnr[64]; int i, j, type, sync, iod, ncell, rng, rng_m, rate, prv, cpv, rrv, lock[64]; int ex[64], half[64]; type = getbitu(rtcm->buff, 24, 12); /* decode msm header */ if ((ncell = decode_msm_head(rtcm, sys, &sync, &iod, &h, &i)) < 0) { return -1; } if (i + h.nsat * 36 + ncell * 80 > rtcm->len * 8) { trace(2, "rtcm3 %d length error: nsat=%d ncell=%d len=%d\n", type, h.nsat, ncell, rtcm->len); return -1; } for (j = 0; j < h.nsat; j++) { r[j] = rr[j] = 0.0; ex[j] = 15; } for (j = 0; j < ncell; j++) { pr[j] = cp[j] = rrf[j] = -1E16; } /* decode satellite data */ for (j = 0; j < h.nsat; j++) { /* range */ rng = getbitu(rtcm->buff, i, 8); i += 8; if (rng != 255) { r[j] = rng * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { /* extended info */ ex[j] = getbitu(rtcm->buff, i, 4); i += 4; } for (j = 0; j < h.nsat; j++) { rng_m = getbitu(rtcm->buff, i, 10); i += 10; if (r[j] != 0.0) { r[j] += rng_m * TWO_N10 * RANGE_MS; } } for (j = 0; j < h.nsat; j++) { /* phaserangerate */ rate = getbits(rtcm->buff, i, 14); i += 14; if (rate != -8192) { rr[j] = rate * 1.0; } } /* decode signal data */ for (j = 0; j < ncell; j++) { /* pseudorange */ prv = getbits(rtcm->buff, i, 20); i += 20; if (prv != -524288) { pr[j] = prv * TWO_N29 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* phaserange */ cpv = getbits(rtcm->buff, i, 24); i += 24; if (cpv != -8388608) { cp[j] = cpv * TWO_N31 * RANGE_MS; } } for (j = 0; j < ncell; j++) { /* lock time */ lock[j] = getbitu(rtcm->buff, i, 10); i += 10; } for (j = 0; j < ncell; j++) { /* half-cycle amiguity */ half[j] = getbitu(rtcm->buff, i, 1); i += 1; } for (j = 0; j < ncell; j++) { /* cnr */ cnr[j] = getbitu(rtcm->buff, i, 10) * 0.0625; i += 10; } for (j = 0; j < ncell; j++) { /* phaserangerate */ rrv = getbits(rtcm->buff, i, 15); i += 15; if (rrv != -16384) { rrf[j] = rrv * 0.0001; } } /* save obs data in msm message */ save_msm_obs(rtcm, sys, &h, r, pr, cp, rr, rrf, cnr, lock, ex, half); rtcm->obsflag = !sync; return sync ? 0 : 1; } /* decode type 1230: glonass L1 and L2 code-phase biases ---------------------*/ int decode_type1230(rtcm_t *rtcm __attribute__((unused))) { trace(2, "rtcm3 1230: not supported message\n"); return 0; } /* decode rtcm ver.3 message -------------------------------------------------*/ int decode_rtcm3(rtcm_t *rtcm) { double tow; int ret = 0, type = getbitu(rtcm->buff, 24, 12), week; trace(3, "decode_rtcm3: len=%3d type=%d\n", rtcm->len, type); if (rtcm->outtype) { sprintf(rtcm->msgtype, "RTCM %4d (%4d):", type, rtcm->len); } /* real-time input option */ if (strstr(rtcm->opt, "-RT_INP")) { tow = time2gpst(utc2gpst(timeget()), &week); rtcm->time = gpst2time(week, floor(tow)); } switch (type) { case 1001: ret = decode_type1001(rtcm); break; /* not supported */ case 1002: ret = decode_type1002(rtcm); break; case 1003: ret = decode_type1003(rtcm); break; /* not supported */ case 1004: ret = decode_type1004(rtcm); break; case 1005: ret = decode_type1005(rtcm); break; case 1006: ret = decode_type1006(rtcm); break; case 1007: ret = decode_type1007(rtcm); break; case 1008: ret = decode_type1008(rtcm); break; case 1009: ret = decode_type1009(rtcm); break; /* not supported */ case 1010: ret = decode_type1010(rtcm); break; case 1011: ret = decode_type1011(rtcm); break; /* not supported */ case 1012: ret = decode_type1012(rtcm); break; case 1013: ret = decode_type1013(rtcm); break; /* not supported */ case 1019: ret = decode_type1019(rtcm); break; case 1020: ret = decode_type1020(rtcm); break; case 1021: ret = decode_type1021(rtcm); break; /* not supported */ case 1022: ret = decode_type1022(rtcm); break; /* not supported */ case 1023: ret = decode_type1023(rtcm); break; /* not supported */ case 1024: ret = decode_type1024(rtcm); break; /* not supported */ case 1025: ret = decode_type1025(rtcm); break; /* not supported */ case 1026: ret = decode_type1026(rtcm); break; /* not supported */ case 1027: ret = decode_type1027(rtcm); break; /* not supported */ case 1029: ret = decode_type1029(rtcm); break; case 1030: ret = decode_type1030(rtcm); break; /* not supported */ case 1031: ret = decode_type1031(rtcm); break; /* not supported */ case 1032: ret = decode_type1032(rtcm); break; /* not supported */ case 1033: ret = decode_type1033(rtcm); break; case 1034: ret = decode_type1034(rtcm); break; /* not supported */ case 1035: ret = decode_type1035(rtcm); break; /* not supported */ case 1037: ret = decode_type1037(rtcm); break; /* not supported */ case 1038: ret = decode_type1038(rtcm); break; /* not supported */ case 1039: ret = decode_type1039(rtcm); break; /* not supported */ case 1044: ret = decode_type1044(rtcm); break; case 1045: ret = decode_type1045(rtcm); break; case 1046: ret = decode_type1046(rtcm); break; /* extension for IGS MGEX */ case 1047: ret = decode_type1047(rtcm); break; /* beidou ephemeris (tentative mt) */ case 63: ret = decode_type63(rtcm); break; /* beidou ephemeris (rtcm draft) */ case 1057: ret = decode_ssr1(rtcm, SYS_GPS); break; case 1058: ret = decode_ssr2(rtcm, SYS_GPS); break; case 1059: ret = decode_ssr3(rtcm, SYS_GPS); break; case 1060: ret = decode_ssr4(rtcm, SYS_GPS); break; case 1061: ret = decode_ssr5(rtcm, SYS_GPS); break; case 1062: ret = decode_ssr6(rtcm, SYS_GPS); break; case 1063: ret = decode_ssr1(rtcm, SYS_GLO); break; case 1064: ret = decode_ssr2(rtcm, SYS_GLO); break; case 1065: ret = decode_ssr3(rtcm, SYS_GLO); break; case 1066: ret = decode_ssr4(rtcm, SYS_GLO); break; case 1067: ret = decode_ssr5(rtcm, SYS_GLO); break; case 1068: ret = decode_ssr6(rtcm, SYS_GLO); break; case 1071: ret = decode_msm0(rtcm, SYS_GPS); break; /* not supported */ case 1072: ret = decode_msm0(rtcm, SYS_GPS); break; /* not supported */ case 1073: ret = decode_msm0(rtcm, SYS_GPS); break; /* not supported */ case 1074: ret = decode_msm4(rtcm, SYS_GPS); break; case 1075: ret = decode_msm5(rtcm, SYS_GPS); break; case 1076: ret = decode_msm6(rtcm, SYS_GPS); break; case 1077: ret = decode_msm7(rtcm, SYS_GPS); break; case 1081: ret = decode_msm0(rtcm, SYS_GLO); break; /* not supported */ case 1082: ret = decode_msm0(rtcm, SYS_GLO); break; /* not supported */ case 1083: ret = decode_msm0(rtcm, SYS_GLO); break; /* not supported */ case 1084: ret = decode_msm4(rtcm, SYS_GLO); break; case 1085: ret = decode_msm5(rtcm, SYS_GLO); break; case 1086: ret = decode_msm6(rtcm, SYS_GLO); break; case 1087: ret = decode_msm7(rtcm, SYS_GLO); break; case 1091: ret = decode_msm0(rtcm, SYS_GAL); break; /* not supported */ case 1092: ret = decode_msm0(rtcm, SYS_GAL); break; /* not supported */ case 1093: ret = decode_msm0(rtcm, SYS_GAL); break; /* not supported */ case 1094: ret = decode_msm4(rtcm, SYS_GAL); break; case 1095: ret = decode_msm5(rtcm, SYS_GAL); break; case 1096: ret = decode_msm6(rtcm, SYS_GAL); break; case 1097: ret = decode_msm7(rtcm, SYS_GAL); break; case 1101: ret = decode_msm0(rtcm, SYS_SBS); break; /* not supported */ case 1102: ret = decode_msm0(rtcm, SYS_SBS); break; /* not supported */ case 1103: ret = decode_msm0(rtcm, SYS_SBS); break; /* not supported */ case 1104: ret = decode_msm4(rtcm, SYS_SBS); break; case 1105: ret = decode_msm5(rtcm, SYS_SBS); break; case 1106: ret = decode_msm6(rtcm, SYS_SBS); break; case 1107: ret = decode_msm7(rtcm, SYS_SBS); break; case 1111: ret = decode_msm0(rtcm, SYS_QZS); break; /* not supported */ case 1112: ret = decode_msm0(rtcm, SYS_QZS); break; /* not supported */ case 1113: ret = decode_msm0(rtcm, SYS_QZS); break; /* not supported */ case 1114: ret = decode_msm4(rtcm, SYS_QZS); break; case 1115: ret = decode_msm5(rtcm, SYS_QZS); break; case 1116: ret = decode_msm6(rtcm, SYS_QZS); break; case 1117: ret = decode_msm7(rtcm, SYS_QZS); break; case 1121: ret = decode_msm0(rtcm, SYS_BDS); break; /* not supported */ case 1122: ret = decode_msm0(rtcm, SYS_BDS); break; /* not supported */ case 1123: ret = decode_msm0(rtcm, SYS_BDS); break; /* not supported */ case 1124: ret = decode_msm4(rtcm, SYS_BDS); break; case 1125: ret = decode_msm5(rtcm, SYS_BDS); break; case 1126: ret = decode_msm6(rtcm, SYS_BDS); break; case 1127: ret = decode_msm7(rtcm, SYS_BDS); break; case 1230: ret = decode_type1230(rtcm); break; /* not supported */ case 1240: ret = decode_ssr1(rtcm, SYS_GAL); break; case 1241: ret = decode_ssr2(rtcm, SYS_GAL); break; case 1242: ret = decode_ssr3(rtcm, SYS_GAL); break; case 1243: ret = decode_ssr4(rtcm, SYS_GAL); break; case 1244: ret = decode_ssr5(rtcm, SYS_GAL); break; case 1245: ret = decode_ssr6(rtcm, SYS_GAL); break; case 1246: ret = decode_ssr1(rtcm, SYS_QZS); break; case 1247: ret = decode_ssr2(rtcm, SYS_QZS); break; case 1248: ret = decode_ssr3(rtcm, SYS_QZS); break; case 1249: ret = decode_ssr4(rtcm, SYS_QZS); break; case 1250: ret = decode_ssr5(rtcm, SYS_QZS); break; case 1251: ret = decode_ssr6(rtcm, SYS_QZS); break; case 1252: ret = decode_ssr1(rtcm, SYS_SBS); break; case 1253: ret = decode_ssr2(rtcm, SYS_SBS); break; case 1254: ret = decode_ssr3(rtcm, SYS_SBS); break; case 1255: ret = decode_ssr4(rtcm, SYS_SBS); break; case 1256: ret = decode_ssr5(rtcm, SYS_SBS); break; case 1257: ret = decode_ssr6(rtcm, SYS_SBS); break; case 1258: ret = decode_ssr1(rtcm, SYS_BDS); break; case 1259: ret = decode_ssr2(rtcm, SYS_BDS); break; case 1260: ret = decode_ssr3(rtcm, SYS_BDS); break; case 1261: ret = decode_ssr4(rtcm, SYS_BDS); break; case 1262: ret = decode_ssr5(rtcm, SYS_BDS); break; case 1263: ret = decode_ssr6(rtcm, SYS_BDS); break; case 2065: ret = decode_ssr7(rtcm, SYS_GPS); break; /* tentative */ case 2066: ret = decode_ssr7(rtcm, SYS_GLO); break; /* tentative */ case 2067: ret = decode_ssr7(rtcm, SYS_GAL); break; /* tentative */ case 2068: ret = decode_ssr7(rtcm, SYS_QZS); break; /* tentative */ case 2070: ret = decode_ssr7(rtcm, SYS_BDS); break; /* tentative */ } if (ret >= 0) { type -= 1000; if (1 <= type && type <= 299) { rtcm->nmsg3[type]++; /* 1001-1299 */ } else if (1000 <= type && type <= 1099) { rtcm->nmsg3[type - 700]++; /* 2000-2099 */ } else { rtcm->nmsg3[0]++; } } return ret; } src/algorithms/libs/rtklib/rtklib_rtcm3.h000066400000000000000000000161171352176506000210210ustar00rootroot00000000000000/*! * \file rtklib_rtcm3.h * \brief RTCM v3 functions headers * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RTCM3_H_ #define GNSS_SDR_RTKLIB_RTCM3_H_ #include "rtklib.h" /* constants -----------------------------------------------------------------*/ const double PRUNIT_GPS = 299792.458; /* rtcm ver.3 unit of gps pseudorange (m) */ const double PRUNIT_GLO = 599584.916; /* rtcm ver.3 unit of glonass pseudorange (m) */ const double RANGE_MS = SPEED_OF_LIGHT * 0.001; /* range in 1 ms */ /* ssr update intervals ------------------------------------------------------*/ const double SSRUDINT[16] = { 1, 2, 5, 10, 15, 30, 60, 120, 240, 300, 600, 900, 1800, 3600, 7200, 10800}; /* ssr 3 and 7 signal and tracking mode ids ----------------------------------*/ const int CODES_GPS[] = { CODE_L1C, CODE_L1P, CODE_L1W, CODE_L1Y, CODE_L1M, CODE_L2C, CODE_L2D, CODE_L2S, CODE_L2L, CODE_L2X, CODE_L2P, CODE_L2W, CODE_L2Y, CODE_L2M, CODE_L5I, CODE_L5Q, CODE_L5X}; const int CODES_GLO[] = { CODE_L1C, CODE_L1P, CODE_L2C, CODE_L2P}; const int CODES_GAL[] = { CODE_L1A, CODE_L1B, CODE_L1C, CODE_L1X, CODE_L1Z, CODE_L5I, CODE_L5Q, CODE_L5X, CODE_L7I, CODE_L7Q, CODE_L7X, CODE_L8I, CODE_L8Q, CODE_L8X, CODE_L6A, CODE_L6B, CODE_L6C, CODE_L6X, CODE_L6Z}; const int CODES_QZS[] = { CODE_L1C, CODE_L1S, CODE_L1L, CODE_L2S, CODE_L2L, CODE_L2X, CODE_L5I, CODE_L5Q, CODE_L5X, CODE_L6S, CODE_L6L, CODE_L6X, CODE_L1X}; const int CODES_BDS[] = { CODE_L1I, CODE_L1Q, CODE_L1X, CODE_L7I, CODE_L7Q, CODE_L7X, CODE_L6I, CODE_L6Q, CODE_L6X}; const int CODES_SBS[] = { CODE_L1C, CODE_L5I, CODE_L5Q, CODE_L5X}; double getbitg(const unsigned char *buff, int pos, int len); void adjweek(rtcm_t *rtcm, double tow); int adjbdtweek(int week); void adjday_glot(rtcm_t *rtcm, double tod); double adjcp(rtcm_t *rtcm, int sat, int freq, double cp); int lossoflock(rtcm_t *rtcm, int sat, int freq, int lock); unsigned char snratio(double snr); int obsindex3(obs_t *obs, gtime_t time, int sat); int test_staid(rtcm_t *rtcm, int staid); int decode_head1001(rtcm_t *rtcm, int *sync); int decode_type1001(rtcm_t *rtcm); int decode_type1002(rtcm_t *rtcm); int decode_type1003(rtcm_t *rtcm); int decode_type1004(rtcm_t *rtcm); double getbits_38(const unsigned char *buff, int pos); int decode_type1005(rtcm_t *rtcm); int decode_type1006(rtcm_t *rtcm); int decode_type1007(rtcm_t *rtcm); int decode_type1008(rtcm_t *rtcm); int decode_head1009(rtcm_t *rtcm, int *sync); int decode_type1009(rtcm_t *rtcm); int decode_type1010(rtcm_t *rtcm); int decode_type1011(rtcm_t *rtcm); int decode_type1012(rtcm_t *rtcm); int decode_type1013(rtcm_t *rtcm); int decode_type1019(rtcm_t *rtcm); int decode_type1020(rtcm_t *rtcm); int decode_type1021(rtcm_t *rtcm); int decode_type1022(rtcm_t *rtcm); int decode_type1023(rtcm_t *rtcm); int decode_type1024(rtcm_t *rtcm); int decode_type1025(rtcm_t *rtcm); int decode_type1026(rtcm_t *rtcm); int decode_type1027(rtcm_t *rtcm); int decode_type1029(rtcm_t *rtcm); int decode_type1030(rtcm_t *rtcm); int decode_type1031(rtcm_t *rtcm); int decode_type1032(rtcm_t *rtcm); int decode_type1033(rtcm_t *rtcm); int decode_type1034(rtcm_t *rtcm); int decode_type1035(rtcm_t *rtcm); int decode_type1037(rtcm_t *rtcm); int decode_type1038(rtcm_t *rtcm); int decode_type1039(rtcm_t *rtcm); int decode_type1044(rtcm_t *rtcm); int decode_type1045(rtcm_t *rtcm); int decode_type1046(rtcm_t *rtcm); int decode_type1047(rtcm_t *rtcm); int decode_type1063(rtcm_t *rtcm); int decode_ssr1_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *refd, int *hsize); int decode_ssr2_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *hsize); int decode_ssr7_head(rtcm_t *rtcm, int sys, int *sync, int *iod, double *udint, int *dispe, int *mw, int *hsize); int decode_ssr1(rtcm_t *rtcm, int sys); int decode_ssr2(rtcm_t *rtcm, int sys); int decode_ssr3(rtcm_t *rtcm, int sys); int decode_ssr4(rtcm_t *rtcm, int sys); int decode_ssr5(rtcm_t *rtcm, int sys); int decode_ssr6(rtcm_t *rtcm, int sys); int decode_ssr7(rtcm_t *rtcm, int sys); void sigindex(int sys, const unsigned char *code, const int *freq, int n, const char *opt, int *ind); void save_msm_obs(rtcm_t *rtcm, int sys, msm_h_t *h, const double *r, const double *pr, const double *cp, const double *rr, const double *rrf, const double *cnr, const int *lock, const int *ex, const int *half); int decode_msm_head(rtcm_t *rtcm, int sys, int *sync, int *iod, msm_h_t *h, int *hsize); int decode_msm0(rtcm_t *rtcm, int sys); int decode_msm4(rtcm_t *rtcm, int sys); int decode_msm5(rtcm_t *rtcm, int sys); int decode_msm6(rtcm_t *rtcm, int sys); int decode_msm7(rtcm_t *rtcm, int sys); int decode_type1230(rtcm_t *rtcm); int decode_rtcm3(rtcm_t *rtcm); #endif src/algorithms/libs/rtklib/rtklib_rtkcmn.cc000066400000000000000000005220121352176506000214210ustar00rootroot00000000000000/*! * \file rtklib_rtkcmn.cc * \brief rtklib common functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_rtkcmn.h" #include #include #include #include #include #include #include #include const double GPST0[] = {1980, 1, 6, 0, 0, 0}; /* gps time reference */ const double GST0[] = {1999, 8, 22, 0, 0, 0}; /* galileo system time reference */ const double BDT0[] = {2006, 1, 1, 0, 0, 0}; /* beidou time reference */ static double timeoffset_ = 0.0; double leaps[MAXLEAPS + 1][7] = {/* leap seconds (y,m,d,h,m,s,utc-gpst) */ {2017, 1, 1, 0, 0, 0, -18}, {2015, 7, 1, 0, 0, 0, -17}, {2012, 7, 1, 0, 0, 0, -16}, {2009, 1, 1, 0, 0, 0, -15}, {2006, 1, 1, 0, 0, 0, -14}, {1999, 1, 1, 0, 0, 0, -13}, {1997, 7, 1, 0, 0, 0, -12}, {1996, 1, 1, 0, 0, 0, -11}, {1994, 7, 1, 0, 0, 0, -10}, {1993, 7, 1, 0, 0, 0, -9}, {1992, 7, 1, 0, 0, 0, -8}, {1991, 1, 1, 0, 0, 0, -7}, {1990, 1, 1, 0, 0, 0, -6}, {1988, 1, 1, 0, 0, 0, -5}, {1985, 7, 1, 0, 0, 0, -4}, {1983, 7, 1, 0, 0, 0, -3}, {1982, 7, 1, 0, 0, 0, -2}, {1981, 7, 1, 0, 0, 0, -1}, {}}; const char *formatstrs[32] = {/* stream format strings */ "RTCM 2", /* 0 */ "RTCM 3", /* 1 */ "NovAtel OEM6", /* 2 */ "NovAtel OEM3", /* 3 */ "u-blox", /* 4 */ "Superstar II", /* 5 */ "Hemisphere", /* 6 */ "SkyTraq", /* 7 */ "GW10", /* 8 */ "Javad", /* 9 */ "NVS BINR", /* 10 */ "BINEX", /* 11 */ "Trimble RT17", /* 12 */ "Septentrio", /* 13 */ "CMR/CMR+", /* 14 */ "LEX Receiver", /* 15 */ "RINEX", /* 16 */ "SP3", /* 17 */ "RINEX CLK", /* 18 */ "SBAS", /* 19 */ "NMEA 0183", /* 20 */ nullptr}; char obscodes[][3] = { /* observation code strings */ "", "1C", "1P", "1W", "1Y", "1M", "1N", "1S", "1L", "1E", /* 0- 9 */ "1A", "1B", "1X", "1Z", "2C", "2D", "2S", "2L", "2X", "2P", /* 10-19 */ "2W", "2Y", "2M", "2N", "5I", "5Q", "5X", "7I", "7Q", "7X", /* 20-29 */ "6A", "6B", "6C", "6X", "6Z", "6S", "6L", "8L", "8Q", "8X", /* 30-39 */ "2I", "2Q", "6I", "6Q", "3I", "3Q", "3X", "1I", "1Q", "5A", /* 40-49 */ "5B", "5C", "9A", "9B", "9C", "9X", "", "", "", "" /* 50-59 */ }; unsigned char obsfreqs[] = { /* 1:L1/E1, 2:L2/B1, 3:L5/E5a/L3, 4:L6/LEX/B3, 5:E5b/B2, 6:E5(a+b), 7:S */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0- 9 */ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, /* 10-19 */ 2, 2, 2, 2, 3, 3, 3, 5, 5, 5, /* 20-29 */ 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, /* 30-39 */ 2, 2, 4, 4, 3, 3, 3, 1, 1, 3, /* 40-49 */ 3, 3, 7, 7, 7, 7, 0, 0, 0, 0 /* 50-59 */ }; char codepris[7][MAXFREQ][16] = { /* code priority table */ /* L1/E1 L2/B1 L5/E5a/L3 L6/LEX/B3 E5b/B2 E5(a+b) S */ {"CPYWMNSL", "PYWCMNDSLX", "IQX", "", "", "", ""}, /* GPS */ {"PC", "PC", "IQX", "", "", "", ""}, /* GLO */ {"CABXZ", "", "IQX", "ABCXZ", "IQX", "IQX", ""}, /* GAL */ {"CSLXZ", "SLX", "IQX", "SLX", "", "", ""}, /* QZS */ {"C", "", "IQX", "", "", "", ""}, /* SBS */ {"IQX", "IQX", "IQX", "IQX", "IQX", "", ""}, /* BDS */ {"", "", "ABCX", "", "", "", "ABCX"} /* IRN */ }; fatalfunc_t *fatalfunc = nullptr; /* fatal callback function */ /* crc tables generated by util/gencrc ---------------------------------------*/ const uint16_t TBL_CR_C16[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; const unsigned int TBL_CR_C24_Q[] = { 0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, 0x9F7F17, 0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, 0x3EFE2E, 0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, 0x5A319E, 0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7, 0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE, 0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7, 0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077, 0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, 0xF7614E, 0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, 0x86DCC5, 0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, 0x275DFC, 0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C, 0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375, 0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C, 0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15, 0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, 0x4F43A5, 0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, 0xEEC29C, 0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, 0xAC38B3, 0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A, 0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A, 0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703, 0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A, 0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, 0x016863, 0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, 0x65A7D3, 0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, 0xC426EA, 0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61, 0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58, 0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8, 0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1, 0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, 0xB94A88, 0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, 0x18CBB1, 0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, 0x7C0401, 0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538}; extern "C" { void dgemm_(char *, char *, int *, int *, int *, double *, double *, int *, double *, int *, double *, double *, int *); extern void dgetrf_(int *, int *, double *, int *, int *, int *); extern void dgetri_(int *, double *, int *, int *, double *, int *, int *); extern void dgetrs_(char *, int *, int *, double *, int *, int *, double *, int *, int *); } /* function prototypes -------------------------------------------------------*/ #ifdef IERS_MODEL extern int gmf_(double *mjd, double *lat, double *lon, double *hgt, double *zd, double *gmfh, double *gmfw); #endif /* fatal error ---------------------------------------------------------------*/ void fatalerr(const char *format, ...) { char msg[1024]; va_list ap; va_start(ap, format); vsprintf(msg, format, ap); va_end(ap); fprintf(stderr, "%s", msg); exit(-9); } /* satellite system+prn/slot number to satellite number ------------------------ * convert satellite system+prn/slot number to satellite number * args : int sys I satellite system (SYS_GPS,SYS_GLO,...) * int prn I satellite prn/slot number * return : satellite number (0:error) *-----------------------------------------------------------------------------*/ int satno(int sys, int prn) { if (prn <= 0) { return 0; } switch (sys) { case SYS_GPS: if (prn < MINPRNGPS || MAXPRNGPS < prn) { return 0; } return prn - MINPRNGPS + 1; case SYS_GLO: if (prn < MINPRNGLO || MAXPRNGLO < prn) { return 0; } return NSATGPS + prn - MINPRNGLO + 1; case SYS_GAL: if (prn < MINPRNGAL || MAXPRNGAL < prn) { return 0; } return NSATGPS + NSATGLO + prn - MINPRNGAL + 1; case SYS_QZS: if (prn < MINPRNQZS || MAXPRNQZS < prn) { return 0; } return NSATGPS + NSATGLO + NSATGAL + prn - MINPRNQZS + 1; case SYS_BDS: if (prn < MINPRNBDS || MAXPRNBDS < prn) { return 0; } return NSATGPS + NSATGLO + NSATGAL + NSATQZS + prn - MINPRNBDS + 1; case SYS_IRN: if (prn < MINPRNIRN || MAXPRNIRN < prn) { return 0; } return NSATGPS + NSATGLO + NSATGAL + NSATQZS + NSATBDS + prn - MINPRNIRN + 1; case SYS_LEO: if (prn < MINPRNLEO || MAXPRNLEO < prn) { return 0; } return NSATGPS + NSATGLO + NSATGAL + NSATQZS + NSATBDS + NSATIRN + prn - MINPRNLEO + 1; case SYS_SBS: if (prn < MINPRNSBS || MAXPRNSBS < prn) { return 0; } return NSATGPS + NSATGLO + NSATGAL + NSATQZS + NSATBDS + NSATIRN + NSATLEO + prn - MINPRNSBS + 1; } return 0; } /* satellite number to satellite system ---------------------------------------- * convert satellite number to satellite system * args : int sat I satellite number (1-MAXSAT) * int *prn IO satellite prn/slot number (NULL: no output) * return : satellite system (SYS_GPS,SYS_GLO,...) *-----------------------------------------------------------------------------*/ int satsys(int sat, int *prn) { int sys = SYS_NONE; if (sat <= 0 || MAXSAT < sat) { sat = 0; } else if (sat <= NSATGPS) { sys = SYS_GPS; sat += MINPRNGPS - 1; } else if ((sat -= NSATGPS) <= NSATGLO) { sys = SYS_GLO; sat += MINPRNGLO - 1; } else if ((sat -= NSATGLO) <= NSATGAL) { sys = SYS_GAL; sat += MINPRNGAL - 1; } else if ((sat -= NSATGAL) <= NSATQZS) { sys = SYS_QZS; sat += MINPRNQZS - 1; } else if ((sat -= NSATQZS) <= NSATBDS) { sys = SYS_BDS; sat += MINPRNBDS - 1; } else if ((sat -= NSATBDS) <= NSATIRN) { sys = SYS_IRN; sat += MINPRNIRN - 1; } else if ((sat -= NSATIRN) <= NSATLEO) { sys = SYS_LEO; sat += MINPRNLEO - 1; } else if ((sat -= NSATLEO) <= NSATSBS) { sys = SYS_SBS; sat += MINPRNSBS - 1; } else { sat = 0; } if (prn) { *prn = sat; } return sys; } /* satellite id to satellite number -------------------------------------------- * convert satellite id to satellite number * args : char *id I satellite id (nn,Gnn,Rnn,Enn,Jnn,Cnn,Inn or Snn) * return : satellite number (0: error) * notes : 120-142 and 193-199 are also recognized as sbas and qzss *-----------------------------------------------------------------------------*/ int satid2no(const char *id) { int sys, prn; char code; if (sscanf(id, "%d", &prn) == 1) { if (MINPRNGPS <= prn && prn <= MAXPRNGPS) { sys = SYS_GPS; } else if (MINPRNSBS <= prn && prn <= MAXPRNSBS) { sys = SYS_SBS; } else if (MINPRNQZS <= prn && prn <= MAXPRNQZS) { sys = SYS_QZS; } else { return 0; } return satno(sys, prn); } if (sscanf(id, "%c%d", &code, &prn) < 2) { return 0; } switch (code) { case 'G': sys = SYS_GPS; prn += MINPRNGPS - 1; break; case 'R': sys = SYS_GLO; prn += MINPRNGLO - 1; break; case 'E': sys = SYS_GAL; prn += MINPRNGAL - 1; break; case 'J': sys = SYS_QZS; prn += MINPRNQZS - 1; break; case 'C': sys = SYS_BDS; prn += MINPRNBDS - 1; break; case 'I': sys = SYS_IRN; prn += MINPRNIRN - 1; break; case 'L': sys = SYS_LEO; prn += MINPRNLEO - 1; break; case 'S': sys = SYS_SBS; prn += 100; break; default: return 0; } return satno(sys, prn); } /* satellite number to satellite id -------------------------------------------- * convert satellite number to satellite id * args : int sat I satellite number * char *id O satellite id (Gnn,Rnn,Enn,Jnn,Cnn,Inn or nnn) * return : none *-----------------------------------------------------------------------------*/ void satno2id(int sat, char *id) { int prn; switch (satsys(sat, &prn)) { case SYS_GPS: sprintf(id, "G%02d", prn - MINPRNGPS + 1); return; case SYS_GLO: sprintf(id, "R%02d", prn - MINPRNGLO + 1); return; case SYS_GAL: sprintf(id, "E%02d", prn - MINPRNGAL + 1); return; case SYS_QZS: sprintf(id, "J%02d", prn - MINPRNQZS + 1); return; case SYS_BDS: sprintf(id, "C%02d", prn - MINPRNBDS + 1); return; case SYS_IRN: sprintf(id, "I%02d", prn - MINPRNIRN + 1); return; case SYS_LEO: sprintf(id, "L%02d", prn - MINPRNLEO + 1); return; case SYS_SBS: sprintf(id, "%03d", prn); return; } strcpy(id, ""); } /* test excluded satellite ----------------------------------------------------- * test excluded satellite * args : int sat I satellite number * int svh I sv health flag * prcopt_t *opt I processing options (NULL: not used) * return : status (1:excluded,0:not excluded) *-----------------------------------------------------------------------------*/ int satexclude(int sat, int svh, const prcopt_t *opt) { int sys = satsys(sat, nullptr); if (svh < 0) { trace(3, "ephemeris unavailable: sat=%3d svh=%02X\n", sat, svh); return 1; /* ephemeris unavailable */ } if (opt) { if (opt->exsats[sat - 1] == 1) { trace(3, "excluded satellite: sat=%3d svh=%02X\n", sat, svh); return 1; /* excluded satellite */ } if (opt->exsats[sat - 1] == 2) { return 0; /* included satellite */ } if (!(sys & opt->navsys)) { trace(3, "unselected sat sys: sat=%3d svh=%02X\n", sat, svh); return 1; /* unselected sat sys */ } } if (sys == SYS_QZS) { svh &= 0xFE; /* mask QZSS LEX health */ } if (svh) { trace(3, "unhealthy satellite: sat=%3d svh=%02X\n", sat, svh); return 1; } return 0; } /* test SNR mask --------------------------------------------------------------- * test SNR mask * args : int base I rover or base-station (0:rover,1:base station) * int freq I frequency (0:L1,1:L2,2:L3,...) * double el I elevation angle (rad) * double snr I C/N0 (dBHz) * snrmask_t *mask I SNR mask * return : status (1:masked,0:unmasked) *-----------------------------------------------------------------------------*/ int testsnr(int base, int freq, double el, double snr, const snrmask_t *mask) { double minsnr, a; int i; if (!mask->ena[base] || freq < 0 || freq >= NFREQ) { return 0; } a = (el * R2D + 5.0) / 10.0; i = static_cast(floor(a)); a -= i; if (i < 1) { minsnr = mask->mask[freq][0]; } else if (i > 8) { minsnr = mask->mask[freq][8]; } else { minsnr = (1.0 - a) * mask->mask[freq][i - 1] + a * mask->mask[freq][i]; } return snr < minsnr; } /* obs type string to obs code ------------------------------------------------- * convert obs code type string to obs code * args : char *str I obs code string ("1C","1P","1Y",...) * int *freq IO frequency (1:L1,2:L2,3:L5,4:L6,5:L7,6:L8,0:err) * (NULL: no output) * return : obs code (CODE_???) * notes : obs codes are based on reference [6] and qzss extension *-----------------------------------------------------------------------------*/ unsigned char obs2code(const char *obs, int *freq) { int i; if (freq) { *freq = 0; } for (i = 1; *obscodes[i]; i++) { if (strcmp(obscodes[i], obs) != 0) { continue; } if (freq) { *freq = obsfreqs[i]; } return static_cast(i); } return CODE_NONE; } /* obs code to obs code string ------------------------------------------------- * convert obs code to obs code string * args : unsigned char code I obs code (CODE_???) * int *freq IO frequency (NULL: no output) * (1:L1/E1, 2:L2/B1, 3:L5/E5a/L3, 4:L6/LEX/B3, 5:E5b/B2, 6:E5(a+b), 7:S) * return : obs code string ("1C","1P","1P",...) * notes : obs codes are based on reference [6] and qzss extension *-----------------------------------------------------------------------------*/ char *code2obs(unsigned char code, int *freq) { if (freq) { *freq = 0; } if (code <= CODE_NONE || MAXCODE < code) { return (char *)""; } if (freq) { *freq = obsfreqs[code]; } return obscodes[code]; } /* set code priority ----------------------------------------------------------- * set code priority for multiple codes in a frequency * args : int sys I system (or of SYS_???) * int freq I frequency (1:L1,2:L2,3:L5,4:L6,5:L7,6:L8,7:L9) * char *pri I priority of codes (series of code characters) * (higher priority precedes lower) * return : none *-----------------------------------------------------------------------------*/ void setcodepri(int sys, int freq, const char *pri) { trace(3, "setcodepri : sys=%d freq=%d pri=%s\n", sys, freq, pri); if (freq <= 0 || MAXFREQ < freq) { return; } if (strlen(pri) < 17) { if (sys & SYS_GPS) { strcpy(codepris[0][freq - 1], pri); } if (sys & SYS_GLO) { strcpy(codepris[1][freq - 1], pri); } if (sys & SYS_GAL) { strcpy(codepris[2][freq - 1], pri); } if (sys & SYS_QZS) { strcpy(codepris[3][freq - 1], pri); } if (sys & SYS_SBS) { strcpy(codepris[4][freq - 1], pri); } if (sys & SYS_BDS) { strcpy(codepris[5][freq - 1], pri); } if (sys & SYS_IRN) { strcpy(codepris[6][freq - 1], pri); } } else { trace(1, "pri array is too long"); } } /* get code priority ----------------------------------------------------------- * get code priority for multiple codes in a frequency * args : int sys I system (SYS_???) * unsigned char code I obs code (CODE_???) * char *opt I code options (NULL:no option) * return : priority (15:highest-1:lowest,0:error) *-----------------------------------------------------------------------------*/ int getcodepri(int sys, unsigned char code, const char *opt) { const char *p, *optstr; char *obs, str[8] = ""; int i, j; switch (sys) { case SYS_GPS: i = 0; optstr = "-GL%2s"; break; case SYS_GLO: i = 1; optstr = "-RL%2s"; break; case SYS_GAL: i = 2; optstr = "-EL%2s"; break; case SYS_QZS: i = 3; optstr = "-JL%2s"; break; case SYS_SBS: i = 4; optstr = "-SL%2s"; break; case SYS_BDS: i = 5; optstr = "-CL%2s"; break; case SYS_IRN: i = 6; optstr = "-IL%2s"; break; default: return 0; } obs = code2obs(code, &j); /* parse code options */ for (p = opt; p && (p = strchr(p, '-')); p++) { if (sscanf(p, optstr, str) < 1 || str[0] != obs[0]) { continue; } return str[1] == obs[1] ? 15 : 0; } /* search code priority */ return (p = strchr(codepris[i][j - 1], obs[1])) ? 14 - static_cast(p - codepris[i][j - 1]) : 0; } /* extract unsigned/signed bits ------------------------------------------------ * extract unsigned/signed bits from byte data * args : unsigned char *buff I byte data * int pos I bit position from start of data (bits) * int len I bit length (bits) (len <= 32) * return : extracted unsigned/signed bits *-----------------------------------------------------------------------------*/ unsigned int getbitu(const unsigned char *buff, int pos, int len) { unsigned int bits = 0; int i; for (i = pos; i < pos + len; i++) { bits = (bits << 1) + ((buff[i / 8] >> (7 - i % 8)) & 1U); } return bits; } int getbits(const unsigned char *buff, int pos, int len) { unsigned int bits = getbitu(buff, pos, len); if (len <= 0 || 32 <= len || !(bits & (1U << (len - 1)))) { return static_cast(bits); } return static_cast(bits | (~0U << len)); /* extend sign */ } /* set unsigned/signed bits ---------------------------------------------------- * set unsigned/signed bits to byte data * args : unsigned char *buff IO byte data * int pos I bit position from start of data (bits) * int len I bit length (bits) (len <= 32) * (unsigned) int I unsigned/signed data * return : none *-----------------------------------------------------------------------------*/ void setbitu(unsigned char *buff, int pos, int len, unsigned int data) { unsigned int mask = 1U << (len - 1); int i; if (len <= 0 || 32 < len) { return; } for (i = pos; i < pos + len; i++, mask >>= 1) { if (data & mask) { buff[i / 8] |= 1U << (7 - i % 8); } else { buff[i / 8] &= ~(1U << (7 - i % 8)); } } } void setbits(unsigned char *buff, int pos, int len, int data) { if (data < 0) { data |= 1 << (len - 1); } else { data &= ~(1 << (len - 1)); /* set sign bit */ } setbitu(buff, pos, len, static_cast(data)); } /* crc-32 parity --------------------------------------------------------------- * compute crc-32 parity for novatel raw * args : unsigned char *buff I data * int len I data length (bytes) * return : crc-32 parity * notes : see NovAtel OEMV firmware manual 1.7 32-bit CRC *-----------------------------------------------------------------------------*/ unsigned int rtk_crc32(const unsigned char *buff, int len) { unsigned int crc = 0; int i, j; trace(4, "rtk_crc32: len=%d\n", len); for (i = 0; i < len; i++) { crc ^= buff[i]; for (j = 0; j < 8; j++) { if (crc & 1) { crc = (crc >> 1) ^ POLYCRC32; } else { crc >>= 1; } } } return crc; } /* crc-24q parity -------------------------------------------------------------- * compute crc-24q parity for sbas, rtcm3 * args : unsigned char *buff I data * int len I data length (bytes) * return : crc-24Q parity * notes : see reference [2] A.4.3.3 Parity *-----------------------------------------------------------------------------*/ unsigned int rtk_crc24q(const unsigned char *buff, int len) { unsigned int crc = 0; int i; trace(4, "rtk_crc24q: len=%d\n", len); for (i = 0; i < len; i++) { crc = ((crc << 8) & 0xFFFFFF) ^ TBL_CR_C24_Q[(crc >> 16) ^ buff[i]]; } return crc; } /* crc-16 parity --------------------------------------------------------------- * compute crc-16 parity for binex, nvs * args : unsigned char *buff I data * int len I data length (bytes) * return : crc-16 parity * notes : see reference [10] A.3. *-----------------------------------------------------------------------------*/ uint16_t rtk_crc16(const unsigned char *buff, int len) { uint16_t crc = 0; int i; trace(4, "rtk_crc16: len=%d\n", len); for (i = 0; i < len; i++) { crc = (crc << 8) ^ TBL_CR_C16[((crc >> 8) ^ buff[i]) & 0xFF]; } return crc; } /* decode navigation data word ------------------------------------------------- * check party and decode navigation data word * args : unsigned int word I navigation data word (2+30bit) * (previous word D29*-30* + current word D1-30) * unsigned char *data O decoded navigation data without parity * (8bitx3) * return : status (1:ok,0:parity error) * notes : see reference [1] 20.3.5.2 user parity algorithm *-----------------------------------------------------------------------------*/ int decode_word(unsigned int word, unsigned char *data) { const unsigned int hamming[] = { 0xBB1F3480, 0x5D8F9A40, 0xAEC7CD00, 0x5763E680, 0x6BB1F340, 0x8B7A89C0}; unsigned int parity = 0, w; int i; trace(5, "decodeword: word=%08x\n", word); if (word & 0x40000000) { word ^= 0x3FFFFFC0; } for (i = 0; i < 6; i++) { parity <<= 1; for (w = (word & hamming[i]) >> 6; w; w >>= 1) { parity ^= w & 1; } } if (parity != (word & 0x3F)) { return 0; } for (i = 0; i < 3; i++) { data[i] = static_cast(word >> (22 - i * 8)); } return 1; } /* new matrix ------------------------------------------------------------------ * allocate memory of matrix * args : int n,m I number of rows and columns of matrix * return : matrix pointer (if n<=0 or m<=0, return NULL) *-----------------------------------------------------------------------------*/ double *mat(int n, int m) { double *p; if (n <= 0 || m <= 0) { return nullptr; } if (!(p = static_cast(malloc(sizeof(double) * n * m)))) { fatalerr("matrix memory allocation error: n=%d,m=%d\n", n, m); } return p; } /* new integer matrix ---------------------------------------------------------- * allocate memory of integer matrix * args : int n,m I number of rows and columns of matrix * return : matrix pointer (if n <= 0 or m <= 0, return NULL) *-----------------------------------------------------------------------------*/ int *imat(int n, int m) { int *p; if (n <= 0 || m <= 0) { return nullptr; } if (!(p = static_cast(malloc(sizeof(int) * n * m)))) { fatalerr("integer matrix memory allocation error: n=%d,m=%d\n", n, m); } return p; } /* zero matrix ----------------------------------------------------------------- * generate new zero matrix * args : int n,m I number of rows and columns of matrix * return : matrix pointer (if n <= 0 or m <= 0, return NULL) *-----------------------------------------------------------------------------*/ double *zeros(int n, int m) { double *p; #if NOCALLOC if ((p = mat(n, m))) for (n = n * m - 1; n >= 0; n--) p[n] = 0.0; #else if (n <= 0 || m <= 0) { return nullptr; } if (!(p = static_cast(calloc(sizeof(double), n * m)))) { fatalerr("matrix memory allocation error: n=%d,m=%d\n", n, m); } #endif return p; } /* identity matrix ------------------------------------------------------------- * generate new identity matrix * args : int n I number of rows and columns of matrix * return : matrix pointer (if n <= 0, return NULL) *-----------------------------------------------------------------------------*/ double *eye(int n) { double *p; int i; if ((p = zeros(n, n))) { for (i = 0; i < n; i++) { p[i + i * n] = 1.0; } } return p; } /* inner product --------------------------------------------------------------- * inner product of vectors * args : double *a,*b I vector a,b (n x 1) * int n I size of vector a,b * return : a'*b *-----------------------------------------------------------------------------*/ double dot(const double *a, const double *b, int n) { double c = 0.0; while (--n >= 0) { c += a[n] * b[n]; } return c; } /* euclid norm ----------------------------------------------------------------- * euclid norm of vector * args : double *a I vector a (n x 1) * int n I size of vector a * return : || a || *-----------------------------------------------------------------------------*/ double norm_rtk(const double *a, int n) { return std::sqrt(dot(a, a, n)); } /* outer product of 3d vectors ------------------------------------------------- * outer product of 3d vectors * args : double *a,*b I vector a,b (3 x 1) * double *c O outer product (a x b) (3 x 1) * return : none *-----------------------------------------------------------------------------*/ void cross3(const double *a, const double *b, double *c) { c[0] = a[1] * b[2] - a[2] * b[1]; c[1] = a[2] * b[0] - a[0] * b[2]; c[2] = a[0] * b[1] - a[1] * b[0]; } /* normalize 3d vector --------------------------------------------------------- * normalize 3d vector * args : double *a I vector a (3 x 1) * double *b O normlized vector (3 x 1) || b || = 1 * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ int normv3(const double *a, double *b) { double r; if ((r = norm_rtk(a, 3)) <= 0.0) { return 0; } b[0] = a[0] / r; b[1] = a[1] / r; b[2] = a[2] / r; return 1; } /* copy matrix ----------------------------------------------------------------- * copy matrix * args : double *A O destination matrix A (n x m) * double *B I source matrix B (n x m) * int n,m I number of rows and columns of matrix * return : none *-----------------------------------------------------------------------------*/ void matcpy(double *A, const double *B, int n, int m) { memcpy(A, B, sizeof(double) * n * m); } /* matrix routines -----------------------------------------------------------*/ /* multiply matrix (wrapper of blas dgemm) ------------------------------------- * multiply matrix by matrix (C=alpha*A*B+beta*C) * args : char *tr I transpose flags ("N":normal,"T":transpose) * int n,k,m I size of (transposed) matrix A,B * double alpha I alpha * double *A,*B I (transposed) matrix A (n x m), B (m x k) * double beta I beta * double *C IO matrix C (n x k) * return : none *-----------------------------------------------------------------------------*/ void matmul(const char *tr, int n, int k, int m, double alpha, const double *A, const double *B, double beta, double *C) { int lda = tr[0] == 'T' ? m : n, ldb = tr[1] == 'T' ? k : m; dgemm_(const_cast(tr), const_cast(tr) + 1, &n, &k, &m, &alpha, const_cast(A), &lda, const_cast(B), &ldb, &beta, C, &n); } /* inverse of matrix ----------------------------------------------------------- * inverse of matrix (A=A^-1) * args : double *A IO matrix (n x n) * int n I size of matrix A * return : status (0:ok,0>:error) *-----------------------------------------------------------------------------*/ int matinv(double *A, int n) { double *work; int info, lwork = n * 16, *ipiv = imat(n, 1); work = mat(lwork, 1); dgetrf_(&n, &n, A, &n, ipiv, &info); if (!info) { dgetri_(&n, A, &n, ipiv, work, &lwork, &info); } free(ipiv); free(work); return info; } /* solve linear equation ------------------------------------------------------- * solve linear equation (X=A\Y or X=A'\Y) * args : char *tr I transpose flag ("N":normal,"T":transpose) * double *A I input matrix A (n x n) * double *Y I input matrix Y (n x m) * int n,m I size of matrix A,Y * double *X O X=A\Y or X=A'\Y (n x m) * return : status (0:ok,0>:error) * notes : matirix stored by column-major order (fortran convention) * X can be same as Y *-----------------------------------------------------------------------------*/ int solve(const char *tr, const double *A, const double *Y, int n, int m, double *X) { double *B = mat(n, n); int info, *ipiv = imat(n, 1); matcpy(B, A, n, n); matcpy(X, Y, n, m); dgetrf_(&n, &n, B, &n, ipiv, &info); if (!info) { dgetrs_(const_cast(tr), &n, &m, B, &n, ipiv, X, &n, &info); } free(ipiv); free(B); return info; } /* end of matrix routines ----------------------------------------------------*/ /* least square estimation ----------------------------------------------------- * least square estimation by solving normal equation (x=(A*A')^-1*A*y) * args : double *A I transpose of (weighted) design matrix (n x m) * double *y I (weighted) measurements (m x 1) * int n,m I number of parameters and measurements (n <= m) * double *x O estmated parameters (n x 1) * double *Q O esimated parameters covariance matrix (n x n) * return : status (0:ok,0>:error) * notes : for weighted least square, replace A and y by A*w and w*y (w=W^(1/2)) * matirix stored by column-major order (fortran convention) *-----------------------------------------------------------------------------*/ int lsq(const double *A, const double *y, int n, int m, double *x, double *Q) { double *Ay; int info; if (m < n) { return -1; } Ay = mat(n, 1); matmul("NN", n, 1, m, 1.0, A, y, 0.0, Ay); /* Ay=A*y */ matmul("NT", n, n, m, 1.0, A, A, 0.0, Q); /* Q=A*A' */ if (!(info = matinv(Q, n))) { matmul("NN", n, 1, n, 1.0, Q, Ay, 0.0, x); /* x=Q^-1*Ay */ } free(Ay); return info; } /* kalman filter --------------------------------------------------------------- * kalman filter state update as follows: * * K=P*H*(H'*P*H+R)^-1, xp=x+K*v, Pp=(I-K*H')*P * * args : double *x I states vector (n x 1) * double *P I covariance matrix of states (n x n) * double *H I transpose of design matrix (n x m) * double *v I innovation (measurement - model) (m x 1) * double *R I covariance matrix of measurement error (m x m) * int n,m I number of states and measurements * double *xp O states vector after update (n x 1) * double *Pp O covariance matrix of states after update (n x n) * return : status (0:ok,<0:error) * notes : matirix stored by column-major order (fortran convention) * if state x[i]==0.0, not updates state x[i]/P[i+i*n] *-----------------------------------------------------------------------------*/ int filter_(const double *x, const double *P, const double *H, const double *v, const double *R, int n, int m, double *xp, double *Pp) { double *F = mat(n, m), *Q = mat(m, m), *K = mat(n, m), *I = eye(n); int info; matcpy(Q, R, m, m); matcpy(xp, x, n, 1); matmul("NN", n, m, n, 1.0, P, H, 0.0, F); /* Q=H'*P*H+R */ matmul("TN", m, m, n, 1.0, H, F, 1.0, Q); if (!(info = matinv(Q, m))) { matmul("NN", n, m, m, 1.0, F, Q, 0.0, K); /* K=P*H*Q^-1 */ matmul("NN", n, 1, m, 1.0, K, v, 1.0, xp); /* xp=x+K*v */ matmul("NT", n, n, m, -1.0, K, H, 1.0, I); /* Pp=(I-K*H')*P */ matmul("NN", n, n, n, 1.0, I, P, 0.0, Pp); } free(F); free(Q); free(K); free(I); return info; } int filter(double *x, double *P, const double *H, const double *v, const double *R, int n, int m) { double *x_, *xp_, *P_, *Pp_, *H_; int i, j, k, info, *ix; ix = imat(n, 1); for (i = k = 0; i < n; i++) { if (x[i] != 0.0 && P[i + i * n] > 0.0) { ix[k++] = i; } } x_ = mat(k, 1); xp_ = mat(k, 1); P_ = mat(k, k); Pp_ = mat(k, k); H_ = mat(k, m); for (i = 0; i < k; i++) { x_[i] = x[ix[i]]; for (j = 0; j < k; j++) { P_[i + j * k] = P[ix[i] + ix[j] * n]; } for (j = 0; j < m; j++) { H_[i + j * k] = H[ix[i] + j * n]; } } info = filter_(x_, P_, H_, v, R, k, m, xp_, Pp_); for (i = 0; i < k; i++) { x[ix[i]] = xp_[i]; for (j = 0; j < k; j++) { P[ix[i] + ix[j] * n] = Pp_[i + j * k]; } } free(ix); free(x_); free(xp_); free(P_); free(Pp_); free(H_); return info; } /* smoother -------------------------------------------------------------------- * combine forward and backward filters by fixed-interval smoother as follows: * * xs=Qs*(Qf^-1*xf+Qb^-1*xb), Qs=(Qf^-1+Qb^-1)^-1) * * args : double *xf I forward solutions (n x 1) * args : double *Qf I forward solutions covariance matrix (n x n) * double *xb I backward solutions (n x 1) * double *Qb I backward solutions covariance matrix (n x n) * int n I number of solutions * double *xs O smoothed solutions (n x 1) * double *Qs O smoothed solutions covariance matrix (n x n) * return : status (0:ok,0>:error) * notes : see reference [4] 5.2 * matirix stored by column-major order (fortran convention) *-----------------------------------------------------------------------------*/ int smoother(const double *xf, const double *Qf, const double *xb, const double *Qb, int n, double *xs, double *Qs) { double *invQf = mat(n, n), *invQb = mat(n, n), *xx = mat(n, 1); int i, info = -1; matcpy(invQf, Qf, n, n); matcpy(invQb, Qb, n, n); if (!matinv(invQf, n) && !matinv(invQb, n)) { for (i = 0; i < n * n; i++) { Qs[i] = invQf[i] + invQb[i]; } if (!(info = matinv(Qs, n))) { matmul("NN", n, 1, n, 1.0, invQf, xf, 0.0, xx); matmul("NN", n, 1, n, 1.0, invQb, xb, 1.0, xx); matmul("NN", n, 1, n, 1.0, Qs, xx, 0.0, xs); } } free(invQf); free(invQb); free(xx); return info; } /* print matrix ---------------------------------------------------------------- * print matrix to stdout * args : double *A I matrix A (n x m) * int n,m I number of rows and columns of A * int p,q I total columns, columns under decimal point * (FILE *fp I output file pointer) * return : none * notes : matirix stored by column-major order (fortran convention) *-----------------------------------------------------------------------------*/ void matfprint(const double A[], int n, int m, int p, int q, FILE *fp) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { fprintf(fp, " %*.*f", p, q, A[i + j * n]); } fprintf(fp, "\n"); } } void matsprint(const double A[], int n, int m, int p, int q, std::string &buffer) { int i, j; buffer += '\n'; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { char buf_[256]; sprintf(buf_, " %*.*f", p, q, A[i + j * n]); std::string s(buf_); buffer = buffer + s; } buffer += '\n'; } } void matprint(const double A[], int n, int m, int p, int q) { matfprint(A, n, m, p, q, stdout); } /* string to number ------------------------------------------------------------ * convert substring in string to number * args : char *s I string ("... nnn.nnn ...") * int i,n I substring position and width * return : converted number (0.0:error) *-----------------------------------------------------------------------------*/ double str2num(const char *s, int i, int n) { double value; char str[256], *p = str; if (i < 0 || static_cast(strlen(s)) < i || static_cast(sizeof(str)) - 1 < n) { return 0.0; } for (s += i; *s && --n >= 0; s++) { *p++ = *s == 'd' || *s == 'D' ? 'E' : *s; } *p = '\0'; return sscanf(str, "%lf", &value) == 1 ? value : 0.0; } /* string to time -------------------------------------------------------------- * convert substring in string to gtime_t struct * args : char *s I string ("... yyyy mm dd hh mm ss ...") * int i,n I substring position and width * gtime_t *t O gtime_t struct * return : status (0:ok,0>:error) *-----------------------------------------------------------------------------*/ int str2time(const char *s, int i, int n, gtime_t *t) { double ep[6]; char str[256], *p = str; if (i < 0 || static_cast(strlen(s)) < i || static_cast(sizeof(str)) - 1 < i) { return -1; } for (s += i; *s && --n >= 0;) { *p++ = *s++; } *p = '\0'; if (sscanf(str, "%lf %lf %lf %lf %lf %lf", ep, ep + 1, ep + 2, ep + 3, ep + 4, ep + 5) < 6) { return -1; } if (ep[0] < 100.0) { ep[0] += ep[0] < 80.0 ? 2000.0 : 1900.0; } *t = epoch2time(ep); return 0; } /* convert calendar day/time to time ------------------------------------------- * convert calendar day/time to gtime_t struct * args : double *ep I day/time {year,month,day,hour,min,sec} * return : gtime_t struct * notes : proper in 1970-2037 or 1970-2099 (64bit time_t) *-----------------------------------------------------------------------------*/ gtime_t epoch2time(const double *ep) { const int doy[] = {1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; gtime_t time = {0, 0}; int days, sec, year = static_cast(ep[0]), mon = static_cast(ep[1]), day = static_cast(ep[2]); if (year < 1970 || 2099 < year || mon < 1 || 12 < mon) { return time; } /* leap year if year%4==0 in 1901-2099 */ days = (year - 1970) * 365 + (year - 1969) / 4 + doy[mon - 1] + day - 2 + (year % 4 == 0 && mon >= 3 ? 1 : 0); sec = static_cast(floor(ep[5])); time.time = static_cast(days) * 86400 + static_cast(ep[3]) * 3600 + static_cast(ep[4]) * 60 + sec; time.sec = ep[5] - sec; return time; } /* time to calendar day/time --------------------------------------------------- * convert gtime_t struct to calendar day/time * args : gtime_t t I gtime_t struct * double *ep O day/time {year,month,day,hour,min,sec} * return : none * notes : proper in 1970-2037 or 1970-2099 (64bit time_t) *-----------------------------------------------------------------------------*/ void time2epoch(gtime_t t, double *ep) { const int mday[] = {/* # of days in a month */ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int days, sec, mon, day; /* leap year if year%4==0 in 1901-2099 */ days = static_cast(t.time / 86400); sec = static_cast(t.time - static_cast(days) * 86400); for (day = days % 1461, mon = 0; mon < 48; mon++) { if (day >= mday[mon]) { day -= mday[mon]; } else { break; } } ep[0] = 1970 + days / 1461 * 4 + mon / 12; ep[1] = mon % 12 + 1; ep[2] = day + 1; ep[3] = sec / 3600; ep[4] = sec % 3600 / 60; ep[5] = sec % 60 + t.sec; } /* gps time to time ------------------------------------------------------------ * convert week and tow in gps time to gtime_t struct * args : int week I week number in gps time * double sec I time of week in gps time (s) * return : gtime_t struct *-----------------------------------------------------------------------------*/ gtime_t gpst2time(int week, double sec) { gtime_t t = epoch2time(GPST0); if (sec < -1e9 || 1e9 < sec) { sec = 0.0; } t.time += static_cast(86400) * 7 * week + static_cast(sec); t.sec = sec - static_cast(sec); return t; } /* time to gps time ------------------------------------------------------------ * convert gtime_t struct to week and tow in gps time * args : gtime_t t I gtime_t struct * int *week IO week number in gps time (NULL: no output) * return : time of week in gps time (s) *-----------------------------------------------------------------------------*/ double time2gpst(gtime_t t, int *week) { gtime_t t0 = epoch2time(GPST0); time_t sec = t.time - t0.time; int w = static_cast(sec / 604800); if (week) { *week = w; } return (static_cast(sec - static_cast(w * 604800)) + t.sec); } /* galileo system time to time ------------------------------------------------- * convert week and tow in galileo system time (gst) to gtime_t struct * args : int week I week number in gst * double sec I time of week in gst (s) * return : gtime_t struct *-----------------------------------------------------------------------------*/ gtime_t gst2time(int week, double sec) { gtime_t t = epoch2time(GST0); if (sec < -1e9 || 1e9 < sec) { sec = 0.0; } t.time += static_cast(86400) * 7 * week + static_cast(sec); t.sec = sec - static_cast(sec); return t; } /* time to galileo system time ------------------------------------------------- * convert gtime_t struct to week and tow in galileo system time (gst) * args : gtime_t t I gtime_t struct * int *week IO week number in gst (NULL: no output) * return : time of week in gst (s) *-----------------------------------------------------------------------------*/ double time2gst(gtime_t t, int *week) { gtime_t t0 = epoch2time(GST0); time_t sec = t.time - t0.time; int w = static_cast(sec / (86400 * 7)); if (week) { *week = w; } return (sec - static_cast(w) * 86400 * 7) + t.sec; } /* beidou time (bdt) to time --------------------------------------------------- * convert week and tow in beidou time (bdt) to gtime_t struct * args : int week I week number in bdt * double sec I time of week in bdt (s) * return : gtime_t struct *-----------------------------------------------------------------------------*/ gtime_t bdt2time(int week, double sec) { gtime_t t = epoch2time(BDT0); if (sec < -1e9 || 1e9 < sec) { sec = 0.0; } t.time += static_cast(86400) * 7 * week + static_cast(sec); t.sec = sec - static_cast(sec); return t; } /* time to beidouo time (bdt) -------------------------------------------------- * convert gtime_t struct to week and tow in beidou time (bdt) * args : gtime_t t I gtime_t struct * int *week IO week number in bdt (NULL: no output) * return : time of week in bdt (s) *-----------------------------------------------------------------------------*/ double time2bdt(gtime_t t, int *week) { gtime_t t0 = epoch2time(BDT0); time_t sec = t.time - t0.time; int w = static_cast(sec / (86400 * 7)); if (week) { *week = w; } return (sec - static_cast(w) * 86400 * 7) + t.sec; } /* add time -------------------------------------------------------------------- * add time to gtime_t struct * args : gtime_t t I gtime_t struct * double sec I time to add (s) * return : gtime_t struct (t+sec) *-----------------------------------------------------------------------------*/ gtime_t timeadd(gtime_t t, double sec) { double tt; t.sec += sec; tt = floor(t.sec); t.time += static_cast(tt); t.sec -= tt; return t; } /* time difference ------------------------------------------------------------- * difference between gtime_t structs * args : gtime_t t1,t2 I gtime_t structs * return : time difference (t1-t2) (s) *-----------------------------------------------------------------------------*/ double timediff(gtime_t t1, gtime_t t2) { return difftime(t1.time, t2.time) + t1.sec - t2.sec; } /* time difference accounting with week crossovers ------------------------------------------------------------- * difference between gtime_t structs * args : gtime_t t1,t2 I gtime_t structs * return : time difference (t1-t2) (s) *-----------------------------------------------------------------------------*/ double timediffweekcrossover(gtime_t t1, gtime_t t2) { //as stated in IS-GPS-200J table 20-IV footnote among other parts of the ICD, //if tk=(t - toe) > 302400s then tk = tk - s //if tk=(t - toe) < -302400s then tk = tk + 604800s double tk = difftime(t1.time, t2.time) + t1.sec - t2.sec; if (tk > 302400.0) { tk -= 604800.0; } else if (tk < -302400.0) { tk += 604800.0; } return tk; } /* get current time in utc ----------------------------------------------------- * get current time in utc * args : none * return : current time in utc *-----------------------------------------------------------------------------*/ gtime_t timeget(void) { gtime_t time; double ep[6] = {}; struct timeval tv { }; struct tm *tt; if (!gettimeofday(&tv, nullptr) && (tt = gmtime(&tv.tv_sec))) { ep[0] = tt->tm_year + 1900; ep[1] = tt->tm_mon + 1; ep[2] = tt->tm_mday; ep[3] = tt->tm_hour; ep[4] = tt->tm_min; ep[5] = tt->tm_sec + tv.tv_usec * 1e-6; } time = epoch2time(ep); #ifdef CPUTIME_IN_GPST /* cputime operated in gpst */ time = gpst2utc(time); #endif return time; } /* set current time in utc ----------------------------------------------------- * set current time in utc * args : gtime_t I current time in utc * return : none * notes : just set time offset between cpu time and current time * the time offset is reflected to only timeget() * not reentrant *-----------------------------------------------------------------------------*/ void timeset(gtime_t t) { timeoffset_ += timediff(t, timeget()); } /* read leap seconds table by text -------------------------------------------*/ int read_leaps_text(FILE *fp) { char buff[256], *p; int i, n = 0, ep[6], ls; rewind(fp); while (fgets(buff, sizeof(buff), fp) && n < MAXLEAPS) { if ((p = strchr(buff, '#'))) { *p = '\0'; } if (sscanf(buff, "%d %d %d %d %d %d %d", ep, ep + 1, ep + 2, ep + 3, ep + 4, ep + 5, &ls) < 7) { continue; } for (i = 0; i < 6; i++) { leaps[n][i] = ep[i]; } leaps[n++][6] = ls; } return n; } /* read leap seconds table by usno -------------------------------------------*/ int read_leaps_usno(FILE *fp) { static const char *months[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; double jd, tai_utc; char buff[256], month[32], ls[MAXLEAPS][7] = {}; int i, j, y, m, d, n = 0; rewind(fp); while (fgets(buff, sizeof(buff), fp) && n < MAXLEAPS) { if (sscanf(buff, "%d %s %d =JD %lf TAI-UTC= %lf", &y, month, &d, &jd, &tai_utc) < 5) { continue; } if (y < 1980) { continue; } for (m = 1; m <= 12; m++) { if (!strcmp(months[m - 1], month)) { break; } } if (m >= 13) { continue; } ls[n][0] = y; ls[n][1] = m; ls[n][2] = d; ls[n++][6] = static_cast(19.0 - tai_utc); } for (i = 0; i < n; i++) { for (j = 0; j < 7; j++) { leaps[i][j] = ls[n - i - 1][j]; } } return n; } /* read leap seconds table ----------------------------------------------------- * read leap seconds table * args : char *file I leap seconds table file * return : status (1:ok,0:error) * notes : The leap second table should be as follows or leapsec.dat provided * by USNO. * (1) The records in the table file cosist of the following fields: * year month day hour min sec UTC-GPST(s) * (2) The date and time indicate the start UTC time for the UTC-GPST * (3) The date and time should be descending order. *-----------------------------------------------------------------------------*/ int read_leaps(const char *file) { FILE *fp; int i, n; if (!(fp = fopen(file, "re"))) { return 0; } /* read leap seconds table by text or usno */ if (!(n = read_leaps_text(fp)) && !(n = read_leaps_usno(fp))) { fclose(fp); return 0; } for (i = 0; i < 7; i++) { leaps[n][i] = 0.0; } fclose(fp); return 1; } /* gpstime to utc -------------------------------------------------------------- * convert gpstime to utc considering leap seconds * args : gtime_t t I time expressed in gpstime * return : time expressed in utc * notes : ignore slight time offset under 100 ns *-----------------------------------------------------------------------------*/ gtime_t gpst2utc(gtime_t t) { gtime_t tu; int i; for (i = 0; leaps[i][0] > 0; i++) { tu = timeadd(t, leaps[i][6]); if (timediff(tu, epoch2time(leaps[i])) >= 0.0) { return tu; } } return t; } /* utc to gpstime -------------------------------------------------------------- * convert utc to gpstime considering leap seconds * args : gtime_t t I time expressed in utc * return : time expressed in gpstime * notes : ignore slight time offset under 100 ns *-----------------------------------------------------------------------------*/ gtime_t utc2gpst(gtime_t t) { int i; for (i = 0; leaps[i][0] > 0; i++) { if (timediff(t, epoch2time(leaps[i])) >= 0.0) { return timeadd(t, -leaps[i][6]); } } return t; } /* gpstime to bdt -------------------------------------------------------------- * convert gpstime to bdt (beidou navigation satellite system time) * args : gtime_t t I time expressed in gpstime * return : time expressed in bdt * notes : ref [8] 3.3, 2006/1/1 00:00 BDT = 2006/1/1 00:00 UTC * no leap seconds in BDT * ignore slight time offset under 100 ns *-----------------------------------------------------------------------------*/ gtime_t gpst2bdt(gtime_t t) { return timeadd(t, -14.0); } /* bdt to gpstime -------------------------------------------------------------- * convert bdt (beidou navigation satellite system time) to gpstime * args : gtime_t t I time expressed in bdt * return : time expressed in gpstime * notes : see gpst2bdt() *-----------------------------------------------------------------------------*/ gtime_t bdt2gpst(gtime_t t) { return timeadd(t, 14.0); } /* time to day and sec -------------------------------------------------------*/ double time2sec(gtime_t time, gtime_t *day) { double ep[6], sec; time2epoch(time, ep); sec = ep[3] * 3600.0 + ep[4] * 60.0 + ep[5]; ep[3] = ep[4] = ep[5] = 0.0; *day = epoch2time(ep); return sec; } /* utc to gmst ----------------------------------------------------------------- * convert utc to gmst (Greenwich mean sidereal time) * args : gtime_t t I time expressed in utc * double ut1_utc I UT1-UTC (s) * return : gmst (rad) *-----------------------------------------------------------------------------*/ double utc2gmst(gtime_t t, double ut1_utc) { const double ep2000[] = {2000, 1, 1, 12, 0, 0}; gtime_t tut, tut0; double ut, t1, t2, t3, gmst0, gmst; tut = timeadd(t, ut1_utc); ut = time2sec(tut, &tut0); t1 = timediff(tut0, epoch2time(ep2000)) / 86400.0 / 36525.0; t2 = t1 * t1; t3 = t2 * t1; gmst0 = 24110.54841 + 8640184.812866 * t1 + 0.093104 * t2 - 6.2E-6 * t3; gmst = gmst0 + 1.002737909350795 * ut; return fmod(gmst, 86400.0) * PI / 43200.0; /* 0 <= gmst <= 2*PI */ } /* time to string -------------------------------------------------------------- * convert gtime_t struct to string * args : gtime_t t I gtime_t struct * char *s O string ("yyyy/mm/dd hh:mm:ss.ssss") * int n I number of decimals * return : none *-----------------------------------------------------------------------------*/ void time2str(gtime_t t, char *s, int n) { double ep[6]; if (n < 0) { n = 0; } else if (n > 12) { n = 12; } if (1.0 - t.sec < 0.5 / pow(10.0, n)) { t.time++; t.sec = 0.0; }; time2epoch(t, ep); sprintf(s, "%04.0f/%02.0f/%02.0f %02.0f:%02.0f:%0*.*f", ep[0], ep[1], ep[2], ep[3], ep[4], n <= 0 ? 2 : n + 3, n <= 0 ? 0 : n, ep[5]); } /* get time string ------------------------------------------------------------- * get time string * args : gtime_t t I gtime_t struct * int n I number of decimals * return : time string * notes : not reentrant, do not use multiple in a function *-----------------------------------------------------------------------------*/ char *time_str(gtime_t t, int n) { static char buff[64]; time2str(t, buff, n); return buff; } /* time to day of year --------------------------------------------------------- * convert time to day of year * args : gtime_t t I gtime_t struct * return : day of year (days) *-----------------------------------------------------------------------------*/ double time2doy(gtime_t t) { double ep[6]; time2epoch(t, ep); ep[1] = ep[2] = 1.0; ep[3] = ep[4] = ep[5] = 0.0; return timediff(t, epoch2time(ep)) / 86400.0 + 1.0; } /* adjust gps week number ------------------------------------------------------ * adjust gps week number using cpu time * args : int week I not-adjusted gps week number * return : adjusted gps week number *-----------------------------------------------------------------------------*/ int adjgpsweek(int week) { // int w; // if (week < 512) // { // //assume receiver date > 7 april 2019 // w = week + 2048; //add weeks from 6-january-1980 to week rollover in 7 april 2019 // } // else // { // //assume receiver date < 7 april 2019 // w = week + 1024; //add weeks from 6-january-1980 to week rollover in 22 august 2019 // } int w; (void)time2gpst(utc2gpst(timeget()), &w); if (w < 1560) { w = 1560; /* use 2009/12/1 if time is earlier than 2009/12/1 */ } return week + (w - week + 512) / 1024 * 1024; // return w; } /* get tick time --------------------------------------------------------------- * get current tick in ms * args : none * return : current tick in ms *-----------------------------------------------------------------------------*/ unsigned int tickget(void) { struct timespec tp = {0, 0}; struct timeval tv = {0, 0}; #ifdef CLOCK_MONOTONIC_RAW /* linux kernel > 2.6.28 */ if (!clock_gettime(CLOCK_MONOTONIC_RAW, &tp)) { return tp.tv_sec * 1000U + tp.tv_nsec / 1000000U; } gettimeofday(&tv, nullptr); return tv.tv_sec * 1000U + tv.tv_usec / 1000U; #else gettimeofday(&tv, NULL); return tv.tv_sec * 1000u + tv.tv_usec / 1000u; #endif } /* sleep ms -------------------------------------------------------------------- * sleep ms * args : int ms I milliseconds to sleep (<0:no sleep) * return : none *-----------------------------------------------------------------------------*/ void sleepms(int ms) { struct timespec ts = {0, 0}; if (ms <= 0) { return; } ts.tv_sec = static_cast(ms / 1000); ts.tv_nsec = static_cast(ms % 1000 * 1000000); nanosleep(&ts, nullptr); } /* convert degree to deg-min-sec ----------------------------------------------- * convert degree to degree-minute-second * args : double deg I degree * double *dms O degree-minute-second {deg, min, sec} * int ndec I number of decimals of second * return : none *-----------------------------------------------------------------------------*/ void deg2dms(double deg, double *dms, int ndec) { double sign = deg < 0.0 ? -1.0 : 1.0, a = fabs(deg); double unit = pow(0.1, ndec); dms[0] = floor(a); a = (a - dms[0]) * 60.0; dms[1] = floor(a); a = (a - dms[1]) * 60.0; dms[2] = floor(a / unit + 0.5) * unit; if (dms[2] >= 60.0) { dms[2] = 0.0; dms[1] += 1.0; if (dms[1] >= 60.0) { dms[1] = 0.0; dms[0] += 1.0; } } dms[0] *= sign; } void deg2dms(double deg, double *dms) { double sign = deg < 0.0 ? -1.0 : 1.0, a = fabs(deg); dms[0] = floor(a); a = (a - dms[0]) * 60.0; dms[1] = floor(a); a = (a - dms[1]) * 60.0; dms[2] = a; dms[0] *= sign; } /* convert deg-min-sec to degree ----------------------------------------------- * convert degree-minute-second to degree * args : double *dms I degree-minute-second {deg,min,sec} * return : degree *-----------------------------------------------------------------------------*/ double dms2deg(const double *dms) { double sign = dms[0] < 0.0 ? -1.0 : 1.0; return sign * (fabs(dms[0]) + dms[1] / 60.0 + dms[2] / 3600.0); } /* transform ecef to geodetic position ------------------------------------------ * transform ecef position to geodetic position * args : double *r I ecef position {x,y,z} (m) * double *pos O geodetic position {lat,lon,h} (rad,m) * return : none * notes : WGS84, ellipsoidal height *-----------------------------------------------------------------------------*/ void ecef2pos(const double *r, double *pos) { double e2 = FE_WGS84 * (2.0 - FE_WGS84), r2 = dot(r, r, 2), z, zk, v = RE_WGS84, sinp; for (z = r[2], zk = 0.0; fabs(z - zk) >= 1e-4;) { zk = z; sinp = z / sqrt(r2 + z * z); v = RE_WGS84 / sqrt(1.0 - e2 * sinp * sinp); z = r[2] + v * e2 * sinp; } pos[0] = r2 > 1e-12 ? atan(z / sqrt(r2)) : (r[2] > 0.0 ? PI / 2.0 : -PI / 2.0); pos[1] = r2 > 1e-12 ? atan2(r[1], r[0]) : 0.0; pos[2] = sqrt(r2 + z * z) - v; } /* transform geodetic to ecef position ----------------------------------------- * transform geodetic position to ecef position * args : double *pos I geodetic position {lat, lon,h} (rad,m) * double *r O ecef position {x,y,z} (m) * return : none * notes : WGS84, ellipsoidal height *-----------------------------------------------------------------------------*/ void pos2ecef(const double *pos, double *r) { double sinp = sin(pos[0]), cosp = cos(pos[0]), sinl = sin(pos[1]), cosl = cos(pos[1]); double e2 = FE_WGS84 * (2.0 - FE_WGS84), v = RE_WGS84 / sqrt(1.0 - e2 * sinp * sinp); r[0] = (v + pos[2]) * cosp * cosl; r[1] = (v + pos[2]) * cosp * sinl; r[2] = (v * (1.0 - e2) + pos[2]) * sinp; } /* ecef to local coordinate transformation matrix ------------------------------ * compute ecef to local coordinate transformation matrix * args : double *pos I geodetic position {lat,lon} (rad) * double *E O ecef to local coord transformation matrix (3x3) * return : none * notes : matirix stored by column-major order (fortran convention) *-----------------------------------------------------------------------------*/ void xyz2enu(const double *pos, double *E) { double sinp = sin(pos[0]), cosp = cos(pos[0]), sinl = sin(pos[1]), cosl = cos(pos[1]); E[0] = -sinl; E[3] = cosl; E[6] = 0.0; E[1] = -sinp * cosl; E[4] = -sinp * sinl; E[7] = cosp; E[2] = cosp * cosl; E[5] = cosp * sinl; E[8] = sinp; } /* transform ecef vector to local tangental coordinate ------------------------- * transform ecef vector to local tangental coordinate * args : double *pos I geodetic position {lat,lon} (rad) * double *r I vector in ecef coordinate {x,y,z} * double *e O vector in local tangental coordinate {e,n,u} * return : none *-----------------------------------------------------------------------------*/ void ecef2enu(const double *pos, const double *r, double *e) { double E[9]; xyz2enu(pos, E); matmul("NN", 3, 1, 3, 1.0, E, r, 0.0, e); } /* transform local vector to ecef coordinate ----------------------------------- * transform local tangental coordinate vector to ecef * args : double *pos I geodetic position {lat,lon} (rad) * double *e I vector in local tangental coordinate {e,n,u} * double *r O vector in ecef coordinate {x,y,z} * return : none *-----------------------------------------------------------------------------*/ void enu2ecef(const double *pos, const double *e, double *r) { double E[9]; xyz2enu(pos, E); matmul("TN", 3, 1, 3, 1.0, E, e, 0.0, r); } /* 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 *-----------------------------------------------------------------------------*/ void covenu(const double *pos, const double *P, double *Q) { double E[9], EP[9]; xyz2enu(pos, E); matmul("NN", 3, 3, 3, 1.0, E, P, 0.0, EP); matmul("NT", 3, 3, 3, 1.0, EP, E, 0.0, Q); } /* 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 *-----------------------------------------------------------------------------*/ void covecef(const double *pos, const double *Q, double *P) { double E[9], EQ[9]; xyz2enu(pos, E); matmul("TN", 3, 3, 3, 1.0, E, Q, 0.0, EQ); matmul("NN", 3, 3, 3, 1.0, EQ, E, 0.0, P); } /* astronomical arguments: f={l,l',F,D,OMG} (rad) ----------------------------*/ void ast_args(double t, double *f) { static const double fc[][5] = {/* coefficients for iau 1980 nutation */ {134.96340251, 1717915923.2178, 31.8792, 0.051635, -0.00024470}, {357.52910918, 129596581.0481, -0.5532, 0.000136, -0.00001149}, {93.27209062, 1739527262.8478, -12.7512, -0.001037, 0.00000417}, {297.85019547, 1602961601.2090, -6.3706, 0.006593, -0.00003169}, {125.04455501, -6962890.2665, 7.4722, 0.007702, -0.00005939}}; double tt[4]; int i, j; for (tt[0] = t, i = 1; i < 4; i++) { tt[i] = tt[i - 1] * t; } for (i = 0; i < 5; i++) { f[i] = fc[i][0] * 3600.0; for (j = 0; j < 4; j++) { f[i] += fc[i][j + 1] * tt[j]; } f[i] = fmod(f[i] * AS2R, 2.0 * PI); } } /* iau 1980 nutation ---------------------------------------------------------*/ void nut_iau1980(double t, const double *f, double *dpsi, double *deps) { static const double nut[106][10] = { {0, 0, 0, 0, 1, -6798.4, -171996, -174.2, 92025, 8.9}, {0, 0, 2, -2, 2, 182.6, -13187, -1.6, 5736, -3.1}, {0, 0, 2, 0, 2, 13.7, -2274, -0.2, 977, -0.5}, {0, 0, 0, 0, 2, -3399.2, 2062, 0.2, -895, 0.5}, {0, -1, 0, 0, 0, -365.3, -1426, 3.4, 54, -0.1}, {1, 0, 0, 0, 0, 27.6, 712, 0.1, -7, 0.0}, {0, 1, 2, -2, 2, 121.7, -517, 1.2, 224, -0.6}, {0, 0, 2, 0, 1, 13.6, -386, -0.4, 200, 0.0}, {1, 0, 2, 0, 2, 9.1, -301, 0.0, 129, -0.1}, {0, -1, 2, -2, 2, 365.2, 217, -0.5, -95, 0.3}, {-1, 0, 0, 2, 0, 31.8, 158, 0.0, -1, 0.0}, {0, 0, 2, -2, 1, 177.8, 129, 0.1, -70, 0.0}, {-1, 0, 2, 0, 2, 27.1, 123, 0.0, -53, 0.0}, {1, 0, 0, 0, 1, 27.7, 63, 0.1, -33, 0.0}, {0, 0, 0, 2, 0, 14.8, 63, 0.0, -2, 0.0}, {-1, 0, 2, 2, 2, 9.6, -59, 0.0, 26, 0.0}, {-1, 0, 0, 0, 1, -27.4, -58, -0.1, 32, 0.0}, {1, 0, 2, 0, 1, 9.1, -51, 0.0, 27, 0.0}, {-2, 0, 0, 2, 0, -205.9, -48, 0.0, 1, 0.0}, {-2, 0, 2, 0, 1, 1305.5, 46, 0.0, -24, 0.0}, {0, 0, 2, 2, 2, 7.1, -38, 0.0, 16, 0.0}, {2, 0, 2, 0, 2, 6.9, -31, 0.0, 13, 0.0}, {2, 0, 0, 0, 0, 13.8, 29, 0.0, -1, 0.0}, {1, 0, 2, -2, 2, 23.9, 29, 0.0, -12, 0.0}, {0, 0, 2, 0, 0, 13.6, 26, 0.0, -1, 0.0}, {0, 0, 2, -2, 0, 173.3, -22, 0.0, 0, 0.0}, {-1, 0, 2, 0, 1, 27.0, 21, 0.0, -10, 0.0}, {0, 2, 0, 0, 0, 182.6, 17, -0.1, 0, 0.0}, {0, 2, 2, -2, 2, 91.3, -16, 0.1, 7, 0.0}, {-1, 0, 0, 2, 1, 32.0, 16, 0.0, -8, 0.0}, {0, 1, 0, 0, 1, 386.0, -15, 0.0, 9, 0.0}, {1, 0, 0, -2, 1, -31.7, -13, 0.0, 7, 0.0}, {0, -1, 0, 0, 1, -346.6, -12, 0.0, 6, 0.0}, {2, 0, -2, 0, 0, -1095.2, 11, 0.0, 0, 0.0}, {-1, 0, 2, 2, 1, 9.5, -10, 0.0, 5, 0.0}, {1, 0, 2, 2, 2, 5.6, -8, 0.0, 3, 0.0}, {0, -1, 2, 0, 2, 14.2, -7, 0.0, 3, 0.0}, {0, 0, 2, 2, 1, 7.1, -7, 0.0, 3, 0.0}, {1, 1, 0, -2, 0, -34.8, -7, 0.0, 0, 0.0}, {0, 1, 2, 0, 2, 13.2, 7, 0.0, -3, 0.0}, {-2, 0, 0, 2, 1, -199.8, -6, 0.0, 3, 0.0}, {0, 0, 0, 2, 1, 14.8, -6, 0.0, 3, 0.0}, {2, 0, 2, -2, 2, 12.8, 6, 0.0, -3, 0.0}, {1, 0, 0, 2, 0, 9.6, 6, 0.0, 0, 0.0}, {1, 0, 2, -2, 1, 23.9, 6, 0.0, -3, 0.0}, {0, 0, 0, -2, 1, -14.7, -5, 0.0, 3, 0.0}, {0, -1, 2, -2, 1, 346.6, -5, 0.0, 3, 0.0}, {2, 0, 2, 0, 1, 6.9, -5, 0.0, 3, 0.0}, {1, -1, 0, 0, 0, 29.8, 5, 0.0, 0, 0.0}, {1, 0, 0, -1, 0, 411.8, -4, 0.0, 0, 0.0}, {0, 0, 0, 1, 0, 29.5, -4, 0.0, 0, 0.0}, {0, 1, 0, -2, 0, -15.4, -4, 0.0, 0, 0.0}, {1, 0, -2, 0, 0, -26.9, 4, 0.0, 0, 0.0}, {2, 0, 0, -2, 1, 212.3, 4, 0.0, -2, 0.0}, {0, 1, 2, -2, 1, 119.6, 4, 0.0, -2, 0.0}, {1, 1, 0, 0, 0, 25.6, -3, 0.0, 0, 0.0}, {1, -1, 0, -1, 0, -3232.9, -3, 0.0, 0, 0.0}, {-1, -1, 2, 2, 2, 9.8, -3, 0.0, 1, 0.0}, {0, -1, 2, 2, 2, 7.2, -3, 0.0, 1, 0.0}, {1, -1, 2, 0, 2, 9.4, -3, 0.0, 1, 0.0}, {3, 0, 2, 0, 2, 5.5, -3, 0.0, 1, 0.0}, {-2, 0, 2, 0, 2, 1615.7, -3, 0.0, 1, 0.0}, {1, 0, 2, 0, 0, 9.1, 3, 0.0, 0, 0.0}, {-1, 0, 2, 4, 2, 5.8, -2, 0.0, 1, 0.0}, {1, 0, 0, 0, 2, 27.8, -2, 0.0, 1, 0.0}, {-1, 0, 2, -2, 1, -32.6, -2, 0.0, 1, 0.0}, {0, -2, 2, -2, 1, 6786.3, -2, 0.0, 1, 0.0}, {-2, 0, 0, 0, 1, -13.7, -2, 0.0, 1, 0.0}, {2, 0, 0, 0, 1, 13.8, 2, 0.0, -1, 0.0}, {3, 0, 0, 0, 0, 9.2, 2, 0.0, 0, 0.0}, {1, 1, 2, 0, 2, 8.9, 2, 0.0, -1, 0.0}, {0, 0, 2, 1, 2, 9.3, 2, 0.0, -1, 0.0}, {1, 0, 0, 2, 1, 9.6, -1, 0.0, 0, 0.0}, {1, 0, 2, 2, 1, 5.6, -1, 0.0, 1, 0.0}, {1, 1, 0, -2, 1, -34.7, -1, 0.0, 0, 0.0}, {0, 1, 0, 2, 0, 14.2, -1, 0.0, 0, 0.0}, {0, 1, 2, -2, 0, 117.5, -1, 0.0, 0, 0.0}, {0, 1, -2, 2, 0, -329.8, -1, 0.0, 0, 0.0}, {1, 0, -2, 2, 0, 23.8, -1, 0.0, 0, 0.0}, {1, 0, -2, -2, 0, -9.5, -1, 0.0, 0, 0.0}, {1, 0, 2, -2, 0, 32.8, -1, 0.0, 0, 0.0}, {1, 0, 0, -4, 0, -10.1, -1, 0.0, 0, 0.0}, {2, 0, 0, -4, 0, -15.9, -1, 0.0, 0, 0.0}, {0, 0, 2, 4, 2, 4.8, -1, 0.0, 0, 0.0}, {0, 0, 2, -1, 2, 25.4, -1, 0.0, 0, 0.0}, {-2, 0, 2, 4, 2, 7.3, -1, 0.0, 1, 0.0}, {2, 0, 2, 2, 2, 4.7, -1, 0.0, 0, 0.0}, {0, -1, 2, 0, 1, 14.2, -1, 0.0, 0, 0.0}, {0, 0, -2, 0, 1, -13.6, -1, 0.0, 0, 0.0}, {0, 0, 4, -2, 2, 12.7, 1, 0.0, 0, 0.0}, {0, 1, 0, 0, 2, 409.2, 1, 0.0, 0, 0.0}, {1, 1, 2, -2, 2, 22.5, 1, 0.0, -1, 0.0}, {3, 0, 2, -2, 2, 8.7, 1, 0.0, 0, 0.0}, {-2, 0, 2, 2, 2, 14.6, 1, 0.0, -1, 0.0}, {-1, 0, 0, 0, 2, -27.3, 1, 0.0, -1, 0.0}, {0, 0, -2, 2, 1, -169.0, 1, 0.0, 0, 0.0}, {0, 1, 2, 0, 1, 13.1, 1, 0.0, 0, 0.0}, {-1, 0, 4, 0, 2, 9.1, 1, 0.0, 0, 0.0}, {2, 1, 0, -2, 0, 131.7, 1, 0.0, 0, 0.0}, {2, 0, 0, 2, 0, 7.1, 1, 0.0, 0, 0.0}, {2, 0, 2, -2, 1, 12.8, 1, 0.0, -1, 0.0}, {2, 0, -2, 0, 1, -943.2, 1, 0.0, 0, 0.0}, {1, -1, 0, -2, 0, -29.3, 1, 0.0, 0, 0.0}, {-1, 0, 0, 1, 1, -388.3, 1, 0.0, 0, 0.0}, {-1, -1, 0, 2, 1, 35.0, 1, 0.0, 0, 0.0}, {0, 1, 0, 1, 0, 27.3, 1, 0.0, 0, 0.0}}; double ang; int i, j; *dpsi = *deps = 0.0; for (i = 0; i < 106; i++) { ang = 0.0; for (j = 0; j < 5; j++) { ang += nut[i][j] * f[j]; } *dpsi += (nut[i][6] + nut[i][7] * t) * sin(ang); *deps += (nut[i][8] + nut[i][9] * t) * cos(ang); } *dpsi *= 1e-4 * AS2R; /* 0.1 mas -> rad */ *deps *= 1e-4 * AS2R; } /* eci to ecef transformation matrix ------------------------------------------- * compute eci to ecef transformation matrix * args : gtime_t tutc I time in utc * double *erpv I erp values {xp,yp,ut1_utc,lod} (rad,rad,s,s/d) * double *U O eci to ecef transformation matrix (3 x 3) * double *gmst IO greenwich mean sidereal time (rad) * (NULL: no output) * return : none * note : see ref [3] chap 5 * not thread-safe *-----------------------------------------------------------------------------*/ void eci2ecef(gtime_t tutc, const double *erpv, double *U, double *gmst) { const double ep2000[] = {2000, 1, 1, 12, 0, 0}; static gtime_t tutc_; static double U_[9], gmst_; gtime_t tgps; double eps, ze, th, z, t, t2, t3, dpsi, deps, gast, f[5]; double R1[9], R2[9], R3[9], R[9], W[9], N[9], P[9], NP[9]; int i; trace(4, "eci2ecef: tutc=%s\n", time_str(tutc, 3)); if (fabs(timediff(tutc, tutc_)) < 0.01) { /* read cache */ for (i = 0; i < 9; i++) { U[i] = U_[i]; } if (gmst) { *gmst = gmst_; } return; } tutc_ = tutc; /* terrestrial time */ tgps = utc2gpst(tutc_); t = (timediff(tgps, epoch2time(ep2000)) + 19.0 + 32.184) / 86400.0 / 36525.0; t2 = t * t; t3 = t2 * t; /* astronomical arguments */ ast_args(t, f); /* iau 1976 precession */ ze = (2306.2181 * t + 0.30188 * t2 + 0.017998 * t3) * AS2R; th = (2004.3109 * t - 0.42665 * t2 - 0.041833 * t3) * AS2R; z = (2306.2181 * t + 1.09468 * t2 + 0.018203 * t3) * AS2R; eps = (84381.448 - 46.8150 * t - 0.00059 * t2 + 0.001813 * t3) * AS2R; Rz(-z, R1); Ry(th, R2); Rz(-ze, R3); matmul("NN", 3, 3, 3, 1.0, R1, R2, 0.0, R); matmul("NN", 3, 3, 3, 1.0, R, R3, 0.0, P); /* P=Rz(-z)*Ry(th)*Rz(-ze) */ /* iau 1980 nutation */ nut_iau1980(t, f, &dpsi, &deps); Rx(-eps - deps, R1); Rz(-dpsi, R2); Rx(eps, R3); matmul("NN", 3, 3, 3, 1.0, R1, R2, 0.0, R); matmul("NN", 3, 3, 3, 1.0, R, R3, 0.0, N); /* N=Rx(-eps)*Rz(-dspi)*Rx(eps) */ /* greenwich apparent sidereal time (rad) */ gmst_ = utc2gmst(tutc_, erpv[2]); gast = gmst_ + dpsi * cos(eps); gast += (0.00264 * sin(f[4]) + 0.000063 * sin(2.0 * f[4])) * AS2R; /* eci to ecef transformation matrix */ Ry(-erpv[0], R1); Rx(-erpv[1], R2); Rz(gast, R3); matmul("NN", 3, 3, 3, 1.0, R1, R2, 0.0, W); matmul("NN", 3, 3, 3, 1.0, W, R3, 0.0, R); /* W=Ry(-xp)*Rx(-yp) */ matmul("NN", 3, 3, 3, 1.0, N, P, 0.0, NP); matmul("NN", 3, 3, 3, 1.0, R, NP, 0.0, U_); /* U=W*Rz(gast)*N*P */ for (i = 0; i < 9; i++) { U[i] = U_[i]; } if (gmst) { *gmst = gmst_; } trace(5, "gmst=%.12f gast=%.12f\n", gmst_, gast); trace(5, "P=\n"); tracemat(5, P, 3, 3, 15, 12); trace(5, "N=\n"); tracemat(5, N, 3, 3, 15, 12); trace(5, "W=\n"); tracemat(5, W, 3, 3, 15, 12); trace(5, "U=\n"); tracemat(5, U, 3, 3, 15, 12); } /* decode antenna parameter field --------------------------------------------*/ int decodef(char *p, int n, double *v) { int i; for (i = 0; i < n; i++) { v[i] = 0.0; } for (i = 0, p = strtok(p, " "); p && i < n; p = strtok(nullptr, " ")) { v[i++] = atof(p) * 1e-3; } return i; } /* add antenna parameter -----------------------------------------------------*/ void addpcv(const pcv_t *pcv, pcvs_t *pcvs) { pcv_t *pcvs_pcv; if (pcvs->nmax <= pcvs->n) { pcvs->nmax += 256; if (!(pcvs_pcv = static_cast(realloc(pcvs->pcv, sizeof(pcv_t) * pcvs->nmax)))) { trace(1, "addpcv: memory allocation error\n"); free(pcvs->pcv); pcvs->pcv = nullptr; pcvs->n = pcvs->nmax = 0; return; } pcvs->pcv = pcvs_pcv; } pcvs->pcv[pcvs->n++] = *pcv; } /* read ngs antenna parameter file -------------------------------------------*/ int readngspcv(const char *file, pcvs_t *pcvs) { FILE *fp; static const pcv_t pcv0 = {0, {}, {}, {0, 0}, {0, 0}, {{}, {}}, {{}, {}}}; pcv_t pcv = {0, {}, {}, {0, 0}, {0, 0}, {{}, {}}, {{}, {}}}; double neu[3]; int n = 0; char buff[256]; if (!(fp = fopen(file, "re"))) { trace(2, "ngs pcv file open error: %s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if (strlen(buff) >= 62 && buff[61] == '|') { continue; } if (buff[0] != ' ') { n = 0; /* start line */ } if (++n == 1) { pcv = pcv0; strncpy(pcv.type, buff, 61); pcv.type[61] = '\0'; } else if (n == 2) { if (decodef(buff, 3, neu) < 3) { continue; } pcv.off[0][0] = neu[1]; pcv.off[0][1] = neu[0]; pcv.off[0][2] = neu[2]; } else if (n == 3) { decodef(buff, 10, pcv.var[0]); } else if (n == 4) { decodef(buff, 9, pcv.var[0] + 10); } else if (n == 5) { if (decodef(buff, 3, neu) < 3) { continue; }; pcv.off[1][0] = neu[1]; pcv.off[1][1] = neu[0]; pcv.off[1][2] = neu[2]; } else if (n == 6) { decodef(buff, 10, pcv.var[1]); } else if (n == 7) { decodef(buff, 9, pcv.var[1] + 10); addpcv(&pcv, pcvs); } } fclose(fp); return 1; } /* read antex file ----------------------------------------------------------*/ int readantex(const char *file, pcvs_t *pcvs) { FILE *fp; static const pcv_t pcv0 = {0, {}, {}, {0, 0}, {0, 0}, {{}, {}}, {{}, {}}}; pcv_t pcv = {0, {}, {}, {0, 0}, {0, 0}, {{}, {}}, {{}, {}}}; double neu[3]; int i, f, freq = 0, state = 0, freqs[] = {1, 2, 5, 6, 7, 8, 0}; char buff[256]; trace(3, "readantex: file=%s\n", file); if (!(fp = fopen(file, "re"))) { trace(2, "antex pcv file open error: %s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if (strlen(buff) < 60 || strstr(buff + 60, "COMMENT")) { continue; } if (strstr(buff + 60, "START OF ANTENNA")) { pcv = pcv0; state = 1; } if (strstr(buff + 60, "END OF ANTENNA")) { addpcv(&pcv, pcvs); state = 0; } if (!state) { continue; } if (strstr(buff + 60, "TYPE / SERIAL NO")) { strncpy(pcv.type, buff, 20); pcv.type[20] = '\0'; strncpy(pcv.code, buff + 20, 20); pcv.code[20] = '\0'; if (!strncmp(pcv.code + 3, " ", 8)) { pcv.sat = satid2no(pcv.code); } } else if (strstr(buff + 60, "VALID FROM")) { if (!str2time(buff, 0, 43, &pcv.ts)) { continue; } } else if (strstr(buff + 60, "VALID UNTIL")) { if (!str2time(buff, 0, 43, &pcv.te)) { continue; } } else if (strstr(buff + 60, "START OF FREQUENCY")) { if (sscanf(buff + 4, "%d", &f) < 1) { continue; } for (i = 0; i < NFREQ; i++) { if (freqs[i] == f) { break; } } if (i < NFREQ) { freq = i + 1; } } else if (strstr(buff + 60, "END OF FREQUENCY")) { freq = 0; } else if (strstr(buff + 60, "NORTH / EAST / UP")) { if (freq < 1 || NFREQ < freq) { continue; } if (decodef(buff, 3, neu) < 3) { continue; } pcv.off[freq - 1][0] = neu[pcv.sat ? 0 : 1]; /* x or e */ pcv.off[freq - 1][1] = neu[pcv.sat ? 1 : 0]; /* y or n */ pcv.off[freq - 1][2] = neu[2]; /* z or u */ } else if (strstr(buff, "NOAZI")) { if (freq < 1 || NFREQ < freq) { continue; } if ((i = decodef(buff + 8, 19, pcv.var[freq - 1])) <= 0) { continue; } for (; i < 19; i++) { pcv.var[freq - 1][i] = pcv.var[freq - 1][i - 1]; } } } fclose(fp); return 1; } /* read antenna parameters ------------------------------------------------------ * read antenna parameters * args : char *file I antenna parameter file (antex) * pcvs_t *pcvs IO antenna parameters * return : status (1:ok,0:file open error) * notes : file with the externsion .atx or .ATX is recognized as antex * file except for antex is recognized ngs antenna parameters * see reference [3] * only support non-azimuth-depedent parameters *-----------------------------------------------------------------------------*/ int readpcv(const char *file, pcvs_t *pcvs) { pcv_t *pcv; const char *ext; int i, stat; trace(3, "readpcv: file=%s\n", file); if (!(ext = strrchr(file, '.'))) { ext = ""; } if (!strcmp(ext, ".atx") || !strcmp(ext, ".ATX")) { stat = readantex(file, pcvs); } else { stat = readngspcv(file, pcvs); } for (i = 0; i < pcvs->n; i++) { pcv = pcvs->pcv + i; trace(4, "sat=%2d type=%20s code=%s off=%8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n", pcv->sat, pcv->type, pcv->code, pcv->off[0][0], pcv->off[0][1], pcv->off[0][2], pcv->off[1][0], pcv->off[1][1], pcv->off[1][2]); } return stat; } /* search antenna parameter ---------------------------------------------------- * read satellite antenna phase center position * args : int sat I satellite number (0: receiver antenna) * char *type I antenna type for receiver antenna * gtime_t time I time to search parameters * pcvs_t *pcvs IO antenna parameters * return : antenna parameter (NULL: no antenna) *-----------------------------------------------------------------------------*/ pcv_t *searchpcv(int sat, const char *type, gtime_t time, const pcvs_t *pcvs) { pcv_t *pcv; char buff[MAXANT] = "", *types[2], *p; int i, j, n = 0; trace(3, "searchpcv: sat=%2d type=%s\n", sat, type); if (sat) { /* search satellite antenna */ for (i = 0; i < pcvs->n; i++) { pcv = pcvs->pcv + i; if (pcv->sat != sat) { continue; } if (pcv->ts.time != 0 && timediff(pcv->ts, time) > 0.0) { continue; } if (pcv->te.time != 0 && timediff(pcv->te, time) < 0.0) { continue; } return pcv; } } else { if (strlen(type) < MAXANT + 1) { strcpy(buff, type); } else { trace(1, "type array is too long"); } for (p = strtok(buff, " "); p && n < 2; p = strtok(nullptr, " ")) { types[n++] = p; } if (n <= 0) { return nullptr; } /* search receiver antenna with radome at first */ for (i = 0; i < pcvs->n; i++) { pcv = pcvs->pcv + i; for (j = 0; j < n; j++) { if (!strstr(pcv->type, types[j])) { break; } } if (j >= n) { return pcv; } } /* search receiver antenna without radome */ for (i = 0; i < pcvs->n; i++) { pcv = pcvs->pcv + i; if (strstr(pcv->type, types[0]) != pcv->type) { continue; } trace(2, "pcv without radome is used type=%s\n", type); return pcv; } } return nullptr; } /* read station positions ------------------------------------------------------ * read positions from station position file * args : char *file I station position file containing * lat(deg) lon(deg) height(m) name in a line * char *rcvs I station name * double *pos O station position {lat,lon,h} (rad/m) * (all 0 if search error) * return : none *-----------------------------------------------------------------------------*/ void readpos(const char *file, const char *rcv, double *pos) { static double poss[2048][3]; static char stas[2048][16]; FILE *fp; int i, j, len, np = 0; char buff[256], str[256]; trace(3, "readpos: file=%s\n", file); if (!(fp = fopen(file, "re"))) { fprintf(stderr, "reference position file open error : %s\n", file); return; } while (np < 2048 && fgets(buff, sizeof(buff), fp)) { if (buff[0] == '%' || buff[0] == '#') { continue; } if (sscanf(buff, "%lf %lf %lf %s", &poss[np][0], &poss[np][1], &poss[np][2], str) < 4) { continue; } // strncpy(stas[np], str, 15); This line triggers a warning. Replaced by: memcpy(stas[np], str, 15 * sizeof(stas[np][0])); stas[np++][15] = '\0'; } fclose(fp); len = static_cast(strlen(rcv)); for (i = 0; i < np; i++) { if (strncmp(stas[i], rcv, len) != 0) { continue; } for (j = 0; j < 3; j++) { pos[j] = poss[i][j]; } pos[0] *= D2R; pos[1] *= D2R; return; } pos[0] = pos[1] = pos[2] = 0.0; } /* read blq record -----------------------------------------------------------*/ int readblqrecord(FILE *fp, double *odisp) { double v[11]; char buff[256]; int i, n = 0; while (fgets(buff, sizeof(buff), fp)) { if (!strncmp(buff, "$$", 2)) { continue; } if (sscanf(buff, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", v, v + 1, v + 2, v + 3, v + 4, v + 5, v + 6, v + 7, v + 8, v + 9, v + 10) < 11) { continue; } for (i = 0; i < 11; i++) { odisp[n + i * 6] = v[i]; } if (++n == 6) { return 1; } } return 0; } /* read blq ocean tide loading parameters -------------------------------------- * read blq ocean tide loading parameters * args : char *file I BLQ ocean tide loading parameter file * char *sta I station name * double *odisp O ocean tide loading parameters * return : status (1:ok, 0:file open error) *-----------------------------------------------------------------------------*/ int readblq(const char *file, const char *sta, double *odisp) { FILE *fp; char buff[256], staname[32] = "", name[32], *p; /* station name to upper case */ sscanf(sta, "%16s", staname); for (p = staname; (*p = static_cast(toupper(static_cast(*p)))); p++) { ; } if (!(fp = fopen(file, "re"))) { trace(2, "blq file open error: file=%s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if (!strncmp(buff, "$$", 2) || strlen(buff) < 2) { continue; } if (sscanf(buff + 2, "%16s", name) < 1) { continue; } for (p = name; (*p = static_cast(toupper(static_cast(*p)))); p++) { ; } if (strcmp(name, staname) != 0) { continue; } /* read blq record */ if (readblqrecord(fp, odisp)) { fclose(fp); return 1; } } fclose(fp); trace(2, "no otl parameters: sta=%s file=%s\n", sta, file); return 0; } /* read earth rotation parameters ---------------------------------------------- * read earth rotation parameters * args : char *file I IGS ERP file (IGS ERP ver.2) * erp_t *erp O earth rotation parameters * return : status (1:ok,0:file open error) *-----------------------------------------------------------------------------*/ int readerp(const char *file, erp_t *erp) { FILE *fp; erpd_t *erp_data; double v[14] = {}; char buff[256]; trace(3, "readerp: file=%s\n", file); if (!(fp = fopen(file, "re"))) { trace(2, "erp file open error: file=%s\n", file); return 0; } while (fgets(buff, sizeof(buff), fp)) { if (sscanf(buff, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", v, v + 1, v + 2, v + 3, v + 4, v + 5, v + 6, v + 7, v + 8, v + 9, v + 10, v + 11, v + 12, v + 13) < 5) { continue; } if (erp->n >= erp->nmax) { erp->nmax = erp->nmax <= 0 ? 128 : erp->nmax * 2; erp_data = static_cast(realloc(erp->data, sizeof(erpd_t) * erp->nmax)); if (!erp_data) { free(erp->data); erp->data = nullptr; erp->n = erp->nmax = 0; fclose(fp); return 0; } erp->data = erp_data; } erp->data[erp->n].mjd = v[0]; erp->data[erp->n].xp = v[1] * 1e-6 * AS2R; erp->data[erp->n].yp = v[2] * 1e-6 * AS2R; erp->data[erp->n].ut1_utc = v[3] * 1e-7; erp->data[erp->n].lod = v[4] * 1e-7; erp->data[erp->n].xpr = v[12] * 1e-6 * AS2R; erp->data[erp->n++].ypr = v[13] * 1e-6 * AS2R; } fclose(fp); return 1; } /* get earth rotation parameter values ----------------------------------------- * get earth rotation parameter values * args : erp_t *erp I earth rotation parameters * gtime_t time I time (gpst) * double *erpv O erp values {xp,yp,ut1_utc,lod} (rad,rad,s,s/d) * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ int geterp(const erp_t *erp, gtime_t time, double *erpv) { const double ep[] = {2000, 1, 1, 12, 0, 0}; double mjd, day, a; int i, j, k; trace(4, "geterp:\n"); if (erp->n <= 0) { return 0; } mjd = 51544.5 + (timediff(gpst2utc(time), epoch2time(ep))) / 86400.0; if (mjd <= erp->data[0].mjd) { day = mjd - erp->data[0].mjd; erpv[0] = erp->data[0].xp + erp->data[0].xpr * day; erpv[1] = erp->data[0].yp + erp->data[0].ypr * day; erpv[2] = erp->data[0].ut1_utc - erp->data[0].lod * day; erpv[3] = erp->data[0].lod; return 1; } if (mjd >= erp->data[erp->n - 1].mjd) { day = mjd - erp->data[erp->n - 1].mjd; erpv[0] = erp->data[erp->n - 1].xp + erp->data[erp->n - 1].xpr * day; erpv[1] = erp->data[erp->n - 1].yp + erp->data[erp->n - 1].ypr * day; erpv[2] = erp->data[erp->n - 1].ut1_utc - erp->data[erp->n - 1].lod * day; erpv[3] = erp->data[erp->n - 1].lod; return 1; } for (j = 0, k = erp->n - 1; j < k - 1;) { i = (j + k) / 2; if (mjd < erp->data[i].mjd) { k = i; } else { j = i; } } if (erp->data[j].mjd == erp->data[j + 1].mjd) { a = 0.5; } else { a = (mjd - erp->data[j].mjd) / (erp->data[j + 1].mjd - erp->data[j].mjd); } erpv[0] = (1.0 - a) * erp->data[j].xp + a * erp->data[j + 1].xp; erpv[1] = (1.0 - a) * erp->data[j].yp + a * erp->data[j + 1].yp; erpv[2] = (1.0 - a) * erp->data[j].ut1_utc + a * erp->data[j + 1].ut1_utc; erpv[3] = (1.0 - a) * erp->data[j].lod + a * erp->data[j + 1].lod; return 1; } /* compare ephemeris ---------------------------------------------------------*/ int cmpeph(const void *p1, const void *p2) { auto *q1 = (eph_t *)p1, *q2 = (eph_t *)p2; return q1->ttr.time != q2->ttr.time ? static_cast(q1->ttr.time - q2->ttr.time) : (q1->toe.time != q2->toe.time ? static_cast(q1->toe.time - q2->toe.time) : q1->sat - q2->sat); } /* sort and unique ephemeris -------------------------------------------------*/ void uniqeph(nav_t *nav) { eph_t *nav_eph; int i, j; trace(3, "uniqeph: n=%d\n", nav->n); if (nav->n <= 0) { return; } qsort(nav->eph, nav->n, sizeof(eph_t), cmpeph); for (i = 1, j = 0; i < nav->n; i++) { if (nav->eph[i].sat != nav->eph[j].sat || nav->eph[i].iode != nav->eph[j].iode) { nav->eph[++j] = nav->eph[i]; } } nav->n = j + 1; if (!(nav_eph = static_cast(realloc(nav->eph, sizeof(eph_t) * nav->n)))) { trace(1, "uniqeph malloc error n=%d\n", nav->n); free(nav->eph); nav->eph = nullptr; nav->n = nav->nmax = 0; return; } nav->eph = nav_eph; nav->nmax = nav->n; trace(4, "uniqeph: n=%d\n", nav->n); } /* compare glonass ephemeris -------------------------------------------------*/ int cmpgeph(const void *p1, const void *p2) { auto *q1 = (geph_t *)p1, *q2 = (geph_t *)p2; return q1->tof.time != q2->tof.time ? static_cast(q1->tof.time - q2->tof.time) : (q1->toe.time != q2->toe.time ? static_cast(q1->toe.time - q2->toe.time) : q1->sat - q2->sat); } /* sort and unique glonass ephemeris -----------------------------------------*/ void uniqgeph(nav_t *nav) { geph_t *nav_geph; int i, j; trace(3, "uniqgeph: ng=%d\n", nav->ng); if (nav->ng <= 0) { return; } qsort(nav->geph, nav->ng, sizeof(geph_t), cmpgeph); for (i = j = 0; i < nav->ng; i++) { if (nav->geph[i].sat != nav->geph[j].sat || nav->geph[i].toe.time != nav->geph[j].toe.time || nav->geph[i].svh != nav->geph[j].svh) { nav->geph[++j] = nav->geph[i]; } } nav->ng = j + 1; if (!(nav_geph = static_cast(realloc(nav->geph, sizeof(geph_t) * nav->ng)))) { trace(1, "uniqgeph malloc error ng=%d\n", nav->ng); free(nav->geph); nav->geph = nullptr; nav->ng = nav->ngmax = 0; return; } nav->geph = nav_geph; nav->ngmax = nav->ng; trace(4, "uniqgeph: ng=%d\n", nav->ng); } /* compare sbas ephemeris ----------------------------------------------------*/ int cmpseph(const void *p1, const void *p2) { auto *q1 = (seph_t *)p1, *q2 = (seph_t *)p2; return q1->tof.time != q2->tof.time ? static_cast(q1->tof.time - q2->tof.time) : (q1->t0.time != q2->t0.time ? static_cast(q1->t0.time - q2->t0.time) : q1->sat - q2->sat); } /* sort and unique sbas ephemeris --------------------------------------------*/ void uniqseph(nav_t *nav) { seph_t *nav_seph; int i, j; trace(3, "uniqseph: ns=%d\n", nav->ns); if (nav->ns <= 0) { return; } qsort(nav->seph, nav->ns, sizeof(seph_t), cmpseph); for (i = j = 0; i < nav->ns; i++) { if (nav->seph[i].sat != nav->seph[j].sat || nav->seph[i].t0.time != nav->seph[j].t0.time) { nav->seph[++j] = nav->seph[i]; } } nav->ns = j + 1; if (!(nav_seph = static_cast(realloc(nav->seph, sizeof(seph_t) * nav->ns)))) { trace(1, "uniqseph malloc error ns=%d\n", nav->ns); free(nav->seph); nav->seph = nullptr; nav->ns = nav->nsmax = 0; return; } nav->seph = nav_seph; nav->nsmax = nav->ns; trace(4, "uniqseph: ns=%d\n", nav->ns); } /* unique ephemerides ---------------------------------------------------------- * unique ephemerides in navigation data and update carrier wave length * args : nav_t *nav IO navigation data * return : number of epochs *-----------------------------------------------------------------------------*/ void uniqnav(nav_t *nav) { int i, j; trace(3, "uniqnav: neph=%d ngeph=%d nseph=%d\n", nav->n, nav->ng, nav->ns); /* unique ephemeris */ uniqeph(nav); uniqgeph(nav); uniqseph(nav); /* update carrier wave length */ for (i = 0; i < MAXSAT; i++) { for (j = 0; j < NFREQ; j++) { nav->lam[i][j] = satwavelen(i + 1, j, nav); } } } /* compare observation data -------------------------------------------------*/ int cmpobs(const void *p1, const void *p2) { auto *q1 = (obsd_t *)p1, *q2 = (obsd_t *)p2; double tt = timediff(q1->time, q2->time); if (fabs(tt) > DTTOL) { return tt < 0 ? -1 : 1; } if (q1->rcv != q2->rcv) { return static_cast(q1->rcv) - static_cast(q2->rcv); } return static_cast(q1->sat) - static_cast(q2->sat); } /* sort and unique observation data -------------------------------------------- * sort and unique observation data by time, rcv, sat * args : obs_t *obs IO observation data * return : number of epochs *-----------------------------------------------------------------------------*/ int sortobs(obs_t *obs) { int i, j, n; trace(3, "sortobs: nobs=%d\n", obs->n); if (obs->n <= 0) { return 0; } qsort(obs->data, obs->n, sizeof(obsd_t), cmpobs); /* delete duplicated data */ for (i = j = 0; i < obs->n; i++) { if (obs->data[i].sat != obs->data[j].sat || obs->data[i].rcv != obs->data[j].rcv || timediff(obs->data[i].time, obs->data[j].time) != 0.0) { obs->data[++j] = obs->data[i]; } } obs->n = j + 1; for (i = n = 0; i < obs->n; i = j, n++) { for (j = i + 1; j < obs->n; j++) { if (timediff(obs->data[j].time, obs->data[i].time) > DTTOL) { break; } } } return n; } /* screen by time -------------------------------------------------------------- * screening by time start, time end, and time interval * args : gtime_t time I time * gtime_t ts I time start (ts.time==0:no screening by ts) * gtime_t te I time end (te.time==0:no screening by te) * double tint I time interval (s) (0.0:no screen by tint) * return : 1:on condition, 0:not on condition *-----------------------------------------------------------------------------*/ int screent(gtime_t time, gtime_t ts, gtime_t te, double tint) { return (tint <= 0.0 || fmod(time2gpst(time, nullptr) + DTTOL, tint) <= DTTOL * 2.0) && (ts.time == 0 || timediff(time, ts) >= -DTTOL) && (te.time == 0 || timediff(time, te) < DTTOL); } /* read/save navigation data --------------------------------------------------- * save or load navigation data * args : char file I file path * nav_t nav O/I navigation data * return : status (1:ok,0:no file) *-----------------------------------------------------------------------------*/ int readnav(const char *file, nav_t *nav) { FILE *fp; eph_t eph0 = {0, 0, 0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {}, {}, 0.0, 0.0}; geph_t geph0 = {0, 0, 0, 0, 0, 0, {0, 0}, {0, 0}, {}, {}, {}, 0.0, 0.0, 0.0}; char buff[4096], *p; int32_t toe_time, tof_time, toc_time, ttr_time; int i, sat, prn; trace(3, "loadnav: file=%s\n", file); if (!(fp = fopen(file, "re"))) { return 0; } while (fgets(buff, sizeof(buff), fp)) { if (!strncmp(buff, "IONUTC", 6)) { for (i = 0; i < 8; i++) { nav->ion_gps[i] = 0.0; } for (i = 0; i < 4; i++) { nav->utc_gps[i] = 0.0; } nav->leaps = 0; sscanf(buff, "IONUTC,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d", &nav->ion_gps[0], &nav->ion_gps[1], &nav->ion_gps[2], &nav->ion_gps[3], &nav->ion_gps[4], &nav->ion_gps[5], &nav->ion_gps[6], &nav->ion_gps[7], &nav->utc_gps[0], &nav->utc_gps[1], &nav->utc_gps[2], &nav->utc_gps[3], &nav->leaps); continue; } if ((p = strchr(buff, ','))) { *p = '\0'; } else { continue; } if (!(sat = satid2no(buff))) { continue; } if (satsys(sat, &prn) == SYS_GLO) { nav->geph[prn - 1] = geph0; nav->geph[prn - 1].sat = sat; toe_time = tof_time = 0; sscanf(p + 1, "%d,%d,%d,%d,%d,%d,%d,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf," "%lf,%lf,%lf,%lf", &nav->geph[prn - 1].iode, &nav->geph[prn - 1].frq, &nav->geph[prn - 1].svh, &nav->geph[prn - 1].sva, &nav->geph[prn - 1].age, &toe_time, &tof_time, &nav->geph[prn - 1].pos[0], &nav->geph[prn - 1].pos[1], &nav->geph[prn - 1].pos[2], &nav->geph[prn - 1].vel[0], &nav->geph[prn - 1].vel[1], &nav->geph[prn - 1].vel[2], &nav->geph[prn - 1].acc[0], &nav->geph[prn - 1].acc[1], &nav->geph[prn - 1].acc[2], &nav->geph[prn - 1].taun, &nav->geph[prn - 1].gamn, &nav->geph[prn - 1].dtaun); nav->geph[prn - 1].toe.time = toe_time; nav->geph[prn - 1].tof.time = tof_time; } else { nav->eph[sat - 1] = eph0; nav->eph[sat - 1].sat = sat; toe_time = toc_time = ttr_time = 0; sscanf(p + 1, "%d,%d,%d,%d,%d,%d,%d,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf," "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d", &nav->eph[sat - 1].iode, &nav->eph[sat - 1].iodc, &nav->eph[sat - 1].sva, &nav->eph[sat - 1].svh, &toe_time, &toc_time, &ttr_time, &nav->eph[sat - 1].A, &nav->eph[sat - 1].e, &nav->eph[sat - 1].i0, &nav->eph[sat - 1].OMG0, &nav->eph[sat - 1].omg, &nav->eph[sat - 1].M0, &nav->eph[sat - 1].deln, &nav->eph[sat - 1].OMGd, &nav->eph[sat - 1].idot, &nav->eph[sat - 1].crc, &nav->eph[sat - 1].crs, &nav->eph[sat - 1].cuc, &nav->eph[sat - 1].cus, &nav->eph[sat - 1].cic, &nav->eph[sat - 1].cis, &nav->eph[sat - 1].toes, &nav->eph[sat - 1].fit, &nav->eph[sat - 1].f0, &nav->eph[sat - 1].f1, &nav->eph[sat - 1].f2, &nav->eph[sat - 1].tgd[0], &nav->eph[sat - 1].code, &nav->eph[sat - 1].flag); nav->eph[sat - 1].toe.time = toe_time; nav->eph[sat - 1].toc.time = toc_time; nav->eph[sat - 1].ttr.time = ttr_time; } } fclose(fp); return 1; } int savenav(const char *file, const nav_t *nav) { FILE *fp; int i; char id[32]; trace(3, "savenav: file=%s\n", file); if (!(fp = fopen(file, "we"))) { return 0; } for (i = 0; i < MAXSAT; i++) { if (nav->eph[i].ttr.time == 0) { continue; } satno2id(nav->eph[i].sat, id); fprintf(fp, "%s,%d,%d,%d,%d,%d,%d,%d,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E," "%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E," "%.14E,%.14E,%.14E,%.14E,%.14E,%d,%d\n", id, nav->eph[i].iode, nav->eph[i].iodc, nav->eph[i].sva, nav->eph[i].svh, static_cast(nav->eph[i].toe.time), static_cast(nav->eph[i].toc.time), static_cast(nav->eph[i].ttr.time), nav->eph[i].A, nav->eph[i].e, nav->eph[i].i0, nav->eph[i].OMG0, nav->eph[i].omg, nav->eph[i].M0, nav->eph[i].deln, nav->eph[i].OMGd, nav->eph[i].idot, nav->eph[i].crc, nav->eph[i].crs, nav->eph[i].cuc, nav->eph[i].cus, nav->eph[i].cic, nav->eph[i].cis, nav->eph[i].toes, nav->eph[i].fit, nav->eph[i].f0, nav->eph[i].f1, nav->eph[i].f2, nav->eph[i].tgd[0], nav->eph[i].code, nav->eph[i].flag); } for (i = 0; i < MAXPRNGLO; i++) { if (nav->geph[i].tof.time == 0) { continue; } satno2id(nav->geph[i].sat, id); fprintf(fp, "%s,%d,%d,%d,%d,%d,%d,%d,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E," "%.14E,%.14E,%.14E,%.14E,%.14E,%.14E\n", id, nav->geph[i].iode, nav->geph[i].frq, nav->geph[i].svh, nav->geph[i].sva, nav->geph[i].age, static_cast(nav->geph[i].toe.time), static_cast(nav->geph[i].tof.time), nav->geph[i].pos[0], nav->geph[i].pos[1], nav->geph[i].pos[2], nav->geph[i].vel[0], nav->geph[i].vel[1], nav->geph[i].vel[2], nav->geph[i].acc[0], nav->geph[i].acc[1], nav->geph[i].acc[2], nav->geph[i].taun, nav->geph[i].gamn, nav->geph[i].dtaun); } fprintf(fp, "IONUTC,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E,%.14E," "%.14E,%.14E,%.14E,%d", nav->ion_gps[0], nav->ion_gps[1], nav->ion_gps[2], nav->ion_gps[3], nav->ion_gps[4], nav->ion_gps[5], nav->ion_gps[6], nav->ion_gps[7], nav->utc_gps[0], nav->utc_gps[1], nav->utc_gps[2], nav->utc_gps[3], nav->leaps); fclose(fp); return 1; } /* free observation data ------------------------------------------------------- * free memory for observation data * args : obs_t *obs IO observation data * return : none *-----------------------------------------------------------------------------*/ void freeobs(obs_t *obs) { free(obs->data); obs->data = nullptr; obs->n = obs->nmax = 0; } /* free navigation data --------------------------------------------------------- * free memory for navigation data * args : nav_t *nav IO navigation data * int opt I option (one of the following) * (0x01: gps/qzs ephmeris, 0x02: glonass ephemeris, * 0x04: sbas ephemeris, 0x08: precise ephemeris, * 0x10: precise clock 0x20: almanac, * 0x40: tec data) * return : none *-----------------------------------------------------------------------------*/ void freenav(nav_t *nav, int opt) { if (opt & 0x01) { free(nav->eph); nav->eph = nullptr; nav->n = nav->nmax = 0; } if (opt & 0x02) { free(nav->geph); nav->geph = nullptr; nav->ng = nav->ngmax = 0; } if (opt & 0x04) { free(nav->seph); nav->seph = nullptr; nav->ns = nav->nsmax = 0; } if (opt & 0x08) { free(nav->peph); nav->peph = nullptr; nav->ne = nav->nemax = 0; } if (opt & 0x10) { free(nav->pclk); nav->pclk = nullptr; nav->nc = nav->ncmax = 0; } if (opt & 0x20) { free(nav->alm); nav->alm = nullptr; nav->na = nav->namax = 0; } if (opt & 0x40) { free(nav->tec); nav->tec = nullptr; nav->nt = nav->ntmax = 0; } if (opt & 0x80) { free(nav->fcb); nav->fcb = nullptr; nav->nf = nav->nfmax = 0; } } /* debug trace functions -----------------------------------------------------*/ //#ifdef TRACE // FILE *fp_trace = nullptr; /* file pointer of trace */ char file_trace[1024]; /* trace file */ static int level_trace = 0; /* level of trace */ unsigned int tick_trace = 0; /* tick time at traceopen (ms) */ gtime_t time_trace = {0, 0.0}; /* time at traceopen */ pthread_mutex_t lock_trace; /* lock for trace */ void traceswap(void) { gtime_t time = utc2gpst(timeget()); char path[1024]; rtk_lock(&lock_trace); if (static_cast(time2gpst(time, nullptr) / INT_SWAP_TRAC) == static_cast(time2gpst(time_trace, nullptr) / INT_SWAP_TRAC)) { rtk_unlock(&lock_trace); return; } time_trace = time; if (!reppath(file_trace, path, time, "", "")) { rtk_unlock(&lock_trace); return; } if (fp_trace) { fclose(fp_trace); } if (!(fp_trace = fopen(path, "we"))) { fp_trace = stderr; } rtk_unlock(&lock_trace); } void traceopen(const char *file) { gtime_t time = utc2gpst(timeget()); char path[1024]; reppath(file, path, time, "", ""); if (!*path || !(fp_trace = fopen(path, "we"))) { fp_trace = stderr; } if (strlen(file) < 1025) { strcpy(file_trace, file); } else { trace(1, "file array is too long"); } tick_trace = tickget(); time_trace = time; initlock(&lock_trace); } void traceclose(void) { if (fp_trace && fp_trace != stderr) { fclose(fp_trace); } fp_trace = nullptr; file_trace[0] = '\0'; } void tracelevel(int level) { level_trace = level; } //extern void trace(int level, const char *format, ...) //{ // va_list ap; // // /* print error message to stderr */ // if (level <= 1) { // va_start(ap,format); vfprintf(stderr,format,ap); va_end(ap); // } // if (!fp_trace||level>level_trace) return; // traceswap(); // fprintf(fp_trace,"%d ",level); // va_start(ap,format); vfprintf(fp_trace,format,ap); va_end(ap); // fflush(fp_trace); //} void tracet(int level, const char *format, ...) { va_list ap; if (!fp_trace || level > level_trace) { return; } traceswap(); fprintf(fp_trace, "%d %9.3f: ", level, (tickget() - tick_trace) / 1000.0); va_start(ap, format); vfprintf(fp_trace, format, ap); va_end(ap); fflush(fp_trace); } void tracemat(int level, const double *A, int n, int m, int p, int q) { std::string buffer_; matsprint(A, n, m, p, q, buffer_); VLOG(level) << buffer_; } void traceobs(int level __attribute__((unused)), const obsd_t *obs __attribute__((unused)), int n __attribute__((unused))) { // char str[64],id[16]; // int i; // // if (!fp_trace||level>level_trace) return; // for (i=0;ilevel_trace) return; // for (i=0;in;i++) { // time2str(nav->eph[i].toe,s1,0); // time2str(nav->eph[i].ttr,s2,0); // satno2id(nav->eph[i].sat,id); // fprintf(fp_trace,"(%3d) %-3s : %s %s %3d %3d %02x\n",i+1, // id,s1,s2,nav->eph[i].iode,nav->eph[i].iodc,nav->eph[i].svh); // } // fprintf(fp_trace,"(ion) %9.4e %9.4e %9.4e %9.4e\n",nav->ion_gps[0], // nav->ion_gps[1],nav->ion_gps[2],nav->ion_gps[3]); // fprintf(fp_trace,"(ion) %9.4e %9.4e %9.4e %9.4e\n",nav->ion_gps[4], // nav->ion_gps[5],nav->ion_gps[6],nav->ion_gps[7]); // fprintf(fp_trace,"(ion) %9.4e %9.4e %9.4e %9.4e\n",nav->ion_gal[0], // nav->ion_gal[1],nav->ion_gal[2],nav->ion_gal[3]); //} //extern void tracegnav(int level, const nav_t *nav) //{ // char s1[64],s2[64],id[16]; // int i; // // if (!fp_trace||level>level_trace) return; // for (i=0;ing;i++) { // time2str(nav->geph[i].toe,s1,0); // time2str(nav->geph[i].tof,s2,0); // satno2id(nav->geph[i].sat,id); // fprintf(fp_trace,"(%3d) %-3s : %s %s %2d %2d %8.3f\n",i+1, // id,s1,s2,nav->geph[i].frq,nav->geph[i].svh,nav->geph[i].taun*1e6); // } //} //extern void tracehnav(int level, const nav_t *nav) //{ // char s1[64],s2[64],id[16]; // int i; // // if (!fp_trace||level>level_trace) return; // for (i=0;ins;i++) { // time2str(nav->seph[i].t0,s1,0); // time2str(nav->seph[i].tof,s2,0); // satno2id(nav->seph[i].sat,id); // fprintf(fp_trace,"(%3d) %-3s : %s %s %2d %2d\n",i+1, // id,s1,s2,nav->seph[i].svh,nav->seph[i].sva); // } //} //extern void tracepeph(int level, const nav_t *nav) //{ // char s[64],id[16]; // int i,j; // // if (!fp_trace||level>level_trace) return; // // for (i=0;ine;i++) { // time2str(nav->peph[i].time,s,0); // for (j=0;jpeph[i].index,id, // nav->peph[i].pos[j][0],nav->peph[i].pos[j][1], // nav->peph[i].pos[j][2],nav->peph[i].pos[j][3]*1e9, // nav->peph[i].std[j][0],nav->peph[i].std[j][1], // nav->peph[i].std[j][2],nav->peph[i].std[j][3]*1e9); // } // } //} //extern void tracepclk(int level, const nav_t *nav) //{ // char s[64],id[16]; // int i,j; // // if (!fp_trace||level>level_trace) return; // // for (i=0;inc;i++) { // time2str(nav->pclk[i].time,s,0); // for (j=0;jpclk[i].index,id, // nav->pclk[i].clk[j][0]*1e9,nav->pclk[i].std[j][0]*1e9); // } // } //} //extern void traceb(int level, const unsigned char *p, int n) //{ // int i; // if (!fp_trace||level>level_trace) return; // for (i=0;i:error) *-----------------------------------------------------------------------------*/ int execcmd(const char *cmd) { trace(3, "execcmd: cmd=%s\n", cmd); return system(cmd); } /* create directory ------------------------------------------------------------ * create directory if not exist * args : char *path I file path to be saved * return : none * notes : not recursive. only one level *-----------------------------------------------------------------------------*/ void createdir(const char *path) { char buff[1024], *p; //tracet(3, "createdir: path=%s\n", path); if (strlen(path) < 1025) { strcpy(buff, path); } else { trace(1, "path is too long"); } if (!(p = strrchr(buff, FILEPATHSEP))) { return; } *p = '\0'; if (mkdir(buff, 0777) != 0) { trace(1, "Error creating folder"); } } /* replace string ------------------------------------------------------------*/ int repstr(char *str, const char *pat, const char *rep) { int len = static_cast(strlen(pat)); char buff[1024], *p, *q, *r; for (p = str, r = buff; *p; p = q + len) { if (!(q = strstr(p, pat))) { break; } strncpy(r, p, q - p); r += q - p; r += sprintf(r, "%s", rep); } if (p <= str) { return 0; } if (strlen(p) < 1025) { strcpy(r, p); } else { trace(1, "pat array is too long"); } strcpy(str, buff); return 1; } /* replace keywords in file path ----------------------------------------------- * replace keywords in file path with date, time, rover and base station id * args : char *path I file path (see below) * char *rpath O file path in which keywords replaced (see below) * gtime_t time I time (gpst) (time.time==0: not replaced) * char *rov I rover id string ("": not replaced) * char *base I base station id string ("": not replaced) * return : status (1:keywords replaced, 0:no valid keyword in the path, * -1:no valid time) * notes : the following keywords in path are replaced by date, time and name * %Y -> yyyy : year (4 digits) (1900-2099) * %y -> yy : year (2 digits) (00-99) * %m -> mm : month (01-12) * %d -> dd : day of month (01-31) * %h -> hh : hours (00-23) * %M -> mm : minutes (00-59) * %S -> ss : seconds (00-59) * %n -> ddd : day of year (001-366) * %W -> wwww : gps week (0001-9999) * %D -> d : day of gps week (0-6) * %H -> h : hour code (a=0,b=1,c=2,...,x=23) * %ha-> hh : 3 hours (00,03,06,...,21) * %hb-> hh : 6 hours (00,06,12,18) * %hc-> hh : 12 hours (00,12) * %t -> mm : 15 minutes (00,15,30,45) * %r -> rrrr : rover id * %b -> bbbb : base station id *-----------------------------------------------------------------------------*/ int reppath(const char *path, char *rpath, gtime_t time, const char *rov, const char *base) { double ep[6], ep0[6] = {2000, 1, 1, 0, 0, 0}; int week, dow, doy, stat = 0; char rep[64]; strcpy(rpath, path); if (!strstr(rpath, "%")) { return 0; } if (*rov) { stat |= repstr(rpath, "%r", rov); } if (*base) { stat |= repstr(rpath, "%b", base); } if (time.time != 0) { time2epoch(time, ep); ep0[0] = ep[0]; dow = static_cast(floor(time2gpst(time, &week) / 86400.0)); doy = static_cast(floor(timediff(time, epoch2time(ep0)) / 86400.0)) + 1; sprintf(rep, "%02d", (static_cast(ep[3]) / 3) * 3); stat |= repstr(rpath, "%ha", rep); sprintf(rep, "%02d", (static_cast(ep[3]) / 6) * 6); stat |= repstr(rpath, "%hb", rep); sprintf(rep, "%02d", (static_cast(ep[3]) / 12) * 12); stat |= repstr(rpath, "%hc", rep); sprintf(rep, "%04.0f", ep[0]); stat |= repstr(rpath, "%Y", rep); sprintf(rep, "%02.0f", fmod(ep[0], 100.0)); stat |= repstr(rpath, "%y", rep); sprintf(rep, "%02.0f", ep[1]); stat |= repstr(rpath, "%m", rep); sprintf(rep, "%02.0f", ep[2]); stat |= repstr(rpath, "%d", rep); sprintf(rep, "%02.0f", ep[3]); stat |= repstr(rpath, "%h", rep); sprintf(rep, "%02.0f", ep[4]); stat |= repstr(rpath, "%M", rep); sprintf(rep, "%02.0f", floor(ep[5])); stat |= repstr(rpath, "%S", rep); sprintf(rep, "%03d", doy); stat |= repstr(rpath, "%n", rep); sprintf(rep, "%04d", week); stat |= repstr(rpath, "%W", rep); sprintf(rep, "%d", dow); stat |= repstr(rpath, "%D", rep); sprintf(rep, "%c", 'a' + static_cast(ep[3])); stat |= repstr(rpath, "%H", rep); sprintf(rep, "%02d", (static_cast(ep[4]) / 15) * 15); stat |= repstr(rpath, "%t", rep); } else if (strstr(rpath, "%ha") || strstr(rpath, "%hb") || strstr(rpath, "%hc") || strstr(rpath, "%Y") || strstr(rpath, "%y") || strstr(rpath, "%m") || strstr(rpath, "%d") || strstr(rpath, "%h") || strstr(rpath, "%M") || strstr(rpath, "%S") || strstr(rpath, "%n") || strstr(rpath, "%W") || strstr(rpath, "%D") || strstr(rpath, "%H") || strstr(rpath, "%t")) { return -1; /* no valid time */ } return stat; } /* replace keywords in file path and generate multiple paths ------------------- * replace keywords in file path with date, time, rover and base station id * generate multiple keywords-replaced paths * args : char *path I file path (see below) * char *rpath[] O file paths in which keywords replaced * int nmax I max number of output file paths * gtime_t ts I time start (gpst) * gtime_t te I time end (gpst) * char *rov I rover id string ("": not replaced) * char *base I base station id string ("": not replaced) * return : number of replaced file paths * notes : see reppath() for replacements of keywords. * minimum interval of time replaced is 900s. *-----------------------------------------------------------------------------*/ int reppaths(const char *path, char *rpath[], int nmax, gtime_t ts, gtime_t te, const char *rov, const char *base) { gtime_t time; double tow, tint = 86400.0; int i, n = 0, week; trace(3, "reppaths: path =%s nmax=%d rov=%s base=%s\n", path, nmax, rov, base); if (ts.time == 0 || te.time == 0 || timediff(ts, te) > 0.0) { return 0; } if (strstr(path, "%S") || strstr(path, "%M") || strstr(path, "%t")) { tint = 900.0; } else if (strstr(path, "%h") || strstr(path, "%H")) { tint = 3600.0; } tow = time2gpst(ts, &week); time = gpst2time(week, floor(tow / tint) * tint); while (timediff(time, te) <= 0.0 && n < nmax) { reppath(path, rpath[n], time, rov, base); if (n == 0 || strcmp(rpath[n], rpath[n - 1]) != 0) { n++; } time = timeadd(time, tint); } for (i = 0; i < n; i++) { trace(3, "reppaths: rpath=%s\n", rpath[i]); } return n; } /* satellite carrier wave length ----------------------------------------------- * get satellite carrier wave lengths * args : int sat I satellite number * int frq I frequency index (0:L1,1:L2,2:L5/3,...) * nav_t *nav I navigation messages * return : carrier wave length (m) (0.0: error) *-----------------------------------------------------------------------------*/ double satwavelen(int sat, int frq, const nav_t *nav) { const double freq_glo[] = {FREQ1_GLO, FREQ2_GLO}; const double dfrq_glo[] = {DFRQ1_GLO, DFRQ2_GLO}; int i, sys = satsys(sat, nullptr); if (sys == SYS_GLO) { if (0 <= frq && frq <= 1) { for (i = 0; i < nav->ng; i++) { if (nav->geph[i].sat != sat) { continue; } return SPEED_OF_LIGHT / (freq_glo[frq] + dfrq_glo[frq] * nav->geph[i].frq); } } else if (frq == 2) { /* L3 */ return SPEED_OF_LIGHT / FREQ3_GLO; } } else if (sys == SYS_BDS) { if (frq == 0) { return SPEED_OF_LIGHT / FREQ1_BDS; /* B1 */ } if (frq == 1) { return SPEED_OF_LIGHT / FREQ2_BDS; /* B2 */ } if (frq == 2) { return SPEED_OF_LIGHT / FREQ3_BDS; /* B3 */ } } else { if (frq == 0) { return SPEED_OF_LIGHT / FREQ1; /* L1/E1 */ } if (frq == 1) { return SPEED_OF_LIGHT / FREQ2; /* L2 */ } if (frq == 2) { return SPEED_OF_LIGHT / FREQ5; /* L5/E5a */ } if (frq == 3) { return SPEED_OF_LIGHT / FREQ6; /* L6/LEX */ } if (frq == 4) { return SPEED_OF_LIGHT / FREQ7; /* E5b */ } if (frq == 5) { return SPEED_OF_LIGHT / FREQ8; /* E5a+b */ } if (frq == 6) { return SPEED_OF_LIGHT / FREQ9; /* S */ } } return 0.0; } /* geometric distance ---------------------------------------------------------- * compute geometric distance and receiver-to-satellite unit vector * args : double *rs I satellilte position (ecef at transmission) (m) * double *rr I receiver position (ecef at reception) (m) * double *e O line-of-sight vector (ecef) * return : geometric distance (m) (0>:error/no satellite position) * notes : distance includes sagnac effect correction *-----------------------------------------------------------------------------*/ double geodist(const double *rs, const double *rr, double *e) { double r; int i; if (norm_rtk(rs, 3) < RE_WGS84) { return -1.0; } for (i = 0; i < 3; i++) { e[i] = rs[i] - rr[i]; } r = norm_rtk(e, 3); for (i = 0; i < 3; i++) { e[i] /= r; } return r + DEFAULT_OMEGA_EARTH_DOT * (rs[0] * rr[1] - rs[1] * rr[0]) / SPEED_OF_LIGHT; } /* satellite azimuth/elevation angle ------------------------------------------- * compute satellite azimuth/elevation angle * args : double *pos I geodetic position {lat,lon,h} (rad,m) * double *e I receiver-to-satellilte unit vector (ecef) * double *azel IO azimuth/elevation {az,el} (rad) (NULL: no output) * (0.0<=azel[0]<2*pi,-pi/2<=azel[1]<=pi/2) * return : elevation angle (rad) *-----------------------------------------------------------------------------*/ double satazel(const double *pos, const double *e, double *azel) { double az = 0.0, el = PI / 2.0, enu[3]; if (pos[2] > -RE_WGS84) { ecef2enu(pos, e, enu); az = dot(enu, enu, 2) < 1e-12 ? 0.0 : atan2(enu[0], enu[1]); if (az < 0.0) { az += 2 * PI; } el = asin(enu[2]); } if (azel) { azel[0] = az; azel[1] = el; } return el; } /* compute dops ---------------------------------------------------------------- * compute DOP (dilution of precision) * args : int ns I number of satellites * double *azel I satellite azimuth/elevation angle (rad) * double elmin I elevation cutoff angle (rad) * double *dop O DOPs {GDOP,PDOP,HDOP,VDOP} * return : none * notes : dop[0]-[3] return 0 in case of dop computation error *-----------------------------------------------------------------------------*/ void dops(int ns, const double *azel, double elmin, double *dop) { double H[4 * MAXSAT], Q[16], cosel, sinel; int i, n; for (i = 0; i < 4; i++) { dop[i] = 0.0; } for (i = n = 0; i < ns && i < MAXSAT; i++) { if (azel[1 + i * 2] < elmin || azel[1 + i * 2] <= 0.0) { continue; } cosel = cos(azel[1 + i * 2]); sinel = sin(azel[1 + i * 2]); H[4 * n] = cosel * sin(azel[i * 2]); H[1 + 4 * n] = cosel * cos(azel[i * 2]); H[2 + 4 * n] = sinel; H[3 + 4 * n++] = 1.0; } if (n < 4) { return; } matmul("NT", 4, 4, n, 1.0, H, H, 0.0, Q); if (!matinv(Q, 4)) { dop[0] = std::sqrt(Q[0] + Q[5] + Q[10] + Q[15]); /* GDOP */ dop[1] = std::sqrt(Q[0] + Q[5] + Q[10]); /* PDOP */ dop[2] = std::sqrt(Q[0] + Q[5]); /* HDOP */ dop[3] = std::sqrt(Q[10]); /* VDOP */ } } /* ionosphere model ------------------------------------------------------------ * compute ionospheric delay by broadcast ionosphere model (klobuchar model) * args : gtime_t t I time (gpst) * double *ion I iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} * double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * return : ionospheric delay (L1) (m) *-----------------------------------------------------------------------------*/ double ionmodel(gtime_t t, const double *ion, const double *pos, const double *azel) { const double ion_default[] = {/* 2004/1/1 */ 0.1118E-07, -0.7451e-08, -0.5961e-07, 0.1192E-06, 0.1167E+06, -0.2294E+06, -0.1311e+06, 0.1049E+07}; double tt, f, psi, phi, lam, amp, per, x; int week; if (pos[2] < -1e3 || azel[1] <= 0) { return 0.0; } if (norm_rtk(ion, 8) <= 0.0) { ion = ion_default; } /* earth centered angle (semi-circle) */ psi = 0.0137 / (azel[1] / PI + 0.11) - 0.022; /* subionospheric latitude/longitude (semi-circle) */ phi = pos[0] / PI + psi * cos(azel[0]); if (phi > 0.416) { phi = 0.416; } else if (phi < -0.416) { phi = -0.416; } lam = pos[1] / PI + psi * sin(azel[0]) / cos(phi * PI); /* geomagnetic latitude (semi-circle) */ phi += 0.064 * cos((lam - 1.617) * PI); /* local time (s) */ tt = 43200.0 * lam + time2gpst(t, &week); tt -= floor(tt / 86400.0) * 86400.0; /* 0 <= tt<86400 */ /* slant factor */ f = 1.0 + 16.0 * pow(0.53 - azel[1] / PI, 3.0); /* ionospheric delay */ amp = ion[0] + phi * (ion[1] + phi * (ion[2] + phi * ion[3])); per = ion[4] + phi * (ion[5] + phi * (ion[6] + phi * ion[7])); amp = amp < 0.0 ? 0.0 : amp; per = per < 72000.0 ? 72000.0 : per; x = 2.0 * PI * (tt - 50400.0) / per; return SPEED_OF_LIGHT * f * (fabs(x) < 1.57 ? 5E-9 + amp * (1.0 + x * x * (-0.5 + x * x / 24.0)) : 5E-9); } /* ionosphere mapping function ------------------------------------------------- * compute ionospheric delay mapping function by single layer model * args : double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * return : ionospheric mapping function *-----------------------------------------------------------------------------*/ double ionmapf(const double *pos, const double *azel) { if (pos[2] >= HION) { return 1.0; } return 1.0 / cos(asin((RE_WGS84 + pos[2]) / (RE_WGS84 + HION) * sin(PI / 2.0 - azel[1]))); } /* ionospheric pierce point position ------------------------------------------- * compute ionospheric pierce point (ipp) position and slant factor * args : double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * double re I earth radius (km) * double hion I altitude of ionosphere (km) * double *posp O pierce point position {lat,lon,h} (rad,m) * return : slant factor * notes : see ref [2], only valid on the earth surface * fixing bug on ref [2] A.4.4.10.1 A-22,23 *-----------------------------------------------------------------------------*/ double ionppp(const double *pos, const double *azel, double re, double hion, double *posp) { double cosaz, rp, ap, sinap, tanap; rp = re / (re + hion) * cos(azel[1]); ap = PI / 2.0 - azel[1] - asin(rp); sinap = sin(ap); tanap = tan(ap); cosaz = cos(azel[0]); posp[0] = asin(sin(pos[0]) * cos(ap) + cos(pos[0]) * sinap * cosaz); if ((pos[0] > 70.0 * D2R && tanap * cosaz > tan(PI / 2.0 - pos[0])) || (pos[0] < -70.0 * D2R && -tanap * cosaz > tan(PI / 2.0 + pos[0]))) { posp[1] = pos[1] + PI - asin(sinap * sin(azel[0]) / cos(posp[0])); } else { posp[1] = pos[1] + asin(sinap * sin(azel[0]) / cos(posp[0])); } return 1.0 / sqrt(1.0 - rp * rp); } /* troposphere model ----------------------------------------------------------- * compute tropospheric delay by standard atmosphere and saastamoinen model * args : gtime_t time I time * double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * double humi I relative humidity * return : tropospheric delay (m) *-----------------------------------------------------------------------------*/ double tropmodel(gtime_t time __attribute__((unused)), const double *pos, const double *azel, double humi) { const double temp0 = 15.0; /* temparature at sea level */ double hgt, pres, temp, e, z, trph, trpw; if (pos[2] < -100.0 || 1e4 < pos[2] || azel[1] <= 0) { return 0.0; } /* standard atmosphere */ hgt = pos[2] < 0.0 ? 0.0 : pos[2]; pres = 1013.25 * pow(1.0 - 2.2557E-5 * hgt, 5.2568); temp = temp0 - 6.5E-3 * hgt + 273.16; e = 6.108 * humi * exp((17.15 * temp - 4684.0) / (temp - 38.45)); /* saastamoninen model */ z = PI / 2.0 - azel[1]; trph = 0.0022768 * pres / (1.0 - 0.00266 * cos(2.0 * pos[0]) - 0.00028 * hgt / 1e3) / cos(z); trpw = 0.002277 * (1255.0 / temp + 0.05) * e / cos(z); return trph + trpw; } #ifndef IERS_MODEL double interpc(const double coef[], double lat) { int i = static_cast(lat / 15.0); if (i < 1) { return coef[0]; } if (i > 4) { return coef[4]; } return coef[i - 1] * (1.0 - lat / 15.0 + i) + coef[i] * (lat / 15.0 - i); } double mapf(double el, double a, double b, double c) { double sinel = sin(el); return (1.0 + a / (1.0 + b / (1.0 + c))) / (sinel + (a / (sinel + b / (sinel + c)))); } double nmf(gtime_t time, const double pos[], const double azel[], double *mapfw) { /* ref [5] table 3 */ /* hydro-ave-a,b,c, hydro-amp-a,b,c, wet-a,b,c at latitude 15,30,45,60,75 */ const double coef[][5] = { {1.2769934E-3, 1.2683230E-3, 1.2465397E-3, 1.2196049E-3, 1.2045996E-3}, {2.9153695E-3, 2.9152299E-3, 2.9288445E-3, 2.9022565E-3, 2.9024912E-3}, {62.610505E-3, 62.837393E-3, 63.721774E-3, 63.824265E-3, 64.258455E-3}, {0.0000000E-0, 1.2709626E-5, 2.6523662E-5, 3.4000452E-5, 4.1202191e-5}, {0.0000000E-0, 2.1414979E-5, 3.0160779E-5, 7.2562722E-5, 11.723375E-5}, {0.0000000E-0, 9.0128400E-5, 4.3497037E-5, 84.795348E-5, 170.37206E-5}, {5.8021897E-4, 5.6794847E-4, 5.8118019E-4, 5.9727542E-4, 6.1641693E-4}, {1.4275268E-3, 1.5138625E-3, 1.4572752E-3, 1.5007428E-3, 1.7599082E-3}, {4.3472961e-2, 4.6729510E-2, 4.3908931e-2, 4.4626982E-2, 5.4736038E-2}}; const double aht[] = {2.53E-5, 5.49E-3, 1.14E-3}; /* height correction */ double y, cosy, ah[3], aw[3], dm, el = azel[1], lat = pos[0] * R2D, hgt = pos[2]; int i; if (el <= 0.0) { if (mapfw) { *mapfw = 0.0; } return 0.0; } /* year from doy 28, added half a year for southern latitudes */ y = (time2doy(time) - 28.0) / 365.25 + (lat < 0.0 ? 0.5 : 0.0); cosy = cos(2.0 * PI * y); lat = fabs(lat); for (i = 0; i < 3; i++) { ah[i] = interpc(coef[i], lat) - interpc(coef[i + 3], lat) * cosy; aw[i] = interpc(coef[i + 6], lat); } /* ellipsoidal height is used instead of height above sea level */ dm = (1.0 / sin(el) - mapf(el, aht[0], aht[1], aht[2])) * hgt / 1e3; if (mapfw) { *mapfw = mapf(el, aw[0], aw[1], aw[2]); } return mapf(el, ah[0], ah[1], ah[2]) + dm; } #endif /* !IERS_MODEL */ /* troposphere mapping function ------------------------------------------------ * compute tropospheric mapping function by NMF * args : gtime_t t I time * double *pos I receiver position {lat,lon,h} (rad,m) * double *azel I azimuth/elevation angle {az,el} (rad) * double *mapfw IO wet mapping function (NULL: not output) * return : dry mapping function * note : see ref [5] (NMF) and [9] (GMF) * original JGR paper of [5] has bugs in eq.(4) and (5). the corrected * paper is obtained from: * ftp://web.haystack.edu/pub/aen/nmf/NMF_JGR.pdf *-----------------------------------------------------------------------------*/ double tropmapf(gtime_t time, const double pos[], const double azel[], double *mapfw) { #ifdef IERS_MODEL const double ep[] = {2000, 1, 1, 12, 0, 0}; double mjd, lat, lon, hgt, zd, gmfh, gmfw; #endif trace(4, "tropmapf: pos=%10.6f %11.6f %6.1f azel=%5.1f %4.1f\n", pos[0] * R2D, pos[1] * R2D, pos[2], azel[0] * R2D, azel[1] * R2D); if (pos[2] < -1000.0 || pos[2] > 20000.0) { if (mapfw) { *mapfw = 0.0; } return 0.0; } #ifdef IERS_MODEL mjd = 51544.5 + (timediff(time, epoch2time(ep))) / 86400.0; lat = pos[0]; lon = pos[1]; hgt = pos[2] - geoidh(pos); /* height in m (mean sea level) */ zd = PI / 2.0 - azel[1]; /* call GMF */ gmf_(&mjd, &lat, &lon, &hgt, &zd, &gmfh, &gmfw); if (mapfw) *mapfw = gmfw; return gmfh; #else return nmf(time, pos, azel, mapfw); /* NMF */ #endif } /* interpolate antenna phase center variation --------------------------------*/ double interpvar(double ang, const double *var) { double a = ang / 5.0; /* ang=0-90 */ int i = static_cast(a); if (i < 0) { return var[0]; } if (i >= 18) { return var[18]; } return var[i] * (1.0 - a + i) + var[i + 1] * (a - i); } /* receiver antenna model ------------------------------------------------------ * compute antenna offset by antenna phase center parameters * args : pcv_t *pcv I antenna phase center parameters * double *azel I azimuth/elevation for receiver {az,el} (rad) * int opt I option (0:only offset,1:offset+pcv) * double *dant O range offsets for each frequency (m) * return : none * notes : current version does not support azimuth dependent terms *-----------------------------------------------------------------------------*/ void antmodel(const pcv_t *pcv, const double *del, const double *azel, int opt, double *dant) { double e[3], off[3], cosel = cos(azel[1]); int i, j; trace(4, "antmodel: azel=%6.1f %4.1f opt=%d\n", azel[0] * R2D, azel[1] * R2D, opt); e[0] = sin(azel[0]) * cosel; e[1] = cos(azel[0]) * cosel; e[2] = sin(azel[1]); for (i = 0; i < NFREQ; i++) { for (j = 0; j < 3; j++) { off[j] = pcv->off[i][j] + del[j]; } dant[i] = -dot(off, e, 3) + (opt ? interpvar(90.0 - azel[1] * R2D, pcv->var[i]) : 0.0); } trace(5, "antmodel: dant=%6.3f %6.3f\n", dant[0], dant[1]); } /* satellite antenna model ------------------------------------------------------ * compute satellite antenna phase center parameters * args : pcv_t *pcv I antenna phase center parameters * double nadir I nadir angle for satellite (rad) * double *dant O range offsets for each frequency (m) * return : none *-----------------------------------------------------------------------------*/ void antmodel_s(const pcv_t *pcv, double nadir, double *dant) { int i; trace(4, "antmodel_s: nadir=%6.1f\n", nadir * R2D); for (i = 0; i < NFREQ; i++) { dant[i] = interpvar(nadir * R2D * 5.0, pcv->var[i]); } trace(5, "antmodel_s: dant=%6.3f %6.3f\n", dant[0], dant[1]); } /* sun and moon position in eci (ref [4] 5.1.1, 5.2.1) -----------------------*/ void sunmoonpos_eci(gtime_t tut, double *rsun, double *rmoon) { const double ep2000[] = {2000, 1, 1, 12, 0, 0}; double t, f[5], eps, Ms, ls, rs, lm, pm, rm, sine, cose, sinp, cosp, sinl, cosl; trace(4, "sunmoonpos_eci: tut=%s\n", time_str(tut, 3)); t = timediff(tut, epoch2time(ep2000)) / 86400.0 / 36525.0; /* astronomical arguments */ ast_args(t, f); /* obliquity of the ecliptic */ eps = 23.439291 - 0.0130042 * t; sine = sin(eps * D2R); cose = cos(eps * D2R); /* sun position in eci */ if (rsun) { Ms = 357.5277233 + 35999.05034 * t; ls = 280.460 + 36000.770 * t + 1.914666471 * sin(Ms * D2R) + 0.019994643 * sin(2.0 * Ms * D2R); rs = AU * (1.000140612 - 0.016708617 * cos(Ms * D2R) - 0.000139589 * cos(2.0 * Ms * D2R)); sinl = sin(ls * D2R); cosl = cos(ls * D2R); rsun[0] = rs * cosl; rsun[1] = rs * cose * sinl; rsun[2] = rs * sine * sinl; trace(5, "rsun =%.3f %.3f %.3f\n", rsun[0], rsun[1], rsun[2]); } /* moon position in eci */ if (rmoon) { lm = 218.32 + 481267.883 * t + 6.29 * sin(f[0]) - 1.27 * sin(f[0] - 2.0 * f[3]) + 0.66 * sin(2.0 * f[3]) + 0.21 * sin(2.0 * f[0]) - 0.19 * sin(f[1]) - 0.11 * sin(2.0 * f[2]); pm = 5.13 * sin(f[2]) + 0.28 * sin(f[0] + f[2]) - 0.28 * sin(f[2] - f[0]) - 0.17 * sin(f[2] - 2.0 * f[3]); rm = RE_WGS84 / sin((0.9508 + 0.0518 * cos(f[0]) + 0.0095 * cos(f[0] - 2.0 * f[3]) + 0.0078 * cos(2.0 * f[3]) + 0.0028 * cos(2.0 * f[0])) * D2R); sinl = sin(lm * D2R); cosl = cos(lm * D2R); sinp = sin(pm * D2R); cosp = cos(pm * D2R); rmoon[0] = rm * cosp * cosl; rmoon[1] = rm * (cose * cosp * sinl - sine * sinp); rmoon[2] = rm * (sine * cosp * sinl + cose * sinp); trace(5, "rmoon=%.3f %.3f %.3f\n", rmoon[0], rmoon[1], rmoon[2]); } } /* sun and moon position ------------------------------------------------------- * get sun and moon position in ecef * args : gtime_t tut I time in ut1 * double *erpv I erp value {xp,yp,ut1_utc,lod} (rad,rad,s,s/d) * double *rsun IO sun position in ecef (m) (NULL: not output) * double *rmoon IO moon position in ecef (m) (NULL: not output) * double *gmst O gmst (rad) * return : none *-----------------------------------------------------------------------------*/ void sunmoonpos(gtime_t tutc, const double *erpv, double *rsun, double *rmoon, double *gmst) { gtime_t tut; double rs[3], rm[3], U[9], gmst_; trace(4, "sunmoonpos: tutc=%s\n", time_str(tutc, 3)); tut = timeadd(tutc, erpv[2]); /* utc -> ut1 */ /* sun and moon position in eci */ sunmoonpos_eci(tut, rsun ? rs : nullptr, rmoon ? rm : nullptr); /* eci to ecef transformation matrix */ eci2ecef(tutc, erpv, U, &gmst_); /* sun and moon position in ecef */ if (rsun) { matmul("NN", 3, 1, 3, 1.0, U, rs, 0.0, rsun); } if (rmoon) { matmul("NN", 3, 1, 3, 1.0, U, rm, 0.0, rmoon); } if (gmst) { *gmst = gmst_; } } /* carrier smoothing ----------------------------------------------------------- * carrier smoothing by Hatch filter * args : obs_t *obs IO raw observation data/smoothed observation data * int ns I smoothing window size (epochs) * return : none *-----------------------------------------------------------------------------*/ void csmooth(obs_t *obs, int ns) { double Ps[2][MAXSAT][NFREQ] = {}, Lp[2][MAXSAT][NFREQ] = {}, dcp; int i, j, s, r, n[2][MAXSAT][NFREQ] = {}; obsd_t *p; trace(3, "csmooth: nobs=%d,ns=%d\n", obs->n, ns); for (i = 0; i < obs->n; i++) { p = &obs->data[i]; s = p->sat; r = p->rcv; for (j = 0; j < NFREQ; j++) { if (s <= 0 || MAXSAT < s || r <= 0 || 2 < r) { continue; } if (p->P[j] == 0.0 || p->L[j] == 0.0) { continue; } if (p->LLI[j]) { n[r - 1][s - 1][j] = 0; } if (n[r - 1][s - 1][j] == 0) { Ps[r - 1][s - 1][j] = p->P[j]; } else { dcp = LAM_CARR[j] * (p->L[j] - Lp[r - 1][s - 1][j]); Ps[r - 1][s - 1][j] = p->P[j] / ns + (Ps[r - 1][s - 1][j] + dcp) * (ns - 1) / ns; } if (++n[r - 1][s - 1][j] < ns) { p->P[j] = 0.0; } else { p->P[j] = Ps[r - 1][s - 1][j]; } Lp[r - 1][s - 1][j] = p->L[j]; } } } /* uncompress file ------------------------------------------------------------- * uncompress (uncompress/unzip/uncompact hatanaka-compression/tar) file * args : char *file I input file * char *uncfile O uncompressed file * return : status (-1:error,0:not compressed file,1:uncompress completed) * note : creates uncompressed file in tempolary directory * gzip and crx2rnx commands have to be installed in commands path *-----------------------------------------------------------------------------*/ int rtk_uncompress(const char *file, char *uncfile) { int stat = 0; char *p, cmd[2048] = "", tmpfile[1024] = "", buff[1024], *fname, *dir = (char *)""; trace(3, "rtk_uncompress: file=%s\n", file); if (strlen(file) < 1025) { strcpy(tmpfile, file); } else { trace(1, "file array is too long"); } if (!(p = strrchr(tmpfile, '.'))) { return 0; } /* uncompress by gzip */ if (!strcmp(p, ".z") || !strcmp(p, ".Z") || !strcmp(p, ".gz") || !strcmp(p, ".GZ") || !strcmp(p, ".zip") || !strcmp(p, ".ZIP")) { strcpy(uncfile, tmpfile); uncfile[p - tmpfile] = '\0'; sprintf(cmd, R"(gzip -f -d -c "%s" > "%s")", tmpfile, uncfile); if (execcmd(cmd)) { if (remove(uncfile) != 0) { trace(1, "Error removing file"); } return -1; } if (strlen(uncfile) < 1025) { strcpy(tmpfile, uncfile); } stat = 1; } /* extract tar file */ if ((p = strrchr(tmpfile, '.')) && !strcmp(p, ".tar")) { strcpy(uncfile, tmpfile); uncfile[p - tmpfile] = '\0'; strcpy(buff, tmpfile); fname = buff; if ((p = strrchr(buff, '/'))) { *p = '\0'; dir = fname; fname = p + 1; } // sprintf(cmd, "tar -C \"%s\" -xf \"%s\"", dir, tmpfile); // NOTE: This sprintf triggers a format overflow warning. Replaced by: std::ostringstream temp; std::string s_aux1(dir); std::string s_aux2(tmpfile); temp << "tar -C " << s_aux1 << " -xf " << s_aux2; std::string s_aux = temp.str(); int n = s_aux.length(); if (n < 2048) { for (int i = 0; i < n; i++) { cmd[i] = s_aux[i]; } } if (execcmd(cmd)) { if (stat) { if (remove(tmpfile) != 0) { trace(1, "Error removing file"); } } return -1; } if (stat) { if (remove(tmpfile) != 0) { trace(1, "Error removing file"); } } stat = 1; } /* extract hatanaka-compressed file by cnx2rnx */ else if ((p = strrchr(tmpfile, '.')) && strlen(p) > 3 && (*(p + 3) == 'd' || *(p + 3) == 'D')) { strcpy(uncfile, tmpfile); uncfile[p - tmpfile + 3] = *(p + 3) == 'D' ? 'O' : 'o'; sprintf(cmd, R"(crx2rnx < "%s" > "%s")", tmpfile, uncfile); if (execcmd(cmd)) { if (remove(uncfile) != 0) { trace(1, "Error removing file"); } if (stat) { if (remove(tmpfile) != 0) { trace(1, "Error removing file"); } } return -1; } if (stat) { if (remove(tmpfile) != 0) { trace(1, "Error removing file"); } } stat = 1; } trace(3, "rtk_uncompress: stat=%d\n", stat); return stat; } /* expand file path ------------------------------------------------------------ * expand file path with wild-card (*) in file * args : char *path I file path to expand (captal insensitive) * char *paths O expanded file paths * int nmax I max number of expanded file paths * return : number of expanded file paths * notes : the order of expanded files is alphabetical order *-----------------------------------------------------------------------------*/ int expath(const char *path, char *paths[], int nmax) { int i, j, n = 0; char tmp[1024] = ""; struct dirent *d; DIR *dp; const char *file = path; char dir[1024] = "", s1[1024], s2[1024], *p, *q, *r; trace(3, "expath : path=%s nmax=%d\n", path, nmax); //TODO: Fix invalid conversion from ‘const char*’ to ‘char*’ //if ((p=strrchr(path,'/')) || (p=strrchr(path,'\\'))) { // file=p+1; strncpy(dir,path,p-path+1); dir[p-path+1]='\0'; //} if (!(dp = opendir(*dir ? dir : "."))) { return 0; } while ((d = readdir(dp))) { if (*(d->d_name) == '.') { continue; } sprintf(s1, "^%s$", d->d_name); sprintf(s2, "^%s$", file); for (p = s1; *p; p++) { *p = static_cast(tolower(static_cast(*p))); } for (p = s2; *p; p++) { *p = static_cast(tolower(static_cast(*p))); } for (p = s1, q = strtok_r(s2, "*", &r); q; q = strtok_r(nullptr, "*", &r)) { if ((p = strstr(p, q))) { p += strlen(q); } else { break; } } if (p && n < nmax) { sprintf(paths[n++], "%s%s", dir, d->d_name); } } closedir(dp); /* sort paths in alphabetical order */ for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (strcmp(paths[i], paths[j]) > 0) { if (strlen(paths[i]) < 1025) { strcpy(tmp, paths[i]); } else { trace(1, "Path is too long"); } strcpy(paths[i], paths[j]); strcpy(paths[j], tmp); } } } for (i = 0; i < n; i++) { trace(3, "expath : file=%s\n", paths[i]); } return n; } /* From RTKLIB 2.4.2 */ void windupcorr(gtime_t time, const double *rs, const double *rr, double *phw) { double ek[3], exs[3], eys[3], ezs[3], ess[3], exr[3], eyr[3], eks[3], ekr[3], E[9]; double dr[3], ds[3], drs[3], r[3], pos[3], rsun[3], cosp, ph, erpv[5] = {0}; int i; trace(4, "windupcorr: time=%s\n", time_str(time, 0)); /* sun position in ecef */ sunmoonpos(gpst2utc(time), erpv, rsun, nullptr, nullptr); /* unit vector satellite to receiver */ for (i = 0; i < 3; i++) { r[i] = rr[i] - rs[i]; } if (!normv3(r, ek)) { return; } /* unit vectors of satellite antenna */ for (i = 0; i < 3; i++) { r[i] = -rs[i]; } if (!normv3(r, ezs)) { return; } for (i = 0; i < 3; i++) { r[i] = rsun[i] - rs[i]; } if (!normv3(r, ess)) { return; } cross3(ezs, ess, r); if (!normv3(r, eys)) { return; } cross3(eys, ezs, exs); /* unit vectors of receiver antenna */ ecef2pos(rr, pos); xyz2enu(pos, E); exr[0] = E[1]; exr[1] = E[4]; exr[2] = E[7]; /* x = north */ eyr[0] = -E[0]; eyr[1] = -E[3]; eyr[2] = -E[6]; /* y = west */ /* phase windup effect */ cross3(ek, eys, eks); cross3(ek, eyr, ekr); for (i = 0; i < 3; i++) { ds[i] = exs[i] - ek[i] * dot(ek, exs, 3) - eks[i]; dr[i] = exr[i] - ek[i] * dot(ek, exr, 3) + ekr[i]; } cosp = dot(ds, dr, 3) / norm_rtk(ds, 3) / norm_rtk(dr, 3); if (cosp < -1.0) { cosp = -1.0; } else if (cosp > 1.0) { cosp = 1.0; } ph = acos(cosp) / 2.0 / PI; cross3(ds, dr, drs); if (dot(ek, drs, 3) < 0.0) { ph = -ph; } *phw = ph + floor(*phw - ph + 0.5); /* in cycle */ } src/algorithms/libs/rtklib/rtklib_rtkcmn.h000066400000000000000000000316261352176506000212710ustar00rootroot00000000000000/*! * \file rtklib_rtkcmn.h * \brief rtklib common functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * References : * [1] IS-GPS-200D, Navstar GPS Space Segment/Navigation User Interfaces, * 7 March, 2006 * [2] RTCA/DO-229C, Minimum operational performanc standards for global * positioning system/wide area augmentation system airborne equipment, * RTCA inc, November 28, 2001 * [3] M.Rothacher, R.Schmid, ANTEX: The Antenna Exchange Format Version 1.4, * 15 September, 2010 * [4] A.Gelb ed., Applied Optimal Estimation, The M.I.T Press, 1974 * [5] A.E.Niell, Global mapping functions for the atmosphere delay at radio * wavelengths, Jounal of geophysical research, 1996 * [6] W.Gurtner and L.Estey, RINEX The Receiver Independent Exchange Format * Version 3.00, November 28, 2007 * [7] J.Kouba, A Guide to using International GNSS Service (IGS) products, * May 2009 * [8] China Satellite Navigation Office, BeiDou navigation satellite system * signal in space interface control document, open service signal B1I * (version 1.0), Dec 2012 * [9] J.Boehm, A.Niell, P.Tregoning and H.Shuh, Global Mapping Function * (GMF): A new empirical mapping function base on numerical weather * model data, Geophysical Research Letters, 33, L07304, 2006 * [10] GLONASS/GPS/Galileo/Compass/SBAS NV08C receiver series BINR interface * protocol specification ver.1.3, August, 2012 * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RTKCMN_H_ #define GNSS_SDR_RTKLIB_RTKCMN_H_ #include "rtklib.h" /* coordinate rotation matrix ------------------------------------------------*/ #define Rx(t, X) \ do \ { \ (X)[0] = 1.0; \ (X)[1] = (X)[2] = (X)[3] = (X)[6] = 0.0; \ (X)[4] = (X)[8] = cos(t); \ (X)[7] = sin(t); \ (X)[5] = -(X)[7]; \ } \ while (0) #define Ry(t, X) \ do \ { \ (X)[4] = 1.0; \ (X)[1] = (X)[3] = (X)[5] = (X)[7] = 0.0; \ (X)[0] = (X)[8] = cos(t); \ (X)[2] = sin(t); \ (X)[6] = -(X)[2]; \ } \ while (0) #define Rz(t, X) \ do \ { \ (X)[8] = 1.0; \ (X)[2] = (X)[5] = (X)[6] = (X)[7] = 0.0; \ (X)[0] = (X)[4] = cos(t); \ (X)[3] = sin(t); \ (X)[1] = -(X)[3]; \ } \ while (0) void fatalerr(const char *format, ...); int satno(int sys, int prn); int satsys(int sat, int *prn); int satid2no(const char *id); void satno2id(int sat, char *id); int satexclude(int sat, int svh, const prcopt_t *opt); int testsnr(int base, int freq, double el, double snr, const snrmask_t *mask); unsigned char obs2code(const char *obs, int *freq); char *code2obs(unsigned char code, int *freq); void setcodepri(int sys, int freq, const char *pri); int getcodepri(int sys, unsigned char code, const char *opt); unsigned int getbitu(const unsigned char *buff, int pos, int len); int getbits(const unsigned char *buff, int pos, int len); void setbitu(unsigned char *buff, int pos, int len, unsigned int data); void setbits(unsigned char *buff, int pos, int len, int data); unsigned int rtk_crc32(const unsigned char *buff, int len); unsigned int rtk_crc24q(const unsigned char *buff, int len); unsigned short rtk_crc16(const unsigned char *buff, int len); int decode_word(unsigned int word, unsigned char *data); double *mat(int n, int m); int *imat(int n, int m); double *zeros(int n, int m); double *eye(int n); double dot(const double *a, const double *b, int n); double norm_rtk(const double *a, int n); void cross3(const double *a, const double *b, double *c); int normv3(const double *a, double *b); void matcpy(double *A, const double *B, int n, int m); void matmul(const char *tr, int n, int k, int m, double alpha, const double *A, const double *B, double beta, double *C); int matinv(double *A, int n); int solve(const char *tr, const double *A, const double *Y, int n, int m, double *X); int lsq(const double *A, const double *y, int n, int m, double *x, double *Q); int filter_(const double *x, const double *P, const double *H, const double *v, const double *R, int n, int m, double *xp, double *Pp); int filter(double *x, double *P, const double *H, const double *v, const double *R, int n, int m); int smoother(const double *xf, const double *Qf, const double *xb, const double *Qb, int n, double *xs, double *Qs); void matfprint(const double A[], int n, int m, int p, int q, FILE *fp); void matsprint(const double A[], int n, int m, int p, int q, std::string &buffer); void matprint(const double A[], int n, int m, int p, int q); double str2num(const char *s, int i, int n); int str2time(const char *s, int i, int n, gtime_t *t); gtime_t epoch2time(const double *ep); void time2epoch(gtime_t t, double *ep); gtime_t gpst2time(int week, double sec); double time2gpst(gtime_t t, int *week); gtime_t gst2time(int week, double sec); double time2gst(gtime_t t, int *week); gtime_t bdt2time(int week, double sec); double time2bdt(gtime_t t, int *week); gtime_t timeadd(gtime_t t, double sec); double timediff(gtime_t t1, gtime_t t2); double timediffweekcrossover(gtime_t t1, gtime_t t2); gtime_t timeget(void); void timeset(gtime_t t); int read_leaps_text(FILE *fp); int read_leaps_usno(FILE *fp); int read_leaps(const char *file); gtime_t gpst2utc(gtime_t t); gtime_t utc2gpst(gtime_t t); gtime_t gpst2bdt(gtime_t t); gtime_t bdt2gpst(gtime_t t); double time2sec(gtime_t time, gtime_t *day); double utc2gmst(gtime_t t, double ut1_utc); void time2str(gtime_t t, char *s, int n); char *time_str(gtime_t t, int n); double time2doy(gtime_t t); int adjgpsweek(int week); unsigned int tickget(void); void sleepms(int ms); void deg2dms(double deg, double *dms, int ndec); void deg2dms(double deg, double *dms); double dms2deg(const double *dms); void ecef2pos(const double *r, double *pos); void pos2ecef(const double *pos, double *r); void xyz2enu(const double *pos, double *E); void ecef2enu(const double *pos, const double *r, double *e); void enu2ecef(const double *pos, const double *e, double *r); void covenu(const double *pos, const double *P, double *Q); void covecef(const double *pos, const double *Q, double *P); void ast_args(double t, double *f); void nut_iau1980(double t, const double *f, double *dpsi, double *deps); void eci2ecef(gtime_t tutc, const double *erpv, double *U, double *gmst); int decodef(char *p, int n, double *v); void addpcv(const pcv_t *pcv, pcvs_t *pcvs); int readngspcv(const char *file, pcvs_t *pcvs); int readantex(const char *file, pcvs_t *pcvs); int readpcv(const char *file, pcvs_t *pcvs); pcv_t *searchpcv(int sat, const char *type, gtime_t time, const pcvs_t *pcvs); void readpos(const char *file, const char *rcv, double *pos); int readblqrecord(FILE *fp, double *odisp); int readblq(const char *file, const char *sta, double *odisp); int readerp(const char *file, erp_t *erp); int geterp(const erp_t *erp, gtime_t time, double *erpv); int cmpeph(const void *p1, const void *p2); void uniqeph(nav_t *nav); int cmpgeph(const void *p1, const void *p2); void uniqgeph(nav_t *nav); int cmpseph(const void *p1, const void *p2); void uniqseph(nav_t *nav); void uniqnav(nav_t *nav); int cmpobs(const void *p1, const void *p2); int sortobs(obs_t *obs); int screent(gtime_t time, gtime_t ts, gtime_t te, double tint); int readnav(const char *file, nav_t *nav); int savenav(const char *file, const nav_t *nav); void freeobs(obs_t *obs); void freenav(nav_t *nav, int opt); void traceopen(const char *file); void traceclose(void); void tracelevel(int level); void traceswap(void); void trace(int level, const char *format, ...); void tracet(int level, const char *format, ...); void tracemat(int level, const double *A, int n, int m, int p, int q); void traceobs(int level, const obsd_t *obs, int n); //void tracenav(int level, const nav_t *nav); //void tracegnav(int level, const nav_t *nav); //void tracehnav(int level, const nav_t *nav); //void tracepeph(int level, const nav_t *nav); //void tracepclk(int level, const nav_t *nav); //void traceb (int level, const unsigned char *p, int n); int execcmd(const char *cmd); void createdir(const char *path); int repstr(char *str, const char *pat, const char *rep); int reppath(const char *path, char *rpath, gtime_t time, const char *rov, const char *base); int reppaths(const char *path, char *rpath[], int nmax, gtime_t ts, gtime_t te, const char *rov, const char *base); double satwavelen(int sat, int frq, const nav_t *nav); double geodist(const double *rs, const double *rr, double *e); double satazel(const double *pos, const double *e, double *azel); //#define SQRT(x) ((x)<0.0?0.0:sqrt(x)) void dops(int ns, const double *azel, double elmin, double *dop); double ionmodel(gtime_t t, const double *ion, const double *pos, const double *azel); double ionmapf(const double *pos, const double *azel); double ionppp(const double *pos, const double *azel, double re, double hion, double *posp); double tropmodel(gtime_t time, const double *pos, const double *azel, double humi); double interpc(const double coef[], double lat); double mapf(double el, double a, double b, double c); double nmf(gtime_t time, const double pos[], const double azel[], double *mapfw); double tropmapf(gtime_t time, const double pos[], const double azel[], double *mapfw); double interpvar(double ang, const double *var); void antmodel(const pcv_t *pcv, const double *del, const double *azel, int opt, double *dant); void antmodel_s(const pcv_t *pcv, double nadir, double *dant); void sunmoonpos_eci(gtime_t tut, double *rsun, double *rmoon); void sunmoonpos(gtime_t tutc, const double *erpv, double *rsun, double *rmoon, double *gmst); void csmooth(obs_t *obs, int ns); int rtk_uncompress(const char *file, char *uncfile); int expath(const char *path, char *paths[], int nmax); void windupcorr(gtime_t time, const double *rs, const double *rr, double *phw); #endif /* GNSS_SDR_RTKLIB_RTKCMN_H_ */ src/algorithms/libs/rtklib/rtklib_rtkpos.cc000066400000000000000000002751401352176506000214540ustar00rootroot00000000000000/*! * \file rtklib_rtkpos.cc * \brief rtklib ppp-related functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_rtkpos.h" #include "rtklib_ephemeris.h" #include "rtklib_lambda.h" #include "rtklib_pntpos.h" #include "rtklib_ppp.h" #include "rtklib_tides.h" #include #include static int resamb_WLNL(rtk_t *rtk __attribute((unused)), const obsd_t *obs __attribute((unused)), const int *sat __attribute((unused)), const int *iu __attribute((unused)), const int *ir __attribute((unused)), int ns __attribute__((unused)), const nav_t *nav __attribute((unused)), const double *azel __attribute((unused))) { return 0; } static int resamb_TCAR(rtk_t *rtk __attribute((unused)), const obsd_t *obs __attribute((unused)), const int *sat __attribute((unused)), const int *iu __attribute((unused)), const int *ir __attribute((unused)), int ns __attribute((unused)), const nav_t *nav __attribute((unused)), const double *azel __attribute((unused))) { return 0; } /* global variables ----------------------------------------------------------*/ static int statlevel = 0; /* rtk status output level (0:off) */ static FILE *fp_stat = nullptr; /* rtk status file pointer */ static char file_stat[1024] = ""; /* rtk status file original path */ static gtime_t time_stat = {0, 0}; /* rtk status file time */ /* open solution status file --------------------------------------------------- * open solution status file and set output level * args : char *file I rtk status file * int level I rtk status level (0: off) * return : status (1:ok,0:error) * notes : file can constain time keywords (%Y,%y,%m...) defined in reppath(). * The time to replace keywords is based on UTC of CPU time. * output : solution status file record format * * $POS,week,tow,stat,posx,posy,posz,posxf,posyf,poszf * week/tow : gps week no/time of week (s) * stat : solution status * posx/posy/posz : position x/y/z ecef (m) float * posxf/posyf/poszf : position x/y/z ecef (m) fixed * * $VELACC,week,tow,stat,vele,veln,velu,acce,accn,accu,velef,velnf,veluf,accef,accnf,accuf * week/tow : gps week no/time of week (s) * stat : solution status * vele/veln/velu : velocity e/n/u (m/s) float * acce/accn/accu : acceleration e/n/u (m/s^2) float * velef/velnf/veluf : velocity e/n/u (m/s) fixed * accef/accnf/accuf : acceleration e/n/u (m/s^2) fixed * * $CLK,week,tow,stat,clk1,clk2,clk3,clk4 * week/tow : gps week no/time of week (s) * stat : solution status * clk1 : receiver clock bias GPS (ns) * clk2 : receiver clock bias GLO-GPS (ns) * clk3 : receiver clock bias GAL-GPS (ns) * clk4 : receiver clock bias BDS-GPS (ns) * * $ION,week,tow,stat,sat,az,el,ion,ion-fixed * week/tow : gps week no/time of week (s) * stat : solution status * sat : satellite id * az/el : azimuth/elevation angle(deg) * ion : vertical ionospheric delay L1 (m) float * ion-fixed: vertical ionospheric delay L1 (m) fixed * * $TROP,week,tow,stat,rcv,ztd,ztdf * week/tow : gps week no/time of week (s) * stat : solution status * rcv : receiver (1:rover,2:base station) * ztd : zenith total delay (m) float * ztdf : zenith total delay (m) fixed * * $HWBIAS,week,tow,stat,frq,bias,biasf * week/tow : gps week no/time of week (s) * stat : solution status * frq : frequency (1:L1,2:L2,...) * bias : h/w bias coefficient (m/MHz) float * biasf : h/w bias coefficient (m/MHz) fixed * * $SAT,week,tow,sat,frq,az,el,resp,resc,vsat,snr,fix,slip,lock,outc,slipc,rejc * week/tow : gps week no/time of week (s) * sat/frq : satellite id/frequency (1:L1,2:L2,...) * az/el : azimuth/elevation angle (deg) * resp : pseudorange residual (m) * resc : carrier-phase residual (m) * vsat : valid data flag (0:invalid,1:valid) * snr : signal strength (dbHz) * fix : ambiguity flag (0:no data,1:float,2:fixed,3:hold,4:ppp) * slip : cycle-slip flag (bit1:slip,bit2:parity unknown) * lock : carrier-lock count * outc : data outage count * slipc : cycle-slip count * rejc : data reject (outlier) count * *-----------------------------------------------------------------------------*/ int rtkopenstat(const char *file, int level) { gtime_t time = utc2gpst(timeget()); char path[1024]; trace(3, "rtkopenstat: file=%s level=%d\n", file, level); if (level <= 0) { return 0; } reppath(file, path, time, "", ""); if (!(fp_stat = fopen(path, "we"))) { trace(1, "rtkopenstat: file open error path=%s\n", path); return 0; } if (strlen(file) < 1025) { strcpy(file_stat, file); } else { trace(1, "File name is too long"); } time_stat = time; statlevel = level; return 1; } /* close solution status file -------------------------------------------------- * close solution status file * args : none * return : none *-----------------------------------------------------------------------------*/ void rtkclosestat(void) { trace(3, "rtkclosestat:\n"); if (fp_stat) { fclose(fp_stat); } fp_stat = nullptr; file_stat[0] = '\0'; statlevel = 0; } /* write solution status to buffer -------------------------------------------*/ void rtkoutstat(rtk_t *rtk, char *buff __attribute__((unused))) { ssat_t *ssat; double tow, pos[3], vel[3], acc[3], vela[3] = {0}, acca[3] = {0}, xa[3]; int i, j, week, est, nfreq, nf = NF_RTK(&rtk->opt); char id[32]; if (statlevel <= 0 || !fp_stat) { return; } trace(3, "outsolstat:\n"); /* swap solution status file */ swapsolstat(); est = rtk->opt.mode >= PMODE_DGPS; nfreq = est ? nf : 1; tow = time2gpst(rtk->sol.time, &week); /* receiver position */ if (est) { for (i = 0; i < 3; i++) { xa[i] = i < rtk->na ? rtk->xa[i] : 0.0; } fprintf(fp_stat, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->x[0], rtk->x[1], rtk->x[2], xa[0], xa[1], xa[2]); } else { fprintf(fp_stat, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->sol.rr[0], rtk->sol.rr[1], rtk->sol.rr[2], 0.0, 0.0, 0.0); } /* receiver velocity and acceleration */ if (est && rtk->opt.dynamics) { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->x + 3, vel); ecef2enu(pos, rtk->x + 6, acc); if (rtk->na >= 6) { ecef2enu(pos, rtk->xa + 3, vela); } if (rtk->na >= 9) { ecef2enu(pos, rtk->xa + 6, acca); } fprintf(fp_stat, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], acc[0], acc[1], acc[2], vela[0], vela[1], vela[2], acca[0], acca[1], acca[2]); } else { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->sol.rr + 3, vel); fprintf(fp_stat, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } /* receiver clocks */ fprintf(fp_stat, "$CLK,%d,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f\n", week, tow, rtk->sol.stat, 1, rtk->sol.dtr[0] * 1E9, rtk->sol.dtr[1] * 1E9, rtk->sol.dtr[2] * 1E9, rtk->sol.dtr[3] * 1E9); /* ionospheric parameters */ if (est && rtk->opt.ionoopt == IONOOPT_EST) { for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) { continue; } satno2id(i + 1, id); j = II_RTK(i + 1, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$ION,%d,%.3f,%d,%s,%.1f,%.1f,%.4f,%.4f\n", week, tow, rtk->sol.stat, id, ssat->azel[0] * R2D, ssat->azel[1] * R2D, rtk->x[j], xa[0]); } } /* tropospheric parameters */ if (est && (rtk->opt.tropopt == TROPOPT_EST || rtk->opt.tropopt == TROPOPT_ESTG)) { for (i = 0; i < 2; i++) { j = IT_RTK(i, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$TROP,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, i + 1, rtk->x[j], xa[0]); } } /* receiver h/w bias */ if (est && rtk->opt.glomodear == 2) { for (i = 0; i < nfreq; i++) { j = IL_RTK(i, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$HWBIAS,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, i + 1, rtk->x[j], xa[0]); } } if (rtk->sol.stat == SOLQ_NONE || statlevel <= 1) { return; } /* residuals and status */ for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) { continue; } satno2id(i + 1, id); for (j = 0; j < nfreq; j++) { fprintf(fp_stat, "$SAT,%d,%.3f,%s,%d,%.1f,%.1f,%.4f,%.4f,%d,%.0f,%d,%d,%d,%d,%d,%d\n", week, tow, id, j + 1, ssat->azel[0] * R2D, ssat->azel[1] * R2D, ssat->resp[j], ssat->resc[j], ssat->vsat[j], ssat->snr[j] * 0.25, ssat->fix[j], ssat->slip[j] & 3, ssat->lock[j], ssat->outc[j], ssat->slipc[j], ssat->rejc[j]); } } } /* swap solution status file -------------------------------------------------*/ void swapsolstat(void) { gtime_t time = utc2gpst(timeget()); char path[1024]; if (static_cast(time2gpst(time, nullptr) / INT_SWAP_STAT) == static_cast(time2gpst(time_stat, nullptr) / INT_SWAP_STAT)) { return; } time_stat = time; if (!reppath(file_stat, path, time, "", "")) { return; } if (fp_stat) { fclose(fp_stat); } if (!(fp_stat = fopen(path, "we"))) { trace(2, "swapsolstat: file open error path=%s\n", path); return; } trace(3, "swapsolstat: path=%s\n", path); } /* output solution status ----------------------------------------------------*/ void outsolstat(rtk_t *rtk) { ssat_t *ssat; double tow, pos[3], vel[3], acc[3], vela[3] = {0}, acca[3] = {0}, xa[3]; int i, j, week, est, nfreq, nf = NF_RTK(&rtk->opt); char id[32]; if (statlevel <= 0 || !fp_stat) { return; } trace(3, "outsolstat:\n"); /* swap solution status file */ swapsolstat(); est = rtk->opt.mode >= PMODE_DGPS; nfreq = est ? nf : 1; tow = time2gpst(rtk->sol.time, &week); /* receiver position */ if (est) { for (i = 0; i < 3; i++) { xa[i] = i < rtk->na ? rtk->xa[i] : 0.0; } fprintf(fp_stat, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->x[0], rtk->x[1], rtk->x[2], xa[0], xa[1], xa[2]); } else { fprintf(fp_stat, "$POS,%d,%.3f,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n", week, tow, rtk->sol.stat, rtk->sol.rr[0], rtk->sol.rr[1], rtk->sol.rr[2], 0.0, 0.0, 0.0); } /* receiver velocity and acceleration */ if (est && rtk->opt.dynamics) { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->x + 3, vel); ecef2enu(pos, rtk->x + 6, acc); if (rtk->na >= 6) { ecef2enu(pos, rtk->xa + 3, vela); } if (rtk->na >= 9) { ecef2enu(pos, rtk->xa + 6, acca); } fprintf(fp_stat, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], acc[0], acc[1], acc[2], vela[0], vela[1], vela[2], acca[0], acca[1], acca[2]); } else { ecef2pos(rtk->sol.rr, pos); ecef2enu(pos, rtk->sol.rr + 3, vel); fprintf(fp_stat, "$VELACC,%d,%.3f,%d,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f,%.4f,%.4f,%.4f,%.5f,%.5f,%.5f\n", week, tow, rtk->sol.stat, vel[0], vel[1], vel[2], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } /* receiver clocks */ fprintf(fp_stat, "$CLK,%d,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f\n", week, tow, rtk->sol.stat, 1, rtk->sol.dtr[0] * 1E9, rtk->sol.dtr[1] * 1E9, rtk->sol.dtr[2] * 1E9, rtk->sol.dtr[3] * 1E9); /* ionospheric parameters */ if (est && rtk->opt.ionoopt == IONOOPT_EST) { for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) { continue; } satno2id(i + 1, id); j = II_RTK(i + 1, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$ION,%d,%.3f,%d,%s,%.1f,%.1f,%.4f,%.4f\n", week, tow, rtk->sol.stat, id, ssat->azel[0] * R2D, ssat->azel[1] * R2D, rtk->x[j], xa[0]); } } /* tropospheric parameters */ if (est && (rtk->opt.tropopt == TROPOPT_EST || rtk->opt.tropopt == TROPOPT_ESTG)) { for (i = 0; i < 2; i++) { j = IT_RTK(i, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$TROP,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, i + 1, rtk->x[j], xa[0]); } } /* receiver h/w bias */ if (est && rtk->opt.glomodear == 2) { for (i = 0; i < nfreq; i++) { j = IL_RTK(i, &rtk->opt); xa[0] = j < rtk->na ? rtk->xa[j] : 0.0; fprintf(fp_stat, "$HWBIAS,%d,%.3f,%d,%d,%.4f,%.4f\n", week, tow, rtk->sol.stat, i + 1, rtk->x[j], xa[0]); } } if (rtk->sol.stat == SOLQ_NONE || statlevel <= 1) { return; } /* residuals and status */ for (i = 0; i < MAXSAT; i++) { ssat = rtk->ssat + i; if (!ssat->vs) { continue; } satno2id(i + 1, id); for (j = 0; j < nfreq; j++) { fprintf(fp_stat, "$SAT,%d,%.3f,%s,%d,%.1f,%.1f,%.4f,%.4f,%d,%.0f,%d,%d,%d,%d,%d,%d\n", week, tow, id, j + 1, ssat->azel[0] * R2D, ssat->azel[1] * R2D, ssat->resp[j], ssat->resc[j], ssat->vsat[j], ssat->snr[j] * 0.25, ssat->fix[j], ssat->slip[j] & 3, ssat->lock[j], ssat->outc[j], ssat->slipc[j], ssat->rejc[j]); } } } /* save error message --------------------------------------------------------*/ void errmsg(rtk_t *rtk, const char *format, ...) { char buff[256], tstr[32]; int n; va_list ap; time2str(rtk->sol.time, tstr, 2); n = sprintf(buff, "%s: ", tstr + 11); va_start(ap, format); n += vsprintf(buff + n, format, ap); va_end(ap); n = n < MAXERRMSG - rtk->neb ? n : MAXERRMSG - rtk->neb; memcpy(rtk->errbuf + rtk->neb, buff, n); rtk->neb += n; trace(2, "%s", buff); } /* single-differenced observable ---------------------------------------------*/ double sdobs(const obsd_t *obs, int i, int j, int f) { double pi = f < NFREQ ? obs[i].L[f] : obs[i].P[f - NFREQ]; double pj = f < NFREQ ? obs[j].L[f] : obs[j].P[f - NFREQ]; return pi == 0.0 || pj == 0.0 ? 0.0 : pi - pj; } /* single-differenced geometry-free linear combination of phase --------------*/ double gfobs_L1L2(const obsd_t *obs, int i, int j, const double *lam) { double pi = sdobs(obs, i, j, 0) * lam[0], pj = sdobs(obs, i, j, 1) * lam[1]; return pi == 0.0 || pj == 0.0 ? 0.0 : pi - pj; } double gfobs_L1L5(const obsd_t *obs, int i, int j, const double *lam) { double pi = sdobs(obs, i, j, 0) * lam[0], pj = sdobs(obs, i, j, 2) * lam[2]; return pi == 0.0 || pj == 0.0 ? 0.0 : pi - pj; } /* single-differenced measurement error variance -----------------------------*/ double varerr(int sat __attribute((unused)), int sys, double el, double bl, double dt, int f, const prcopt_t *opt) { double a, b, c = opt->err[3] * bl / 1e4, d = SPEED_OF_LIGHT * opt->sclkstab * dt, fact = 1.0; double sinel = sin(el); int i = sys == SYS_GLO ? 1 : (sys == SYS_GAL ? 2 : 0), nf = NF_RTK(opt); /* extended error model */ if (f >= nf && opt->exterr.ena[0]) { /* code */ a = opt->exterr.cerr[i][(f - nf) * 2]; b = opt->exterr.cerr[i][1 + (f - nf) * 2]; if (sys == SYS_SBS) { a *= EFACT_SBS; b *= EFACT_SBS; } } else if (f < nf && opt->exterr.ena[1]) { /* phase */ a = opt->exterr.perr[i][f * 2]; b = opt->exterr.perr[i][1 + f * 2]; if (sys == SYS_SBS) { a *= EFACT_SBS; b *= EFACT_SBS; } } else { /* normal error model */ if (f >= nf) { fact = opt->eratio[f - nf]; } if (fact <= 0.0) { fact = opt->eratio[0]; } fact *= sys == SYS_GLO ? EFACT_GLO : (sys == SYS_SBS ? EFACT_SBS : EFACT_GPS); a = fact * opt->err[1]; b = fact * opt->err[2]; } return 2.0 * (opt->ionoopt == IONOOPT_IFLC ? 3.0 : 1.0) * (a * a + b * b / sinel / sinel + c * c) + d * d; } /* baseline length -----------------------------------------------------------*/ double baseline(const double *ru, const double *rb, double *dr) { int i; for (i = 0; i < 3; i++) { dr[i] = ru[i] - rb[i]; } return norm_rtk(dr, 3); } /* initialize state and covariance -------------------------------------------*/ void initx_rtk(rtk_t *rtk, double xi, double var, int i) { int j; rtk->x[i] = xi; for (j = 0; j < rtk->nx; j++) { rtk->P[i + j * rtk->nx] = rtk->P[j + i * rtk->nx] = i == j ? var : 0.0; } } /* select common satellites between rover and reference station --------------*/ int selsat(const obsd_t *obs, const double *azel, int nu, int nr, const prcopt_t *opt, int *sat, int *iu, int *ir) { int i, j, k = 0; trace(3, "selsat : nu=%d nr=%d\n", nu, nr); for (i = 0, j = nu; i < nu && j < nu + nr; i++, j++) { if (obs[i].sat < obs[j].sat) { j--; } else if (obs[i].sat > obs[j].sat) { i--; } else if (azel[1 + j * 2] >= opt->elmin) { /* elevation at base station */ sat[k] = obs[i].sat; iu[k] = i; ir[k++] = j; trace(4, "(%2d) sat=%3d iu=%2d ir=%2d\n", k - 1, obs[i].sat, i, j); } } return k; } /* temporal update of position/velocity/acceleration -------------------------*/ void udpos(rtk_t *rtk, double tt) { double *F, *FP, *xp, pos[3], Q[9] = {0}, Qv[9], var = 0.0; int i, j; trace(3, "udpos : tt=%.3f\n", tt); /* fixed mode */ if (rtk->opt.mode == PMODE_FIXED) { for (i = 0; i < 3; i++) { initx_rtk(rtk, rtk->opt.ru[i], 1E-8, i); } return; } /* initialize position for first epoch */ if (norm_rtk(rtk->x, 3) <= 0.0) { for (i = 0; i < 3; i++) { initx_rtk(rtk, rtk->sol.rr[i], VAR_POS, i); } if (rtk->opt.dynamics) { for (i = 3; i < 6; i++) { initx_rtk(rtk, rtk->sol.rr[i], VAR_VEL, i); } for (i = 6; i < 9; i++) { initx_rtk(rtk, 1E-6, VAR_ACC, i); } } } /* static mode */ if (rtk->opt.mode == PMODE_STATIC) { return; } /* kinmatic mode without dynamics */ if (!rtk->opt.dynamics) { for (i = 0; i < 3; i++) { initx_rtk(rtk, rtk->sol.rr[i], VAR_POS, i); } return; } /* check variance of estimated position */ for (i = 0; i < 3; i++) { var += rtk->P[i + i * rtk->nx]; } var /= 3.0; if (var > VAR_POS) { /* reset position with large variance */ for (i = 0; i < 3; i++) { initx_rtk(rtk, rtk->sol.rr[i], VAR_POS, i); } for (i = 3; i < 6; i++) { initx_rtk(rtk, rtk->sol.rr[i], VAR_VEL, i); } for (i = 6; i < 9; i++) { initx_rtk(rtk, 1E-6, VAR_ACC, i); } trace(2, "reset rtk position due to large variance: var=%.3f\n", var); return; } /* state transition of position/velocity/acceleration */ F = eye(rtk->nx); FP = mat(rtk->nx, rtk->nx); xp = mat(rtk->nx, 1); for (i = 0; i < 6; i++) { F[i + (i + 3) * rtk->nx] = tt; } /* x=F*x, P=F*P*F+Q */ matmul("NN", rtk->nx, 1, rtk->nx, 1.0, F, rtk->x, 0.0, xp); matcpy(rtk->x, xp, rtk->nx, 1); matmul("NN", rtk->nx, rtk->nx, rtk->nx, 1.0, F, rtk->P, 0.0, FP); matmul("NT", rtk->nx, rtk->nx, rtk->nx, 1.0, FP, F, 0.0, rtk->P); /* process noise added to only acceleration */ Q[0] = Q[4] = std::pow(rtk->opt.prn[3], 2.0); Q[8] = std::pow(rtk->opt.prn[4], 2.0); ecef2pos(rtk->x, pos); covecef(pos, Q, Qv); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { rtk->P[i + 6 + (j + 6) * rtk->nx] += Qv[i + j * 3]; } } free(F); free(FP); free(xp); } /* temporal update of ionospheric parameters ---------------------------------*/ void udion(rtk_t *rtk, double tt, double bl, const int *sat, int ns) { double el, fact; int i, j; trace(3, "udion : tt=%.1f bl=%.0f ns=%d\n", tt, bl, ns); for (i = 1; i <= MAXSAT; i++) { j = II_RTK(i, &rtk->opt); if (rtk->x[j] != 0.0 && rtk->ssat[i - 1].outc[0] > GAP_RESION && rtk->ssat[i - 1].outc[1] > GAP_RESION) { rtk->x[j] = 0.0; } } for (i = 0; i < ns; i++) { j = II_RTK(sat[i], &rtk->opt); if (rtk->x[j] == 0.0) { initx_rtk(rtk, 1E-6, std::pow(rtk->opt.std[1] * bl / 1e4, 2.0), j); } else { /* elevation dependent factor of process noise */ el = rtk->ssat[sat[i] - 1].azel[1]; fact = cos(el); rtk->P[j + j * rtk->nx] += std::pow(rtk->opt.prn[1] * bl / 1e4 * fact, 2.0) * tt; } } } /* temporal update of tropospheric parameters --------------------------------*/ void udtrop(rtk_t *rtk, double tt, double bl __attribute((unused))) { int i, j, k; trace(3, "udtrop : tt=%.1f\n", tt); for (i = 0; i < 2; i++) { j = IT_RTK(i, &rtk->opt); if (rtk->x[j] == 0.0) { initx_rtk(rtk, INIT_ZWD, std::pow(rtk->opt.std[2], 2.0), j); /* initial zwd */ if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (k = 0; k < 2; k++) { initx_rtk(rtk, 1e-6, VAR_GRA, ++j); } } } else { rtk->P[j + j * rtk->nx] += std::pow(rtk->opt.prn[2], 2.0) * tt; if (rtk->opt.tropopt >= TROPOPT_ESTG) { for (k = 0; k < 2; k++) { rtk->P[++j * (1 + rtk->nx)] += std::pow(rtk->opt.prn[2] * 0.3, 2.0) * fabs(rtk->tt); } } } } } /* temporal update of receiver h/w biases ------------------------------------*/ void udrcvbias(rtk_t *rtk, double tt) { int i, j; trace(3, "udrcvbias: tt=%.1f\n", tt); for (i = 0; i < NFREQGLO; i++) { j = IL_RTK(i, &rtk->opt); if (rtk->x[j] == 0.0) { initx_rtk(rtk, 1E-6, VAR_HWBIAS, j); } /* hold to fixed solution */ else if (rtk->nfix >= rtk->opt.minfix && rtk->sol.ratio > rtk->opt.thresar[0]) { initx_rtk(rtk, rtk->xa[j], rtk->Pa[j + j * rtk->na], j); } else { rtk->P[j + j * rtk->nx] += std::pow(PRN_HWBIAS, 2.0) * tt; } } } /* detect cycle slip by LLI --------------------------------------------------*/ void detslp_ll(rtk_t *rtk, const obsd_t *obs, int i, int rcv) { unsigned int slip, LLI; int f, sat = obs[i].sat; trace(3, "detslp_ll: i=%d rcv=%d\n", i, rcv); for (f = 0; f < rtk->opt.nf; f++) { if (obs[i].L[f] == 0.0) { continue; } /* restore previous LLI */ if (rcv == 1) { LLI = getbitu(&rtk->ssat[sat - 1].slip[f], 0, 2); /* rover */ } else { LLI = getbitu(&rtk->ssat[sat - 1].slip[f], 2, 2); /* base */ } /* detect slip by cycle slip flag in LLI */ if (rtk->tt >= 0.0) { /* forward */ if (obs[i].LLI[f] & 1) { errmsg(rtk, "slip detected forward (sat=%2d rcv=%d F=%d LLI=%x)\n", sat, rcv, f + 1, obs[i].LLI[f]); } slip = obs[i].LLI[f]; } else { /* backward */ if (LLI & 1) { errmsg(rtk, "slip detected backward (sat=%2d rcv=%d F=%d LLI=%x)\n", sat, rcv, f + 1, LLI); } slip = LLI; } /* detect slip by parity unknown flag transition in LLI */ if (((LLI & 2) && !(obs[i].LLI[f] & 2)) || (!(LLI & 2) && (obs[i].LLI[f] & 2))) { errmsg(rtk, "slip detected half-cyc (sat=%2d rcv=%d F=%d LLI=%x->%x)\n", sat, rcv, f + 1, LLI, obs[i].LLI[f]); slip |= 1; } /* save current LLI */ if (rcv == 1) { setbitu(&rtk->ssat[sat - 1].slip[f], 0, 2, obs[i].LLI[f]); } else { setbitu(&rtk->ssat[sat - 1].slip[f], 2, 2, obs[i].LLI[f]); } /* save slip and half-cycle valid flag */ rtk->ssat[sat - 1].slip[f] |= static_cast(slip); rtk->ssat[sat - 1].half[f] = (obs[i].LLI[f] & 2) ? 0 : 1; } } /* detect cycle slip by L1-L2 geometry free phase jump -----------------------*/ void detslp_gf_L1L2(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav) { int sat = obs[i].sat; double g0, g1; trace(3, "detslp_gf_L1L2: i=%d j=%d\n", i, j); if (rtk->opt.nf <= 1 || (g1 = gfobs_L1L2(obs, i, j, nav->lam[sat - 1])) == 0.0) { return; } g0 = rtk->ssat[sat - 1].gf; rtk->ssat[sat - 1].gf = g1; if (g0 != 0.0 && fabs(g1 - g0) > rtk->opt.thresslip) { rtk->ssat[sat - 1].slip[0] |= 1; rtk->ssat[sat - 1].slip[1] |= 1; errmsg(rtk, "slip detected (sat=%2d GF_L1_L2=%.3f %.3f)\n", sat, g0, g1); } } /* detect cycle slip by L1-L5 geometry free phase jump -----------------------*/ void detslp_gf_L1L5(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav) { int sat = obs[i].sat; double g0, g1; trace(3, "detslp_gf_L1L5: i=%d j=%d\n", i, j); if (rtk->opt.nf <= 2 || (g1 = gfobs_L1L5(obs, i, j, nav->lam[sat - 1])) == 0.0) { return; } g0 = rtk->ssat[sat - 1].gf2; rtk->ssat[sat - 1].gf2 = g1; if (g0 != 0.0 && fabs(g1 - g0) > rtk->opt.thresslip) { rtk->ssat[sat - 1].slip[0] |= 1; rtk->ssat[sat - 1].slip[2] |= 1; errmsg(rtk, "slip detected (sat=%2d GF_L1_L5=%.3f %.3f)\n", sat, g0, g1); } } /* detect cycle slip by doppler and phase difference -------------------------*/ void detslp_dop(rtk_t *rtk __attribute__((unused)), const obsd_t *obs __attribute__((unused)), int i __attribute__((unused)), int rcv __attribute__((unused)), const nav_t *nav __attribute__((unused))) { /* detection with doppler disabled because of clock-jump issue (v.2.3.0) */ #if 0 int f,sat = obs[i].sat; double tt,dph,dpt,lam,thres; trace(3,"detslp_dop: i=%d rcv=%d\n",i,rcv); for (f = 0;fopt.nf;f++) { if (obs[i].L[f] == 0.0 || obs[i].D[f] == 0.0 || rtk->ph[rcv-1][sat-1][f] == 0.0) { continue; } if (fabs(tt = timediff(obs[i].time,rtk->pt[rcv-1][sat-1][f]))lam[sat-1][f])<=0.0) continue; /* cycle slip threshold (cycle) */ thres = MAXACC*tt*tt/2.0/lam+rtk->opt.err[4]*fabs(tt)*4.0; /* phase difference and doppler x time (cycle) */ dph = obs[i].L[f]-rtk->ph[rcv-1][sat-1][f]; dpt = -obs[i].D[f]*tt; if (fabs(dph-dpt)<=thres) continue; rtk->slip[sat-1][f]| = 1; errmsg(rtk,"slip detected (sat=%2d rcv=%d L%d=%.3f %.3f thres=%.3f)\n", sat,rcv,f+1,dph,dpt,thres); } #endif } /* temporal update of phase biases -------------------------------------------*/ void udbias(rtk_t *rtk, double tt, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav) { double cp, pr, cp1, cp2, pr1, pr2, *bias, offset, lami, lam1, lam2, C1, C2; int i, j, f, slip, reset, nf = NF_RTK(&rtk->opt); trace(3, "udbias : tt=%.1f ns=%d\n", tt, ns); for (i = 0; i < ns; i++) { /* detect cycle slip by LLI */ for (f = 0; f < rtk->opt.nf; f++) { rtk->ssat[sat[i] - 1].slip[f] &= 0xFC; } detslp_ll(rtk, obs, iu[i], 1); detslp_ll(rtk, obs, ir[i], 2); /* detect cycle slip by geometry-free phase jump */ detslp_gf_L1L2(rtk, obs, iu[i], ir[i], nav); detslp_gf_L1L5(rtk, obs, iu[i], ir[i], nav); /* detect cycle slip by doppler and phase difference */ detslp_dop(rtk, obs, iu[i], 1, nav); detslp_dop(rtk, obs, ir[i], 2, nav); /* update half-cycle valid flag */ for (f = 0; f < nf; f++) { rtk->ssat[sat[i] - 1].half[f] = !((obs[iu[i]].LLI[f] & 2) || (obs[ir[i]].LLI[f] & 2)); } } for (f = 0; f < nf; f++) { /* reset phase-bias if instantaneous AR or expire obs outage counter */ for (i = 1; i <= MAXSAT; i++) { reset = ++rtk->ssat[i - 1].outc[f] > static_cast(rtk->opt.maxout); if (rtk->opt.modear == ARMODE_INST && rtk->x[IB_RTK(i, f, &rtk->opt)] != 0.0) { initx_rtk(rtk, 0.0, 0.0, IB_RTK(i, f, &rtk->opt)); } else if (reset && rtk->x[IB_RTK(i, f, &rtk->opt)] != 0.0) { initx_rtk(rtk, 0.0, 0.0, IB_RTK(i, f, &rtk->opt)); trace(3, "udbias : obs outage counter overflow (sat=%3d L%d n=%d)\n", i, f + 1, rtk->ssat[i - 1].outc[f]); } if (rtk->opt.modear != ARMODE_INST && reset) { rtk->ssat[i - 1].lock[f] = -rtk->opt.minlock; } } /* reset phase-bias if detecting cycle slip */ for (i = 0; i < ns; i++) { j = IB_RTK(sat[i], f, &rtk->opt); rtk->P[j + j * rtk->nx] += rtk->opt.prn[0] * rtk->opt.prn[0] * tt; slip = rtk->ssat[sat[i] - 1].slip[f]; if (rtk->opt.ionoopt == IONOOPT_IFLC) { slip |= rtk->ssat[sat[i] - 1].slip[1]; } if (rtk->opt.modear == ARMODE_INST || !(slip & 1)) { continue; } rtk->x[j] = 0.0; rtk->ssat[sat[i] - 1].lock[f] = -rtk->opt.minlock; } bias = zeros(ns, 1); /* estimate approximate phase-bias by phase - code */ for (i = j = 0, offset = 0.0; i < ns; i++) { if (rtk->opt.ionoopt != IONOOPT_IFLC) { cp = sdobs(obs, iu[i], ir[i], f); /* cycle */ pr = sdobs(obs, iu[i], ir[i], f + NFREQ); lami = nav->lam[sat[i] - 1][f]; if (cp == 0.0 || pr == 0.0 || lami <= 0.0) { continue; } bias[i] = cp - pr / lami; } else { cp1 = sdobs(obs, iu[i], ir[i], 0); cp2 = sdobs(obs, iu[i], ir[i], 1); pr1 = sdobs(obs, iu[i], ir[i], NFREQ); pr2 = sdobs(obs, iu[i], ir[i], NFREQ + 1); lam1 = nav->lam[sat[i] - 1][0]; lam2 = nav->lam[sat[i] - 1][1]; if (cp1 == 0.0 || cp2 == 0.0 || pr1 == 0.0 || pr2 == 0.0 || lam1 <= 0.0 || lam2 <= 0.0) { continue; } C1 = std::pow(lam2, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); C2 = -std::pow(lam1, 2.0) / (std::pow(lam2, 2.0) - std::pow(lam1, 2.0)); bias[i] = (C1 * lam1 * cp1 + C2 * lam2 * cp2) - (C1 * pr1 + C2 * pr2); } if (rtk->x[IB_RTK(sat[i], f, &rtk->opt)] != 0.0) { offset += bias[i] - rtk->x[IB_RTK(sat[i], f, &rtk->opt)]; j++; } } /* correct phase-bias offset to enssure phase-code coherency */ if (j > 0) { for (i = 1; i <= MAXSAT; i++) { if (rtk->x[IB_RTK(i, f, &rtk->opt)] != 0.0) { rtk->x[IB_RTK(i, f, &rtk->opt)] += offset / j; } } } /* set initial states of phase-bias */ for (i = 0; i < ns; i++) { if (bias[i] == 0.0 || rtk->x[IB_RTK(sat[i], f, &rtk->opt)] != 0.0) { continue; } initx_rtk(rtk, bias[i], std::pow(rtk->opt.std[0], 2.0), IB_RTK(sat[i], f, &rtk->opt)); } free(bias); } } /* temporal update of states --------------------------------------------------*/ void udstate(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav) { double tt = fabs(rtk->tt), bl = 0.0, dr[3]; trace(3, "udstate : ns=%d\n", ns); /* temporal update of position/velocity/acceleration */ udpos(rtk, tt); /* temporal update of ionospheric parameters */ if (rtk->opt.ionoopt >= IONOOPT_EST) { bl = baseline(rtk->x, rtk->rb, dr); udion(rtk, tt, bl, sat, ns); } /* temporal update of tropospheric parameters */ if (rtk->opt.tropopt >= TROPOPT_EST) { udtrop(rtk, tt, bl); } /* temporal update of eceiver h/w bias */ if (rtk->opt.glomodear == 2 && (rtk->opt.navsys & SYS_GLO)) { udrcvbias(rtk, tt); } /* temporal update of phase-bias */ if (rtk->opt.mode > PMODE_DGPS) { udbias(rtk, tt, obs, sat, iu, ir, ns, nav); } } /* undifferenced phase/code residual for satellite ---------------------------*/ void zdres_sat(int base, double r, const obsd_t *obs, const nav_t *nav, const double *azel, const double *dant, const prcopt_t *opt, double *y) { const double *lam = nav->lam[obs->sat - 1]; double f1, f2, C1, C2, dant_if; int i, nf = NF_RTK(opt); if (opt->ionoopt == IONOOPT_IFLC) { /* iono-free linear combination */ if (lam[0] == 0.0 || lam[1] == 0.0) { return; } if (testsnr(base, 0, azel[1], obs->SNR[0] * 0.25, &opt->snrmask) || testsnr(base, 1, azel[1], obs->SNR[1] * 0.25, &opt->snrmask)) { return; } f1 = SPEED_OF_LIGHT / lam[0]; f2 = SPEED_OF_LIGHT / lam[1]; C1 = std::pow(f1, 2.0) / (std::pow(f1, 2.0) - std::pow(f2, 2.0)); C2 = -std::pow(f2, 2.0) / (std::pow(f1, 2.0) - std::pow(f2, 2.0)); dant_if = C1 * dant[0] + C2 * dant[1]; if (obs->L[0] != 0.0 && obs->L[1] != 0.0) { y[0] = C1 * obs->L[0] * lam[0] + C2 * obs->L[1] * lam[1] - r - dant_if; } if (obs->P[0] != 0.0 && obs->P[1] != 0.0) { y[1] = C1 * obs->P[0] + C2 * obs->P[1] - r - dant_if; } } else { for (i = 0; i < nf; i++) { if (lam[i] == 0.0) { continue; } /* check snr mask */ if (testsnr(base, i, azel[1], obs->SNR[i] * 0.25, &opt->snrmask)) { continue; } /* residuals = observable - pseudorange */ if (obs->L[i] != 0.0) { y[i] = obs->L[i] * lam[i] - r - dant[i]; } if (obs->P[i] != 0.0) { y[i + nf] = obs->P[i] - r - dant[i]; } } } } /* undifferenced phase/code residuals ----------------------------------------*/ int zdres(int base, const obsd_t *obs, int n, const double *rs, const double *dts, const int *svh, const nav_t *nav, const double *rr, const prcopt_t *opt, int index, double *y, double *e, double *azel) { double r, rr_[3], pos[3], dant[NFREQ] = {0}, disp[3]; double zhd, zazel[] = {0.0, 90.0 * D2R}; int i, nf = NF_RTK(opt); trace(3, "zdres : n=%d\n", n); for (i = 0; i < n * nf * 2; i++) { y[i] = 0.0; } if (norm_rtk(rr, 3) <= 0.0) { return 0; /* no receiver position */ } for (i = 0; i < 3; i++) { rr_[i] = rr[i]; } /* earth tide correction */ if (opt->tidecorr) { tidedisp(gpst2utc(obs[0].time), rr_, opt->tidecorr, &nav->erp, opt->odisp[base], disp); for (i = 0; i < 3; i++) { rr_[i] += disp[i]; } } ecef2pos(rr_, pos); for (i = 0; i < n; i++) { /* compute geometric-range and azimuth/elevation angle */ if ((r = geodist(rs + i * 6, rr_, e + i * 3)) <= 0.0) { continue; } if (satazel(pos, e + i * 3, azel + i * 2) < opt->elmin) { continue; } /* excluded satellite? */ if (satexclude(obs[i].sat, svh[i], opt)) { continue; } /* satellite clock-bias */ r += -SPEED_OF_LIGHT * dts[i * 2]; /* troposphere delay model (hydrostatic) */ zhd = tropmodel(obs[0].time, pos, zazel, 0.0); r += tropmapf(obs[i].time, pos, azel + i * 2, nullptr) * zhd; /* receiver antenna phase center correction */ antmodel(opt->pcvr + index, opt->antdel[index], azel + i * 2, opt->posopt[1], dant); /* undifferenced phase/code residual for satellite */ zdres_sat(base, r, obs + i, nav, azel + i * 2, dant, opt, y + i * nf * 2); } trace(4, "rr_=%.3f %.3f %.3f\n", rr_[0], rr_[1], rr_[2]); trace(4, "pos=%.9f %.9f %.3f\n", pos[0] * R2D, pos[1] * R2D, pos[2]); for (i = 0; i < n; i++) { trace(4, "sat=%2d %13.3f %13.3f %13.3f %13.10f %6.1f %5.1f\n", obs[i].sat, rs[i * 6], rs[1 + i * 6], rs[2 + i * 6], dts[i * 2], azel[i * 2] * R2D, azel[1 + i * 2] * R2D); } trace(4, "y=\n"); tracemat(4, y, nf * 2, n, 13, 3); return 1; } /* test valid observation data -----------------------------------------------*/ int validobs(int i, int j, int f, int nf, const double *y) { /* if no phase observable, psudorange is also unusable */ return y[f + i * nf * 2] != 0.0 && y[f + j * nf * 2] != 0.0 && (f < nf || (y[f - nf + i * nf * 2] != 0.0 && y[f - nf + j * nf * 2] != 0.0)); } /* double-differenced measurement error covariance ---------------------------*/ void ddcov(const int *nb, int n, const double *Ri, const double *Rj, int nv, double *R) { int i, j, k = 0, b; trace(3, "ddcov : n=%d\n", n); for (i = 0; i < nv * nv; i++) { R[i] = 0.0; } for (b = 0; b < n; k += nb[b++]) { for (i = 0; i < nb[b]; i++) { for (j = 0; j < nb[b]; j++) { R[k + i + (k + j) * nv] = Ri[k + i] + (i == j ? Rj[k + i] : 0.0); } } } trace(5, "R=\n"); tracemat(5, R, nv, nv, 8, 6); } /* baseline length constraint ------------------------------------------------*/ int constbl(rtk_t *rtk, const double *x, const double *P, double *v, double *H, double *Ri, double *Rj, int index) { const double thres = 0.1; /* threshold for nonliearity (v.2.3.0) */ double xb[3], b[3], bb, var = 0.0; int i; trace(3, "constbl : \n"); /* no constraint */ if (rtk->opt.baseline[0] <= 0.0) { return 0; } /* time-adjusted baseline vector and length */ for (i = 0; i < 3; i++) { xb[i] = rtk->rb[i] + rtk->rb[i + 3] * rtk->sol.age; b[i] = x[i] - xb[i]; } bb = norm_rtk(b, 3); /* approximate variance of solution */ if (P) { for (i = 0; i < 3; i++) { var += P[i + i * rtk->nx]; } var /= 3.0; } /* check nonlinearity */ if (var > thres * thres * bb * bb) { trace(3, "constbl : equation nonlinear (bb=%.3f var=%.3f)\n", bb, var); return 0; } /* constraint to baseline length */ v[index] = rtk->opt.baseline[0] - bb; if (H) { for (i = 0; i < 3; i++) { H[i + index * rtk->nx] = b[i] / bb; } } Ri[index] = 0.0; Rj[index] = std::pow(rtk->opt.baseline[1], 2.0); trace(4, "baseline len v=%13.3f R=%8.6f %8.6f\n", v[index], Ri[index], Rj[index]); return 1; } /* precise tropspheric model -------------------------------------------------*/ double prectrop(gtime_t time, const double *pos, int r, const double *azel, const prcopt_t *opt, const double *x, double *dtdx) { double m_w = 0.0, cotz, grad_n, grad_e; int i = IT_RTK(r, opt); /* wet mapping function */ tropmapf(time, pos, azel, &m_w); if (opt->tropopt >= TROPOPT_ESTG && azel[1] > 0.0) { /* m_w=m_0+m_0*cot(el)*(Gn*cos(az)+Ge*sin(az)): ref [6] */ cotz = 1.0 / tan(azel[1]); grad_n = m_w * cotz * cos(azel[0]); grad_e = m_w * cotz * sin(azel[0]); m_w += grad_n * x[i + 1] + grad_e * x[i + 2]; dtdx[1] = grad_n * x[i]; dtdx[2] = grad_e * x[i]; } else { dtdx[1] = dtdx[2] = 0.0; } dtdx[0] = m_w; return m_w * x[i]; } /* glonass inter-channel bias correction -------------------------------------*/ double gloicbcorr(int sat1 __attribute((unused)), int sat2 __attribute((unused)), const prcopt_t *opt, double lam1, double lam2, int f) { double dfreq; if (f >= NFREQGLO || f >= opt->nf || !opt->exterr.ena[2]) { return 0.0; } dfreq = (SPEED_OF_LIGHT / lam1 - SPEED_OF_LIGHT / lam2) / (f == 0 ? DFRQ1_GLO : DFRQ2_GLO); return opt->exterr.gloicb[f] * 0.01 * dfreq; /* (m) */ } /* test navi system (m=0:gps/qzs/sbs,1:glo,2:gal,3:bds) ----------------------*/ int test_sys(int sys, int m) { switch (sys) { case SYS_GPS: return m == 0; case SYS_QZS: return m == 0; case SYS_SBS: return m == 0; case SYS_GLO: return m == 1; case SYS_GAL: return m == 2; case SYS_BDS: return m == 3; } return 0; } /* double-differenced phase/code residuals -----------------------------------*/ int ddres(rtk_t *rtk, const nav_t *nav, double dt, const double *x, const double *P, const int *sat, double *y, const double *e, double *azel, const int *iu, const int *ir, int ns, double *v, double *H, double *R, int *vflg) { prcopt_t *opt = &rtk->opt; double bl, dr[3], posu[3], posr[3], didxi = 0.0, didxj = 0.0, *im; double *tropr, *tropu, *dtdxr, *dtdxu, *Ri, *Rj, lami, lamj, fi, fj, df, *Hi = nullptr; int i, j, k, m, f, ff, nv = 0, nb[NFREQ * 4 * 2 + 2] = {0}, b = 0, sysi, sysj, nf = NF_RTK(opt); trace(3, "ddres : dt=%.1f nx=%d ns=%d\n", dt, rtk->nx, ns); bl = baseline(x, rtk->rb, dr); ecef2pos(x, posu); ecef2pos(rtk->rb, posr); Ri = mat(ns * nf * 2 + 2, 1); Rj = mat(ns * nf * 2 + 2, 1); im = mat(ns, 1); tropu = mat(ns, 1); tropr = mat(ns, 1); dtdxu = mat(ns, 3); dtdxr = mat(ns, 3); for (i = 0; i < MAXSAT; i++) { for (j = 0; j < NFREQ; j++) { rtk->ssat[i].resp[j] = rtk->ssat[i].resc[j] = 0.0; } } /* compute factors of ionospheric and tropospheric delay */ for (i = 0; i < ns; i++) { if (opt->ionoopt >= IONOOPT_EST) { im[i] = (ionmapf(posu, azel + iu[i] * 2) + ionmapf(posr, azel + ir[i] * 2)) / 2.0; } if (opt->tropopt >= TROPOPT_EST) { tropu[i] = prectrop(rtk->sol.time, posu, 0, azel + iu[i] * 2, opt, x, dtdxu + i * 3); tropr[i] = prectrop(rtk->sol.time, posr, 1, azel + ir[i] * 2, opt, x, dtdxr + i * 3); } } for (m = 0; m < 4; m++) { /* m=0:gps/qzs/sbs, 1:glo, 2:gal, 3:bds */ for (f = opt->mode > PMODE_DGPS ? 0 : nf; f < nf * 2; f++) { /* search reference satellite with highest elevation */ for (i = -1, j = 0; j < ns; j++) { sysi = rtk->ssat[sat[j] - 1].sys; if (!test_sys(sysi, m)) { continue; } if (!validobs(iu[j], ir[j], f, nf, y)) { continue; } if (i < 0 || azel[1 + iu[j] * 2] >= azel[1 + iu[i] * 2]) { i = j; } } if (i < 0) { continue; } /* make double difference */ for (j = 0; j < ns; j++) { if (i == j) { continue; } sysi = rtk->ssat[sat[i] - 1].sys; sysj = rtk->ssat[sat[j] - 1].sys; if (!test_sys(sysj, m)) { continue; } if (!validobs(iu[j], ir[j], f, nf, y)) { continue; } ff = f % nf; lami = nav->lam[sat[i] - 1][ff]; lamj = nav->lam[sat[j] - 1][ff]; if (lami <= 0.0 || lamj <= 0.0) { continue; } if (H) { Hi = H + nv * rtk->nx; for (k = 0; k < rtk->nx; k++) { Hi[k] = 0.0; } } /* double-differenced residual */ v[nv] = (y[f + iu[i] * nf * 2] - y[f + ir[i] * nf * 2]) - (y[f + iu[j] * nf * 2] - y[f + ir[j] * nf * 2]); /* partial derivatives by rover position */ if (H) { for (k = 0; k < 3; k++) { Hi[k] = -e[k + iu[i] * 3] + e[k + iu[j] * 3]; } } /* double-differenced ionospheric delay term */ if (opt->ionoopt == IONOOPT_EST) { fi = lami / LAM_CARR[0]; fj = lamj / LAM_CARR[0]; didxi = (f < nf ? -1.0 : 1.0) * fi * fi * im[i]; didxj = (f < nf ? -1.0 : 1.0) * fj * fj * im[j]; v[nv] -= didxi * x[II_RTK(sat[i], opt)] - didxj * x[II_RTK(sat[j], opt)]; if (H) { Hi[II_RTK(sat[i], opt)] = didxi; Hi[II_RTK(sat[j], opt)] = -didxj; } } /* double-differenced tropospheric delay term */ if (opt->tropopt == TROPOPT_EST || opt->tropopt == TROPOPT_ESTG) { v[nv] -= (tropu[i] - tropu[j]) - (tropr[i] - tropr[j]); for (k = 0; k < (opt->tropopt < TROPOPT_ESTG ? 1 : 3); k++) { if (!H) { continue; } Hi[IT_RTK(0, opt) + k] = (dtdxu[k + i * 3] - dtdxu[k + j * 3]); Hi[IT_RTK(1, opt) + k] = -(dtdxr[k + i * 3] - dtdxr[k + j * 3]); } } /* double-differenced phase-bias term */ if (f < nf) { if (opt->ionoopt != IONOOPT_IFLC) { v[nv] -= lami * x[IB_RTK(sat[i], f, opt)] - lamj * x[IB_RTK(sat[j], f, opt)]; if (H) { Hi[IB_RTK(sat[i], f, opt)] = lami; Hi[IB_RTK(sat[j], f, opt)] = -lamj; } } else { v[nv] -= x[IB_RTK(sat[i], f, opt)] - x[IB_RTK(sat[j], f, opt)]; if (H) { Hi[IB_RTK(sat[i], f, opt)] = 1.0; Hi[IB_RTK(sat[j], f, opt)] = -1.0; } } } /* glonass receiver h/w bias term */ if (rtk->opt.glomodear == 2 && sysi == SYS_GLO && sysj == SYS_GLO && ff < NFREQGLO) { df = (SPEED_OF_LIGHT / lami - SPEED_OF_LIGHT / lamj) / 1E6; /* freq-difference (MHz) */ v[nv] -= df * x[IL_RTK(ff, opt)]; if (H) { Hi[IL_RTK(ff, opt)] = df; } } /* glonass interchannel bias correction */ else if (sysi == SYS_GLO && sysj == SYS_GLO) { v[nv] -= gloicbcorr(sat[i], sat[j], &rtk->opt, lami, lamj, f); } if (f < nf) { rtk->ssat[sat[j] - 1].resc[f] = v[nv]; } else { rtk->ssat[sat[j] - 1].resp[f - nf] = v[nv]; } /* test innovation */ if (opt->maxinno > 0.0 && fabs(v[nv]) > opt->maxinno) { if (f < nf) { rtk->ssat[sat[i] - 1].rejc[f]++; rtk->ssat[sat[j] - 1].rejc[f]++; } errmsg(rtk, "outlier rejected (sat=%3d-%3d %s%d v=%.3f)\n", sat[i], sat[j], f < nf ? "L" : "P", f % nf + 1, v[nv]); continue; } /* single-differenced measurement error variances */ Ri[nv] = varerr(sat[i], sysi, azel[1 + iu[i] * 2], bl, dt, f, opt); Rj[nv] = varerr(sat[j], sysj, azel[1 + iu[j] * 2], bl, dt, f, opt); /* set valid data flags */ if (opt->mode > PMODE_DGPS) { if (f < nf) { rtk->ssat[sat[i] - 1].vsat[f] = rtk->ssat[sat[j] - 1].vsat[f] = 1; } } else { rtk->ssat[sat[i] - 1].vsat[f - nf] = rtk->ssat[sat[j] - 1].vsat[f - nf] = 1; } trace(4, "sat=%3d-%3d %s%d v=%13.3f R=%8.6f %8.6f\n", sat[i], sat[j], f < nf ? "L" : "P", f % nf + 1, v[nv], Ri[nv], Rj[nv]); vflg[nv++] = (sat[i] << 16) | (sat[j] << 8) | ((f < nf ? 0 : 1) << 4) | (f % nf); nb[b]++; } #if 0 /* residuals referenced to reference satellite (2.4.2 p11) */ /* restore single-differenced residuals assuming sum equal zero */ if (fssat[j].resc[f]; s/=nb[b]+1; for (j=0;jssat[j].resc[f]!=0.0) rtk->ssat[j].resc[f]-=s; } } else { for (j=0,s=0.0;jssat[j].resp[f-nf]; s/=nb[b]+1; for (j=0;jssat[j].resp[f-nf]!=0.0) rtk->ssat[j].resp[f-nf]-=s; } } #endif b++; } } /* end of system loop */ /* baseline length constraint for moving baseline */ if (opt->mode == PMODE_MOVEB && constbl(rtk, x, P, v, H, Ri, Rj, nv)) { vflg[nv++] = 3 << 4; nb[b++]++; } if (H) { trace(5, "H=\n"); tracemat(5, H, rtk->nx, nv, 7, 4); } /* double-differenced measurement error covariance */ ddcov(nb, b, Ri, Rj, nv, R); free(Ri); free(Rj); free(im); free(tropu); free(tropr); free(dtdxu); free(dtdxr); return nv; } /* time-interpolation of residuals (for post-mission) ------------------------*/ double intpres(gtime_t time, const obsd_t *obs, int n, const nav_t *nav, rtk_t *rtk, double *y) { static obsd_t obsb[MAXOBS]; static double yb[MAXOBS * NFREQ * 2], rs[MAXOBS * 6], dts[MAXOBS * 2], var[MAXOBS]; static double e[MAXOBS * 3], azel[MAXOBS * 2]; static int nb = 0, svh[MAXOBS * 2]; prcopt_t *opt = &rtk->opt; double tt = timediff(time, obs[0].time), ttb, *p, *q; int i, j, k, nf = NF_RTK(opt); trace(3, "intpres : n=%d tt=%.1f\n", n, tt); if (nb == 0 || fabs(tt) < DTTOL) { nb = n; for (i = 0; i < n; i++) { obsb[i] = obs[i]; } return tt; } ttb = timediff(time, obsb[0].time); if (fabs(ttb) > opt->maxtdiff * 2.0 || ttb == tt) { return tt; } satposs(time, obsb, nb, nav, opt->sateph, rs, dts, var, svh); if (!zdres(1, obsb, nb, rs, dts, svh, nav, rtk->rb, opt, 1, yb, e, azel)) { return tt; } for (i = 0; i < n; i++) { for (j = 0; j < nb; j++) { if (obsb[j].sat == obs[i].sat) { break; } } if (j >= nb) { continue; } for (k = 0, p = y + i * nf * 2, q = yb + j * nf * 2; k < nf * 2; k++, p++, q++) { if (*p == 0.0 || *q == 0.0) { *p = 0.0; } else { *p = (ttb * (*p) - tt * (*q)) / (ttb - tt); } } } return fabs(ttb) > fabs(tt) ? ttb : tt; } /* single to double-difference transformation matrix (D') --------------------*/ int ddmat(rtk_t *rtk, double *D) { int i, j, k, m, f, nb = 0, nx = rtk->nx, na = rtk->na, nf = NF_RTK(&rtk->opt), nofix; trace(3, "ddmat :\n"); for (i = 0; i < MAXSAT; i++) { for (j = 0; j < NFREQ; j++) { rtk->ssat[i].fix[j] = 0; } } for (i = 0; i < na; i++) { D[i + i * nx] = 1.0; } for (m = 0; m < 4; m++) { /* m=0:gps/qzs/sbs, 1:glo, 2:gal, 3:bds */ nofix = (m == 1 && rtk->opt.glomodear == 0) || (m == 3 && rtk->opt.bdsmodear == 0); for (f = 0, k = na; f < nf; f++, k += MAXSAT) { if (i < k + MAXSAT) { for (i = k; i < k + MAXSAT; i++) { if (rtk->x[i] == 0.0 || !test_sys(rtk->ssat[i - k].sys, m) || !rtk->ssat[i - k].vsat[f] || !rtk->ssat[i - k].half[f]) { continue; } if (rtk->ssat[i - k].lock[f] > 0 && !(rtk->ssat[i - k].slip[f] & 2) && rtk->ssat[i - k].azel[1] >= rtk->opt.elmaskar && !nofix) { rtk->ssat[i - k].fix[f] = 2; /* fix */ break; } rtk->ssat[i - k].fix[f] = 1; } for (j = k; j < k + MAXSAT; j++) { if (i == j || rtk->x[j] == 0.0 || !test_sys(rtk->ssat[j - k].sys, m) || !rtk->ssat[j - k].vsat[f]) { continue; } if (rtk->ssat[j - k].lock[f] > 0 && !(rtk->ssat[j - k].slip[f] & 2) && rtk->ssat[i - k].vsat[f] && rtk->ssat[j - k].azel[1] >= rtk->opt.elmaskar && !nofix) { D[i + (na + nb) * nx] = 1.0; D[j + (na + nb) * nx] = -1.0; nb++; rtk->ssat[j - k].fix[f] = 2; /* fix */ } else { rtk->ssat[j - k].fix[f] = 1; } } } } } trace(5, "D=\n"); tracemat(5, D, nx, na + nb, 2, 0); return nb; } /* restore single-differenced ambiguity --------------------------------------*/ void restamb(rtk_t *rtk, const double *bias, int nb __attribute((unused)), double *xa) { int i, n, m, f, index[MAXSAT], nv = 0, nf = NF_RTK(&rtk->opt); trace(3, "restamb :\n"); for (i = 0; i < rtk->nx; i++) { xa[i] = rtk->x[i]; } for (i = 0; i < rtk->na; i++) { xa[i] = rtk->xa[i]; } for (m = 0; m < 4; m++) { for (f = 0; f < nf; f++) { for (n = i = 0; i < MAXSAT; i++) { if (!test_sys(rtk->ssat[i].sys, m) || rtk->ssat[i].fix[f] != 2) { continue; } index[n++] = IB_RTK(i + 1, f, &rtk->opt); } if (n < 2) { continue; } xa[index[0]] = rtk->x[index[0]]; for (i = 1; i < n; i++) { xa[index[i]] = xa[index[0]] - bias[nv++]; } } } } /* hold integer ambiguity ----------------------------------------------------*/ void holdamb(rtk_t *rtk, const double *xa) { double *v, *H, *R; int i, n, m, f, info, index[MAXSAT], nb = rtk->nx - rtk->na, nv = 0, nf = NF_RTK(&rtk->opt); trace(3, "holdamb :\n"); v = mat(nb, 1); H = zeros(nb, rtk->nx); for (m = 0; m < 4; m++) { for (f = 0; f < nf; f++) { for (n = i = 0; i < MAXSAT; i++) { if (!test_sys(rtk->ssat[i].sys, m) || rtk->ssat[i].fix[f] != 2 || rtk->ssat[i].azel[1] < rtk->opt.elmaskhold) { continue; } index[n++] = IB_RTK(i + 1, f, &rtk->opt); rtk->ssat[i].fix[f] = 3; /* hold */ } /* constraint to fixed ambiguity */ for (i = 1; i < n; i++) { v[nv] = (xa[index[0]] - xa[index[i]]) - (rtk->x[index[0]] - rtk->x[index[i]]); H[index[0] + nv * rtk->nx] = 1.0; H[index[i] + nv * rtk->nx] = -1.0; nv++; } } } if (nv > 0) { R = zeros(nv, nv); for (i = 0; i < nv; i++) { R[i + i * nv] = VAR_HOLDAMB; } /* update states with constraints */ if ((info = filter(rtk->x, rtk->P, H, v, R, rtk->nx, nv))) { errmsg(rtk, "filter error (info=%d)\n", info); } free(R); } free(v); free(H); } /* resolve integer ambiguity by LAMBDA ---------------------------------------*/ int resamb_LAMBDA(rtk_t *rtk, double *bias, double *xa) { prcopt_t *opt = &rtk->opt; int i, j, ny, nb, info, nx = rtk->nx, na = rtk->na; double *D, *DP, *y, *Qy, *b, *db, *Qb, *Qab, *QQ, s[2]; trace(3, "resamb_LAMBDA : nx=%d\n", nx); rtk->sol.ratio = 0.0; if (rtk->opt.mode <= PMODE_DGPS || rtk->opt.modear == ARMODE_OFF || rtk->opt.thresar[0] < 1.0) { return 0; } /* single to double-difference transformation matrix (D') */ D = zeros(nx, nx); if ((nb = ddmat(rtk, D)) <= 0) { errmsg(rtk, "no valid double-difference\n"); free(D); return 0; } ny = na + nb; y = mat(ny, 1); Qy = mat(ny, ny); DP = mat(ny, nx); b = mat(nb, 2); db = mat(nb, 1); Qb = mat(nb, nb); Qab = mat(na, nb); QQ = mat(na, nb); /* transform single to double-differenced phase-bias (y=D'*x, Qy=D'*P*D) */ matmul("TN", ny, 1, nx, 1.0, D, rtk->x, 0.0, y); matmul("TN", ny, nx, nx, 1.0, D, rtk->P, 0.0, DP); matmul("NN", ny, ny, nx, 1.0, DP, D, 0.0, Qy); /* phase-bias covariance (Qb) and real-parameters to bias covariance (Qab) */ for (i = 0; i < nb; i++) { for (j = 0; j < nb; j++) { Qb[i + j * nb] = Qy[na + i + (na + j) * ny]; } } for (i = 0; i < na; i++) { for (j = 0; j < nb; j++) { Qab[i + j * na] = Qy[i + (na + j) * ny]; } } trace(4, "N(0)="); tracemat(4, y + na, 1, nb, 10, 3); /* lambda/mlambda integer least-square estimation */ if (!(info = lambda(nb, 2, y + na, Qb, b, s))) { trace(4, "N(1)="); tracemat(4, b, 1, nb, 10, 3); trace(4, "N(2)="); tracemat(4, b + nb, 1, nb, 10, 3); rtk->sol.ratio = s[0] > 0 ? static_cast(s[1] / s[0]) : 0.0F; if (rtk->sol.ratio > 999.9) { rtk->sol.ratio = 999.9F; } /* validation by popular ratio-test */ if (s[0] <= 0.0 || s[1] / s[0] >= opt->thresar[0]) { /* transform float to fixed solution (xa=xa-Qab*Qb\(b0-b)) */ for (i = 0; i < na; i++) { rtk->xa[i] = rtk->x[i]; for (j = 0; j < na; j++) { rtk->Pa[i + j * na] = rtk->P[i + j * nx]; } } for (i = 0; i < nb; i++) { bias[i] = b[i]; y[na + i] -= b[i]; } if (!matinv(Qb, nb)) { matmul("NN", nb, 1, nb, 1.0, Qb, y + na, 0.0, db); matmul("NN", na, 1, nb, -1.0, Qab, db, 1.0, rtk->xa); /* covariance of fixed solution (Qa=Qa-Qab*Qb^-1*Qab') */ matmul("NN", na, nb, nb, 1.0, Qab, Qb, 0.0, QQ); matmul("NT", na, na, nb, -1.0, QQ, Qab, 1.0, rtk->Pa); trace(3, "resamb : validation ok (nb=%d ratio=%.2f s=%.2f/%.2f)\n", nb, s[0] == 0.0 ? 0.0 : s[1] / s[0], s[0], s[1]); /* restore single-differenced ambiguity */ restamb(rtk, bias, nb, xa); } else { nb = 0; } } else { /* validation failed */ errmsg(rtk, "ambiguity validation failed (nb=%d ratio=%.2f s=%.2f/%.2f)\n", nb, s[1] / s[0], s[0], s[1]); nb = 0; } } else { errmsg(rtk, "lambda error (info=%d)\n", info); } free(D); free(y); free(Qy); free(DP); free(b); free(db); free(Qb); free(Qab); free(QQ); return nb; /* number of ambiguities */ } /* validation of solution ----------------------------------------------------*/ int valpos(rtk_t *rtk, const double *v, const double *R, const int *vflg, int nv, double thres) { #if 0 prcopt_t *opt = &rtk->opt; double vv = 0.0; #endif double fact = thres * thres; int i, stat = 1, sat1, sat2, type, freq; char stype; trace(3, "valpos : nv=%d thres=%.1f\n", nv, thres); /* post-fit residual test */ for (i = 0; i < nv; i++) { if (v[i] * v[i] <= fact * R[i + i * nv]) { continue; } sat1 = (vflg[i] >> 16) & 0xFF; sat2 = (vflg[i] >> 8) & 0xFF; type = (vflg[i] >> 4) & 0xF; freq = vflg[i] & 0xF; stype = type == 0 ? 'L' : (type == 1 ? 'L' : 'C'); errmsg(rtk, "large residual (sat=%2d-%2d %s%d v=%6.3f sig=%.3f)\n", sat1, sat2, stype, freq + 1, v[i], std::sqrt(R[i + i * nv])); } #if 0 /* omitted v.2.4.0 */ if (stat&&nv>NP(opt)) { /* chi-square validation */ for (i = 0; i chisqr[nv-NP(opt)-1]) { errmsg(rtk,"residuals validation failed (nv=%d np=%d vv=%.2f cs=%.2f)\n", nv, NP(opt), vv, chisqr[nv-NP(opt)-1]); stat = 0; } else { trace(3,"valpos : validation ok (%s nv=%d np=%d vv=%.2f cs=%.2f)\n", rtk->tstr, nv, NP(opt), vv, chisqr[nv-NP(opt)-1]); } } #endif return stat; } /* relative positioning ------------------------------------------------------*/ int relpos(rtk_t *rtk, const obsd_t *obs, int nu, int nr, const nav_t *nav) { prcopt_t *opt = &rtk->opt; gtime_t time = obs[0].time; double *rs, *dts, *var, *y, *e, *azel, *v, *H, *R, *xp, *Pp, *xa, *bias, dt; int i, j, f, n = nu + nr, ns, ny, nv, sat[MAXSAT], iu[MAXSAT], ir[MAXSAT], niter; int info, vflg[MAXOBS * NFREQ * 2 + 1], svh[MAXOBS * 2]; int stat = rtk->opt.mode <= PMODE_DGPS ? SOLQ_DGPS : SOLQ_FLOAT; int nf = opt->ionoopt == IONOOPT_IFLC ? 1 : opt->nf; trace(3, "relpos : nx=%d nu=%d nr=%d\n", rtk->nx, nu, nr); dt = timediff(time, obs[nu].time); rs = mat(6, n); dts = mat(2, n); var = mat(1, n); y = mat(nf * 2, n); e = mat(3, n); azel = zeros(2, n); for (i = 0; i < MAXSAT; i++) { rtk->ssat[i].sys = satsys(i + 1, nullptr); for (j = 0; j < NFREQ; j++) { rtk->ssat[i].vsat[j] = rtk->ssat[i].snr[j] = 0; } } /* satellite positions/clocks */ satposs(time, obs, n, nav, opt->sateph, rs, dts, var, svh); /* undifferenced residuals for base station */ if (!zdres(1, obs + nu, nr, rs + nu * 6, dts + nu * 2, svh + nu, nav, rtk->rb, opt, 1, y + nu * nf * 2, e + nu * 3, azel + nu * 2)) { errmsg(rtk, "initial base station position error\n"); free(rs); free(dts); free(var); free(y); free(e); free(azel); return 0; } /* time-interpolation of residuals (for post-processing) */ if (opt->intpref) { dt = intpres(time, obs + nu, nr, nav, rtk, y + nu * nf * 2); } /* select common satellites between rover and base-station */ if ((ns = selsat(obs, azel, nu, nr, opt, sat, iu, ir)) <= 0) { errmsg(rtk, "no common satellite\n"); free(rs); free(dts); free(var); free(y); free(e); free(azel); return 0; } /* temporal update of states */ udstate(rtk, obs, sat, iu, ir, ns, nav); trace(4, "x(0)="); tracemat(4, rtk->x, 1, NR_RTK(opt), 13, 4); xp = mat(rtk->nx, 1); Pp = zeros(rtk->nx, rtk->nx); xa = mat(rtk->nx, 1); matcpy(xp, rtk->x, rtk->nx, 1); ny = ns * nf * 2 + 2; v = mat(ny, 1); H = zeros(rtk->nx, ny); R = mat(ny, ny); bias = mat(rtk->nx, 1); /* add 2 iterations for baseline-constraint moving-base */ niter = opt->niter + (opt->mode == PMODE_MOVEB && opt->baseline[0] > 0.0 ? 2 : 0); for (i = 0; i < niter; i++) { /* undifferenced residuals for rover */ if (!zdres(0, obs, nu, rs, dts, svh, nav, xp, opt, 0, y, e, azel)) { errmsg(rtk, "rover initial position error\n"); stat = SOLQ_NONE; break; } /* double-differenced residuals and partial derivatives */ if ((nv = ddres(rtk, nav, dt, xp, Pp, sat, y, e, azel, iu, ir, ns, v, H, R, vflg)) < 1) { errmsg(rtk, "no double-differenced residual\n"); stat = SOLQ_NONE; break; } /* kalman filter measurement update */ matcpy(Pp, rtk->P, rtk->nx, rtk->nx); if ((info = filter(xp, Pp, H, v, R, rtk->nx, nv))) { errmsg(rtk, "filter error (info=%d)\n", info); stat = SOLQ_NONE; break; } trace(4, "x(%d)=", i + 1); tracemat(4, xp, 1, NR_RTK(opt), 13, 4); } if (stat != SOLQ_NONE && zdres(0, obs, nu, rs, dts, svh, nav, xp, opt, 0, y, e, azel)) { /* post-fit residuals for float solution */ nv = ddres(rtk, nav, dt, xp, Pp, sat, y, e, azel, iu, ir, ns, v, nullptr, R, vflg); /* validation of float solution */ if (valpos(rtk, v, R, vflg, nv, 4.0)) { /* update state and covariance matrix */ matcpy(rtk->x, xp, rtk->nx, 1); matcpy(rtk->P, Pp, rtk->nx, rtk->nx); /* update ambiguity control struct */ rtk->sol.ns = 0; for (i = 0; i < ns; i++) { for (f = 0; f < nf; f++) { if (!rtk->ssat[sat[i] - 1].vsat[f]) { continue; } rtk->ssat[sat[i] - 1].lock[f]++; rtk->ssat[sat[i] - 1].outc[f] = 0; if (f == 0) { rtk->sol.ns++; /* valid satellite count by L1 */ } } } /* lack of valid satellites */ if (rtk->sol.ns < 4) { stat = SOLQ_NONE; } } else { stat = SOLQ_NONE; } } /* resolve integer ambiguity by WL-NL */ if (stat != SOLQ_NONE && rtk->opt.modear == ARMODE_WLNL) { if (resamb_WLNL(rtk, obs, sat, iu, ir, ns, nav, azel)) { stat = SOLQ_FIX; } } /* resolve integer ambiguity by TCAR */ else if (stat != SOLQ_NONE && rtk->opt.modear == ARMODE_TCAR) { if (resamb_TCAR(rtk, obs, sat, iu, ir, ns, nav, azel)) { stat = SOLQ_FIX; } } /* resolve integer ambiguity by LAMBDA */ else if (stat != SOLQ_NONE && resamb_LAMBDA(rtk, bias, xa) > 1) { if (zdres(0, obs, nu, rs, dts, svh, nav, xa, opt, 0, y, e, azel)) { /* post-fit reisiduals for fixed solution */ nv = ddres(rtk, nav, dt, xa, nullptr, sat, y, e, azel, iu, ir, ns, v, nullptr, R, vflg); /* validation of fixed solution */ if (valpos(rtk, v, R, vflg, nv, 4.0)) { /* hold integer ambiguity */ if (++rtk->nfix >= rtk->opt.minfix && rtk->opt.modear == ARMODE_FIXHOLD) { holdamb(rtk, xa); } stat = SOLQ_FIX; } } } /* save solution status */ if (stat == SOLQ_FIX) { for (i = 0; i < 3; i++) { rtk->sol.rr[i] = rtk->xa[i]; rtk->sol.qr[i] = static_cast(rtk->Pa[i + i * rtk->na]); } rtk->sol.qr[3] = static_cast(rtk->Pa[1]); rtk->sol.qr[4] = static_cast(rtk->Pa[1 + 2 * rtk->na]); rtk->sol.qr[5] = static_cast(rtk->Pa[2]); } else { for (i = 0; i < 3; i++) { rtk->sol.rr[i] = rtk->x[i]; rtk->sol.qr[i] = static_cast(rtk->P[i + i * rtk->nx]); } rtk->sol.qr[3] = static_cast(rtk->P[1]); rtk->sol.qr[4] = static_cast(rtk->P[1 + 2 * rtk->nx]); rtk->sol.qr[5] = static_cast(rtk->P[2]); rtk->nfix = 0; } for (i = 0; i < n; i++) { for (j = 0; j < nf; j++) { if (obs[i].L[j] == 0.0) { continue; } rtk->ssat[obs[i].sat - 1].pt[obs[i].rcv - 1][j] = obs[i].time; rtk->ssat[obs[i].sat - 1].ph[obs[i].rcv - 1][j] = obs[i].L[j]; } } for (i = 0; i < ns; i++) { for (j = 0; j < nf; j++) { /* output snr of rover receiver */ rtk->ssat[sat[i] - 1].snr[j] = obs[iu[i]].SNR[j]; } } for (i = 0; i < MAXSAT; i++) { for (j = 0; j < nf; j++) { if (rtk->ssat[i].fix[j] == 2 && stat != SOLQ_FIX) { rtk->ssat[i].fix[j] = 1; } if (rtk->ssat[i].slip[j] & 1) { rtk->ssat[i].slipc[j]++; } } } free(rs); free(dts); free(var); free(y); free(e); free(azel); free(xp); free(Pp); free(xa); free(v); free(H); free(R); free(bias); if (stat != SOLQ_NONE) { rtk->sol.stat = stat; } return stat != SOLQ_NONE; } /* initialize rtk control ------------------------------------------------------ * initialize rtk control struct * args : rtk_t *rtk IO rtk control/result struct * prcopt_t *opt I positioning options (see rtklib.h) * return : none *-----------------------------------------------------------------------------*/ void rtkinit(rtk_t *rtk, const prcopt_t *opt) { sol_t sol0 = {{0, 0}, {}, {}, {}, '0', '0', '0', 0.0, 0.0, 0.0}; ambc_t ambc0 = {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {}, {}, {}, 0, {}}; ssat_t ssat0 = {0, 0, {0.0}, {0.0}, {0.0}, {'0'}, {'0'}, {'0'}, {'0'}, {'0'}, {}, {}, {}, {}, 0.0, 0.0, 0.0, 0.0, {{{0, 0}}, {{0, 0}}}, {{}, {}}}; int i; trace(3, "rtkinit :\n"); rtk->sol = sol0; for (i = 0; i < 6; i++) { rtk->rb[i] = 0.0; } rtk->nx = opt->mode <= PMODE_FIXED ? NX_RTK(opt) : pppnx(opt); rtk->na = opt->mode <= PMODE_FIXED ? NR_RTK(opt) : pppnx(opt); rtk->tt = 0.0; rtk->x = zeros(rtk->nx, 1); rtk->P = zeros(rtk->nx, rtk->nx); rtk->xa = zeros(rtk->na, 1); rtk->Pa = zeros(rtk->na, rtk->na); rtk->nfix = rtk->neb = 0; for (i = 0; i < MAXSAT; i++) { rtk->ambc[i] = ambc0; rtk->ssat[i] = ssat0; } for (i = 0; i < MAXERRMSG; i++) { rtk->errbuf[i] = 0; } rtk->opt = *opt; } /* free rtk control ------------------------------------------------------------ * free memory for rtk control struct * args : rtk_t *rtk IO rtk control/result struct * return : none *-----------------------------------------------------------------------------*/ void rtkfree(rtk_t *rtk) { trace(3, "rtkfree :\n"); rtk->nx = rtk->na = 0; free(rtk->x); rtk->x = nullptr; free(rtk->P); rtk->P = nullptr; free(rtk->xa); rtk->xa = nullptr; free(rtk->Pa); rtk->Pa = nullptr; } /* precise positioning --------------------------------------------------------- * input observation data and navigation message, compute rover position by * precise positioning * args : rtk_t *rtk IO rtk control/result struct * rtk->sol IO solution * .time O solution time * .rr[] IO rover position/velocity * (I:fixed mode,O:single mode) * .dtr[0] O receiver clock bias (s) * .dtr[1] O receiver glonass-gps time offset (s) * .Qr[] O rover position covarinace * .stat O solution status (SOLQ_???) * .ns O number of valid satellites * .age O age of differential (s) * .ratio O ratio factor for ambiguity validation * rtk->rb[] IO base station position/velocity * (I:relative mode,O:moving-base mode) * rtk->nx I number of all states * rtk->na I number of integer states * rtk->ns O number of valid satellite * rtk->tt O time difference between current and previous (s) * rtk->x[] IO float states pre-filter and post-filter * rtk->P[] IO float covariance pre-filter and post-filter * rtk->xa[] O fixed states after AR * rtk->Pa[] O fixed covariance after AR * rtk->ssat[s] IO sat(s+1) status * .sys O system (SYS_???) * .az [r] O azimuth angle (rad) (r=0:rover,1:base) * .el [r] O elevation angle (rad) (r=0:rover,1:base) * .vs [r] O data valid single (r=0:rover,1:base) * .resp [f] O freq(f+1) pseudorange residual (m) * .resc [f] O freq(f+1) carrier-phase residual (m) * .vsat [f] O freq(f+1) data valid (0:invalid,1:valid) * .fix [f] O freq(f+1) ambiguity flag * (0:nodata,1:float,2:fix,3:hold) * .slip [f] O freq(f+1) slip flag * (bit8-7:rcv1 LLI, bit6-5:rcv2 LLI, * bit2:parity unknown, bit1:slip) * .lock [f] IO freq(f+1) carrier lock count * .outc [f] IO freq(f+1) carrier outage count * .slipc[f] IO freq(f+1) cycle slip count * .rejc [f] IO freq(f+1) data reject count * .gf IO geometry-free phase (L1-L2) (m) * .gf2 IO geometry-free phase (L1-L5) (m) * rtk->nfix IO number of continuous fixes of ambiguity * rtk->neb IO bytes of error message buffer * rtk->errbuf IO error message buffer * rtk->tstr O time string for debug * rtk->opt I processing options * obsd_t *obs I observation data for an epoch * obs[i].rcv=1:rover,2:reference * sorted by receiver and satellte * int n I number of observation data * nav_t *nav I navigation messages * return : status (0:no solution,1:valid solution) * notes : before calling function, base station position rtk->sol.rb[] should * be properly set for relative mode except for moving-baseline *-----------------------------------------------------------------------------*/ int rtkpos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav) { prcopt_t *opt = &rtk->opt; sol_t solb = {{0, 0}, {}, {}, {}, '0', '0', '0', 0.0, 0.0, 0.0}; gtime_t time; int i, nu, nr; char msg[128] = ""; trace(3, "rtkpos : time=%s n=%d\n", time_str(obs[0].time, 3), n); trace(4, "obs=\n"); traceobs(4, obs, n); /*trace(5,"nav=\n"); tracenav(5,nav);*/ /* set base station position */ if (opt->refpos <= POSOPT_RINEX && opt->mode != PMODE_SINGLE && opt->mode != PMODE_MOVEB) { for (i = 0; i < 6; i++) { rtk->rb[i] = i < 3 ? opt->rb[i] : 0.0; } } /* count rover/base station observations */ for (nu = 0; nu < n && obs[nu].rcv == 1; nu++) { ; } for (nr = 0; nu + nr < n && obs[nu + nr].rcv == 2; nr++) { ; } time = rtk->sol.time; /* previous epoch */ /* rover position by single point positioning */ if (!pntpos(obs, nu, nav, &rtk->opt, &rtk->sol, nullptr, rtk->ssat, msg)) { errmsg(rtk, "point pos error (%s)\n", msg); if (!rtk->opt.dynamics) { outsolstat(rtk); return 0; } } if (time.time != 0) { rtk->tt = timediff(rtk->sol.time, time); } /* single point positioning */ if (opt->mode == PMODE_SINGLE) { outsolstat(rtk); return 1; } /* suppress output of single solution */ if (!opt->outsingle) { rtk->sol.stat = SOLQ_NONE; } /* precise point positioning */ if (opt->mode >= PMODE_PPP_KINEMA) { pppos(rtk, obs, nu, nav); outsolstat(rtk); return 1; } /* check number of data of base station and age of differential */ if (nr == 0) { errmsg(rtk, "no base station observation data for rtk\n"); outsolstat(rtk); return 1; } if (opt->mode == PMODE_MOVEB) { /* moving baseline */ /* estimate position/velocity of base station */ if (!pntpos(obs + nu, nr, nav, &rtk->opt, &solb, nullptr, nullptr, msg)) { errmsg(rtk, "base station position error (%s)\n", msg); return 0; } rtk->sol.age = static_cast(timediff(rtk->sol.time, solb.time)); if (std::fabs(rtk->sol.age) > TTOL_MOVEB) { errmsg(rtk, "time sync error for moving-base (age=%.1f)\n", rtk->sol.age); return 0; } for (i = 0; i < 6; i++) { rtk->rb[i] = solb.rr[i]; } /* time-synchronized position of base station */ for (i = 0; i < 3; i++) { rtk->rb[i] += rtk->rb[i + 3] * rtk->sol.age; } } else { rtk->sol.age = static_cast(timediff(obs[0].time, obs[nu].time)); if (std::fabs(rtk->sol.age) > opt->maxtdiff) { errmsg(rtk, "age of differential error (age=%.1f)\n", rtk->sol.age); outsolstat(rtk); return 1; } } /* relative potitioning */ relpos(rtk, obs, nu, nr, nav); outsolstat(rtk); return 1; } src/algorithms/libs/rtklib/rtklib_rtkpos.h000066400000000000000000000163001352176506000213050ustar00rootroot00000000000000/*! * \file rtklib_rtkpos.h * \brief rtklib ppp-related functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RKTPOS_H_ #define GNSS_SDR_RTKLIB_RKTPOS_H_ #include "rtklib.h" #include "rtklib_rtkcmn.h" /* constants/macros ----------------------------------------------------------*/ const double VAR_POS = std::pow(30.0, 2.0); /* initial variance of receiver pos (m^2) */ const double VAR_VEL = std::pow(10.0, 2.0); /* initial variance of receiver vel ((m/s)^2) */ const double VAR_ACC = std::pow(10.0, 2.0); /* initial variance of receiver acc ((m/ss)^2) */ const double VAR_HWBIAS = std::pow(1.0, 2.0); /* initial variance of h/w bias ((m/MHz)^2) */ const double VAR_GRA = std::pow(0.001, 2.0); /* initial variance of gradient (m^2) */ const double INIT_ZWD = 0.15; /* initial zwd (m) */ const double PRN_HWBIA = 1E-6; /* process noise of h/w bias (m/MHz/sqrt(s)) */ const double MAXAC = 30.0; /* max accel for doppler slip detection (m/s^2) */ const double VAR_HOLDAMB = 0.001; /* constraint to hold ambiguity (cycle^2) */ const double TTOL_MOVEB = (1.0 + 2 * DTTOL); /* time sync tolerance for moving-baseline (s) */ /* number of parameters (pos,ionos,tropos,hw-bias,phase-bias,real,estimated) */ /* state variable index */ #define II_RTK(s, opt) (NP_RTK(opt) + (s)-1) /* ionos (s:satellite no) */ #define IT_RTK(r, opt) (NP_RTK(opt) + NI_RTK(opt) + NT_RTK(opt) / 2 * (r)) /* tropos (r:0=rov,1:ref) */ #define IL_RTK(f, opt) (NP_RTK(opt) + NI_RTK(opt) + NT_RTK(opt) + (f)) /* receiver h/w bias */ #define IB_RTK(s, f, opt) (NR_RTK(opt) + MAXSAT * (f) + (s)-1) /* phase bias (s:satno,f:freq) */ int rtkopenstat(const char *file, int level); void rtkclosestat(void); void rtkoutstat(rtk_t *rtk); void swapsolstat(void); void outsolstat(rtk_t *rtk); void errmsg(rtk_t *rtk, const char *format, ...); double sdobs(const obsd_t *obs, int i, int j, int f); double gfobs_L1L2(const obsd_t *obs, int i, int j, const double *lam); double gfobs_L1L5(const obsd_t *obs, int i, int j, const double *lam); double varerr(int sat, int sys, double el, double bl, double dt, int f, const prcopt_t *opt); double baseline(const double *ru, const double *rb, double *dr); void initx_rtk(rtk_t *rtk, double xi, double var, int i); int selsat(const obsd_t *obs, const double *azel, int nu, int nr, const prcopt_t *opt, int *sat, int *iu, int *ir); void udpos(rtk_t *rtk, double tt); void udion(rtk_t *rtk, double tt, double bl, const int *sat, int ns); void udtrop(rtk_t *rtk, double tt, double bl); void udrcvbias(rtk_t *rtk, double tt); void detslp_ll(rtk_t *rtk, const obsd_t *obs, int i, int rcv); void detslp_gf_L1L2(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav); void detslp_gf_L1L5(rtk_t *rtk, const obsd_t *obs, int i, int j, const nav_t *nav); void detslp_dop(rtk_t *rtk, const obsd_t *obs, int i, int rcv, const nav_t *nav); void udbias(rtk_t *rtk, double tt, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav); void udstate(rtk_t *rtk, const obsd_t *obs, const int *sat, const int *iu, const int *ir, int ns, const nav_t *nav); void zdres_sat(int base, double r, const obsd_t *obs, const nav_t *nav, const double *azel, const double *dant, const prcopt_t *opt, double *y); int zdres(int base, const obsd_t *obs, int n, const double *rs, const double *dts, const int *svh, const nav_t *nav, const double *rr, const prcopt_t *opt, int index, double *y, double *e, double *azel); int validobs(int i, int j, int f, int nf, const double *y); void ddcov(const int *nb, int n, const double *Ri, const double *Rj, int nv, double *R); int constbl(rtk_t *rtk, const double *x, const double *P, double *v, double *H, double *Ri, double *Rj, int index); double prectrop(gtime_t time, const double *pos, int r, const double *azel, const prcopt_t *opt, const double *x, double *dtdx); double gloicbcorr(int sat1, int sat2, const prcopt_t *opt, double lam1, double lam2, int f); int test_sys(int sys, int m); int ddres(rtk_t *rtk, const nav_t *nav, double dt, const double *x, const double *P, const int *sat, double *y, const double *e, double *azel, const int *iu, const int *ir, int ns, double *v, double *H, double *R, int *vflg); double intpres(gtime_t time, const obsd_t *obs, int n, const nav_t *nav, rtk_t *rtk, double *y); int ddmat(rtk_t *rtk, double *D); void restamb(rtk_t *rtk, const double *bias, int nb, double *xa); void holdamb(rtk_t *rtk, const double *xa); int resamb_LAMBDA(rtk_t *rtk, double *bias, double *xa); int valpos(rtk_t *rtk, const double *v, const double *R, const int *vflg, int nv, double thres); int relpos(rtk_t *rtk, const obsd_t *obs, int nu, int nr, const nav_t *nav); void rtkinit(rtk_t *rtk, const prcopt_t *opt); void rtkfree(rtk_t *rtk); int rtkpos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav); #endif src/algorithms/libs/rtklib/rtklib_rtksvr.cc000066400000000000000000001235611352176506000214640ustar00rootroot00000000000000/*! * \file rtklib_rtksvr.cc * \brief rtk server functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_rtksvr.h" #include "rtklib_preceph.h" #include "rtklib_rtcm.h" #include "rtklib_rtkcmn.h" #include "rtklib_rtkpos.h" #include "rtklib_sbas.h" #include "rtklib_solution.h" #include "rtklib_stream.h" #include /* write solution header to output stream ------------------------------------*/ void writesolhead(stream_t *stream, const solopt_t *solopt) { unsigned char buff[1024]; int n; n = outsolheads(buff, solopt); strwrite(stream, buff, n); } /* save output buffer --------------------------------------------------------*/ void saveoutbuf(rtksvr_t *svr, unsigned char *buff, int n, int index) { rtksvrlock(svr); n = n < svr->buffsize - svr->nsb[index] ? n : svr->buffsize - svr->nsb[index]; memcpy(svr->sbuf[index] + svr->nsb[index], buff, n); svr->nsb[index] += n; rtksvrunlock(svr); } /* write solution to output stream -------------------------------------------*/ void writesol(rtksvr_t *svr, int index) { solopt_t solopt = SOLOPT_DEFAULT; unsigned char buff[1024]; int i, n; tracet(4, "writesol: index=%d\n", index); for (i = 0; i < 2; i++) { /* output solution */ n = outsols(buff, &svr->rtk.sol, svr->rtk.rb, svr->solopt + i); strwrite(svr->stream + i + 3, buff, n); /* save output buffer */ saveoutbuf(svr, buff, n, i); /* output extended solution */ n = outsolexs(buff, &svr->rtk.sol, svr->rtk.ssat, svr->solopt + i); strwrite(svr->stream + i + 3, buff, n); /* save output buffer */ saveoutbuf(svr, buff, n, i); } /* output solution to monitor port */ if (svr->moni) { n = outsols(buff, &svr->rtk.sol, svr->rtk.rb, &solopt); strwrite(svr->moni, buff, n); } /* save solution buffer */ if (svr->nsol < MAXSOLBUF) { rtksvrlock(svr); svr->solbuf[svr->nsol++] = svr->rtk.sol; rtksvrunlock(svr); } } /* update navigation data ----------------------------------------------------*/ void updatenav(nav_t *nav) { int i, j; for (i = 0; i < MAXSAT; i++) { for (j = 0; j < NFREQ; j++) { nav->lam[i][j] = satwavelen(i + 1, j, nav); } } } /* update glonass frequency channel number in raw data struct ----------------*/ void updatefcn(rtksvr_t *svr) { int i, j, sat, frq; for (i = 0; i < MAXPRNGLO; i++) { sat = satno(SYS_GLO, i + 1); for (j = 0, frq = -999; j < 3; j++) { if (svr->raw[j].nav.geph[i].sat != sat) { continue; } frq = svr->raw[j].nav.geph[i].frq; } if (frq < -7 || frq > 6) { continue; } for (j = 0; j < 3; j++) { if (svr->raw[j].nav.geph[i].sat == sat) { continue; } svr->raw[j].nav.geph[i].sat = sat; svr->raw[j].nav.geph[i].frq = frq; } } } /* update rtk server struct --------------------------------------------------*/ void updatesvr(rtksvr_t *svr, int ret, obs_t *obs, nav_t *nav, int sat, sbsmsg_t *sbsmsg, int index, int iobs) { eph_t *eph1, *eph2, *eph3; geph_t *geph1, *geph2, *geph3; // gtime_t tof; double pos[3], del[3] = {0}, dr[3]; int i, n = 0, prn, sbssat = svr->rtk.opt.sbassatsel, sys, iode; tracet(4, "updatesvr: ret=%d sat=%2d index=%d\n", ret, sat, index); if (ret == 1) { /* observation data */ if (iobs < MAXOBSBUF) { for (i = 0; i < obs->n; i++) { if (svr->rtk.opt.exsats[obs->data[i].sat - 1] == 1 || !(satsys(obs->data[i].sat, nullptr) & svr->rtk.opt.navsys)) { continue; } svr->obs[index][iobs].data[n] = obs->data[i]; svr->obs[index][iobs].data[n++].rcv = index + 1; } svr->obs[index][iobs].n = n; sortobs(&svr->obs[index][iobs]); } svr->nmsg[index][0]++; } else if (ret == 2) { /* ephemeris */ if (satsys(sat, &prn) != SYS_GLO) { if (!svr->navsel || svr->navsel == index + 1) { eph1 = nav->eph + sat - 1; eph2 = svr->nav.eph + sat - 1; eph3 = svr->nav.eph + sat - 1 + MAXSAT; if (eph2->ttr.time == 0 || (eph1->iode != eph3->iode && eph1->iode != eph2->iode) || (timediff(eph1->toe, eph3->toe) != 0.0 && timediff(eph1->toe, eph2->toe) != 0.0)) { *eph3 = *eph2; *eph2 = *eph1; updatenav(&svr->nav); } } svr->nmsg[index][1]++; } else { if (!svr->navsel || svr->navsel == index + 1) { geph1 = nav->geph + prn - 1; geph2 = svr->nav.geph + prn - 1; geph3 = svr->nav.geph + prn - 1 + MAXPRNGLO; if (geph2->tof.time == 0 || (geph1->iode != geph3->iode && geph1->iode != geph2->iode)) { *geph3 = *geph2; *geph2 = *geph1; updatenav(&svr->nav); updatefcn(svr); } } svr->nmsg[index][6]++; } } else if (ret == 3) { /* sbas message */ if (sbsmsg && (sbssat == sbsmsg->prn || sbssat == 0)) { if (svr->nsbs < MAXSBSMSG) { svr->sbsmsg[svr->nsbs++] = *sbsmsg; } else { for (i = 0; i < MAXSBSMSG - 1; i++) { svr->sbsmsg[i] = svr->sbsmsg[i + 1]; } svr->sbsmsg[i] = *sbsmsg; } sbsupdatecorr(sbsmsg, &svr->nav); } svr->nmsg[index][3]++; } else if (ret == 9) { /* ion/utc parameters */ if (svr->navsel == index || svr->navsel >= 3) { for (i = 0; i < 8; i++) { svr->nav.ion_gps[i] = nav->ion_gps[i]; } for (i = 0; i < 4; i++) { svr->nav.utc_gps[i] = nav->utc_gps[i]; } for (i = 0; i < 4; i++) { svr->nav.ion_gal[i] = nav->ion_gal[i]; } for (i = 0; i < 4; i++) { svr->nav.utc_gal[i] = nav->utc_gal[i]; } for (i = 0; i < 8; i++) { svr->nav.ion_qzs[i] = nav->ion_qzs[i]; } for (i = 0; i < 4; i++) { svr->nav.utc_qzs[i] = nav->utc_qzs[i]; } svr->nav.leaps = nav->leaps; } svr->nmsg[index][2]++; } else if (ret == 5) { /* antenna position parameters */ if (svr->rtk.opt.refpos == 4 && index == 1) { for (i = 0; i < 3; i++) { svr->rtk.rb[i] = svr->rtcm[1].sta.pos[i]; } /* antenna delta */ ecef2pos(svr->rtk.rb, pos); if (svr->rtcm[1].sta.deltype) { /* xyz */ del[2] = svr->rtcm[1].sta.hgt; enu2ecef(pos, del, dr); for (i = 0; i < 3; i++) { svr->rtk.rb[i] += svr->rtcm[1].sta.del[i] + dr[i]; } } else { /* enu */ enu2ecef(pos, svr->rtcm[1].sta.del, dr); for (i = 0; i < 3; i++) { svr->rtk.rb[i] += dr[i]; } } } svr->nmsg[index][4]++; } else if (ret == 7) { /* dgps correction */ svr->nmsg[index][5]++; } else if (ret == 10) { /* ssr message */ for (i = 0; i < MAXSAT; i++) { if (!svr->rtcm[index].ssr[i].update) { continue; } svr->rtcm[index].ssr[i].update = 0; iode = svr->rtcm[index].ssr[i].iode; sys = satsys(i + 1, &prn); /* check corresponding ephemeris exists */ if (sys == SYS_GPS || sys == SYS_GAL || sys == SYS_QZS) { if (svr->nav.eph[i].iode != iode && svr->nav.eph[i + MAXSAT].iode != iode) { continue; } } else if (sys == SYS_GLO) { if (svr->nav.geph[prn - 1].iode != iode && svr->nav.geph[prn - 1 + MAXPRNGLO].iode != iode) { continue; } } svr->nav.ssr[i] = svr->rtcm[index].ssr[i]; } svr->nmsg[index][7]++; } else if (ret == 31) { /* lex message */ // lexupdatecorr(&svr->raw[index].lexmsg, &svr->nav, &tof); svr->nmsg[index][8]++; } else if (ret == -1) { /* error */ svr->nmsg[index][9]++; } } /* decode receiver raw/rtcm data ---------------------------------------------*/ int decoderaw(rtksvr_t *svr, int index) { obs_t *obs; nav_t *nav; sbsmsg_t *sbsmsg = nullptr; int i, ret = 0, sat, fobs = 0; tracet(4, "decoderaw: index=%d\n", index); rtksvrlock(svr); for (i = 0; i < svr->nb[index]; i++) { /* input rtcm/receiver raw data from stream */ if (svr->format[index] == STRFMT_RTCM2) { ret = input_rtcm2(svr->rtcm + index, svr->buff[index][i]); obs = &svr->rtcm[index].obs; nav = &svr->rtcm[index].nav; sat = svr->rtcm[index].ephsat; } else if (svr->format[index] == STRFMT_RTCM3) { ret = input_rtcm3(svr->rtcm + index, svr->buff[index][i]); obs = &svr->rtcm[index].obs; nav = &svr->rtcm[index].nav; sat = svr->rtcm[index].ephsat; } else { // Disabled !! //ret = input_raw(svr->raw+index, svr->format[index], svr->buff[index][i]); obs = &svr->raw[index].obs; nav = &svr->raw[index].nav; sat = svr->raw[index].ephsat; sbsmsg = &svr->raw[index].sbsmsg; } #if 0 /* record for receiving tick */ if (ret == 1) { trace(0, "%d %10d T=%s NS=%2d\n", index, tickget(), time_str(obs->data[0].time, 0), obs->n); } #endif /* update rtk server */ if (ret > 0) { updatesvr(svr, ret, obs, nav, sat, sbsmsg, index, fobs); } /* observation data received */ if (ret == 1) { if (fobs < MAXOBSBUF) { fobs++; } else { svr->prcout++; } } } svr->nb[index] = 0; rtksvrunlock(svr); return fobs; } /* decode download file ------------------------------------------------------*/ void decodefile(rtksvr_t *svr, int index) { int i = 0; char glo_fcn[MAXPRNGLO + 1]; // Allocate space for GLONASS frequency channels depending on availability for (i = 0; i < MAXPRNGLO + 1; i++) { glo_fcn[i] = '0'; } pcv_t pcvt0[MAXSAT] = {{0, {'0'}, {'0'}, {0, 0.0}, {0, 0.0}, {{0.0}, {0.0}}, {{0.0}, {0.0}}}}; sbsfcorr_t sbsfcorr0 = {{0, 0.0}, 0.0, 0.0, 0.0, 0, 0, 0}; sbslcorr_t sbslcorr0 = {{0, 0.0}, 0, {0.0}, {0.0}, 0.0, 0.0}; sbssat_t sbssat0 = {0, 0, 0, {{0, sbsfcorr0, sbslcorr0}}}; sbsigp_t sbsigp0[MAXNIGP] = {{{0, 0.0}, 0, 0, 0, 0.0}}; sbsion_t sbsion0[MAXBAND + 1] = {{0, 0, {*sbsigp0}}}; dgps_t dgps0[MAXSAT] = {{{0, 0.0}, 0.0, 0.0, 0, 0.0}}; ssr_t ssr0[MAXSAT] = {{{{0, 0.0}}, {0.0}, {0}, 0, 0, 0, 0, {0.0}, {0.0}, {0.0}, 0.0, {0.0}, {0.0}, {0.0}, 0.0, 0.0, '0'}}; lexeph_t lexeph0[MAXSAT] = {{{0, 0.0}, {0, 0.0}, 0, 0, 0, {0.0}, {0.0}, {0.0}, {0.0}, 0.0, 0.0, 0.0, {0.0}}}; stec_t stec0[MAXSTA] = {{{0, 0.0}, 0, 0.0, 0.0, {0.0}, 0}}; trop_t trop0[MAXSTA] = {{{0, 0.0}, {0.0}, {0.0}}}; pppcorr_t pppcorr0 = {0, {{0}, {0}}, {{0.0}, {0.0}}, {0}, {0}, {0}, {0}, {stec0}, {trop0}}; nav_t nav = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, {0, 0, (erpd_t *){nullptr}}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, {0.0}, 0, {{0.0}, {0.0}}, {{0.0}, {0.0}}, {{{0.0}}, {{0.0}}, {{0.0}}}, {0.0}, {0.0}, {*glo_fcn}, {*pcvt0}, sbssat0, {*sbsion0}, {*dgps0}, {*ssr0}, {*lexeph0}, {{0, 0.0}, 0.0, {0.0}, {{0.0}, {0.0}}}, pppcorr0}; char file[1024]; int nb; tracet(4, "decodefile: index=%d\n", index); rtksvrlock(svr); /* check file path completed */ if ((nb = svr->nb[index]) <= 2 || svr->buff[index][nb - 2] != '\r' || svr->buff[index][nb - 1] != '\n') { rtksvrunlock(svr); return; } strncpy(file, reinterpret_cast(svr->buff[index]), nb - 2); file[nb - 2] = '\0'; svr->nb[index] = 0; rtksvrunlock(svr); if (svr->format[index] == STRFMT_SP3) { /* precise ephemeris */ /* read sp3 precise ephemeris */ readsp3(file, &nav, 0); if (nav.ne <= 0) { tracet(1, "sp3 file read error: %s\n", file); return; } /* update precise ephemeris */ rtksvrlock(svr); if (svr->nav.peph) { free(svr->nav.peph); } svr->nav.ne = svr->nav.nemax = nav.ne; svr->nav.peph = nav.peph; svr->ftime[index] = utc2gpst(timeget()); strcpy(svr->files[index], file); rtksvrunlock(svr); } else if (svr->format[index] == STRFMT_RNXCLK) { /* precise clock */ /* read rinex clock */ // Disabled!! if (true /*readrnxc(file, &nav)<=0 */) { tracet(1, "rinex clock file read error: %s\n", file); return; } /* update precise clock */ rtksvrlock(svr); if (svr->nav.pclk) { free(svr->nav.pclk); } svr->nav.nc = svr->nav.ncmax = nav.nc; svr->nav.pclk = nav.pclk; svr->ftime[index] = utc2gpst(timeget()); strcpy(svr->files[index], file); rtksvrunlock(svr); } } /* rtk server thread ---------------------------------------------------------*/ void *rtksvrthread(void *arg) { auto *svr = static_cast(arg); obs_t obs; obsd_t data[MAXOBS * 2]; double tt; unsigned int tick, ticknmea; unsigned char *p, *q; int i, j, n, fobs[3] = {0}, cycle, cputime; tracet(3, "rtksvrthread:\n"); svr->state = 1; obs.data = data; svr->tick = tickget(); ticknmea = svr->tick - 1000; for (cycle = 0; svr->state; cycle++) { tick = tickget(); for (i = 0; i < 3; i++) { p = svr->buff[i] + svr->nb[i]; q = svr->buff[i] + svr->buffsize; /* read receiver raw/rtcm data from input stream */ if ((n = strread(svr->stream + i, p, static_cast(q[0]) - static_cast(p[0]))) <= 0) { continue; } /* write receiver raw/rtcm data to log stream */ strwrite(svr->stream + i + 5, p, n); svr->nb[i] += n; /* save peek buffer */ rtksvrlock(svr); n = n < svr->buffsize - svr->npb[i] ? n : svr->buffsize - svr->npb[i]; memcpy(svr->pbuf[i] + svr->npb[i], p, n); svr->npb[i] += n; rtksvrunlock(svr); } for (i = 0; i < 3; i++) { if (svr->format[i] == STRFMT_SP3 || svr->format[i] == STRFMT_RNXCLK) { /* decode download file */ decodefile(svr, i); } else { /* decode receiver raw/rtcm data */ fobs[i] = decoderaw(svr, i); } } for (i = 0; i < fobs[0]; i++) { /* for each rover observation data */ obs.n = 0; for (j = 0; j < svr->obs[0][i].n && obs.n < MAXOBS * 2; j++) { obs.data[obs.n++] = svr->obs[0][i].data[j]; } for (j = 0; j < svr->obs[1][0].n && obs.n < MAXOBS * 2; j++) { obs.data[obs.n++] = svr->obs[1][0].data[j]; } /* rtk positioning */ rtksvrlock(svr); rtkpos(&svr->rtk, obs.data, obs.n, &svr->nav); rtksvrunlock(svr); if (svr->rtk.sol.stat != SOLQ_NONE) { /* adjust current time */ tt = static_cast(tickget() - tick) / 1000.0 + DTTOL; timeset(gpst2utc(timeadd(svr->rtk.sol.time, tt))); /* write solution */ writesol(svr, i); } /* if cpu overload, inclement obs outage counter and break */ if (static_cast(tickget() - tick) >= svr->cycle) { svr->prcout += fobs[0] - i - 1; #if 0 /* omitted v.2.4.1 */ break; #endif } } /* send null solution if no solution (1hz) */ if (svr->rtk.sol.stat == SOLQ_NONE && cycle % (1000 / svr->cycle) == 0) { writesol(svr, 0); } /* send nmea request to base/nrtk input stream */ if (svr->nmeacycle > 0 && static_cast(tick - ticknmea) >= svr->nmeacycle) { if (svr->stream[1].state == 1) { if (svr->nmeareq == 1) { strsendnmea(svr->stream + 1, svr->nmeapos); } else if (svr->nmeareq == 2 && norm_rtk(svr->rtk.sol.rr, 3) > 0.0) { strsendnmea(svr->stream + 1, svr->rtk.sol.rr); } } ticknmea = tick; } if ((cputime = static_cast(tickget() - tick)) > 0) { svr->cputime = cputime; } /* sleep until next cycle */ sleepms(svr->cycle - cputime); } for (i = 0; i < MAXSTRRTK; i++) { strclose(svr->stream + i); } for (i = 0; i < 3; i++) { svr->nb[i] = svr->npb[i] = 0; free(svr->buff[i]); svr->buff[i] = nullptr; free(svr->pbuf[i]); svr->pbuf[i] = nullptr; //free_raw (svr->raw +i); free_rtcm(svr->rtcm + i); } for (i = 0; i < 2; i++) { svr->nsb[i] = 0; free(svr->sbuf[i]); svr->sbuf[i] = nullptr; } return nullptr; } /* initialize rtk server ------------------------------------------------------- * initialize rtk server * args : rtksvr_t *svr IO rtk server * return : status (0:error, 1:ok) *-----------------------------------------------------------------------------*/ int rtksvrinit(rtksvr_t *svr) { gtime_t time0 = {0, 0.0}; sol_t sol0 = {{0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, '0', '0', '0', 0, 0, 0}; eph_t eph0 = {0, -1, -1, 0, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0, 0.0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, {0.0}, {0.0}, 0.0, 0.0}; geph_t geph0 = {0, -1, 0, 0, 0, 0, {0, 0.0}, {0, 0.0}, {0.0}, {0.0}, {0.0}, 0.0, 0.0, 0.0}; seph_t seph0 = {0, {0, 0.0}, {0, 0.0}, 0, 0, {0.0}, {0.0}, {0.0}, 0.0, 0.0}; int i, j; tracet(3, "rtksvrinit:\n"); svr->state = svr->cycle = svr->nmeacycle = svr->nmeareq = 0; for (i = 0; i < 3; i++) { svr->nmeapos[i] = 0.0; } svr->buffsize = 0; for (i = 0; i < 3; i++) { svr->format[i] = 0; } for (i = 0; i < 2; i++) { svr->solopt[i] = SOLOPT_DEFAULT; } svr->navsel = svr->nsbs = svr->nsol = 0; rtkinit(&svr->rtk, &PRCOPT_DEFAULT); for (i = 0; i < 3; i++) { svr->nb[i] = 0; } for (i = 0; i < 2; i++) { svr->nsb[i] = 0; } for (i = 0; i < 3; i++) { svr->npb[i] = 0; } for (i = 0; i < 3; i++) { svr->buff[i] = nullptr; } for (i = 0; i < 2; i++) { svr->sbuf[i] = nullptr; } for (i = 0; i < 3; i++) { svr->pbuf[i] = nullptr; } for (i = 0; i < MAXSOLBUF; i++) { svr->solbuf[i] = sol0; } for (i = 0; i < 3; i++) { for (j = 0; j < 10; j++) { svr->nmsg[i][j] = 0; } } for (i = 0; i < 3; i++) { svr->ftime[i] = time0; } for (i = 0; i < 3; i++) { svr->files[i][0] = '\0'; } svr->moni = nullptr; svr->tick = 0; svr->thread = 0; // NOLINT svr->cputime = svr->prcout = 0; if (!(svr->nav.eph = static_cast(malloc(sizeof(eph_t) * MAXSAT * 2))) || !(svr->nav.geph = static_cast(malloc(sizeof(geph_t) * NSATGLO * 2))) || !(svr->nav.seph = static_cast(malloc(sizeof(seph_t) * NSATSBS * 2)))) { tracet(1, "rtksvrinit: malloc error\n"); return 0; } for (i = 0; i < MAXSAT * 2; i++) { svr->nav.eph[i] = eph0; } for (i = 0; i < NSATGLO * 2; i++) { svr->nav.geph[i] = geph0; } for (i = 0; i < NSATSBS * 2; i++) { svr->nav.seph[i] = seph0; } svr->nav.n = MAXSAT * 2; svr->nav.ng = NSATGLO * 2; svr->nav.ns = NSATSBS * 2; for (i = 0; i < 3; i++) { for (j = 0; j < MAXOBSBUF; j++) { if (!(svr->obs[i][j].data = static_cast(malloc(sizeof(obsd_t) * MAXOBS)))) { tracet(1, "rtksvrinit: malloc error\n"); return 0; } } } for (i = 0; i < 3; i++) { memset(svr->raw + i, 0, sizeof(raw_t)); memset(svr->rtcm + i, 0, sizeof(rtcm_t)); } for (i = 0; i < MAXSTRRTK; i++) { strinit(svr->stream + i); } initlock(&svr->lock); return 1; } /* free rtk server ------------------------------------------------------------- * free rtk server * args : rtksvr_t *svr IO rtk server * return : none *-----------------------------------------------------------------------------*/ void rtksvrfree(rtksvr_t *svr) { int i, j; free(svr->nav.eph); free(svr->nav.geph); free(svr->nav.seph); for (i = 0; i < 3; i++) { for (j = 0; j < MAXOBSBUF; j++) { free(svr->obs[i][j].data); } } } /* lock/unlock rtk server ------------------------------------------------------ * lock/unlock rtk server * args : rtksvr_t *svr IO rtk server * return : status (1:ok 0:error) *-----------------------------------------------------------------------------*/ void rtksvrlock(rtksvr_t *svr) { rtk_lock(&svr->lock); } void rtksvrunlock(rtksvr_t *svr) { rtk_unlock(&svr->lock); } /* start rtk server ------------------------------------------------------------ * start rtk server thread * args : rtksvr_t *svr IO rtk server * int cycle I server cycle (ms) * int buffsize I input buffer size (bytes) * int *strs I stream types (STR_???) * types[0]=input stream rover * types[1]=input stream base station * types[2]=input stream correction * types[3]=output stream solution 1 * types[4]=output stream solution 2 * types[5]=log stream rover * types[6]=log stream base station * types[7]=log stream correction * char *paths I input stream paths * int *format I input stream formats (STRFMT_???) * format[0]=input stream rover * format[1]=input stream base station * format[2]=input stream correction * int navsel I navigation message select * (0:rover, 1:base, 2:ephem, 3:all) * char **cmds I input stream start commands * cmds[0]=input stream rover (NULL: no command) * cmds[1]=input stream base (NULL: no command) * cmds[2]=input stream corr (NULL: no command) * char **rcvopts I receiver options * rcvopt[0]=receiver option rover * rcvopt[1]=receiver option base * rcvopt[2]=receiver option corr * int nmeacycle I nmea request cycle (ms) (0:no request) * int nmeareq I nmea request type (0:no, 1:base pos, 2:single sol) * double *nmeapos I transmitted nmea position (ecef) (m) * prcopt_t *prcopt I rtk processing options * solopt_t *solopt I solution options * solopt[0]=solution 1 options * solopt[1]=solution 2 options * stream_t *moni I monitor stream (NULL: not used) * return : status (1:ok 0:error) *-----------------------------------------------------------------------------*/ int rtksvrstart(rtksvr_t *svr, int cycle, int buffsize, int *strs, char **paths, const int *formats, int navsel, char **cmds, char **rcvopts, int nmeacycle, int nmeareq, const double *nmeapos, prcopt_t *prcopt, solopt_t *solopt, stream_t *moni) { gtime_t time, time0 = {0, 0.0}; int i, j, rw; tracet(3, "rtksvrstart: cycle=%d buffsize=%d navsel=%d nmeacycle=%d nmeareq=%d\n", cycle, buffsize, navsel, nmeacycle, nmeareq); if (svr->state) { return 0; } strinitcom(); svr->cycle = cycle > 1 ? cycle : 1; svr->nmeacycle = nmeacycle > 1000 ? nmeacycle : 1000; svr->nmeareq = nmeareq; for (i = 0; i < 3; i++) { svr->nmeapos[i] = nmeapos[i]; } svr->buffsize = buffsize > 4096 ? buffsize : 4096; for (i = 0; i < 3; i++) { svr->format[i] = formats[i]; } svr->navsel = navsel; svr->nsbs = 0; svr->nsol = 0; svr->prcout = 0; rtkfree(&svr->rtk); rtkinit(&svr->rtk, prcopt); for (i = 0; i < 3; i++) { /* input/log streams */ svr->nb[i] = svr->npb[i] = 0; if (!(svr->buff[i] = static_cast(malloc(buffsize))) || !(svr->pbuf[i] = static_cast(malloc(buffsize)))) { tracet(1, "rtksvrstart: malloc error\n"); return 0; } for (j = 0; j < 10; j++) { svr->nmsg[i][j] = 0; } for (j = 0; j < MAXOBSBUF; j++) { svr->obs[i][j].n = 0; } /* initialize receiver raw and rtcm control */ //init_raw (svr->raw +i); init_rtcm(svr->rtcm + i); /* set receiver and rtcm option */ if (strlen(rcvopts[i]) < 256) { strcpy(svr->raw[i].opt, rcvopts[i]); } if (strlen(rcvopts[i]) < 256) { strcpy(svr->rtcm[i].opt, rcvopts[i]); } /* connect dgps corrections */ svr->rtcm[i].dgps = svr->nav.dgps; } for (i = 0; i < 2; i++) { /* output peek buffer */ if (!(svr->sbuf[i] = static_cast(malloc(buffsize)))) { tracet(1, "rtksvrstart: malloc error\n"); return 0; } } /* set solution options */ for (i = 0; i < 2; i++) { svr->solopt[i] = solopt[i]; } /* set base station position */ for (i = 0; i < 6; i++) { svr->rtk.rb[i] = i < 3 ? prcopt->rb[i] : 0.0; } /* update navigation data */ for (i = 0; i < MAXSAT * 2; i++) { svr->nav.eph[i].ttr = time0; } for (i = 0; i < NSATGLO * 2; i++) { svr->nav.geph[i].tof = time0; } for (i = 0; i < NSATSBS * 2; i++) { svr->nav.seph[i].tof = time0; } updatenav(&svr->nav); /* set monitor stream */ svr->moni = moni; /* open input streams */ for (i = 0; i < 8; i++) { rw = i < 3 ? STR_MODE_R : STR_MODE_W; if (strs[i] != STR_FILE) { rw |= STR_MODE_W; } if (!stropen(svr->stream + i, strs[i], rw, paths[i])) { for (i--; i >= 0; i--) { strclose(svr->stream + i); } return 0; } /* set initial time for rtcm and raw */ if (i < 3) { time = utc2gpst(timeget()); svr->raw[i].time = strs[i] == STR_FILE ? strgettime(svr->stream + i) : time; svr->rtcm[i].time = strs[i] == STR_FILE ? strgettime(svr->stream + i) : time; } } /* sync input streams */ strsync(svr->stream, svr->stream + 1); strsync(svr->stream, svr->stream + 2); /* write start commands to input streams */ for (i = 0; i < 3; i++) { if (cmds[i]) { strsendcmd(svr->stream + i, cmds[i]); } } /* write solution header to solution streams */ for (i = 3; i < 5; i++) { writesolhead(svr->stream + i, svr->solopt + i - 3); } /* create rtk server thread */ if (pthread_create(&svr->thread, nullptr, rtksvrthread, svr)) { for (i = 0; i < MAXSTRRTK; i++) { strclose(svr->stream + i); } return 0; } return 1; } /* stop rtk server ------------------------------------------------------------- * start rtk server thread * args : rtksvr_t *svr IO rtk server * char **cmds I input stream stop commands * cmds[0]=input stream rover (NULL: no command) * cmds[1]=input stream base (NULL: no command) * cmds[2]=input stream ephem (NULL: no command) * return : none *-----------------------------------------------------------------------------*/ void rtksvrstop(rtksvr_t *svr, char **cmds) { int i; tracet(3, "rtksvrstop:\n"); /* write stop commands to input streams */ rtksvrlock(svr); for (i = 0; i < 3; i++) { if (cmds[i]) { strsendcmd(svr->stream + i, cmds[i]); } } rtksvrunlock(svr); /* stop rtk server */ svr->state = 0; /* free rtk server thread */ pthread_join(svr->thread, nullptr); } /* open output/log stream ------------------------------------------------------ * open output/log stream * args : rtksvr_t *svr IO rtk server * int index I output/log stream index * (3:solution 1, 4:solution 2, 5:log rover, * 6:log base station, 7:log correction) * int str I output/log stream types (STR_???) * char *path I output/log stream path * solopt_t *solopt I solution options * return : status (1:ok 0:error) *-----------------------------------------------------------------------------*/ int rtksvropenstr(rtksvr_t *svr, int index, int str, const char *path, const solopt_t *solopt) { tracet(3, "rtksvropenstr: index=%d str=%d path=%s\n", index, str, path); if (index < 3 || index > 7 || !svr->state) { return 0; } rtksvrlock(svr); if (svr->stream[index].state > 0) { rtksvrunlock(svr); return 0; } if (!stropen(svr->stream + index, str, STR_MODE_W, path)) { tracet(2, "stream open error: index=%d\n", index); rtksvrunlock(svr); return 0; } if (index <= 4) { svr->solopt[index - 3] = *solopt; /* write solution header to solution stream */ writesolhead(svr->stream + index, svr->solopt + index - 3); } rtksvrunlock(svr); return 1; } /* close output/log stream ----------------------------------------------------- * close output/log stream * args : rtksvr_t *svr IO rtk server * int index I output/log stream index * (3:solution 1, 4:solution 2, 5:log rover, * 6:log base station, 7:log correction) * return : none *-----------------------------------------------------------------------------*/ void rtksvrclosestr(rtksvr_t *svr, int index) { tracet(3, "rtksvrclosestr: index=%d\n", index); if (index < 3 || index > 7 || !svr->state) { return; } rtksvrlock(svr); strclose(svr->stream + index); rtksvrunlock(svr); } /* get observation data status ------------------------------------------------- * get current observation data status * args : rtksvr_t *svr I rtk server * int rcv I receiver (0:rover, 1:base, 2:ephem) * gtime_t *time O time of observation data * int *sat O satellite prn numbers * double *az O satellite azimuth angles (rad) * double *el O satellite elevation angles (rad) * int **snr O satellite snr for each freq (dBHz) * snr[i][j] = sat i freq j snr * int *vsat O valid satellite flag * return : number of satellites *-----------------------------------------------------------------------------*/ int rtksvrostat(rtksvr_t *svr, int rcv, gtime_t *time, int *sat, double *az, double *el, int **snr, int *vsat) { int i, j, ns; tracet(4, "rtksvrostat: rcv=%d\n", rcv); if (!svr->state) { return 0; } rtksvrlock(svr); ns = svr->obs[rcv][0].n; if (ns > 0) { *time = svr->obs[rcv][0].data[0].time; } for (i = 0; i < ns; i++) { sat[i] = svr->obs[rcv][0].data[i].sat; az[i] = svr->rtk.ssat[sat[i] - 1].azel[0]; el[i] = svr->rtk.ssat[sat[i] - 1].azel[1]; for (j = 0; j < NFREQ; j++) { snr[i][j] = static_cast(svr->obs[rcv][0].data[i].SNR[j] * 0.25); } if (svr->rtk.sol.stat == SOLQ_NONE || svr->rtk.sol.stat == SOLQ_SINGLE) { vsat[i] = svr->rtk.ssat[sat[i] - 1].vs; } else { vsat[i] = svr->rtk.ssat[sat[i] - 1].vsat[0]; } } rtksvrunlock(svr); return ns; } /* get stream status ----------------------------------------------------------- * get current stream status * args : rtksvr_t *svr I rtk server * int *sstat O status of streams * char *msg O status messages * return : none *-----------------------------------------------------------------------------*/ void rtksvrsstat(rtksvr_t *svr, int *sstat, char *msg) { int i; char s[MAXSTRMSG], *p = msg; tracet(4, "rtksvrsstat:\n"); rtksvrlock(svr); for (i = 0; i < MAXSTRRTK; i++) { sstat[i] = strstat(svr->stream + i, s); if (*s) { p += sprintf(p, "(%d) %s ", i + 1, s); } } rtksvrunlock(svr); } src/algorithms/libs/rtklib/rtklib_rtksvr.h000066400000000000000000000127451352176506000213270ustar00rootroot00000000000000/*! * \file rtklib_rtksvr.h * \brief rtk server functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_RKTSVR_H_ #define GNSS_SDR_RTKLIB_RKTSVR_H_ #include "rtklib.h" const solopt_t SOLOPT_DEFAULT = { /* defaults solution output options */ SOLF_LLH, TIMES_GPST, 1, 3, /* posf, times, timef, timeu */ 0, 1, 0, 0, 0, 0, /* degf, outhead, outopt, datum, height, geoid */ 0, 0, 0, /* solstatic, sstat, trace */ {0.0, 0.0}, /* nmeaintv */ " ", "", 0 /* separator/program name */ }; const prcopt_t PRCOPT_DEFAULT = { /* defaults processing options */ PMODE_SINGLE, 0, 2, SYS_GPS, /* mode, soltype, nf, navsys */ 15.0 * D2R, {{}, {{}, {}}}, /* elmin, snrmask */ 0, 1, 1, 1, /* sateph, modear, glomodear, bdsmodear */ 5, 0, 10, 1, /* maxout, minlock, minfix, armaxiter */ 0, 0, 0, 0, /* estion, esttrop, dynamics, tidecorr */ 1, 0, 0, 0, 0, /* niter, codesmooth, intpref, sbascorr, sbassatsel */ 0, 0, /* rovpos, refpos */ {100.0, 100.0, 100.0}, /* eratio[] */ {100.0, 0.003, 0.003, 0.0, 1.0}, /* err[] */ {30.0, 0.03, 0.3}, /* std[] */ {1e-4, 1e-3, 1e-4, 1e-1, 1e-2, 0.0}, /* prn[] */ 5E-12, /* sclkstab */ {3.0, 0.9999, 0.25, 0.1, 0.05, 0, 0, 0}, /* thresar */ 0.0, 0.0, 0.05, /* elmaskar, almaskhold, thresslip */ 30.0, 30.0, 30.0, /* maxtdif, maxinno, maxgdop */ {}, {}, {}, /* baseline, ru, rb */ {"", ""}, /* anttype */ {}, {}, {}, /* antdel, pcv, exsats */ 0, 0, 0, {"", ""}, {}, 0, {{}, {}}, {{}, {{}, {}}, {{}, {}}, {}, {}}, 0, {}}; void writesolhead(stream_t *stream, const solopt_t *solopt); void saveoutbuf(rtksvr_t *svr, unsigned char *buff, int n, int index); void writesol(rtksvr_t *svr, int index); void updatenav(nav_t *nav); void updatefcn(rtksvr_t *svr); void updatesvr(rtksvr_t *svr, int ret, obs_t *obs, nav_t *nav, int sat, sbsmsg_t *sbsmsg, int index, int iobs); int decoderaw(rtksvr_t *svr, int index); void decodefile(rtksvr_t *svr, int index); void *rtksvrthread(void *arg); int rtksvrinit(rtksvr_t *svr); void rtksvrfree(rtksvr_t *svr); void rtksvrlock(rtksvr_t *svr); void rtksvrunlock(rtksvr_t *svr); int rtksvrstart(rtksvr_t *svr, int cycle, int buffsize, int *strs, char **paths, const int *formats, int navsel, char **cmds, char **rcvopts, int nmeacycle, int nmeareq, const double *nmeapos, prcopt_t *prcopt, solopt_t *solopt, stream_t *moni); void rtksvrstop(rtksvr_t *svr, char **cmds); int rtksvropenstr(rtksvr_t *svr, int index, int str, const char *path, const solopt_t *solopt); void rtksvrclosestr(rtksvr_t *svr, int index); int rtksvrostat(rtksvr_t *svr, int rcv, gtime_t *time, int *sat, double *az, double *el, int **snr, int *vsat); void rtksvrsstat(rtksvr_t *svr, int *sstat, char *msg); #endif src/algorithms/libs/rtklib/rtklib_sbas.cc000066400000000000000000001226511352176506000210600ustar00rootroot00000000000000/*! * \file rtklib_sbas.cc * \brief sbas functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * References : * [1] RTCA/DO-229C, Minimum operational performanc standards for global * positioning system/wide area augmentation system airborne equipment, * RTCA inc, November 28, 2001 * [2] IS-QZSS v.1.1, Quasi-Zenith Satellite System Navigation Service * Interface Specification for QZSS, Japan Aerospace Exploration Agency, * July 31, 2009 * *----------------------------------------------------------------------------*/ #include "rtklib_sbas.h" #include "rtklib_rtkcmn.h" #include /* extract field from line ---------------------------------------------------*/ char *getfield(char *p, int pos) { for (pos--; pos > 0; pos--, p++) { if (!(p = strchr(p, ','))) { return nullptr; } } return p; } /* variance of fast correction (udre=UDRE+1) ---------------------------------*/ double varfcorr(int udre) { const double var[14] = { 0.052, 0.0924, 0.1444, 0.283, 0.4678, 0.8315, 1.2992, 1.8709, 2.5465, 3.326, 5.1968, 20.7870, 230.9661, 2078.695}; return 0 < udre && udre <= 14 ? var[udre - 1] : 0.0; } /* variance of ionosphere correction (give=GIVEI+1) --------------------------*/ double varicorr(int give) { const double var[15] = { 0.0084, 0.0333, 0.0749, 0.1331, 0.2079, 0.2994, 0.4075, 0.5322, 0.6735, 0.8315, 1.1974, 1.8709, 3.326, 20.787, 187.0826}; return 0 < give && give <= 15 ? var[give - 1] : 0.0; } /* fast correction degradation -----------------------------------------------*/ double degfcorr(int ai) { const double degf[16] = { 0.00000, 0.00005, 0.00009, 0.00012, 0.00015, 0.00020, 0.00030, 0.00045, 0.00060, 0.00090, 0.00150, 0.00210, 0.00270, 0.00330, 0.00460, 0.00580}; return 0 < ai && ai <= 15 ? degf[ai] : 0.0058; } /* decode type 1: prn masks --------------------------------------------------*/ int decode_sbstype1(const sbsmsg_t *msg, sbssat_t *sbssat) { int i, n, sat; trace(4, "decode_sbstype1:\n"); for (i = 1, n = 0; i <= 210 && n < MAXSAT; i++) { if (getbitu(msg->msg, 13 + i, 1)) { if (i <= 37) { sat = satno(SYS_GPS, i); /* 0- 37: gps */ } else if (i <= 61) { sat = satno(SYS_GLO, i - 37); /* 38- 61: glonass */ } else if (i <= 119) { sat = 0; /* 62-119: future gnss */ } else if (i <= 138) { sat = satno(SYS_SBS, i); /* 120-138: geo/waas */ } else if (i <= 182) { sat = 0; /* 139-182: reserved */ } else if (i <= 192) { sat = satno(SYS_SBS, i + 10); /* 183-192: qzss ref [2] */ } else if (i <= 202) { sat = satno(SYS_QZS, i); /* 193-202: qzss ref [2] */ } else { sat = 0; /* 203- : reserved */ } sbssat->sat[n++].sat = sat; } } sbssat->iodp = getbitu(msg->msg, 224, 2); sbssat->nsat = n; trace(5, "decode_sbstype1: nprn=%d iodp=%d\n", n, sbssat->iodp); return 1; } /* decode type 2-5,0: fast corrections ---------------------------------------*/ int decode_sbstype2(const sbsmsg_t *msg, sbssat_t *sbssat) { int i, j, iodf, type, udre; double prc, dt; gtime_t t0; trace(4, "decode_sbstype2:\n"); if (sbssat->iodp != static_cast(getbitu(msg->msg, 16, 2))) { return 0; } type = getbitu(msg->msg, 8, 6); iodf = getbitu(msg->msg, 14, 2); for (i = 0; i < 13; i++) { if ((j = 13 * ((type == 0 ? 2 : type) - 2) + i) >= sbssat->nsat) { break; } udre = getbitu(msg->msg, 174 + 4 * i, 4); t0 = sbssat->sat[j].fcorr.t0; prc = sbssat->sat[j].fcorr.prc; sbssat->sat[j].fcorr.t0 = gpst2time(msg->week, msg->tow); sbssat->sat[j].fcorr.prc = getbits(msg->msg, 18 + i * 12, 12) * 0.125F; sbssat->sat[j].fcorr.udre = udre + 1; dt = timediff(sbssat->sat[j].fcorr.t0, t0); if (t0.time == 0 || dt <= 0.0 || 18.0 < dt || sbssat->sat[j].fcorr.ai == 0) { sbssat->sat[j].fcorr.rrc = 0.0; sbssat->sat[j].fcorr.dt = 0.0; } else { sbssat->sat[j].fcorr.rrc = (sbssat->sat[j].fcorr.prc - prc) / dt; sbssat->sat[j].fcorr.dt = dt; } sbssat->sat[j].fcorr.iodf = iodf; } trace(5, "decode_sbstype2: type=%d iodf=%d\n", type, iodf); return 1; } /* decode type 6: integrity info ---------------------------------------------*/ int decode_sbstype6(const sbsmsg_t *msg, sbssat_t *sbssat) { int i, iodf[4], udre; trace(4, "decode_sbstype6:\n"); for (i = 0; i < 4; i++) { iodf[i] = getbitu(msg->msg, 14 + i * 2, 2); } for (i = 0; i < sbssat->nsat && i < MAXSAT; i++) { if (sbssat->sat[i].fcorr.iodf != iodf[i / 39]) { continue; } udre = getbitu(msg->msg, 22 + i * 4, 4); sbssat->sat[i].fcorr.udre = udre + 1; } trace(5, "decode_sbstype6: iodf=%d %d %d %d\n", iodf[0], iodf[1], iodf[2], iodf[3]); return 1; } /* decode type 7: fast correction degradation factor -------------------------*/ int decode_sbstype7(const sbsmsg_t *msg, sbssat_t *sbssat) { int i; trace(4, "decode_sbstype7\n"); if (sbssat->iodp != static_cast(getbitu(msg->msg, 18, 2))) { return 0; } sbssat->tlat = getbitu(msg->msg, 14, 4); for (i = 0; i < sbssat->nsat && i < MAXSAT; i++) { sbssat->sat[i].fcorr.ai = getbitu(msg->msg, 22 + i * 4, 4); } return 1; } /* decode type 9: geo navigation message -------------------------------------*/ int decode_sbstype9(const sbsmsg_t *msg, nav_t *nav) { seph_t seph = {0, {0, 0}, {0, 0}, 0, 0, {}, {}, {}, 0.0, 0.0}; int i, sat, t; trace(4, "decode_sbstype9:\n"); if (!(sat = satno(SYS_SBS, msg->prn))) { trace(2, "invalid prn in sbas type 9: prn=%3d\n", msg->prn); return 0; } t = static_cast(getbitu(msg->msg, 22, 13)) * 16 - msg->tow % 86400; if (t <= -43200) { t += 86400; } else if (t > 43200) { t -= 86400; } seph.sat = sat; seph.t0 = gpst2time(msg->week, msg->tow + t); seph.tof = gpst2time(msg->week, msg->tow); seph.sva = getbitu(msg->msg, 35, 4); seph.svh = seph.sva == 15 ? 1 : 0; /* unhealthy if ura==15 */ seph.pos[0] = getbits(msg->msg, 39, 30) * 0.08; seph.pos[1] = getbits(msg->msg, 69, 30) * 0.08; seph.pos[2] = getbits(msg->msg, 99, 25) * 0.4; seph.vel[0] = getbits(msg->msg, 124, 17) * 0.000625; seph.vel[1] = getbits(msg->msg, 141, 17) * 0.000625; seph.vel[2] = getbits(msg->msg, 158, 18) * 0.004; seph.acc[0] = getbits(msg->msg, 176, 10) * 0.0000125; seph.acc[1] = getbits(msg->msg, 186, 10) * 0.0000125; seph.acc[2] = getbits(msg->msg, 196, 10) * 0.0000625; seph.af0 = getbits(msg->msg, 206, 12) * TWO_N31; seph.af1 = getbits(msg->msg, 218, 8) * TWO_N39 / 2.0; i = msg->prn - MINPRNSBS; if (!nav->seph || fabs(timediff(nav->seph[i].t0, seph.t0)) < 1e-3) { /* not change */ return 0; } nav->seph[NSATSBS + i] = nav->seph[i]; /* previous */ nav->seph[i] = seph; /* current */ trace(5, "decode_sbstype9: prn=%d\n", msg->prn); return 1; } /* decode type 18: ionospheric grid point masks ------------------------------*/ int decode_sbstype18(const sbsmsg_t *msg, sbsion_t *sbsion) { const sbsigpband_t *p; int i, j, n, m, band = getbitu(msg->msg, 18, 4); trace(4, "decode_sbstype18:\n"); if (0 <= band && band <= 8) { p = IGPBAND1[band]; m = 8; } else if (9 <= band && band <= 10) { p = IGPBAND2[band - 9]; m = 5; } else { return 0; } sbsion[band].iodi = static_cast(getbitu(msg->msg, 22, 2)); for (i = 1, n = 0; i <= 201; i++) { if (!getbitu(msg->msg, 23 + i, 1)) { continue; } for (j = 0; j < m; j++) { if (i < p[j].bits || p[j].bite < i) { continue; } sbsion[band].igp[n].lat = band <= 8 ? p[j].y[i - p[j].bits] : p[j].x; sbsion[band].igp[n++].lon = band <= 8 ? p[j].x : p[j].y[i - p[j].bits]; break; } } sbsion[band].nigp = n; trace(5, "decode_sbstype18: band=%d nigp=%d\n", band, n); return 1; } /* decode half long term correction (vel code=0) -----------------------------*/ int decode_longcorr0(const sbsmsg_t *msg, int p, sbssat_t *sbssat) { int i, n = getbitu(msg->msg, p, 6); trace(4, "decode_longcorr0:\n"); if (n == 0 || n > MAXSAT) { return 0; } sbssat->sat[n - 1].lcorr.iode = getbitu(msg->msg, p + 6, 8); for (i = 0; i < 3; i++) { sbssat->sat[n - 1].lcorr.dpos[i] = getbits(msg->msg, p + 14 + 9 * i, 9) * 0.125; sbssat->sat[n - 1].lcorr.dvel[i] = 0.0; } sbssat->sat[n - 1].lcorr.daf0 = getbits(msg->msg, p + 41, 10) * TWO_N31; sbssat->sat[n - 1].lcorr.daf1 = 0.0; sbssat->sat[n - 1].lcorr.t0 = gpst2time(msg->week, msg->tow); trace(5, "decode_longcorr0:sat=%2d\n", sbssat->sat[n - 1].sat); return 1; } /* decode half long term correction (vel code=1) -----------------------------*/ int decode_longcorr1(const sbsmsg_t *msg, int p, sbssat_t *sbssat) { int i, n = getbitu(msg->msg, p, 6), t; trace(4, "decode_longcorr1:\n"); if (n == 0 || n > MAXSAT) { return 0; } sbssat->sat[n - 1].lcorr.iode = getbitu(msg->msg, p + 6, 8); for (i = 0; i < 3; i++) { sbssat->sat[n - 1].lcorr.dpos[i] = getbits(msg->msg, p + 14 + i * 11, 11) * 0.125; sbssat->sat[n - 1].lcorr.dvel[i] = getbits(msg->msg, p + 58 + i * 8, 8) * TWO_N11; } sbssat->sat[n - 1].lcorr.daf0 = getbits(msg->msg, p + 47, 11) * TWO_N31; sbssat->sat[n - 1].lcorr.daf1 = getbits(msg->msg, p + 82, 8) * TWO_N39; t = static_cast(getbitu(msg->msg, p + 90, 13)) * 16 - msg->tow % 86400; if (t <= -43200) { t += 86400; } else if (t > 43200) { t -= 86400; } sbssat->sat[n - 1].lcorr.t0 = gpst2time(msg->week, msg->tow + t); trace(5, "decode_longcorr1: sat=%2d\n", sbssat->sat[n - 1].sat); return 1; } /* decode half long term correction ------------------------------------------*/ int decode_longcorrh(const sbsmsg_t *msg, int p, sbssat_t *sbssat) { trace(4, "decode_longcorrh:\n"); if (getbitu(msg->msg, p, 1) == 0) { /* vel code=0 */ if (sbssat->iodp == static_cast(getbitu(msg->msg, p + 103, 2))) { return decode_longcorr0(msg, p + 1, sbssat) && decode_longcorr0(msg, p + 52, sbssat); } } else if (sbssat->iodp == static_cast(getbitu(msg->msg, p + 104, 2))) { return decode_longcorr1(msg, p + 1, sbssat); } return 0; } /* decode type 24: mixed fast/long term correction ---------------------------*/ int decode_sbstype24(const sbsmsg_t *msg, sbssat_t *sbssat) { int i, j, iodf, blk, udre; trace(4, "decode_sbstype24:\n"); if (sbssat->iodp != static_cast(getbitu(msg->msg, 110, 2))) { return 0; /* check IODP */ } blk = getbitu(msg->msg, 112, 2); iodf = getbitu(msg->msg, 114, 2); for (i = 0; i < 6; i++) { if ((j = 13 * blk + i) >= sbssat->nsat) { break; } udre = getbitu(msg->msg, 86 + 4 * i, 4); sbssat->sat[j].fcorr.t0 = gpst2time(msg->week, msg->tow); sbssat->sat[j].fcorr.prc = getbits(msg->msg, 14 + i * 12, 12) * 0.125F; sbssat->sat[j].fcorr.udre = udre + 1; sbssat->sat[j].fcorr.iodf = iodf; } return decode_longcorrh(msg, 120, sbssat); } /* decode type 25: long term satellite error correction ----------------------*/ int decode_sbstype25(const sbsmsg_t *msg, sbssat_t *sbssat) { trace(4, "decode_sbstype25:\n"); return decode_longcorrh(msg, 14, sbssat) && decode_longcorrh(msg, 120, sbssat); } /* decode type 26: ionospheric deley corrections -----------------------------*/ int decode_sbstype26(const sbsmsg_t *msg, sbsion_t *sbsion) { int i, j, block, delay, give, band = getbitu(msg->msg, 14, 4); trace(4, "decode_sbstype26:\n"); if (band > MAXBAND || sbsion[band].iodi != static_cast(getbitu(msg->msg, 217, 2))) { return 0; } block = getbitu(msg->msg, 18, 4); for (i = 0; i < 15; i++) { if ((j = block * 15 + i) >= sbsion[band].nigp) { continue; } give = getbitu(msg->msg, 22 + i * 13 + 9, 4); delay = getbitu(msg->msg, 22 + i * 13, 9); sbsion[band].igp[j].t0 = gpst2time(msg->week, msg->tow); sbsion[band].igp[j].delay = delay == 0x1FF ? 0.0F : delay * 0.125F; sbsion[band].igp[j].give = give + 1; if (sbsion[band].igp[j].give >= 16) { sbsion[band].igp[j].give = 0; } } trace(5, "decode_sbstype26: band=%d block=%d\n", band, block); return 1; } /* update sbas corrections ----------------------------------------------------- * update sbas correction parameters in navigation data with a sbas message * args : sbsmg_t *msg I sbas message * nav_t *nav IO navigation data * return : message type (-1: error or not supported type) * notes : nav->seph must point to seph[NSATSBS*2] (array of seph_t) * seph[prn-MINPRNSBS+1] : sat prn current epehmeris * seph[prn-MINPRNSBS+1+MAXPRNSBS]: sat prn previous epehmeris *-----------------------------------------------------------------------------*/ int sbsupdatecorr(const sbsmsg_t *msg, nav_t *nav) { int type = getbitu(msg->msg, 8, 6), stat = -1; trace(3, "sbsupdatecorr: type=%d\n", type); if (msg->week == 0) { return -1; } switch (type) { case 0: stat = decode_sbstype2(msg, &nav->sbssat); break; case 1: stat = decode_sbstype1(msg, &nav->sbssat); break; case 2: case 3: case 4: case 5: stat = decode_sbstype2(msg, &nav->sbssat); break; case 6: stat = decode_sbstype6(msg, &nav->sbssat); break; case 7: stat = decode_sbstype7(msg, &nav->sbssat); break; case 9: stat = decode_sbstype9(msg, nav); break; case 18: stat = decode_sbstype18(msg, nav->sbsion); break; case 24: stat = decode_sbstype24(msg, &nav->sbssat); break; case 25: stat = decode_sbstype25(msg, &nav->sbssat); break; case 26: stat = decode_sbstype26(msg, nav->sbsion); break; case 63: break; /* null message */ /*default: trace(2, "unsupported sbas message: type=%d\n", type); break;*/ } return stat ? type : -1; } /* read sbas log file --------------------------------------------------------*/ void readmsgs(const char *file, int sel, gtime_t ts, gtime_t te, sbs_t *sbs) { sbsmsg_t *sbs_msgs; int i, week, prn, ch, msg; unsigned int b; double tow, ep[6] = {}; char buff[256], *p; gtime_t time; FILE *fp; trace(3, "readmsgs: file=%s sel=%d\n", file, sel); if (!(fp = fopen(file, "re"))) { trace(2, "sbas message file open error: %s\n", file); return; } while (fgets(buff, sizeof(buff), fp)) { if (sscanf(buff, "%d %lf %d", &week, &tow, &prn) == 3 && (p = strstr(buff, ": "))) { p += 2; /* rtklib form */ } else if (sscanf(buff, "%d %lf %lf %lf %lf %lf %lf %d", &prn, ep, ep + 1, ep + 2, ep + 3, ep + 4, ep + 5, &msg) == 8) { /* ems (EGNOS Message Service) form */ ep[0] += ep[0] < 70.0 ? 2000.0 : 1900.0; tow = time2gpst(epoch2time(ep), &week); p = buff + (msg >= 10 ? 25 : 24); } else if (!strncmp(buff, "#RAWWAASFRAMEA", 14)) { /* NovAtel OEM4/V */ if (!(p = getfield(buff, 6))) { continue; } if (sscanf(p, "%d,%lf", &week, &tow) < 2) { continue; } if (!(p = strchr(++p, ';'))) { continue; } if (sscanf(++p, "%d,%d", &ch, &prn) < 2) { continue; } if (!(p = getfield(p, 4))) { continue; } } else if (!strncmp(buff, "$FRMA", 5)) { /* NovAtel OEM3 */ if (!(p = getfield(buff, 2))) { continue; } if (sscanf(p, "%d,%lf,%d", &week, &tow, &prn) < 3) { continue; } if (!(p = getfield(p, 6))) { continue; } if (week < WEEKOFFSET) { week += WEEKOFFSET; } } else { continue; } if (sel != 0 && sel != prn) { continue; } time = gpst2time(week, tow); if (!screent(time, ts, te, 0.0)) { continue; } if (sbs->n >= sbs->nmax) { sbs->nmax = sbs->nmax == 0 ? 1024 : sbs->nmax * 2; if (!(sbs_msgs = static_cast(realloc(sbs->msgs, sbs->nmax * sizeof(sbsmsg_t))))) { trace(1, "readsbsmsg malloc error: nmax=%d\n", sbs->nmax); free(sbs->msgs); sbs->msgs = nullptr; sbs->n = sbs->nmax = 0; fclose(fp); return; } sbs->msgs = sbs_msgs; } sbs->msgs[sbs->n].week = week; sbs->msgs[sbs->n].tow = static_cast(tow + 0.5); sbs->msgs[sbs->n].prn = prn; for (i = 0; i < 29; i++) { sbs->msgs[sbs->n].msg[i] = 0; } for (i = 0; *(p - 1) && *p && i < 29; p += 2, i++) { if (sscanf(p, "%2X", &b) == 1) { sbs->msgs[sbs->n].msg[i] = static_cast(b); } } sbs->msgs[sbs->n++].msg[28] &= 0xC0; } fclose(fp); } /* compare sbas messages -----------------------------------------------------*/ int cmpmsgs(const void *p1, const void *p2) { auto *q1 = (sbsmsg_t *)p1, *q2 = (sbsmsg_t *)p2; return q1->week != q2->week ? q1->week - q2->week : (q1->tow < q2->tow ? -1 : (q1->tow > q2->tow ? 1 : q1->prn - q2->prn)); } /* read sbas message file ------------------------------------------------------ * read sbas message file * args : char *file I sbas message file (wind-card * is expanded) * int sel I sbas satellite prn number selection (0:all) * (gtime_t ts I start time) * (gtime_t te I end time ) * sbs_t *sbs IO sbas messages * return : number of sbas messages * notes : sbas message are appended and sorted. before calling the function, * sbs->n, sbs->nmax and sbs->msgs must be set properly. (initially * sbs->n=sbs->nmax=0, sbs->msgs=NULL) * only the following file extensions after wild card expanded are valid * to read. others are skipped * .sbs, .SBS, .ems, .EMS *-----------------------------------------------------------------------------*/ int sbsreadmsgt(const char *file, int sel, gtime_t ts, gtime_t te, sbs_t *sbs) { char *efiles[MAXEXFILE] = {}, *ext; int i, n; trace(3, "sbsreadmsgt: file=%s sel=%d\n", file, sel); for (i = 0; i < MAXEXFILE; i++) { if (!(efiles[i] = static_cast(malloc(1024)))) { for (i--; i >= 0; i--) { free(efiles[i]); } return 0; } } /* expand wild card in file path */ n = expath(file, efiles, MAXEXFILE); for (i = 0; i < n; i++) { if (!(ext = strrchr(efiles[i], '.'))) { continue; } if (strcmp(ext, ".sbs") != 0 && strcmp(ext, ".SBS") != 0 && strcmp(ext, ".ems") != 0 && strcmp(ext, ".EMS") != 0) { continue; } readmsgs(efiles[i], sel, ts, te, sbs); } for (i = 0; i < MAXEXFILE; i++) { free(efiles[i]); } /* sort messages */ if (sbs->n > 0) { qsort(sbs->msgs, sbs->n, sizeof(sbsmsg_t), cmpmsgs); } return sbs->n; } int sbsreadmsg(const char *file, int sel, sbs_t *sbs) { gtime_t ts = {0, 0}, te = {0, 0}; trace(3, "sbsreadmsg: file=%s sel=%d\n", file, sel); return sbsreadmsgt(file, sel, ts, te, sbs); } /* output sbas messages -------------------------------------------------------- * output sbas message record to output file in rtklib sbas log format * args : FILE *fp I output file pointer * sbsmsg_t *sbsmsg I sbas messages * return : none *-----------------------------------------------------------------------------*/ void sbsoutmsg(FILE *fp, sbsmsg_t *sbsmsg) { int i, type = sbsmsg->msg[1] >> 2; trace(4, "sbsoutmsg:\n"); fprintf(fp, "%4d %6d %3d %2d : ", sbsmsg->week, sbsmsg->tow, sbsmsg->prn, type); for (i = 0; i < 29; i++) { fprintf(fp, "%02X", sbsmsg->msg[i]); } fprintf(fp, "\n"); } /* search igps ---------------------------------------------------------------*/ void searchigp(gtime_t time __attribute__((unused)), const double *pos, const sbsion_t *ion, const sbsigp_t **igp, double *x, double *y) { int i, latp[2], lonp[4]; double lat = pos[0] * R2D, lon = pos[1] * R2D; const sbsigp_t *p; trace(4, "searchigp: pos=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D); if (lon >= 180.0) { lon -= 360.0; } if (-55.0 <= lat && lat < 55.0) { latp[0] = static_cast(floor(lat / 5.0)) * 5; latp[1] = latp[0] + 5; lonp[0] = lonp[1] = static_cast(floor(lon / 5.0)) * 5; lonp[2] = lonp[3] = lonp[0] + 5; *x = (lon - lonp[0]) / 5.0; *y = (lat - latp[0]) / 5.0; } else { latp[0] = static_cast(floor((lat - 5.0) / 10.0)) * 10 + 5; latp[1] = latp[0] + 10; lonp[0] = lonp[1] = static_cast(floor(lon / 10.0)) * 10; lonp[2] = lonp[3] = lonp[0] + 10; *x = (lon - lonp[0]) / 10.0; *y = (lat - latp[0]) / 10.0; if (75.0 <= lat && lat < 85.0) { lonp[1] = static_cast(floor(lon / 90.0)) * 90; lonp[3] = lonp[1] + 90; } else if (-85.0 <= lat && lat < -75.0) { lonp[0] = static_cast(floor((lon - 50.0) / 90.0)) * 90 + 40; lonp[2] = lonp[0] + 90; } else if (lat >= 85.0) { for (i = 0; i < 4; i++) { lonp[i] = static_cast(floor(lon / 90.0)) * 90; } } else if (lat < -85.0) { for (i = 0; i < 4; i++) { lonp[i] = static_cast(floor((lon - 50.0) / 90.0)) * 90 + 40; } } } for (i = 0; i < 4; i++) { if (lonp[i] == 180) { lonp[i] = -180; } } for (i = 0; i <= MAXBAND; i++) { for (p = ion[i].igp; p < ion[i].igp + ion[i].nigp; p++) { if (p->t0.time == 0) { continue; } if (p->lat == latp[0] && p->lon == lonp[0] && p->give > 0) { igp[0] = p; } else if (p->lat == latp[1] && p->lon == lonp[1] && p->give > 0) { igp[1] = p; } else if (p->lat == latp[0] && p->lon == lonp[2] && p->give > 0) { igp[2] = p; } else if (p->lat == latp[1] && p->lon == lonp[3] && p->give > 0) { igp[3] = p; } if (igp[0] && igp[1] && igp[2] && igp[3]) { return; } } } } /* sbas ionospheric delay correction ------------------------------------------- * compute sbas ionosphric delay correction * args : gtime_t time I time * nav_t *nav I navigation data * double *pos I receiver position {lat,lon,height} (rad/m) * double *azel I satellite azimuth/elavation angle (rad) * double *delay O slant ionospheric delay (L1) (m) * double *var O variance of ionospheric delay (m^2) * return : status (1:ok, 0:no correction) * notes : before calling the function, sbas ionosphere correction parameters * in navigation data (nav->sbsion) must be set by calling * sbsupdatecorr() *-----------------------------------------------------------------------------*/ int sbsioncorr(gtime_t time, const nav_t *nav, const double *pos, const double *azel, double *delay, double *var) { const double re = 6378.1363, hion = 350.0; int i, err = 0; double fp, posp[2], x = 0.0, y = 0.0, t, w[4] = {}; const sbsigp_t *igp[4] = {}; /* {ws,wn,es,en} */ trace(4, "sbsioncorr: pos=%.3f %.3f azel=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); *delay = *var = 0.0; if (pos[2] < -100.0 || azel[1] <= 0) { return 1; } /* ipp (ionospheric pierce point) position */ fp = ionppp(pos, azel, re, hion, posp); /* search igps around ipp */ searchigp(time, posp, nav->sbsion, igp, &x, &y); /* weight of igps */ if (igp[0] && igp[1] && igp[2] && igp[3]) { w[0] = (1.0 - x) * (1.0 - y); w[1] = (1.0 - x) * y; w[2] = x * (1.0 - y); w[3] = x * y; } else if (igp[0] && igp[1] && igp[2]) { w[1] = y; w[2] = x; if ((w[0] = 1.0 - w[1] - w[2]) < 0.0) { err = 1; } } else if (igp[0] && igp[2] && igp[3]) { w[0] = 1.0 - x; w[3] = y; if ((w[2] = 1.0 - w[0] - w[3]) < 0.0) { err = 1; } } else if (igp[0] && igp[1] && igp[3]) { w[0] = 1.0 - y; w[3] = x; if ((w[1] = 1.0 - w[0] - w[3]) < 0.0) { err = 1; } } else if (igp[1] && igp[2] && igp[3]) { w[1] = 1.0 - x; w[2] = 1.0 - y; if ((w[3] = 1.0 - w[1] - w[2]) < 0.0) { err = 1; } } else { err = 1; } if (err) { trace(2, "no sbas iono correction: lat=%3.0f lon=%4.0f\n", posp[0] * R2D, posp[1] * R2D); return 0; } for (i = 0; i < 4; i++) { if (!igp[i]) { continue; } t = timediff(time, igp[i]->t0); *delay += w[i] * igp[i]->delay; *var += w[i] * varicorr(igp[i]->give) * 9e-8 * fabs(t); } *delay *= fp; *var *= fp * fp; trace(5, "sbsioncorr: dion=%7.2f sig=%7.2f\n", *delay, sqrt(*var)); return 1; } /* get meterological parameters ----------------------------------------------*/ void getmet(double lat, double *met) { static const double metprm[][10] = {/* lat=15,30,45,60,75 */ {1013.25, 299.65, 26.31, 6.30E-3, 2.77, 0.00, 0.00, 0.00, 0.00E-3, 0.00}, {1017.25, 294.15, 21.79, 6.05E-3, 3.15, -3.75, 7.00, 8.85, 0.25E-3, 0.33}, {1015.75, 283.15, 11.66, 5.58E-3, 2.57, -2.25, 11.00, 7.24, 0.32E-3, 0.46}, {1011.75, 272.15, 6.78, 5.39E-3, 1.81, -1.75, 15.00, 5.36, 0.81E-3, 0.74}, {1013.00, 263.65, 4.11, 4.53E-3, 1.55, -0.50, 14.50, 3.39, 0.62E-3, 0.30}}; int i, j; double a; lat = fabs(lat); if (lat <= 15.0) { for (i = 0; i < 10; i++) { met[i] = metprm[0][i]; } } else if (lat >= 75.0) { for (i = 0; i < 10; i++) { met[i] = metprm[4][i]; } } else { j = static_cast(lat / 15.0); a = (lat - j * 15.0) / 15.0; for (i = 0; i < 10; i++) { met[i] = (1.0 - a) * metprm[j - 1][i] + a * metprm[j][i]; } } } /* tropospheric delay correction ----------------------------------------------- * compute sbas tropospheric delay correction (mops model) * args : gtime_t time I time * double *pos I receiver position {lat,lon,height} (rad/m) * double *azel I satellite azimuth/elavation (rad) * double *var O variance of troposphric error (m^2) * return : slant tropospheric delay (m) *-----------------------------------------------------------------------------*/ double sbstropcorr(gtime_t time, const double *pos, const double *azel, double *var) { const double k1 = 77.604, k2 = 382000.0, rd = 287.054, gm = 9.784, g = 9.80665; static double pos_[3] = {}, zh = 0.0, zw = 0.0; int i; double c, met[10], sinel = sin(azel[1]), h = pos[2], m; trace(4, "sbstropcorr: pos=%.3f %.3f azel=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D, azel[0] * R2D, azel[1] * R2D); if (pos[2] < -100.0 || 10000.0 < pos[2] || azel[1] <= 0) { *var = 0.0; return 0.0; } if (zh == 0.0 || fabs(pos[0] - pos_[0]) > 1e-7 || fabs(pos[1] - pos_[1]) > 1e-7 || fabs(pos[2] - pos_[2]) > 1.0) { getmet(pos[0] * R2D, met); c = cos(2.0 * PI * (time2doy(time) - (pos[0] >= 0.0 ? 28.0 : 211.0)) / 365.25); for (i = 0; i < 5; i++) { met[i] -= met[i + 5] * c; } zh = 1e-6 * k1 * rd * met[0] / gm; zw = 1e-6 * k2 * rd / (gm * (met[4] + 1.0) - met[3] * rd) * met[2] / met[1]; zh *= pow(1.0 - met[3] * h / met[1], g / (rd * met[3])); zw *= pow(1.0 - met[3] * h / met[1], (met[4] + 1.0) * g / (rd * met[3]) - 1.0); for (i = 0; i < 3; i++) { pos_[i] = pos[i]; } } m = 1.001 / sqrt(0.002001 + sinel * sinel); *var = 0.12 * 0.12 * m * m; return (zh + zw) * m; } /* long term correction ------------------------------------------------------*/ int sbslongcorr(gtime_t time, int sat, const sbssat_t *sbssat, double *drs, double *ddts) { const sbssatp_t *p; double t; int i; trace(3, "sbslongcorr: sat=%2d\n", sat); for (p = sbssat->sat; p < sbssat->sat + sbssat->nsat; p++) { if (p->sat != sat || p->lcorr.t0.time == 0) { continue; } t = timediff(time, p->lcorr.t0); if (fabs(t) > MAXSBSAGEL) { trace(2, "sbas long-term correction expired: %s sat=%2d t=%5.0f\n", time_str(time, 0), sat, t); return 0; } for (i = 0; i < 3; i++) { drs[i] = p->lcorr.dpos[i] + p->lcorr.dvel[i] * t; } *ddts = p->lcorr.daf0 + p->lcorr.daf1 * t; trace(5, "sbslongcorr: sat=%2d drs=%7.2f%7.2f%7.2f ddts=%7.2f\n", sat, drs[0], drs[1], drs[2], *ddts * SPEED_OF_LIGHT); return 1; } /* if sbas satellite without correction, no correction applied */ if (satsys(sat, nullptr) == SYS_SBS) { return 1; } trace(2, "no sbas long-term correction: %s sat=%2d\n", time_str(time, 0), sat); return 0; } /* fast correction -----------------------------------------------------------*/ int sbsfastcorr(gtime_t time, int sat, const sbssat_t *sbssat, double *prc, double *var) { const sbssatp_t *p; double t; trace(3, "sbsfastcorr: sat=%2d\n", sat); for (p = sbssat->sat; p < sbssat->sat + sbssat->nsat; p++) { if (p->sat != sat) { continue; } if (p->fcorr.t0.time == 0) { break; } t = timediff(time, p->fcorr.t0) + sbssat->tlat; /* expire age of correction or UDRE==14 (not monitored) */ if (fabs(t) > MAXSBSAGEF || p->fcorr.udre >= 15) { continue; } *prc = p->fcorr.prc; #ifdef RRCENA if (p->fcorr.ai > 0 && fabs(t) <= 8.0 * p->fcorr.dt) { *prc += p->fcorr.rrc * t; } #endif *var = varfcorr(p->fcorr.udre) + degfcorr(p->fcorr.ai) * t * t / 2.0; trace(5, "sbsfastcorr: sat=%3d prc=%7.2f sig=%7.2f t=%5.0f\n", sat, *prc, sqrt(*var), t); return 1; } trace(2, "no sbas fast correction: %s sat=%2d\n", time_str(time, 0), sat); return 0; } /* sbas satellite ephemeris and clock correction ------------------------------- * correct satellite position and clock bias with sbas satellite corrections * args : gtime_t time I reception time * int sat I satellite * nav_t *nav I navigation data * double *rs IO sat position and corrected {x,y,z} (ecef) (m) * double *dts IO sat clock bias and corrected (s) * double *var O sat position and clock variance (m^2) * return : status (1:ok,0:no correction) * notes : before calling the function, sbas satellite correction parameters * in navigation data (nav->sbssat) must be set by calling * sbsupdatecorr(). * satellite clock correction include long-term correction and fast * correction. * sbas clock correction is usually based on L1C/A code. TGD or DCB has * to be considered for other codes *-----------------------------------------------------------------------------*/ int sbssatcorr(gtime_t time, int sat, const nav_t *nav, double *rs, double *dts, double *var) { double drs[3] = {}, dclk = 0.0, prc = 0.0; int i; trace(3, "sbssatcorr : sat=%2d\n", sat); /* sbas long term corrections */ if (!sbslongcorr(time, sat, &nav->sbssat, drs, &dclk)) { return 0; } /* sbas fast corrections */ if (!sbsfastcorr(time, sat, &nav->sbssat, &prc, var)) { return 0; } for (i = 0; i < 3; i++) { rs[i] += drs[i]; } dts[0] += dclk + prc / SPEED_OF_LIGHT; trace(5, "sbssatcorr: sat=%2d drs=%6.3f %6.3f %6.3f dclk=%.3f %.3f var=%.3f\n", sat, drs[0], drs[1], drs[2], dclk, prc / SPEED_OF_LIGHT, *var); return 1; } /* decode sbas message --------------------------------------------------------- * decode sbas message frame words and check crc * args : gtime_t time I reception time * int prn I sbas satellite prn number * unsigned int *word I message frame words (24bit x 10) * sbsmsg_t *sbsmsg O sbas message * return : status (1:ok,0:crc error) *-----------------------------------------------------------------------------*/ int sbsdecodemsg(gtime_t time, int prn, const unsigned int *words, sbsmsg_t *sbsmsg) { int i, j; unsigned char f[29]; double tow; trace(5, "sbsdecodemsg: prn=%d\n", prn); if (time.time == 0) { return 0; } tow = time2gpst(time, &sbsmsg->week); sbsmsg->tow = static_cast(tow + DTTOL); sbsmsg->prn = prn; for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { sbsmsg->msg[i * 4 + j] = static_cast(words[i] >> ((3 - j) * 8)); } } sbsmsg->msg[28] = static_cast(words[7] >> 18) & 0xC0; for (i = 28; i > 0; i--) { f[i] = (sbsmsg->msg[i] >> 6) + (sbsmsg->msg[i - 1] << 2); } f[0] = sbsmsg->msg[0] >> 6; return rtk_crc24q(f, 29) == (words[7] & 0xFFFFFF); /* check crc */ } src/algorithms/libs/rtklib/rtklib_sbas.h000066400000000000000000000202441352176506000207150ustar00rootroot00000000000000/*! * \file rtklib_sbas.h * \brief sbas functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * References : * [1] RTCA/DO-229C, Minimum operational performanc standards for global * positioning system/wide area augmentation system airborne equipment, * RTCA inc, November 28, 2001 * [2] IS-QZSS v.1.1, Quasi-Zenith Satellite System Navigation Service * Interface Specification for QZSS, Japan Aerospace Exploration Agency, * July 31, 2009 * *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_SBAS_H_ #define GNSS_SDR_RTKLIB_SBAS_H_ #include "rtklib.h" /* constants -----------------------------------------------------------------*/ const int WEEKOFFSET = 1024; /* gps week offset for NovAtel OEM-3 */ /* sbas igp definition -------------------------------------------------------*/ static const short X1[] = {-75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75, 85}, X2[] = {-55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55}, X3[] = {-75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75}, X4[] = {-85, -75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75}, X5[] = {-180, -175, -170, -165, -160, -155, -150, -145, -140, -135, -130, -125, -120, -115, -110, -105, -100, -95, -90, -85, -80, -75, -70, -65, -60, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175}, X6[] = {-180, -170, -160, -150, -140, -130, -120, -110, -100, -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170}, X7[] = {-180, -150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150}, X8[] = {-170, -140, -110, -80, -50, -20, 10, 40, 70, 100, 130, 160}; const sbsigpband_t IGPBAND1[9][8] = {/* band 0-8 */ {{-180, X1, 1, 28}, {-175, X2, 29, 51}, {-170, X3, 52, 78}, {-165, X2, 79, 101}, {-160, X3, 102, 128}, {-155, X2, 129, 151}, {-150, X3, 152, 178}, {-145, X2, 179, 201}}, {{-140, X4, 1, 28}, {-135, X2, 29, 51}, {-130, X3, 52, 78}, {-125, X2, 79, 101}, {-120, X3, 102, 128}, {-115, X2, 129, 151}, {-110, X3, 152, 178}, {-105, X2, 179, 201}}, {{-100, X3, 1, 27}, {-95, X2, 28, 50}, {-90, X1, 51, 78}, {-85, X2, 79, 101}, {-80, X3, 102, 128}, {-75, X2, 129, 151}, {-70, X3, 152, 178}, {-65, X2, 179, 201}}, {{-60, X3, 1, 27}, {-55, X2, 28, 50}, {-50, X4, 51, 78}, {-45, X2, 79, 101}, {-40, X3, 102, 128}, {-35, X2, 129, 151}, {-30, X3, 152, 178}, {-25, X2, 179, 201}}, {{-20, X3, 1, 27}, {-15, X2, 28, 50}, {-10, X3, 51, 77}, {-5, X2, 78, 100}, {0, X1, 101, 128}, {5, X2, 129, 151}, {10, X3, 152, 178}, {15, X2, 179, 201}}, {{20, X3, 1, 27}, {25, X2, 28, 50}, {30, X3, 51, 77}, {35, X2, 78, 100}, {40, X4, 101, 128}, {45, X2, 129, 151}, {50, X3, 152, 178}, {55, X2, 179, 201}}, {{60, X3, 1, 27}, {65, X2, 28, 50}, {70, X3, 51, 77}, {75, X2, 78, 100}, {80, X3, 101, 127}, {85, X2, 128, 150}, {90, X1, 151, 178}, {95, X2, 179, 201}}, {{100, X3, 1, 27}, {105, X2, 28, 50}, {110, X3, 51, 77}, {115, X2, 78, 100}, {120, X3, 101, 127}, {125, X2, 128, 150}, {130, X4, 151, 178}, {135, X2, 179, 201}}, {{140, X3, 1, 27}, {145, X2, 28, 50}, {150, X3, 51, 77}, {155, X2, 78, 100}, {160, X3, 101, 127}, {165, X2, 128, 150}, {170, X3, 151, 177}, {175, X2, 178, 200}}}; const sbsigpband_t IGPBAND2[2][5] = {/* band 9-10 */ {{60, X5, 1, 72}, {65, X6, 73, 108}, {70, X6, 109, 144}, {75, X6, 145, 180}, {85, X7, 181, 192}}, {{-60, X5, 1, 72}, {-65, X6, 73, 108}, {-70, X6, 109, 144}, {-75, X6, 145, 180}, {-85, X8, 181, 192}}}; char *getfield(char *p, int pos); double varfcorr(int udre); double varicorr(int give); double degfcorr(int ai); int decode_sbstype1(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype2(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype6(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype7(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype9(const sbsmsg_t *msg, nav_t *nav); int decode_sbstype18(const sbsmsg_t *msg, sbsion_t *sbsion); int decode_longcorr0(const sbsmsg_t *msg, int p, sbssat_t *sbssat); int decode_longcorr1(const sbsmsg_t *msg, int p, sbssat_t *sbssat); int decode_longcorrh(const sbsmsg_t *msg, int p, sbssat_t *sbssat); int decode_sbstype24(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype25(const sbsmsg_t *msg, sbssat_t *sbssat); int decode_sbstype26(const sbsmsg_t *msg, sbsion_t *sbsion); int sbsupdatecorr(const sbsmsg_t *msg, nav_t *nav); void readmsgs(const char *file, int sel, gtime_t ts, gtime_t te, sbs_t *sbs); int cmpmsgs(const void *p1, const void *p2); int sbsreadmsgt(const char *file, int sel, gtime_t ts, gtime_t te, sbs_t *sbs); int sbsreadmsg(const char *file, int sel, sbs_t *sbs); void sbsoutmsg(FILE *fp, sbsmsg_t *sbsmsg); void searchigp(gtime_t time, const double *pos, const sbsion_t *ion, const sbsigp_t **igp, double *x, double *y); int sbsioncorr(gtime_t time, const nav_t *nav, const double *pos, const double *azel, double *delay, double *var); void getmet(double lat, double *met); double sbstropcorr(gtime_t time, const double *pos, const double *azel, double *var); int sbslongcorr(gtime_t time, int sat, const sbssat_t *sbssat, double *drs, double *ddts); int sbsfastcorr(gtime_t time, int sat, const sbssat_t *sbssat, double *prc, double *var); int sbssatcorr(gtime_t time, int sat, const nav_t *nav, double *rs, double *dts, double *var); int sbsdecodemsg(gtime_t time, int prn, const unsigned int *words, sbsmsg_t *sbsmsg); #endif /* GNSS_SDR_RTKLIB_SBAS_H_ */ src/algorithms/libs/rtklib/rtklib_solution.cc000066400000000000000000002131441352176506000220020ustar00rootroot00000000000000/*! * \file rtklib_solution.cc * \brief solution functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *----------------------------------------------------------------------------*/ #include "rtklib_solution.h" #include "rtklib_rtkcmn.h" #include "rtklib_rtksvr.h" #include #include /* constants and macros ------------------------------------------------------*/ #define SQR_SOL(x) ((x) < 0.0 ? -(x) * (x) : (x) * (x)) #define SQRT_SOL(x) ((x) < 0.0 ? 0.0 : sqrt(x)) const int MAXFIELD = 64; /* max number of fields in a record */ const double KNOT2M = 0.514444444; /* m/knot */ static const int SOLQ_NMEA[] = {/* nmea quality flags to rtklib sol quality */ /* nmea 0183 v.2.3 quality flags: */ /* 0=invalid, 1=gps fix (sps), 2=dgps fix, 3=pps fix, 4=rtk, 5=float rtk */ /* 6=estimated (dead reckoning), 7=manual input, 8=simulation */ SOLQ_NONE, SOLQ_SINGLE, SOLQ_DGPS, SOLQ_PPP, SOLQ_FIX, SOLQ_FLOAT, SOLQ_DR, SOLQ_NONE, SOLQ_NONE, SOLQ_NONE}; /* solution option to field separator ----------------------------------------*/ const char *opt2sep(const solopt_t *opt) { if (!*opt->sep) { return " "; } if (!strcmp(opt->sep, "\\t")) { return "\t"; } return opt->sep; } /* separate fields -----------------------------------------------------------*/ int tonum(char *buff, const char *sep, double *v) { int n, len = static_cast(strlen(sep)); char *p, *q; for (p = buff, n = 0; n < MAXFIELD; p = q + len) { if ((q = strstr(p, sep))) { *q = '\0'; } if (*p) { v[n++] = atof(p); } if (!q) { break; } } return n; } /* sqrt of covariance --------------------------------------------------------*/ double sqvar(double covar) { return covar < 0.0 ? -sqrt(-covar) : sqrt(covar); } /* convert ddmm.mm in nmea format to deg -------------------------------------*/ double dmm2deg(double dmm) { return floor(dmm / 100.0) + fmod(dmm, 100.0) / 60.0; } /* convert time in nmea format to time ---------------------------------------*/ void septime(double t, double *t1, double *t2, double *t3) { *t1 = floor(t / 10000.0); t -= *t1 * 10000.0; *t2 = floor(t / 100.0); *t3 = t - *t2 * 100.0; } /* solution to covariance ----------------------------------------------------*/ void soltocov(const sol_t *sol, double *P) { P[0] = sol->qr[0]; /* xx or ee */ P[4] = sol->qr[1]; /* yy or nn */ P[8] = sol->qr[2]; /* zz or uu */ P[1] = P[3] = sol->qr[3]; /* xy or en */ P[5] = P[7] = sol->qr[4]; /* yz or nu */ P[2] = P[6] = sol->qr[5]; /* zx or ue */ } /* covariance to solution ----------------------------------------------------*/ void covtosol(const double *P, sol_t *sol) { sol->qr[0] = static_cast(P[0]); /* xx or ee */ sol->qr[1] = static_cast(P[4]); /* yy or nn */ sol->qr[2] = static_cast(P[8]); /* zz or uu */ sol->qr[3] = static_cast(P[1]); /* xy or en */ sol->qr[4] = static_cast(P[5]); /* yz or nu */ sol->qr[5] = static_cast(P[2]); /* zx or ue */ } /* decode nmea gprmc: recommended minimum data for gps -----------------------*/ int decode_nmearmc(char **val, int n, sol_t *sol) { double tod = 0.0, lat = 0.0, lon = 0.0, vel = 0.0, dir = 0.0, date = 0.0, ang = 0.0, ep[6]; double pos[3] = {0}; char act = ' ', ns = 'N', ew = 'E', mew = 'E', mode = 'A'; int i; trace(4, "decode_nmearmc: n=%d\n", n); for (i = 0; i < n; i++) { switch (i) { case 0: tod = atof(val[i]); break; /* time in utc (hhmmss) */ case 1: act = *val[i]; break; /* A=active,V=void */ case 2: lat = atof(val[i]); break; /* latitude (ddmm.mmm) */ case 3: ns = *val[i]; break; /* N=north,S=south */ case 4: lon = atof(val[i]); break; /* longitude (dddmm.mmm) */ case 5: ew = *val[i]; break; /* E=east,W=west */ case 6: vel = atof(val[i]); break; /* speed (knots) */ case 7: dir = atof(val[i]); break; /* track angle (deg) */ case 8: date = atof(val[i]); break; /* date (ddmmyy) */ case 9: ang = atof(val[i]); break; /* magnetic variation */ case 10: mew = *val[i]; break; /* E=east,W=west */ case 11: mode = *val[i]; break; /* mode indicator (>nmea 2) */ /* A=autonomous,D=differential */ /* E=estimated,N=not valid,S=simulator */ } } if ((act != 'A' && act != 'V') || (ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) { trace(2, "invalid nmea gprmc format\n"); return 0; } pos[0] = (ns == 'S' ? -1.0 : 1.0) * dmm2deg(lat) * D2R; pos[1] = (ew == 'W' ? -1.0 : 1.0) * dmm2deg(lon) * D2R; septime(date, ep + 2, ep + 1, ep); septime(tod, ep + 3, ep + 4, ep + 5); ep[0] += ep[0] < 80.0 ? 2000.0 : 1900.0; sol->time = utc2gpst(epoch2time(ep)); pos2ecef(pos, sol->rr); sol->stat = mode == 'D' ? SOLQ_DGPS : SOLQ_SINGLE; sol->ns = 0; sol->type = 0; /* position type = xyz */ trace(5, "decode_nmearmc: %s rr=%.3f %.3f %.3f stat=%d ns=%d vel=%.2f dir=%.0f ang=%.0f mew=%c mode=%c\n", time_str(sol->time, 0), sol->rr[0], sol->rr[1], sol->rr[2], sol->stat, sol->ns, vel, dir, ang, mew, mode); return 1; } /* decode nmea gpgga: fix information ----------------------------------------*/ int decode_nmeagga(char **val, int n, sol_t *sol) { gtime_t time; double tod = 0.0, lat = 0.0, lon = 0.0, hdop = 0.0, alt = 0.0, msl = 0.0, ep[6], tt; double pos[3] = {0}; char ns = 'N', ew = 'E', ua = ' ', um = ' '; int i, solq = 0, nrcv = 0; trace(4, "decode_nmeagga: n=%d\n", n); for (i = 0; i < n; i++) { switch (i) { case 0: tod = atof(val[i]); break; /* time in utc (hhmmss) */ case 1: lat = atof(val[i]); break; /* latitude (ddmm.mmm) */ case 2: ns = *val[i]; break; /* N=north,S=south */ case 3: lon = atof(val[i]); break; /* longitude (dddmm.mmm) */ case 4: ew = *val[i]; break; /* E=east,W=west */ case 5: solq = atoi(val[i]); break; /* fix quality */ case 6: nrcv = atoi(val[i]); break; /* # of satellite tracked */ case 7: hdop = atof(val[i]); break; /* hdop */ case 8: alt = atof(val[i]); break; /* altitude in msl */ case 9: ua = *val[i]; break; /* unit (M) */ case 10: msl = atof(val[i]); break; /* height of geoid */ case 11: um = *val[i]; break; /* unit (M) */ } } if ((ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) { trace(2, "invalid nmea gpgga format\n"); return 0; } if (sol->time.time == 0.0) { trace(2, "no date info for nmea gpgga\n"); return 0; } pos[0] = (ns == 'N' ? 1.0 : -1.0) * dmm2deg(lat) * D2R; pos[1] = (ew == 'E' ? 1.0 : -1.0) * dmm2deg(lon) * D2R; pos[2] = alt + msl; time2epoch(sol->time, ep); septime(tod, ep + 3, ep + 4, ep + 5); time = utc2gpst(epoch2time(ep)); tt = timediff(time, sol->time); if (tt < -43200.0) { sol->time = timeadd(time, 86400.0); } else if (tt > 43200.0) { sol->time = timeadd(time, -86400.0); } else { sol->time = time; } pos2ecef(pos, sol->rr); sol->stat = 0 <= solq && solq <= 8 ? SOLQ_NMEA[solq] : SOLQ_NONE; sol->ns = nrcv; sol->type = 0; /* position type = xyz */ trace(5, "decode_nmeagga: %s rr=%.3f %.3f %.3f stat=%d ns=%d hdop=%.1f ua=%c um=%c\n", time_str(sol->time, 0), sol->rr[0], sol->rr[1], sol->rr[2], sol->stat, sol->ns, hdop, ua, um); return 1; } /* decode nmea ---------------------------------------------------------------*/ int decode_nmea(char *buff, sol_t *sol) { char *p, *q, *val[MAXFIELD] = {nullptr}; int n = 0; trace(4, "decode_nmea: buff=%s\n", buff); /* parse fields */ for (p = buff; *p && n < MAXFIELD; p = q + 1) { if ((q = strchr(p, ',')) || (q = strchr(p, '*'))) { val[n++] = p; *q = '\0'; } else { break; } } /* decode nmea sentence */ if (!strcmp(val[0], "$GPRMC")) { return decode_nmearmc(val + 1, n - 1, sol); } if (!strcmp(val[0], "$GPGGA")) { return decode_nmeagga(val + 1, n - 1, sol); } return 0; } /* decode solution time ------------------------------------------------------*/ char *decode_soltime(char *buff, const solopt_t *opt, gtime_t *time) { double v[MAXFIELD]; char *p, *q, s[64] = " "; int n, len; trace(4, "decode_soltime:\n"); if (!strcmp(opt->sep, "\\t")) { strcpy(s, "\t"); } else if (*opt->sep) { strcpy(s, opt->sep); } len = static_cast(strlen(s)); /* yyyy/mm/dd hh:mm:ss or yyyy mm dd hh:mm:ss */ if (sscanf(buff, "%lf/%lf/%lf %lf:%lf:%lf", v, v + 1, v + 2, v + 3, v + 4, v + 5) >= 6) { if (v[0] < 100.0) { v[0] += v[0] < 80.0 ? 2000.0 : 1900.0; } *time = epoch2time(v); if (opt->times == TIMES_UTC) { *time = utc2gpst(*time); } else if (opt->times == TIMES_JST) { *time = utc2gpst(timeadd(*time, -9 * 3600.0)); } if (!(p = strchr(buff, ':')) || !(p = strchr(p + 1, ':'))) { return nullptr; } for (p++; isdigit(static_cast(*p)) || *p == '.';) { p++; } return p + len; } if (opt->posf == SOLF_GSIF) { if (sscanf(buff, "%lf %lf %lf %lf:%lf:%lf", v, v + 1, v + 2, v + 3, v + 4, v + 5) < 6) { return nullptr; } *time = timeadd(epoch2time(v), -12.0 * 3600.0); if (!(p = strchr(buff, ':')) || !(p = strchr(p + 1, ':'))) { return nullptr; } for (p++; isdigit(static_cast(*p)) || *p == '.';) { p++; } return p + len; } /* wwww ssss */ for (p = buff, n = 0; n < 2; p = q + len) { if ((q = strstr(p, s))) { *q = '\0'; } if (*p) { v[n++] = atof(p); } if (!q) { break; } } if (n >= 2 && 0.0 <= v[0] && v[0] <= 3000.0 && 0.0 <= v[1] && v[1] < 604800.0) { *time = gpst2time(static_cast(v[0]), v[1]); return p; } return nullptr; } /* decode x/y/z-ecef ---------------------------------------------------------*/ int decode_solxyz(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD], P[9] = {0}; int i = 0, j, n; const char *sep = opt2sep(opt); trace(4, "decode_solxyz:\n"); if ((n = tonum(buff, sep, val)) < 3) { return 0; } for (j = 0; j < 3; j++) { sol->rr[j] = val[i++]; /* xyz */ } if (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { P[0] = val[i] * val[i]; i++; /* sdx */ P[4] = val[i] * val[i]; i++; /* sdy */ P[8] = val[i] * val[i]; i++; /* sdz */ if (i + 3 < n) { P[1] = P[3] = SQR_SOL(val[i]); i++; /* sdxy */ P[5] = P[7] = SQR_SOL(val[i]); i++; /* sdyz */ P[2] = P[6] = SQR_SOL(val[i]); i++; /* sdzx */ } covtosol(P, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 0; /* position type = xyz */ if (MAXSOLQ < sol->stat) { sol->stat = SOLQ_NONE; } return 1; } /* decode lat/lon/height -----------------------------------------------------*/ int decode_solllh(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD], pos[3], Q[9] = {0}, P[9]; int i = 0, n; const char *sep = opt2sep(opt); trace(4, "decode_solllh:\n"); n = tonum(buff, sep, val); if (!opt->degf) { if (n < 3) { return 0; } pos[0] = val[i++] * D2R; /* lat/lon/hgt (ddd.ddd) */ pos[1] = val[i++] * D2R; pos[2] = val[i++]; } else { if (n < 7) { return 0; } pos[0] = dms2deg(val) * D2R; /* lat/lon/hgt (ddd mm ss) */ pos[1] = dms2deg(val + 3) * D2R; pos[2] = val[6]; i += 7; } pos2ecef(pos, sol->rr); if (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { Q[4] = val[i] * val[i]; i++; /* sdn */ Q[0] = val[i] * val[i]; i++; /* sde */ Q[8] = val[i] * val[i]; i++; /* sdu */ if (i + 3 < n) { Q[1] = Q[3] = SQR_SOL(val[i]); i++; /* sdne */ Q[2] = Q[6] = SQR_SOL(val[i]); i++; /* sdeu */ Q[5] = Q[7] = SQR_SOL(val[i]); i++; /* sdun */ } covecef(pos, Q, P); covtosol(P, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 0; /* position type = xyz */ if (MAXSOLQ < sol->stat) { sol->stat = SOLQ_NONE; } return 1; } /* decode e/n/u-baseline -----------------------------------------------------*/ int decode_solenu(char *buff, const solopt_t *opt, sol_t *sol) { double val[MAXFIELD], Q[9] = {0}; int i = 0, j, n; const char *sep = opt2sep(opt); trace(4, "decode_solenu:\n"); if ((n = tonum(buff, sep, val)) < 3) { return 0; } for (j = 0; j < 3; j++) { sol->rr[j] = val[i++]; /* enu */ } if (i < n) { sol->stat = static_cast(val[i++]); } if (i < n) { sol->ns = static_cast(val[i++]); } if (i + 3 < n) { Q[0] = val[i] * val[i]; i++; /* sde */ Q[4] = val[i] * val[i]; i++; /* sdn */ Q[8] = val[i] * val[i]; i++; /* sdu */ if (i + 3 < n) { Q[1] = Q[3] = SQR_SOL(val[i]); i++; /* sden */ Q[5] = Q[7] = SQR_SOL(val[i]); i++; /* sdnu */ Q[2] = Q[6] = SQR_SOL(val[i]); i++; /* sdue */ } covtosol(Q, sol); } if (i < n) { sol->age = static_cast(val[i++]); } if (i < n) { sol->ratio = static_cast(val[i]); } sol->type = 1; /* position type = enu */ if (MAXSOLQ < sol->stat) { sol->stat = SOLQ_NONE; } return 1; } /* decode gsi f solution -----------------------------------------------------*/ int decode_solgsi(char *buff, const solopt_t *opt __attribute((unused)), sol_t *sol) { double val[MAXFIELD]; int i = 0, j; trace(4, "decode_solgsi:\n"); if (tonum(buff, " ", val) < 3) { return 0; } for (j = 0; j < 3; j++) { sol->rr[j] = val[i++]; /* xyz */ } sol->stat = SOLQ_FIX; return 1; } /* decode solution position --------------------------------------------------*/ int decode_solpos(char *buff, const solopt_t *opt, sol_t *sol) { sol_t sol0 = {{0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, '0', '0', '0', 0, 0, 0}; char *p = buff; trace(4, "decode_solpos: buff=%s\n", buff); *sol = sol0; /* decode solution time */ if (!(p = decode_soltime(p, opt, &sol->time))) { return 0; } /* decode solution position */ switch (opt->posf) { case SOLF_XYZ: return decode_solxyz(p, opt, sol); case SOLF_LLH: return decode_solllh(p, opt, sol); case SOLF_ENU: return decode_solenu(p, opt, sol); case SOLF_GSIF: return decode_solgsi(p, opt, sol); } return 0; } /* decode reference position -------------------------------------------------*/ void decode_refpos(char *buff, const solopt_t *opt, double *rb) { double val[MAXFIELD], pos[3]; int i, n; const char *sep = opt2sep(opt); trace(3, "decode_refpos: buff=%s\n", buff); if ((n = tonum(buff, sep, val)) < 3) { return; } if (opt->posf == SOLF_XYZ) { /* xyz */ for (i = 0; i < 3; i++) { rb[i] = val[i]; } } else if (opt->degf == 0) { /* lat/lon/hgt (ddd.ddd) */ pos[0] = val[0] * D2R; pos[1] = val[1] * D2R; pos[2] = val[2]; pos2ecef(pos, rb); } else if (opt->degf == 1 && n >= 7) { /* lat/lon/hgt (ddd mm ss) */ pos[0] = dms2deg(val) * D2R; pos[1] = dms2deg(val + 3) * D2R; pos[2] = val[6]; pos2ecef(pos, rb); } } /* decode solution -----------------------------------------------------------*/ int decode_sol(char *buff, const solopt_t *opt, sol_t *sol, double *rb) { char *p; trace(4, "decode_sol: buff=%s\n", buff); if (!strncmp(buff, COMMENTH, 1)) { /* reference position */ if (!strstr(buff, "ref pos") && !strstr(buff, "slave pos")) { return 0; } if (!(p = strchr(buff, ':'))) { return 0; } decode_refpos(p + 1, opt, rb); return 0; } if (!strncmp(buff, "$GP", 3)) { /* decode nmea */ if (!decode_nmea(buff, sol)) { return 0; } /* for time update only */ if (opt->posf != SOLF_NMEA && !strncmp(buff, "$GPRMC", 6)) { return 2; } } else { /* decode position record */ if (!decode_solpos(buff, opt, sol)) { return 0; } } return 1; } /* decode solution options ---------------------------------------------------*/ void decode_solopt(char *buff, solopt_t *opt) { char *p; trace(4, "decode_solhead: buff=%s\n", buff); if (strncmp(buff, COMMENTH, 1) != 0 && strncmp(buff, "+", 1) != 0) { return; } if (strstr(buff, "GPST")) { opt->times = TIMES_GPST; } else if (strstr(buff, "UTC")) { opt->times = TIMES_UTC; } else if (strstr(buff, "JST")) { opt->times = TIMES_JST; } if ((p = strstr(buff, "x-ecef(m)"))) { opt->posf = SOLF_XYZ; opt->degf = 0; strncpy(opt->sep, p + 9, 1); opt->sep[1] = '\0'; } else if ((p = strstr(buff, "latitude(d'\")"))) { opt->posf = SOLF_LLH; opt->degf = 1; strncpy(opt->sep, p + 14, 1); opt->sep[1] = '\0'; } else if ((p = strstr(buff, "latitude(deg)"))) { opt->posf = SOLF_LLH; opt->degf = 0; strncpy(opt->sep, p + 13, 1); opt->sep[1] = '\0'; } else if ((p = strstr(buff, "e-baseline(m)"))) { opt->posf = SOLF_ENU; opt->degf = 0; strncpy(opt->sep, p + 13, 1); opt->sep[1] = '\0'; } else if ((p = strstr(buff, "+SITE/INF"))) { /* gsi f2/f3 solution */ opt->times = TIMES_GPST; opt->posf = SOLF_GSIF; opt->degf = 0; strcpy(opt->sep, " "); } } /* read solution option ------------------------------------------------------*/ void readsolopt(FILE *fp, solopt_t *opt) { char buff[MAXSOLMSG + 1]; int i; trace(3, "readsolopt:\n"); for (i = 0; fgets(buff, sizeof(buff), fp) && i < 100; i++) { /* only 100 lines */ /* decode solution options */ decode_solopt(buff, opt); } } /* input solution data from stream --------------------------------------------- * input solution data from stream * args : unsigned char data I stream data * gtime_t ts I start time (ts.time == 0: from start) * gtime_t te I end time (te.time == 0: to end) * double tint I time interval (0: all) * int qflag I quality flag (0: all) * solbuf_t *solbuf IO solution buffer * return : status (1:solution received,0:no solution,-1:disconnect received) *-----------------------------------------------------------------------------*/ int inputsol(unsigned char data, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf) { sol_t sol = {{0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, '0', '0', '0', 0, 0, 0}; int stat; trace(4, "inputsol: data=0x%02x\n", data); sol.time = solbuf->time; if (data == '$' || (!isprint(data) && data != '\r' && data != '\n')) { /* sync header */ solbuf->nb = 0; } solbuf->buff[solbuf->nb++] = data; if (data != '\n' && solbuf->nb < MAXSOLMSG) { return 0; /* sync trailer */ } solbuf->buff[solbuf->nb] = '\0'; solbuf->nb = 0; /* check disconnect message */ if (!strcmp(reinterpret_cast(solbuf->buff), MSG_DISCONN)) { trace(3, "disconnect received\n"); return -1; } /* decode solution */ if ((stat = decode_sol(reinterpret_cast(solbuf->buff), opt, &sol, solbuf->rb)) > 0) { solbuf->time = sol.time; /* update current time */ } if (stat != 1 || !screent(sol.time, ts, te, tint) || (qflag && sol.stat != qflag)) { return 0; } /* add solution to solution buffer */ return addsol(solbuf, &sol); } /* read solution data --------------------------------------------------------*/ int readsoldata(FILE *fp, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf) { int c; trace(3, "readsoldata:\n"); while ((c = fgetc(fp)) != EOF) { /* input solution */ inputsol(static_cast(c), ts, te, tint, qflag, opt, solbuf); } return solbuf->n > 0; } /* compare solution data -----------------------------------------------------*/ int cmpsol(const void *p1, const void *p2) { auto *q1 = (sol_t *)p1, *q2 = (sol_t *)p2; double tt = timediff(q1->time, q2->time); return tt < -0.0 ? -1 : (tt > 0.0 ? 1 : 0); } /* sort solution data --------------------------------------------------------*/ int sort_solbuf(solbuf_t *solbuf) { sol_t *solbuf_data; trace(4, "sort_solbuf: n=%d\n", solbuf->n); if (solbuf->n <= 0) { return 0; } if (!(solbuf_data = static_cast(realloc(solbuf->data, sizeof(sol_t) * solbuf->n)))) { trace(1, "sort_solbuf: memory allocation error\n"); free(solbuf->data); solbuf->data = nullptr; solbuf->n = solbuf->nmax = 0; return 0; } solbuf->data = solbuf_data; qsort(solbuf->data, solbuf->n, sizeof(sol_t), cmpsol); solbuf->nmax = solbuf->n; solbuf->start = 0; solbuf->end = solbuf->n - 1; return 1; } /* read solutions data from solution files ------------------------------------- * read solution data from soluiton files * args : char *files[] I solution files * int nfile I number of files * (gtime_t ts) I start time (ts.time == 0: from start) * (gtime_t te) I end time (te.time == 0: to end) * (double tint) I time interval (0: all) * (int qflag) I quality flag (0: all) * solbuf_t *solbuf O solution buffer * return : status (1:ok,0:no data or error) *-----------------------------------------------------------------------------*/ int readsolt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, int qflag, solbuf_t *solbuf) { FILE *fp; solopt_t opt = SOLOPT_DEFAULT; int i; trace(3, "readsolt: nfile=%d\n", nfile); initsolbuf(solbuf, 0, 0); for (i = 0; i < nfile; i++) { if (!(fp = fopen(files[i], "rbe"))) { trace(1, "readsolt: file open error %s\n", files[i]); continue; } /* read solution options in header */ readsolopt(fp, &opt); rewind(fp); /* read solution data */ if (!readsoldata(fp, ts, te, tint, qflag, &opt, solbuf)) { trace(1, "readsolt: no solution in %s\n", files[i]); } fclose(fp); } return sort_solbuf(solbuf); } int readsol(char *files[], int nfile, solbuf_t *sol) { gtime_t time = {0, 0.0}; trace(3, "readsol: nfile=%d\n", nfile); return readsolt(files, nfile, time, time, 0.0, 0, sol); } /* add solution data to solution buffer ---------------------------------------- * add solution data to solution buffer * args : solbuf_t *solbuf IO solution buffer * sol_t *sol I solution data * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ int addsol(solbuf_t *solbuf, const sol_t *sol) { sol_t *solbuf_data; trace(4, "addsol:\n"); if (solbuf->cyclic) { /* ring buffer */ if (solbuf->nmax <= 1) { return 0; } solbuf->data[solbuf->end] = *sol; if (++solbuf->end >= solbuf->nmax) { solbuf->end = 0; } if (solbuf->start == solbuf->end) { if (++solbuf->start >= solbuf->nmax) { solbuf->start = 0; } } else { solbuf->n++; } return 1; } if (solbuf->n >= solbuf->nmax) { solbuf->nmax = solbuf->nmax == 0 ? 8192 : solbuf->nmax * 2; if (!(solbuf_data = static_cast(realloc(solbuf->data, sizeof(sol_t) * solbuf->nmax)))) { trace(1, "addsol: memory allocation error\n"); free(solbuf->data); solbuf->data = nullptr; solbuf->n = solbuf->nmax = 0; return 0; } solbuf->data = solbuf_data; } solbuf->data[solbuf->n++] = *sol; return 1; } /* get solution data from solution buffer -------------------------------------- * get solution data by index from solution buffer * args : solbuf_t *solbuf I solution buffer * int index I index of solution (0...) * return : solution data pointer (NULL: no solution, out of range) *-----------------------------------------------------------------------------*/ sol_t *getsol(solbuf_t *solbuf, int index) { trace(4, "getsol: index=%d\n", index); if (index < 0 || solbuf->n <= index) { return nullptr; } if ((index = solbuf->start + index) >= solbuf->nmax) { index -= solbuf->nmax; } return solbuf->data + index; } /* initialize solution buffer -------------------------------------------------- * initialize position solutions * args : solbuf_t *solbuf I solution buffer * int cyclic I solution data buffer type (0:linear,1:cyclic) * int nmax I initial number of solution data * return : status (1:ok,0:error) *-----------------------------------------------------------------------------*/ void initsolbuf(solbuf_t *solbuf, int cyclic, int nmax) { gtime_t time0 = {0, 0.0}; trace(3, "initsolbuf: cyclic=%d nmax=%d\n", cyclic, nmax); solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = 0; solbuf->cyclic = cyclic; solbuf->time = time0; solbuf->data = nullptr; if (cyclic) { if (nmax <= 2) { nmax = 2; } if (!(solbuf->data = static_cast(malloc(sizeof(sol_t) * nmax)))) { trace(1, "initsolbuf: memory allocation error\n"); return; } solbuf->nmax = nmax; } } /* free solution --------------------------------------------------------------- * free memory for solution buffer * args : solbuf_t *solbuf I solution buffer * return : none *-----------------------------------------------------------------------------*/ void freesolbuf(solbuf_t *solbuf) { trace(3, "freesolbuf: n=%d\n", solbuf->n); free(solbuf->data); solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = 0; solbuf->data = nullptr; } void freesolstatbuf(solstatbuf_t *solstatbuf) { trace(3, "freesolstatbuf: n=%d\n", solstatbuf->n); solstatbuf->n = solstatbuf->nmax = 0; free(solstatbuf->data); solstatbuf->data = nullptr; } /* compare solution status ---------------------------------------------------*/ int cmpsolstat(const void *p1, const void *p2) { auto *q1 = (solstat_t *)p1, *q2 = (solstat_t *)p2; double tt = timediff(q1->time, q2->time); return tt < -0.0 ? -1 : (tt > 0.0 ? 1 : 0); } /* sort solution data --------------------------------------------------------*/ int sort_solstat(solstatbuf_t *statbuf) { solstat_t *statbuf_data; trace(4, "sort_solstat: n=%d\n", statbuf->n); if (statbuf->n <= 0) { return 0; } if (!(statbuf_data = static_cast(realloc(statbuf->data, sizeof(solstat_t) * statbuf->n)))) { trace(1, "sort_solstat: memory allocation error\n"); free(statbuf->data); statbuf->data = nullptr; statbuf->n = statbuf->nmax = 0; return 0; } statbuf->data = statbuf_data; qsort(statbuf->data, statbuf->n, sizeof(solstat_t), cmpsolstat); statbuf->nmax = statbuf->n; return 1; } /* decode solution status ----------------------------------------------------*/ int decode_solstat(char *buff, solstat_t *stat) { static const solstat_t stat0 = {{0, 0.0}, '0', '0', 0, 0, 0, 0, '0', '0', 0, 0, 0, 0}; double tow, az, el, resp, resc; int n, week, sat, frq, vsat, snr, fix, slip, lock, outc, slipc, rejc; char id[32] = "", *p; trace(4, "decode_solstat: buff=%s\n", buff); if (strstr(buff, "$SAT") != buff) { return 0; } for (p = buff; *p; p++) { if (*p == ',') { *p = ' '; } } n = sscanf(buff, "$SAT%d%lf%s%d%lf%lf%lf%lf%d%d%d%d%d%d%d%d", &week, &tow, id, &frq, &az, &el, &resp, &resc, &vsat, &snr, &fix, &slip, &lock, &outc, &slipc, &rejc); if (n < 15) { trace(2, "invalid format of solution status: %s\n", buff); return 0; } if ((sat = satid2no(id)) <= 0) { trace(2, "invalid satellite in solution status: %s\n", id); return 0; } *stat = stat0; stat->time = gpst2time(week, tow); stat->sat = static_cast(sat); stat->frq = static_cast(frq); stat->az = static_cast(az * D2R); stat->el = static_cast(el * D2R); stat->resp = static_cast(resp); stat->resc = static_cast(resc); stat->flag = static_cast((vsat << 5) + (slip << 3) + fix); stat->snr = static_cast(snr * 4.0 + 0.5); stat->lock = static_cast(lock); stat->outc = static_cast(outc); stat->slipc = static_cast(slipc); stat->rejc = static_cast(rejc); return 1; } /* add solution status data --------------------------------------------------*/ void addsolstat(solstatbuf_t *statbuf, const solstat_t *stat) { solstat_t *statbuf_data; trace(4, "addsolstat:\n"); if (statbuf->n >= statbuf->nmax) { statbuf->nmax = statbuf->nmax == 0 ? 8192 : statbuf->nmax * 2; if (!(statbuf_data = static_cast(realloc(statbuf->data, sizeof(solstat_t) * statbuf->nmax)))) { trace(1, "addsolstat: memory allocation error\n"); free(statbuf->data); statbuf->data = nullptr; statbuf->n = statbuf->nmax = 0; return; } statbuf->data = statbuf_data; } statbuf->data[statbuf->n++] = *stat; } /* read solution status data -------------------------------------------------*/ int readsolstatdata(FILE *fp, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf) { solstat_t stat = {{0, 0.0}, '0', '0', 0, 0, 0, 0, '0', '0', 0, 0, 0, 0}; char buff[MAXSOLMSG + 1]; trace(3, "readsolstatdata:\n"); while (fgets(buff, sizeof(buff), fp)) { /* decode solution status */ if (!decode_solstat(buff, &stat)) { continue; } /* add solution to solution buffer */ if (screent(stat.time, ts, te, tint)) { addsolstat(statbuf, &stat); } } return statbuf->n > 0; } /* read solution status -------------------------------------------------------- * read solution status from solution status files * args : char *files[] I solution status files * int nfile I number of files * (gtime_t ts) I start time (ts.time == 0: from start) * (gtime_t te) I end time (te.time == 0: to end) * (double tint) I time interval (0: all) * solstatbuf_t *statbuf O solution status buffer * return : status (1:ok,0:no data or error) *-----------------------------------------------------------------------------*/ int readsolstatt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf) { FILE *fp; char path[1024]; int i; trace(3, "readsolstatt: nfile=%d\n", nfile); statbuf->n = statbuf->nmax = 0; statbuf->data = nullptr; for (i = 0; i < nfile; i++) { sprintf(path, "%s.stat", files[i]); if (!(fp = fopen(path, "re"))) { trace(1, "readsolstatt: file open error %s\n", path); continue; } /* read solution status data */ if (!readsolstatdata(fp, ts, te, tint, statbuf)) { trace(1, "readsolt: no solution in %s\n", path); } fclose(fp); } return sort_solstat(statbuf); } int readsolstat(char *files[], int nfile, solstatbuf_t *statbuf) { gtime_t time = {0, 0.0}; trace(3, "readsolstat: nfile=%d\n", nfile); return readsolstatt(files, nfile, time, time, 0.0, statbuf); } /* output solution as the form of x/y/z-ecef ---------------------------------*/ int outecef(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt) { const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); trace(3, "outecef:\n"); p += snprintf(p, 255, "%s%s%14.4f%s%14.4f%s%14.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\n", s, sep, sol->rr[0], sep, sol->rr[1], sep, sol->rr[2], sep, sol->stat, sep, sol->ns, sep, SQRT_SOL(sol->qr[0]), sep, SQRT_SOL(sol->qr[1]), sep, SQRT_SOL(sol->qr[2]), sep, sqvar(sol->qr[3]), sep, sqvar(sol->qr[4]), sep, sqvar(sol->qr[5]), sep, sol->age, sep, sol->ratio); return p - reinterpret_cast(buff); } /* output solution as the form of lat/lon/height -----------------------------*/ int outpos(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt) { double pos[3], dms1[3], dms2[3], P[9], Q[9]; const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); trace(3, "outpos :\n"); ecef2pos(sol->rr, pos); soltocov(sol, P); covenu(pos, P, Q); if (opt->height == 1) { /* geodetic height */ // pos[2] -= geoidh(pos); } if (opt->degf) { deg2dms(pos[0] * R2D, dms1); deg2dms(pos[1] * R2D, dms2); p += sprintf(p, "%s%s%4.0f%s%02.0f%s%08.5f%s%4.0f%s%02.0f%s%08.5f", s, sep, dms1[0], sep, dms1[1], sep, dms1[2], sep, dms2[0], sep, dms2[1], sep, dms2[2]); } else { p += sprintf(p, "%s%s%14.9f%s%14.9f", s, sep, pos[0] * R2D, sep, pos[1] * R2D); } p += sprintf(p, "%s%10.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\n", sep, pos[2], sep, sol->stat, sep, sol->ns, sep, SQRT_SOL(Q[4]), sep, SQRT_SOL(Q[0]), sep, SQRT_SOL(Q[8]), sep, sqvar(Q[1]), sep, sqvar(Q[2]), sep, sqvar(Q[5]), sep, sol->age, sep, sol->ratio); return p - reinterpret_cast(buff); } /* output solution as the form of e/n/u-baseline -----------------------------*/ int outenu(unsigned char *buff, const char *s, const sol_t *sol, const double *rb, const solopt_t *opt) { double pos[3], rr[3], enu[3], P[9], Q[9]; int i; const char *sep = opt2sep(opt); char *p = reinterpret_cast(buff); trace(3, "outenu :\n"); for (i = 0; i < 3; i++) { rr[i] = sol->rr[i] - rb[i]; } ecef2pos(rb, pos); soltocov(sol, P); covenu(pos, P, Q); ecef2enu(pos, rr, enu); p += sprintf(p, "%s%s%14.4f%s%14.4f%s%14.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\n", s, sep, enu[0], sep, enu[1], sep, enu[2], sep, sol->stat, sep, sol->ns, sep, SQRT_SOL(Q[0]), sep, SQRT_SOL(Q[4]), sep, SQRT_SOL(Q[8]), sep, sqvar(Q[1]), sep, sqvar(Q[5]), sep, sqvar(Q[2]), sep, sol->age, sep, sol->ratio); return p - reinterpret_cast(buff); } /* output solution in the form of nmea RMC sentence --------------------------*/ int outnmea_rmc(unsigned char *buff, const sol_t *sol) { static double dirp = 0.0; gtime_t time; double ep[6], pos[3], enuv[3], dms1[3], dms2[3], vel, dir, amag = 0.0; char *p = reinterpret_cast(buff), *q, sum, *emag = (char *)"E"; trace(3, "outnmea_rmc:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p, "$GPRMC,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } time = gpst2utc(sol->time); if (time.sec >= 0.995) { time.time++; time.sec = 0.0; } time2epoch(time, ep); ecef2pos(sol->rr, pos); ecef2enu(pos, sol->rr + 3, enuv); vel = norm_rtk(enuv, 3); if (vel >= 1.0) { dir = atan2(enuv[0], enuv[1]) * R2D; if (dir < 0.0) { dir += 360.0; } dirp = dir; } else { dir = dirp; } deg2dms(fabs(pos[0]) * R2D, dms1); deg2dms(fabs(pos[1]) * R2D, dms2); p += sprintf(p, "$GPRMC,%02.0f%02.0f%05.2f,A,%02.0f%010.7f,%s,%03.0f%010.7f,%s,%4.2f,%4.2f,%02.0f%02.0f%02d,%.1f,%s,%s", ep[3], ep[4], ep[5], dms1[0], dms1[1] + dms1[2] / 60.0, pos[0] >= 0 ? "N" : "S", dms2[0], dms2[1] + dms2[2] / 60.0, pos[1] >= 0 ? "E" : "W", vel / KNOT2M, dir, ep[2], ep[1], static_cast(ep[0]) % 100, amag, emag, sol->stat == SOLQ_DGPS || sol->stat == SOLQ_FLOAT || sol->stat == SOLQ_FIX ? "D" : "A"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } /* output solution in the form of nmea GGA sentence --------------------------*/ int outnmea_gga(unsigned char *buff, const sol_t *sol) { gtime_t time; double h, ep[6], pos[3], dms1[3], dms2[3], dop = 1.0; int solq; char *p = reinterpret_cast(buff), *q, sum; trace(3, "outnmea_gga:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p, "$GPGGA,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } for (solq = 0; solq < 8; solq++) { if (SOLQ_NMEA[solq] == sol->stat) { break; } } if (solq >= 8) { solq = 0; } time = gpst2utc(sol->time); if (time.sec >= 0.995) { time.time++; time.sec = 0.0; } time2epoch(time, ep); ecef2pos(sol->rr, pos); h = 0; //geoidh(pos); deg2dms(fabs(pos[0]) * R2D, dms1); deg2dms(fabs(pos[1]) * R2D, dms2); p += sprintf(p, "$GPGGA,%02.0f%02.0f%05.2f,%02.0f%010.7f,%s,%03.0f%010.7f,%s,%d,%02d,%.1f,%.3f,M,%.3f,M,%.1f,", ep[3], ep[4], ep[5], dms1[0], dms1[1] + dms1[2] / 60.0, pos[0] >= 0 ? "N" : "S", dms2[0], dms2[1] + dms2[2] / 60.0, pos[1] >= 0 ? "E" : "W", solq, sol->ns, dop, pos[2] - h, h, sol->age); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } /* output solution in the form of nmea GSA sentences -------------------------*/ int outnmea_gsa(unsigned char *buff, const sol_t *sol, const ssat_t *ssat) { double azel[MAXSAT * 2], dop[4]; int i, sat, sys, nsat, prn[MAXSAT]; char *p = reinterpret_cast(buff), *q, *s, sum; trace(3, "outnmea_gsa:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p, "$GPGSA,A,1,,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } /* GPGSA: gps/sbas */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } sys = satsys(sat, prn + nsat); if (sys != SYS_GPS && sys != SYS_SBS) { continue; } if (sys == SYS_SBS) { prn[nsat] += 33 - MINPRNSBS; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; p += sprintf(p, "$GPGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += sprintf(p, ",%02d", prn[i]); } else { p += sprintf(p, ","); } } dops(nsat, azel, 0.0, dop); p += sprintf(p, ",%3.1f,%3.1f,%3.1f,1", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GLGSA: glonass */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } if (satsys(sat, prn + nsat) != SYS_GLO) { continue; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; p += sprintf(p, "$GLGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += sprintf(p, ",%02d", prn[i] + 64); } else { p += sprintf(p, ","); } } dops(nsat, azel, 0.0, dop); p += sprintf(p, ",%3.1f,%3.1f,%3.1f,2", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GAGSA: galileo */ for (sat = 1, nsat = 0; sat <= MAXSAT && nsat < 12; sat++) { if (!ssat[sat - 1].vs || ssat[sat - 1].azel[1] <= 0.0) { continue; } if (satsys(sat, prn + nsat) != SYS_GAL) { continue; } for (i = 0; i < 2; i++) { azel[i + nsat * 2] = ssat[sat - 1].azel[i]; } nsat++; } if (nsat > 0) { s = p; p += sprintf(p, "$GAGSA,A,%d", sol->stat <= 0 ? 1 : 3); for (i = 0; i < 12; i++) { if (i < nsat) { p += sprintf(p, ",%02d", prn[i]); } else { p += sprintf(p, ","); } } dops(nsat, azel, 0.0, dop); p += sprintf(p, ",%3.1f,%3.1f,%3.1f,3", dop[1], dop[2], dop[3]); for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } return p - reinterpret_cast(buff); } /* output solution in the form of nmea GSV sentence --------------------------*/ int outnmea_gsv(unsigned char *buff, const sol_t *sol, const ssat_t *ssat) { double az, el, snr; int i, j, k, n, sat, prn, sys, nmsg, sats[MAXSAT]; char *p = reinterpret_cast(buff), *q, *s, sum; trace(3, "outnmea_gsv:\n"); if (sol->stat <= SOLQ_NONE) { p += sprintf(p, "$GPGSV,1,1,0,,,,,,,,,,,,,,,,"); for (q = reinterpret_cast(buff) + 1, sum = 0; *q; q++) { sum ^= *q; } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); return p - reinterpret_cast(buff); } /* GPGSV: gps/sbas */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { sys = satsys(sat, &prn); if (sys != SYS_GPS && sys != SYS_SBS) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += sprintf(p, "$GPGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { if (satsys(sats[k], &prn) == SYS_SBS) { prn += 33 - MINPRNSBS; } az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += sprintf(p, ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += sprintf(p, ",,,,"); } } p += sprintf(p, ",1"); /* L1C/A */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GLGSV: glonass */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { if (satsys(sat, &prn) != SYS_GLO) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += sprintf(p, "$GLGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { satsys(sats[k], &prn); prn += 64; /* 65-99 */ az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += sprintf(p, ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += sprintf(p, ",,,,"); } } p += sprintf(p, ",1"); /* L1C/A */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } /* GAGSV: galileo */ for (sat = 1, n = 0; sat < MAXSAT && n < 12; sat++) { if (satsys(sat, &prn) != SYS_GAL) { continue; } if (ssat[sat - 1].vs && ssat[sat - 1].azel[1] > 0.0) { sats[n++] = sat; } } nmsg = n <= 0 ? 0 : (n - 1) / 4 + 1; for (i = k = 0; i < nmsg; i++) { s = p; p += sprintf(p, "$GAGSV,%d,%d,%02d", nmsg, i + 1, n); for (j = 0; j < 4; j++, k++) { if (k < n) { satsys(sats[k], &prn); /* 1-36 */ az = ssat[sats[k] - 1].azel[0] * R2D; if (az < 0.0) { az += 360.0; } el = ssat[sats[k] - 1].azel[1] * R2D; snr = ssat[sats[k] - 1].snr[0] * 0.25; p += sprintf(p, ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr); } else { p += sprintf(p, ",,,,"); } } p += sprintf(p, ",7"); /* L1BC */ for (q = s + 1, sum = 0; *q; q++) { sum ^= *q; /* check-sum */ } p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A); } return p - reinterpret_cast(buff); } /* output processing options --------------------------------------------------- * output processing options to buffer * args : unsigned char *buff IO output buffer * prcopt_t *opt I processign options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outprcopts(unsigned char *buff, const prcopt_t *opt) { const int sys[] = {SYS_GPS, SYS_GLO, SYS_GAL, SYS_QZS, SYS_SBS, 0}; const char *s1[] = {"single", "dgps", "kinematic", "static", "moving-base", "fixed", "ppp-kinematic", "ppp-static", "ppp-fixed", ""}; const char *s2[] = {"L1", "L1+L2", "L1+L2+L5", "L1+L2+L5+L6", "L1+L2+L5+L6+L7", "L1+L2+L5+L6+L7+L8", ""}; const char *s3[] = {"forward", "backward", "combined"}; const char *s4[] = {"off", "broadcast", "sbas", "iono-free", "estimation", "ionex tec", "qzs", "lex", "vtec_sf", "vtec_ef", "gtec", ""}; const char *s5[] = {"off", "saastamoinen", "sbas", "est ztd", "est ztd+grad", ""}; const char *s6[] = {"broadcast", "precise", "broadcast+sbas", "broadcast+ssr apc", "broadcast+ssr com", "qzss lex", ""}; const char *s7[] = {"gps", "glonass", "galileo", "qzss", "sbas", ""}; const char *s8[] = {"off", "continuous", "instantaneous", "fix and hold", ""}; const char *s9[] = {"off", "on", "auto calib", "external calib", ""}; int i; char *p = reinterpret_cast(buff); trace(3, "outprcopts:\n"); p += sprintf(p, "%s pos mode : %s\n", COMMENTH, s1[opt->mode]); if (PMODE_DGPS <= opt->mode && opt->mode <= PMODE_FIXED) { p += sprintf(p, "%s freqs : %s\n", COMMENTH, s2[opt->nf - 1]); } if (opt->mode > PMODE_SINGLE) { p += sprintf(p, "%s solution : %s\n", COMMENTH, s3[opt->soltype]); } p += sprintf(p, "%s elev mask : %.1f deg\n", COMMENTH, opt->elmin * R2D); if (opt->mode > PMODE_SINGLE) { p += sprintf(p, "%s dynamics : %s\n", COMMENTH, opt->dynamics ? "on" : "off"); p += sprintf(p, "%s tidecorr : %s\n", COMMENTH, opt->tidecorr ? "on" : "off"); } if (opt->mode <= PMODE_FIXED) { p += sprintf(p, "%s ionos opt : %s\n", COMMENTH, s4[opt->ionoopt]); } p += sprintf(p, "%s tropo opt : %s\n", COMMENTH, s5[opt->tropopt]); p += sprintf(p, "%s ephemeris : %s\n", COMMENTH, s6[opt->sateph]); if (opt->navsys != SYS_GPS) { p += sprintf(p, "%s navi sys :", COMMENTH); for (i = 0; sys[i]; i++) { if (opt->navsys & sys[i]) { p += sprintf(p, " %s", s7[i]); } } p += sprintf(p, "\n"); } if (PMODE_KINEMA <= opt->mode && opt->mode <= PMODE_FIXED) { p += sprintf(p, "%s amb res : %s\n", COMMENTH, s8[opt->modear]); if (opt->navsys & SYS_GLO) { p += sprintf(p, "%s amb glo : %s\n", COMMENTH, s9[opt->glomodear]); } if (opt->thresar[0] > 0.0) { p += sprintf(p, "%s val thres : %.1f\n", COMMENTH, opt->thresar[0]); } } if (opt->mode == PMODE_MOVEB && opt->baseline[0] > 0.0) { p += sprintf(p, "%s baseline : %.4f %.4f m\n", COMMENTH, opt->baseline[0], opt->baseline[1]); } for (i = 0; i < 2; i++) { if (opt->mode == PMODE_SINGLE || (i >= 1 && opt->mode > PMODE_FIXED)) { continue; } p += sprintf(p, "%s antenna%d : %-21s (%7.4f %7.4f %7.4f)\n", COMMENTH, i + 1, opt->anttype[i], opt->antdel[i][0], opt->antdel[i][1], opt->antdel[i][2]); } return p - reinterpret_cast(buff); } /* output solution header ------------------------------------------------------ * output solution header to buffer * args : unsigned char *buff IO output buffer * solopt_t *opt I solution options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outsolheads(unsigned char *buff, const solopt_t *opt) { const char *s1[] = {"WGS84", "Tokyo"}, *s2[] = {"ellipsoidal", "geodetic"}; const char *s3[] = {"GPST", "UTC ", "JST "}, *sep = opt2sep(opt); char *p = reinterpret_cast(buff); int timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu); trace(3, "outsolheads:\n"); if (opt->posf == SOLF_NMEA) { return 0; } if (opt->outhead) { p += sprintf(p, "%s (", COMMENTH); if (opt->posf == SOLF_XYZ) { p += sprintf(p, "x/y/z-ecef=WGS84"); } else if (opt->posf == SOLF_ENU) { p += sprintf(p, "e/n/u-baseline=WGS84"); } else { p += sprintf(p, "lat/lon/height=%s/%s", s1[opt->datum], s2[opt->height]); } p += sprintf(p, ",Q=1:fix,2:float,3:sbas,4:dgps,5:single,6:ppp,ns=# of satellites)\n"); } p += sprintf(p, "%s %-*s%s", COMMENTH, (opt->timef ? 16 : 8) + timeu + 1, s3[opt->times], sep); if (opt->posf == SOLF_LLH) { /* lat/lon/hgt */ if (opt->degf) { p += sprintf(p, "%16s%s%16s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "latitude(d'\")", sep, "longitude(d'\")", sep, "height(m)", sep, "Q", sep, "ns", sep, "sdn(m)", sep, "sde(m)", sep, "sdu(m)", sep, "sdne(m)", sep, "sdeu(m)", sep, "sdue(m)", sep, "age(s)", sep, "ratio"); } else { p += sprintf(p, "%14s%s%14s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "latitude(deg)", sep, "longitude(deg)", sep, "height(m)", sep, "Q", sep, "ns", sep, "sdn(m)", sep, "sde(m)", sep, "sdu(m)", sep, "sdne(m)", sep, "sdeu(m)", sep, "sdun(m)", sep, "age(s)", sep, "ratio"); } } else if (opt->posf == SOLF_XYZ) { /* x/y/z-ecef */ p += sprintf(p, "%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "x-ecef(m)", sep, "y-ecef(m)", sep, "z-ecef(m)", sep, "Q", sep, "ns", sep, "sdx(m)", sep, "sdy(m)", sep, "sdz(m)", sep, "sdxy(m)", sep, "sdyz(m)", sep, "sdzx(m)", sep, "age(s)", sep, "ratio"); } else if (opt->posf == SOLF_ENU) { /* e/n/u-baseline */ p += sprintf(p, "%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s%s%6s%s%6s\n", "e-baseline(m)", sep, "n-baseline(m)", sep, "u-baseline(m)", sep, "Q", sep, "ns", sep, "sde(m)", sep, "sdn(m)", sep, "sdu(m)", sep, "sden(m)", sep, "sdnu(m)", sep, "sdue(m)", sep, "age(s)", sep, "ratio"); } return p - reinterpret_cast(buff); } /* output solution body -------------------------------------------------------- * output solution body to buffer * args : unsigned char *buff IO output buffer * sol_t *sol I solution * double *rb I base station position {x,y,z} (ecef) (m) * solopt_t *opt I solution options * return : number of output bytes *-----------------------------------------------------------------------------*/ int outsols(unsigned char *buff, const sol_t *sol, const double *rb, const solopt_t *opt) { gtime_t time, ts = {0, 0.0}; double gpst; int week, timeu; const char *sep = opt2sep(opt); char s[255]; unsigned char *p = buff; trace(3, "outsols :\n"); if (opt->posf == SOLF_NMEA) { if (opt->nmeaintv[0] < 0.0) { return 0; } if (!screent(sol->time, ts, ts, opt->nmeaintv[0])) { return 0; } } if (sol->stat <= SOLQ_NONE || (opt->posf == SOLF_ENU && norm_rtk(rb, 3) <= 0.0)) { return 0; } timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu); time = sol->time; if (opt->times >= TIMES_UTC) { time = gpst2utc(time); } if (opt->times == TIMES_JST) { time = timeadd(time, 9 * 3600.0); } if (opt->timef) { time2str(time, s, timeu); } else { gpst = time2gpst(time, &week); if (86400 * 7 - gpst < 0.5 / pow(10.0, timeu)) { week++; gpst = 0.0; } snprintf(s, 255, "%4d%s%*.*f", week, sep, 6 + (timeu <= 0 ? 0 : timeu + 1), timeu, gpst); } switch (opt->posf) { case SOLF_LLH: p += outpos(p, s, sol, opt); break; case SOLF_XYZ: p += outecef(p, s, sol, opt); break; case SOLF_ENU: p += outenu(p, s, sol, rb, opt); break; case SOLF_NMEA: p += outnmea_rmc(p, sol); p += outnmea_gga(p, sol); break; } return p - buff; } /* output solution extended ---------------------------------------------------- * output solution extended information * args : unsigned char *buff IO output buffer * sol_t *sol I solution * ssat_t *ssat I satellite status * solopt_t *opt I solution options * return : number of output bytes * notes : only support nmea *-----------------------------------------------------------------------------*/ int outsolexs(unsigned char *buff, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt) { gtime_t ts = {0, 0.0}; unsigned char *p = buff; trace(3, "outsolexs:\n"); if (opt->posf == SOLF_NMEA) { if (opt->nmeaintv[1] < 0.0) { return 0; } if (!screent(sol->time, ts, ts, opt->nmeaintv[1])) { return 0; } } if (opt->posf == SOLF_NMEA) { p += outnmea_gsa(p, sol, ssat); p += outnmea_gsv(p, sol, ssat); } return p - buff; } /* output processing option ---------------------------------------------------- * output processing option to file * args : FILE *fp I output file pointer * prcopt_t *opt I processing options * return : none *-----------------------------------------------------------------------------*/ void outprcopt(FILE *fp, const prcopt_t *opt) { unsigned char buff[MAXSOLMSG + 1]; int n; trace(3, "outprcopt:\n"); if ((n = outprcopts(buff, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution header ------------------------------------------------------ * output solution heade to file * args : FILE *fp I output file pointer * solopt_t *opt I solution options * return : none *-----------------------------------------------------------------------------*/ void outsolhead(FILE *fp, const solopt_t *opt) { unsigned char buff[MAXSOLMSG + 1]; int n; trace(3, "outsolhead:\n"); if ((n = outsolheads(buff, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution body -------------------------------------------------------- * output solution body to file * args : FILE *fp I output file pointer * sol_t *sol I solution * double *rb I base station position {x,y,z} (ecef) (m) * solopt_t *opt I solution options * return : none *-----------------------------------------------------------------------------*/ void outsol(FILE *fp, const sol_t *sol, const double *rb, const solopt_t *opt) { unsigned char buff[MAXSOLMSG + 1]; int n; trace(3, "outsol :\n"); if ((n = outsols(buff, sol, rb, opt)) > 0) { fwrite(buff, n, 1, fp); } } /* output solution extended ---------------------------------------------------- * output solution extended information to file * args : FILE *fp I output file pointer * sol_t *sol I solution * ssat_t *ssat I satellite status * solopt_t *opt I solution options * return : output size (bytes) * notes : only support nmea *-----------------------------------------------------------------------------*/ void outsolex(FILE *fp, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt) { unsigned char buff[MAXSOLMSG + 1]; int n; trace(3, "outsolex:\n"); if ((n = outsolexs(buff, sol, ssat, opt)) > 0) { fwrite(buff, n, 1, fp); } } src/algorithms/libs/rtklib/rtklib_solution.h000066400000000000000000000137501352176506000216450ustar00rootroot00000000000000/*! * \file rtklib_solution.h * \brief solution functions headers * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *-----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_SOLUTION_H_ #define GNSS_SDR_RTKLIB_SOLUTION_H_ #include "rtklib.h" #define COMMENTH "%" /* comment line indicator for solution */ #define MSG_DISCONN "$_DISCONNECT\r\n" /* disconnect message */ const char *opt2sep(const solopt_t *opt); int tonum(char *buff, const char *sep, double *v); double sqvar(double covar); double dmm2deg(double dmm); void septime(double t, double *t1, double *t2, double *t3); void soltocov(const sol_t *sol, double *P); void covtosol(const double *P, sol_t *sol); int decode_nmearmc(char **val, int n, sol_t *sol); int decode_nmeagga(char **val, int n, sol_t *sol); int decode_nmea(char *buff, sol_t *sol); char *decode_soltime(char *buff, const solopt_t *opt, gtime_t *time); int decode_solxyz(char *buff, const solopt_t *opt, sol_t *sol); int decode_solllh(char *buff, const solopt_t *opt, sol_t *sol); int decode_solenu(char *buff, const solopt_t *opt, sol_t *sol); int decode_solgsi(char *buff, const solopt_t *opt, sol_t *sol); int decode_solpos(char *buff, const solopt_t *opt, sol_t *sol); void decode_refpos(char *buff, const solopt_t *opt, double *rb); int decode_sol(char *buff, const solopt_t *opt, sol_t *sol, double *rb); void decode_solopt(char *buff, solopt_t *opt); void readsolopt(FILE *fp, solopt_t *opt); int inputsol(unsigned char data, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf); int readsoldata(FILE *fp, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf); int cmpsol(const void *p1, const void *p2); int sort_solbuf(solbuf_t *solbuf); int readsolt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, int qflag, solbuf_t *solbuf); int readsol(char *files[], int nfile, solbuf_t *sol); int addsol(solbuf_t *solbuf, const sol_t *sol); sol_t *getsol(solbuf_t *solbuf, int index); void initsolbuf(solbuf_t *solbuf, int cyclic, int nmax); void freesolbuf(solbuf_t *solbuf); void freesolstatbuf(solstatbuf_t *solstatbuf); int cmpsolstat(const void *p1, const void *p2); int sort_solstat(solstatbuf_t *statbuf); int decode_solstat(char *buff, solstat_t *stat); void addsolstat(solstatbuf_t *statbuf, const solstat_t *stat); int readsolstatdata(FILE *fp, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf); int readsolstatt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf); int readsolstat(char *files[], int nfile, solstatbuf_t *statbuf); int outecef(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt); int outpos(unsigned char *buff, const char *s, const sol_t *sol, const solopt_t *opt); int outenu(unsigned char *buff, const char *s, const sol_t *sol, const double *rb, const solopt_t *opt); int outnmea_rmc(unsigned char *buff, const sol_t *sol); int outnmea_gga(unsigned char *buff, const sol_t *sol); int outnmea_gsa(unsigned char *buff, const sol_t *sol, const ssat_t *ssat); int outnmea_gsv(unsigned char *buff, const sol_t *sol, const ssat_t *ssat); int outprcopts(unsigned char *buff, const prcopt_t *opt); int outsolheads(unsigned char *buff, const solopt_t *opt); int outsols(unsigned char *buff, const sol_t *sol, const double *rb, const solopt_t *opt); int outsolexs(unsigned char *buff, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt); void outprcopt(FILE *fp, const prcopt_t *opt); void outsolhead(FILE *fp, const solopt_t *opt); void outsol(FILE *fp, const sol_t *sol, const double *rb, const solopt_t *opt); void outsolex(FILE *fp, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt); #endif src/algorithms/libs/rtklib/rtklib_stream.cc000066400000000000000000002510761352176506000214270ustar00rootroot00000000000000/*! * \file rtklib_stream.cc * \brief streaming functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_stream.h" #include "rtklib_rtkcmn.h" #include "rtklib_solution.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* global options ------------------------------------------------------------*/ static int toinact = 10000; /* inactive timeout (ms) */ static int ticonnect = 10000; /* interval to re-connect (ms) */ static int tirate = 1000; /* avraging time for data rate (ms) */ static int buffsize = 32768; /* receive/send buffer size (bytes) */ static char localdir[1024] = ""; /* local directory for ftp/http */ static char proxyaddr[256] = ""; /* http/ntrip/ftp proxy address */ static unsigned int tick_master = 0; /* time tick master for replay */ static int fswapmargin = 30; /* file swap margin (s) */ /* open serial ---------------------------------------------------------------*/ serial_t *openserial(const char *path, int mode, char *msg) { const int br[] = { 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400}; serial_t *serial; int i, brate = 9600, bsize = 8, stopb = 1; char *p, parity = 'N', dev[128], port[128], fctr[64] = ""; const speed_t bs[] = { B300, B600, B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400}; struct termios ios { }; int rw = 0; tracet(3, "openserial: path=%s mode=%d\n", path, mode); if (!(serial = static_cast(malloc(sizeof(serial_t))))) { return nullptr; } if ((p = strchr(const_cast(path), ':'))) { strncpy(port, path, p - path); port[p - path] = '\0'; sscanf(p, ":%d:%d:%c:%d:%s", &brate, &bsize, &parity, &stopb, fctr); } else if (strlen(path) < 128) { strcpy(port, path); } for (i = 0; i < 10; i++) { if (br[i] == brate) { break; } } if (i >= 11) { sprintf(msg, "bitrate error (%d)", brate); tracet(1, "openserial: %s path=%s\n", msg, path); free(serial); return nullptr; } parity = static_cast(toupper(static_cast(parity))); // sprintf(dev, "/dev/%s", port); This line triggers a warning. Replaced by: std::string s_aux = "/dev/" + std::string(port); s_aux.resize(128, '\0'); int n = s_aux.length(); for (int i = 0; i < n; i++) { dev[i] = s_aux[i]; } if (n == 0) { dev[0] = '\0'; } if ((mode & STR_MODE_R) && (mode & STR_MODE_W)) { rw = O_RDWR; } else if (mode & STR_MODE_R) { rw = O_RDONLY; } else if (mode & STR_MODE_W) { rw = O_WRONLY; } if ((serial->dev = open(dev, rw | O_NOCTTY | O_NONBLOCK)) < 0) { sprintf(msg, "device open error (%d)", errno); tracet(1, "openserial: %s dev=%s\n", msg, dev); free(serial); return nullptr; } tcgetattr(serial->dev, &ios); ios.c_iflag = 0; ios.c_oflag = 0; ios.c_lflag = 0; /* non-canonical */ ios.c_cc[VMIN] = 0; /* non-block-mode */ ios.c_cc[VTIME] = 0; cfsetospeed(&ios, bs[i]); cfsetispeed(&ios, bs[i]); ios.c_cflag |= bsize == 7 ? CS7 : CS8; ios.c_cflag |= parity == 'O' ? (PARENB | PARODD) : (parity == 'E' ? PARENB : 0); ios.c_cflag |= stopb == 2 ? CSTOPB : 0; ios.c_cflag |= !strcmp(fctr, "rts") ? CRTSCTS : 0; tcsetattr(serial->dev, TCSANOW, &ios); tcflush(serial->dev, TCIOFLUSH); return serial; } /* close serial --------------------------------------------------------------*/ void closeserial(serial_t *serial) { if (!serial) { return; } tracet(3, "closeserial: dev=%d\n", serial->dev); close(serial->dev); free(serial); } /* read serial ---------------------------------------------------------------*/ int readserial(serial_t *serial, unsigned char *buff, int n, char *msg __attribute__((unused))) { int nr; if (!serial) { return 0; } tracet(4, "readserial: dev=%d n=%d\n", serial->dev, n); if ((nr = read(serial->dev, buff, n)) < 0) { return 0; } tracet(5, "readserial: exit dev=%d nr=%d\n", serial->dev, nr); return nr; } /* write serial --------------------------------------------------------------*/ int writeserial(serial_t *serial, unsigned char *buff, int n, char *msg __attribute__((unused))) { int ns; if (!serial) { return 0; } tracet(3, "writeserial: dev=%d n=%d\n", serial->dev, n); if ((ns = write(serial->dev, buff, n)) < 0) { return 0; } tracet(5, "writeserial: exit dev=%d ns=%d\n", serial->dev, ns); return ns; } /* get state serial ----------------------------------------------------------*/ int stateserial(serial_t *serial) { return !serial ? 0 : (serial->error ? -1 : 2); } /* open file -----------------------------------------------------------------*/ int openfile_(file_t *file, gtime_t time, char *msg) { FILE *fp; char *rw, tagpath[MAXSTRPATH + 4] = ""; char tagh[TIMETAGH_LEN + 1] = ""; tracet(3, "openfile_: path=%s time=%s\n", file->path, time_str(time, 0)); file->time = utc2gpst(timeget()); file->tick = file->tick_f = tickget(); file->fpos = 0; /* use stdin or stdout if file path is null */ if (!*file->path) { file->fp = file->mode & STR_MODE_R ? stdin : stdout; return 1; } /* replace keywords */ reppath(file->path, file->openpath, time, "", ""); /* create directory */ if ((file->mode & STR_MODE_W) && !(file->mode & STR_MODE_R)) { createdir(file->openpath); } if (file->mode & STR_MODE_R) { rw = (char *)"rb"; } else { rw = (char *)"wb"; } if (!(file->fp = fopen(file->openpath, rw))) { sprintf(msg, "file open error: %s", file->openpath); tracet(1, "openfile: %s\n", msg); return 0; } tracet(4, "openfile_: open file %s (%s)\n", file->openpath, rw); sprintf(tagpath, "%s.tag", file->openpath); if (file->timetag) { /* output/sync time-tag */ if (!(file->fp_tag = fopen(tagpath, rw))) { sprintf(msg, "tag open error: %s", tagpath); tracet(1, "openfile: %s\n", msg); fclose(file->fp); return 0; } tracet(4, "openfile_: open tag file %s (%s)\n", tagpath, rw); if (file->mode & STR_MODE_R) { if (fread(&tagh, TIMETAGH_LEN, 1, file->fp_tag) == 1 && fread(&file->time, sizeof(file->time), 1, file->fp_tag) == 1) { memcpy(&file->tick_f, tagh + TIMETAGH_LEN - 4, sizeof(file->tick_f)); } else { file->tick_f = 0; } /* adust time to read playback file */ timeset(file->time); } else { sprintf(tagh, "TIMETAG RTKLIB %s", VER_RTKLIB); memcpy(tagh + TIMETAGH_LEN - 4, &file->tick_f, sizeof(file->tick_f)); fwrite(&tagh, 1, TIMETAGH_LEN, file->fp_tag); fwrite(&file->time, 1, sizeof(file->time), file->fp_tag); /* time tag file structure */ /* HEADER(60)+TICK(4)+TIME(12)+ */ /* TICK0(4)+FPOS0(4/8)+ */ /* TICK1(4)+FPOS1(4/8)+... */ } } else if (file->mode & STR_MODE_W) { /* remove time-tag */ if ((fp = fopen(tagpath, "rbe"))) { fclose(fp); if (remove(tagpath) != 0) { trace(1, "Error removing file"); } } } return 1; } /* close file ----------------------------------------------------------------*/ void closefile_(file_t *file) { tracet(3, "closefile_: path=%s\n", file->path); if (file->fp) { fclose(file->fp); } if (file->fp_tag) { fclose(file->fp_tag); } if (file->fp_tmp) { fclose(file->fp_tmp); } if (file->fp_tag_tmp) { fclose(file->fp_tag_tmp); } file->fp = file->fp_tag = file->fp_tmp = file->fp_tag_tmp = nullptr; } /* open file (path=filepath[::T[::+][::x]][::S=swapintv]) --------*/ file_t *openfile(const char *path, int mode, char *msg) { file_t *file; gtime_t time, time0 = {0, 0.0}; double speed = 0.0, start = 0.0, swapintv = 0.0; char *p; int timetag = 0; tracet(3, "openfile: path=%s mode=%d\n", path, mode); if (!(mode & (STR_MODE_R | STR_MODE_W))) { return nullptr; } /* file options */ for (p = const_cast(path); (p = strstr(p, "::")); p += 2) { /* file options */ if (*(p + 2) == 'T') { timetag = 1; } else if (*(p + 2) == '+') { sscanf(p + 2, "+%lf", &start); } else if (*(p + 2) == 'x') { sscanf(p + 2, "x%lf", &speed); } else if (*(p + 2) == 'S') { sscanf(p + 2, "S=%lf", &swapintv); } } if (start <= 0.0) { start = 0.0; } if (swapintv <= 0.0) { swapintv = 0.0; } if (!(file = static_cast(malloc(sizeof(file_t))))) { return nullptr; } file->fp = file->fp_tag = file->fp_tmp = file->fp_tag_tmp = nullptr; if (strlen(path) < MAXSTRPATH) { strcpy(file->path, path); } if ((p = strstr(file->path, "::"))) { *p = '\0'; } file->openpath[0] = '\0'; file->mode = mode; file->timetag = timetag; file->repmode = 0; file->offset = 0; file->time = file->wtime = time0; file->tick = file->tick_f = file->fpos = 0; file->start = start; file->speed = speed; file->swapintv = swapintv; initlock(&file->lock); time = utc2gpst(timeget()); /* open new file */ if (!openfile_(file, time, msg)) { free(file); return nullptr; } return file; } /* close file ----------------------------------------------------------------*/ void closefile(file_t *file) { if (!file) { return; } tracet(3, "closefile: fp=%d\n", file->fp); closefile_(file); free(file); } /* open new swap file --------------------------------------------------------*/ void swapfile(file_t *file, gtime_t time, char *msg) { char openpath[MAXSTRPATH]; tracet(3, "swapfile: fp=%d time=%s\n", file->fp, time_str(time, 0)); /* return if old swap file open */ if (file->fp_tmp || file->fp_tag_tmp) { return; } /* check path of new swap file */ reppath(file->path, openpath, time, "", ""); if (!strcmp(openpath, file->openpath)) { tracet(2, "swapfile: no need to swap %s\n", openpath); return; } /* save file pointer to temporary pointer */ file->fp_tmp = file->fp; file->fp_tag_tmp = file->fp_tag; /* open new swap file */ openfile_(file, time, msg); } /* close old swap file -------------------------------------------------------*/ void swapclose(file_t *file) { tracet(3, "swapclose: fp_tmp=%d\n", file->fp_tmp); if (file->fp_tmp) { fclose(file->fp_tmp); } if (file->fp_tag_tmp) { fclose(file->fp_tag_tmp); } file->fp_tmp = file->fp_tag_tmp = nullptr; } /* get state file ------------------------------------------------------------*/ int statefile(file_t *file) { return file ? 2 : 0; } /* read file -----------------------------------------------------------------*/ int readfile(file_t *file, unsigned char *buff, int nmax, char *msg) { struct timeval tv = {0, 0}; fd_set rs; unsigned int t, tick; int nr = 0; size_t fpos; if (!file) { return 0; } tracet(4, "readfile: fp=%d nmax=%d\n", file->fp, nmax); if (file->fp == stdin) { /* input from stdin */ FD_ZERO(&rs); FD_SET(0, &rs); if (!select(1, &rs, nullptr, nullptr, &tv)) { return 0; } if ((nr = read(0, buff, nmax)) < 0) { return 0; } return nr; } if (file->fp_tag) { if (file->repmode) { /* slave */ t = (tick_master + file->offset); } else { /* master */ t = static_cast((tickget() - file->tick) * file->speed + file->start * 1000.0); } for (;;) { /* seek file position */ if (fread(&tick, sizeof(tick), 1, file->fp_tag) < 1 || fread(&fpos, sizeof(fpos), 1, file->fp_tag) < 1) { if (fseek(file->fp, 0, SEEK_END) != 0) { trace(1, "fseek error"); } sprintf(msg, "end"); break; } if (file->repmode || file->speed > 0.0) { if (static_cast(tick - t) < 1) { continue; } } if (!file->repmode) { tick_master = tick; } sprintf(msg, "T%+.1fs", static_cast(tick) < 0 ? 0.0 : static_cast(tick) / 1000.0); if (static_cast(fpos - file->fpos) >= nmax) { if (fseek(file->fp, fpos, SEEK_SET) != 0) { trace(1, "Error fseek"); } file->fpos = fpos; return 0; } nmax = static_cast(fpos - file->fpos); if (file->repmode || file->speed > 0.0) { if (fseek(file->fp_tag, -static_cast(sizeof(tick) + sizeof(fpos)), SEEK_CUR) != 0) { trace(1, "Error fseek"); } } break; } } if (nmax > 0) { nr = fread(buff, 1, nmax, file->fp); file->fpos += nr; if (nr <= 0) { sprintf(msg, "end"); } } tracet(5, "readfile: fp=%d nr=%d fpos=%d\n", file->fp, nr, file->fpos); return nr; } /* write file ----------------------------------------------------------------*/ int writefile(file_t *file, unsigned char *buff, int n, char *msg) { gtime_t wtime; unsigned int ns, tick = tickget(); int week1, week2; double tow1, tow2, intv; size_t fpos, fpos_tmp; if (!file) { return 0; } tracet(3, "writefile: fp=%d n=%d\n", file->fp, n); wtime = utc2gpst(timeget()); /* write time in gpst */ /* swap writing file */ if (file->swapintv > 0.0 && file->wtime.time != 0) { intv = file->swapintv * 3600.0; tow1 = time2gpst(file->wtime, &week1); tow2 = time2gpst(wtime, &week2); tow2 += 604800.0 * (week2 - week1); /* open new swap file */ if (floor((tow1 + fswapmargin) / intv) < floor((tow2 + fswapmargin) / intv)) { swapfile(file, timeadd(wtime, fswapmargin), msg); } /* close old swap file */ if (floor((tow1 - fswapmargin) / intv) < floor((tow2 - fswapmargin) / intv)) { swapclose(file); } } if (!file->fp) { return 0; } ns = fwrite(buff, 1, n, file->fp); fpos = ftell(file->fp); fflush(file->fp); file->wtime = wtime; if (file->fp_tmp) { fwrite(buff, 1, n, file->fp_tmp); fpos_tmp = ftell(file->fp_tmp); fflush(file->fp_tmp); } if (file->fp_tag) { tick -= file->tick; fwrite(&tick, 1, sizeof(tick), file->fp_tag); fwrite(&fpos, 1, sizeof(fpos), file->fp_tag); fflush(file->fp_tag); if (file->fp_tag_tmp) { fwrite(&tick, 1, sizeof(tick), file->fp_tag_tmp); fwrite(&fpos_tmp, 1, sizeof(fpos_tmp), file->fp_tag_tmp); fflush(file->fp_tag_tmp); } } tracet(5, "writefile: fp=%d ns=%d tick=%5d fpos=%d\n", file->fp, ns, tick, fpos); return static_cast(ns); } /* sync files by time-tag ----------------------------------------------------*/ void syncfile(file_t *file1, file_t *file2) { if (!file1->fp_tag || !file2->fp_tag) { return; } file1->repmode = 0; file2->repmode = 1; file2->offset = static_cast(file1->tick_f - file2->tick_f); } /* decode tcp/ntrip path (path=[user[:passwd]@]addr[:port][/mntpnt[:str]]) ---*/ void decodetcppath(const char *path, char *addr, char *port, char *user, char *passwd, char *mntpnt, char *str) { char buff[MAXSTRPATH], *p, *q; tracet(4, "decodetcpepath: path=%s\n", path); if (port) { *port = '\0'; } if (user) { *user = '\0'; } if (passwd) { *passwd = '\0'; } if (mntpnt) { *mntpnt = '\0'; } if (str) { *str = '\0'; } if (strlen(path) < MAXSTRPATH) { strcpy(buff, path); } if (!(p = strrchr(buff, '@'))) { p = buff; } if ((p = strchr(p, '/'))) { if ((q = strchr(p + 1, ':'))) { *q = '\0'; if (str) { strcpy(str, q + 1); } } *p = '\0'; if (mntpnt) { strcpy(mntpnt, p + 1); } } if ((p = strrchr(buff, '@'))) { *p++ = '\0'; if ((q = strchr(buff, ':'))) { *q = '\0'; if (passwd) { strcpy(passwd, q + 1); } } if (user) { strcpy(user, buff); } } else { p = buff; } if ((q = strchr(p, ':'))) { *q = '\0'; if (port) { strcpy(port, q + 1); } } if (addr) { strcpy(addr, p); } } /* get socket error ----------------------------------------------------------*/ int errsock(void) { return errno; } /* set socket option ---------------------------------------------------------*/ int setsock(socket_t sock, char *msg) { int bs = buffsize, mode = 1; struct timeval tv = {0, 0}; tracet(3, "setsock: sock=%d\n", sock); if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&tv), sizeof(tv)) == -1 || setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&tv), sizeof(tv)) == -1) { sprintf(msg, "sockopt error: notimeo"); tracet(1, "setsock: setsockopt error 1 sock=%d err=%d\n", sock, errsock()); closesocket(sock); return 0; } if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&bs), sizeof(bs)) == -1 || setsockopt(sock, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&bs), sizeof(bs)) == -1) { tracet(1, "setsock: setsockopt error 2 sock=%d err=%d bs=%d\n", sock, errsock(), bs); sprintf(msg, "sockopt error: bufsiz"); } if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&mode), sizeof(mode)) == -1) { tracet(1, "setsock: setsockopt error 3 sock=%d err=%d\n", sock, errsock()); sprintf(msg, "sockopt error: nodelay"); } return 1; } /* non-block accept ----------------------------------------------------------*/ socket_t accept_nb(socket_t sock, struct sockaddr *addr, socklen_t *len) { struct timeval tv = {0, 0}; fd_set rs; FD_ZERO(&rs); FD_SET(sock, &rs); if (!select(sock + 1, &rs, nullptr, nullptr, &tv)) { return 0; } return accept(sock, addr, len); } /* non-block connect ---------------------------------------------------------*/ int connect_nb(socket_t sock, struct sockaddr *addr, socklen_t len) { struct timeval tv = {0, 0}; fd_set rs, ws; int err, flag; flag = fcntl(sock, F_GETFL, 0); if (fcntl(sock, F_SETFL, flag | O_NONBLOCK) == -1) { trace(1, "fcntl error"); } if (connect(sock, addr, len) == -1) { err = errsock(); if (err != EISCONN && err != EINPROGRESS && err != EALREADY) { return -1; } FD_ZERO(&rs); FD_SET(sock, &rs); ws = rs; if (select(sock + 1, &rs, &ws, nullptr, &tv) == 0) { return 0; } } return 1; } /* non-block receive ---------------------------------------------------------*/ int recv_nb(socket_t sock, unsigned char *buff, int n) { struct timeval tv = {0, 0}; fd_set rs; FD_ZERO(&rs); FD_SET(sock, &rs); if (!select(sock + 1, &rs, nullptr, nullptr, &tv)) { return 0; } return recv(sock, reinterpret_cast(buff), n, 0); } /* non-block send ------------------------------------------------------------*/ int send_nb(socket_t sock, unsigned char *buff, int n) { struct timeval tv = {0, 0}; fd_set ws; FD_ZERO(&ws); FD_SET(sock, &ws); if (!select(sock + 1, nullptr, &ws, nullptr, &tv)) { return 0; } return send(sock, reinterpret_cast(buff), n, 0); } /* generate tcp socket -------------------------------------------------------*/ int gentcp(tcp_t *tcp, int type, char *msg) { struct hostent *hp; #ifdef SVR_REUSEADDR int opt = 1; #endif tracet(3, "gentcp: type=%d\n", type); /* generate socket */ if ((tcp->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { sprintf(msg, "socket error (%d)", errsock()); tracet(1, "gentcp: socket error err=%d\n", errsock()); tcp->state = -1; return 0; } if (!setsock(tcp->sock, msg)) { tcp->state = -1; return 0; } memset(&tcp->addr, 0, sizeof(tcp->addr)); tcp->addr.sin_family = AF_INET; tcp->addr.sin_port = htons(tcp->port); if (type == 0) { /* server socket */ #ifdef SVR_REUSEADDR /* multiple-use of server socket */ setsockopt(tcp->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); #endif if (bind(tcp->sock, reinterpret_cast(&tcp->addr), sizeof(tcp->addr)) == -1) { sprintf(msg, "bind error (%d) : %d", errsock(), tcp->port); tracet(1, "gentcp: bind error port=%d err=%d\n", tcp->port, errsock()); closesocket(tcp->sock); tcp->state = -1; return 0; } listen(tcp->sock, 5); } else { /* client socket */ if (!(hp = gethostbyname(tcp->saddr))) { sprintf(msg, "address error (%s)", tcp->saddr); tracet(1, "gentcp: gethostbyname error addr=%s err=%d\n", tcp->saddr, errsock()); closesocket(tcp->sock); tcp->state = 0; tcp->tcon = ticonnect; tcp->tdis = tickget(); return 0; } memcpy(&tcp->addr.sin_addr, hp->h_addr, hp->h_length); } tcp->state = 1; tcp->tact = tickget(); tracet(5, "gentcp: exit sock=%d\n", tcp->sock); return 1; } /* disconnect tcp ------------------------------------------------------------*/ void discontcp(tcp_t *tcp, int tcon) { tracet(3, "discontcp: sock=%d tcon=%d\n", tcp->sock, tcon); closesocket(tcp->sock); tcp->state = 0; tcp->tcon = tcon; tcp->tdis = tickget(); } /* open tcp server -----------------------------------------------------------*/ tcpsvr_t *opentcpsvr(const char *path, char *msg) { tcpsvr_t *tcpsvr, tcpsvr0{}; char port[256] = ""; tracet(3, "opentcpsvr: path=%s\n", path); if (!(tcpsvr = static_cast(malloc(sizeof(tcpsvr_t))))) { return nullptr; } *tcpsvr = tcpsvr0; decodetcppath(path, tcpsvr->svr.saddr, port, nullptr, nullptr, nullptr, nullptr); if (sscanf(port, "%d", &tcpsvr->svr.port) < 1) { sprintf(msg, "port error: %s", port); tracet(1, "opentcpsvr: port error port=%s\n", port); free(tcpsvr); return nullptr; } if (!gentcp(&tcpsvr->svr, 0, msg)) { free(tcpsvr); return nullptr; } tcpsvr->svr.tcon = 0; return tcpsvr; } /* close tcp server ----------------------------------------------------------*/ void closetcpsvr(tcpsvr_t *tcpsvr) { int i; tracet(3, "closetcpsvr:\n"); for (i = 0; i < MAXCLI; i++) { if (tcpsvr->cli[i].state) { closesocket(tcpsvr->cli[i].sock); } } closesocket(tcpsvr->svr.sock); free(tcpsvr); } /* update tcp server ---------------------------------------------------------*/ void updatetcpsvr(tcpsvr_t *tcpsvr, char *msg) { char saddr[256] = ""; int i, j, n = 0; tracet(3, "updatetcpsvr: state=%d\n", tcpsvr->svr.state); if (tcpsvr->svr.state == 0) { return; } for (i = 0; i < MAXCLI; i++) { if (tcpsvr->cli[i].state) { continue; } for (j = i + 1; j < MAXCLI; j++) { if (!tcpsvr->cli[j].state) { continue; } tcpsvr->cli[i] = tcpsvr->cli[j]; tcpsvr->cli[j].state = 0; break; } } for (i = 0; i < MAXCLI; i++) { if (!tcpsvr->cli[i].state) { continue; } strcpy(saddr, tcpsvr->cli[i].saddr); n++; } if (n == 0) { tcpsvr->svr.state = 1; sprintf(msg, "waiting..."); return; } tcpsvr->svr.state = 2; if (n == 1) { sprintf(msg, "%s", saddr); } else { sprintf(msg, "%d clients", n); } } /* accept client connection --------------------------------------------------*/ int accsock(tcpsvr_t *tcpsvr, char *msg) { struct sockaddr_in addr { }; socket_t sock; socklen_t len = sizeof(addr); int i, err; tracet(3, "accsock: sock=%d\n", tcpsvr->svr.sock); for (i = 0; i < MAXCLI; i++) { if (tcpsvr->cli[i].state == 0) { break; } } if (i >= MAXCLI) { return 0; /* too many client */ } if ((sock = accept_nb(tcpsvr->svr.sock, reinterpret_cast(&addr), &len)) == -1) { err = errsock(); sprintf(msg, "accept error (%d)", err); tracet(1, "accsock: accept error sock=%d err=%d\n", tcpsvr->svr.sock, err); closesocket(tcpsvr->svr.sock); tcpsvr->svr.state = 0; return 0; } if (sock == 0) { return 0; } tcpsvr->cli[i].sock = sock; if (!setsock(tcpsvr->cli[i].sock, msg)) { return 0; } memcpy(&tcpsvr->cli[i].addr, &addr, sizeof(addr)); if (strlen(inet_ntoa(addr.sin_addr)) < 256) { strcpy(tcpsvr->cli[i].saddr, inet_ntoa(addr.sin_addr)); } sprintf(msg, "%s", tcpsvr->cli[i].saddr); tracet(2, "accsock: connected sock=%d addr=%s\n", tcpsvr->cli[i].sock, tcpsvr->cli[i].saddr); tcpsvr->cli[i].state = 2; tcpsvr->cli[i].tact = tickget(); return 1; } /* wait socket accept --------------------------------------------------------*/ int waittcpsvr(tcpsvr_t *tcpsvr, char *msg) { tracet(4, "waittcpsvr: sock=%d state=%d\n", tcpsvr->svr.sock, tcpsvr->svr.state); if (tcpsvr->svr.state <= 0) { return 0; } while (accsock(tcpsvr, msg)) { ; } updatetcpsvr(tcpsvr, msg); return tcpsvr->svr.state == 2; } /* read tcp server -----------------------------------------------------------*/ int readtcpsvr(tcpsvr_t *tcpsvr, unsigned char *buff, int n, char *msg) { int nr, err; tracet(4, "readtcpsvr: state=%d n=%d\n", tcpsvr->svr.state, n); if (!waittcpsvr(tcpsvr, msg) || tcpsvr->cli[0].state != 2) { return 0; } if ((nr = recv_nb(tcpsvr->cli[0].sock, buff, n)) == -1) { err = errsock(); tracet(1, "readtcpsvr: recv error sock=%d err=%d\n", tcpsvr->cli[0].sock, err); sprintf(msg, "recv error (%d)", err); discontcp(&tcpsvr->cli[0], ticonnect); updatetcpsvr(tcpsvr, msg); return 0; } if (nr > 0) { tcpsvr->cli[0].tact = tickget(); } tracet(5, "readtcpsvr: exit sock=%d nr=%d\n", tcpsvr->cli[0].sock, nr); return nr; } /* write tcp server ----------------------------------------------------------*/ int writetcpsvr(tcpsvr_t *tcpsvr, unsigned char *buff, int n, char *msg) { int i, ns = 0, err; tracet(3, "writetcpsvr: state=%d n=%d\n", tcpsvr->svr.state, n); if (!waittcpsvr(tcpsvr, msg)) { return 0; } for (i = 0; i < MAXCLI; i++) { if (tcpsvr->cli[i].state != 2) { continue; } if ((ns = send_nb(tcpsvr->cli[i].sock, buff, n)) == -1) { err = errsock(); tracet(1, "writetcpsvr: send error i=%d sock=%d err=%d\n", i, tcpsvr->cli[i].sock, err); sprintf(msg, "send error (%d)", err); discontcp(&tcpsvr->cli[i], ticonnect); updatetcpsvr(tcpsvr, msg); return 0; } if (ns > 0) { tcpsvr->cli[i].tact = tickget(); } tracet(5, "writetcpsvr: send i=%d ns=%d\n", i, ns); } return ns; } /* get state tcp server ------------------------------------------------------*/ int statetcpsvr(tcpsvr_t *tcpsvr) { return tcpsvr ? tcpsvr->svr.state : 0; } /* connect server ------------------------------------------------------------*/ int consock(tcpcli_t *tcpcli, char *msg) { int stat, err; tracet(3, "consock: sock=%d\n", tcpcli->svr.sock); /* wait re-connect */ if (tcpcli->svr.tcon < 0 || (tcpcli->svr.tcon > 0 && static_cast(tickget() - tcpcli->svr.tdis) < tcpcli->svr.tcon)) { return 0; } /* non-block connect */ if ((stat = connect_nb(tcpcli->svr.sock, reinterpret_cast(&tcpcli->svr.addr), sizeof(tcpcli->svr.addr))) == -1) { err = errsock(); sprintf(msg, "connect error (%d)", err); tracet(1, "consock: connect error sock=%d err=%d\n", tcpcli->svr.sock, err); closesocket(tcpcli->svr.sock); tcpcli->svr.state = 0; return 0; } if (!stat) { /* not connect */ sprintf(msg, "connecting..."); return 0; } sprintf(msg, "%s", tcpcli->svr.saddr); tracet(2, "consock: connected sock=%d addr=%s\n", tcpcli->svr.sock, tcpcli->svr.saddr); tcpcli->svr.state = 2; tcpcli->svr.tact = tickget(); return 1; } /* open tcp client -----------------------------------------------------------*/ tcpcli_t *opentcpcli(const char *path, char *msg) { tcpcli_t *tcpcli, tcpcli0{}; char port[256] = ""; tracet(3, "opentcpcli: path=%s\n", path); if (!(tcpcli = static_cast(malloc(sizeof(tcpcli_t))))) { return nullptr; } *tcpcli = tcpcli0; decodetcppath(path, tcpcli->svr.saddr, port, nullptr, nullptr, nullptr, nullptr); if (sscanf(port, "%d", &tcpcli->svr.port) < 1) { sprintf(msg, "port error: %s", port); tracet(1, "opentcp: port error port=%s\n", port); free(tcpcli); return nullptr; } tcpcli->svr.tcon = 0; tcpcli->toinact = toinact; tcpcli->tirecon = ticonnect; return tcpcli; } /* close tcp client ----------------------------------------------------------*/ void closetcpcli(tcpcli_t *tcpcli) { tracet(3, "closetcpcli: sock=%d\n", tcpcli->svr.sock); closesocket(tcpcli->svr.sock); free(tcpcli); } /* wait socket connect -------------------------------------------------------*/ int waittcpcli(tcpcli_t *tcpcli, char *msg) { tracet(4, "waittcpcli: sock=%d state=%d\n", tcpcli->svr.sock, tcpcli->svr.state); if (tcpcli->svr.state < 0) { return 0; } if (tcpcli->svr.state == 0) { /* close */ if (!gentcp(&tcpcli->svr, 1, msg)) { return 0; } } if (tcpcli->svr.state == 1) { /* wait */ if (!consock(tcpcli, msg)) { return 0; } } if (tcpcli->svr.state == 2) { /* connect */ if (tcpcli->toinact > 0 && static_cast(tickget() - tcpcli->svr.tact) > tcpcli->toinact) { sprintf(msg, "timeout"); tracet(2, "waittcpcli: inactive timeout sock=%d\n", tcpcli->svr.sock); discontcp(&tcpcli->svr, tcpcli->tirecon); return 0; } } return 1; } /* read tcp client -----------------------------------------------------------*/ int readtcpcli(tcpcli_t *tcpcli, unsigned char *buff, int n, char *msg) { int nr, err; tracet(4, "readtcpcli: sock=%d state=%d n=%d\n", tcpcli->svr.sock, tcpcli->svr.state, n); if (!waittcpcli(tcpcli, msg)) { return 0; } if ((nr = recv_nb(tcpcli->svr.sock, buff, n)) == -1) { err = errsock(); tracet(1, "readtcpcli: recv error sock=%d err=%d\n", tcpcli->svr.sock, err); sprintf(msg, "recv error (%d)", err); discontcp(&tcpcli->svr, tcpcli->tirecon); return 0; } if (nr > 0) { tcpcli->svr.tact = tickget(); } tracet(5, "readtcpcli: exit sock=%d nr=%d\n", tcpcli->svr.sock, nr); return nr; } /* write tcp client ----------------------------------------------------------*/ int writetcpcli(tcpcli_t *tcpcli, unsigned char *buff, int n, char *msg) { int ns, err; tracet(3, "writetcpcli: sock=%d state=%d n=%d\n", tcpcli->svr.sock, tcpcli->svr.state, n); if (!waittcpcli(tcpcli, msg)) { return 0; } if ((ns = send_nb(tcpcli->svr.sock, buff, n)) == -1) { err = errsock(); tracet(1, "writetcp: send error sock=%d err=%d\n", tcpcli->svr.sock, err); sprintf(msg, "send error (%d)", err); discontcp(&tcpcli->svr, tcpcli->tirecon); return 0; } if (ns > 0) { tcpcli->svr.tact = tickget(); } tracet(5, "writetcpcli: exit sock=%d ns=%d\n", tcpcli->svr.sock, ns); return ns; } /* get state tcp client ------------------------------------------------------*/ int statetcpcli(tcpcli_t *tcpcli) { return tcpcli ? tcpcli->svr.state : 0; } /* base64 encoder ------------------------------------------------------------*/ int encbase64(char *str, const unsigned char *byte, int n) { const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i, j, k, b; tracet(4, "encbase64: n=%d\n", n); for (i = j = 0; i / 8 < n;) { for (k = b = 0; k < 6; k++, i++) { b <<= 1; if (i / 8 < n) { b |= (byte[i / 8] >> (7 - i % 8)) & 0x1; } } str[j++] = table[b]; } while (j & 0x3) { str[j++] = '='; } str[j] = '\0'; tracet(5, "encbase64: str=%s\n", str); return j; } /* send ntrip server request -------------------------------------------------*/ int reqntrip_s(ntrip_t *ntrip, char *msg) { char buff[256 + NTRIP_MAXSTR], *p = buff; tracet(3, "reqntrip_s: state=%d\n", ntrip->state); p += snprintf(p, 256 + NTRIP_MAXSTR, "SOURCE %s %s\r\n", ntrip->passwd, ntrip->mntpnt); p += sprintf(p, "Source-Agent: NTRIP %s\r\n", NTRIP_AGENT); p += sprintf(p, "STR: %s\r\n", ntrip->str); p += sprintf(p, "\r\n"); if (writetcpcli(ntrip->tcp, reinterpret_cast(buff), p - buff, msg) != p - buff) { return 0; } tracet(2, "reqntrip_s: send request state=%d ns=%d\n", ntrip->state, p - buff); tracet(5, "reqntrip_s: n=%d buff=\n%s\n", p - buff, buff); ntrip->state = 1; return 1; } /* send ntrip client request -------------------------------------------------*/ int reqntrip_c(ntrip_t *ntrip, char *msg) { char buff[1024], user[512], *p = buff; tracet(3, "reqntrip_c: state=%d\n", ntrip->state); p += sprintf(p, "GET %s/%s HTTP/1.0\r\n", ntrip->url, ntrip->mntpnt); p += sprintf(p, "User-Agent: NTRIP %s\r\n", NTRIP_AGENT); if (!*ntrip->user) { p += sprintf(p, "Accept: */*\r\n"); p += sprintf(p, "Connection: close\r\n"); } else { sprintf(user, "%s:%s", ntrip->user, ntrip->passwd); p += sprintf(p, "Authorization: Basic "); p += encbase64(p, reinterpret_cast(user), strlen(user)); p += sprintf(p, "\r\n"); } p += sprintf(p, "\r\n"); if (writetcpcli(ntrip->tcp, reinterpret_cast(buff), p - buff, msg) != p - buff) { return 0; } tracet(2, "reqntrip_c: send request state=%d ns=%d\n", ntrip->state, p - buff); tracet(5, "reqntrip_c: n=%d buff=\n%s\n", p - buff, buff); ntrip->state = 1; return 1; } /* test ntrip server response ------------------------------------------------*/ int rspntrip_s(ntrip_t *ntrip, char *msg) { int i, nb; char *p, *q; tracet(3, "rspntrip_s: state=%d nb=%d\n", ntrip->state, ntrip->nb); ntrip->buff[ntrip->nb] = '0'; tracet(5, "rspntrip_s: n=%d buff=\n%s\n", ntrip->nb, ntrip->buff); if ((p = strstr(reinterpret_cast(ntrip->buff), NTRIP_RSP_OK_SVR))) { /* ok */ q = reinterpret_cast(ntrip->buff); p += strlen(NTRIP_RSP_OK_SVR); ntrip->nb -= p - q; for (i = 0; i < ntrip->nb; i++) { *q++ = *p++; } ntrip->state = 2; sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt); tracet(2, "rspntrip_s: response ok nb=%d\n", ntrip->nb); return 1; } if ((p = strstr(reinterpret_cast(ntrip->buff), NTRIP_RSP_ERROR))) { /* error */ nb = ntrip->nb < MAXSTATMSG ? ntrip->nb : MAXSTATMSG; // strncpy(msg, (char *)ntrip->buff, nb); This line triggers a warning. Replaced by; std::string s_aux(reinterpret_cast(ntrip->buff)); s_aux.resize(nb, '\0'); for (int i = 0; i < nb; i++) { msg[i] = s_aux[i]; } msg[nb] = 0; tracet(1, "rspntrip_s: %s nb=%d\n", msg, ntrip->nb); ntrip->nb = 0; ntrip->buff[0] = '\0'; ntrip->state = 0; discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon); } else if (ntrip->nb >= NTRIP_MAXRSP) { /* buffer overflow */ sprintf(msg, "response overflow"); tracet(1, "rspntrip_s: response overflow nb=%d\n", ntrip->nb); ntrip->nb = 0; ntrip->buff[0] = '\0'; ntrip->state = 0; discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon); } tracet(5, "rspntrip_s: exit state=%d nb=%d\n", ntrip->state, ntrip->nb); return 0; } /* test ntrip client response ------------------------------------------------*/ int rspntrip_c(ntrip_t *ntrip, char *msg) { int i; char *p, *q; tracet(3, "rspntrip_c: state=%d nb=%d\n", ntrip->state, ntrip->nb); ntrip->buff[ntrip->nb] = '0'; tracet(5, "rspntrip_c: n=%d buff=\n%s\n", ntrip->nb, ntrip->buff); if ((p = strstr(reinterpret_cast(ntrip->buff), NTRIP_RSP_OK_CLI))) { /* ok */ q = reinterpret_cast(ntrip->buff); p += strlen(NTRIP_RSP_OK_CLI); ntrip->nb -= p - q; for (i = 0; i < ntrip->nb; i++) { *q++ = *p++; } ntrip->state = 2; sprintf(msg, "%s/%s", ntrip->tcp->svr.saddr, ntrip->mntpnt); tracet(2, "rspntrip_c: response ok nb=%d\n", ntrip->nb); return 1; } if ((p = strstr(reinterpret_cast(ntrip->buff), NTRIP_RSP_SRCTBL))) { /* source table */ if (!*ntrip->mntpnt) { /* source table request */ ntrip->state = 2; sprintf(msg, "source table received"); tracet(2, "rspntrip_c: receive source table nb=%d\n", ntrip->nb); return 1; } sprintf(msg, "no mountp. reconnect..."); tracet(2, "rspntrip_c: no mount point nb=%d\n", ntrip->nb); ntrip->nb = 0; ntrip->buff[0] = '\0'; ntrip->state = 0; discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon); } else if ((p = strstr(reinterpret_cast(ntrip->buff), NTRIP_RSP_HTTP))) { /* http response */ if ((q = strchr(p, '\r'))) { *q = '\0'; } else { ntrip->buff[128] = '\0'; } strcpy(msg, p); tracet(1, "rspntrip_s: %s nb=%d\n", msg, ntrip->nb); ntrip->nb = 0; ntrip->buff[0] = '\0'; ntrip->state = 0; discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon); } else if (ntrip->nb >= NTRIP_MAXRSP) { /* buffer overflow */ sprintf(msg, "response overflow"); tracet(1, "rspntrip_s: response overflow nb=%d\n", ntrip->nb); ntrip->nb = 0; ntrip->buff[0] = '\0'; ntrip->state = 0; discontcp(&ntrip->tcp->svr, ntrip->tcp->tirecon); } tracet(5, "rspntrip_c: exit state=%d nb=%d\n", ntrip->state, ntrip->nb); return 0; } /* wait ntrip request/response -----------------------------------------------*/ int waitntrip(ntrip_t *ntrip, char *msg) { int n; char *p; tracet(4, "waitntrip: state=%d nb=%d\n", ntrip->state, ntrip->nb); if (ntrip->state < 0) { return 0; /* error */ } if (ntrip->tcp->svr.state < 2) { ntrip->state = 0; /* tcp disconnected */ } if (ntrip->state == 0) { /* send request */ if (!(ntrip->type == 0 ? reqntrip_s(ntrip, msg) : reqntrip_c(ntrip, msg))) { return 0; } tracet(2, "waitntrip: state=%d nb=%d\n", ntrip->state, ntrip->nb); } if (ntrip->state == 1) { /* read response */ p = reinterpret_cast(ntrip->buff) + ntrip->nb; if ((n = readtcpcli(ntrip->tcp, reinterpret_cast(p), NTRIP_MAXRSP - ntrip->nb - 1, msg)) == 0) { tracet(5, "waitntrip: readtcp n=%d\n", n); return 0; } ntrip->nb += n; ntrip->buff[ntrip->nb] = '\0'; /* wait response */ return ntrip->type == 0 ? rspntrip_s(ntrip, msg) : rspntrip_c(ntrip, msg); } return 1; } /* open ntrip ----------------------------------------------------------------*/ ntrip_t *openntrip(const char *path, int type, char *msg) { ntrip_t *ntrip; int i; char addr[256] = "", port[256] = "", tpath[MAXSTRPATH]; tracet(3, "openntrip: path=%s type=%d\n", path, type); if (!(ntrip = static_cast(malloc(sizeof(ntrip_t))))) { return nullptr; } ntrip->state = 0; ntrip->type = type; /* 0:server, 1:client */ ntrip->nb = 0; ntrip->url[0] = '\0'; ntrip->mntpnt[0] = ntrip->user[0] = ntrip->passwd[0] = ntrip->str[0] = '\0'; for (i = 0; i < NTRIP_MAXRSP; i++) { ntrip->buff[i] = 0; } /* decode tcp/ntrip path */ decodetcppath(path, addr, port, ntrip->user, ntrip->passwd, ntrip->mntpnt, ntrip->str); /* use default port if no port specified */ if (!*port) { sprintf(port, "%d", type ? NTRIP_CLI_PORT : NTRIP_SVR_PORT); } sprintf(tpath, "%s:%s", addr, port); /* ntrip access via proxy server */ if (*proxyaddr) { // sprintf(ntrip->url, "http://%s", tpath); This line triggers a warning. Replaced by: std::string s_aux = "http://" + std::string(tpath); int n = s_aux.length(); if (n < 256) { for (int k = 0; k < n; k++) { ntrip->url[k] = s_aux[k]; } } strcpy(tpath, proxyaddr); } /* open tcp client stream */ if (!(ntrip->tcp = opentcpcli(tpath, msg))) { tracet(1, "openntrip: opentcp error\n"); free(ntrip); return nullptr; } return ntrip; } /* close ntrip ---------------------------------------------------------------*/ void closentrip(ntrip_t *ntrip) { tracet(3, "closentrip: state=%d\n", ntrip->state); closetcpcli(ntrip->tcp); free(ntrip); } /* read ntrip ----------------------------------------------------------------*/ int readntrip(ntrip_t *ntrip, unsigned char *buff, int n, char *msg) { int nb; tracet(4, "readntrip: n=%d\n", n); if (!waitntrip(ntrip, msg)) { return 0; } if (ntrip->nb > 0) { /* read response buffer first */ nb = ntrip->nb <= n ? ntrip->nb : n; memcpy(buff, ntrip->buff + ntrip->nb - nb, nb); ntrip->nb = 0; return nb; } return readtcpcli(ntrip->tcp, buff, n, msg); } /* write ntrip ---------------------------------------------------------------*/ int writentrip(ntrip_t *ntrip, unsigned char *buff, int n, char *msg) { tracet(3, "writentrip: n=%d\n", n); if (!waitntrip(ntrip, msg)) { return 0; } return writetcpcli(ntrip->tcp, buff, n, msg); } /* get state ntrip -----------------------------------------------------------*/ int statentrip(ntrip_t *ntrip) { return !ntrip ? 0 : (ntrip->state == 0 ? ntrip->tcp->svr.state : ntrip->state); } /* decode ftp path ----------------------------------------------------------*/ void decodeftppath(const char *path, char *addr, char *file, char *user, char *passwd, int *topts) { char buff[MAXSTRPATH], *p, *q; tracet(4, "decodeftpath: path=%s\n", path); if (user) { *user = '\0'; } if (passwd) { *passwd = '\0'; } if (topts) { topts[0] = 0; /* time offset in path (s) */ topts[1] = 3600; /* download interval (s) */ topts[2] = 0; /* download time offset (s) */ topts[3] = 0; /* retry interval (s) (0: no retry) */ } if (strlen(path) < MAXSTRPATH) { strcpy(buff, path); } if ((p = strchr(buff, '/'))) { if ((q = strstr(p + 1, "::"))) { *q = '\0'; if (topts) { sscanf(q + 2, "T=%d, %d, %d, %d", topts, topts + 1, topts + 2, topts + 3); } } strcpy(file, p + 1); *p = '\0'; } else { file[0] = '\0'; } if ((p = strrchr(buff, '@'))) { *p++ = '\0'; if ((q = strchr(buff, ':'))) { *q = '\0'; if (passwd) { strcpy(passwd, q + 1); } } if (user) { strcpy(user, buff); } } else { p = buff; } strcpy(addr, p); } /* next download time --------------------------------------------------------*/ gtime_t nextdltime(const int *topts, int stat) { gtime_t time; double tow; int week, tint; tracet(3, "nextdltime: topts=%d %d %d %d stat=%d\n", topts[0], topts[1], topts[2], topts[3], stat); /* current time (gpst) */ time = utc2gpst(timeget()); tow = time2gpst(time, &week); /* next retry time */ if (stat == 0 && topts[3] > 0) { tow = (floor((tow - topts[2]) / topts[3]) + 1.0) * topts[3] + topts[2]; return gpst2time(week, tow); } /* next interval time */ tint = topts[1] <= 0 ? 3600 : topts[1]; tow = (floor((tow - topts[2]) / tint) + 1.0) * tint + topts[2]; time = gpst2time(week, tow); return time; } /* ftp thread ----------------------------------------------------------------*/ void *ftpthread(void *arg) { auto *ftp = static_cast(arg); FILE *fp; gtime_t time; char remote[1024], local[1024], tmpfile[1024], errfile[1024], *p; char cmd[2048], env[1024] = "", opt[1024], *proxyopt = (char *)"", *proto; int ret; tracet(3, "ftpthread:\n"); if (!*localdir) { tracet(1, "no local directory\n"); ftp->error = 11; ftp->state = 3; return nullptr; } /* replace keyword in file path and local path */ time = timeadd(utc2gpst(timeget()), ftp->topts[0]); reppath(ftp->file, remote, time, "", ""); if ((p = strrchr(remote, '/'))) { p++; } else { p = remote; } // sprintf(local, "%s%c%s", localdir, FILEPATHSEP, p); This line triggers a warning. Replaced by: std::string s_aux = std::string(localdir) + std::to_string(FILEPATHSEP) + std::string(p); int n = s_aux.length(); if (n < 1024) { for (int i = 0; i < n; i++) { local[i] = s_aux[i]; } } // sprintf(errfile, "%s.err", local); This line triggers a warning. Replaced by: std::string s_aux2 = std::string(local) + ".err"; n = s_aux2.length(); if (n < 1024) { for (int i = 0; i < n; i++) { errfile[i] = s_aux2[i]; } } /* if local file exist, skip download */ strcpy(tmpfile, local); if ((p = strrchr(tmpfile, '.')) && (!strcmp(p, ".z") || !strcmp(p, ".gz") || !strcmp(p, ".zip") || !strcmp(p, ".Z") || !strcmp(p, ".GZ") || !strcmp(p, ".ZIP"))) { *p = '\0'; } if ((fp = fopen(tmpfile, "rbe"))) { fclose(fp); strcpy(ftp->local, tmpfile); tracet(3, "ftpthread: file exists %s\n", ftp->local); ftp->state = 2; return nullptr; } /* proxy settings for wget (ref [2]) */ if (*proxyaddr) { proto = ftp->proto ? (char *)"http" : (char *)"ftp"; sprintf(env, "set %s_proxy=http://%s & ", proto, proxyaddr); proxyopt = (char *)"--proxy=on "; } /* download command (ref [2]) */ if (ftp->proto == 0) { /* ftp */ // sprintf(opt, "--ftp-user=%s --ftp-password=%s --glob=off --passive-ftp %s-t 1 -T %d -O \"%s\"", // ftp->user, ftp->passwd, proxyopt, FTP_TIMEOUT, local); This line triggers a warning. Replaced by: std::string s_aux = "--ftp-user=" + std::string(ftp->user) + " --ftp-password=" + std::string(ftp->passwd) + " --glob=off --passive-ftp " + std::string(proxyopt) + "s-t 1 -T " + std::to_string(FTP_TIMEOUT) + " -O \"" + std::string(local) + "\""; int k = s_aux.length(); if (k < 1024) { for (int i = 0; i < k; i++) { opt[i] = s_aux[i]; } } // sprintf(cmd, "%s%s %s \"ftp://%s/%s\" 2> \"%s\"\n", env, FTP_CMD, opt, ftp->addr, // remote, errfile); This line triggers a warning. Replaced by: std::string s_aux2 = std::string(env) + std::string(FTP_CMD) + " " + std::string(opt) + " " + "\"ftp://" + std::string(ftp->addr) + "/" + std::string(remote) + "\" 2> \"" + std::string(errfile) + "\"\n"; k = s_aux2.length(); for (int i = 0; (i < k) && (i < 1024); i++) { cmd[i] = s_aux2[i]; } } else { /* http */ // sprintf(opt, "%s-t 1 -T %d -O \"%s\"", proxyopt, FTP_TIMEOUT, local); This line triggers a warning. Replaced by: std::string s_aux = std::string(proxyopt) + " -t 1 -T " + std::to_string(FTP_TIMEOUT) + " -O \"" + std::string(local) + "\""; int l = s_aux.length(); for (int i = 0; (i < l) && (i < 1024); i++) { opt[i] = s_aux[i]; } // sprintf(cmd, "%s%s %s \"http://%s/%s\" 2> \"%s\"\n", env, FTP_CMD, opt, ftp->addr, // remote, errfile); This line triggers a warning. Replaced by: std::string s_aux2 = std::string(env) + std::string(FTP_CMD) + " " + std::string(opt) + " " + "\"http://" + std::string(ftp->addr) + "/" + std::string(remote) + "\" 2> \"" + std::string(errfile) + "\"\n"; l = s_aux2.length(); for (int i = 0; (i < l) && (i < 1024); i++) { cmd[i] = s_aux2[i]; } } /* execute download command */ if ((ret = execcmd(cmd))) { if (remove(local) != 0) { trace(1, "Error removing file"); } tracet(1, "execcmd error: cmd=%s ret=%d\n", cmd, ret); ftp->error = ret; ftp->state = 3; return nullptr; } if (remove(errfile) != 0) { trace(1, "Error removing file"); } /* uncompress downloaded file */ if ((p = strrchr(local, '.')) && (!strcmp(p, ".z") || !strcmp(p, ".gz") || !strcmp(p, ".zip") || !strcmp(p, ".Z") || !strcmp(p, ".GZ") || !strcmp(p, ".ZIP"))) { if (rtk_uncompress(local, tmpfile)) { if (remove(local) != 0) { trace(1, "Error removing file"); } if (strlen(tmpfile) < 1024) { strcpy(local, tmpfile); } } else { tracet(1, "file uncompact error: %s\n", local); ftp->error = 12; ftp->state = 3; return nullptr; } } if (strlen(local) < 1024) { strcpy(ftp->local, local); } ftp->state = 2; /* ftp completed */ tracet(3, "ftpthread: complete cmd=%s\n", cmd); return nullptr; } /* open ftp ------------------------------------------------------------------*/ ftp_t *openftp(const char *path, int type, char *msg) { ftp_t *ftp; tracet(3, "openftp: path=%s type=%d\n", path, type); msg[0] = '\0'; if (!(ftp = static_cast(malloc(sizeof(ftp_t))))) { return nullptr; } ftp->state = 0; ftp->proto = type; ftp->error = 0; ftp->thread = 0; // NOLINT ftp->local[0] = '\0'; /* decode ftp path */ decodeftppath(path, ftp->addr, ftp->file, ftp->user, ftp->passwd, ftp->topts); /* set first download time */ ftp->tnext = timeadd(timeget(), 10.0); return ftp; } /* close ftp -----------------------------------------------------------------*/ void closeftp(ftp_t *ftp) { tracet(3, "closeftp: state=%d\n", ftp->state); if (ftp->state != 1) { free(ftp); } } /* read ftp ------------------------------------------------------------------*/ int readftp(ftp_t *ftp, unsigned char *buff, int n, char *msg) { gtime_t time; unsigned char *p, *q; tracet(4, "readftp: n=%d\n", n); time = utc2gpst(timeget()); if (timediff(time, ftp->tnext) < 0.0) { /* until download time? */ return 0; } if (ftp->state <= 0) { /* ftp/http not executed? */ ftp->state = 1; sprintf(msg, "%s://%s", ftp->proto ? "http" : "ftp", ftp->addr); if (pthread_create(&ftp->thread, nullptr, ftpthread, ftp)) { tracet(1, "readftp: ftp thread create error\n"); ftp->state = 3; strcpy(msg, "ftp thread error"); return 0; } } if (ftp->state <= 1) { return 0; /* ftp/http on going? */ } if (ftp->state == 3) { /* ftp error */ sprintf(msg, "%s error (%d)", ftp->proto ? "http" : "ftp", ftp->error); /* set next retry time */ ftp->tnext = nextdltime(ftp->topts, 0); ftp->state = 0; return 0; } /* return local file path if ftp completed */ p = buff; q = reinterpret_cast(ftp->local); while (*q && static_cast(p - buff) < n) { *p++ = *q++; } p += sprintf(reinterpret_cast(p), "\r\n"); /* set next download time */ ftp->tnext = nextdltime(ftp->topts, 1); ftp->state = 0; strcpy(msg, ""); return static_cast(p - buff); } /* get state ftp -------------------------------------------------------------*/ int stateftp(ftp_t *ftp) { return !ftp ? 0 : (ftp->state == 0 ? 2 : (ftp->state <= 2 ? 3 : -1)); } /* initialize stream environment ----------------------------------------------- * initialize stream environment * args : none * return : none *-----------------------------------------------------------------------------*/ void strinitcom(void) { tracet(3, "strinitcom:\n"); } /* initialize stream ----------------------------------------------------------- * initialize stream struct * args : stream_t *stream IO stream * return : none *-----------------------------------------------------------------------------*/ void strinit(stream_t *stream) { tracet(3, "strinit:\n"); stream->type = 0; stream->mode = 0; stream->state = 0; stream->inb = stream->inr = stream->outb = stream->outr = 0; stream->tick = stream->tact = stream->inbt = stream->outbt = 0; initlock(&stream->lock); stream->port = nullptr; stream->path[0] = '\0'; stream->msg[0] = '\0'; } /* open stream ----------------------------------------------------------------- * open stream for read or write * args : stream_t *stream IO stream * int type I stream type (STR_SERIAL, STR_FILE, STR_TCPSVR, ...) * int mode I stream mode (STR_MODE_???) * char *path I stream path (see below) * return : status (0:error, 1:ok) * notes : see reference [1] for NTRIP * STR_FTP/HTTP needs "wget" in command search paths * * stream path ([] options): * * STR_SERIAL port[:brate[:bsize[:parity[:stopb[:fctr]]]]] * port = COM?? (windows), tty??? (linuex, omit /dev/) * brate = bit rate (bps) * bsize = bit size (7|8) * parity= parity (n|o|e) * stopb = stop bits (1|2) * fctr = flow control (off|rts) * STR_FILE file_path[::T][::+start][::xseppd][::S=swap] * ::T = enable time tag * start = replay start offset (s) * speed = replay speed factor * swap = output swap interval (hr) (0: no swap) * STR_TCPSVR :port * STR_TCPCLI address:port * STR_NTRIPSVR user[:passwd]@address[:port]/moutpoint[:string] * STR_NTRIPCLI [user[:passwd]]@address[:port][/mountpoint] * STR_FTP [user[:passwd]]@address/file_path[::T=poff[, tint[, toff, tret]]]] * STR_HTTP address/file_path[::T=poff[, tint[, toff, tret]]]] * poff = time offset for path extension (s) * tint = download interval (s) * toff = download time offset (s) * tret = download retry interval (s) (0:no retry) *-----------------------------------------------------------------------------*/ int stropen(stream_t *stream, int type, int mode, const char *path) { tracet(3, "stropen: type=%d mode=%d path=%s\n", type, mode, path); stream->type = type; stream->mode = mode; if (strlen(path) < MAXSTRPATH) { strcpy(stream->path, path); } stream->inb = stream->inr = stream->outb = stream->outr = 0; stream->tick = tickget(); stream->inbt = stream->outbt = 0; stream->msg[0] = '\0'; stream->port = nullptr; switch (type) { case STR_SERIAL: stream->port = openserial(path, mode, stream->msg); break; case STR_FILE: stream->port = openfile(path, mode, stream->msg); break; case STR_TCPSVR: stream->port = opentcpsvr(path, stream->msg); break; case STR_TCPCLI: stream->port = opentcpcli(path, stream->msg); break; case STR_NTRIPSVR: stream->port = openntrip(path, 0, stream->msg); break; case STR_NTRIPCLI: stream->port = openntrip(path, 1, stream->msg); break; case STR_FTP: stream->port = openftp(path, 0, stream->msg); break; case STR_HTTP: stream->port = openftp(path, 1, stream->msg); break; default: stream->state = 0; return 1; } stream->state = !stream->port ? -1 : 1; return stream->port != nullptr; } /* close stream ---------------------------------------------------------------- * close stream * args : stream_t *stream IO stream * return : none *-----------------------------------------------------------------------------*/ void strclose(stream_t *stream) { tracet(3, "strclose: type=%d mode=%d\n", stream->type, stream->mode); if (stream->port) { switch (stream->type) { case STR_SERIAL: closeserial(static_cast(stream->port)); break; case STR_FILE: closefile(static_cast(stream->port)); break; case STR_TCPSVR: closetcpsvr(static_cast(stream->port)); break; case STR_TCPCLI: closetcpcli(static_cast(stream->port)); break; case STR_NTRIPSVR: closentrip(static_cast(stream->port)); break; case STR_NTRIPCLI: closentrip(static_cast(stream->port)); break; case STR_FTP: closeftp(static_cast(stream->port)); break; case STR_HTTP: closeftp(static_cast(stream->port)); break; } } else { trace(2, "no port to close stream: type=%d\n", stream->type); } stream->type = 0; stream->mode = 0; stream->state = 0; stream->inr = stream->outr = 0; stream->path[0] = '\0'; stream->msg[0] = '\0'; stream->port = nullptr; } /* sync streams ---------------------------------------------------------------- * sync time for streams * args : stream_t *stream1 IO stream 1 * stream_t *stream2 IO stream 2 * return : none * notes : for replay files with time tags *-----------------------------------------------------------------------------*/ void strsync(stream_t *stream1, stream_t *stream2) { file_t *file1, *file2; if (stream1->type != STR_FILE || stream2->type != STR_FILE) { return; } file1 = static_cast(stream1->port); file2 = static_cast(stream2->port); if (file1 && file2) { syncfile(file1, file2); } } /* lock/unlock stream ---------------------------------------------------------- * lock/unlock stream * args : stream_t *stream I stream * return : none *-----------------------------------------------------------------------------*/ void strlock(stream_t *stream) { rtk_lock(&stream->lock); } void strunlock(stream_t *stream) { rtk_unlock(&stream->lock); } /* read stream ----------------------------------------------------------------- * read data from stream (unblocked) * args : stream_t *stream I stream * unsigned char *buff O data buffer * int n I maximum data length * return : read data length * notes : if no data, return immediately with no data *-----------------------------------------------------------------------------*/ int strread(stream_t *stream, unsigned char *buff, int n) { unsigned int tick; char *msg = stream->msg; int nr; tracet(4, "strread: n=%d\n", n); if (!(stream->mode & STR_MODE_R) || !stream->port) { return 0; } strlock(stream); switch (stream->type) { case STR_SERIAL: nr = readserial(static_cast(stream->port), buff, n, msg); break; case STR_FILE: nr = readfile(static_cast(stream->port), buff, n, msg); break; case STR_TCPSVR: nr = readtcpsvr(static_cast(stream->port), buff, n, msg); break; case STR_TCPCLI: nr = readtcpcli(static_cast(stream->port), buff, n, msg); break; case STR_NTRIPCLI: nr = readntrip(static_cast(stream->port), buff, n, msg); break; case STR_FTP: nr = readftp(static_cast(stream->port), buff, n, msg); break; case STR_HTTP: nr = readftp(static_cast(stream->port), buff, n, msg); break; default: strunlock(stream); return 0; } stream->inb += nr; tick = tickget(); if (nr > 0) { stream->tact = tick; } if (static_cast(tick - stream->tick) >= tirate) { stream->inr = (stream->inb - stream->inbt) * 8000 / (tick - stream->tick); stream->tick = tick; stream->inbt = stream->inb; } strunlock(stream); return nr; } /* write stream ---------------------------------------------------------------- * write data to stream (unblocked) * args : stream_t *stream I stream * unsigned char *buff I data buffer * int n I data length * return : status (0:error, 1:ok) * notes : write data to buffer and return immediately *-----------------------------------------------------------------------------*/ int strwrite(stream_t *stream, unsigned char *buff, int n) { unsigned int tick; char *msg = stream->msg; int ns; tracet(3, "strwrite: n=%d\n", n); if (!(stream->mode & STR_MODE_W) || !stream->port) { return 0; } strlock(stream); switch (stream->type) { case STR_SERIAL: ns = writeserial(static_cast(stream->port), buff, n, msg); break; case STR_FILE: ns = writefile(static_cast(stream->port), buff, n, msg); break; case STR_TCPSVR: ns = writetcpsvr(static_cast(stream->port), buff, n, msg); break; case STR_TCPCLI: ns = writetcpcli(static_cast(stream->port), buff, n, msg); break; case STR_NTRIPCLI: case STR_NTRIPSVR: ns = writentrip(static_cast(stream->port), buff, n, msg); break; case STR_FTP: case STR_HTTP: default: strunlock(stream); return 0; } stream->outb += ns; tick = tickget(); if (ns > 0) { stream->tact = tick; } if (static_cast(tick - stream->tick) > tirate) { stream->outr = (stream->outb - stream->outbt) * 8000 / (tick - stream->tick); stream->tick = tick; stream->outbt = stream->outb; } strunlock(stream); return ns; } /* get stream status ----------------------------------------------------------- * get stream status * args : stream_t *stream I stream * char *msg IO status message (NULL: no output) * return : status (-1:error, 0:close, 1:wait, 2:connect, 3:active) *-----------------------------------------------------------------------------*/ int strstat(stream_t *stream, char *msg) { int state; tracet(4, "strstat:\n"); strlock(stream); if (msg) { // strncpy(msg, stream->msg, MAXSTRMSG - 1); This line triggers a warning. Replaced by: std::string aux_s(stream->msg); aux_s.resize(MAXSTRMSG - 1, '0'); for (int i = 0; i < MAXSTRMSG - 1; i++) { msg[i] = aux_s[i]; } msg[MAXSTRMSG - 1] = '\0'; } if (!stream->port) { strunlock(stream); return stream->state; } switch (stream->type) { case STR_SERIAL: state = stateserial(static_cast(stream->port)); break; case STR_FILE: state = statefile(static_cast(stream->port)); break; case STR_TCPSVR: state = statetcpsvr(static_cast(stream->port)); break; case STR_TCPCLI: state = statetcpcli(static_cast(stream->port)); break; case STR_NTRIPSVR: case STR_NTRIPCLI: state = statentrip(static_cast(stream->port)); break; case STR_FTP: state = stateftp(static_cast(stream->port)); break; case STR_HTTP: state = stateftp(static_cast(stream->port)); break; default: strunlock(stream); return 0; } if (state == 2 && static_cast(tickget() - stream->tact) <= TINTACT) { state = 3; } strunlock(stream); return state; } /* get stream statistics summary ----------------------------------------------- * get stream statistics summary * args : stream_t *stream I stream * int *inb IO bytes of input (NULL: no output) * int *inr IO bps of input (NULL: no output) * int *outb IO bytes of output (NULL: no output) * int *outr IO bps of output (NULL: no output) * return : none *-----------------------------------------------------------------------------*/ void strsum(stream_t *stream, int *inb, int *inr, int *outb, int *outr) { tracet(4, "strsum:\n"); strlock(stream); if (inb) { *inb = stream->inb; } if (inr) { *inr = stream->inr; } if (outb) { *outb = stream->outb; } if (outr) { *outr = stream->outr; } strunlock(stream); } /* set global stream options --------------------------------------------------- * set global stream options * args : int *opt I options * opt[0]= inactive timeout (ms) (0: no timeout) * opt[1]= interval to reconnect (ms) * opt[2]= averaging time of data rate (ms) * opt[3]= receive/send buffer size (bytes); * opt[4]= file swap margin (s) * opt[5]= reserved * opt[6]= reserved * opt[7]= reserved * return : none *-----------------------------------------------------------------------------*/ void strsetopt(const int *opt) { tracet(3, "strsetopt: opt=%d %d %d %d %d %d %d %d\n", opt[0], opt[1], opt[2], opt[3], opt[4], opt[5], opt[6], opt[7]); toinact = 0 < opt[0] && opt[0] < 1000 ? 1000 : opt[0]; /* >=1s */ ticonnect = opt[1] < 1000 ? 1000 : opt[1]; /* >=1s */ tirate = opt[2] < 100 ? 100 : opt[2]; /* >=0.1s */ buffsize = opt[3] < 4096 ? 4096 : opt[3]; /* >=4096byte */ fswapmargin = opt[4] < 0 ? 0 : opt[4]; } /* set timeout time ------------------------------------------------------------ * set timeout time * args : stream_t *stream I stream (STR_TCPCLI, STR_NTRIPCLI, STR_NTRIPSVR) * int toinact I inactive timeout (ms) (0: no timeout) * int tirecon I reconnect interval (ms) (0: no reconnect) * return : none *-----------------------------------------------------------------------------*/ void strsettimeout(stream_t *stream, int toinact, int tirecon) { tcpcli_t *tcpcli; tracet(3, "strsettimeout: toinact=%d tirecon=%d\n", toinact, tirecon); if (stream->type == STR_TCPCLI) { tcpcli = static_cast(stream->port); } else if (stream->type == STR_NTRIPCLI || stream->type == STR_NTRIPSVR) { tcpcli = (static_cast(stream->port))->tcp; } else { return; } tcpcli->toinact = toinact; tcpcli->tirecon = tirecon; } /* set local directory --------------------------------------------------------- * set local directory path for ftp/http download * args : char *dir I directory for download files * return : none *-----------------------------------------------------------------------------*/ void strsetdir(const char *dir) { tracet(3, "strsetdir: dir=%s\n", dir); if (strlen(dir) < 1024) { strcpy(localdir, dir); } } /* set http/ntrip proxy address ------------------------------------------------ * set http/ntrip proxy address * args : char *addr I http/ntrip proxy address
: * return : none *-----------------------------------------------------------------------------*/ void strsetproxy(const char *addr) { tracet(3, "strsetproxy: addr=%s\n", addr); if (strlen(addr) < 256) { strcpy(proxyaddr, addr); } } /* get stream time ------------------------------------------------------------- * get stream time * args : stream_t *stream I stream * return : current time or replay time for playback file *-----------------------------------------------------------------------------*/ gtime_t strgettime(stream_t *stream) { file_t *file; if (stream->type == STR_FILE && (stream->mode & STR_MODE_R) && (file = static_cast(stream->port))) { return timeadd(file->time, file->start); /* replay start time */ } return utc2gpst(timeget()); } /* send nmea request ----------------------------------------------------------- * send nmea gpgga message to stream * args : stream_t *stream I stream * double *pos I position {x, y, z} (ecef) (m) * return : none *-----------------------------------------------------------------------------*/ void strsendnmea(stream_t *stream, const double *pos) { sol_t sol = {{0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, '0', '0', '0', 0, 0, 0}; unsigned char buff[1024]; int i, n; tracet(3, "strsendnmea: pos=%.3f %.3f %.3f\n", pos[0], pos[1], pos[2]); sol.stat = SOLQ_SINGLE; sol.time = utc2gpst(timeget()); for (i = 0; i < 3; i++) { sol.rr[i] = pos[i]; } n = outnmea_gga(buff, &sol); strwrite(stream, buff, n); } /* generate general hex message ----------------------------------------------*/ int gen_hex(const char *msg, unsigned char *buff) { unsigned char *q = buff; char mbuff[1024] = "", *args[256], *p; unsigned int byte; int i, narg = 0; trace(4, "gen_hex: msg=%s\n", msg); strncpy(mbuff, msg, 1023); for (p = strtok(mbuff, " "); p && narg < 256; p = strtok(nullptr, " ")) { args[narg++] = p; } for (i = 0; i < narg; i++) { if (sscanf(args[i], "%x", &byte)) { *q++ = static_cast(byte); } } return static_cast(q - buff); } /* send receiver command ------------------------------------------------------- * send receiver commands to stream * args : stream_t *stream I stream * char *cmd I receiver command strings * return : none *-----------------------------------------------------------------------------*/ void strsendcmd(stream_t *str, const char *cmd) { unsigned char buff[1024]; const char *p = cmd, *q; char msg[1024], cmdend[] = "\r\n"; int n, m, ms; tracet(3, "strsendcmd: cmd=%s\n", cmd); for (;;) { for (q = p;; q++) { if (*q == '\r' || *q == '\n' || *q == '\0') { break; } } n = static_cast(q - p); strncpy(msg, p, n); msg[n] = '\0'; if (!*msg || *msg == '#') { /* null or comment */ ; } else if (*msg == '!') { /* binary escape */ if (!strncmp(msg + 1, "WAIT", 4)) { /* wait */ if (sscanf(msg + 5, "%d", &ms) < 1) { ms = 100; } if (ms > 3000) { ms = 3000; /* max 3 s */ } sleepms(ms); } //else if (!strncmp(msg+1, "UBX", 3)) //{ /* ublox */ // if ((m=gen_ubx(msg+4, buff))>0) strwrite(str, buff, m); //} //else if (!strncmp(msg+1, "STQ", 3)) //{ /* skytraq */ // if ((m=gen_stq(msg+4, buff))>0) strwrite(str, buff, m); //} //else if (!strncmp(msg+1, "NVS", 3)) //{ /* nvs */ // if ((m=gen_nvs(msg+4, buff))>0) strwrite(str, buff, m); //} //else if (!strncmp(msg+1, "LEXR", 4)) //{ /* lex receiver */ // if ((m=gen_lexr(msg+5, buff))>0) strwrite(str, buff, m); //} else if (!strncmp(msg + 1, "HEX", 3)) { /* general hex message */ if ((m = gen_hex(msg + 4, buff)) > 0) { strwrite(str, buff, m); } } } else { strwrite(str, reinterpret_cast(msg), n); strwrite(str, reinterpret_cast(cmdend), 2); } if (*q == '\0') { break; } p = q + 1; } } src/algorithms/libs/rtklib/rtklib_stream.h000066400000000000000000000170231352176506000212610ustar00rootroot00000000000000/*! * \file rtklib_stream.h * \brief streaming functions * \authors
    *
  • 2007-2013, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2013, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *-----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_STREAM_H_ #define GNSS_SDR_RTKLIB_STREAM_H_ #include "rtklib.h" /* constants -----------------------------------------------------------------*/ #define TINTACT 200 /* period for stream active (ms) */ #define SERIBUFFSIZE 4096 /* serial buffer size (bytes) */ #define TIMETAGH_LEN 64 /* time tag file header length */ #define MAXCLI 32 /* max client connection for tcp svr */ #define MAXSTATMSG 32 /* max length of status message */ #define VER_RTKLIB "2.4.2" #define NTRIP_AGENT "RTKLIB/" VER_RTKLIB #define NTRIP_CLI_PORT 2101 /* default ntrip-client connection port */ #define NTRIP_SVR_PORT 80 /* default ntrip-server connection port */ #define NTRIP_MAXRSP 32768 /* max size of ntrip response */ #define NTRIP_MAXSTR 256 /* max length of mountpoint string */ #define NTRIP_RSP_OK_CLI "ICY 200 OK\r\n" /* ntrip response: client */ #define NTRIP_RSP_OK_SVR "OK\r\n" /* ntrip response: server */ #define NTRIP_RSP_SRCTBL "SOURCETABLE 200 OK\r\n" /* ntrip response: source table */ #define NTRIP_RSP_TBLEND "ENDSOURCETABLE" #define NTRIP_RSP_HTTP "HTTP/" /* ntrip response: http */ #define NTRIP_RSP_ERROR "ERROR" /* ntrip response: error */ #define FTP_CMD "wget" /* ftp/http command */ #define FTP_TIMEOUT 30 /* ftp/http timeout (s) */ serial_t *openserial(const char *path, int mode, char *msg); void closeserial(serial_t *serial); int readserial(serial_t *serial, unsigned char *buff, int n, char *msg); int writeserial(serial_t *serial, unsigned char *buff, int n, char *msg); int stateserial(serial_t *serial); int openfile_(file_t *file, gtime_t time, char *msg); void closefile_(file_t *file); file_t *openfile(const char *path, int mode, char *msg); void closefile(file_t *file); void swapfile(file_t *file, gtime_t time, char *msg); void swapclose(file_t *file); int statefile(file_t *file); int readfile(file_t *file, unsigned char *buff, int nmax, char *msg); int writefile(file_t *file, unsigned char *buff, int n, char *msg); void syncfile(file_t *file1, file_t *file2); void decodetcppath(const char *path, char *addr, char *port, char *user, char *passwd, char *mntpnt, char *str); int errsock(void); int setsock(socket_t sock, char *msg); socket_t accept_nb(socket_t sock, struct sockaddr *addr, socklen_t *len); int connect_nb(socket_t sock, struct sockaddr *addr, socklen_t len); int recv_nb(socket_t sock, unsigned char *buff, int n); int send_nb(socket_t sock, unsigned char *buff, int n); int gentcp(tcp_t *tcp, int type, char *msg); void discontcp(tcp_t *tcp, int tcon); tcpsvr_t *opentcpsvr(const char *path, char *msg); void closetcpsvr(tcpsvr_t *tcpsvr); void updatetcpsvr(tcpsvr_t *tcpsvr, char *msg); int accsock(tcpsvr_t *tcpsvr, char *msg); int waittcpsvr(tcpsvr_t *tcpsvr, char *msg); int readtcpsvr(tcpsvr_t *tcpsvr, unsigned char *buff, int n, char *msg); int writetcpsvr(tcpsvr_t *tcpsvr, unsigned char *buff, int n, char *msg); int statetcpsvr(tcpsvr_t *tcpsvr); int consock(tcpcli_t *tcpcli, char *msg); tcpcli_t *opentcpcli(const char *path, char *msg); void closetcpcli(tcpcli_t *tcpcli); int waittcpcli(tcpcli_t *tcpcli, char *msg); int readtcpcli(tcpcli_t *tcpcli, unsigned char *buff, int n, char *msg); int writetcpcli(tcpcli_t *tcpcli, unsigned char *buff, int n, char *msg); int statetcpcli(tcpcli_t *tcpcli); int encbase64(char *str, const unsigned char *byte, int n); int reqntrip_s(ntrip_t *ntrip, char *msg); int reqntrip_c(ntrip_t *ntrip, char *msg); int rspntrip_s(ntrip_t *ntrip, char *msg); int rspntrip_c(ntrip_t *ntrip, char *msg); int waitntrip(ntrip_t *ntrip, char *msg); ntrip_t *openntrip(const char *path, int type, char *msg); void closentrip(ntrip_t *ntrip); int readntrip(ntrip_t *ntrip, unsigned char *buff, int n, char *msg); int writentrip(ntrip_t *ntrip, unsigned char *buff, int n, char *msg); int statentrip(ntrip_t *ntrip); void decodeftppath(const char *path, char *addr, char *file, char *user, char *passwd, int *topts); gtime_t nextdltime(const int *topts, int stat); void *ftpthread(void *arg); ftp_t *openftp(const char *path, int type, char *msg); void closeftp(ftp_t *ftp); int readftp(ftp_t *ftp, unsigned char *buff, int n, char *msg); int stateftp(ftp_t *ftp); void strinitcom(void); void strinit(stream_t *stream); int stropen(stream_t *stream, int type, int mode, const char *path); void strclose(stream_t *stream); void strsync(stream_t *stream1, stream_t *stream2); void strlock(stream_t *stream); void strunlock(stream_t *stream); int strread(stream_t *stream, unsigned char *buff, int n); int strwrite(stream_t *stream, unsigned char *buff, int n); int strstat(stream_t *stream, char *msg); void strsum(stream_t *stream, int *inb, int *inr, int *outb, int *outr); void strsetopt(const int *opt); void strsettimeout(stream_t *stream, int toinact, int tirecon); void strsetdir(const char *dir); void strsetproxy(const char *addr); gtime_t strgettime(stream_t *stream); void strsendnmea(stream_t *stream, const double *pos); int gen_hex(const char *msg, unsigned char *buff); void strsendcmd(stream_t *str, const char *cmd); #endif src/algorithms/libs/rtklib/rtklib_tides.cc000066400000000000000000000306551352176506000212420ustar00rootroot00000000000000/*! * \file rtklib_tides.cc * \brief Tidal displacement corrections * \authors
    *
  • 2007-2008, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2007-2008, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ #include "rtklib_tides.h" #include "rtklib_rtkcmn.h" /* solar/lunar tides (ref [2] 7) ---------------------------------------------*/ //#ifndef IERS_MODEL void tide_pl(const double *eu, const double *rp, double GMp, const double *pos, double *dr) { const double H3 = 0.292, L3 = 0.015; double r, ep[3], latp, lonp, p, K2, K3, a, H2, L2, dp, du, cosp, sinl, cosl; int i; trace(4, "tide_pl : pos=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D); if ((r = norm_rtk(rp, 3)) <= 0.0) { return; } for (i = 0; i < 3; i++) { ep[i] = rp[i] / r; } K2 = GMp / GME * std::pow(RE_WGS84, 2.04) * std::pow(RE_WGS84, 2.0) / (r * r * r); K3 = K2 * RE_WGS84 / r; latp = asin(ep[2]); lonp = atan2(ep[1], ep[0]); cosp = cos(latp); sinl = sin(pos[0]); cosl = cos(pos[0]); /* step1 in phase (degree 2) */ p = (3.0 * sinl * sinl - 1.0) / 2.0; H2 = 0.6078 - 0.0006 * p; L2 = 0.0847 + 0.0002 * p; a = dot(ep, eu, 3); dp = K2 * 3.0 * L2 * a; du = K2 * (H2 * (1.5 * a * a - 0.5) - 3.0 * L2 * a * a); /* step1 in phase (degree 3) */ dp += K3 * L3 * (7.5 * a * a - 1.5); du += K3 * (H3 * (2.5 * a * a * a - 1.5 * a) - L3 * (7.5 * a * a - 1.5) * a); /* step1 out-of-phase (only radial) */ du += 3.0 / 4.0 * 0.0025 * K2 * sin(2.0 * latp) * sin(2.0 * pos[0]) * sin(pos[1] - lonp); du += 3.0 / 4.0 * 0.0022 * K2 * cosp * cosp * cosl * cosl * sin(2.0 * (pos[1] - lonp)); dr[0] = dp * ep[0] + du * eu[0]; dr[1] = dp * ep[1] + du * eu[1]; dr[2] = dp * ep[2] + du * eu[2]; trace(5, "tide_pl : dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } /* displacement by solid earth tide (ref [2] 7) ------------------------------*/ void tide_solid(const double *rsun, const double *rmoon, const double *pos, const double *E, double gmst, int opt, double *dr) { double dr1[3], dr2[3], eu[3], du, dn, sinl, sin2l; trace(3, "tide_solid: pos=%.3f %.3f opt=%d\n", pos[0] * R2D, pos[1] * R2D, opt); /* step1: time domain */ eu[0] = E[2]; eu[1] = E[5]; eu[2] = E[8]; tide_pl(eu, rsun, GMS, pos, dr1); tide_pl(eu, rmoon, GMM, pos, dr2); /* step2: frequency domain, only K1 radial */ sin2l = sin(2.0 * pos[0]); du = -0.012 * sin2l * sin(gmst + pos[1]); dr[0] = dr1[0] + dr2[0] + du * E[2]; dr[1] = dr1[1] + dr2[1] + du * E[5]; dr[2] = dr1[2] + dr2[2] + du * E[8]; /* eliminate permanent deformation */ if (opt & 8) { sinl = sin(pos[0]); du = 0.1196 * (1.5 * sinl * sinl - 0.5); dn = 0.0247 * sin2l; dr[0] += du * E[2] + dn * E[1]; dr[1] += du * E[5] + dn * E[4]; dr[2] += du * E[8] + dn * E[7]; } trace(5, "tide_solid: dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } //#endif /* !IERS_MODEL */ /* displacement by ocean tide loading (ref [2] 7) ----------------------------*/ void tide_oload(gtime_t tut, const double *odisp, double *denu) { const double args[][5] = { {1.40519E-4, 2.0, -2.0, 0.0, 0.00}, /* M2 */ {1.45444E-4, 0.0, 0.0, 0.0, 0.00}, /* S2 */ {1.37880E-4, 2.0, -3.0, 1.0, 0.00}, /* N2 */ {1.45842E-4, 2.0, 0.0, 0.0, 0.00}, /* K2 */ {0.72921E-4, 1.0, 0.0, 0.0, 0.25}, /* K1 */ {0.67598E-4, 1.0, -2.0, 0.0, -0.25}, /* O1 */ {0.72523E-4, -1.0, 0.0, 0.0, -0.25}, /* P1 */ {0.64959E-4, 1.0, -3.0, 1.0, -0.25}, /* Q1 */ {0.53234E-5, 0.0, 2.0, 0.0, 0.00}, /* Mf */ {0.26392E-5, 0.0, 1.0, -1.0, 0.00}, /* Mm */ {0.03982E-5, 2.0, 0.0, 0.0, 0.00} /* Ssa */ }; const double ep1975[] = {1975, 1, 1, 0, 0, 0}; double ep[6], fday, days, t, t2, t3, a[5], ang, dp[3] = {0}; int i, j; trace(3, "tide_oload:\n"); /* angular argument: see subroutine arg.f for reference [1] */ time2epoch(tut, ep); fday = ep[3] * 3600.0 + ep[4] * 60.0 + ep[5]; ep[3] = ep[4] = ep[5] = 0.0; days = timediff(epoch2time(ep), epoch2time(ep1975)) / 86400.0 + 1.0; t = (27392.500528 + 1.000000035 * days) / 36525.0; t2 = t * t; t3 = t2 * t; a[0] = fday; a[1] = (279.69668 + 36000.768930485 * t + 3.03E-4 * t2) * D2R; /* H0 */ a[2] = (270.434358 + 481267.88314137 * t - 0.001133 * t2 + 1.9E-6 * t3) * D2R; /* S0 */ a[3] = (334.329653 + 4069.0340329577 * t - 0.010325 * t2 - 1.2E-5 * t3) * D2R; /* P0 */ a[4] = 2.0 * PI; /* displacements by 11 constituents */ for (i = 0; i < 11; i++) { ang = 0.0; for (j = 0; j < 5; j++) { ang += a[j] * args[i][j]; } for (j = 0; j < 3; j++) { dp[j] += odisp[j + i * 6] * cos(ang - odisp[j + 3 + i * 6] * D2R); } } denu[0] = -dp[1]; denu[1] = -dp[2]; denu[2] = dp[0]; trace(5, "tide_oload: denu=%.3f %.3f %.3f\n", denu[0], denu[1], denu[2]); } /* iers mean pole (ref [7] eq.7.25) ------------------------------------------*/ void iers_mean_pole(gtime_t tut, double *xp_bar, double *yp_bar) { const double ep2000[] = {2000, 1, 1, 0, 0, 0}; double y, y2, y3; y = timediff(tut, epoch2time(ep2000)) / 86400.0 / 365.25; if (y < 3653.0 / 365.25) { /* until 2010.0 */ y2 = y * y; y3 = y2 * y; *xp_bar = 55.974 + 1.8243 * y + 0.18413 * y2 + 0.007024 * y3; /* (mas) */ *yp_bar = 346.346 + 1.7896 * y - 0.10729 * y2 - 0.000908 * y3; } else { /* after 2010.0 */ *xp_bar = 23.513 + 7.6141 * y; /* (mas) */ *yp_bar = 358.891 - 0.6287 * y; } } /* displacement by pole tide (ref [7] eq.7.26) --------------------------------*/ void tide_pole(gtime_t tut, const double *pos, const double *erpv, double *denu) { double xp_bar, yp_bar, m1, m2, cosl, sinl; trace(3, "tide_pole: pos=%.3f %.3f\n", pos[0] * R2D, pos[1] * R2D); /* iers mean pole (mas) */ iers_mean_pole(tut, &xp_bar, &yp_bar); /* ref [7] eq.7.24 */ m1 = erpv[0] / AS2R - xp_bar * 1E-3; /* (as) */ m2 = -erpv[1] / AS2R + yp_bar * 1E-3; /* sin(2*theta) = sin(2*phi), cos(2*theta)=-cos(2*phi) */ cosl = cos(pos[1]); sinl = sin(pos[1]); denu[0] = 9E-3 * sin(pos[0]) * (m1 * sinl - m2 * cosl); /* de= Slambda (m) */ denu[1] = -9E-3 * cos(2.0 * pos[0]) * (m1 * cosl + m2 * sinl); /* dn=-Stheta (m) */ denu[2] = -33E-3 * sin(2.0 * pos[0]) * (m1 * cosl + m2 * sinl); /* du= Sr (m) */ trace(5, "tide_pole : denu=%.3f %.3f %.3f\n", denu[0], denu[1], denu[2]); } /* tidal displacement ---------------------------------------------------------- * displacements by earth tides * args : gtime_t tutc I time in utc * double *rr I site position (ecef) (m) * int opt I options (one of the following) * 1: solid earth tide * 2: ocean tide loading * 4: pole tide * 8: elimate permanent deformation * double *erp I earth rotation parameters (NULL: not used) * double *odisp I ocean loading parameters (NULL: not used) * odisp[0+i*6]: consituent i amplitude radial(m) * odisp[1+i*6]: consituent i amplitude west (m) * odisp[2+i*6]: consituent i amplitude south (m) * odisp[3+i*6]: consituent i phase radial (deg) * odisp[4+i*6]: consituent i phase west (deg) * odisp[5+i*6]: consituent i phase south (deg) * (i=0:M2,1:S2,2:N2,3:K2,4:K1,5:O1,6:P1,7:Q1, * 8:Mf,9:Mm,10:Ssa) * double *dr O displacement by earth tides (ecef) (m) * return : none * notes : see ref [1], [2] chap 7 * see ref [4] 5.2.1, 5.2.2, 5.2.3 * ver.2.4.0 does not use ocean loading and pole tide corrections *-----------------------------------------------------------------------------*/ void tidedisp(gtime_t tutc, const double *rr, int opt, const erp_t *erp, const double *odisp, double *dr) { gtime_t tut; double pos[2], E[9], drt[3], denu[3], rs[3], rm[3], gmst, erpv[5] = {0}; int i; #ifdef IERS_MODEL double ep[6], fhr; int year, mon, day; #endif trace(3, "tidedisp: tutc=%s\n", time_str(tutc, 0)); if (erp) { geterp(erp, tutc, erpv); } tut = timeadd(tutc, erpv[2]); dr[0] = dr[1] = dr[2] = 0.0; if (norm_rtk(rr, 3) <= 0.0) { return; } pos[0] = asin(rr[2] / norm_rtk(rr, 3)); pos[1] = atan2(rr[1], rr[0]); xyz2enu(pos, E); if (opt & 1) { /* solid earth tides */ /* sun and moon position in ecef */ sunmoonpos(tutc, erpv, rs, rm, &gmst); #ifdef IERS_MODEL time2epoch(tutc, ep); year = (int)ep[0]; mon = (int)ep[1]; day = (int)ep[2]; fhr = ep[3] + ep[4] / 60.0 + ep[5] / 3600.0; /* call DEHANTTIDEINEL */ // dehanttideinel_((double *)rr,&year,&mon,&day,&fhr,rs,rm,drt); #else tide_solid(rs, rm, pos, E, gmst, opt, drt); #endif for (i = 0; i < 3; i++) { dr[i] += drt[i]; } } if ((opt & 2) && odisp) { /* ocean tide loading */ tide_oload(tut, odisp, denu); matmul("TN", 3, 1, 3, 1.0, E, denu, 0.0, drt); for (i = 0; i < 3; i++) { dr[i] += drt[i]; } } if ((opt & 4) && erp) { /* pole tide */ tide_pole(tut, pos, erpv, denu); matmul("TN", 3, 1, 3, 1.0, E, denu, 0.0, drt); for (i = 0; i < 3; i++) { dr[i] += drt[i]; } } trace(5, "tidedisp: dr=%.3f %.3f %.3f\n", dr[0], dr[1], dr[2]); } src/algorithms/libs/rtklib/rtklib_tides.h000066400000000000000000000073751352176506000211070ustar00rootroot00000000000000/*! * \file rtklib_tides.h * \brief Tidal displacement corrections * \authors
    *
  • 2015, T. Takasu *
  • 2017, Javier Arribas *
  • 2017, Carles Fernandez *
* * This is a derived work from RTKLIB http://www.rtklib.com/ * The original source code at https://github.com/tomojitakasu/RTKLIB is * released under the BSD 2-clause license with an additional exclusive clause * that does not apply here. This additional clause is reproduced below: * * " The software package includes some companion executive binaries or shared * libraries necessary to execute APs on Windows. These licenses succeed to the * original ones of these software. " * * Neither the executive binaries nor the shared libraries are required by, used * or included in GNSS-SDR. * * ------------------------------------------------------------------------- * Copyright (C) 2015, T. Takasu * Copyright (C) 2017, Javier Arribas * Copyright (C) 2017, Carles Fernandez * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * References: * [1] D.D.McCarthy, IERS Technical Note 21, IERS Conventions 1996, July 1996 * [2] D.D.McCarthy and G.Petit, IERS Technical Note 32, IERS Conventions * 2003, November 2003 * [3] D.A.Vallado, Fundamentals of Astrodynamics and Applications 2nd ed, * Space Technology Library, 2004 * [4] J.Kouba, A Guide to using International GNSS Service (IGS) products, * May 2009 * [5] G.Petit and B.Luzum (eds), IERS Technical Note No. 36, IERS * Conventions (2010), 2010 *----------------------------------------------------------------------------*/ #ifndef GNSS_SDR_RTKLIB_TIDES_H_ #define GNSS_SDR_RTKLIB_TIDES_H_ #include "rtklib.h" const double GME = 3.986004415E+14; /* earth gravitational constant */ const double GMS = 1.327124E+20; /* sun gravitational constant */ const double GMM = 4.902801E+12; /* moon gravitational constant */ void tide_pl(const double *eu, const double *rp, double GMp, const double *pos, double *dr); void tide_solid(const double *rsun, const double *rmoon, const double *pos, const double *E, double gmst, int opt, double *dr); void tide_oload(gtime_t tut, const double *odisp, double *denu); void iers_mean_pole(gtime_t tut, double *xp_bar, double *yp_bar); void tide_pole(gtime_t tut, const double *pos, const double *erpv, double *denu); void tidedisp(gtime_t tutc, const double *rr, int opt, const erp_t *erp, const double *odisp, double *dr); #endif src/algorithms/libs/short_x2_to_cshort.cc000066400000000000000000000047501352176506000211250ustar00rootroot00000000000000/*! * \file short_x2_to_cshort.cc * \brief Adapts two short streams into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "short_x2_to_cshort.h" #include #include #include // for max #include // for complex short_x2_to_cshort_sptr make_short_x2_to_cshort() { return short_x2_to_cshort_sptr(new short_x2_to_cshort()); } short_x2_to_cshort::short_x2_to_cshort() : sync_block("short_x2_to_cshort", gr::io_signature::make(2, 2, sizeof(int16_t)), gr::io_signature::make(1, 1, sizeof(lv_16sc_t))) { const int alignment_multiple = volk_gnsssdr_get_alignment() / sizeof(lv_16sc_t); set_alignment(std::max(1, alignment_multiple)); } int short_x2_to_cshort::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const auto *in0 = reinterpret_cast(input_items[0]); const auto *in1 = reinterpret_cast(input_items[1]); auto *out = reinterpret_cast(output_items[0]); // This could be put into a volk kernel int16_t real_part; int16_t imag_part; for (int number = 0; number < noutput_items; number++) { real_part = *in0++; imag_part = *in1++; *out++ = lv_cmake(real_part, imag_part); } return noutput_items; } src/algorithms/libs/short_x2_to_cshort.h000066400000000000000000000035661352176506000207730ustar00rootroot00000000000000/*! * \file short_x2_to_cshort.h * \brief Adapts two short streams into a std::complex stream * \author Carles Fernandez Prades, cfernandez(at)cttc.es * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_SHORT_X2_TO_CSHORT_H_ #define GNSS_SDR_SHORT_X2_TO_CSHORT_H_ #include #include #include // for gr_vector_const_void_star class short_x2_to_cshort; using short_x2_to_cshort_sptr = boost::shared_ptr; short_x2_to_cshort_sptr make_short_x2_to_cshort(); /*! * \brief This class adapts two short streams into a std::complex stream */ class short_x2_to_cshort : public gr::sync_block { private: friend short_x2_to_cshort_sptr make_short_x2_to_cshort(); short_x2_to_cshort(); public: int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; #endif src/algorithms/libs/volk_gnsssdr_module/000077500000000000000000000000001352176506000210375ustar00rootroot00000000000000src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/000077500000000000000000000000001352176506000235555ustar00rootroot00000000000000src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/.gitignore000066400000000000000000000000721352176506000255440ustar00rootroot00000000000000*~ *.pyc *.pyo html/ build/ .project .cproject /.DS_Store src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/CMakeLists.txt000066400000000000000000000424241352176506000263230ustar00rootroot00000000000000# # Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) # # This file is part of GNSS-SDR. # # GNSS-SDR is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # GNSS-SDR 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNSS-SDR. If not, see . # ######################################################################## # Project setup ######################################################################## cmake_minimum_required(VERSION 2.8.12) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose build type: None Debug Release RelWithDebInfo MinSizeRel") project(volk_gnsssdr) enable_language(CXX) enable_language(C) enable_testing() set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # allows this to be a sub-project set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # allows this to be a sub-project list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) # location for custom "Modules" if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if(POLICY CMP0068) cmake_policy(SET CMP0068 NEW) endif() if(POLICY CMP0057) cmake_policy(SET CMP0057 NEW) endif() # Set compiler flags set(GNSSSDR_CLANG_MIN_VERSION "3.4.0") set(GNSSSDR_APPLECLANG_MIN_VERSION "500") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") execute_process(COMMAND ${CMAKE_CXX_COMPILER} -v RESULT_VARIABLE _res ERROR_VARIABLE _err ERROR_STRIP_TRAILING_WHITESPACE) if(${_res} STREQUAL "0") # output is in error stream string(REGEX MATCH "^Apple.*" IS_APPLE ${_err}) if("${IS_APPLE}" STREQUAL "") set(MIN_VERSION ${GNSSSDR_CLANG_MIN_VERSION}) set(APPLE_STR "") # retrieve the compiler's version from it string(REGEX MATCH "clang version [0-9.]+" CLANG_OTHER_VERSION ${_err}) string(REGEX MATCH "[0-9.]+" CLANG_VERSION ${CLANG_OTHER_VERSION}) else() set(MIN_VERSION ${GNSSSDR_APPLECLANG_MIN_VERSION}) set(APPLE_STR "Apple ") # retrieve the compiler's version from it string(REGEX MATCH "(clang-[0-9.]+)" CLANG_APPLE_VERSION ${_err}) string(REGEX MATCH "[0-9.]+" CLANG_VERSION ${CLANG_APPLE_VERSION}) endif() if(${CLANG_VERSION} VERSION_LESS "${MIN_VERSION}") message(WARNING "\nThe compiler selected to build VOLK-GNSSSDR (${APPLE_STR}Clang version ${CLANG_VERSION} : ${CMAKE_CXX_COMPILER}) is older than that officially supported (${MIN_VERSION} minimum). This build may or not work. We highly recommend using Apple Clang version ${APPLECLANG_MIN_VERSION} or more recent, or Clang version ${CLANG_MIN_VERSION} or more recent.") endif() else() message(WARNING "\nCannot determine the version of the compiler selected to build VOLK-GNSSSDR (${APPLE_STR}Clang : ${CMAKE_CXX_COMPILER}). This build may or not work. We highly recommend using Apple Clang version ${APPLECLANG_MIN_VERSION} or more recent, or Clang version ${CLANG_MIN_VERSION} or more recent.") endif() endif() #### Set C++ standard set(CMAKE_CXX_EXTENSIONS OFF) # Check if we have std::filesystem if(CMAKE_VERSION VERSION_LESS 3.8) set(FILESYSTEM_FOUND FALSE) else() find_package(FILESYSTEM COMPONENTS Final Experimental) endif() if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.1.1") if(CMAKE_VERSION VERSION_LESS "3.1") set(MY_CXX_FLAGS "${MY_CXX_FLAGS} -std=c++11") else() set(CMAKE_CXX_STANDARD 11) endif() else() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.0.0") set(CMAKE_CXX_STANDARD 14) else() if(${FILESYSTEM_FOUND}) if(CMAKE_VERSION VERSION_LESS 3.12) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) else() set(CMAKE_CXX_STANDARD 14) endif() endif() endif() set(MY_CXX_FLAGS "${MY_CXX_FLAGS} -Wall -Wextra") # Add warning flags: For "-Wall" see http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # See https://trac.macports.org/wiki/XcodeVersionInfo for Apple Clang version equivalences if(CLANG_VERSION VERSION_LESS "600") set(CMAKE_CXX_STANDARD 11) else() if(CLANG_VERSION VERSION_LESS "1000") set(CMAKE_CXX_STANDARD 14) else() if(${FILESYSTEM_FOUND}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) else() set(CMAKE_CXX_STANDARD 14) endif() endif() endif() else() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.5.0") if(CMAKE_VERSION VERSION_LESS "3.1") set(MY_CXX_FLAGS "${MY_CXX_FLAGS} -std=c++11") else() set(CMAKE_CXX_STANDARD 11) endif() else() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.0.0") set(CMAKE_CXX_STANDARD 14) else() if(${FILESYSTEM_FOUND}) if(CMAKE_VERSION VERSION_LESS 3.12) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) else() set(CMAKE_CXX_STANDARD 14) endif() endif() endif() endif() endif() if(NOT ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND NOT WIN32) AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) if(NOT (CMAKE_VERSION VERSION_LESS "3.1")) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 11) endif() endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_CXX_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") option(ENABLE_STRIP "Create a stripped volk_gnsssdr_profile binary (without shared libraries)" OFF) if(ENABLE_STRIP) set(CMAKE_VERBOSE_MAKEFILE ON) endif() include(VolkBuildTypes) # select the release build type by default to get optimization flags if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") message(STATUS "Build type not specified: defaulting to release.") endif() volk_check_build_type(${CMAKE_BUILD_TYPE}) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}.") set(VERSION_INFO_MAJOR_VERSION 0) set(VERSION_INFO_MINOR_VERSION 0) set(VERSION_INFO_MAINT_VERSION 11) include(VolkVersion) # setup version info ######################################################################## # Environment setup ######################################################################## if(NOT DEFINED BOOST_ROOT AND NOT DEFINED ENV{BOOST_ROOT}) set(BOOST_ROOT ${CMAKE_INSTALL_PREFIX}) endif() if(NOT DEFINED CROSSCOMPILE_MULTILIB) set(CROSSCOMPILE_MULTILIB "") endif() set(CROSSCOMPILE_MULTILIB ${CROSSCOMPILE_MULTILIB} CACHE STRING "Define \"true\" if you have and want to use multiple C development libs installed for cross compile") if(MSVC) add_definitions(-D_USE_MATH_DEFINES) # enables math constants on all supported versions of MSVC add_compile_options(/W1) # reduce warnings add_compile_options(/wo4309) add_compile_options(/wd4752) add_compile_options(/wo4273) add_compile_options(/wo4838) endif() # allow 'large' files in 32 bit builds if(UNIX) add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D_FORTIFY_SOURCE=2 ) endif() ######################################################################## # Dependencies setup ######################################################################## # Python include(VolkPython) # sets PYTHON_EXECUTABLE and PYTHON_DASH_B volk_python_check_module("python >= 2.7" sys "sys.version.split()[0] >= '2.7'" PYTHON_MIN_VER_FOUND) volk_python_check_module("mako >= 0.4.2" mako "mako.__version__ >= '0.4.2'" MAKO_FOUND) volk_python_check_module("six - python 2 and 3 compatibility library" six "True" SIX_FOUND) if(NOT PYTHON_MIN_VER_FOUND) message(FATAL_ERROR "Python 2.7 or greater required to build VOLK_GNSSSDR") endif() # Mako if(NOT MAKO_FOUND) message(FATAL_ERROR "Mako templates required to build VOLK_GNSSSDR") endif() # Six if(NOT SIX_FOUND) message(FATAL_ERROR "six - python 2 and 3 compatibility library required to build VOLK") endif() # Boost if(MSVC) if(NOT DEFINED BOOST_ALL_DYN_LINK) set(BOOST_ALL_DYN_LINK TRUE) endif() set(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") if(BOOST_ALL_DYN_LINK) add_definitions(-DBOOST_ALL_DYN_LINK) # setup boost auto-linking in msvc else() unset(BOOST_REQUIRED_COMPONENTS) # empty components list for static link endif() endif() if(${FILESYSTEM_FOUND}) set(Boost_LIBRARIES "") set(Boost_INCLUDE_DIRS "") set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH}) else() include(VolkBoost) if(NOT Boost_FOUND) message(FATAL_ERROR "VOLK-GNSSSDR requires Boost to build") endif() endif() # Orc option(ENABLE_ORC "Enable Orc" TRUE) if(ENABLE_ORC) find_package(ORC) else() message(STATUS "Disabling use of ORC") endif() ######################################################################## # Setup doxygen ######################################################################## find_package(Doxygen) if(DOXYGEN_FOUND) configure_file( ${PROJECT_SOURCE_DIR}/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMENT "Generating documentation with Doxygen" VERBATIM ) endif() ######################################################################## # Setup the package config file ######################################################################## # set variables found in the pc.in file set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/lib${LIB_SUFFIX}") set(includedir "\${prefix}/include") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/volk_gnsssdr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr.pc @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/volk_gnsssdr.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig COMPONENT "volk_gnsssdr_devel" ) ######################################################################## # Install all headers in the include directories ######################################################################## set(VOLK_RUNTIME_DIR bin) set(VOLK_LIBRARY_DIR lib${LIB_SUFFIX}) set(VOLK_INCLUDE_DIR include) install( DIRECTORY ${PROJECT_SOURCE_DIR}/kernels/volk_gnsssdr DESTINATION include COMPONENT "volk_gnsssdr_devel" FILES_MATCHING PATTERN "*.h" ) install(FILES ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_prefs.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_complex.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_common.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/saturation_arithmetic.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_avx_intrinsics.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_sse_intrinsics.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_sse3_intrinsics.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_neon_intrinsics.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_cpu.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_config_fixed.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_typedefs.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_malloc.h ${PROJECT_SOURCE_DIR}/include/volk_gnsssdr/volk_gnsssdr_sine_table.h DESTINATION include/volk_gnsssdr COMPONENT "volk_gnsssdr_devel" ) ######################################################################## # On Apple only, set install name and use rpath correctly, if not already set ######################################################################## if(APPLE) if(NOT CMAKE_INSTALL_NAME_DIR) set(CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/${VOLK_LIBRARY_DIR} CACHE PATH "Library Install Name Destination Directory" FORCE) endif() if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${VOLK_LIBRARY_DIR} CACHE PATH "Library Install RPath" FORCE) endif() if(NOT CMAKE_BUILD_WITH_INSTALL_RPATH) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "Do Build Using Library Install RPath" FORCE) endif() endif() ######################################################################## # Create uninstall target ######################################################################## configure_file( ${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY) # Only add the target if there isn't one defined already if(NOT TARGET uninstall) add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake ) endif() ######################################################################## # Install our Cmake modules into $prefix/lib/cmake/volk_gnsssdr # See "Package Configuration Files" on page: # http://www.cmake.org/Wiki/CMake/Tutorials/Packaging ######################################################################## configure_file( ${CMAKE_SOURCE_DIR}/cmake/Modules/VolkGnsssdrConfig.cmake.in ${CMAKE_BINARY_DIR}/cmake/Modules/VolkGnsssdrConfig.cmake @ONLY) configure_file( ${PROJECT_SOURCE_DIR}/cmake/Modules/VolkGnsssdrConfigVersion.cmake.in ${PROJECT_BINARY_DIR}/cmake/Modules/VolkGnsssdrConfigVersion.cmake @ONLY) ######################################################################## # Install cmake search routine for external use ######################################################################## if(NOT CMAKE_MODULES_DIR) set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake) endif() install( FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/Modules/VolkGnsssdrConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake/Modules/VolkGnsssdrConfigVersion.cmake DESTINATION ${CMAKE_MODULES_DIR}/volk_gnsssdr COMPONENT "volk_gnsssdr_devel" ) install(EXPORT VOLK_GNSSSDR-export FILE VolkGnsssdrTargets.cmake NAMESPACE VolkGnsssdr:: DESTINATION ${CMAKE_MODULES_DIR}/volk_gnsssdr ) ######################################################################## # Option to enable QA testing, on by default ######################################################################## option(ENABLE_TESTING "Enable QA testing" ON) if(ENABLE_TESTING) message(STATUS "QA Testing is enabled.") else() message(STATUS "QA Testing is disabled.") endif() message(STATUS " Modify using: -DENABLE_TESTING=ON/OFF") ######################################################################## # Option to enable post-build profiling using volk_profile, off by default ######################################################################## option(ENABLE_PROFILING "Launch system profiler after build" OFF) if(ENABLE_PROFILING) set(ENABLE_STATIC_LIBS ON) if(DEFINED VOLK_CONFIGPATH) get_filename_component(VOLK_CONFIGPATH ${VOLK_CONFIGPATH} ABSOLUTE) set(VOLK_CONFIGPATH "${VOLK_CONFIGPATH}/volk_gnsssdr") message(STATUS "System profiling is enabled, using path: ${VOLK_CONFIGPATH}") elseif(DEFINED ENV{VOLK_CONFIGPATH}) set(VOLK_CONFIGPATH "$ENV{VOLK_CONFIGPATH}/volk_gnsssdr") message(STATUS "System profiling is enabled, using env path: $ENV{VOLK_CONFIGPATH}") else() message(STATUS "System profiling is enabled with default paths.") if(DEFINED ENV{HOME}) set(VOLK_CONFIGPATH "$ENV{HOME}/.volk_gnsssdr") elseif(DEFINED ENV{APPDATA}) set(VOLK_CONFIGPATH "$ENV{APPDATA}/.volk_gnsssdr") endif() endif() else() message(STATUS "System profiling is disabled.") endif() message(STATUS " Modify using: -DENABLE_PROFILING=ON/OFF") ######################################################################## ######################################################################## # Setup the library ######################################################################## add_subdirectory(lib) ######################################################################## # And the utility apps ######################################################################## add_subdirectory(apps) add_subdirectory(python/volk_gnsssdr_modtool) ######################################################################## # Print summary ######################################################################## message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/COPYING000066400000000000000000001057611352176506000246220ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . src/algorithms/libs/volk_gnsssdr_module/volk_gnsssdr/Doxyfile.in000066400000000000000000003102041352176506000256700ustar00rootroot00000000000000# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See https://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Vector-Optimized Library of Kernels for GNSS-SDR" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Architecture-tuned implementations of math kernels" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. # TODO: configure this to be a special docs directory. nw tried, but running # make doc won' create the directory, but with doxygen it will. why? OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @PROJECT_SOURCE_DIR@/DoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = @PROJECT_SOURCE_DIR@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.for \ *.tcl \ *.vhd \ *.vhdl \ *.ucf \ *.qsf \ *.as \ *.js # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = @PROJECT_BINARY_DIR@ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = @PROJECT_SOURCE_DIR@/docs/images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: https://developer.apple.com/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # https://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdnjs.com/libraries/mathjax/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.com/libraries/mathjax/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /