From 72a4ed3878227a69c3090fbaec99211887fbbb93 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Fri, 11 Jul 2025 11:30:33 +0200 Subject: [PATCH 1/6] Avoid tracking the same PRN in multiple channels (Fixes: #885) --- docs/CHANGELOG.md | 10 +++++++++- src/core/receiver/gnss_flowgraph.cc | 14 +++++++++++++- src/core/system_parameters/gnss_satellite.h | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7a3a81195..83e39048a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,11 @@ All notable changes to GNSS-SDR will be documented in this file. ## [Unreleased](https://github.com/gnss-sdr/gnss-sdr/tree/next) +### Improvements in Interoperability: + +- Enabled multi-band processing off-loading to the FPGA when using the MAX2771 + RF front-end. + ### Improvements in Maintainability: - Added a CI job to detect lines longer than 512 characters (avoid this @@ -27,7 +32,7 @@ All notable changes to GNSS-SDR will be documented in this file. ### Improvements in Portability: -- Fix building option `-DENABLE_ION=ON` when using CMake >= 4.0. +- Fixed building option `-DENABLE_ION=ON` when using CMake >= 4.0. - Updated `gsl-lite` to v1.0.1. See the [gsl-lite release](https://github.com/gsl-lite/gsl-lite/releases/tag/v1.0.1). @@ -38,6 +43,9 @@ All notable changes to GNSS-SDR will be documented in this file. PDF format. It requires `numpy` and `matplotlib`. - `File_Signal_Source` fixed file length and sample skip calculations on 32-bit systems. +- Fixed tracking the same PRN in multiple channels. Previously, this could + happen when the number of acquisition channels was close to the number of + available PRNs for a given signal. See the definitions of concepts and metrics at https://gnss-sdr.org/design-forces/ diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index 232b0b530..feaf930cb 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -2192,6 +2192,14 @@ Gnss_Signal GNSSFlowgraph::search_next_signal(const std::string& searched_signal std::string assist_signal = ""; auto& available_signals = available_signals_map_.at(searched_signal); + if (available_signals.empty()) + { + const auto& entry = signal_mapping.at(searched_signal); + const auto& gnss_system_str = entry.first; + const auto& signal_pretty_str = entry.second; + throw std::runtime_error("More ACQUISITION channels than PRNs for signal " + gnss_system_str + " " + signal_pretty_str); + } + switch (mapStringValues_[searched_signal]) { case evGPS_2S: @@ -2250,7 +2258,11 @@ Gnss_Signal GNSSFlowgraph::search_next_signal(const std::string& searched_signal { result = available_signals.front(); available_signals.pop_front(); - available_signals.push_back(result); + const std::string sys = result.get_satellite().get_system(); + if ((sys == "Glonass") || (sys == "Beidou")) + { + available_signals.push_back(result); + } } return result; diff --git a/src/core/system_parameters/gnss_satellite.h b/src/core/system_parameters/gnss_satellite.h index e7a2759f8..c4585ce3a 100644 --- a/src/core/system_parameters/gnss_satellite.h +++ b/src/core/system_parameters/gnss_satellite.h @@ -54,7 +54,7 @@ public: void update_PRN(uint32_t PRN); //!< Updates the PRN Number when information is decoded, only applies to GLONASS GNAV messages uint32_t get_PRN() const; //!< Gets satellite's PRN int32_t get_rf_link() const; //!< Gets the satellite's rf link - std::string get_system() const; //!< Gets the satellite system {"GPS", "GLONASS", "SBAS", "Galileo", "Beidou"} + std::string get_system() const; //!< Gets the satellite system {"GPS", "Glonass", "SBAS", "Galileo", "Beidou"} std::string get_system_short() const; //!< Gets the satellite system {"G", "R", "SBAS", "E", "C"} std::string get_block() const; //!< Gets the satellite block. If GPS, returns {"IIA", "IIR", "IIR-M", "IIF"} std::string what_block(const std::string& system_, uint32_t PRN_); //!< Gets the block of a given satellite From ef01835b283bb784529e62ab874f9abc1f1942c0 Mon Sep 17 00:00:00 2001 From: pedromiguelcp Date: Wed, 9 Jul 2025 22:19:20 +0100 Subject: [PATCH 2/6] feat: add signal source adapter for NTLab NT1065 --- conf/File_input/MultiCons/gnss-sdr_ntlab.conf | 362 ++++++++++++++++++ .../signal_source/adapters/CMakeLists.txt | 2 + .../adapters/ntlab_file_signal_source.cc | 132 +++++++ .../adapters/ntlab_file_signal_source.h | 72 ++++ .../gnuradio_blocks/CMakeLists.txt | 2 + .../unpack_ntlab_2bit_samples.cc | 87 +++++ .../unpack_ntlab_2bit_samples.h | 70 ++++ src/core/receiver/gnss_block_factory.cc | 5 + 8 files changed, 732 insertions(+) create mode 100644 conf/File_input/MultiCons/gnss-sdr_ntlab.conf create mode 100644 src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc create mode 100644 src/algorithms/signal_source/adapters/ntlab_file_signal_source.h create mode 100644 src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc create mode 100644 src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h diff --git a/conf/File_input/MultiCons/gnss-sdr_ntlab.conf b/conf/File_input/MultiCons/gnss-sdr_ntlab.conf new file mode 100644 index 000000000..d3366e704 --- /dev/null +++ b/conf/File_input/MultiCons/gnss-sdr_ntlab.conf @@ -0,0 +1,362 @@ +; This is a GNSS-SDR configuration file +; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ +; SPDX-License-Identifier: GPL-3.0-or-later +; SPDX-FileCopyrightText: (C) 2010-2020 (see AUTHORS file for a list of contributors) + +; 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=39750000 +GNSS-SDR.telecommand_enabled=true +GNSS-SDR.telecommand_tcp_port=3333 +GNSS-SDR.osnma_enable=false + + +;######### SUPL RRLP GPS assistance configuration ##### +; Check https://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=NTLab_File_Signal_Source +SignalSource.filename=./ntlab.bin ; <- PUT YOUR FILE HERE +SignalSource.item_type=byte +SignalSource.big_endian_bytes=false +SignalSource.sample_type=real +SignalSource.sampling_frequency=79500000 +SignalSource.samples=0 +SignalSource.repeat=false +SignalSource.dump=false +SignalSource.dump_filename=./signal_source.dat +SignalSource.RF_channels=4 +SignalSource.enable_throttle_control=false + + +;######### SIGNAL_CONDITIONER CONFIG ############ +SignalConditioner0.implementation=Signal_Conditioner +SignalConditioner1.implementation=Signal_Conditioner +SignalConditioner2.implementation=Signal_Conditioner +SignalConditioner3.implementation=Signal_Conditioner + + +;######### DATA_TYPE_ADAPTER CONFIG ############ +DataTypeAdapter0.implementation=Pass_Through +DataTypeAdapter0.item_type=float +DataTypeAdapter0.inverted_spectrum=true +DataTypeAdapter1.implementation=Pass_Through +DataTypeAdapter1.item_type=float +DataTypeAdapter2.implementation=Pass_Through +DataTypeAdapter2.item_type=float +DataTypeAdapter2.inverted_spectrum=true +DataTypeAdapter3.implementation=Pass_Through +DataTypeAdapter3.item_type=float + + +;######### INPUT_FILTER CONFIG ############ +; CHANNEL 0 +InputFilter0.implementation=Freq_Xlating_Fir_Filter +InputFilter0.dump=false +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=79500000 +InputFilter0.IF=-14580000 +InputFilter0.decimation_factor=2 +; CHANNEL 1 +InputFilter1.implementation=Freq_Xlating_Fir_Filter +InputFilter1.dump=false +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=79500000 +InputFilter1.IF=-14580000 +InputFilter1.decimation_factor=2 +; CHANNEL 2 +InputFilter2.implementation=Freq_Xlating_Fir_Filter +InputFilter2.dump=false +InputFilter2.input_item_type=float +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=79500000 +InputFilter2.IF=-23550000 +InputFilter2.decimation_factor=2 +; CHANNEL 3 +InputFilter3.implementation=Freq_Xlating_Fir_Filter +InputFilter3.dump=false +InputFilter3.input_item_type=float +InputFilter3.output_item_type=gr_complex +InputFilter3.taps_item_type=float +InputFilter3.number_of_taps=5 +InputFilter3.number_of_bands=2 +InputFilter3.band1_begin=0.0 +InputFilter3.band1_end=0.45 +InputFilter3.band2_begin=0.55 +InputFilter3.band2_end=1.0 +InputFilter3.ampl1_begin=1.0 +InputFilter3.ampl1_end=1.0 +InputFilter3.ampl2_begin=0.0 +InputFilter3.ampl2_end=0.0 +InputFilter3.band1_error=1.0 +InputFilter3.band2_error=1.0 +InputFilter3.filter_type=bandpass +InputFilter3.grid_density=16 +InputFilter3.sampling_frequency=79500000 +InputFilter3.IF=-2355000000 +InputFilter3.decimation_factor=2 + + +;######### RESAMPLER CONFIG ############ +Resampler0.implementation=Pass_Through +Resampler0.item_type=gr_complex +Resampler1.implementation=Pass_Through +Resampler1.item_type=gr_complex +Resampler2.implementation=Pass_Through +Resampler2.item_type=gr_complex +Resampler3.implementation=Pass_Through +Resampler3.item_type=gr_complex + + +;######### CHANNELS GLOBAL CONFIG ############ +Channels_1C.count=8 +Channels_1B.count=8 +Channels_5X.count=8 +Channels_L5.count=8 +Channels.in_acquisition=1 + +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=2 +Channel9.RF_channel_ID=2 +Channel10.RF_channel_ID=2 +Channel11.RF_channel_ID=2 +Channel12.RF_channel_ID=2 +Channel13.RF_channel_ID=2 +Channel14.RF_channel_ID=2 +Channel15.RF_channel_ID=2 + +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=2 +Channel25.RF_channel_ID=2 +Channel26.RF_channel_ID=2 +Channel27.RF_channel_ID=2 +Channel28.RF_channel_ID=2 +Channel29.RF_channel_ID=2 +Channel30.RF_channel_ID=2 +Channel31.RF_channel_ID=2 + +;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=L5 +;Channel9.signal=L5 +;Channel10.signal=L5 +;Channel11.signal=L5 +;Channel12.signal=L5 +;Channel13.signal=L5 +;Channel14.signal=L5 +;Channel15.signal=L5 +; +;Channel16.signal=1B +;Channel17.signal=1B +;Channel18.signal=1B +;Channel19.signal=1B +;Channel20.signal=1B +;Channel21.signal=1B +;Channel22.signal=1B +;Channel23.signal=1B +; +;Channel24.signal=5X +;Channel25.signal=5X +;Channel26.signal=5X +;Channel27.signal=5X +;Channel28.signal=5X +;Channel29.signal=5X +;Channel30.signal=5X +;Channel31.signal=5X + + +;######### ACQUISITION CONFIG ############ +;# GPS L1 +Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition +Acquisition_1C.item_type=gr_complex +Acquisition_L5.threshold=2 +Acquisition_1C.doppler_max=5000 +Acquisition_1C.doppler_step=500 +Acquisition_1C.dump=false +Acquisition_1C.dump_filename=./acq_dump.dat +;# GPS L5 +Acquisition_L5.implementation=GPS_L5i_PCPS_Acquisition +Acquisition_L5.item_type=gr_complex +Acquisition_L5.threshold=2 +Acquisition_L5.doppler_max=5000 +Acquisition_L5.doppler_step=125 +Acquisition_L5.dump=false +;# Galileo E1b +Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition +Acquisition_1B.item_type=gr_complex +Acquisition_1B.threshold=2 +Acquisition_1B.doppler_max=5000 +Acquisition_1B.doppler_step=125 +Acquisition_1B.dump=false +;# Galileo E5a +Acquisition_5X.implementation=Galileo_E5a_Pcps_Acquisition +Acquisition_5X.item_type=gr_complex +Acquisition_5X.threshold=2 +Acquisition_5X.doppler_max=5000 +Acquisition_5X.doppler_step=125 +Acquisition_5X.dump=false + + +;######### TRACKING CONFIG ############ +;# GPS L1 +Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking +Tracking_1C.item_type=gr_complex +Tracking_1C.dump=false +Tracking_1C.dump_filename=./epl_tracking_ch_ +;# GPS L5 +Tracking_L5.implementation=GPS_L5_DLL_PLL_Tracking +Tracking_L5.item_type=gr_complex +Tracking_L5.dump=false +Tracking_L5.dump_mat=false +;# Galileo E1b +Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking +Tracking_1B.item_type=gr_complex +Tracking_1B.dump=false +Tracking_1B.dump_mat=false +;# Galileo E5a +Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking +Tracking_5X.item_type=gr_complex +Tracking_5X.dump=false +Tracking_5X.dump_mat=false + + +;######### TELEMETRY DECODER CONFIG ############ +TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder +TelemetryDecoder_1C.dump=false +TelemetryDecoder_1C.dump_mat=false +TelemetryDecoder_L5.implementation=GPS_L5_Telemetry_Decoder +TelemetryDecoder_L5.dump=false +TelemetryDecoder_L5.dump_mat=false +TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder +TelemetryDecoder_1B.dump=false +TelemetryDecoder_1B.dump_mat=false +TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder +TelemetryDecoder_5X.dump=false +TelemetryDecoder_5X.dump_mat=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;PPP_Kinematic;PPP_Static; +PVT.output_rate_ms=500 +PVT.display_rate_ms=500 +PVT.iono_model=Broadcast;Iono-Free-LC; +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_output_enabled=true +PVT.rinexobs_rate_ms=1000 +PVT.enable_monitor=true +PVT.monitor_client_addresses=127.0.0.1 +PVT.monitor_udp_port=0101 +PVT.flag_nmea_tty_port=true +PVT.nmea_dump_devname=/dev/pts/12 +PVT.nmea_output_file_enabled=true +PVT.enable_rx_clock_correction=false +PVT.elevation_mask=5 +PVT.dump=true +PVT.dump_mat=false + + +;######### MONITOR CONFIG ############ +Monitor.enable_monitor=false +Monitor.decimator_factor=50 +Monitor.client_addresses=127.0.0.1 +Monitor.udp_port=0101 \ No newline at end of file diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt index 48e18e5f3..f214af9a6 100644 --- a/src/algorithms/signal_source/adapters/CMakeLists.txt +++ b/src/algorithms/signal_source/adapters/CMakeLists.txt @@ -130,6 +130,7 @@ set(SIGNAL_SOURCE_ADAPTER_SOURCES labsat_signal_source.cc two_bit_cpx_file_signal_source.cc two_bit_packed_file_signal_source.cc + ntlab_file_signal_source.cc four_bit_cpx_file_signal_source.cc file_timestamp_signal_source.cc ${OPT_DRIVER_SOURCES} @@ -149,6 +150,7 @@ set(SIGNAL_SOURCE_ADAPTER_HEADERS labsat_signal_source.h two_bit_cpx_file_signal_source.h two_bit_packed_file_signal_source.h + ntlab_file_signal_source.h four_bit_cpx_file_signal_source.h file_timestamp_signal_source.h ${OPT_DRIVER_HEADERS} diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc new file mode 100644 index 000000000..64adb286e --- /dev/null +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc @@ -0,0 +1,132 @@ +/*! + * \file ntlab_file_signal_source.cc + * \brief Interface of a class that reads signal samples from a file. Each + * sample is two bits from multiple channels. + * + * \author Pedro Pereira, 2025 pereirapedrocp (at) gmail.com + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "ntlab_file_signal_source.h" +#include "configuration_interface.h" +#include "gnss_sdr_string_literals.h" + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +using namespace std::string_literals; + +MultiChannelTwoBitPackedFileSignalSource::MultiChannelTwoBitPackedFileSignalSource( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams, + Concurrent_Queue* queue) + : FileSourceBase(configuration, role, "ntlab_file_signal_source"s, queue, "byte"s), + sample_type_(configuration->property(role + ".sample_type", "real"s)) +{ + int default_n_channlels_ = 4; + n_channels_ = configuration->property(role + ".total_channels", default_n_channlels_); + if (n_channels_ == 0) + { + n_channels_ = configuration->property(role + ".RF_channels", default_n_channlels_); + } + + if (in_streams > 0) + { + LOG(ERROR) << "A signal source does not have input streams"; + } + if (out_streams > 1) + { + LOG(ERROR) << "This implementation only supports one output stream"; + } +} + +std::tuple MultiChannelTwoBitPackedFileSignalSource::itemTypeToSize() +{ + auto is_complex_t = false; + auto item_size = sizeof(char); // default + + if (item_type() == "byte") + { + item_size = sizeof(char); + } + else + { + LOG(WARNING) << "Configuration error: Unsupported item type. Using byte."; + } + + if (sample_type_ != "real") + { + is_complex_t = false; + } + else + { + LOG(WARNING) << "Configuration error: Unsupported sample type. Using real."; + } + + return std::make_tuple(item_size, is_complex_t); +} + +double MultiChannelTwoBitPackedFileSignalSource::packetsPerSample() const +{ + return 1.0; +} + +gnss_shared_ptr MultiChannelTwoBitPackedFileSignalSource::source() const +{ + return unpack_samples_; +} + +void MultiChannelTwoBitPackedFileSignalSource::create_file_source_hook() +{ + unpack_samples_ = make_unpack_ntlab_2bit_samples(item_size(), n_channels_); + DLOG(INFO) << "unpack_byte_2bit_samples(" << unpack_samples_->unique_id() << ")"; +} + +void MultiChannelTwoBitPackedFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block) +{ + top_block->connect(file_source(), 0, unpack_samples_, 0); + DLOG(INFO) << "connected file source to samples unpacker"; + + for (int n = 1; n < n_channels_; n++) + { + top_block->connect(unpack_samples_, n, valve(), n); + DLOG(INFO) << "connected samples unpacker to valve port " << n; + } +} + +void MultiChannelTwoBitPackedFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block) +{ + top_block->disconnect(file_source(), 0, unpack_samples_, 0); + DLOG(INFO) << "disconnected file source of samples unpacker"; + + for (int n = 1; n < n_channels_; n++) + { + top_block->disconnect(unpack_samples_, n, valve(), n); + DLOG(INFO) << "disconnected samples unpacker of valve port " << n; + } +} + +gr::basic_block_sptr MultiChannelTwoBitPackedFileSignalSource::get_left_block() +{ + LOG(WARNING) << "Left block of a signal source should not be retrieved"; + return gr::block_sptr(); +} + +gr::basic_block_sptr MultiChannelTwoBitPackedFileSignalSource::get_right_block() +{ + return valve(); +} diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h new file mode 100644 index 000000000..aab8c80ff --- /dev/null +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h @@ -0,0 +1,72 @@ +/*! + * \file ntlab_file_signal_source.h + * \brief Interface of a class that reads signal samples from a file. Each + * sample is two bits from multiple channels. + * + * \author Pedro Pereira, 2025 pereirapedrocp (at) gmail.com + * + * This class represents a file signal source. + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_NTLAB_FILE_SIGNAL_SOURCE_H +#define GNSS_SDR_NTLAB_FILE_SIGNAL_SOURCE_H + +#include "file_source_base.h" +#include "unpack_ntlab_2bit_samples.h" +#include +#include +#include + + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_adapters + * \{ */ + + +class ConfigurationInterface; + +/*! + * \brief Class that reads signals samples from a file + * and adapts it to a SignalSourceInterface + */ +class MultiChannelTwoBitPackedFileSignalSource : public FileSourceBase +{ +public: + MultiChannelTwoBitPackedFileSignalSource(const ConfigurationInterface* configuration, + const std::string& role, unsigned int in_streams, + unsigned int out_streams, Concurrent_Queue* queue); + + ~MultiChannelTwoBitPackedFileSignalSource() = default; + + gr::basic_block_sptr get_left_block() override; + gr::basic_block_sptr get_right_block() override; + +protected: + std::tuple itemTypeToSize() override; + double packetsPerSample() const override; + gnss_shared_ptr source() const override; + void create_file_source_hook() override; + void pre_connect_hook(gr::top_block_sptr top_block) override; + void pre_disconnect_hook(gr::top_block_sptr top_block) override; + +private: + std::string sample_type_; + unpack_ntlab_2bit_samples_sptr unpack_samples_; + int n_channels_; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_NTLAB_FILE_SIGNAL_SOURCE_H diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt index b5f993fb1..a47fdef22 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt +++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt @@ -29,6 +29,7 @@ set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES unpack_short_byte_samples.cc rtl_tcp_signal_source_c.cc unpack_2bit_samples.cc + unpack_ntlab_2bit_samples.cc unpack_spir_gss6450_samples.cc labsat23_source.cc ${OPT_DRIVER_SOURCES} @@ -43,6 +44,7 @@ set(SIGNAL_SOURCE_GR_BLOCKS_HEADERS unpack_short_byte_samples.h rtl_tcp_signal_source_c.h unpack_2bit_samples.h + unpack_ntlab_2bit_samples.h unpack_spir_gss6450_samples.h labsat23_source.h ${OPT_DRIVER_HEADERS} diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc new file mode 100644 index 000000000..77e7d3044 --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc @@ -0,0 +1,87 @@ +/*! + * \file unpack_ntlab_2bit_samples.cc + * + * \brief Unpacks multichannel 2-bit samples into 4 real-valued floats + * per input byte. + * \author Pedro Pereira pereirapedrocp (at) gmail.com + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#include "unpack_ntlab_2bit_samples.h" +#include + + +unpack_ntlab_2bit_samples_sptr make_unpack_ntlab_2bit_samples(size_t item_size, + int nchannels) +{ + return unpack_ntlab_2bit_samples_sptr( + new unpack_ntlab_2bit_samples(item_size, + nchannels)); +} + + +unpack_ntlab_2bit_samples::unpack_ntlab_2bit_samples(size_t item_size, + int nchannels) + : sync_interpolator("unpack_ntlab_2bit_samples", + gr::io_signature::make(1, 1, item_size), + gr::io_signature::make(nchannels, nchannels, sizeof(float)), + 1), // we make 4 floats out for every byte in + item_size_(item_size), + nchannels_(nchannels) +{ +} + + +int unpack_ntlab_2bit_samples::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + auto const *in = reinterpret_cast(input_items[0]); + + float *out[4]; + for (int n = 0; n < nchannels_; ++n) + { + out[n] = static_cast(output_items[n]); + } + + const int nbytes = noutput_items; + + for (int i = 0; i < nbytes; ++i) + { + // Unpack each of the four 2-bit samples in the byte 'b' into four real-valued outputs. + // + // The NTLAB format encodes samples as sign+magnitude pairs in each byte: + // bits 7-6 = [M0 S0] -> sample 0 + // bits 5-4 = [M1 S1] -> sample 1 + // bits 3-2 = [M2 S2] -> sample 2 + // bits 1-0 = [M3 S3] -> sample 3 + // + // Here we loop over channel index n = 0...3, compute the bit shift to extract + // the two bits for that channel (shift = 6,4,2,0), then: + // - M = magnitude bit (1->|sample|=3, 0->|sample|=1) + // - S = sign bit (1->positive, 0->negative) + // We reconstruct the signed sample value (+/-1 or +/-3) and store it in out[n][i]. + uint8_t b = static_cast(in[i]); + for (int n = 0; n < nchannels_; ++n) + { + int shift = 2 * (3 - n); // 6, 4, 2, 0 + int M = (b >> (shift + 1)) & 0x1; // magnitude bit + int S = (b >> shift) & 0x1; // sign bit + int mag = M ? 3 : 1; + int val = S ? +mag : -mag; + out[n][i] = static_cast(val); + } + } + + return nbytes; +} diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h new file mode 100644 index 000000000..60f252057 --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h @@ -0,0 +1,70 @@ +/*! + * \file unpack_ntlab_2bit_samples.h + * + * \brief Unpacks multichannel 2-bit samples into 4 real-valued floats + * per input byte. + * \author Pedro Pereira pereirapedrocp (at) gmail.com + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_UNPACK_NTLAB_2BIT_SAMPLES_H +#define GNSS_SDR_UNPACK_NTLAB_2BIT_SAMPLES_H + +#include "gnss_block_interface.h" +#include +#include +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_gnuradio_blocks + * \{ */ + + +class unpack_ntlab_2bit_samples; + +using unpack_ntlab_2bit_samples_sptr = gnss_shared_ptr; + +unpack_ntlab_2bit_samples_sptr make_unpack_ntlab_2bit_samples( + size_t item_size, + int nchannels = 4); + +/*! + * \brief This class implements conversion between byte packet multichannel samples + * to 2bit samples 1 byte = 4 2bit samples + */ +class unpack_ntlab_2bit_samples : public gr::sync_interpolator +{ +public: + ~unpack_ntlab_2bit_samples() = default; + + unpack_ntlab_2bit_samples(size_t item_size, + int nchannels); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +private: + friend unpack_ntlab_2bit_samples_sptr make_unpack_ntlab_2bit_samples_sptr( + size_t item_size, + int nchannels); + + std::vector work_buffer_; + size_t item_size_; + int nchannels_; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_UNPACK_NTLAB_2BIT_SAMPLES_H diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index 21606eb58..d28a1f0e8 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -102,6 +102,7 @@ #include "notch_filter.h" #include "notch_filter_lite.h" #include "nsr_file_signal_source.h" +#include "ntlab_file_signal_source.h" #include "pass_through.h" #include "pulse_blanking_filter.h" #include "rtklib_pvt.h" @@ -320,6 +321,10 @@ std::unique_ptr get_signal_source_block( { return std::make_unique(configuration, role, in_streams, out_streams, queue); } + else if (implementation == "NTLab_File_Signal_Source") + { + return std::make_unique(configuration, role, in_streams, out_streams, queue); + } else if (implementation == "Spir_File_Signal_Source") { return std::make_unique(configuration, role, in_streams, out_streams, queue); From f1eaf5253ecff81cd53283fdf6384ac948d47f4a Mon Sep 17 00:00:00 2001 From: pedromiguelcp Date: Thu, 10 Jul 2025 23:28:46 +0100 Subject: [PATCH 3/6] feat: add multichannel support for ntlab adapter --- conf/File_input/MultiCons/gnss-sdr_ntlab.conf | 182 ++++++++---------- .../adapters/ntlab_file_signal_source.cc | 24 ++- .../adapters/ntlab_file_signal_source.h | 6 +- .../unpack_ntlab_2bit_samples.cc | 40 ++-- .../unpack_ntlab_2bit_samples.h | 15 +- src/core/receiver/gnss_block_factory.cc | 2 +- 6 files changed, 127 insertions(+), 142 deletions(-) diff --git a/conf/File_input/MultiCons/gnss-sdr_ntlab.conf b/conf/File_input/MultiCons/gnss-sdr_ntlab.conf index d3366e704..c549d4d53 100644 --- a/conf/File_input/MultiCons/gnss-sdr_ntlab.conf +++ b/conf/File_input/MultiCons/gnss-sdr_ntlab.conf @@ -10,7 +10,7 @@ [GNSS-SDR] ;######### GLOBAL OPTIONS ################## -GNSS-SDR.internal_fs_sps=39750000 +GNSS-SDR.internal_fs_sps=9937500 GNSS-SDR.telecommand_enabled=true GNSS-SDR.telecommand_tcp_port=3333 GNSS-SDR.osnma_enable=false @@ -33,41 +33,44 @@ GNSS-SDR.SUPL_CI=0x31b0 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource.implementation=NTLab_File_Signal_Source -SignalSource.filename=./ntlab.bin ; <- PUT YOUR FILE HERE -SignalSource.item_type=byte -SignalSource.big_endian_bytes=false -SignalSource.sample_type=real +SignalSource.filename=ntlab.bin ; <- PUT YOUR FILE HERE SignalSource.sampling_frequency=79500000 -SignalSource.samples=0 -SignalSource.repeat=false -SignalSource.dump=false -SignalSource.dump_filename=./signal_source.dat +SignalSource.sample_type=real +SignalSource.item_type=byte SignalSource.RF_channels=4 -SignalSource.enable_throttle_control=false +SignalSource.dump=false ;######### SIGNAL_CONDITIONER CONFIG ############ +; RF CHANNEL 0 SignalConditioner0.implementation=Signal_Conditioner +; RF CHANNEL 1 SignalConditioner1.implementation=Signal_Conditioner +; RF CHANNEL 2 SignalConditioner2.implementation=Signal_Conditioner +; RF CHANNEL 3 SignalConditioner3.implementation=Signal_Conditioner ;######### DATA_TYPE_ADAPTER CONFIG ############ +; RF CHANNEL 0 DataTypeAdapter0.implementation=Pass_Through DataTypeAdapter0.item_type=float DataTypeAdapter0.inverted_spectrum=true +; RF CHANNEL 1 DataTypeAdapter1.implementation=Pass_Through DataTypeAdapter1.item_type=float +; RF CHANNEL 2 DataTypeAdapter2.implementation=Pass_Through DataTypeAdapter2.item_type=float DataTypeAdapter2.inverted_spectrum=true +; RF CHANNEL 3 DataTypeAdapter3.implementation=Pass_Through DataTypeAdapter3.item_type=float ;######### INPUT_FILTER CONFIG ############ -; CHANNEL 0 +; RF CHANNEL 0 InputFilter0.implementation=Freq_Xlating_Fir_Filter InputFilter0.dump=false InputFilter0.input_item_type=float @@ -88,9 +91,9 @@ InputFilter0.band2_error=1.0 InputFilter0.filter_type=bandpass InputFilter0.grid_density=16 InputFilter0.sampling_frequency=79500000 -InputFilter0.IF=-14580000 -InputFilter0.decimation_factor=2 -; CHANNEL 1 +InputFilter0.IF=14580000 +InputFilter0.decimation_factor=8 +; RF CHANNEL 1 InputFilter1.implementation=Freq_Xlating_Fir_Filter InputFilter1.dump=false InputFilter1.input_item_type=float @@ -111,9 +114,9 @@ InputFilter1.band2_error=1.0 InputFilter1.filter_type=bandpass InputFilter1.grid_density=16 InputFilter1.sampling_frequency=79500000 -InputFilter1.IF=-14580000 -InputFilter1.decimation_factor=2 -; CHANNEL 2 +InputFilter1.IF=0 +InputFilter1.decimation_factor=8 +; RF CHANNEL 2 InputFilter2.implementation=Freq_Xlating_Fir_Filter InputFilter2.dump=false InputFilter2.input_item_type=float @@ -134,9 +137,9 @@ InputFilter2.band2_error=1.0 InputFilter2.filter_type=bandpass InputFilter2.grid_density=16 InputFilter2.sampling_frequency=79500000 -InputFilter2.IF=-23550000 -InputFilter2.decimation_factor=2 -; CHANNEL 3 +InputFilter2.IF=23550000 +InputFilter2.decimation_factor=8 +; RF CHANNEL 3 InputFilter3.implementation=Freq_Xlating_Fir_Filter InputFilter3.dump=false InputFilter3.input_item_type=float @@ -157,108 +160,49 @@ InputFilter3.band2_error=1.0 InputFilter3.filter_type=bandpass InputFilter3.grid_density=16 InputFilter3.sampling_frequency=79500000 -InputFilter3.IF=-2355000000 -InputFilter3.decimation_factor=2 +InputFilter3.IF=27600000 +InputFilter3.decimation_factor=8 ;######### RESAMPLER CONFIG ############ +; RF CHANNEL 0 Resampler0.implementation=Pass_Through Resampler0.item_type=gr_complex +; RF CHANNEL 1 Resampler1.implementation=Pass_Through Resampler1.item_type=gr_complex +; RF CHANNEL 2 Resampler2.implementation=Pass_Through Resampler2.item_type=gr_complex +; RF CHANNEL 3 Resampler3.implementation=Pass_Through Resampler3.item_type=gr_complex ;######### CHANNELS GLOBAL CONFIG ############ -Channels_1C.count=8 -Channels_1B.count=8 -Channels_5X.count=8 -Channels_L5.count=8 Channels.in_acquisition=1 - -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=2 -Channel9.RF_channel_ID=2 -Channel10.RF_channel_ID=2 -Channel11.RF_channel_ID=2 -Channel12.RF_channel_ID=2 -Channel13.RF_channel_ID=2 -Channel14.RF_channel_ID=2 -Channel15.RF_channel_ID=2 - -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=2 -Channel25.RF_channel_ID=2 -Channel26.RF_channel_ID=2 -Channel27.RF_channel_ID=2 -Channel28.RF_channel_ID=2 -Channel29.RF_channel_ID=2 -Channel30.RF_channel_ID=2 -Channel31.RF_channel_ID=2 - -;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=L5 -;Channel9.signal=L5 -;Channel10.signal=L5 -;Channel11.signal=L5 -;Channel12.signal=L5 -;Channel13.signal=L5 -;Channel14.signal=L5 -;Channel15.signal=L5 -; -;Channel16.signal=1B -;Channel17.signal=1B -;Channel18.signal=1B -;Channel19.signal=1B -;Channel20.signal=1B -;Channel21.signal=1B -;Channel22.signal=1B -;Channel23.signal=1B -; -;Channel24.signal=5X -;Channel25.signal=5X -;Channel26.signal=5X -;Channel27.signal=5X -;Channel28.signal=5X -;Channel29.signal=5X -;Channel30.signal=5X -;Channel31.signal=5X +Channels_1C.count=8 ;# GPS L1 +Channels_L5.count=8 ;# GPS L5 +Channels_2S.count=8 ;# GPS L2C +Channels_1B.count=8 ;# Galileo E1b +Channels_5X.count=8 ;# Galileo E5a +Channels_B1.count=8 ;# Beidou B1I +; RF CHANNEL MAPPING +Channels_1C.RF_channel_ID=0 +Channels_L5.RF_channel_ID=2 +Channels_2S.RF_channel_ID=3 +Channels_1B.RF_channel_ID=0 +Channels_5X.RF_channel_ID=2 +Channels_B1.RF_channel_ID=0 ;######### ACQUISITION CONFIG ############ ;# GPS L1 Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition Acquisition_1C.item_type=gr_complex -Acquisition_L5.threshold=2 +Acquisition_1C.threshold=2 Acquisition_1C.doppler_max=5000 -Acquisition_1C.doppler_step=500 +Acquisition_1C.doppler_step=125 Acquisition_1C.dump=false Acquisition_1C.dump_filename=./acq_dump.dat ;# GPS L5 @@ -268,6 +212,13 @@ Acquisition_L5.threshold=2 Acquisition_L5.doppler_max=5000 Acquisition_L5.doppler_step=125 Acquisition_L5.dump=false +;# GPS L2C +Acquisition_2S.implementation=GPS_L2_M_PCPS_Acquisition +Acquisition_2S.item_type=gr_complex +Acquisition_2S.threshold=2 +Acquisition_2S.doppler_max=5000 +Acquisition_2S.doppler_step=150 +Acquisition_2S.dump=false ;# Galileo E1b Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition Acquisition_1B.item_type=gr_complex @@ -282,6 +233,13 @@ Acquisition_5X.threshold=2 Acquisition_5X.doppler_max=5000 Acquisition_5X.doppler_step=125 Acquisition_5X.dump=false +;# Beidou B1 +Acquisition_B1.implementation=BEIDOU_B1I_PCPS_Acquisition +Acquisition_B1.item_type=gr_complex +Acquisition_B1.threshold=2 +Acquisition_B1.doppler_max=5000 +Acquisition_B1.doppler_step=150 +Acquisition_B1.dump=false ;######### TRACKING CONFIG ############ @@ -289,12 +247,17 @@ Acquisition_5X.dump=false Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking Tracking_1C.item_type=gr_complex Tracking_1C.dump=false -Tracking_1C.dump_filename=./epl_tracking_ch_ +Tracking_1C.dump_mat=false ;# GPS L5 Tracking_L5.implementation=GPS_L5_DLL_PLL_Tracking Tracking_L5.item_type=gr_complex Tracking_L5.dump=false Tracking_L5.dump_mat=false +;# GPS L2C +Tracking_2S.implementation=GPS_L2_M_DLL_PLL_Tracking +Tracking_2S.item_type=gr_complex +Tracking_2S.dump=false +Tracking_2S.dump_mat=false ;# Galileo E1b Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking Tracking_1B.item_type=gr_complex @@ -305,21 +268,38 @@ Tracking_5X.implementation=Galileo_E5a_DLL_PLL_Tracking Tracking_5X.item_type=gr_complex Tracking_5X.dump=false Tracking_5X.dump_mat=false +;# Beidou B1 +Tracking_B1.implementation=BEIDOU_B1I_DLL_PLL_Tracking +Tracking_B1.item_type=gr_complex +Tracking_B1.dump=false +Tracking_B1.dump_mat=false ;######### TELEMETRY DECODER CONFIG ############ +;# GPS L1 TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder TelemetryDecoder_1C.dump=false TelemetryDecoder_1C.dump_mat=false +;# GPS L5 TelemetryDecoder_L5.implementation=GPS_L5_Telemetry_Decoder TelemetryDecoder_L5.dump=false TelemetryDecoder_L5.dump_mat=false +;# GPS L2C +TelemetryDecoder_2S.implementation=GPS_L2C_Telemetry_Decoder +TelemetryDecoder_2S.dump=false +TelemetryDecoder_2S.dump_mat=false +;# Galileo E1b TelemetryDecoder_1B.implementation=Galileo_E1B_Telemetry_Decoder TelemetryDecoder_1B.dump=false TelemetryDecoder_1B.dump_mat=false +;# Galileo E5a TelemetryDecoder_5X.implementation=Galileo_E5a_Telemetry_Decoder TelemetryDecoder_5X.dump=false TelemetryDecoder_5X.dump_mat=false +;# Beidou B1 +TelemetryDecoder_B1.implementation=BEIDOU_B1I_Telemetry_Decoder +TelemetryDecoder_B1.dump=false +TelemetryDecoder_B1.dump_mat=false ;######### OBSERVABLES CONFIG ############ diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc index 64adb286e..c547c8703 100644 --- a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc @@ -28,13 +28,13 @@ using namespace std::string_literals; -MultiChannelTwoBitPackedFileSignalSource::MultiChannelTwoBitPackedFileSignalSource( +NTLabFileSignalSource::NTLabFileSignalSource( const ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams, Concurrent_Queue* queue) - : FileSourceBase(configuration, role, "ntlab_file_signal_source"s, queue, "byte"s), + : FileSourceBase(configuration, role, "NTLab_File_Signal_Source"s, queue, "byte"s), sample_type_(configuration->property(role + ".sample_type", "real"s)) { int default_n_channlels_ = 4; @@ -43,6 +43,10 @@ MultiChannelTwoBitPackedFileSignalSource::MultiChannelTwoBitPackedFileSignalSour { n_channels_ = configuration->property(role + ".RF_channels", default_n_channlels_); } + if ((n_channels_ != 1) && (n_channels_ != 2) && (n_channels_ != 4)) + { + LOG(ERROR) << "Number of channels must be 1, 2 or 4 (got " << n_channels_ << ")"; + } if (in_streams > 0) { @@ -54,7 +58,7 @@ MultiChannelTwoBitPackedFileSignalSource::MultiChannelTwoBitPackedFileSignalSour } } -std::tuple MultiChannelTwoBitPackedFileSignalSource::itemTypeToSize() +std::tuple NTLabFileSignalSource::itemTypeToSize() { auto is_complex_t = false; auto item_size = sizeof(char); // default @@ -80,23 +84,23 @@ std::tuple MultiChannelTwoBitPackedFileSignalSource::itemTypeToSiz return std::make_tuple(item_size, is_complex_t); } -double MultiChannelTwoBitPackedFileSignalSource::packetsPerSample() const +double NTLabFileSignalSource::packetsPerSample() const { return 1.0; } -gnss_shared_ptr MultiChannelTwoBitPackedFileSignalSource::source() const +gnss_shared_ptr NTLabFileSignalSource::source() const { return unpack_samples_; } -void MultiChannelTwoBitPackedFileSignalSource::create_file_source_hook() +void NTLabFileSignalSource::create_file_source_hook() { unpack_samples_ = make_unpack_ntlab_2bit_samples(item_size(), n_channels_); DLOG(INFO) << "unpack_byte_2bit_samples(" << unpack_samples_->unique_id() << ")"; } -void MultiChannelTwoBitPackedFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block) +void NTLabFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block) { top_block->connect(file_source(), 0, unpack_samples_, 0); DLOG(INFO) << "connected file source to samples unpacker"; @@ -108,7 +112,7 @@ void MultiChannelTwoBitPackedFileSignalSource::pre_connect_hook(gr::top_block_sp } } -void MultiChannelTwoBitPackedFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block) +void NTLabFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block) { top_block->disconnect(file_source(), 0, unpack_samples_, 0); DLOG(INFO) << "disconnected file source of samples unpacker"; @@ -120,13 +124,13 @@ void MultiChannelTwoBitPackedFileSignalSource::pre_disconnect_hook(gr::top_block } } -gr::basic_block_sptr MultiChannelTwoBitPackedFileSignalSource::get_left_block() +gr::basic_block_sptr NTLabFileSignalSource::get_left_block() { LOG(WARNING) << "Left block of a signal source should not be retrieved"; return gr::block_sptr(); } -gr::basic_block_sptr MultiChannelTwoBitPackedFileSignalSource::get_right_block() +gr::basic_block_sptr NTLabFileSignalSource::get_right_block() { return valve(); } diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h index aab8c80ff..27607f48e 100644 --- a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.h @@ -40,14 +40,14 @@ class ConfigurationInterface; * \brief Class that reads signals samples from a file * and adapts it to a SignalSourceInterface */ -class MultiChannelTwoBitPackedFileSignalSource : public FileSourceBase +class NTLabFileSignalSource : public FileSourceBase { public: - MultiChannelTwoBitPackedFileSignalSource(const ConfigurationInterface* configuration, + NTLabFileSignalSource(const ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams, Concurrent_Queue* queue); - ~MultiChannelTwoBitPackedFileSignalSource() = default; + ~NTLabFileSignalSource() = default; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc index 77e7d3044..877f659fd 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc @@ -35,8 +35,7 @@ unpack_ntlab_2bit_samples::unpack_ntlab_2bit_samples(size_t item_size, : sync_interpolator("unpack_ntlab_2bit_samples", gr::io_signature::make(1, 1, item_size), gr::io_signature::make(nchannels, nchannels, sizeof(float)), - 1), // we make 4 floats out for every byte in - item_size_(item_size), + SAMPLES_PER_BYTE / nchannels), nchannels_(nchannels) { } @@ -47,41 +46,32 @@ int unpack_ntlab_2bit_samples::work(int noutput_items, gr_vector_void_star &output_items) { auto const *in = reinterpret_cast(input_items[0]); + int const nch = nchannels_; - float *out[4]; - for (int n = 0; n < nchannels_; ++n) + std::vector out(nch); + for (int ch = 0; ch < nch; ++ch) { - out[n] = static_cast(output_items[n]); + out[ch] = static_cast(output_items[ch]); } - const int nbytes = noutput_items; - - for (int i = 0; i < nbytes; ++i) + for (int i = 0; i < noutput_items; ++i) { - // Unpack each of the four 2-bit samples in the byte 'b' into four real-valued outputs. - // - // The NTLAB format encodes samples as sign+magnitude pairs in each byte: - // bits 7-6 = [M0 S0] -> sample 0 - // bits 5-4 = [M1 S1] -> sample 1 - // bits 3-2 = [M2 S2] -> sample 2 - // bits 1-0 = [M3 S3] -> sample 3 - // - // Here we loop over channel index n = 0...3, compute the bit shift to extract - // the two bits for that channel (shift = 6,4,2,0), then: - // - M = magnitude bit (1->|sample|=3, 0->|sample|=1) - // - S = sign bit (1->positive, 0->negative) - // We reconstruct the signed sample value (+/-1 or +/-3) and store it in out[n][i]. - uint8_t b = static_cast(in[i]); - for (int n = 0; n < nchannels_; ++n) + auto b = static_cast(in[i]); + int j = 0; + for (int n = 0; n < SAMPLES_PER_BYTE; ++n) { int shift = 2 * (3 - n); // 6, 4, 2, 0 int M = (b >> (shift + 1)) & 0x1; // magnitude bit int S = (b >> shift) & 0x1; // sign bit int mag = M ? 3 : 1; int val = S ? +mag : -mag; - out[n][i] = static_cast(val); + + out[j][i] = static_cast(val); + + if (++j == nch) // iterate through each channel + j = 0; } } - return nbytes; + return noutput_items; } diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h index 60f252057..ab7d492ff 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h +++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.h @@ -41,6 +41,17 @@ unpack_ntlab_2bit_samples_sptr make_unpack_ntlab_2bit_samples( /*! * \brief This class implements conversion between byte packet multichannel samples * to 2bit samples 1 byte = 4 2bit samples + * + * Unpack each of the four 2-bit samples in the byte 'b' into four real-valued outputs. + * + * The NTLAB format encodes samples as sign+magnitude pairs in each byte: + * bits 7-6 = [M0 S0] -> sample 0 + * bits 5-4 = [M1 S1] -> sample 1 + * bits 3-2 = [M2 S2] -> sample 2 + * bits 1-0 = [M3 S3] -> sample 3 + * + * M = magnitude bit (1->|sample|=3, 0->|sample|=1) + * S = sign bit (1->positive, 0->negative) */ class unpack_ntlab_2bit_samples : public gr::sync_interpolator { @@ -55,12 +66,12 @@ public: gr_vector_void_star &output_items); private: + static constexpr int SAMPLES_PER_BYTE = 4; + friend unpack_ntlab_2bit_samples_sptr make_unpack_ntlab_2bit_samples_sptr( size_t item_size, int nchannels); - std::vector work_buffer_; - size_t item_size_; int nchannels_; }; diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index d28a1f0e8..2fbe8d1c4 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -323,7 +323,7 @@ std::unique_ptr get_signal_source_block( } else if (implementation == "NTLab_File_Signal_Source") { - return std::make_unique(configuration, role, in_streams, out_streams, queue); + return std::make_unique(configuration, role, in_streams, out_streams, queue); } else if (implementation == "Spir_File_Signal_Source") { From 24f0ba702ea65fc17521c6aa8aee83895c2adbdc Mon Sep 17 00:00:00 2001 From: pedromiguelcp Date: Fri, 11 Jul 2025 10:03:13 +0100 Subject: [PATCH 4/6] chore: make clang-tidy happy --- .../gnuradio_blocks/unpack_ntlab_2bit_samples.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc index 877f659fd..e012f4489 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_ntlab_2bit_samples.cc @@ -69,7 +69,9 @@ int unpack_ntlab_2bit_samples::work(int noutput_items, out[j][i] = static_cast(val); if (++j == nch) // iterate through each channel - j = 0; + { + j = 0; + } } } From 5927ca5e479646db463fd94291af0e49c555dc82 Mon Sep 17 00:00:00 2001 From: pedromiguelcp Date: Fri, 11 Jul 2025 12:40:12 +0100 Subject: [PATCH 5/6] fix: correct packetsPerSample to reflect channel count --- .../signal_source/adapters/ntlab_file_signal_source.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc index c547c8703..c36290cb0 100644 --- a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc @@ -38,11 +38,7 @@ NTLabFileSignalSource::NTLabFileSignalSource( sample_type_(configuration->property(role + ".sample_type", "real"s)) { int default_n_channlels_ = 4; - n_channels_ = configuration->property(role + ".total_channels", default_n_channlels_); - if (n_channels_ == 0) - { - n_channels_ = configuration->property(role + ".RF_channels", default_n_channlels_); - } + n_channels_ = configuration->property(role + ".RF_channels", default_n_channlels_); if ((n_channels_ != 1) && (n_channels_ != 2) && (n_channels_ != 4)) { LOG(ERROR) << "Number of channels must be 1, 2 or 4 (got " << n_channels_ << ")"; @@ -86,7 +82,7 @@ std::tuple NTLabFileSignalSource::itemTypeToSize() double NTLabFileSignalSource::packetsPerSample() const { - return 1.0; + return 4 / n_channels_; // sampling instants in one byte depend on channel count } gnss_shared_ptr NTLabFileSignalSource::source() const From b4e71654e4713b8be8885c6a65204efea2f566c6 Mon Sep 17 00:00:00 2001 From: pedromiguelcp Date: Fri, 11 Jul 2025 14:33:20 +0100 Subject: [PATCH 6/6] fix: add default channel count value --- .../signal_source/adapters/ntlab_file_signal_source.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc index c36290cb0..75549d244 100644 --- a/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/ntlab_file_signal_source.cc @@ -41,7 +41,8 @@ NTLabFileSignalSource::NTLabFileSignalSource( n_channels_ = configuration->property(role + ".RF_channels", default_n_channlels_); if ((n_channels_ != 1) && (n_channels_ != 2) && (n_channels_ != 4)) { - LOG(ERROR) << "Number of channels must be 1, 2 or 4 (got " << n_channels_ << ")"; + n_channels_ = 4; + LOG(ERROR) << "Number of channels must be 1, 2 or 4 (got " << n_channels_ << "). Using 4."; } if (in_streams > 0)