diff --git a/AUTHORS b/AUTHORS index 1557425e9..eac16f677 100644 --- a/AUTHORS +++ b/AUTHORS @@ -71,6 +71,7 @@ Marcus Alagar mvala079@gmail.com Contributor Mathieu Favreau favreau.mathieu@hotmail.com Contributor Miguel Gómez López mgomezl@ing.uc3m.es Contributor Minhaj Uddin Ahmad mahmad12@crimson.ua.edu Contributor +Oleksandr Suvorov github@say.od.ua Contributor Pedro Pereira pereirapedrocp@gmail.com Contributor Piyush Gupta piyush04111999@gmail.com Contributor Rodrigo Muñoz rodrigo.munoz@proteinlab.cl Contributor diff --git a/CITATION.cff b/CITATION.cff index 69ed17281..0e45e1ff7 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -137,6 +137,10 @@ authors: email: wsilberm@google.com family-names: Silberman given-names: Will + - alias: MrCry0 + email: github@say.od.ua + family-names: Suvorov + given-names: Oleksandr - email: tonetto.dev@gmail.com family-names: Tonetto given-names: Leonardo diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f122a462..adb7ee30b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,8 @@ option(ENABLE_OSMOSDR "Enable the use of OsmoSDR and other front-ends (RTL-based option(ENABLE_LIMESDR "Enable the use of LimeSDR and Custom LimeSDR as signal source" OFF) +option(ENABLE_BLADERF "Enable the use of bladeRF front-ends as signal source, requires libbladeRF" OFF) + option(ENABLE_POCKETSDR "Enable the use of Pocket SDR FE front-ends as signal source, requires gr-pocketsdr" OFF) option(ENABLE_FMCOMMS2 "Enable the use of FMCOMMS4-EBZ + ZedBoard hardware, requires gr-iio" OFF) @@ -387,6 +389,7 @@ set(GNSSSDR_CLANG_MIN_VERSION "3.4.0") set(GNSSSDR_GCC_MIN_VERSION "4.7.2") set(GNSSSDR_GFLAGS_MIN_VERSION "2.1.2") set(GNSSSDR_GNURADIO_MIN_VERSION "3.7.3") +set(GNSSSDR_LIBBLADERF_MIN_VERSION "2.6.0") set(GNSSSDR_MAKO_MIN_VERSION "0.4.2") set(GNSSSDR_MATIO_MIN_VERSION "1.5.3") set(GNSSSDR_PROTOBUF_MIN_VERSION "3.0.0") @@ -2893,6 +2896,34 @@ endif() +########################################## +# libbladeRF - OPTIONAL +# https://github.com/Nuand/bladeRF +########################################## +find_package(LIBBLADERF) +set_package_properties(LIBBLADERF PROPERTIES + PURPOSE "Used for communication with bladeRF front-ends." + TYPE OPTIONAL +) +if(ENABLE_BLADERF) + if(LIBBLADERF_FOUND) + message(STATUS "The driver for bladeRF will be compiled.") + message(STATUS " You can disable it with 'cmake -DENABLE_BLADERF=OFF ..'") + else() + if(ENABLE_PACKAGING) + message(WARNING "libbladeRF has not been found. Source blocks depending on it will NOT be built.") + else() + message(FATAL_ERROR "libbladeRF required to build gnss-sdr with the optional BLADERF driver") + endif() + set(ENABLE_BLADERF OFF) + endif() +else() + message(STATUS "The (optional) driver for bladeRF is not enabled.") + message(STATUS " Enable it with 'cmake -DENABLE_BLADERF=ON ..' to add support for bladeRF.") +endif() + + + ################################################ # gr-pocketsdr - OPTIONAL # https://github.com/minhaj6/gr-pocketsdr @@ -3371,6 +3402,7 @@ add_subdirectory(utils) 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_LIMESDR ENABLE_LIMESDR "Enables Limesdr_Signal_Source. Requires gr-limesdr.") +add_feature_info(ENABLE_BLADERF ENABLE_BLADERF "Enables Bladerf_Signal_Source. Requires libbladeRF.") add_feature_info(ENABLE_POCKETSDR ENABLE_POCKETSDR "Enables Pocket_SDR_Signal_Source. Requires gr-pocketsdr.") add_feature_info(ENABLE_FMCOMMS2 ENABLE_FMCOMMS2 "Enables Fmcomms2_Signal_Source for FMCOMMS2/3/4 devices. Requires libiio, libad9361-dev, and gr-iio.") add_feature_info(ENABLE_PLUTOSDR ENABLE_PLUTOSDR "Enables Plutosdr_Signal_Source and Ad936x_Custom_Signal_Source for using ADALM-PLUTO boards. Requires libiio, libad9361-dev, and gr-iio.") diff --git a/README.md b/README.md index bc7574c2c..e248a95a0 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ dependencies with: ``` $ sudo apt install build-essential cmake git gnuradio-dev gr-limesdr gr-osmosdr \ - libabsl-dev libad9361-dev libarmadillo-dev libblas-dev \ + libabsl-dev libad9361-dev libarmadillo-dev libbladerf-dev libblas-dev \ libboost-chrono-dev libboost-date-time-dev libboost-dev \ libboost-filesystem-dev libboost-serialization-dev libboost-thread-dev \ libcpu-features-dev libgtest-dev libiio-dev liblapack-dev libmatio-dev \ @@ -183,7 +183,7 @@ On older versions: ``` $ sudo apt install build-essential cmake git gnuradio-dev gr-limesdr gr-osmosdr \ - libad9361-dev libarmadillo-dev libblas-dev \ + libad9361-dev libarmadillo-dev libbladerf-dev libblas-dev \ libboost-chrono-dev libboost-date-time-dev libboost-dev \ libboost-filesystem-dev libboost-serialization-dev libboost-system-dev \ libboost-thread-dev \ @@ -609,6 +609,36 @@ $ sudo cmake --install build (in order to disable the `Osmosdr_Signal_Source` compilation, you can pass `-DENABLE_OSMOSDR=OFF` to cmake and build GNSS-SDR again). +#### Build BladeRF support (OPTIONAL) + +GNSS-SDR can talk to Nuand's [bladeRF](https://www.nuand.com/) front-ends +(bladeRF x40, x115, and bladeRF 2.0 Micro xA4/xA9) directly through +`libbladeRF`, without going through OsmoSDR. This requires `libbladeRF` v2.6.0 +or newer (needed for reliable bladeRF 2.0 Micro support); older packaged +versions (e.g., the one currently in Ubuntu's `universe` repository) are +rejected at `cmake` configuration time with a message asking you to upgrade. +Install the library and headers (e.g., on Debian / Ubuntu, +`sudo apt install libbladerf-dev`, then check its version with +`pkg-config --modversion libbladeRF`; if it is older than 2.6.0, build +`libbladeRF` from source instead -- see +[Nuand's bladeRF repository](https://github.com/Nuand/bladeRF)), then configure +GNSS-SDR to build the `Bladerf_Signal_Source` by: + +``` +$ cmake -S . -B build -DENABLE_BLADERF=ON +$ cmake --build build +$ sudo cmake --install build +``` + +(in order to disable the `Bladerf_Signal_Source` compilation, you can pass +`-DENABLE_BLADERF=OFF` to cmake and build GNSS-SDR again). A sample +configuration file is provided at +[conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf](./conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf). +Note that, unlike the `Osmosdr_Signal_Source` path, `libbladeRF` exposes a +single overall RX gain instead of separate LNA / VGA1 / VGA2 stages, and only +single-channel (SISO) reception is currently supported (bladeRF 2.0 Micro xA9's +second RX channel is not yet exposed). + #### Build FMCOMMS2 based SDR Hardware support (OPTIONAL) Install the [libiio](https://github.com/analogdevicesinc/libiio.git) (>=v0.11), diff --git a/cmake/Modules/FindLIBBLADERF.cmake b/cmake/Modules/FindLIBBLADERF.cmake new file mode 100644 index 000000000..d3948e741 --- /dev/null +++ b/cmake/Modules/FindLIBBLADERF.cmake @@ -0,0 +1,115 @@ +# GNSS-SDR is a Global Navigation Satellite System software-defined receiver. +# This file is part of GNSS-SDR. +# +# SPDX-FileCopyrightText: 2011-2026 C. Fernandez-Prades cfernandez(at)cttc.es +# SPDX-License-Identifier: BSD-3-Clause + +# Tries to find libbladeRF. +# +# Usage of this module as follows: +# +# find_package(LIBBLADERF) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# LIBBLADERF_ROOT Set this variable to the root installation of +# libbladeRF if the module has problems finding +# the proper installation path. +# +# Variables defined by this module: +# +# LIBBLADERF_FOUND System has libbladeRF libs/headers +# LIBBLADERF_LIBRARIES The libbladeRF libraries (bladeRF) +# LIBBLADERF_INCLUDE_DIRS The location of libbladeRF headers +# +# Provides the following imported target: +# Bladerf::bladerf +# + +if(NOT COMMAND feature_summary) + include(FeatureSummary) +endif() + +if(NOT PKG_CONFIG_FOUND) + include(FindPkgConfig) +endif() + +if(NOT DEFINED GNSSSDR_LIB_PATHS) + include(GnsssdrFindPaths) +endif() + +pkg_check_modules(PC_LIBBLADERF libbladeRF) + +if(NOT LIBBLADERF_ROOT) + set(LIBBLADERF_ROOT_USER_DEFINED /usr) +else() + set(LIBBLADERF_ROOT_USER_DEFINED ${LIBBLADERF_ROOT}) +endif() +if(DEFINED ENV{LIBBLADERF_ROOT}) + set(LIBBLADERF_ROOT_USER_DEFINED + ${LIBBLADERF_ROOT_USER_DEFINED} + $ENV{LIBBLADERF_ROOT} + ) +endif() +set(LIBBLADERF_ROOT_USER_DEFINED + ${LIBBLADERF_ROOT_USER_DEFINED} + ${CMAKE_INSTALL_PREFIX} +) + +find_path(LIBBLADERF_INCLUDE_DIRS + NAMES libbladeRF.h + HINTS ${PC_LIBBLADERF_INCLUDEDIR} + PATHS ${LIBBLADERF_ROOT_USER_DEFINED}/include + ${GNSSSDR_INCLUDE_PATHS} +) + +find_library(LIBBLADERF_LIBRARIES + NAMES bladeRF + HINTS ${PC_LIBBLADERF_LIBDIR} + PATHS ${LIBBLADERF_ROOT_USER_DEFINED}/lib + ${LIBBLADERF_ROOT_USER_DEFINED}/lib64 + ${GNSSSDR_LIB_PATHS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LIBBLADERF DEFAULT_MSG LIBBLADERF_LIBRARIES LIBBLADERF_INCLUDE_DIRS) + +if(PC_LIBBLADERF_VERSION) + set(LIBBLADERF_VERSION ${PC_LIBBLADERF_VERSION}) +endif() + +if(LIBBLADERF_FOUND AND LIBBLADERF_VERSION AND DEFINED GNSSSDR_LIBBLADERF_MIN_VERSION) + if(LIBBLADERF_VERSION VERSION_LESS GNSSSDR_LIBBLADERF_MIN_VERSION) + message(STATUS "libbladeRF v${LIBBLADERF_VERSION} was found, but GNSS-SDR requires at least v${GNSSSDR_LIBBLADERF_MIN_VERSION}. Please upgrade libbladeRF: https://github.com/Nuand/bladeRF") + set(LIBBLADERF_FOUND FALSE) + unset(LIBBLADERF_LIBRARIES CACHE) + unset(LIBBLADERF_INCLUDE_DIRS CACHE) + endif() +endif() + +if(LIBBLADERF_FOUND AND LIBBLADERF_VERSION) + set_package_properties(LIBBLADERF PROPERTIES + DESCRIPTION "Nuand bladeRF library (found: v${LIBBLADERF_VERSION})" + ) +else() + set_package_properties(LIBBLADERF PROPERTIES + DESCRIPTION "Nuand bladeRF library" + ) +endif() + +set_package_properties(LIBBLADERF PROPERTIES + URL "https://github.com/Nuand/bladeRF" +) + +if(LIBBLADERF_FOUND AND NOT TARGET Bladerf::bladerf) + add_library(Bladerf::bladerf SHARED IMPORTED) + set_target_properties(Bladerf::bladerf PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${LIBBLADERF_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBBLADERF_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${LIBBLADERF_LIBRARIES}" + ) +endif() + +mark_as_advanced(LIBBLADERF_LIBRARIES LIBBLADERF_INCLUDE_DIRS) diff --git a/conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf b/conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf new file mode 100644 index 000000000..5caf8ba83 --- /dev/null +++ b/conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf @@ -0,0 +1,113 @@ +; 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-2026 (see AUTHORS file for a list of contributors) + +[GNSS-SDR] + +;######### GLOBAL OPTIONS ################## +GNSS-SDR.internal_fs_sps=2000000 + +;######### SIGNAL_SOURCE CONFIG ############ +; Native libbladeRF signal source (requires the -DENABLE_BLADERF=ON building +; flag). Works with bladeRF x40, x115, and bladeRF 2.0 Micro xA4/xA9 (single +; RX channel). Unlike the Osmosdr_Signal_Source path (see +; gnss-sdr_GPS_L1_bladeRF.conf), libbladeRF exposes a single overall RX gain +; instead of separate LNA/VGA1/VGA2 stages. +SignalSource.implementation=Bladerf_Signal_Source +SignalSource.item_type=gr_complex +SignalSource.sampling_frequency=2000000 +SignalSource.freq=1575420000 +SignalSource.bandwidth=2000000 +SignalSource.gain=40 +SignalSource.AGC_enabled=false +; Enable to power an active GNSS antenna from the RX bias tee (bladeRF 2.0 +; Micro only; ignored with a warning on bladeRF1 boards). +SignalSource.rx_bias_tee=false +; Leave empty to open the first bladeRF found, or set a specific unit's +; serial number here. +SignalSource.device_serial= +SignalSource.samples=0 +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=./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 +Acquisition_1C.item_type=gr_complex +Acquisition_1C.coherent_integration_time_ms=1 +Acquisition_1C.threshold=2.5 +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=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 diff --git a/conf/RealTime_input/gnss-sdr_multichannel_GPS_Galileo_Beidou_Glonass_L1_bladeRF_realtime.conf b/conf/RealTime_input/gnss-sdr_multichannel_GPS_Galileo_Beidou_Glonass_L1_bladeRF_realtime.conf new file mode 100644 index 000000000..b24c517d6 --- /dev/null +++ b/conf/RealTime_input/gnss-sdr_multichannel_GPS_Galileo_Beidou_Glonass_L1_bladeRF_realtime.conf @@ -0,0 +1,254 @@ +; 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-2026 (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=8000000 +GNSS-SDR.Beidou_banned_prns=56,57,58 + + +;######### SIGNAL_SOURCE CONFIG ############ +; Native libbladeRF signal source (requires the -DENABLE_BLADERF=ON building +; flag and libbladeRF v2.6.0+; see gnss-sdr_GPS_L1_bladeRF_native.conf). +; One wideband capture centered between the BeiDou B1I band (1561.098 MHz) +; and the GLONASS L1 band (~1602 MHz) is split, below, into three sub-bands +; by three parallel Freq_Xlating_Fir_Filter chains tapping the same physical +; bladeRF stream (SignalSource.RF_channels=3): BeiDou B1I, GPS L1 C/A + +; Galileo E1 (co-located at 1575.42 MHz), and GLONASS L1 C/A. +; Requires a bladeRF 2.0 Micro (AD9361-based); 56 Msps / 56 MHz bandwidth is +; at its maximum supported bandwidth. +SignalSource.implementation=Bladerf_Signal_Source +SignalSource.item_type=gr_complex +SignalSource.sampling_frequency=56000000 +SignalSource.freq=1584000000 +SignalSource.bandwidth=56000000 +SignalSource.gain=40 +SignalSource.AGC_enabled=false +SignalSource.rx_bias_tee=false +SignalSource.device_serial= +SignalSource.samples=0 +SignalSource.RF_channels=3 +SignalSource.dump=false + + + +;######### SIGNAL_CONDITIONER CONFIG ############ +SignalConditioner0.implementation=Signal_Conditioner +SignalConditioner1.implementation=Signal_Conditioner +SignalConditioner2.implementation=Signal_Conditioner + +;######### DATA_TYPE_ADAPTER CONFIG ############ +DataTypeAdapter0.implementation=Pass_Through +DataTypeAdapter1.implementation=Pass_Through +DataTypeAdapter2.implementation=Pass_Through + +;######### INPUT_FILTER CONFIG ############ +; Sub-band 0: BeiDou B1I (1561.098 MHz), RF_channel_ID=0 +InputFilter0.implementation=Freq_Xlating_Fir_Filter +InputFilter0.decimation_factor=7 +InputFilter0.input_item_type=gr_complex +InputFilter0.output_item_type=gr_complex +InputFilter0.taps_item_type=float +InputFilter0.filter_type=lowpass +InputFilter0.bw=7000000 +InputFilter0.tw=500000 +InputFilter0.IF=-22902000 +InputFilter0.sampling_frequency=56000000 +InputFilter0.dump=false +InputFilter0.dump_filename=./input_filter.dat + +;######### INPUT_FILTER CONFIG ############ +; Sub-band 1: GPS L1 C/A + Galileo E1 (1575.42 MHz), RF_channel_ID=1 +InputFilter1.implementation=Freq_Xlating_Fir_Filter +InputFilter1.decimation_factor=7 +InputFilter1.input_item_type=gr_complex +InputFilter1.output_item_type=gr_complex +InputFilter1.taps_item_type=float +InputFilter1.filter_type=lowpass +InputFilter1.bw=7000000 +InputFilter1.tw=500000 +InputFilter1.IF=-8580000 +InputFilter1.sampling_frequency=56000000 +InputFilter1.dump=false +InputFilter1.dump_filename=./input_filter.dat + +;######### INPUT_FILTER CONFIG ############ +; Sub-band 2: GLONASS L1 C/A (~1602 MHz, FDMA), RF_channel_ID=2 +InputFilter2.implementation=Freq_Xlating_Fir_Filter +InputFilter2.decimation_factor=7 +InputFilter2.input_item_type=gr_complex +InputFilter2.output_item_type=gr_complex +InputFilter2.taps_item_type=float +InputFilter2.filter_type=lowpass +InputFilter2.bw=7000000 +InputFilter2.tw=500000 +InputFilter2.IF=18000000 +InputFilter2.sampling_frequency=56000000 +InputFilter2.dump=false +InputFilter2.dump_filename=./input_filter.dat + +;######### RESAMPLER CONFIG ############ +Resampler0.implementation=Pass_Through +Resampler1.implementation=Pass_Through +Resampler2.implementation=Pass_Through + +;######### CHANNELS GLOBAL CONFIG ############ +Channels_1B.count=10 +Channels_1C.count=10 +Channels_B1.count=14 +Channels_1G.count=8 + +Channels_1B.RF_channel_ID=1 +Channels_1C.RF_channel_ID=1 +Channels_B1.RF_channel_ID=0 +Channels_1G.RF_channel_ID=2 + + +Channels.in_acquisition=2 + +;######### ACQUISITION BEIDOU CONFIG ############ +Acquisition_B1.implementation=BEIDOU_B1I_PCPS_Acquisition +Acquisition_B1.item_type=gr_complex +Acquisition_B1.coherent_integration_time_ms=2 +Acquisition_B1.pfa=0.000002 +Acquisition_B1.doppler_max=6000 +Acquisition_B1.doppler_step=100 +Acquisition_B1.dump=false +Acquisition_B1.dump_filename=./bds_acq +Acquisition_B1.bit_transition_flag=false + +;######### ACQUISITION GPS CONFIG ############ +Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition +Acquisition_1C.item_type=gr_complex +Acquisition_1C.coherent_integration_time_ms=1 +Acquisition_1C.pfa=0.015 +Acquisition_1C.doppler_max=6000 +Acquisition_1C.doppler_step=200 +Acquisition_1C.max_dwells=4 +Acquisition_1C.dump=false +Acquisition_1C.dump_filename=./acq_dump.dat + +;######### ACQUISITION GALILEO CONFIG ############ +Acquisition_1B.implementation=Galileo_E1_PCPS_Ambiguous_Acquisition +Acquisition_1B.coherent_integration_time_ms=2 +Acquisition_1B.pfa=0.025 +Acquisition_1B.doppler_max=6000 +Acquisition_1B.doppler_step=200 +Acquisition_1B.max_dwells=4 +Acquisition_1B.cboc=true +Acquisition_1B.dump=false +Acquisition_1B.dump_filename=./acq_dump.dat + +;######### ACQUISITION GLONASS CONFIG ############ +Acquisition_1G.implementation=GLONASS_L1_CA_PCPS_Acquisition +Acquisition_1G.item_type=gr_complex +Acquisition_1G.coherent_integration_time_ms=1 +Acquisition_1G.max_dwells=4 +Acquisition_1G.pfa=0.02 +Acquisition_1G.doppler_max=6000 +Acquisition_1G.doppler_step=100 +Acquisition_1G.dump=false +Acquisition_1G.dump_filename=./G1_acq + + + +;######### TRACKING BEIDOU CONFIG ############ +Tracking_B1.implementation=BEIDOU_B1I_DLL_PLL_Tracking +Tracking_B1.item_type=gr_complex +Tracking_B1.extend_correlation_symbols=10 +Tracking_B1.pll_bw_hz=50.0 +Tracking_B1.dll_bw_hz=2.00 +Tracking_B1.pll_bw_narrow_hz=15.0 +Tracking_B1.dll_bw_narrow_hz=1.50 +Tracking_B1.dump=false +Tracking_B1.dump_filename=./epl_tracking_ch_ + +;######### TRACKING GPS CONFIG ############ +Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking +Tracking_1C.item_type=gr_complex +Tracking_1C.extend_correlation_symbols=10 +Tracking_1C.early_late_space_chips=0.5 +Tracking_1C.early_late_space_narrow_chips=0.15 +Tracking_1C.pll_bw_hz=30.0 +Tracking_1C.dll_bw_hz=2.0 +Tracking_1C.pll_bw_narrow_hz=10.0 +Tracking_1C.dll_bw_narrow_hz=1.50 +Tracking_1C.fll_bw_hz=10 +Tracking_1C.enable_fll_pull_in=true +Tracking_1C.enable_fll_steady_state=false +Tracking_1C.dump=false +Tracking_1C.dump_filename=tracking_ch_ + +;######### TRACKING GALILEO CONFIG ############ +Tracking_1B.implementation=Galileo_E1_DLL_PLL_VEML_Tracking +Tracking_1B.extend_correlation_symbols=4 +Tracking_1B.item_type=gr_complex +Tracking_1B.pll_bw_hz=30.0 +Tracking_1B.dll_bw_hz=2.0 +Tracking_1B.pll_bw_narrow_hz=20.0 +Tracking_1B.dll_bw_narrow_hz=1.50 +Tracking_1B.track_pilot=true +Tracking_1B.enable_fll_pull_in=true +Tracking_1B.enable_fll_steady_state=false +Tracking_1B.fll_bw_hz=20 + +;######### TRACKING GLONASS CONFIG ############ +Tracking_1G.implementation=GLONASS_L1_CA_DLL_PLL_Tracking +Tracking_1G.item_type=gr_complex +Tracking_1G.pll_bw_hz=40 +Tracking_1G.dll_bw_hz=2.5 +Tracking_1G.extend_correlation_ms=1 +Tracking_1G.pll_bw_narrow_hz=20 +Tracking_1G.dll_bw_narrow_hz=1.5 +Tracking_1G.dump=false +Tracking_1G.dump_filename=./epl_tracking_ch_ + + + +;######### TELEMETRY DECODER BEIDOU CONFIG ############ +TelemetryDecoder_B1.implementation=BEIDOU_B1I_Telemetry_Decoder +TelemetryDecoder_B1.dump=false + +;######### TELEMETRY DECODER GPS 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 + +;######### TELEMETRY DECODER GLONASS CONFIG ############ +TelemetryDecoder_1G.implementation=GLONASS_L1_CA_Telemetry_Decoder +TelemetryDecoder_1G.dump=false + + +;######### OBSERVABLES CONFIG ############ +Observables.implementation=Hybrid_Observables +Observables.dump=false +Observables.dump_filename=./observables.dat + +;######### PVT CONFIG ############ +PVT.implementation=RTKLIB_PVT +PVT.threshold_reject_GDOP=100 +PVT.elevation_mask=3 +PVT.raim_fde=1 +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.enable_rx_clock_correction=true +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 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b80e8561f..19b8d9554 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -220,6 +220,15 @@ All notable changes to GNSS-SDR will be documented in this file. the RTKLIB navigation structure, and adds `qzss_utc_model.xml`, `qzss_iono.xml`, `qzss_cnav_utc_model.xml`, and `qzss_cnav_iono.xml` to the XML storage output. +- Added a `Bladerf_Signal_Source` for interoperability with Nuand's bladeRF + front-ends (bladeRF x40, x115, and bladeRF 2.0 Micro xA4/xA9), streaming RX + samples directly through `libbladeRF` (requires the `-DENABLE_BLADERF=ON` + building flag) instead of going through `gr-osmosdr`. Supports single-channel + (SISO) reception, an optional RX bias tee for powering an active antenna on + the 2.0 Micro, and exposes a single overall RX gain (unlike the `if_gain` / + `rf_gain` split used by `Osmosdr_Signal_Source`). A sample configuration file + is provided at `conf/RealTime_input/gnss-sdr_GPS_L1_bladeRF_native.conf`. + Contributed by @MrCry0. - Added a new Signal Source implementation `Pocket_SDR_Signal_Source`, which supports [Pocket SDR FE](https://www.datagnss.com/products/pocketsdr-gnss-receiver) diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt index bbabfbe75..e9dd7b91f 100644 --- a/src/algorithms/signal_source/adapters/CMakeLists.txt +++ b/src/algorithms/signal_source/adapters/CMakeLists.txt @@ -101,6 +101,13 @@ if(ENABLE_LIMESDR) endif() endif() +if(ENABLE_BLADERF) + if(LIBBLADERF_FOUND) + list(APPEND OPT_DRIVER_SOURCES bladerf_signal_source.cc) + list(APPEND OPT_DRIVER_HEADERS bladerf_signal_source.h) + endif() +endif() + if(ENABLE_POCKETSDR) if(GRPOCKETSDR_FOUND) list(APPEND OPT_DRIVER_SOURCES pocket_sdr_signal_source.cc) diff --git a/src/algorithms/signal_source/adapters/bladerf_signal_source.cc b/src/algorithms/signal_source/adapters/bladerf_signal_source.cc new file mode 100644 index 000000000..753b59aa9 --- /dev/null +++ b/src/algorithms/signal_source/adapters/bladerf_signal_source.cc @@ -0,0 +1,161 @@ +/*! + * \file bladerf_signal_source.cc + * \brief Signal source for the Nuand bladeRF family of front-ends (bladeRF + * x40, x115, and bladeRF 2.0 Micro xA4/xA9), using libbladeRF directly. + * \author Oleksandr Suvorov, 2026. + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "bladerf_signal_source.h" +#include "configuration_interface.h" +#include "gnss_frequencies.h" +#include "gnss_sdr_string_literals.h" +#include "gnss_sdr_valve.h" +#include +#include + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +using namespace std::string_literals; + +BladerfSignalSource::BladerfSignalSource(const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_stream, + unsigned int out_stream, + Concurrent_Queue* queue) + : SignalSourceBase(configuration, role, "Bladerf_Signal_Source"s), + item_type_(configuration->property(role + ".item_type", std::string("gr_complex"))), + dump_filename_(configuration->property(role + ".dump_filename", std::string("./data/signal_source.dat"))), + bladerf_args_(configuration->property(role + ".bladerf_args", std::string())), + device_serial_(configuration->property(role + ".device_serial", std::string())), + sample_rate_(configuration->property(role + ".sampling_frequency", 2.0e6)), + freq_(configuration->property(role + ".freq", FREQ1)), + bandwidth_(configuration->property(role + ".bandwidth", sample_rate_)), + gain_(configuration->property(role + ".gain", 40.0)), + samples_(configuration->property(role + ".samples", int64_t(0))), + in_stream_(in_stream), + out_stream_(out_stream), + num_buffers_(configuration->property(role + ".num_buffers", 32u)), + buffer_size_(configuration->property(role + ".buffer_size", 32768u)), + num_transfers_(configuration->property(role + ".num_transfers", 16u)), + stream_timeout_ms_(configuration->property(role + ".stream_timeout_ms", 3000u)), + agc_enabled_(configuration->property(role + ".AGC_enabled", false)), + rx_bias_tee_(configuration->property(role + ".rx_bias_tee", false)), + dump_(configuration->property(role + ".dump", false)) +{ + if (item_type_ != "gr_complex") + { + std::string error_message = "bladeRF: item_type '" + item_type_ + "' not supported, only 'gr_complex' is supported."; + LOG(ERROR) << error_message; + throw std::invalid_argument(error_message); + } + item_size_ = sizeof(gr_complex); + + try + { + bladerf_source_ = bladerf_make_source_sptr(bladerf_args_, device_serial_, + sample_rate_, freq_, bandwidth_, gain_, agc_enabled_, rx_bias_tee_, + num_buffers_, buffer_size_, num_transfers_, stream_timeout_ms_); + } + catch (const std::exception& e) + { + LOG(WARNING) << "bladeRF exception: " << e.what(); + throw std::invalid_argument("Wrong bladeRF arguments"); + } + + if (samples_ != 0) + { + DLOG(INFO) << "Send STOP signal after " << samples_ << " samples"; + valve_ = gnss_sdr_make_valve(item_size_, samples_, queue); + DLOG(INFO) << "valve(" << valve_->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()); + DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; + } + + if (in_stream_ > 0) + { + LOG(ERROR) << "A signal source does not have an input stream"; + } + if (out_stream_ > 1) + { + LOG(ERROR) << "This implementation only supports one output stream"; + } +} + + +void BladerfSignalSource::connect(gr::top_block_sptr top_block) +{ + if (samples_ != 0) + { + top_block->connect(bladerf_source_, 0, valve_, 0); + DLOG(INFO) << "connected bladeRF source to valve"; + if (dump_) + { + top_block->connect(valve_, 0, file_sink_, 0); + DLOG(INFO) << "connected valve to file sink"; + } + } + else + { + if (dump_) + { + top_block->connect(bladerf_source_, 0, file_sink_, 0); + DLOG(INFO) << "connected bladeRF source to file sink"; + } + } +} + + +void BladerfSignalSource::disconnect(gr::top_block_sptr top_block) +{ + if (samples_ != 0) + { + top_block->disconnect(bladerf_source_, 0, valve_, 0); + if (dump_) + { + top_block->disconnect(valve_, 0, file_sink_, 0); + } + } + else + { + if (dump_) + { + top_block->disconnect(bladerf_source_, 0, file_sink_, 0); + } + } +} + + +gr::basic_block_sptr BladerfSignalSource::get_left_block() +{ + LOG(WARNING) << "Trying to get signal source left block."; + return {}; +} + + +gr::basic_block_sptr BladerfSignalSource::get_right_block() +{ + if (samples_ != 0) + { + return valve_; + } + return bladerf_source_; +} diff --git a/src/algorithms/signal_source/adapters/bladerf_signal_source.h b/src/algorithms/signal_source/adapters/bladerf_signal_source.h new file mode 100644 index 000000000..f3204c84c --- /dev/null +++ b/src/algorithms/signal_source/adapters/bladerf_signal_source.h @@ -0,0 +1,93 @@ +/*! + * \file bladerf_signal_source.h + * \brief Signal source for the Nuand bladeRF family of front-ends (bladeRF + * x40, x115, and bladeRF 2.0 Micro xA4/xA9), using libbladeRF directly. + * \author Oleksandr Suvorov, 2026. + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_BLADERF_SIGNAL_SOURCE_H +#define GNSS_SDR_BLADERF_SIGNAL_SOURCE_H + +#include "bladerf_source.h" +#include "concurrent_queue.h" +#include "signal_source_base.h" +#include +#include +#include +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_adapters + * \{ */ + + +class ConfigurationInterface; + +/*! + * \brief This class instantiates the bladeRF signal source, streaming RX + * samples directly through libbladeRF (no gr-osmosdr or other intermediate + * driver required). + */ +class BladerfSignalSource : public SignalSourceBase +{ +public: + BladerfSignalSource(const ConfigurationInterface* configuration, + const std::string& role, unsigned int in_stream, + unsigned int out_stream, Concurrent_Queue* queue); + + ~BladerfSignalSource() = default; + + 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: + bladerf_source_sptr bladerf_source_; + gnss_shared_ptr valve_; + gr::blocks::file_sink::sptr file_sink_; + + std::string item_type_; + std::string dump_filename_; + std::string bladerf_args_; + std::string device_serial_; + + double sample_rate_; + double freq_; + double bandwidth_; + double gain_; + size_t item_size_; + int64_t samples_; + + unsigned int in_stream_; + unsigned int out_stream_; + unsigned int num_buffers_; + unsigned int buffer_size_; + unsigned int num_transfers_; + unsigned int stream_timeout_ms_; + + bool agc_enabled_; + bool rx_bias_tee_; + bool dump_; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_BLADERF_SIGNAL_SOURCE_H diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt index 337706441..38ee42b95 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt +++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt @@ -20,6 +20,11 @@ if(ENABLE_ION) set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} ion_gsms.h) endif() +if(ENABLE_BLADERF AND LIBBLADERF_FOUND) + set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} bladerf_source.cc) + set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} bladerf_source.h) +endif() + set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES fifo_reader.cc unpack_byte_2bit_samples.cc @@ -103,6 +108,13 @@ if(ENABLE_RAW_UDP AND PCAP_FOUND) ) endif() +if(ENABLE_BLADERF AND LIBBLADERF_FOUND) + target_link_libraries(signal_source_gr_blocks + PRIVATE + Bladerf::bladerf + ) +endif() + # Fix for Boost Asio < 1.70 if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if((CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND (Boost_VERSION_STRING VERSION_LESS 1.70.0)) diff --git a/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.cc b/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.cc new file mode 100644 index 000000000..61dd84f9a --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.cc @@ -0,0 +1,284 @@ +/*! + * \file bladerf_source.cc + * \brief GNU Radio source block for Nuand's bladeRF front-ends (bladeRF x40, + * x115, and bladeRF 2.0 Micro xA4/xA9), implemented directly against + * libbladeRF. + * \author Oleksandr Suvorov, 2026. + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "bladerf_source.h" +#include +// bladeRF2.h relies on macros defined by libbladeRF.h but does not include +// it itself, so this order must be preserved (protected against +// clang-format's alphabetical include sorting). +// clang-format off +#include +#include +// clang-format on +#include +#include +#include +#include + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +namespace +{ +constexpr bladerf_channel kRxChannel = BLADERF_CHANNEL_RX(0); + +std::string make_device_identifier(const std::string &bladerf_args, const std::string &device_serial) +{ + if (!bladerf_args.empty()) + { + return bladerf_args; + } + if (!device_serial.empty()) + { + return "*:serial=" + device_serial; + } + return {}; +} +} // namespace + + +void bladerf_source::bladerf_deleter::operator()(struct bladerf *dev) const +{ + if (dev != nullptr) + { + bladerf_close(dev); + } +} + + +bladerf_source_sptr bladerf_make_source_sptr( + const std::string &bladerf_args, + const std::string &device_serial, + double sample_rate, + double freq, + double bandwidth, + double gain, + bool agc_enabled, + bool rx_bias_tee, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout_ms) +{ + return bladerf_source_sptr(new bladerf_source(bladerf_args, device_serial, + sample_rate, freq, bandwidth, gain, agc_enabled, rx_bias_tee, + num_buffers, buffer_size, num_transfers, stream_timeout_ms)); +} + + +bladerf_source::bladerf_source( + const std::string &bladerf_args, + const std::string &device_serial, + double sample_rate, + double freq, + double bandwidth, + double gain, + bool agc_enabled, + bool rx_bias_tee, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout_ms) + : gr::sync_block("bladerf_source", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(1, 1, sizeof(gr_complex))), + buffer_size_(buffer_size), + stream_timeout_ms_(stream_timeout_ms), + streaming_(false) +{ + const std::string identifier = make_device_identifier(bladerf_args, device_serial); + + struct bladerf *raw_dev = nullptr; + int status = bladerf_open(&raw_dev, identifier.empty() ? nullptr : identifier.c_str()); + if (status != 0) + { + std::ostringstream oss; + oss << "bladeRF: unable to open device '" << identifier << "': " << bladerf_strerror(status); + LOG(ERROR) << oss.str(); + throw std::runtime_error(oss.str()); + } + dev_.reset(raw_dev); + + std::cout << "bladeRF: opened board '" << bladerf_get_board_name(dev_.get()) << "'\n"; + LOG(INFO) << "bladeRF: opened board '" << bladerf_get_board_name(dev_.get()) << "'"; + + bladerf_sample_rate actual_sample_rate = 0; + status = bladerf_set_sample_rate(dev_.get(), kRxChannel, static_cast(sample_rate), &actual_sample_rate); + if (status != 0) + { + std::ostringstream oss; + oss << "bladeRF: unable to set sample rate: " << bladerf_strerror(status); + LOG(ERROR) << oss.str(); + throw std::runtime_error(oss.str()); + } + std::cout << "bladeRF: actual RX sample rate: " << actual_sample_rate << " [SPS]\n"; + + bladerf_bandwidth actual_bandwidth = 0; + status = bladerf_set_bandwidth(dev_.get(), kRxChannel, static_cast(bandwidth), &actual_bandwidth); + if (status != 0) + { + std::ostringstream oss; + oss << "bladeRF: unable to set bandwidth: " << bladerf_strerror(status); + LOG(ERROR) << oss.str(); + throw std::runtime_error(oss.str()); + } + std::cout << "bladeRF: actual RX bandwidth: " << actual_bandwidth << " [Hz]\n"; + + status = bladerf_set_frequency(dev_.get(), kRxChannel, static_cast(freq)); + if (status != 0) + { + std::ostringstream oss; + oss << "bladeRF: unable to set frequency: " << bladerf_strerror(status); + LOG(ERROR) << oss.str(); + throw std::runtime_error(oss.str()); + } + std::cout << "bladeRF: RX frequency set to " << freq << " [Hz]\n"; + + status = bladerf_set_gain_mode(dev_.get(), kRxChannel, agc_enabled ? BLADERF_GAIN_DEFAULT : BLADERF_GAIN_MGC); + if (status != 0) + { + LOG(WARNING) << "bladeRF: unable to set gain mode: " << bladerf_strerror(status); + } + + if (!agc_enabled) + { + status = bladerf_set_gain(dev_.get(), kRxChannel, static_cast(gain)); + if (status != 0) + { + LOG(WARNING) << "bladeRF: unable to set gain: " << bladerf_strerror(status); + } + } + + if (rx_bias_tee) + { + status = bladerf_set_bias_tee(dev_.get(), kRxChannel, true); + if (status != 0) + { + LOG(WARNING) << "bladeRF: unable to enable RX bias tee (not supported on this board?): " << bladerf_strerror(status); + } + } + + status = bladerf_sync_config(dev_.get(), BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META, + num_buffers, buffer_size_, num_transfers, stream_timeout_ms_); + if (status != 0) + { + std::ostringstream oss; + oss << "bladeRF: unable to configure synchronous RX stream: " << bladerf_strerror(status); + LOG(ERROR) << oss.str(); + throw std::runtime_error(oss.str()); + } + + raw_buffer_.resize(static_cast(buffer_size_) * 2); + + // Hint the GNU Radio scheduler to negotiate a large output buffer for + // this block. Without this, the scheduler was observed requesting + // noutput_items as small as ~4096 per work() call regardless of how + // large a batch this block was willing to hand over, forcing + // bladerf_sync_rx() to be invoked far more often than the underlying + // stream's buffering (num_buffers * buffer_size_) could absorb any + // scheduling delay against, causing frequent RX overruns (confirmed via + // BLADERF_META_STATUS_OVERRUN once that reporting was wired up). + this->set_min_output_buffer(2097152); +} + + +bool bladerf_source::start() +{ + int status = bladerf_enable_module(dev_.get(), kRxChannel, true); + if (status != 0) + { + LOG(ERROR) << "bladeRF: unable to enable RX module: " << bladerf_strerror(status); + return false; + } + streaming_ = true; + return true; +} + + +bool bladerf_source::stop() +{ + if (streaming_) + { + int status = bladerf_enable_module(dev_.get(), kRxChannel, false); + if (status != 0) + { + LOG(WARNING) << "bladeRF: unable to disable RX module: " << bladerf_strerror(status); + } + streaming_ = false; + } + return true; +} + + +int bladerf_source::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + (void)input_items; + + // Honor whatever batch size GNU Radio's scheduler is ready to accept, + // instead of artificially capping every call to buffer_size_ (the + // underlying stream's per-transfer size, not a limit on bladerf_sync_rx's + // own request size). Capping here forced far more, smaller round trips + // through bladerf_sync_rx than necessary, which made the source + // sensitive to ordinary OS scheduling jitter on any request that missed + // its real-time window. + const auto num_samples = static_cast(noutput_items); + if (raw_buffer_.size() < static_cast(num_samples) * 2) + { + raw_buffer_.resize(static_cast(num_samples) * 2); + } + + struct bladerf_metadata meta; + std::memset(&meta, 0, sizeof(meta)); + meta.flags = BLADERF_META_FLAG_RX_NOW; + + const int status = bladerf_sync_rx(dev_.get(), raw_buffer_.data(), num_samples, &meta, stream_timeout_ms_); + if (status == BLADERF_ERR_TIMEOUT) + { + return 0; + } + if (status != 0) + { + LOG(ERROR) << "bladeRF: sync_rx failed: " << bladerf_strerror(status); + return -1; + } + if ((meta.status & BLADERF_META_STATUS_OVERRUN) != 0) + { + LOG(WARNING) << "bladeRF: RX overrun detected, samples were dropped (requested " + << num_samples << ", got " << meta.actual_count << ")"; + } + + // meta.actual_count reflects how many samples in raw_buffer_ are + // actually valid for this call; on a discontinuity it can be less than + // num_samples, and reading past it is undefined per libbladeRF's docs. + const unsigned int valid_samples = std::min(num_samples, meta.actual_count); + + auto *out = reinterpret_cast(output_items[0]); + for (unsigned int i = 0; i < valid_samples; i++) + { + const float i_val = static_cast(raw_buffer_[2 * i]) / 2048.0F; + const float q_val = static_cast(raw_buffer_[(2 * i) + 1]) / 2048.0F; + out[i] = gr_complex(i_val, q_val); + } + + return static_cast(valid_samples); +} diff --git a/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.h b/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.h new file mode 100644 index 000000000..84e26094a --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/bladerf_source.h @@ -0,0 +1,115 @@ +/*! + * \file bladerf_source.h + * \brief GNU Radio source block for Nuand's bladeRF front-ends (bladeRF x40, + * x115, and bladeRF 2.0 Micro xA4/xA9), implemented directly against + * libbladeRF. + * \author Oleksandr Suvorov, 2026. + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_BLADERF_SOURCE_H +#define GNSS_SDR_BLADERF_SOURCE_H + +#include "gnss_block_interface.h" +#include +#include +#include +#include +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_gnuradio_blocks + * \{ */ + + +class bladerf_source; + +using bladerf_source_sptr = gnss_shared_ptr; + +bladerf_source_sptr bladerf_make_source_sptr( + const std::string &bladerf_args, + const std::string &device_serial, + double sample_rate, + double freq, + double bandwidth, + double gain, + bool agc_enabled, + bool rx_bias_tee, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout_ms); + +/*! + * \brief This class implements a GNU Radio source block that streams RX + * samples from a bladeRF device (single RX channel) using libbladeRF's + * synchronous streaming API, converting the device's native + * BLADERF_FORMAT_SC16_Q11 samples to gr_complex. + */ +class bladerf_source : public gr::sync_block +{ +public: + ~bladerf_source() override = default; + + bool start() override; + bool stop() override; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) override; + +private: + friend bladerf_source_sptr bladerf_make_source_sptr( + const std::string &bladerf_args, + const std::string &device_serial, + double sample_rate, + double freq, + double bandwidth, + double gain, + bool agc_enabled, + bool rx_bias_tee, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout_ms); + + bladerf_source( + const std::string &bladerf_args, + const std::string &device_serial, + double sample_rate, + double freq, + double bandwidth, + double gain, + bool agc_enabled, + bool rx_bias_tee, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout_ms); + + struct bladerf_deleter + { + void operator()(struct bladerf *dev) const; + }; + + std::unique_ptr dev_; + std::vector raw_buffer_; + unsigned int buffer_size_; + unsigned int stream_timeout_ms_; + bool streaming_; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_BLADERF_SOURCE_H diff --git a/src/core/receiver/CMakeLists.txt b/src/core/receiver/CMakeLists.txt index e2c540155..0ff7416fa 100644 --- a/src/core/receiver/CMakeLists.txt +++ b/src/core/receiver/CMakeLists.txt @@ -124,6 +124,10 @@ if(ENABLE_LIMESDR) target_compile_definitions(core_receiver PRIVATE -DLIMESDR_DRIVER=1) endif() +if(ENABLE_BLADERF) + target_compile_definitions(core_receiver PRIVATE -DBLADERF_DRIVER=1) +endif() + if(ENABLE_POCKETSDR) target_compile_definitions(core_receiver PRIVATE -DPOCKETSDR_DRIVER=1) endif() diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index 0a5f80e91..08e4e25d4 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -146,6 +146,10 @@ #include "limesdr_signal_source.h" #endif +#if BLADERF_DRIVER +#include "bladerf_signal_source.h" +#endif + #if POCKETSDR_DRIVER #include "pocket_sdr_signal_source.h" #endif @@ -348,6 +352,12 @@ std::unique_ptr get_signal_source_block( return std::make_unique(configuration, role, in_streams, out_streams, queue); } #endif +#if BLADERF_DRIVER + else if (implementation == "Bladerf_Signal_Source") + { + return std::make_unique(configuration, role, in_streams, out_streams, queue); + } +#endif #if POCKETSDR_DRIVER else if (implementation == "Pocket_SDR_Signal_Source") {