From c97e12dbe0e368c4910cbb431c173fa398a9262b Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Fri, 20 May 2022 10:40:30 +0200
Subject: [PATCH 01/31] Adding 4 bits complex samples signal source

---
 .../signal_source/adapters/CMakeLists.txt     |   2 +
 .../four_bit_cpx_file_signal_source.cc        | 102 ++++++++++++++++++
 .../four_bit_cpx_file_signal_source.h         |  71 ++++++++++++
 .../unpack_byte_4bit_samples.cc               |  16 +--
 .../unpack_byte_4bit_samples.h                |   4 +-
 src/core/receiver/gnss_block_factory.cc       |   7 ++
 6 files changed, 192 insertions(+), 10 deletions(-)
 create mode 100644 src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
 create mode 100644 src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h

diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt
index 7a4c6e065..32a9c6bc9 100644
--- a/src/algorithms/signal_source/adapters/CMakeLists.txt
+++ b/src/algorithms/signal_source/adapters/CMakeLists.txt
@@ -99,6 +99,7 @@ set(SIGNAL_SOURCE_ADAPTER_SOURCES
     labsat_signal_source.cc
     two_bit_cpx_file_signal_source.cc
     two_bit_packed_file_signal_source.cc
+    four_bit_cpx_file_signal_source.cc
     file_timestamp_signal_source.cc
     ${OPT_DRIVER_SOURCES}
 )
@@ -117,6 +118,7 @@ set(SIGNAL_SOURCE_ADAPTER_HEADERS
     labsat_signal_source.h
     two_bit_cpx_file_signal_source.h
     two_bit_packed_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/four_bit_cpx_file_signal_source.cc b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
new file mode 100644
index 000000000..0fc521fe9
--- /dev/null
+++ b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
@@ -0,0 +1,102 @@
+/*!
+ * \file four_bit_cpx_file_signal_source.cc
+ * \brief Implementation of a class that reads signals samples from a 2 bit complex sampler front-end file
+ * and adapts it to a SignalSourceInterface.
+ * \author Javier Arribas, 2015 jarribas(at)cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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 "four_bit_cpx_file_signal_source.h"
+#include "configuration_interface.h"
+#include "gnss_sdr_string_literals.h"
+#include <glog/logging.h>
+
+using namespace std::string_literals;
+
+
+FourBitCpxFileSignalSource::FourBitCpxFileSignalSource(
+    const ConfigurationInterface* configuration,
+    const std::string& role,
+    unsigned int in_streams, unsigned int out_streams,
+    Concurrent_Queue<pmt::pmt_t>* queue)
+    : FileSourceBase(configuration, role, "Four_Bit_Cpx_File_Signal_Source"s, queue, "byte"s)
+{
+    sample_type_ = configuration->property(role + ".sample_type", "iq"s);
+    // the complex-ness of the input is inferred from the output type
+    if (sample_type_ == "iq")
+        {
+            reverse_interleaving_ = false;
+        }
+    else if (sample_type_ == "qi")
+        {
+            reverse_interleaving_ = true;
+        }
+    else
+        {
+            LOG(WARNING) << sample_type_ << " unrecognized sample type. Assuming: ";
+        }
+
+
+    if (in_streams > 0)
+        {
+            LOG(ERROR) << "A signal source does not have an input stream";
+        }
+    if (out_streams > 1)
+        {
+            LOG(ERROR) << "This implementation only supports one output stream";
+        }
+}
+
+
+std::tuple<size_t, bool> FourBitCpxFileSignalSource::itemTypeToSize()
+{
+    auto is_complex = false;
+    auto item_size = size_t(sizeof(char));  // default
+
+    if (item_type() == "byte")
+        {
+            item_size = sizeof(char);
+        }
+    else
+        {
+            LOG(WARNING) << item_type() << " unrecognized item type. Using byte.";
+        }
+
+    return std::make_tuple(item_size, is_complex);
+}
+
+// 1 byte -> 1 complex samples
+double FourBitCpxFileSignalSource::packetsPerSample() const { return 1.0; }
+gnss_shared_ptr<gr::block> FourBitCpxFileSignalSource::source() const { return inter_shorts_to_cpx_; }
+
+
+void FourBitCpxFileSignalSource::create_file_source_hook()
+{
+    unpack_byte_ = make_unpack_byte_4bit_samples();
+    DLOG(INFO) << "unpack_byte_2bit_cpx_samples(" << unpack_byte_->unique_id() << ")";
+    inter_shorts_to_cpx_ = gr::blocks::interleaved_short_to_complex::make(false, reverse_interleaving_);  // I/Q swap enabled
+    DLOG(INFO) << "interleaved_short_to_complex(" << inter_shorts_to_cpx_->unique_id() << ")";
+}
+
+void FourBitCpxFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block)
+{
+    top_block->connect(file_source(), 0, unpack_byte_, 0);
+    top_block->connect(unpack_byte_, 0, inter_shorts_to_cpx_, 0);
+    DLOG(INFO) << "connected file_source to unpacker";
+}
+
+void FourBitCpxFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block)
+{
+    top_block->disconnect(file_source(), 0, unpack_byte_, 0);
+    top_block->disconnect(unpack_byte_, 0, inter_shorts_to_cpx_, 0);
+    DLOG(INFO) << "disconnected file_source from unpacker";
+}
diff --git a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h
new file mode 100644
index 000000000..ac9b5c3fb
--- /dev/null
+++ b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h
@@ -0,0 +1,71 @@
+/*!
+ * \file FOUR_BIT_cpx_file_signal_source.h
+ * \brief Interface of a class that reads signals samples from a 2 bit complex sampler front-end file
+ * and adapts it to a SignalSourceInterface.
+ * \author Javier Arribas, 2015 jarribas(at)cttc.es
+ *
+ * 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-2021  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#ifndef GNSS_SDR_FOUR_BIT_CPX_FILE_SIGNAL_SOURCE_H
+#define GNSS_SDR_FOUR_BIT_CPX_FILE_SIGNAL_SOURCE_H
+
+#include "file_source_base.h"
+#include "unpack_byte_4bit_samples.h"
+#include <gnuradio/blocks/interleaved_short_to_complex.h>
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+/** \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 FourBitCpxFileSignalSource : public FileSourceBase
+{
+public:
+    FourBitCpxFileSignalSource(const ConfigurationInterface* configuration,
+        const std::string& role,
+        unsigned int in_streams,
+        unsigned int out_streams,
+        Concurrent_Queue<pmt::pmt_t>* queue);
+
+    ~FourBitCpxFileSignalSource() = default;
+
+protected:
+    std::tuple<size_t, bool> itemTypeToSize() override;
+    double packetsPerSample() const override;
+    gnss_shared_ptr<gr::block> 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:
+    unpack_byte_4bit_samples_sptr unpack_byte_;
+    gr::blocks::interleaved_short_to_complex::sptr inter_shorts_to_cpx_;
+    std::string sample_type_;
+    bool reverse_interleaving_;
+};
+
+
+/** \} */
+/** \} */
+#endif  // GNSS_SDR_FOUR_BIT_CPX_FILE_SIGNAL_SOURCE_H
diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.cc b/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.cc
index ca82f23b5..ccc04374f 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.cc
@@ -26,8 +26,8 @@ unpack_byte_4bit_samples_sptr make_unpack_byte_4bit_samples()
 
 
 unpack_byte_4bit_samples::unpack_byte_4bit_samples() : sync_interpolator("unpack_byte_4bit_samples",
-                                                           gr::io_signature::make(1, 1, sizeof(signed char)),
-                                                           gr::io_signature::make(1, 1, sizeof(signed char)),
+                                                           gr::io_signature::make(1, 1, sizeof(int8_t)),
+                                                           gr::io_signature::make(1, 1, sizeof(int16_t)),
                                                            2)
 {
 }
@@ -37,8 +37,8 @@ int unpack_byte_4bit_samples::work(int noutput_items,
     gr_vector_const_void_star &input_items,
     gr_vector_void_star &output_items)
 {
-    const auto *in = reinterpret_cast<const signed char *>(input_items[0]);
-    auto *out = reinterpret_cast<signed char *>(output_items[0]);
+    const auto *in = reinterpret_cast<const int8_t *>(input_items[0]);
+    auto *out = reinterpret_cast<int16_t *>(output_items[0]);
     int n = 0;
     unsigned char tmp_char2;
     for (int i = 0; i < noutput_items / 2; i++)
@@ -46,21 +46,21 @@ int unpack_byte_4bit_samples::work(int noutput_items,
             tmp_char2 = in[i] & 0x0F;
             if (tmp_char2 >= 8)
                 {
-                    out[n++] = 2 * (tmp_char2 - 16) + 1;
+                    out[n++] = static_cast<int16_t>(2 * (tmp_char2 - 16) + 1);
                 }
             else
                 {
-                    out[n++] = 2 * tmp_char2 + 1;
+                    out[n++] = static_cast<int16_t>(2 * tmp_char2 + 1);
                 }
             tmp_char2 = in[i] >> 4;
             tmp_char2 = tmp_char2 & 0x0F;
             if (tmp_char2 >= 8)
                 {
-                    out[n++] = 2 * (tmp_char2 - 16) + 1;
+                    out[n++] = static_cast<int16_t>(2 * (tmp_char2 - 16) + 1);
                 }
             else
                 {
-                    out[n++] = 2 * tmp_char2 + 1;
+                    out[n++] = static_cast<int16_t>(2 * tmp_char2 + 1);
                 }
         }
     return noutput_items;
diff --git a/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.h b/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.h
index 1d3d91da6..2b75c763c 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/unpack_byte_4bit_samples.h
@@ -20,8 +20,8 @@
 #ifndef GNSS_SDR_UNPACK_BYTE_4BIT_SAMPLES_H
 #define GNSS_SDR_UNPACK_BYTE_4BIT_SAMPLES_H
 
+#include "gnss_block_interface.h"
 #include <gnuradio/sync_interpolator.h>
-#include <memory>
 
 /** \addtogroup Signal_Source
  * \{ */
@@ -31,7 +31,7 @@
 
 class unpack_byte_4bit_samples;
 
-using unpack_byte_4bit_samples_sptr = std::shared_ptr<unpack_byte_4bit_samples>;
+using unpack_byte_4bit_samples_sptr = gnss_shared_ptr<unpack_byte_4bit_samples>;
 
 unpack_byte_4bit_samples_sptr make_unpack_byte_4bit_samples();
 
diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc
index 6a92df891..adba9c0df 100644
--- a/src/core/receiver/gnss_block_factory.cc
+++ b/src/core/receiver/gnss_block_factory.cc
@@ -41,6 +41,7 @@
 #include "file_signal_source.h"
 #include "file_timestamp_signal_source.h"
 #include "fir_filter.h"
+#include "four_bit_cpx_file_signal_source.h"
 #include "freq_xlating_fir_filter.h"
 #include "galileo_e1_dll_pll_veml_tracking.h"
 #include "galileo_e1_pcps_8ms_ambiguous_acquisition.h"
@@ -694,6 +695,12 @@ std::unique_ptr<GNSSBlockInterface> GNSSBlockFactory::GetBlock(
                         out_streams, queue);
                     block = std::move(block_);
                 }
+            else if (implementation == "Four_Bit_Cpx_File_Signal_Source")
+                {
+                    std::unique_ptr<GNSSBlockInterface> block_ = std::make_unique<FourBitCpxFileSignalSource>(configuration, role, in_streams,
+                        out_streams, queue);
+                    block = std::move(block_);
+                }
             else if (implementation == "Two_Bit_Packed_File_Signal_Source")
                 {
                     std::unique_ptr<GNSSBlockInterface> block_ = std::make_unique<TwoBitPackedFileSignalSource>(configuration, role, in_streams,

From 742113c55f008128bf44e86c588e4251845d66c7 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Mon, 23 May 2022 12:30:12 +0200
Subject: [PATCH 02/31] Adding timestamp capability to four bits signal source

---
 .../adapters/file_timestamp_signal_source.cc  | 21 ++++++++++---
 .../four_bit_cpx_file_signal_source.cc        | 31 +++++++++++++++++--
 .../four_bit_cpx_file_signal_source.h         |  5 +++
 .../signal_source/libs/gnss_sdr_timestamp.cc  | 10 +++---
 .../signal_source/libs/gnss_sdr_timestamp.h   | 10 ++++--
 5 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
index 1cc29e40a..32d27ac19 100644
--- a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
@@ -16,7 +16,6 @@
 #include "file_timestamp_signal_source.h"
 #include "gnss_sdr_flags.h"
 #include "gnss_sdr_string_literals.h"
-#include "gnss_sdr_timestamp.h"
 #include <glog/logging.h>
 #include <string>
 
@@ -51,10 +50,22 @@ gnss_shared_ptr<gr::block> FileTimestampSignalSource::source() const { return ti
 
 void FileTimestampSignalSource::create_file_source_hook()
 {
-    timestamp_block_ = gnss_sdr_make_Timestamp(
-        std::get<0>(itemTypeToSize()),
-        timestamp_file_,
-        timestamp_clock_offset_ms_);
+    if (is_complex() == false)
+        {
+            timestamp_block_ = gnss_sdr_make_Timestamp(
+                std::get<0>(itemTypeToSize()),
+                timestamp_file_,
+                timestamp_clock_offset_ms_,
+                source_item_size() * 2);
+        }
+    else
+        {
+            timestamp_block_ = gnss_sdr_make_Timestamp(
+                std::get<0>(itemTypeToSize()),
+                timestamp_file_,
+                timestamp_clock_offset_ms_,
+                source_item_size());
+        }
     DLOG(INFO) << "timestamp_block_(" << timestamp_block_->unique_id() << ")";
 }
 
diff --git a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
index 0fc521fe9..c22ababb3 100644
--- a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
@@ -17,6 +17,7 @@
 
 #include "four_bit_cpx_file_signal_source.h"
 #include "configuration_interface.h"
+#include "gnss_sdr_flags.h"
 #include "gnss_sdr_string_literals.h"
 #include <glog/logging.h>
 
@@ -28,7 +29,9 @@ FourBitCpxFileSignalSource::FourBitCpxFileSignalSource(
     const std::string& role,
     unsigned int in_streams, unsigned int out_streams,
     Concurrent_Queue<pmt::pmt_t>* queue)
-    : FileSourceBase(configuration, role, "Four_Bit_Cpx_File_Signal_Source"s, queue, "byte"s)
+    : FileSourceBase(configuration, role, "Four_Bit_Cpx_File_Signal_Source"s, queue, "byte"s),
+      timestamp_file_(configuration->property(role + ".timestamp_filename"s, ""s)),
+      timestamp_clock_offset_ms_(configuration->property(role + ".timestamp_clock_offset_ms"s, 0.0))
 {
     sample_type_ = configuration->property(role + ".sample_type", "iq"s);
     // the complex-ness of the input is inferred from the output type
@@ -54,6 +57,12 @@ FourBitCpxFileSignalSource::FourBitCpxFileSignalSource(
         {
             LOG(ERROR) << "This implementation only supports one output stream";
         }
+
+    // override value with commandline flag, if present
+    if (FLAGS_timestamp_source != "-")
+        {
+            timestamp_file_ = FLAGS_timestamp_source;
+        }
 }
 
 
@@ -76,7 +85,7 @@ std::tuple<size_t, bool> FourBitCpxFileSignalSource::itemTypeToSize()
 
 // 1 byte -> 1 complex samples
 double FourBitCpxFileSignalSource::packetsPerSample() const { return 1.0; }
-gnss_shared_ptr<gr::block> FourBitCpxFileSignalSource::source() const { return inter_shorts_to_cpx_; }
+gnss_shared_ptr<gr::block> FourBitCpxFileSignalSource::source() const { return timestamp_block_; }
 
 
 void FourBitCpxFileSignalSource::create_file_source_hook()
@@ -85,6 +94,14 @@ void FourBitCpxFileSignalSource::create_file_source_hook()
     DLOG(INFO) << "unpack_byte_2bit_cpx_samples(" << unpack_byte_->unique_id() << ")";
     inter_shorts_to_cpx_ = gr::blocks::interleaved_short_to_complex::make(false, reverse_interleaving_);  // I/Q swap enabled
     DLOG(INFO) << "interleaved_short_to_complex(" << inter_shorts_to_cpx_->unique_id() << ")";
+    if (timestamp_file_.size() > 1)
+        {
+            timestamp_block_ = gnss_sdr_make_Timestamp(sizeof(gr_complex),
+                timestamp_file_,
+                timestamp_clock_offset_ms_,
+                1);
+            DLOG(INFO) << "timestamp_block_(" << timestamp_block_->unique_id() << ")";
+        }
 }
 
 void FourBitCpxFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block)
@@ -92,10 +109,20 @@ void FourBitCpxFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block)
     top_block->connect(file_source(), 0, unpack_byte_, 0);
     top_block->connect(unpack_byte_, 0, inter_shorts_to_cpx_, 0);
     DLOG(INFO) << "connected file_source to unpacker";
+    if (timestamp_file_.size() > 1)
+        {
+            top_block->connect(inter_shorts_to_cpx_, 0, timestamp_block_, 0);
+            DLOG(INFO) << "connected file_source to timestamp_block_";
+        }
 }
 
 void FourBitCpxFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block)
 {
+    if (timestamp_file_.size() > 1)
+        {
+            top_block->disconnect(inter_shorts_to_cpx_, 0, timestamp_block_, 0);
+            DLOG(INFO) << "disconnected file_source from timestamp_block_";
+        }
     top_block->disconnect(file_source(), 0, unpack_byte_, 0);
     top_block->disconnect(unpack_byte_, 0, inter_shorts_to_cpx_, 0);
     DLOG(INFO) << "disconnected file_source from unpacker";
diff --git a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h
index ac9b5c3fb..a36776afb 100644
--- a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h
+++ b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.h
@@ -21,6 +21,7 @@
 #define GNSS_SDR_FOUR_BIT_CPX_FILE_SIGNAL_SOURCE_H
 
 #include "file_source_base.h"
+#include "gnss_sdr_timestamp.h"
 #include "unpack_byte_4bit_samples.h"
 #include <gnuradio/blocks/interleaved_short_to_complex.h>
 #include <cstddef>
@@ -63,6 +64,10 @@ private:
     gr::blocks::interleaved_short_to_complex::sptr inter_shorts_to_cpx_;
     std::string sample_type_;
     bool reverse_interleaving_;
+
+    gnss_shared_ptr<Gnss_Sdr_Timestamp> timestamp_block_;
+    std::string timestamp_file_;
+    double timestamp_clock_offset_ms_;
 };
 
 
diff --git a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.cc b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.cc
index 9ac458c04..857acf1e7 100644
--- a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.cc
+++ b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.cc
@@ -28,22 +28,23 @@
 
 
 Gnss_Sdr_Timestamp::Gnss_Sdr_Timestamp(size_t sizeof_stream_item,
-    std::string timestamp_file, double clock_offset_ms)
+    std::string timestamp_file, double clock_offset_ms, int items_to_samples)
     : gr::sync_block("Timestamp",
           gr::io_signature::make(1, 20, sizeof_stream_item),
           gr::io_signature::make(1, 20, sizeof_stream_item)),
       d_timefile(std::move(timestamp_file)),
       d_clock_offset_ms(clock_offset_ms),
       d_fraction_ms_offset(modf(d_clock_offset_ms, &d_integer_ms_offset)),  // optional clockoffset parameter to convert UTC timestamps to GPS time in some receiver's configuration
+      d_items_to_samples(items_to_samples),
       d_next_timetag_samplecount(0),
       d_get_next_timetag(true)
 {
 }
 
 
-gnss_shared_ptr<Gnss_Sdr_Timestamp> gnss_sdr_make_Timestamp(size_t sizeof_stream_item, std::string timestamp_file, double clock_offset_ms)
+gnss_shared_ptr<Gnss_Sdr_Timestamp> gnss_sdr_make_Timestamp(size_t sizeof_stream_item, std::string timestamp_file, double clock_offset_ms, int items_to_samples)
 {
-    gnss_shared_ptr<Gnss_Sdr_Timestamp> Timestamp_(new Gnss_Sdr_Timestamp(sizeof_stream_item, std::move(timestamp_file), clock_offset_ms));
+    gnss_shared_ptr<Gnss_Sdr_Timestamp> Timestamp_(new Gnss_Sdr_Timestamp(sizeof_stream_item, std::move(timestamp_file), clock_offset_ms, items_to_samples));
     return Timestamp_;
 }
 
@@ -110,8 +111,7 @@ int Gnss_Sdr_Timestamp::work(int noutput_items,
     for (size_t ch = 0; ch < output_items.size(); ch++)
         {
             std::memcpy(output_items[ch], input_items[ch], noutput_items * input_signature()->sizeof_stream_item(ch));
-            uint64_t bytes_to_samples = 2;  // todo: improve this.. hardcoded 2 bytes -> 1 complex sample!
-            int64_t diff_samplecount = uint64diff(this->nitems_written(ch), d_next_timetag_samplecount * bytes_to_samples);
+            int64_t diff_samplecount = uint64diff(this->nitems_written(ch), d_next_timetag_samplecount * d_items_to_samples);
             // std::cout << "diff_samplecount: " << diff_samplecount << ", noutput_items: " << noutput_items << "\n";
             if (diff_samplecount <= noutput_items and std::labs(diff_samplecount) <= noutput_items)
                 {
diff --git a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
index 6a7f762e2..11716ca82 100644
--- a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
+++ b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
@@ -39,7 +39,8 @@ class Gnss_Sdr_Timestamp;
 gnss_shared_ptr<Gnss_Sdr_Timestamp> gnss_sdr_make_Timestamp(
     size_t sizeof_stream_item,
     std::string timestamp_file,
-    double clock_offset_ms);
+    double clock_offset_ms,
+    int items_to_samples);
 
 
 class Gnss_Sdr_Timestamp : public gr::sync_block
@@ -54,11 +55,13 @@ private:
     friend gnss_shared_ptr<Gnss_Sdr_Timestamp> gnss_sdr_make_Timestamp(
         size_t sizeof_stream_item,
         std::string timestamp_file,
-        double clock_offset_ms);
+        double clock_offset_ms,
+        int items_to_samples);
 
     Gnss_Sdr_Timestamp(size_t sizeof_stream_item,
         std::string timestamp_file,
-        double clock_offset_ms);
+        double clock_offset_ms,
+        int items_to_samples);
 
     int64_t uint64diff(uint64_t first, uint64_t second);
     bool read_next_timetag();
@@ -69,6 +72,7 @@ private:
     double d_fraction_ms_offset;
     double d_integer_ms_offset;
     uint64_t d_next_timetag_samplecount;
+    int d_items_to_samples;
     bool d_get_next_timetag;
 };
 

From 67e4d7c6e2b7c0cf21085fb42a800c1526f88132 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Mon, 23 May 2022 15:34:06 +0200
Subject: [PATCH 03/31] Added Galileo E6 experimental Pseudorange generation
 using signal source timetag propagation

---
 .../galileo_telemetry_decoder_gs.cc           | 26 ++++++++++++++++++-
 .../galileo_telemetry_decoder_gs.h            |  1 +
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
index db723cdf9..f672a6b21 100644
--- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
+++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
@@ -85,7 +85,8 @@ galileo_telemetry_decoder_gs::galileo_telemetry_decoder_gs(
                       d_enable_navdata_monitor(conf.enable_navdata_monitor),
                       d_dump_crc_stats(conf.dump_crc_stats),
                       d_enable_reed_solomon_inav(false),
-                      d_valid_timetag(false)
+                      d_valid_timetag(false),
+                      d_E6_TOW_set(false)
 {
     // prevent telemetry symbols accumulation in output buffers
     this->set_max_noutput_items(1);
@@ -591,6 +592,7 @@ void galileo_telemetry_decoder_gs::set_satellite(const Gnss_Satellite &satellite
     d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
     d_last_valid_preamble = d_sample_counter;
     d_sent_tlm_failed_msg = false;
+    d_E6_TOW_set = false;
     DLOG(INFO) << "Setting decoder Finite State Machine to satellite " << d_satellite;
     DLOG(INFO) << "Navigation Satellite set to " << d_satellite;
 }
@@ -607,6 +609,7 @@ void galileo_telemetry_decoder_gs::reset()
     d_inav_nav.set_TOW0_flag(false);
     d_last_valid_preamble = d_sample_counter;
     d_sent_tlm_failed_msg = false;
+    d_E6_TOW_set = false;
     d_stat = 0;
     d_viterbi->reset();
     if (d_enable_reed_solomon_inav == true)
@@ -1019,6 +1022,23 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
                 case 3:  // CNAV
                     {
                         // TODO
+                        // Obtain Galileo E6 TOW from timetags, if available
+                        if (d_valid_timetag == true)
+                            {
+                                int rx_tow_at_preamble = d_current_timetag.tow_ms;
+                                uint32_t predicted_tow_at_preamble_ms = 1000 * (rx_tow_at_preamble / 1000);  // floor to integer number of seconds
+
+                                d_TOW_at_Preamble_ms = predicted_tow_at_preamble_ms;
+                                //todo: compute tow at current symbol from the decoder delay similar as d_TOW_at_current_symbol_ms = d_TOW_at_Preamble_ms + static_cast<uint32_t>((d_required_symbols + 1) * GALILEO_FNAV_CODES_PER_SYMBOL * GALILEO_E5A_CODE_PERIOD_MS);
+                                d_TOW_at_current_symbol_ms = predicted_tow_at_preamble_ms + static_cast<uint32_t>((GALILEO_CNAV_SYMBOLS_PER_PAGE - GALILEO_CNAV_PREAMBLE_LENGTH_BITS) * d_PRN_code_period_ms);
+                                if (d_E6_TOW_set == false)
+                                    {
+                                        std::cout << " Sat PRN " << d_satellite.get_PRN() << " E6 TimeTag TOW at preamble: " << predicted_tow_at_preamble_ms
+                                                  //                                                  << " [ms] TOW fraction: " << d_current_timetag.tow_ms_fraction
+                                                  << " [ms] d_TOW_at_current_symbol_ms: " << d_TOW_at_current_symbol_ms << " [ms]\n";
+                                        d_E6_TOW_set = true;
+                                    }
+                            }
                     }
                 }
         }
@@ -1078,6 +1098,10 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
         case 3:  // CNAV
             {
                 // TODO
+                if (d_E6_TOW_set == true)
+                    {
+                        current_symbol.Flag_valid_word = true;
+                    }
                 break;
             }
         }
diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.h b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.h
index 59e5fcb01..98c1494a1 100644
--- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.h
+++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.h
@@ -147,6 +147,7 @@ private:
     bool d_dump_crc_stats;
     bool d_enable_reed_solomon_inav;
     bool d_valid_timetag;
+    bool d_E6_TOW_set;
 };
 
 

From a45998d0db0f762bee177844dead7d39fc4c1e6f Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Mon, 23 May 2022 17:32:09 +0200
Subject: [PATCH 04/31] Fix computation of Galileo E6 pseudoranges using TOW
 from source timetag

---
 .../galileo_telemetry_decoder_gs.cc              | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
index f672a6b21..2ff4a6612 100644
--- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
+++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
@@ -1022,19 +1022,19 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
                 case 3:  // CNAV
                     {
                         // TODO
-                        // Obtain Galileo E6 TOW from timetags, if available
+                        // Add option to use system time to estimate the preamble TOW
+                        // Add option to use Galileo E1 or E5 TOW information..
+
+                        // Done: Obtain Galileo E6 TOW from timetags, if available
                         if (d_valid_timetag == true)
                             {
                                 int rx_tow_at_preamble = d_current_timetag.tow_ms;
                                 uint32_t predicted_tow_at_preamble_ms = 1000 * (rx_tow_at_preamble / 1000);  // floor to integer number of seconds
-
                                 d_TOW_at_Preamble_ms = predicted_tow_at_preamble_ms;
-                                //todo: compute tow at current symbol from the decoder delay similar as d_TOW_at_current_symbol_ms = d_TOW_at_Preamble_ms + static_cast<uint32_t>((d_required_symbols + 1) * GALILEO_FNAV_CODES_PER_SYMBOL * GALILEO_E5A_CODE_PERIOD_MS);
-                                d_TOW_at_current_symbol_ms = predicted_tow_at_preamble_ms + static_cast<uint32_t>((GALILEO_CNAV_SYMBOLS_PER_PAGE - GALILEO_CNAV_PREAMBLE_LENGTH_BITS) * d_PRN_code_period_ms);
+                                d_TOW_at_current_symbol_ms = predicted_tow_at_preamble_ms + static_cast<uint32_t>((d_required_symbols + 1) * d_PRN_code_period_ms);
                                 if (d_E6_TOW_set == false)
                                     {
                                         std::cout << " Sat PRN " << d_satellite.get_PRN() << " E6 TimeTag TOW at preamble: " << predicted_tow_at_preamble_ms
-                                                  //                                                  << " [ms] TOW fraction: " << d_current_timetag.tow_ms_fraction
                                                   << " [ms] d_TOW_at_current_symbol_ms: " << d_TOW_at_current_symbol_ms << " [ms]\n";
                                         d_E6_TOW_set = true;
                                     }
@@ -1064,8 +1064,10 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
                     }
                 case 3:  // CNAV
                     {
-                        // TODO
-                        d_TOW_at_current_symbol_ms += d_PRN_code_period_ms;  // this is not the TOW!
+                        if (d_E6_TOW_set == true)
+                            {
+                                d_TOW_at_current_symbol_ms += d_PRN_code_period_ms;
+                            }
                         break;
                     }
                 }

From 0eebe9c2deafdd6891f7e413a8e7e1a5a7917e23 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 24 May 2022 15:44:11 +0200
Subject: [PATCH 05/31] Fix four bits source adapter

---
 .../adapters/four_bit_cpx_file_signal_source.cc      | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
index c22ababb3..3ac128a05 100644
--- a/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/four_bit_cpx_file_signal_source.cc
@@ -85,7 +85,17 @@ std::tuple<size_t, bool> FourBitCpxFileSignalSource::itemTypeToSize()
 
 // 1 byte -> 1 complex samples
 double FourBitCpxFileSignalSource::packetsPerSample() const { return 1.0; }
-gnss_shared_ptr<gr::block> FourBitCpxFileSignalSource::source() const { return timestamp_block_; }
+gnss_shared_ptr<gr::block> FourBitCpxFileSignalSource::source() const
+{
+    if (timestamp_file_.size() > 1)
+        {
+            return timestamp_block_;
+        }
+    else
+        {
+            return inter_shorts_to_cpx_;
+        }
+}
 
 
 void FourBitCpxFileSignalSource::create_file_source_hook()

From ea573359e0fbd46265eb78d8164623aca6d7851c Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Tue, 24 May 2022 21:18:59 +0200
Subject: [PATCH 06/31] Print IGM messages if TOW is available

---
 src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc       | 8 ++++++--
 src/algorithms/PVT/libs/rtcm.cc                           | 8 ++++----
 .../gnuradio_blocks/galileo_telemetry_decoder_gs.cc       | 4 ++++
 src/core/libs/galileo_e6_has_msg_receiver.cc              | 8 ++++++++
 src/core/system_parameters/galileo_cnav_message.cc        | 2 ++
 src/core/system_parameters/galileo_cnav_message.h         | 5 +++++
 src/core/system_parameters/galileo_has_data.h             | 2 ++
 src/core/system_parameters/galileo_has_page.h             | 1 +
 8 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index 6fb9a2510..83299a356 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -1573,11 +1573,15 @@ void rtklib_pvt_gs::msg_handler_has_data(const pmt::pmt_t& msg) const
             const size_t msg_type_hash_code = pmt::any_ref(msg).type().hash_code();
             if (msg_type_hash_code == d_galileo_has_data_sptr_type_hash_code)
                 {
-                    if (d_enable_has_messages)
+                    const auto has_data = wht::any_cast<std::shared_ptr<Galileo_HAS_data>>(pmt::any_ref(msg));
+                    if (d_has_simple_printer)
                         {
-                            const auto has_data = wht::any_cast<std::shared_ptr<Galileo_HAS_data>>(pmt::any_ref(msg));
                             d_has_simple_printer->print_message(has_data.get());
                         }
+                    if (d_rtcm_printer && has_data->tow <= 604800)
+                        {
+                            d_rtcm_printer->Print_IGM_Messages(*has_data.get());
+                        }
                 }
         }
     catch (const wht::bad_any_cast& e)
diff --git a/src/algorithms/PVT/libs/rtcm.cc b/src/algorithms/PVT/libs/rtcm.cc
index 5beb3d9ea..bbd0abeb1 100644
--- a/src/algorithms/PVT/libs/rtcm.cc
+++ b/src/algorithms/PVT/libs/rtcm.cc
@@ -3428,7 +3428,7 @@ std::string Rtcm::get_IGM01_header(const Galileo_HAS_data& has_data, uint8_t nsy
 {
     std::string header;
 
-    uint32_t tow = 0;                                // TODO
+    uint32_t tow = has_data.tow;
     uint16_t ssr_provider_id = 0;                    // ?
     uint8_t igm_version = 0;                         // ?
     uint8_t ssr_solution_id = 0;                     // ?
@@ -3505,7 +3505,7 @@ std::string Rtcm::get_IGM02_header(const Galileo_HAS_data& has_data, uint8_t nsy
 {
     std::string header;
 
-    uint32_t tow = 0;                                // TODO
+    uint32_t tow = has_data.tow;
     uint16_t ssr_provider_id = 0;                    // ?
     uint8_t igm_version = 0;                         // ?
     uint8_t ssr_solution_id = 0;                     // ?
@@ -3576,7 +3576,7 @@ std::string Rtcm::get_IGM03_header(const Galileo_HAS_data& has_data, uint8_t nsy
 {
     std::string header;
 
-    uint32_t tow = 0;                                // TODO
+    uint32_t tow = has_data.tow;
     uint16_t ssr_provider_id = 0;                    // ?
     uint8_t igm_version = 0;                         // ?
     uint8_t ssr_solution_id = 0;                     // ?
@@ -3661,7 +3661,7 @@ std::string Rtcm::get_IGM05_header(const Galileo_HAS_data& has_data, uint8_t nsy
 {
     std::string header;
 
-    uint32_t tow = 0;                                // TODO
+    uint32_t tow = has_data.tow;
     uint16_t ssr_provider_id = 0;                    // ?
     uint8_t igm_version = 0;                         // ?
     uint8_t ssr_solution_id = 0;                     // ?
diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
index 2ff4a6612..53d4ced5b 100644
--- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
+++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
@@ -571,6 +571,10 @@ void galileo_telemetry_decoder_gs::decode_CNAV_word(uint64_t time_stamp, float *
                 }
             else
                 {
+                    if (d_E6_TOW_set == true)
+                        {
+                            d_cnav_nav.set_tow(d_TOW_at_Preamble_ms / 1000);
+                        }
                     const std::shared_ptr<Galileo_HAS_page> tmp_obj = std::make_shared<Galileo_HAS_page>(d_cnav_nav.get_HAS_encoded_page());
                     this->message_port_pub(pmt::mp("E6_HAS_from_TLM"), pmt::make_any(tmp_obj));
                     if (d_print_cnav_page == true)
diff --git a/src/core/libs/galileo_e6_has_msg_receiver.cc b/src/core/libs/galileo_e6_has_msg_receiver.cc
index 53fc3d6bc..6b4c326d2 100644
--- a/src/core/libs/galileo_e6_has_msg_receiver.cc
+++ b/src/core/libs/galileo_e6_has_msg_receiver.cc
@@ -26,6 +26,7 @@
 #include <glog/logging.h>           // for DLOG
 #include <gnuradio/io_signature.h>  // for gr::io_signature::make
 #include <algorithm>                // for std::find, std::count
+#include <cmath>                    // for std::remainder
 #include <cstddef>                  // for size_t
 #include <iterator>                 // for std::back_inserter
 #include <limits>                   // for std::numeric_limits
@@ -111,6 +112,7 @@ void galileo_e6_has_msg_receiver::set_enable_navdata_monitor(bool enable)
 std::shared_ptr<Galileo_HAS_data> galileo_e6_has_msg_receiver::process_test_page(const pmt::pmt_t& msg)
 {
     int64_t timestamp = std::numeric_limits<uint64_t>::max();
+    uint32_t tow = std::numeric_limits<uint32_t>::max();
     try
         {
             const size_t msg_type_hash_code = pmt::any_ref(msg).type().hash_code();
@@ -126,6 +128,7 @@ std::shared_ptr<Galileo_HAS_data> galileo_e6_has_msg_receiver::process_test_page
                     d_current_has_status = HAS_data_page->has_status;
                     d_current_message_id = HAS_data_page->message_id;
                     timestamp = HAS_data_page->time_stamp;
+                    tow = HAS_data_page->tow;
                     if (d_printed_mids[d_current_message_id] == false)
                         {
                             process_HAS_page(*HAS_data_page.get());
@@ -146,6 +149,7 @@ std::shared_ptr<Galileo_HAS_data> galileo_e6_has_msg_receiver::process_test_page
         {
             d_HAS_data.has_status = d_current_has_status;
             d_HAS_data.message_id = d_current_message_id;
+            d_HAS_data.tow = tow - static_cast<uint32_t>(std::remainder(tow, 3600)) + d_HAS_data.header.toh;
             auto has_data_ptr = std::make_shared<Galileo_HAS_data>(d_HAS_data);
             d_new_message = false;
             d_printed_mids[d_current_message_id] = true;
@@ -160,6 +164,7 @@ void galileo_e6_has_msg_receiver::msg_handler_galileo_e6_has(const pmt::pmt_t& m
 {
     gr::thread::scoped_lock lock(d_setlock);  // require mutex with msg_handler_galileo_e6_has function called by the scheduler
     int64_t timestamp = std::numeric_limits<uint64_t>::max();
+    uint32_t tow = std::numeric_limits<uint32_t>::max();
     try
         {
             const size_t msg_type_hash_code = pmt::any_ref(msg).type().hash_code();
@@ -175,6 +180,7 @@ void galileo_e6_has_msg_receiver::msg_handler_galileo_e6_has(const pmt::pmt_t& m
                     d_current_has_status = HAS_data_page->has_status;
                     d_current_message_id = HAS_data_page->message_id;
                     timestamp = HAS_data_page->time_stamp;
+                    tow = HAS_data_page->tow;
                     if (d_printed_mids[d_current_message_id] == false)
                         {
                             process_HAS_page(*HAS_data_page.get());
@@ -195,6 +201,7 @@ void galileo_e6_has_msg_receiver::msg_handler_galileo_e6_has(const pmt::pmt_t& m
         {
             d_HAS_data.has_status = d_current_has_status;
             d_HAS_data.message_id = d_current_message_id;
+            d_HAS_data.tow = tow - static_cast<uint32_t>(std::remainder(tow, 3600)) + d_HAS_data.header.toh;
             d_printed_mids[d_current_message_id] = true;
             d_printed_timestamps[d_current_message_id] = timestamp;
             auto has_data_ptr = std::make_shared<Galileo_HAS_data>(d_HAS_data);
@@ -399,6 +406,7 @@ int galileo_e6_has_msg_receiver::decode_message_type1(uint8_t message_id, uint8_
 
     // Trigger HAS message content reading and fill the d_HAS_data object
     d_HAS_data = Galileo_HAS_data();
+    d_HAS_data.tow = std::numeric_limits<uint32_t>::max();  // Unknown
 
     read_MT1_header(decoded_message_type_1.substr(0, GALILEO_CNAV_MT1_HEADER_BITS));
 
diff --git a/src/core/system_parameters/galileo_cnav_message.cc b/src/core/system_parameters/galileo_cnav_message.cc
index 0b93f6bf0..e514b00b7 100644
--- a/src/core/system_parameters/galileo_cnav_message.cc
+++ b/src/core/system_parameters/galileo_cnav_message.cc
@@ -21,6 +21,7 @@
 #include <boost/dynamic_bitset.hpp>  // for boost::dynamic_bitset
 #include <glog/logging.h>
 #include <algorithm>  // for reverse
+#include <limits>
 #include <vector>
 
 using CRC_Galileo_CNAV_type = boost::crc_optimal<24, 0x1864CFBU, 0x0, 0x0, false, false>;
@@ -60,6 +61,7 @@ void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
     const std::bitset<GALILEO_CNAV_CRC_LENGTH> checksum(CRC_data);
     d_new_HAS_page = false;
     has_page = Galileo_HAS_page();
+    has_page.tow = std::numeric_limits<uint32_t>::max();  // Unknown
     d_flag_CRC_test = CRC_test(Word_for_CRC_bits, checksum.to_ulong());
     if (d_flag_CRC_test == true)
         {
diff --git a/src/core/system_parameters/galileo_cnav_message.h b/src/core/system_parameters/galileo_cnav_message.h
index 90444175b..436aed93f 100644
--- a/src/core/system_parameters/galileo_cnav_message.h
+++ b/src/core/system_parameters/galileo_cnav_message.h
@@ -74,6 +74,11 @@ public:
         has_page.time_stamp = time_stamp;
     }
 
+    inline void set_tow(uint32_t tow)
+    {
+        has_page.tow = tow;
+    }
+
 private:
     uint8_t read_has_page_header_parameter(const std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS>& bits, const std::pair<int32_t, int32_t>& parameter) const;
     bool CRC_test(const std::bitset<GALILEO_CNAV_BITS_FOR_CRC>& bits, uint32_t checksum) const;
diff --git a/src/core/system_parameters/galileo_has_data.h b/src/core/system_parameters/galileo_has_data.h
index a4a3c289e..79b50eaeb 100644
--- a/src/core/system_parameters/galileo_has_data.h
+++ b/src/core/system_parameters/galileo_has_data.h
@@ -104,6 +104,8 @@ public:
     std::vector<std::vector<int16_t>> phase_bias;                     //!< PB - Phase bias for the m-th signal of the n-th SV. See HAS SIS ICD 1.0 Section 5.2.6
     std::vector<std::vector<uint8_t>> phase_discontinuity_indicator;  //!< PDI - Phase Discontinuity Indicator. See HAS SIS ICD 1.0 Section 5.2.6.
 
+    uint32_t tow;  //!< Time of Week
+
     mt1_header header;   //!< MT1 Header parameters. See HAS SIS ICD 1.0 Section 5.1.1
     uint8_t has_status;  //!< HASS - HAS Status (from HAS page header). See HAS SIS ICD 1.0 Section 3.1.1
     uint8_t message_id;  //!< MID - Message ID (from HAS page header). See HAS SIS ICD 1.0 Section 3.1
diff --git a/src/core/system_parameters/galileo_has_page.h b/src/core/system_parameters/galileo_has_page.h
index 099ddd645..27da69f9b 100644
--- a/src/core/system_parameters/galileo_has_page.h
+++ b/src/core/system_parameters/galileo_has_page.h
@@ -39,6 +39,7 @@ public:
 
     std::string has_message_string;  //!< HAS message content
     uint64_t time_stamp{};           //!< HAS page time stamp, in [s]
+    uint32_t tow{};                  //!< HAS page time of week, in [s]
 
     // HAS page header
     uint8_t has_status{};       //!< HAS status

From 657a7298f352276a147956f7a2a3c4f8eff5d997 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 25 May 2022 14:43:01 +0200
Subject: [PATCH 07/31] Compute PVT with E6 only and xml assistance

---
 .../PVT/gnuradio_blocks/rtklib_pvt_gs.cc      | 11 +++++
 src/algorithms/PVT/libs/rtklib_solver.cc      | 48 +++++++++++++++++++
 .../libs/rtklib/rtklib_conversions.cc         |  4 ++
 src/core/libs/gnss_sdr_supl_client.cc         |  7 +++
 4 files changed, 70 insertions(+)

diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index 83299a356..77468dcfe 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -651,6 +651,13 @@ rtklib_pvt_gs::~rtklib_pvt_gs()
                                 {
                                     ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
                                     boost::archive::xml_oarchive xml(ofs);
+                                    // Annotate as GPS week number
+                                    for (auto gal_eph_iter = d_internal_pvt_solver->galileo_ephemeris_map.begin();
+                                         gal_eph_iter != d_internal_pvt_solver->galileo_ephemeris_map.end();
+                                         ++gal_eph_iter)
+                                        {
+                                            gal_eph_iter->second.WN += 1024;
+                                        }
                                     xml << boost::serialization::make_nvp("GNSS-SDR_gal_ephemeris_map", d_internal_pvt_solver->galileo_ephemeris_map);
                                     LOG(INFO) << "Saved Galileo E1 Ephemeris map data";
                                 }
@@ -2049,6 +2056,10 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
                                             store_valid_observable = true;
                                         }
                                 }
+                            if (std::string(in[i][epoch].Signal) == std::string("E6"))
+                                {
+                                    store_valid_observable = true;
+                                }
 
                             if (store_valid_observable)
                                 {
diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc
index bfa3bd52b..1c9c0b3ea 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.cc
+++ b/src/algorithms/PVT/libs/rtklib_solver.cc
@@ -407,6 +407,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
     bool band1 = false;
     bool band2 = false;
     bool gal_e5_is_e5b = false;
+    bool gal_e6 = false;
     for (gnss_observables_iter = gnss_observables_map.cbegin();
          gnss_observables_iter != gnss_observables_map.cend();
          ++gnss_observables_iter)
@@ -514,6 +515,48 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                                         gal_e5_is_e5b = true;
                                     }
                             }
+                        if (sig_ == "E6")
+                            {
+                                gal_e6 = true;
+                                galileo_ephemeris_iter = galileo_ephemeris_map.find(gnss_observables_iter->second.PRN);
+                                if (galileo_ephemeris_iter != galileo_ephemeris_map.cend())
+                                    {
+                                        bool found_E1_obs = false;
+                                        for (int i = 0; i < valid_obs; i++)
+                                            {
+                                                if (eph_data[i].sat == (static_cast<int>(gnss_observables_iter->second.PRN + NSATGPS + NSATGLO)))
+                                                    {
+                                                        d_obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(d_obs_data[i + glo_valid_obs],
+                                                            gnss_observables_iter->second,
+                                                            galileo_ephemeris_iter->second.WN,
+                                                            0);  // Band 1 (E6)
+                                                        found_E1_obs = true;
+                                                        break;
+                                                    }
+                                            }
+                                        if (!found_E1_obs)
+                                            {
+                                                // insert Galileo E6 obs as new obs and also insert its ephemeris
+                                                // convert ephemeris from GNSS-SDR class to RTKLIB structure
+                                                eph_data[valid_obs] = eph_to_rtklib(galileo_ephemeris_iter->second);
+                                                // convert observation from GNSS-SDR class to RTKLIB structure
+                                                const auto default_code_ = static_cast<unsigned char>(CODE_NONE);
+                                                obsd_t newobs = {{0, 0}, '0', '0', {}, {},
+                                                    {default_code_, default_code_, default_code_},
+                                                    {}, {0.0, 0.0, 0.0}, {}};
+                                                d_obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs,
+                                                    gnss_observables_iter->second,
+                                                    galileo_ephemeris_iter->second.WN,
+                                                    0);  // Band 1 (E6)
+                                                // std::cout << "Week " << galileo_ephemeris_iter->second.WN << '\n';
+                                                valid_obs++;
+                                            }
+                                    }
+                                else  // the ephemeris are not available for this SV
+                                    {
+                                        DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN;
+                                    }
+                            }
                         break;
                     }
                 case 'G':
@@ -879,6 +922,11 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                 {
                     for (int j = 0; j < NFREQ; j++)
                         {
+                            if (j == 0 && gal_e6)
+                                {
+                                    // frq = 3 corresponds to E6 in that function
+                                    nav_data.lam[i][j] = satwavelen(i + 1, 3, &nav_data);
+                                }
                             if (j == 2 && gal_e5_is_e5b)
                                 {
                                     // frq = 4 corresponds to E5B in that function
diff --git a/src/algorithms/libs/rtklib/rtklib_conversions.cc b/src/algorithms/libs/rtklib/rtklib_conversions.cc
index 45eacabcb..b7e50519c 100644
--- a/src/algorithms/libs/rtklib/rtklib_conversions.cc
+++ b/src/algorithms/libs/rtklib/rtklib_conversions.cc
@@ -75,6 +75,10 @@ obsd_t insert_obs_to_rtklib(obsd_t& rtklib_obs, const Gnss_Synchro& gnss_synchro
                 {
                     rtklib_obs.code[band] = static_cast<unsigned char>(CODE_L7X);
                 }
+            if (sig_ == "E6")
+                {
+                    rtklib_obs.code[band] = static_cast<unsigned char>(CODE_L6B);
+                }
             break;
         case 'R':
             rtklib_obs.sat = gnss_synchro.PRN + NSATGPS;
diff --git a/src/core/libs/gnss_sdr_supl_client.cc b/src/core/libs/gnss_sdr_supl_client.cc
index e079b0a9f..33a6969f0 100644
--- a/src/core/libs/gnss_sdr_supl_client.cc
+++ b/src/core/libs/gnss_sdr_supl_client.cc
@@ -414,6 +414,13 @@ bool Gnss_Sdr_Supl_Client::load_gal_ephemeris_xml(const std::string& file_name)
             gal_ephemeris_map.clear();
             xml >> boost::serialization::make_nvp("GNSS-SDR_gal_ephemeris_map", this->gal_ephemeris_map);
             LOG(INFO) << "Loaded Ephemeris map data with " << this->gal_ephemeris_map.size() << " satellites";
+            // Convert to GPS week number
+            for (auto gal_eph_iter = this->gal_ephemeris_map.begin();
+                 gal_eph_iter != this->gal_ephemeris_map.end();
+                 ++gal_eph_iter)
+                {
+                    gal_eph_iter->second.WN -= 1024;
+                }
         }
     catch (std::exception& e)
         {

From ab4020178323e61e2e769f6cc4b55f4e91d118e6 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 25 May 2022 21:04:44 +0200
Subject: [PATCH 08/31] Improve loops

---
 src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc | 8 +++-----
 src/core/libs/gnss_sdr_supl_client.cc               | 8 +++-----
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index 77468dcfe..f44573db8 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -651,12 +651,10 @@ rtklib_pvt_gs::~rtklib_pvt_gs()
                                 {
                                     ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
                                     boost::archive::xml_oarchive xml(ofs);
-                                    // Annotate as GPS week number
-                                    for (auto gal_eph_iter = d_internal_pvt_solver->galileo_ephemeris_map.begin();
-                                         gal_eph_iter != d_internal_pvt_solver->galileo_ephemeris_map.end();
-                                         ++gal_eph_iter)
+                                    // Annotate as full GPS week number
+                                    for (auto& gal_eph_iter : d_internal_pvt_solver->galileo_ephemeris_map)
                                         {
-                                            gal_eph_iter->second.WN += 1024;
+                                            gal_eph_iter.second.WN += 1024;
                                         }
                                     xml << boost::serialization::make_nvp("GNSS-SDR_gal_ephemeris_map", d_internal_pvt_solver->galileo_ephemeris_map);
                                     LOG(INFO) << "Saved Galileo E1 Ephemeris map data";
diff --git a/src/core/libs/gnss_sdr_supl_client.cc b/src/core/libs/gnss_sdr_supl_client.cc
index 33a6969f0..e2e9a3c73 100644
--- a/src/core/libs/gnss_sdr_supl_client.cc
+++ b/src/core/libs/gnss_sdr_supl_client.cc
@@ -414,12 +414,10 @@ bool Gnss_Sdr_Supl_Client::load_gal_ephemeris_xml(const std::string& file_name)
             gal_ephemeris_map.clear();
             xml >> boost::serialization::make_nvp("GNSS-SDR_gal_ephemeris_map", this->gal_ephemeris_map);
             LOG(INFO) << "Loaded Ephemeris map data with " << this->gal_ephemeris_map.size() << " satellites";
-            // Convert to GPS week number
-            for (auto gal_eph_iter = this->gal_ephemeris_map.begin();
-                 gal_eph_iter != this->gal_ephemeris_map.end();
-                 ++gal_eph_iter)
+            // Convert to full GPS week number
+            for (auto& gal_eph_iter : this->gal_ephemeris_map)
                 {
-                    gal_eph_iter->second.WN -= 1024;
+                    gal_eph_iter.second.WN -= 1024;
                 }
         }
     catch (std::exception& e)

From 92b021f1b6855db2f9dbd2137809576e1ea5eda8 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Thu, 26 May 2022 10:02:58 +0200
Subject: [PATCH 09/31] Improve handling of E6 observables

---
 src/algorithms/PVT/libs/rtklib_solver.cc | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc
index 1c9c0b3ea..cc0f96ac4 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.cc
+++ b/src/algorithms/PVT/libs/rtklib_solver.cc
@@ -529,7 +529,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                                                         d_obs_data[i + glo_valid_obs] = insert_obs_to_rtklib(d_obs_data[i + glo_valid_obs],
                                                             gnss_observables_iter->second,
                                                             galileo_ephemeris_iter->second.WN,
-                                                            0);  // Band 1 (E6)
+                                                            2);  // Band E6
                                                         found_E1_obs = true;
                                                         break;
                                                     }
@@ -547,7 +547,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                                                 d_obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs,
                                                     gnss_observables_iter->second,
                                                     galileo_ephemeris_iter->second.WN,
-                                                    0);  // Band 1 (E6)
+                                                    2);  // Band E6
                                                 // std::cout << "Week " << galileo_ephemeris_iter->second.WN << '\n';
                                                 valid_obs++;
                                             }
@@ -922,7 +922,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                 {
                     for (int j = 0; j < NFREQ; j++)
                         {
-                            if (j == 0 && gal_e6)
+                            if (j == 2 && gal_e6)
                                 {
                                     // frq = 3 corresponds to E6 in that function
                                     nav_data.lam[i][j] = satwavelen(i + 1, 3, &nav_data);

From 934bbcaa459c8974104a2d8430cdb354b3db6ae1 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Mon, 30 May 2022 14:20:34 +0200
Subject: [PATCH 10/31] Add Galileo E6B observables in RINEX files if available

---
 src/algorithms/PVT/libs/rinex_printer.cc | 160 +++++++++++++++++++++--
 1 file changed, 146 insertions(+), 14 deletions(-)

diff --git a/src/algorithms/PVT/libs/rinex_printer.cc b/src/algorithms/PVT/libs/rinex_printer.cc
index 4b41dcd2a..af29e783b 100644
--- a/src/algorithms/PVT/libs/rinex_printer.cc
+++ b/src/algorithms/PVT/libs/rinex_printer.cc
@@ -112,7 +112,7 @@ Rinex_Printer::Rinex_Printer(int32_t conf_version,
     observationCode["GALILEO_E5_IQ"] = "8X";      // "8X" GALILEO E5 I+Q
     observationCode["GALILEO_E56_A"] = "6A";      // "6A" GALILEO E6 A
     observationCode["GALILEO_E56_B"] = "6B";      // "6B" GALILEO E6 B
-    observationCode["GALILEO_E56_B"] = "6C";      // "6C" GALILEO E6 C
+    observationCode["GALILEO_E56_C"] = "6C";      // "6C" GALILEO E6 C
     observationCode["GALILEO_E56_BC"] = "6X";     // "6X" GALILEO E6 B+C
     observationCode["GALILEO_E56_ABC"] = "6Z";    // "6Z" GALILEO E6 A+B+C
     observationCode["SBAS_L1_CA"] = "1C";         // "1C" SBAS L1 C/A
@@ -621,10 +621,22 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                             d_rinex_header_written = true;  // do not write header anymore
                         }
                     break;
+                case 100:  // Galileo E6B
+                    if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
+                        {
+                            const std::string gal_signal("E6");
+                            rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, gal_signal);
+                            rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
+                            output_navfilename.push_back(navGalfilename);
+                            log_rinex_nav(navGalFile, pvt_solver->galileo_ephemeris_map);
+                            d_rinex_header_written = true;  // do not write header anymore
+                        }
+                    break;
                 case 101:  // Galileo E1B + Galileo E6B
                     if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                         {
-                            rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time);
+                            const std::string gal_signal("1B E6");
+                            rinex_obs_header(obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, rx_time, gal_signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
                             log_rinex_nav(navGalFile, pvt_solver->galileo_ephemeris_map);
@@ -634,7 +646,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                 case 102:  // Galileo E5a + Galileo E6B
                     if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                         {
-                            const std::string signal("5X");
+                            const std::string signal("5X E6");
                             rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
@@ -645,7 +657,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                 case 103:  // Galileo E5b + Galileo E6B
                     if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                         {
-                            const std::string signal("7X");
+                            const std::string signal("7X E6");
                             rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
@@ -656,7 +668,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                 case 104:  // Galileo E1B + Galileo E5a + Galileo E6B
                     if ((galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend()))
                         {
-                            const std::string gal_signal("1B 5X");
+                            const std::string gal_signal("1B 5X E6");
                             rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, gal_signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
@@ -667,7 +679,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                 case 105:  // Galileo E1B + Galileo E5b + Galileo E6B
                     if ((galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend()))
                         {
-                            const std::string gal_signal("1B 7X");
+                            const std::string gal_signal("1B 7X E6");
                             rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, gal_signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
@@ -678,7 +690,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                 case 106:  // GPS L1 C/A + Galileo E1B + Galileo E6B
                     if ((galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend()) and (gps_ephemeris_iter != pvt_solver->gps_ephemeris_map.cend()))
                         {
-                            const std::string gal_signal("1B");
+                            const std::string gal_signal("1B E6");
                             rinex_obs_header(obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, rx_time, gal_signal);
                             rinex_nav_header(navMixFile, pvt_solver->gps_iono, pvt_solver->gps_utc_model, gps_ephemeris_iter->second, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navMixfilename);
@@ -1077,10 +1089,22 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                                         }
                                 }
                             break;
+                        case 100:  // Galileo E6B
+                            if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
+                                {
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "E6");
+                                }
+                            if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
+                                {
+                                    update_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
+                                    update_obs_header(obsFile, pvt_solver->galileo_utc_model);
+                                    d_rinex_header_updated = true;
+                                }
+                            break;
                         case 101:  // Galileo E1B + Galileo E6B
                             if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                                 {
-                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B");
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B E6");
                                 }
                             if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
                                 {
@@ -1092,7 +1116,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                         case 102:  // Galileo E5a + Galileo E6B
                             if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                                 {
-                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "5X");
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "5X E6");
                                 }
                             if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
                                 {
@@ -1104,7 +1128,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                         case 103:  // Galileo E5b + Galileo E6B
                             if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                                 {
-                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "5X");
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "7X E6");
                                 }
                             if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
                                 {
@@ -1116,7 +1140,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                         case 104:  // Galileo E1B + Galileo E5a + Galileo E6B
                             if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                                 {
-                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B 5X");
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B 5X E6");
                                 }
                             if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
                                 {
@@ -1128,7 +1152,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                         case 105:  // Galileo E1B + Galileo E5b + Galileo E6B
                             if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                                 {
-                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B 7X");
+                                    log_rinex_obs(obsFile, galileo_ephemeris_iter->second, rx_time, gnss_observables_map, "1B 7X E6");
                                 }
                             if (!d_rinex_header_updated and (pvt_solver->galileo_utc_model.A0 != 0))
                                 {
@@ -1317,6 +1341,7 @@ void Rinex_Printer::log_rinex_nav_gal_nav(int type_of_rx, const std::map<int32_t
         case 33:  // L1+E1+E5a
             log_rinex_nav(navMixFile, new_eph, new_gal_eph);
             break;
+        case 100:  // E6B
         case 101:  // E1B + E6B
         case 102:  // Galileo E5a + Galileo E6B
         case 103:  // Galileo E5b + Galileo E6B
@@ -3264,6 +3289,7 @@ void Rinex_Printer::rinex_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps
     out << line << '\n';
 }
 
+
 void Rinex_Printer::rinex_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glo_gnav_utc_model, const Beidou_Dnav_Iono& bds_dnav_iono, const Beidou_Dnav_Utc_Model& bds_dnav_utc_model) const
 {
     std::string line;
@@ -8747,7 +8773,14 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris&
 
     // -------- Line 1
     line = std::string(5, ' ');
-    line += "3.02";
+    if (bands.find("E6") != std::string::npos)
+        {
+            line += "3.05";
+        }
+    else
+        {
+            line += "3.02";
+        }
     line += std::string(11, ' ');
     line += Rinex_Printer::leftJustify("OBSERVATION DATA", 20);
     line += satelliteSystem.find("Galileo")->second;
@@ -8899,7 +8932,6 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris&
             number_of_observations = number_of_observations + 4;
         }
 
-    line.clear();
     signal_ = "7X";
     const std::size_t found_7X = bands.find(signal_);
     if (found_7X != std::string::npos)
@@ -8907,6 +8939,13 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris&
             number_of_observations = number_of_observations + 4;
         }
 
+    signal_ = "E6";
+    const std::size_t found_E6 = bands.find(signal_);
+    if (found_E6 != std::string::npos)
+        {
+            number_of_observations = number_of_observations + 4;
+        }
+
     line.clear();
 
     line += satelliteSystem.find("Galileo")->second;
@@ -8961,6 +9000,22 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out, const Galileo_Ephemeris&
             line += observationCode["GALILEO_E5b_IQ"];
         }
 
+    if (found_E6 != std::string::npos)
+        {
+            line += std::string(1, ' ');
+            line += observationType["PSEUDORANGE"];
+            line += observationCode["GALILEO_E56_B"];
+            line += std::string(1, ' ');
+            line += observationType["CARRIER_PHASE"];
+            line += observationCode["GALILEO_E56_B"];
+            line += std::string(1, ' ');
+            line += observationType["DOPPLER"];
+            line += observationCode["GALILEO_E56_B"];
+            line += std::string(1, ' ');
+            line += observationType["SIGNAL_STRENGTH"];
+            line += observationCode["GALILEO_E56_B"];
+        }
+
     line += std::string(60 - line.size(), ' ');
     line += Rinex_Printer::leftJustify("SYS / # / OBS TYPES", 20);
     Rinex_Printer::lengthCheck(line);
@@ -11586,6 +11641,7 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& ep
     std::map<int32_t, Gnss_Synchro> observablesE1B;
     std::map<int32_t, Gnss_Synchro> observablesE5A;
     std::map<int32_t, Gnss_Synchro> observablesE5B;
+    std::map<int32_t, Gnss_Synchro> observablesE6B;
     std::map<int32_t, Gnss_Synchro>::const_iterator observables_iter;
 
     for (observables_iter = observables.cbegin();
@@ -11606,10 +11662,15 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& ep
                 {
                     observablesE5B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
                 }
+            if ((system_ == "E") && (sig_ == "E6"))
+                {
+                    observablesE6B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
+                }
         }
     const std::size_t found_1B = galileo_bands.find("1B");
     const std::size_t found_E5a = galileo_bands.find("5X");
     const std::size_t found_E5b = galileo_bands.find("7X");
+    const std::size_t found_E6b = galileo_bands.find("E6");
 
     std::multimap<uint32_t, Gnss_Synchro> total_map;
     std::set<uint32_t> available_prns;
@@ -11706,6 +11767,23 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& ep
                     total_map.insert(std::pair<uint32_t, Gnss_Synchro>(prn_, observables_iter->second));
                 }
         }
+
+    if (found_E6b != std::string::npos)
+        {
+            for (observables_iter = observablesE6B.cbegin();
+                 observables_iter != observablesE6B.cend();
+                 observables_iter++)
+                {
+                    const uint32_t prn_ = observables_iter->second.PRN;
+                    total_map.insert(std::pair<uint32_t, Gnss_Synchro>(prn_, observables_iter->second));
+                    it = available_prns.find(prn_);
+                    if (it == available_prns.end())
+                        {
+                            available_prns.insert(prn_);
+                        }
+                }
+        }
+
     const int32_t numSatellitesObserved = available_prns.size();
     line += Rinex_Printer::rightJustify(std::to_string(numSatellitesObserved), 3);
     // Receiver clock offset (optional)
@@ -11834,6 +11912,7 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
     std::map<int32_t, Gnss_Synchro> observablesE1B;
     std::map<int32_t, Gnss_Synchro> observablesE5A;
     std::map<int32_t, Gnss_Synchro> observablesE5B;
+    std::map<int32_t, Gnss_Synchro> observablesE6B;
     std::map<int32_t, Gnss_Synchro>::const_iterator observables_iter;
 
     for (observables_iter = observables.cbegin();
@@ -11854,6 +11933,10 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
                 {
                     observablesE5B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
                 }
+            if ((system_ == "E") && (sig_ == "E6"))
+                {
+                    observablesE6B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
+                }
             if ((system_ == "G") && (sig_ == "1C"))
                 {
                     observablesG1C.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
@@ -11902,6 +11985,19 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
                 }
         }
 
+    for (observables_iter = observablesE6B.cbegin();
+         observables_iter != observablesE6B.cend();
+         observables_iter++)
+        {
+            const uint32_t prn_ = observables_iter->second.PRN;
+            total_gal_map.insert(std::pair<uint32_t, Gnss_Synchro>(prn_, observables_iter->second));
+            it = available_gal_prns.find(prn_);
+            if (it == available_gal_prns.end())
+                {
+                    available_gal_prns.insert(prn_);
+                }
+        }
+
     const int32_t numGalSatellitesObserved = available_gal_prns.size();
     const int32_t numGpsSatellitesObserved = observablesG1C.size();
     const int32_t numSatellitesObserved = numGalSatellitesObserved + numGpsSatellitesObserved;
@@ -12107,6 +12203,7 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& e
     std::map<int32_t, Gnss_Synchro> observablesE1B;
     std::map<int32_t, Gnss_Synchro> observablesE5A;
     std::map<int32_t, Gnss_Synchro> observablesE5B;
+    std::map<int32_t, Gnss_Synchro> observablesE6B;
     std::map<int32_t, Gnss_Synchro>::const_iterator observables_iter;
 
     for (observables_iter = observables.cbegin();
@@ -12127,6 +12224,10 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& e
                 {
                     observablesE5B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
                 }
+            if ((system_ == "E") && (sig_ == "E6"))
+                {
+                    observablesE6B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
+                }
             if ((system_ == "G") && (sig_ == "2S"))
                 {
                     observablesG2S.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
@@ -12181,6 +12282,19 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& e
                 }
         }
 
+    for (observables_iter = observablesE6B.cbegin();
+         observables_iter != observablesE6B.cend();
+         observables_iter++)
+        {
+            const uint32_t prn_ = observables_iter->second.PRN;
+            total_gal_map.insert(std::pair<uint32_t, Gnss_Synchro>(prn_, observables_iter->second));
+            it = available_gal_prns.find(prn_);
+            if (it == available_gal_prns.end())
+                {
+                    available_gal_prns.insert(prn_);
+                }
+        }
+
     for (observables_iter = observablesG2S.cbegin();
          observables_iter != observablesG2S.cend();
          observables_iter++)
@@ -12405,6 +12519,7 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
     std::map<int32_t, Gnss_Synchro> observablesE1B;
     std::map<int32_t, Gnss_Synchro> observablesE5A;
     std::map<int32_t, Gnss_Synchro> observablesE5B;
+    std::map<int32_t, Gnss_Synchro> observablesE6B;
     std::map<int32_t, Gnss_Synchro>::const_iterator observables_iter;
 
     for (observables_iter = observables.cbegin();
@@ -12425,6 +12540,10 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
                 {
                     observablesE5B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
                 }
+            if ((system_ == "E") && (sig_ == "E6"))
+                {
+                    observablesE6B.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
+                }
             if ((system_ == "G") && (sig_ == "2S"))
                 {
                     observablesG2S.insert(std::pair<int32_t, Gnss_Synchro>(observables_iter->first, observables_iter->second));
@@ -12483,6 +12602,19 @@ void Rinex_Printer::log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_ep
                 }
         }
 
+    for (observables_iter = observablesE6B.cbegin();
+         observables_iter != observablesE6B.cend();
+         observables_iter++)
+        {
+            const uint32_t prn_ = observables_iter->second.PRN;
+            total_gal_map.insert(std::pair<uint32_t, Gnss_Synchro>(prn_, observables_iter->second));
+            it = available_gal_prns.find(prn_);
+            if (it == available_gal_prns.end())
+                {
+                    available_gal_prns.insert(prn_);
+                }
+        }
+
     for (observables_iter = observablesG1C.cbegin();
          observables_iter != observablesG1C.cend();
          observables_iter++)

From b347bcbb79500e8a92d02bfc129b862f1257ea0b Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Mon, 30 May 2022 14:42:57 +0200
Subject: [PATCH 11/31] Fix Gal E1/E6 RINEX reporting

---
 src/algorithms/PVT/libs/rinex_printer.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/algorithms/PVT/libs/rinex_printer.cc b/src/algorithms/PVT/libs/rinex_printer.cc
index af29e783b..4a5e8c20c 100644
--- a/src/algorithms/PVT/libs/rinex_printer.cc
+++ b/src/algorithms/PVT/libs/rinex_printer.cc
@@ -636,7 +636,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver, cons
                     if (galileo_ephemeris_iter != pvt_solver->galileo_ephemeris_map.cend())
                         {
                             const std::string gal_signal("1B E6");
-                            rinex_obs_header(obsFile, gps_ephemeris_iter->second, galileo_ephemeris_iter->second, rx_time, gal_signal);
+                            rinex_obs_header(obsFile, galileo_ephemeris_iter->second, rx_time, gal_signal);
                             rinex_nav_header(navGalFile, pvt_solver->galileo_iono, pvt_solver->galileo_utc_model);
                             output_navfilename.push_back(navGalfilename);
                             log_rinex_nav(navGalFile, pvt_solver->galileo_ephemeris_map);

From e074883f06f11fad62d2d9c084461cedfb869c59 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 7 Jun 2022 09:19:29 +0200
Subject: [PATCH 12/31] Adding a native AD936x custom signal source, still not
 usable

---
 CMakeLists.txt                                |   5 +
 .../gnuradio_blocks/CMakeLists.txt            |   4 +
 .../gnuradio_blocks/ad936x_iio_source.cc      |  96 +++++++++++++++++
 .../gnuradio_blocks/ad936x_iio_source.h       | 100 ++++++++++++++++++
 4 files changed, 205 insertions(+)
 create mode 100644 src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
 create mode 100644 src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0a813ad30..a96d63e93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,8 @@ option(ENABLE_FMCOMMS2 "Enable the use of FMCOMMS4-EBZ + ZedBoard hardware, requ
 
 option(ENABLE_PLUTOSDR "Enable the use of ADALM-PLUTO Evaluation Boards (Analog Devices Inc.), requires gr-iio" OFF)
 
+option(ENABLE_AD936X_SDR "Enable the use of AD936X front-ends using libiio, requires libiio" OFF)
+
 option(ENABLE_AD9361 "Enable the use of AD9361 direct to FPGA hardware, requires libiio" OFF)
 
 option(ENABLE_RAW_UDP "Enable the use of high-optimized custom UDP packet sample source, requires libpcap" OFF)
@@ -3055,6 +3057,7 @@ set_package_properties(LIBAD9361 PROPERTIES
 )
 if(NOT LIBAD9361_FOUND)
     set(ENABLE_AD9361 OFF)
+    set(ENABLE_AD936X_SDR OFF)
     set(ENABLE_FMCOMMS2 OFF)
 endif()
 if(ENABLE_PLUTOSDR OR ENABLE_FMCOMMS2)
@@ -3094,6 +3097,7 @@ if(ENABLE_AD9361 OR ENABLE_FMCOMMS2)
         message(STATUS " * gnuradio-iio from https://github.com/analogdevicesinc/gr-iio")
         if(ENABLE_PACKAGING)
             set(ENABLE_AD9361 OFF)
+            set(ENABLE_AD936X_SDR OFF)
             set(ENABLE_FMCOMMS2 OFF)
         else()
             message(FATAL_ERROR "libiio is required for building gnss-sdr with -DENABLE_AD9361=ON.")
@@ -3337,6 +3341,7 @@ add_feature_info(ENABLE_LIMESDR ENABLE_LIMESDR "Enables Limesdr_Signal_Source. R
 add_feature_info(ENABLE_FMCOMMS2 ENABLE_FMCOMMS2 "Enables Fmcomms2_Signal_Source for FMCOMMS2/3/4 devices. Requires gr-iio and libad9361-dev.")
 add_feature_info(ENABLE_PLUTOSDR ENABLE_PLUTOSDR "Enables Plutosdr_Signal_Source for using ADALM-PLUTO boards. Requires gr-iio.")
 add_feature_info(ENABLE_AD9361 ENABLE_AD9361 "Enables Ad9361_Fpga_Signal_Source for devices with the AD9361 chipset. Requires libiio and libad9361-dev.")
+add_feature_info(ENABLE_AD936X_SDR ENABLE_AD9361 "Enables Ad936x_Iio_Signal_Source to access AD936X front-ends using libiio. Requires libiio and libad9361-dev.")
 add_feature_info(ENABLE_RAW_UDP ENABLE_RAW_UDP "Enables Custom_UDP_Signal_Source for custom UDP packet sample source. Requires libpcap.")
 add_feature_info(ENABLE_FLEXIBAND ENABLE_FLEXIBAND "Enables Flexiband_Signal_Source for using Teleorbit's Flexiband RF front-end. Requires gr-teleorbit.")
 add_feature_info(ENABLE_ARRAY ENABLE_ARRAY "Enables Raw_Array_Signal_Source and Array_Signal_Conditioner for using CTTC's antenna array. Requires gr-dbfcttc.")
diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
index 96fa168c2..6172102bc 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
+++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
@@ -12,6 +12,10 @@ if(ENABLE_RAW_UDP AND PCAP_FOUND)
     set(OPT_DRIVER_HEADERS gr_complex_ip_packet_source.h)
 endif()
 
+if(ENABLE_AD936X_SDR)
+    set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} gr_complex_ip_packet_source.cc)
+    set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} gr_complex_ip_packet_source.h)
+endif()
 
 set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES
     fifo_reader.cc
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
new file mode 100644
index 000000000..efee6ad49
--- /dev/null
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -0,0 +1,96 @@
+/*!
+ * \file ad936x_iio_source.cc
+ *
+ * \brief Unpacks capture files in the LabSat 2 (ls2), LabSat 3 (ls3), or LabSat
+ * 3 Wideband (LS3W) formats.
+ * \author Javier Arribas jarribas (at) cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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 "ad936x_iio_source.h"
+#include "INIReader.h"
+#include "command_event.h"
+#include "gnss_sdr_make_unique.h"
+#include <gnuradio/io_signature.h>
+#include <algorithm>
+#include <array>
+#include <bitset>
+#include <exception>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+
+ad936x_iio_source_sptr ad936x_iio_make_source_sptr(Concurrent_Queue<pmt::pmt_t> *queue,
+    std::string pluto_device_uri,
+    std::string board_type,
+    long long bandwidth_,
+    long long sample_rate_,
+    std::vector<std::string> ch_list,
+    std::vector<std::string> ch_gain_mode,
+    std::vector<double> ch_gain_db,
+    std::vector<long int> ch_freq_hz,
+    int ch_sample_size,
+    int ch_sample_bits_shift)
+{
+    return ad936x_iio_source_sptr(new ad936x_iio_source(*queue,
+        pluto_device_uri,
+        board_type,
+        bandwidth_,
+        sample_rate_,
+        ch_list,
+        ch_gain_mode,
+        ch_gain_db,
+        ch_freq_hz,
+        ch_sample_size,
+        ch_sample_bits_shift));
+}
+
+
+ad936x_iio_source::ad936x_iio_source(Concurrent_Queue<pmt::pmt_t> *queue,
+    std::string pluto_device_uri,
+    std::string board_type,
+    long long bandwidth_,
+    long long sample_rate_,
+    std::vector<std::string> ch_list,
+    std::vector<std::string> ch_gain_mode,
+    std::vector<double> ch_gain_db,
+    std::vector<long int> ch_freq_hz,
+    int ch_sample_size,
+    int ch_sample_bits_shift) : gr::block("ad936x_iio_source",
+                                    gr::io_signature::make(0, 0, 0),
+                                    gr::io_signature::make(1, 3, sizeof(gr_complex)))
+{
+}
+
+
+ad936x_iio_source::~ad936x_iio_source()
+{
+}
+
+
+int ad936x_iio_source::general_work(int noutput_items,
+    __attribute__((unused)) gr_vector_int &ninput_items,
+    __attribute__((unused)) gr_vector_const_void_star &input_items,
+    gr_vector_void_star &output_items)
+{
+    std::vector<gr_complex *> out;
+    for (auto &output_item : output_items)
+        {
+            out.push_back(reinterpret_cast<gr_complex *>(output_item));
+        }
+    std::cout << "Warning!!\n";
+    return 0;
+}
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
new file mode 100644
index 000000000..aec4a3c56
--- /dev/null
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
@@ -0,0 +1,100 @@
+/*!
+ * \file ad936x_iio_source.h
+ *
+ * \brief signal source to receive samples from the AD936x FE family over libiio, including special custom functionalities in FPGA firmware.
+ * \author Javier Arribas jarribas (at) cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#ifndef GNSS_SDR_AD9361_IIO_SOURCE_H
+#define GNSS_SDR_AD9361_IIO_SOURCE_H
+
+#include "concurrent_queue.h"
+#include "gnss_block_interface.h"
+#include <gnuradio/block.h>
+#include <iio.h>
+#include <pmt/pmt.h>
+#include <ad9361.h>  //multichip sync and high level functions
+#include <cstddef>
+#include <cstdint>
+#include <fstream>
+#include <string>
+#include <vector>
+
+/** \addtogroup Signal_Source
+ * \{ */
+/** \addtogroup Signal_Source_gnuradio_blocks
+ * \{ */
+
+
+class ad936x_iio_source;
+
+using ad936x_iio_source_sptr = gnss_shared_ptr<ad936x_iio_source>;
+
+ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
+    Concurrent_Queue<pmt::pmt_t> *queue,
+    std::string pluto_device_uri,
+    std::string board_type,
+    long long bandwidth_,
+    long long sample_rate_,
+    std::vector<std::string> ch_list,
+    std::vector<std::string> ch_gain_mode,
+    std::vector<double> ch_gain_db,
+    std::vector<long int> ch_freq_hz,
+    int ch_sample_size,
+    int ch_sample_bits_shift);
+
+/*!
+ * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband
+ * formats to gr_complex
+ */
+class ad936x_iio_source : public gr::block
+{
+public:
+    ~ad936x_iio_source();
+
+    int general_work(int noutput_items,
+        gr_vector_int &ninput_items,
+        gr_vector_const_void_star &input_items,
+        gr_vector_void_star &output_items);
+
+private:
+    friend ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
+        Concurrent_Queue<pmt::pmt_t> *queue,
+        std::string pluto_device_uri,
+        std::string board_type,
+        long long bandwidth_,
+        long long sample_rate_,
+        std::vector<std::string> ch_list,
+        std::vector<std::string> ch_gain_mode,
+        std::vector<double> ch_gain_db,
+        std::vector<long int> ch_freq_hz,
+        int ch_sample_size,
+        int ch_sample_bits_shift);
+
+    ad936x_iio_source(Concurrent_Queue<pmt::pmt_t> *queue,
+        std::string pluto_device_uri,
+        std::string board_type,
+        long long bandwidth_,
+        long long sample_rate_,
+        std::vector<std::string> ch_list,
+        std::vector<std::string> ch_gain_mode,
+        std::vector<double> ch_gain_db,
+        std::vector<long int> ch_freq_hz,
+        int ch_sample_size,
+        int ch_sample_bits_shift);
+};
+
+
+/** \} */
+/** \} */
+#endif  // GNSS_SDR_AD9361_IIO_SOURCE_H

From 0a346c301767a593cc1bed73b79a38ee1bbc9a80 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Wed, 8 Jun 2022 10:21:05 +0200
Subject: [PATCH 13/31] Fix segmentation fault in file_timestamp_signal_source

---
 .../adapters/file_timestamp_signal_source.cc  | 33 +++++++++++++++++--
 1 file changed, 30 insertions(+), 3 deletions(-)

diff --git a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
index 32d27ac19..8e17180f8 100644
--- a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
@@ -50,21 +50,48 @@ gnss_shared_ptr<gr::block> FileTimestampSignalSource::source() const { return ti
 
 void FileTimestampSignalSource::create_file_source_hook()
 {
-    if (is_complex() == false)
+    int source_items_to_samples = 1;
+    bool is_complex = false;
+
+    if (item_type().compare("ibyte") == 0)
         {
+            source_items_to_samples = 1;
+        }
+    else if (item_type().compare("byte") == 0)
+        {
+            source_items_to_samples = 1;
+        }
+    else if (item_type().compare("short") == 0)
+        {
+            source_items_to_samples = 1;
+        }
+    else if (item_type().compare("ishort") == 0)
+        {
+            source_items_to_samples = 1;
+        }
+    else if (item_type().compare("gr_complex") == 0)
+        {
+            source_items_to_samples = 1;
+            is_complex = true;
+        }
+
+    if (is_complex == false)
+        {
+            std::cout << "A : " << std::get<0>(itemTypeToSize()) << "\n";
             timestamp_block_ = gnss_sdr_make_Timestamp(
                 std::get<0>(itemTypeToSize()),
                 timestamp_file_,
                 timestamp_clock_offset_ms_,
-                source_item_size() * 2);
+                source_items_to_samples * 2);
         }
     else
         {
+            std::cout << "B : " << std::get<0>(itemTypeToSize()) << "\n";
             timestamp_block_ = gnss_sdr_make_Timestamp(
                 std::get<0>(itemTypeToSize()),
                 timestamp_file_,
                 timestamp_clock_offset_ms_,
-                source_item_size());
+                source_items_to_samples);
         }
     DLOG(INFO) << "timestamp_block_(" << timestamp_block_->unique_id() << ")";
 }

From a7147702bc2d2ddd9c61b1e90a8f991886bade68 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Thu, 30 Jun 2022 10:38:29 +0200
Subject: [PATCH 14/31] Bug fix: PVT does not resolve position anymore after a
 loss of samples event

---
 .../PVT/gnuradio_blocks/rtklib_pvt_gs.cc      | 25 +++++++++++++++----
 .../PVT/gnuradio_blocks/rtklib_pvt_gs.h       |  3 ++-
 .../gnuradio_blocks/hybrid_observables_gs.cc  | 20 ++++++++++++++-
 .../gnuradio_blocks/hybrid_observables_gs.h   |  1 +
 4 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index 9ec599576..6a4f82616 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -166,11 +166,13 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
       d_flag_monitor_pvt_enabled(conf_.monitor_enabled),
       d_flag_monitor_ephemeris_enabled(conf_.monitor_ephemeris_enabled),
       d_show_local_time_zone(conf_.show_local_time_zone),
-      d_waiting_obs_block_rx_clock_offset_correction_msg(false),
+      d_timestamp_rx_clock_offset_correction_msg_ms(0LL),
       d_enable_rx_clock_correction(conf_.enable_rx_clock_correction),
       d_an_printer_enabled(conf_.an_output_enabled),
       d_log_timetag(conf_.log_source_timetag)
 {
+    //debug
+    d_pvt_errors_counter = 0;
     // Send feedback message to observables block with the receiver clock offset
     this->message_port_register_out(pmt::mp("pvt_to_observables"));
     // Experimental: VLT commands from PVT to tracking channels
@@ -2168,18 +2170,18 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
 
                             if (fabs(Rx_clock_offset_s) * 1000.0 > d_max_obs_block_rx_clock_offset_ms)
                                 {
-                                    if (!d_waiting_obs_block_rx_clock_offset_correction_msg)
+                                    //check if the message was just sent to not duplicate it while it is being applied
+                                    if ((d_local_counter_ms - d_timestamp_rx_clock_offset_correction_msg_ms) > 1000)
                                         {
                                             this->message_port_pub(pmt::mp("pvt_to_observables"), pmt::make_any(Rx_clock_offset_s));
-                                            d_waiting_obs_block_rx_clock_offset_correction_msg = true;
-                                            LOG(INFO) << "Sent clock offset correction to observables: " << Rx_clock_offset_s << "[s]";
+                                            d_timestamp_rx_clock_offset_correction_msg_ms = d_local_counter_ms;
+                                            LOG(INFO) << "PVT: Sent clock offset correction to observables: " << Rx_clock_offset_s << "[s]";
                                         }
                                 }
                             else
                                 {
                                     if (d_enable_rx_clock_correction == true)
                                         {
-                                            d_waiting_obs_block_rx_clock_offset_correction_msg = false;
                                             d_gnss_observables_map_t0 = d_gnss_observables_map_t1;
                                             apply_rx_clock_offset(d_gnss_observables_map, Rx_clock_offset_s);
                                             d_gnss_observables_map_t1 = d_gnss_observables_map;
@@ -2209,6 +2211,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
                                         }
                                     else
                                         {
+                                            d_pvt_errors_counter = 0;
                                             d_rx_time = d_gnss_observables_map.begin()->second.RX_time;
                                             current_RX_time_ms = static_cast<uint32_t>(d_rx_time * 1000.0);
                                             if (current_RX_time_ms % d_output_rate_ms == 0)
@@ -2221,6 +2224,18 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
                                         }
                                 }
                         }
+                    else
+                        {
+                            //sanity check: If the PVT solver is getting 100 consecutive errors, send a reset command to observables block
+                            d_pvt_errors_counter++;
+                            if (d_pvt_errors_counter >= 100)
+                                {
+                                    int command = 1;
+                                    this->message_port_pub(pmt::mp("pvt_to_observables"), pmt::make_any(command));
+                                    LOG(INFO) << "PVT: Number of consecutive position solver error reached, Sent reset to observables.";
+                                    d_pvt_errors_counter = 0;
+                                }
+                        }
 
                     // compute on the fly PVT solution
                     if (flag_compute_pvt_output == true)
diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
index bfd53db63..c9a627e72 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
@@ -282,7 +282,8 @@ private:
     bool d_flag_monitor_pvt_enabled;
     bool d_flag_monitor_ephemeris_enabled;
     bool d_show_local_time_zone;
-    bool d_waiting_obs_block_rx_clock_offset_correction_msg;
+    uint64_t d_timestamp_rx_clock_offset_correction_msg_ms;
+    uint32_t d_pvt_errors_counter;
     bool d_enable_rx_clock_correction;
     bool d_enable_has_messages;
     bool d_an_printer_enabled;
diff --git a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc
index 82b86fc9e..0778dd5c3 100644
--- a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc
+++ b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc
@@ -226,6 +226,25 @@ void hybrid_observables_gs::msg_handler_pvt_to_observables(const pmt::pmt_t &msg
 
                     LOG(INFO) << "Corrected new RX Time offset: " << static_cast<int>(round(new_rx_clock_offset_s * 1000.0)) << "[ms]";
                 }
+            if (pmt::any_ref(msg).type().hash_code() == d_int_type_hash_code)
+                {
+                    const auto command_from_pvt = wht::any_cast<int>(pmt::any_ref(msg));
+                    switch (command_from_pvt)
+                        {
+                        case 1:  //reset TOW
+                            d_T_rx_TOW_ms = 0;
+                            d_last_rx_clock_round20ms_error = 0;
+                            d_T_rx_TOW_set = false;
+                            for (uint32_t n = 0; n < d_nchannels_out; n++)
+                                {
+                                    d_gnss_synchro_history->clear(n);
+                                }
+                            LOG(INFO) << "Received reset observables TOW command from PVT";
+                            break;
+                        default:
+                            break;
+                        }
+                }
         }
     catch (const wht::bad_any_cast &e)
         {
@@ -786,7 +805,6 @@ int hybrid_observables_gs::general_work(int noutput_items __attribute__((unused)
                         }
                     epoch_data[n] = interpolated_gnss_synchro;
                 }
-
             if (d_T_rx_TOW_set)
                 {
                     update_TOW(epoch_data);
diff --git a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.h b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.h
index a6966b5d6..5c4863c7c 100644
--- a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.h
+++ b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.h
@@ -70,6 +70,7 @@ private:
     explicit hybrid_observables_gs(const Obs_Conf& conf_);
 
     const size_t d_double_type_hash_code = typeid(double).hash_code();
+    const size_t d_int_type_hash_code = typeid(int).hash_code();
 
     void msg_handler_pvt_to_observables(const pmt::pmt_t& msg);
     double compute_T_rx_s(const Gnss_Synchro& a) const;

From 5292f0d8fc36384afaad98bb4fba86a6177aeb12 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Thu, 30 Jun 2022 12:31:25 +0200
Subject: [PATCH 15/31] Improve PVT error detection

---
 src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index bb989eb96..dca10600a 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -2128,6 +2128,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
                     // #### solve PVT and store the corrected observable set
                     if (d_internal_pvt_solver->get_PVT(d_gnss_observables_map, false))
                         {
+                            d_pvt_errors_counter = 0;  // Reset consecutive PVT error counter
                             const double Rx_clock_offset_s = d_internal_pvt_solver->get_time_offset_s();
 
                             // **************** time tags ****************
@@ -2211,7 +2212,6 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
                                         }
                                     else
                                         {
-                                            d_pvt_errors_counter = 0;
                                             d_rx_time = d_gnss_observables_map.begin()->second.RX_time;
                                             current_RX_time_ms = static_cast<uint32_t>(d_rx_time * 1000.0);
                                             if (current_RX_time_ms % d_output_rate_ms == 0)

From 6311530cb4a8072c640cb5d6b200d65cd37958c3 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Sun, 14 Aug 2022 12:10:59 +0200
Subject: [PATCH 16/31] Adding new Ad936x custom signal source, initial commit,
 experimental

---
 .../signal_source/adapters/CMakeLists.txt     |    6 +
 .../adapters/ad936x_custom_signal_source.cc   |  325 +++++
 .../adapters/ad936x_custom_signal_source.h    |  118 ++
 .../gnuradio_blocks/CMakeLists.txt            |    7 +
 .../gnuradio_blocks/ad936x_iio_source.cc      |  262 +++-
 .../gnuradio_blocks/ad936x_iio_source.h       |  115 +-
 .../signal_source/libs/CMakeLists.txt         |   18 +-
 .../signal_source/libs/ad936x_iio_custom.cc   | 1195 +++++++++++++++++
 .../signal_source/libs/ad936x_iio_custom.h    |  144 ++
 .../signal_source/libs/ad936x_iio_samples.cc  |   26 +
 .../signal_source/libs/ad936x_iio_samples.h   |   41 +
 .../signal_source/libs/pps_samplestamp.h      |   20 +
 src/algorithms/signal_source/libs/ppstcprx.cc |  154 +++
 src/algorithms/signal_source/libs/ppstcprx.h  |   34 +
 src/core/receiver/concurrent_queue.h          |   12 +
 src/core/receiver/gnss_block_factory.cc       |    7 +
 16 files changed, 2405 insertions(+), 79 deletions(-)
 create mode 100644 src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
 create mode 100644 src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
 create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_custom.cc
 create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_custom.h
 create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_samples.cc
 create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_samples.h
 create mode 100644 src/algorithms/signal_source/libs/pps_samplestamp.h
 create mode 100644 src/algorithms/signal_source/libs/ppstcprx.cc
 create mode 100644 src/algorithms/signal_source/libs/ppstcprx.h

diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt
index e99748358..40e72049c 100644
--- a/src/algorithms/signal_source/adapters/CMakeLists.txt
+++ b/src/algorithms/signal_source/adapters/CMakeLists.txt
@@ -21,6 +21,12 @@ if(ENABLE_PLUTOSDR)
     ##############################################
     set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} plutosdr_signal_source.cc)
     set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} plutosdr_signal_source.h)
+    
+    ##############################################
+    # CUSTOM AD936X IIO SOURCE
+    ##############################################
+    set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} ad936x_custom_signal_source.cc)
+    set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} ad936x_custom_signal_source.h)
 endif()
 
 
diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
new file mode 100644
index 000000000..91f11b410
--- /dev/null
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
@@ -0,0 +1,325 @@
+/*!
+ * \file ad936x_custom_signal_source.cc
+ * \brief A direct IIO custom front-end gnss-sdr signal source for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+
+#include "ad936x_custom_signal_source.h"
+#include "configuration_interface.h"
+#include "gnss_frequencies.h"
+#include "gnss_sdr_string_literals.h"
+#include "gnss_sdr_valve.h"
+#include <boost/exception/diagnostic_information.hpp>
+#include <glog/logging.h>
+#include <gnuradio/blocks/file_sink.h>
+#include <iostream>
+
+using namespace std::string_literals;
+
+Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface* configuration,
+    const std::string& role,
+    unsigned int in_stream,
+    unsigned int out_stream,
+    Concurrent_Queue<pmt::pmt_t>* queue __attribute__((unused)))
+    : SignalSourceBase(configuration, role, "Ad936x_Custom_Signal_Source"s),
+      in_stream_(in_stream),
+      out_stream_(out_stream),
+      item_type_(configuration->property(role + ".item_type", std::string("gr_complex"))),
+      samples_(configuration->property(role + ".samples", int64_t(0))),
+      dump_(configuration->property(role + ".dump", false)),
+      dump_filename_(configuration->property(role + ".dump_filename", std::string("./data/signal_source.dat"))),
+      pluto_uri_(configuration->property(role + ".pluto_uri", std::string("local"))),
+      board_type_(configuration->property(role + ".board_type", std::string("single_ad9361"))),
+      sample_rate_(configuration->property(role + ".sampling_frequency", 4.0e6)),
+      bandwidth_(configuration->property(role + ".bandwidth", configuration->property(role + ".sampling_frequency", 4.0e6) / 1.1)),
+      freq_(configuration->property(role + ".freq", FREQ1)),
+      freq_2ch(configuration->property(role + ".freq_2ch", FREQ1)),
+      rf_port_select_(configuration->property(role + ".rf_port_select", std::string("A_BALANCED"))),
+      rf_filter(configuration->property(role + ".rf_filter", std::string("none"))),
+      gain_mode_rx0_(configuration->property(role + ".gain_mode_rx0", std::string("slow_attack"))),
+      gain_mode_rx1_(configuration->property(role + ".gain_mode_rx1", std::string("slow_attack"))),
+      rf_gain_rx0_(configuration->property(role + ".gain_rx0", 40.0)),
+      rf_gain_rx1_(configuration->property(role + ".gain_rx1", 40.0)),
+      enable_ch0(configuration->property(role + ".enable_ch0", true)),
+      enable_ch1(configuration->property(role + ".enable_ch1", false)),
+      PPS_mode_(configuration->property(role + ".PPS_mode", false)),
+      fe_ip_(configuration->property(role + ".fe_ip", std::string("192.168.2.1"))),
+      fe_ctlport_(configuration->property(role + ".fe_ctlport", int32_t(10000))),
+      ssize_(configuration->property(role + ".ssize", int32_t(16))),
+      bshift_(configuration->property(role + ".bshift", int64_t(0))),
+      spattern_(configuration->property(role + ".spattern", false)),
+      inverted_spectrum_ch0_(configuration->property(role + ".inverted_spectrum_ch0", false)),
+      inverted_spectrum_ch1_(configuration->property(role + ".inverted_spectrum_ch1", false))
+
+
+{
+    if (item_type_ == "gr_complex")
+        {
+            item_size_ = sizeof(gr_complex);
+            // 1. Make the driver instance
+            bool customsamplesize = false;
+            if (ssize_ != 16 or spattern_ == true) customsamplesize = true;
+
+            ad936x_iio_source = ad936x_iio_make_source_sptr(
+                pluto_uri_,
+                board_type_,
+                bandwidth_,
+                sample_rate_,
+                freq_,
+                rf_port_select_,
+                rf_filter,
+                gain_mode_rx0_,
+                gain_mode_rx1_,
+                rf_gain_rx0_,
+                rf_gain_rx1_,
+                enable_ch0,
+                enable_ch1,
+                freq_2ch,
+                PPS_mode_,
+                customsamplesize,
+                fe_ip_,
+                fe_ctlport_,
+                ssize_,
+                bshift_,
+                spattern_);
+
+            n_channels = 1;
+            if (enable_ch0 == true and enable_ch1 == true)
+                {
+                    n_channels = 2;
+                }
+
+            for (int n = 0; n < n_channels; n++)
+                {
+                    if (ssize_ == 16)
+                        {
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make());
+                        }
+                    else if (ssize_ == 8)
+                        {
+                            gr_interleaved_char_to_complex_.push_back(gr::blocks::interleaved_char_to_complex::make());
+                        }
+                    else if (ssize_ == 4)
+                        {
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false));
+                            unpack_byte_fourbits.push_back(make_unpack_byte_4bit_samples());
+                        }
+                    else if (ssize_ == 2)
+                        {
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false));
+                            unpack_byte_twobits.push_back(make_unpack_byte_2bit_cpx_samples());
+                        }
+                }
+        }
+    else
+        {
+            LOG(ERROR) << item_type_ << " unrecognized item type";
+            exit(1);
+        }
+
+
+    if (dump_)
+        {
+            for (int n = 0; n < n_channels; n++)
+                {
+                    DLOG(INFO) << "Dumping output into file " << (dump_filename_ + "c_h" + std::to_string(n) + ".bin");
+                    sink_.emplace_back(gr::blocks::file_sink::make(item_size_, (dump_filename_ + "_ch" + std::to_string(n) + ".bin").c_str()));
+                }
+        }
+
+    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 Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
+{
+    for (int n = 0; n < n_channels; n++)
+        {
+            if (ssize_ == 16)
+                {
+                    top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 8)
+                {
+                    top_block->connect(ad936x_iio_source, n, gr_interleaved_char_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 4)
+                {
+                    top_block->connect(ad936x_iio_source, n, unpack_byte_fourbits.at(n), 0);
+                    top_block->connect(unpack_byte_fourbits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 2)
+                {
+                    top_block->connect(ad936x_iio_source, n, unpack_byte_twobits.at(n), 0);
+                    top_block->connect(unpack_byte_twobits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else
+                {
+                    top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+        }
+}
+
+
+void Ad936xCustomSignalSource::disconnect(gr::top_block_sptr top_block)
+{
+    for (int n = 0; n < n_channels; n++)
+        {
+            if (ssize_ == 16)
+                {
+                    top_block->disconnect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 8)
+                {
+                    top_block->disconnect(ad936x_iio_source, n, gr_interleaved_char_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->disconnect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 4)
+                {
+                    top_block->disconnect(ad936x_iio_source, n, unpack_byte_fourbits.at(n), 0);
+                    top_block->disconnect(unpack_byte_fourbits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else if (ssize_ == 2)
+                {
+                    top_block->disconnect(ad936x_iio_source, n, unpack_byte_twobits.at(n), 0);
+                    top_block->disconnect(unpack_byte_twobits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+            else
+                {
+                    top_block->disconnect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
+                    if (dump_)
+                        {
+                            top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
+                        }
+                }
+        }
+}
+
+
+gr::basic_block_sptr Ad936xCustomSignalSource::get_left_block()
+{
+    LOG(WARNING) << "Trying to get signal source left block.";
+    return {};
+}
+
+
+gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block()
+{
+    if (ssize_ == 16)
+        {
+            return gr_interleaved_short_to_complex_.at(0);
+        }
+    else if (ssize_ == 8)
+        {
+            return gr_interleaved_char_to_complex_.at(0);
+        }
+    else if (ssize_ == 4)
+        {
+            return gr_interleaved_short_to_complex_.at(0);
+        }
+    else if (ssize_ == 2)
+        {
+            return gr_interleaved_short_to_complex_.at(0);
+        }
+    else
+        {
+            return gr_interleaved_short_to_complex_.at(0);
+        }
+}
+
+gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block(int RF_channel)
+{
+    if (ssize_ == 16)
+        {
+            return gr_interleaved_short_to_complex_.at(RF_channel);
+        }
+    else if (ssize_ == 8)
+        {
+            return gr_interleaved_char_to_complex_.at(RF_channel);
+        }
+    else if (ssize_ == 4)
+        {
+            return gr_interleaved_short_to_complex_.at(RF_channel);
+        }
+    else if (ssize_ == 2)
+        {
+            return gr_interleaved_short_to_complex_.at(RF_channel);
+        }
+    else
+        {
+            return gr_interleaved_short_to_complex_.at(RF_channel);
+        }
+}
diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
new file mode 100644
index 000000000..cc9abb82b
--- /dev/null
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
@@ -0,0 +1,118 @@
+/*!
+ * \file ad936x_custom_signal_source.h
+ * \brief A direct IIO custom front-end gnss-sdr signal source for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#ifndef GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H
+#define GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H
+
+#include "ad936x_iio_source.h"
+#include "concurrent_queue.h"
+#include "conjugate_cc.h"
+#include "signal_source_base.h"
+#include "unpack_byte_2bit_cpx_samples.h"
+#include "unpack_byte_4bit_samples.h"
+#include <gnuradio/blocks/file_sink.h>
+#include <gnuradio/blocks/interleaved_char_to_complex.h>
+#include <gnuradio/blocks/interleaved_short_to_complex.h>
+#include <pmt/pmt.h>
+#include <cstdint>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+/** \addtogroup Signal_Source
+ * \{ */
+/** \addtogroup Signal_Source_adapters
+ * \{ */
+
+
+class ConfigurationInterface;
+
+/*!
+ * \brief This class instantiates the Ad936xCustom gnuradio signal source.
+ * It has support also for a customized Ad936xCustom firmware and signal source to support PPS samplestamp reading.
+ */
+class Ad936xCustomSignalSource : public SignalSourceBase
+{
+public:
+    Ad936xCustomSignalSource(const ConfigurationInterface* configuration,
+        const std::string& role, unsigned int in_stream,
+        unsigned int out_stream, Concurrent_Queue<pmt::pmt_t>* queue);
+
+    ~Ad936xCustomSignalSource() = 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;
+    gr::basic_block_sptr get_right_block(int RF_channel) override;
+
+private:
+    unsigned int in_stream_;
+    unsigned int out_stream_;
+    gr::block_sptr ad936x_iio_source;
+    std::vector<gr::blocks::file_sink::sptr> sink_;
+    std::vector<std::string> filename_vec_;
+
+    std::vector<gr::blocks::interleaved_short_to_complex::sptr> gr_interleaved_short_to_complex_;
+    std::vector<gr::blocks::interleaved_char_to_complex::sptr> gr_interleaved_char_to_complex_;
+    std::vector<unpack_byte_4bit_samples_sptr> unpack_byte_fourbits;
+    std::vector<unpack_byte_2bit_cpx_samples_sptr> unpack_byte_twobits;
+
+
+    std::string item_type_;
+    size_t item_size_;
+    int64_t samples_;
+    bool dump_;
+    std::string dump_filename_;
+
+    // Front-end settings
+    std::string pluto_uri_;
+    std::string board_type_;
+    long long sample_rate_;
+    long long bandwidth_;
+    long long freq_;
+    long long freq_2ch;
+    std::string rf_port_select_;
+    std::string rf_filter;
+    std::string gain_mode_rx0_;
+    std::string gain_mode_rx1_;
+    double rf_gain_rx0_;
+    double rf_gain_rx1_;
+    bool enable_ch0;
+    bool enable_ch1;
+    bool PPS_mode_;
+    std::string fe_ip_;
+    int fe_ctlport_;
+    int ssize_;
+    int bshift_;
+    bool spattern_;
+    bool inverted_spectrum_ch0_;
+    bool inverted_spectrum_ch1_;
+
+
+    int n_channels;
+};
+
+
+/** \} */
+/** \} */
+#endif  // GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H
diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
index c1198c08b..b09f94cca 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
+++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt
@@ -17,6 +17,13 @@ if(ENABLE_AD936X_SDR)
     set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} gr_complex_ip_packet_source.h)
 endif()
 
+
+if(ENABLE_PLUTOSDR)
+    set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} ad936x_iio_source.cc)
+    set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} ad936x_iio_source.h)
+endif()
+
+
 set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES
     fifo_reader.cc
     unpack_byte_2bit_samples.cc
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
index efee6ad49..f19a7e0a3 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -1,16 +1,14 @@
 /*!
  * \file ad936x_iio_source.cc
- *
- * \brief Unpacks capture files in the LabSat 2 (ls2), LabSat 3 (ls3), or LabSat
- * 3 Wideband (LS3W) formats.
- * \author Javier Arribas jarribas (at) cttc.es
+ * \brief A direct IIO custom front-end gnss-sdr signal gnuradio block for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
  *
  * -----------------------------------------------------------------------------
  *
  * 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)
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
  * SPDX-License-Identifier: GPL-3.0-or-later
  *
  * -----------------------------------------------------------------------------
@@ -19,12 +17,13 @@
 
 #include "ad936x_iio_source.h"
 #include "INIReader.h"
+#include "ad936x_iio_samples.h"
 #include "command_event.h"
 #include "gnss_sdr_make_unique.h"
+#include "pps_samplestamp.h"
 #include <gnuradio/io_signature.h>
 #include <algorithm>
 #include <array>
-#include <bitset>
 #include <exception>
 #include <iomanip>
 #include <iostream>
@@ -33,64 +32,245 @@
 #include <utility>
 
 
-ad936x_iio_source_sptr ad936x_iio_make_source_sptr(Concurrent_Queue<pmt::pmt_t> *queue,
-    std::string pluto_device_uri,
-    std::string board_type,
+ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
+    std::string pluto_uri_,
+    std::string board_type_,
     long long bandwidth_,
     long long sample_rate_,
-    std::vector<std::string> ch_list,
-    std::vector<std::string> ch_gain_mode,
-    std::vector<double> ch_gain_db,
-    std::vector<long int> ch_freq_hz,
-    int ch_sample_size,
-    int ch_sample_bits_shift)
+    long long freq_,
+    std::string rf_port_select_,
+    std::string rf_filter,
+    std::string gain_mode_rx0_,
+    std::string gain_mode_rx1_,
+    double rf_gain_rx0_,
+    double rf_gain_rx1_,
+    bool enable_ch0,
+    bool enable_ch1,
+    long long freq_2ch,
+    bool ppsmode_,
+    bool customsamplesize_,
+    std::string fe_ip_,
+    int fe_ctlport_,
+    int ssize_,
+    int bshift_,
+    bool spattern_)
 {
-    return ad936x_iio_source_sptr(new ad936x_iio_source(*queue,
-        pluto_device_uri,
-        board_type,
+    return ad936x_iio_source_sptr(new ad936x_iio_source(
+        pluto_uri_,
+        board_type_,
         bandwidth_,
         sample_rate_,
-        ch_list,
-        ch_gain_mode,
-        ch_gain_db,
-        ch_freq_hz,
-        ch_sample_size,
-        ch_sample_bits_shift));
+        freq_,
+        rf_port_select_,
+        rf_filter,
+        gain_mode_rx0_,
+        gain_mode_rx1_,
+        rf_gain_rx0_,
+        rf_gain_rx1_,
+        enable_ch0,
+        enable_ch1,
+        freq_2ch,
+        ppsmode_,
+        customsamplesize_,
+        fe_ip_,
+        fe_ctlport_,
+        ssize_,
+        bshift_,
+        spattern_));
 }
 
 
-ad936x_iio_source::ad936x_iio_source(Concurrent_Queue<pmt::pmt_t> *queue,
-    std::string pluto_device_uri,
-    std::string board_type,
+ad936x_iio_source::ad936x_iio_source(
+    std::string pluto_uri_,
+    std::string board_type_,
     long long bandwidth_,
     long long sample_rate_,
-    std::vector<std::string> ch_list,
-    std::vector<std::string> ch_gain_mode,
-    std::vector<double> ch_gain_db,
-    std::vector<long int> ch_freq_hz,
-    int ch_sample_size,
-    int ch_sample_bits_shift) : gr::block("ad936x_iio_source",
-                                    gr::io_signature::make(0, 0, 0),
-                                    gr::io_signature::make(1, 3, sizeof(gr_complex)))
+    long long freq_,
+    std::string rf_port_select_,
+    std::string rf_filter,
+    std::string gain_mode_rx0_,
+    std::string gain_mode_rx1_,
+    double rf_gain_rx0_,
+    double rf_gain_rx1_,
+    bool enable_ch0,
+    bool enable_ch1,
+    long long freq_2ch,
+    bool ppsmode_,
+    bool customsamplesize_,
+    std::string fe_ip_,
+    int fe_ctlport_,
+    int ssize_,
+    int bshift_,
+    bool spattern_) : gr::block("ad936x_iio_source",
+                          gr::io_signature::make(0, 0, 0),
+                          gr::io_signature::make(1, 4, sizeof(int16_t)))
 {
-}
+    ad936x_custom = std::make_unique<ad936x_iio_custom>(0, 0);
+    try
+        {
+            if (ad936x_custom->initialize_device(pluto_uri_, board_type_) == true)
+                {
+                    //configure channels
+                    if (ad936x_custom->init_config_ad9361_rx(bandwidth_,
+                            sample_rate_,
+                            freq_,
+                            rf_port_select_,
+                            rf_filter,
+                            gain_mode_rx0_,
+                            gain_mode_rx1_,
+                            rf_gain_rx0_,
+                            rf_gain_rx1_,
+                            enable_ch0,
+                            enable_ch1,
+                            freq_2ch) == true)
+                        {
+                            std::cout << "ad936x_iio_source HW configured OK!\n";
 
+                            //PPS FPGA Samplestamp information from TCP server
+                            pps_rx = std::make_shared<pps_tcp_rx>();
+                            ppsqueue = std::shared_ptr<Concurrent_Queue<PpsSamplestamp>>(new Concurrent_Queue<PpsSamplestamp>());
+
+                            pps_rx->set_pps_samplestamp_queue(ppsqueue);
+                            ad936x_custom->set_pps_samplestamp_queue(ppsqueue);
+
+                            //start PPS RX thread
+                            if (ppsmode_ == true or customsamplesize_ == true)
+                                {
+                                    pps_rx_thread = std::thread(&pps_tcp_rx::receive_pps, pps_rx, fe_ip_, fe_ctlport_);
+                                    std::this_thread::sleep_for(std::chrono::milliseconds(500));
+                                    //configure custom FPGA options
+                                    switch (ssize_)
+                                        {
+                                        case 16:
+                                            {
+                                                std::cout << "FPGA sample size set to 16 bits per sample.\n";
+                                                if (pps_rx->send_cmd("ssize=16\n") == false) std::cout << "cmd send error!\n";
+                                                break;
+                                            }
+                                        case 8:
+                                            {
+                                                std::cout << "FPGA sample size set to 8 bits per sample.\n";
+                                                if (pps_rx->send_cmd("ssize=8\n") == false) std::cout << "cmd send error!\n";
+                                                break;
+                                            }
+                                        case 4:
+                                            {
+                                                std::cout << "FPGA sample size set to 4 bits per sample.\n";
+                                                if (pps_rx->send_cmd("ssize=4\n") == false) std::cout << "cmd send error!\n";
+                                                break;
+                                            }
+                                        case 2:
+                                            {
+                                                std::cout << "FPGA sample size set to 2 bits per sample.\n";
+                                                if (pps_rx->send_cmd("ssize=2\n") == false) std::cout << "cmd send error!\n";
+                                                break;
+                                            }
+                                        default:
+                                            {
+                                                std::cout << "WARNING: Unsupported ssize. FPGA sample size set to 16 bits per sample.\n";
+                                                if (pps_rx->send_cmd("ssize=16") == false) std::cout << "cmd send error!\n";
+                                            }
+                                        }
+
+                                    if (bshift_ >= 0 and bshift_ <= 14)
+                                        {
+                                            std::cout << "FPGA sample bits shift left set to " + std::to_string(bshift_) + " positions.\n";
+                                            if (pps_rx->send_cmd("bshift=" + std::to_string(bshift_) + "\n") == false) std::cout << "cmd send error!\n";
+                                        }
+                                    else
+                                        {
+                                            std::cout << "WARNING: Unsupported bshift. FPGA sample bits shift left set to 0.\n";
+                                            if (pps_rx->send_cmd("bshift=0\n") == false) std::cout << "cmd send error!\n";
+                                        }
+
+                                    if (spattern_ == true)
+                                        {
+                                            std::cout << "FPGA debug sample pattern is active!.\n";
+                                            if (pps_rx->send_cmd("spattern=1\n") == false) std::cout << "cmd send error!\n";
+                                        }
+                                    else
+                                        {
+                                            std::cout << "FPGA debug sample pattern disabled.\n";
+                                            if (pps_rx->send_cmd("spattern=0\n") == false) std::cout << "cmd send error!\n";
+                                        }
+                                }
+                            else
+                                {
+                                    std::cout << "PPS mode NOT enabled, not configuring PlutoSDR custom timestamping FPGA IP.\n";
+                                }
+                        }
+                    else
+                        {
+                            std::cerr << "ad936x_iio_source IIO initialization error." << std::endl;
+                            exit(1);
+                        }
+                }
+            else
+                {
+                    std::cerr << "ad936x_iio_source IIO initialization error." << std::endl;
+                    exit(1);
+                }
+        }
+    catch (std::exception const &ex)
+        {
+            std::cerr << "STD exception: " << ex.what() << std::endl;
+            exit(1);
+        }
+    catch (...)
+        {
+            std::cerr << "Unexpected catch" << std::endl;
+            exit(1);
+        }
+
+    //set_min_noutput_items(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2);
+    set_min_output_buffer(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2);
+    //std::cout << "max_output_buffer " << min_output_buffer(0) << " min_noutput_items: " << min_noutput_items() << "\n";
+}
 
 ad936x_iio_source::~ad936x_iio_source()
 {
+    // Terminate PPS thread
+    if (pps_rx_thread.joinable())
+        {
+            pthread_t id = pps_rx_thread.native_handle();
+            pps_rx_thread.detach();
+            pthread_cancel(id);
+        }
 }
 
 
+bool ad936x_iio_source::start()
+{
+    return ad936x_custom->start_sample_rx(false);
+}
+
+bool ad936x_iio_source::stop()
+{
+    ad936x_custom->stop_record();
+    return true;
+}
+
 int ad936x_iio_source::general_work(int noutput_items,
     __attribute__((unused)) gr_vector_int &ninput_items,
     __attribute__((unused)) gr_vector_const_void_star &input_items,
     gr_vector_void_star &output_items)
 {
-    std::vector<gr_complex *> out;
-    for (auto &output_item : output_items)
+    std::shared_ptr<ad936x_iio_samples> current_buffer;
+    ad936x_iio_samples *current_samples;
+    ad936x_custom->pop_sample_buffer(current_buffer);
+    current_samples = current_buffer.get();
+
+    //I and Q samples are interleaved in buffer: IQIQIQ...
+
+    for (size_t n = 0; n < ad936x_custom->n_channels; n++)
         {
-            out.push_back(reinterpret_cast<gr_complex *>(output_item));
+            if (output_items.size() > n)  // check if the output channel is connected
+                {
+                    memcpy(reinterpret_cast<void *>(output_items[n]), reinterpret_cast<void *>(current_samples->buffer[n]), current_samples->n_bytes[n]);
+                    produce(n, current_samples->n_samples[n]);
+                }
         }
-    std::cout << "Warning!!\n";
-    return 0;
+
+    ad936x_custom->push_sample_buffer(current_buffer);
+    return this->WORK_CALLED_PRODUCE;
 }
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
index aec4a3c56..497146277 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
@@ -1,8 +1,7 @@
 /*!
  * \file ad936x_iio_source.h
- *
- * \brief signal source to receive samples from the AD936x FE family over libiio, including special custom functionalities in FPGA firmware.
- * \author Javier Arribas jarribas (at) cttc.es
+ * \brief A direct IIO custom front-end gnss-sdr signal gnuradio block for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
  *
  * -----------------------------------------------------------------------------
  *
@@ -15,19 +14,22 @@
  * -----------------------------------------------------------------------------
  */
 
-#ifndef GNSS_SDR_AD9361_IIO_SOURCE_H
-#define GNSS_SDR_AD9361_IIO_SOURCE_H
 
+#ifndef GNSS_SDR_AD936X_IIO_SOURCE_H
+#define GNSS_SDR_AD936X_IIO_SOURCE_H
+
+#include "ad936x_iio_custom.h"
 #include "concurrent_queue.h"
 #include "gnss_block_interface.h"
+#include "ppstcprx.h"
 #include <gnuradio/block.h>
-#include <iio.h>
 #include <pmt/pmt.h>
-#include <ad9361.h>  //multichip sync and high level functions
 #include <cstddef>
 #include <cstdint>
 #include <fstream>
+#include <memory>
 #include <string>
+#include <thread>
 #include <vector>
 
 /** \addtogroup Signal_Source
@@ -41,17 +43,27 @@ class ad936x_iio_source;
 using ad936x_iio_source_sptr = gnss_shared_ptr<ad936x_iio_source>;
 
 ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
-    Concurrent_Queue<pmt::pmt_t> *queue,
-    std::string pluto_device_uri,
-    std::string board_type,
+    std::string pluto_uri_,
+    std::string board_type_,
     long long bandwidth_,
     long long sample_rate_,
-    std::vector<std::string> ch_list,
-    std::vector<std::string> ch_gain_mode,
-    std::vector<double> ch_gain_db,
-    std::vector<long int> ch_freq_hz,
-    int ch_sample_size,
-    int ch_sample_bits_shift);
+    long long freq_,
+    std::string rf_port_select_,
+    std::string rf_filter,
+    std::string gain_mode_rx0_,
+    std::string gain_mode_rx1_,
+    double rf_gain_rx0_,
+    double rf_gain_rx1_,
+    bool enable_ch0,
+    bool enable_ch1,
+    long long freq_2ch,
+    bool ppsmode_,
+    bool customsamplesize_,
+    std::string fe_ip_,
+    int fe_ctlport_,
+    int ssize_,
+    int bshift_,
+    bool spattern_);
 
 /*!
  * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband
@@ -62,6 +74,11 @@ class ad936x_iio_source : public gr::block
 public:
     ~ad936x_iio_source();
 
+    //! start the sample transmission
+    bool start();
+    //! stop the sample transmission
+    bool stop();
+
     int general_work(int noutput_items,
         gr_vector_int &ninput_items,
         gr_vector_const_void_star &input_items,
@@ -69,32 +86,60 @@ public:
 
 private:
     friend ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
-        Concurrent_Queue<pmt::pmt_t> *queue,
-        std::string pluto_device_uri,
-        std::string board_type,
+        std::string pluto_uri_,
+        std::string board_type_,
         long long bandwidth_,
         long long sample_rate_,
-        std::vector<std::string> ch_list,
-        std::vector<std::string> ch_gain_mode,
-        std::vector<double> ch_gain_db,
-        std::vector<long int> ch_freq_hz,
-        int ch_sample_size,
-        int ch_sample_bits_shift);
+        long long freq_,
+        std::string rf_port_select_,
+        std::string rf_filter,
+        std::string gain_mode_rx0_,
+        std::string gain_mode_rx1_,
+        double rf_gain_rx0_,
+        double rf_gain_rx1_,
+        bool enable_ch0,
+        bool enable_ch1,
+        long long freq_2ch,
+        bool ppsmode_,
+        bool customsamplesize_,
+        std::string fe_ip_,
+        int fe_ctlport_,
+        int ssize_,
+        int bshift_,
+        bool spattern_);
 
-    ad936x_iio_source(Concurrent_Queue<pmt::pmt_t> *queue,
-        std::string pluto_device_uri,
-        std::string board_type,
+    ad936x_iio_source(
+        std::string pluto_uri_,
+        std::string board_type_,
         long long bandwidth_,
         long long sample_rate_,
-        std::vector<std::string> ch_list,
-        std::vector<std::string> ch_gain_mode,
-        std::vector<double> ch_gain_db,
-        std::vector<long int> ch_freq_hz,
-        int ch_sample_size,
-        int ch_sample_bits_shift);
+        long long freq_,
+        std::string rf_port_select_,
+        std::string rf_filter,
+        std::string gain_mode_rx0_,
+        std::string gain_mode_rx1_,
+        double rf_gain_rx0_,
+        double rf_gain_rx1_,
+        bool enable_ch0,
+        bool enable_ch1,
+        long long freq_2ch,
+        bool ppsmode_,
+        bool customsamplesize_,
+        std::string fe_ip_,
+        int fe_ctlport_,
+        int ssize_,
+        int bshift_,
+        bool spattern_);
+
+    std::thread pps_rx_thread;
+
+
+    std::unique_ptr<ad936x_iio_custom> ad936x_custom;
+    std::shared_ptr<pps_tcp_rx> pps_rx;
+    std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> ppsqueue;
 };
 
 
 /** \} */
 /** \} */
-#endif  // GNSS_SDR_AD9361_IIO_SOURCE_H
+#endif  // GNSS_SDR_AD936X_IIO_SOURCE_H
diff --git a/src/algorithms/signal_source/libs/CMakeLists.txt b/src/algorithms/signal_source/libs/CMakeLists.txt
index 34cb13e88..00555c424 100644
--- a/src/algorithms/signal_source/libs/CMakeLists.txt
+++ b/src/algorithms/signal_source/libs/CMakeLists.txt
@@ -8,8 +8,8 @@
 set(OPT_SIGNAL_SOURCE_LIB_SOURCES "")
 set(OPT_SIGNAL_SOURCE_LIB_HEADERS "")
 if(ENABLE_FMCOMMS2 OR ENABLE_AD9361)
-    set(OPT_SIGNAL_SOURCE_LIB_SOURCES ad9361_manager.cc)
-    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ad9361_manager.h)
+    set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.cc)
+    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.h)
 endif()
 
 if(ENABLE_FPGA OR ENABLE_AD9361)
@@ -23,6 +23,18 @@ if(ENABLE_FPGA OR ENABLE_AD9361)
     set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_dma.h)
 endif()
 
+
+if(ENABLE_PLUTOSDR)
+    set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_samples.cc)
+    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_samples.h)
+    set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_custom.cc)
+    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_custom.h)
+    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} pps_samplestamp.h)
+    set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ppstcprx.cc)
+    set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ppstcprx.h)
+endif()
+
+
 set(SIGNAL_SOURCE_LIB_SOURCES
     rtl_tcp_commands.cc
     rtl_tcp_dongle_info.cc
@@ -88,7 +100,7 @@ if(GNURADIO_USES_SPDLOG)
     )
 endif()
 
-if(ENABLE_FMCOMMS2 OR ENABLE_AD9361)
+if(ENABLE_FMCOMMS2 OR ENABLE_AD9361 OR ENABLE_PLUTOSDR)
     target_link_libraries(signal_source_libs
         PUBLIC
             Iio::iio
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
new file mode 100644
index 000000000..0076c27fc
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -0,0 +1,1195 @@
+/*!
+ * \file ad936x_iio_custom.cc
+ * \brief A direct IIO custom front-end driver for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+#include "ad936x_iio_custom.h"
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <glog/logging.h>
+#include <chrono>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+ad936x_iio_custom::ad936x_iio_custom(int debug_level_, int log_level_)
+{
+    receive_samples = false;
+    fpga_overflow = false;
+    sample_rate_sps = 0;
+    ctx = NULL;
+    phy = NULL;
+    dds_dev = NULL;
+    stream_dev = NULL;
+    debug_level = debug_level_;
+    log_level = log_level_;
+    PPS_mode = false;
+    n_channels = 0;
+}
+
+ad936x_iio_custom::~ad936x_iio_custom()
+{
+    //disable TX
+    if (phy != NULL) PlutoTxEnable(false);
+
+    //Close device
+    if (ctx != NULL) iio_context_destroy(ctx);
+}
+
+
+void ad936x_iio_custom::set_gnsstime_queue(std::shared_ptr<Concurrent_Queue<GnssTime>> queue)
+{
+    GnssTime_queue = std::move(queue);
+}
+
+void ad936x_iio_custom::set_pps_samplestamp_queue(std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> queue)
+{
+    Pps_queue = std::move(queue);
+}
+
+bool ad936x_iio_custom::initialize_device(std::string pluto_device_uri, std::string board_type)
+{
+    //Find devices
+    if (pluto_device_uri == "local")
+        {
+            struct iio_scan_context *tmp_ctx = iio_create_scan_context("usb", 0);
+            if (!tmp_ctx)
+                {
+                    std::cout << "Unable to create scan context\n";
+                    return false;
+                }
+
+            struct iio_context_info **info;
+            int ret = iio_scan_context_get_info_list(tmp_ctx, &info);
+            if (ret < 0)
+                {
+                    iio_scan_context_destroy(tmp_ctx);
+
+                    std::cout << "Unable to scan for Pluto devices\n";
+                    return false;
+                }
+
+            if (ret == 0)
+                {
+                    iio_context_info_list_free(info);
+                    iio_scan_context_destroy(tmp_ctx);
+                    std::cout << " No Pluto device found\n ";
+                    return false;
+                }
+
+            if (ret > 1)
+                {
+                    std::cout << "More than one Pluto found:\n";
+
+                    for (unsigned int i = 0; i < (size_t)ret; i++)
+                        {
+                            printf("\t%d: %s [%s]\n", i,
+                                iio_context_info_get_description(info[i]),
+                                iio_context_info_get_uri(info[i]));
+                        }
+
+                    std::cout << "We will use the first one.\n";
+                }
+
+            std::string uri(iio_context_info_get_uri(info[0]));
+            iio_context_info_list_free(info);
+            iio_scan_context_destroy(tmp_ctx);
+
+            ctx = iio_create_context_from_uri(uri.c_str());
+        }
+    else
+        {
+            ctx = iio_create_context_from_uri(pluto_device_uri.c_str());
+        }
+
+    if (ctx == NULL)
+        {
+            std::cout << "Unable to create context from uri: " << pluto_device_uri << std::endl;
+            return false;
+        }
+
+
+    phy = iio_context_find_device(ctx, "ad9361-phy");
+
+    if (phy == NULL)
+        {
+            std::cout << "Unable to find ad9361-phy device from uri: " << pluto_device_uri << std::endl;
+            return false;
+        }
+
+    if (board_type.compare("fmcomms5") == 0)
+        {
+            stream_dev = iio_context_find_device(ctx, "cf-ad9361-A");  //first ad9361 in FMCOMMS5
+            if (stream_dev == NULL)
+                {
+                    std::cout << "Unable to find cf-ad9361-A device from uri: " << pluto_device_uri << std::endl;
+                    return false;
+                };
+        }
+    else
+        {
+            stream_dev = iio_context_find_device(ctx, "cf-ad9361-lpc");  //regular AD9361 stream device in single AD9361 boards
+            if (stream_dev == NULL)
+                {
+                    std::cout << "Unable to find cf-ad9361-lpc device from uri: " << pluto_device_uri << std::endl;
+                    return false;
+                };
+            dds_dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");  //DDS core for LO oscillator (external transverter operation)
+            if (stream_dev == NULL)
+                {
+                    std::cout << "Warning: Unable to find cf-ad9361-dds-core-lpc device from uri: " << pluto_device_uri << std::endl;
+                };
+        }
+
+    return true;
+}
+
+
+void ad936x_iio_custom::configure_params(struct iio_device *phy,
+    const std::vector<std::string> &params)
+{
+    for (std::vector<std::string>::const_iterator it = params.begin();
+         it != params.end(); ++it)
+        {
+            struct iio_channel *chn = NULL;
+            const char *attr = NULL;
+            size_t pos;
+            int ret;
+
+            pos = it->find('=');
+            if (pos == std::string::npos)
+                {
+                    std::cerr << "Misformed line: " << *it << std::endl;
+                    continue;
+                }
+
+            std::string key = it->substr(0, pos);
+            std::string val = it->substr(pos + 1, std::string::npos);
+
+            ret = iio_device_identify_filename(phy,
+                key.c_str(), &chn, &attr);
+            if (ret)
+                {
+                    std::cerr << "Parameter not recognized: "
+                              << key << std::endl;
+                    continue;
+                }
+
+            if (chn)
+                ret = iio_channel_attr_write(chn,
+                    attr, val.c_str());
+            else if (iio_device_find_attr(phy, attr))
+                ret = iio_device_attr_write(phy, attr, val.c_str());
+            else
+                ret = iio_device_debug_attr_write(phy,
+                    attr, val.c_str());
+            if (ret < 0)
+                {
+                    std::cerr << "Unable to write attribute " << key
+                              << ": " << ret << std::endl;
+                }
+        }
+}
+
+
+void ad936x_iio_custom::set_params_rx(struct iio_device *phy_device,
+    unsigned long long frequency,
+    unsigned long samplerate, unsigned long bandwidth,
+    bool quadrature, bool rfdc, bool bbdc,
+    std::string gain1, double gain1_value,
+    std::string gain2, double gain2_value,
+    std::string port_select)
+{
+    std::vector<std::string> params;
+
+    params.push_back("out_altvoltage0_RX_LO_frequency=" +
+                     std::to_string(frequency));
+    std::cout << params.back() << "\n";
+    params.push_back("in_voltage_sampling_frequency=" +
+                     std::to_string(samplerate));
+    std::cout << params.back() << "\n";
+    params.push_back("in_voltage_rf_bandwidth=" +
+                     std::to_string(bandwidth));
+    std::cout << params.back() << "\n";
+    params.push_back("in_voltage_quadrature_tracking_en=" +
+                     std::to_string(quadrature));
+    params.push_back("in_voltage_rf_dc_offset_tracking_en=" +
+                     std::to_string(rfdc));
+    params.push_back("in_voltage_bb_dc_offset_tracking_en=" +
+                     std::to_string(bbdc));
+    params.push_back("in_voltage0_gain_control_mode=" +
+                     gain1);
+    params.push_back("in_voltage0_hardwaregain=" +
+                     std::to_string(gain1_value));
+    params.push_back("in_voltage1_gain_control_mode=" +
+                     gain2);
+    params.push_back("in_voltage1_hardwaregain=" +
+                     std::to_string(gain2_value));
+    params.push_back("in_voltage0_rf_port_select=" +
+                     port_select);
+
+    configure_params(phy_device, params);
+}
+
+
+bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
+    double tx_attenuation_db_,
+    int64_t freq_dds_tx_hz_,
+    double scale_dds_,
+    double phase_dds_deg_)
+{
+    // TX stream config
+    std::cout << "Start of AD9361 TX Oscillator DDS configuration\n";
+
+    std::cout << "* Configuring AD9361 for streaming TX\n";
+
+    // ENABLE DDS on TX1
+    // Configure LO channel
+    std::vector<std::string> params_phy;
+
+    params_phy.push_back("out_altvoltage1_TX_LO_frequency=" +
+                         std::to_string(freq_rf_tx_hz_));
+
+    params_phy.push_back("out_voltage0_hardwaregain=" +
+                         std::to_string(-tx_attenuation_db_));
+
+    //disable the other TX
+    params_phy.push_back("out_voltage1_hardwaregain=" +
+                         std::to_string(-tx_attenuation_db_));
+
+
+    configure_params(phy, params_phy);
+
+    std::vector<std::string> params_dds;
+
+    //DDS TX CH1 I (tone #1)
+    params_dds.push_back("out_altvoltage0_TX1_I_F1_frequency=" +
+                         std::to_string(freq_dds_tx_hz_));
+    params_dds.push_back("out_altvoltage0_TX1_I_F1_phase=" +
+                         std::to_string(phase_dds_deg_ * 1000.0));
+    params_dds.push_back("out_altvoltage0_TX1_I_F1_scale=" +
+                         std::to_string(scale_dds_));
+    params_dds.push_back("out_altvoltage0_TX1_I_F1_raw=1");
+    //DDS TX CH1 Q (tone #1)
+    params_dds.push_back("out_altvoltage2_TX1_Q_F1_frequency=" +
+                         std::to_string(freq_dds_tx_hz_));
+    params_dds.push_back("out_altvoltage2_TX1_Q_F1_phase=" +
+                         std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
+    params_dds.push_back("out_altvoltage2_TX1_Q_F1_scale=" +
+                         std::to_string(scale_dds_));
+    params_dds.push_back("out_altvoltage2_TX1_Q_F1_raw=1");
+
+    //DDS TX CH1 I (tone #1)
+    //    params_dds.push_back("out_altvoltage4_TX2_I_F1_frequency=" +
+    //                         std::to_string(freq_dds_tx_hz_));
+    //    params_dds.push_back("out_altvoltage4_TX2_I_F1_phase=" +
+    //                         std::to_string(phase_dds_deg_ * 1000.0));
+    //    params_dds.push_back("out_altvoltage4_TX2_I_F1_scale=" +
+    //                         std::to_string(scale_dds_));
+    //    params_dds.push_back("out_altvoltage4_TX2_I_F1_raw=1");
+    //    //DDS TX CH1 Q (tone #1)
+    //    params_dds.push_back("out_altvoltage6_TX2_Q_F1_frequency=" +
+    //                         std::to_string(freq_dds_tx_hz_));
+    //    params_dds.push_back("out_altvoltage6_TX2_Q_F1_phase=" +
+    //                         std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
+    //    params_dds.push_back("out_altvoltage6_TX2_Q_F1_scale=" +
+    //                         std::to_string(scale_dds_));
+    //    params_dds.push_back("out_altvoltage6_TX2_Q_F1_raw=1");
+
+    configure_params(dds_dev, params_dds);
+
+    return true;
+}
+
+
+bool ad936x_iio_custom::check_device()
+{
+    if (stream_dev != NULL)
+        {
+            return true;
+        }
+    else
+        {
+            return false;
+        }
+}
+
+bool ad936x_iio_custom::get_iio_param(iio_device *dev, const std::string &param, std::string &value)
+{
+    struct iio_channel *chn = 0;
+    const char *attr = 0;
+    char valuestr[256];
+    int ret;
+    ssize_t nchars;
+
+    ret = iio_device_identify_filename(dev, param.c_str(), &chn, &attr);
+
+    if (ret)
+        {
+            std::cerr << "DevicePlutoSDR::get_param: Parameter not recognized: " << param << std::endl;
+            return false;
+        }
+
+    if (chn)
+        {
+            nchars = iio_channel_attr_read(chn, attr, valuestr, 256);
+        }
+    else if (iio_device_find_attr(dev, attr))
+        {
+            nchars = iio_device_attr_read(dev, attr, valuestr, 256);
+        }
+    else
+        {
+            nchars = iio_device_debug_attr_read(dev, attr, valuestr, 256);
+        }
+
+    if (nchars < 0)
+        {
+            std::cerr << "DevicePlutoSDR::get_param: Unable to read attribute " << param << ": " << nchars << std::endl;
+            return false;
+        }
+    else
+        {
+            value.assign(valuestr);
+            return true;
+        }
+}
+
+bool ad936x_iio_custom::read_die_temp(double &temp_c)
+{
+    std::string temp_mC_str;
+
+    if (get_iio_param(phy, "in_temp0_input", temp_mC_str))
+        {
+            try
+                {
+                    uint32_t temp_mC = boost::lexical_cast<uint32_t>(temp_mC_str);
+                    temp_c = static_cast<double>(temp_mC) / 1000.0;
+                    if (temp_c > 120) temp_c = -1;
+                    return true;
+                }
+            catch (const boost::bad_lexical_cast &e)
+                {
+                    std::cerr << "PlutoSDRDevice::getTemp: bad conversion to numeric" << std::endl;
+                    return false;
+                }
+        }
+    else
+        {
+            return false;
+        }
+}
+bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
+    long long sample_rate_,
+    long long freq_,
+    std::string rf_port_select_,
+    std::string rf_filter,
+    std::string gain_mode_rx0_,
+    std::string gain_mode_rx1_,
+    double rf_gain_rx0_,
+    double rf_gain_rx1_,
+    bool enable_ch0,
+    bool enable_ch1,
+    long long freq_2ch)
+
+{
+    if (check_device() == false) return false;
+
+    bool no_errors = true;
+    std::cout << "Configuring phy device parameters...\n";
+    int ret;
+    if (rf_filter.compare("Disabled") == 0)
+        {
+            std::cout << "LNA Filter switch is disabled.\n";
+        }
+    else if (rf_filter.compare("Auto") == 0)
+        {
+            std::cout << "Selecting LNA RF filter based on the selected RF frequency... \n";
+            if (freq_ == 1575420000)
+                {
+                    if (select_rf_filter("E1") == true)
+                        {
+                            std::cout << "LNA RF filter board switch set to E1\n";
+                        }
+                    else
+                        {
+                            std::cout << "Problem setting LNA RF filter switch value\n";
+                        }
+                }
+            else
+                {
+                    if (select_rf_filter("E5E6") == true)
+                        {
+                            std::cout << "LNA RF filter board switch set to E5E6\n";
+                        }
+                    else
+                        {
+                            std::cout << "Problem setting LNA RF filter switch value\n";
+                        }
+                }
+        }
+    else
+        {
+            if (select_rf_filter(rf_filter) == true)
+                {
+                    std::cout << "LNA RF filter board switch set to " << rf_filter << "\n";
+                }
+            else
+                {
+                    std::cout << "Problem setting LNA RF filter switch value\n";
+                }
+        }
+
+    std::vector<std::string> params;
+    // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator)
+    params.push_back("out_altvoltage0_RX_LO_frequency=" +
+                     std::to_string(freq_));
+
+    sample_rate_sps = sample_rate_;
+
+    params.push_back("in_voltage_sampling_frequency=" +
+                     std::to_string(sample_rate_));
+
+    params.push_back("out_voltage_sampling_frequency=" +
+                     std::to_string(sample_rate_));
+
+    params.push_back("in_voltage_rf_bandwidth=" +
+                     std::to_string(bandwidth_));
+
+    params.push_back("out_voltage_rf_bandwidth=" +
+                     std::to_string(bandwidth_));
+
+    params.push_back("in_voltage_quadrature_tracking_en=1");
+    params.push_back("in_voltage_rf_dc_offset_tracking_en=1");
+    params.push_back("in_voltage_bb_dc_offset_tracking_en=1");
+
+    configure_params(phy, params);
+
+    ret = iio_device_attr_write(phy, "trx_rate_governor", "nominal");
+    if (ret < 0)
+        {
+            std::cerr << "Failed to set trx_rate_governor: " << ret << std::endl;
+            no_errors = false;
+        }
+    ret = iio_device_attr_write(phy, "ensm_mode", "fdd");
+    if (ret < 0)
+        {
+            std::cerr << "Failed to set ensm_mode: " << ret << std::endl;
+            no_errors = false;
+        }
+    ret = iio_device_attr_write(phy, "calib_mode", "auto");
+    if (ret < 0)
+        {
+            std::cerr << "Failed to set calib_mode: " << ret << std::endl;
+            no_errors = false;
+        }
+
+
+    std::cout << "no_errors: " << no_errors << "\n";
+
+    if (enable_ch1 == true and enable_ch0 == true and freq_ != freq_2ch)
+        {
+            std::cout << "Two channels enabled with different frequencies, enabling the external RF transverter board:\n";
+            long long int delta_freq_hz = freq_2ch - freq_;
+            if (delta_freq_hz < 0)
+                {
+                    std::cout << "Configuration problem: 2nd channel frequency is " << freq_2ch << " [Hz], must be higher than main channel (" << freq_ << " [Hz])\n";
+                    return false;
+                }
+
+            std::cout << "Configuring DDS Local Oscillator generation. LO Freq. is " << delta_freq_hz << " [Hz]\n";
+            PlutoTxEnable(true);
+            config_ad9361_dds(delta_freq_hz,
+                30,
+                100000,
+                0.9,
+                0);
+            std::cout << "Configuring DDS Local Oscillator generation DONE\n";
+        }
+    else
+        {
+            PlutoTxEnable(false);  //power down the TX LO to reduce interferences
+        }
+
+    if (enable_ch0 == true)
+        {
+            n_channels++;
+            std::cerr << "* Get AD9361 Phy RX channel 0...\n";
+            std::stringstream name;
+            name.str("");
+            name << "voltage";
+            name << 0;
+            struct iio_channel *phy_ch;
+            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  //false means RX
+            if (!phy_ch)
+                {
+                    std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
+                    no_errors = false;
+                }
+
+            ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
+            if (ret < 0)
+                {
+                    std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
+                    no_errors = false;
+                }
+
+            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+            if (ret < 0)
+                {
+                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+                    no_errors = false;
+                }
+
+            long long set_rf_bw;
+            ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
+            if (ret < 0)
+                {
+                    std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
+                    no_errors = false;
+                }
+            else
+                {
+                    std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
+                }
+
+
+            if (setRXGain(0, gain_mode_rx0_, rf_gain_rx0_) == false)
+                {
+                    std::cerr << "Info: setRXGain read returned false \n";
+                    no_errors = false;
+                }
+        }
+
+    if (enable_ch1 == true)
+        {
+            n_channels++;
+            std::cerr << "* Get AD9361 Phy RX channel 1...\n";
+            std::stringstream name;
+            name.str("");
+            name << "voltage";
+            name << 1;
+            struct iio_channel *phy_ch;
+            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  //false means RX
+            if (!phy_ch)
+                {
+                    std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
+                    no_errors = false;
+                }
+
+            ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
+            if (ret < 0)
+                {
+                    std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
+                    no_errors = false;
+                }
+
+            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+            if (ret < 0)
+                {
+                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+                    no_errors = false;
+                }
+
+            if (setRXGain(1, gain_mode_rx1_, rf_gain_rx1_) == false)
+                {
+                    std::cerr << "Info: setRXGain read returned false \n";
+                    no_errors = false;
+                }
+        }
+
+    int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps);
+    if (set_filter_ret != 0)
+        {
+            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+        }
+
+    //testing: set manual RX filter chain
+    //    unsigned long RX_analog_bb_lpf_stop_hz = 1000000;
+    //    unsigned long TX_analog_bb_lpf_stop_hz = 1000000;
+    //
+    //    unsigned long FIR_lpf_passband_hz = 1000000;
+    //    unsigned long FIR_lpf_stopband_hz = 1200000;
+    //  int set_filter_ret = ad9361_set_bb_rate_custom_filter_manual(phy,
+    //        sample_rate_sps,
+    //        FIR_lpf_passband_hz,
+    //        FIR_lpf_stopband_hz,
+    //        RX_analog_bb_lpf_stop_hz,
+    //        TX_analog_bb_lpf_stop_hz);
+    //    if (set_filter_ret != 0)
+    //        {
+    //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+    //        }
+
+    std::cout << "AD936x Front-end configuration summary: \n";
+    std::cout << "Baseband sampling frequency: " << sample_rate_sps << " [SPS]\n";
+    std::cout << "RX chain gain: " << rf_gain_rx0_ << " [dB][only valid in manual mode]\n";
+    std::cout << "RX chain gain mode: " << gain_mode_rx0_ << "\n";
+    //    std::cout << "Analog baseband LPF stop frequency: " << RX_analog_bb_lpf_stop_hz << " [Hz]\n";
+    //    std::cout << "Digital baseband LPF FIR passband frequency: " << FIR_lpf_passband_hz << " [Hz]\n";
+    //    std::cout << "Digital baseband LPF FIR stopband frequency: " << FIR_lpf_stopband_hz << " [Hz]\n";
+    std::cout << "End of AD9361 RX configuration.\n";
+    return no_errors;
+}
+
+bool ad936x_iio_custom::set_rx_frequency(long long freq_hz)
+{
+    if (check_device() == false) return false;
+    // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator)
+    struct iio_channel *lo_ch;
+
+    lo_ch = iio_device_find_channel(phy, "altvoltage0", true);
+    if (!lo_ch)
+        {
+            std::cerr << "Could not find AD9361 RX LO channel altvoltage0\n";
+            return false;
+        }
+    int ret;
+    ret = iio_channel_attr_write_longlong(lo_ch, "frequency", freq_hz);
+    if (ret < 0)
+        {
+            std::cerr << "Warning: RX LO frequency write returned: " << ret << "\n";
+            return false;
+        }
+    return true;
+}
+
+
+bool ad936x_iio_custom::get_rx_frequency(long long &freq_hz)
+{
+    if (check_device() == false) return false;
+    // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator)
+    struct iio_channel *lo_ch;
+
+    lo_ch = iio_device_find_channel(phy, "altvoltage0", true);
+    if (!lo_ch)
+        {
+            std::cerr << "Could not find AD9361 RX LO channel altvoltage0\n";
+            return false;
+        }
+    int ret;
+    ret = iio_channel_attr_read_longlong(lo_ch, "frequency", &freq_hz);
+    if (ret < 0)
+        {
+            std::cerr << "Warning: RX LO frequency read returned: " << ret << "\n";
+            return false;
+        }
+    return true;
+}
+
+bool ad936x_iio_custom::setRXGain(int ch_num, std::string gain_mode, double gain_dB)
+{
+    if (check_device() == false) return false;
+    std::vector<std::string> params;
+    if (ch_num == 0)
+        {
+            params.clear();
+            params.push_back("in_voltage0_gain_control_mode=" + gain_mode);
+            if (gain_mode == "manual")
+                {
+                    params.push_back("in_voltage0_hardwaregain=" + std::to_string(gain_dB));
+                }
+            configure_params(phy, params);
+            return true;
+        }
+    else if (ch_num == 1)
+        {
+            params.clear();
+            params.push_back("in_voltage1_gain_control_mode=" + gain_mode);
+            if (gain_mode == "manual")
+                {
+                    params.push_back("in_voltage1_hardwaregain=" + std::to_string(gain_dB));
+                }
+            configure_params(phy, params);
+            return true;
+        }
+    else
+        {
+            return false;
+        }
+}
+
+double ad936x_iio_custom::get_rx_gain(int ch_num)
+{
+    if (check_device() == false) return -1;
+    double gain_dB;  //gain in dB
+    int ret = 0;
+    if (ch_num == 0)
+        {
+            ret = iio_device_attr_read_double(phy, "in_voltage0_hardwaregain", &gain_dB);
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to read in_voltage0_hardwaregain: " << ret << std::endl;
+                    return -1.0;
+                }
+        }
+    else if (ch_num == 1)
+        {
+            ret = iio_device_attr_read_double(phy, "in_voltage1_hardwaregain", &gain_dB);
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to read in_voltage1_hardwaregain: " << ret << std::endl;
+                    return -1.0;
+                }
+        }
+    else
+        {
+            return -1.0;
+        }
+    return gain_dB;
+}
+
+
+bool ad936x_iio_custom::calibrate(int ch, double bw_hz)
+{
+    if (check_device() == false) return false;
+    //todo
+    return true;
+}
+
+void ad936x_iio_custom::monitor_thread_fn()
+{
+    uint32_t val;
+    int ret;
+
+    /* Give the main thread a moment to start the DMA */
+    sleep(1);
+
+    /* Clear all status bits */
+    ret = iio_device_reg_write(stream_dev, 0x80000088, 0x6);
+    if (ret)
+        {
+            fprintf(stderr, "Failed to clearn DMA status register: %s\n",
+                strerror(-ret));
+        }
+
+    while (receive_samples)
+        {
+            ret = iio_device_reg_read(stream_dev, 0x80000088, &val);
+            if (ret)
+                {
+                    fprintf(stderr, "Failed to read status register: %s\n",
+                        strerror(-ret));
+                    continue;
+                }
+
+            //		if (device_is_tx) {
+            //			if (val & 1)
+            //				fprintf(stderr, "Underflow detected\n");
+            //		} else {
+            if (val & 4)
+                {
+                    std::cout << "WARNING: IIO status register reported overflow!\n";
+                    LOG(INFO) << "WARNING: IIO status register reported overflow!";
+                }
+
+
+            /* Clear bits */
+            if (val)
+                {
+                    ret = iio_device_reg_write(stream_dev, 0x80000088, val);
+                    if (ret)
+                        fprintf(stderr, "Failed to clearn DMA status register: %s\n",
+                            strerror(-ret));
+                }
+            sleep(1);
+        }
+    return;
+}
+
+void ad936x_iio_custom::stop_record()
+{
+    receive_samples = false;
+
+    if (capture_time_thread.joinable() == true)
+        {
+            std::cout << "Joining sample cature thread...\n";
+            capture_samples_thread.join();
+        }
+
+    if (capture_time_thread.joinable() == true)
+        {
+            std::cout << "Joining overflow monitor thread...\n";
+            overflow_monitor_thread.join();
+        }
+
+    if (capture_time_thread.joinable() == true)
+        {
+            std::cout << "Joining time cature thread...\n";
+            capture_time_thread.join();
+        }
+}
+
+
+void ad936x_iio_custom::PlutoTxEnable(bool txon)
+{
+    if (check_device())
+        {
+            int ret;
+            if (txon == false)
+                {
+                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", true);  //turn off TX LO
+                    if (ret < 0)
+                        {
+                            std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl;
+                        }
+                }
+            else
+                {
+                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", false);  //turn on TX LO
+                    if (ret < 0)
+                        {
+                            std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl;
+                        }
+                }
+        }
+}
+
+void ad936x_iio_custom::setPlutoGpo(int p)
+{
+    char pins[11];
+    sprintf(pins, "0x27 0x%x0", p);  //direct access to AD9361 registers... WARNING!
+    pins[9] = 0;
+    int ret;
+    //std::cout << "send: " << pins << " \n";
+    if (check_device())
+        {
+            ret = iio_device_debug_attr_write(phy, "direct_reg_access", pins);
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to write direct_reg_access: " << ret << std::endl;
+                }
+        }
+}
+
+
+bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
+{
+    //	adi,gpo-manual-mode-enable		Enables GPO manual mode, this will conflict with automatic ENSM slave and eLNA mode
+    //	adi,gpo-manual-mode-enable-mask		Enable bit mask, setting or clearing bits will change the level of the corresponding output. Bit0 → GPO, Bit1 → GPO1, Bit2 → GPO2, Bit3 → GP03
+    //	adi,gpo-manual-mode-enable
+    // adi,gpo-manual-mode-enable-mask does not work...
+    // some software use the direct_reg_access (see https://github.com/g4eml/Langstone/blob/master/LangstoneGUI.c)
+
+    //since plutosdr fw 31:
+    //    GPOs can be addressed individual 0..3 or altogether using 0xF as Identifier.
+    //
+    //    SYNTAX:
+    //
+    //    gpo_set <Identifier> <Value>
+    //    Enable
+    //    Value   Function
+    //    0   Disable
+    //    1   Enable
+    //    X   Enable Mask if Identifier=0xF
+
+
+    if (check_device() == false) return false;
+    int plutoGpo = 0;
+    int ret;
+    ret = iio_device_debug_attr_write(phy, "adi,gpo-manual-mode-enable", "1");
+
+    if (ret < 0)
+        {
+            std::cerr << "Failed to write adi,gpo-manual-mode-enable: " << ret << std::endl;
+            return false;
+        }
+
+    if (rf_filter.compare("E1") == 0)
+        {
+            //set gpio0 to switch L1 filter
+            //            setPlutoGpo(plutoGpo);
+            ret = iio_device_debug_attr_write(phy, "gpo_set", "0 0");
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to write gpo_set: " << ret << std::endl;
+                    return false;
+                }
+        }
+    else if (rf_filter.compare("E5E6") == 0)
+        {
+            //set gpio0 to switch L5/L6 filter (GPO0)
+            //            plutoGpo = plutoGpo | 0x10;
+            //            setPlutoGpo(plutoGpo);  //set the Pluto GPO Pin
+            ret = iio_device_debug_attr_write(phy, "gpo_set", "0 1");
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to write gpo_set: " << ret << std::endl;
+                    return false;
+                }
+        }
+    if (rf_filter.compare("none") == 0)
+        {
+            std::cout << "RF external filter not selected\n";
+        }
+
+    else
+        {
+            std::cout << "Unknown filter selected, switching to E1 filter...\n";
+            ret = iio_device_debug_attr_write(phy, "gpo_set", "0 0");
+            if (ret < 0)
+                {
+                    std::cerr << "Failed to write gpo_set: " << ret << std::endl;
+                    return false;
+                }
+        }
+
+    return true;
+}
+void ad936x_iio_custom::get_PPS_timestamp()
+{
+    GnssTime tow;
+    PpsSamplestamp pps;
+    GnssTime_queue->clear();
+    Pps_queue->clear();
+
+    std::cout << "Waiting for uBlox time message synchronization... (wait up to 10 seconds)\n";
+    if (GnssTime_queue->timed_wait_and_pop(tow, 10000) == false)
+        {
+            std::cout << "uBlox time message synchronization error.\n";
+            return;
+        }
+
+    std::cout << "Waiting for PPS Samplestamp message synchronization... (wait up to 10 seconds)\n";
+    if (Pps_queue->timed_wait_and_pop(pps, 10000) == false)
+        {
+            std::cout << "PPS IP message synchronization error.\n";
+            return;
+        }
+
+    //Get new PPS samplestamp and associate it to the corresponding uBlox TP message
+    while (receive_samples == true)
+        {
+            std::cout << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n";
+            LOG(INFO) << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n";
+            //write timestamp information to timestamp metadata file:
+            //uint64_t: absolute sample counter from the beginning of sample capture associated to the rising edge of the PPS signal
+            //            ppstimefile.write(reinterpret_cast<char *>(&pps.samplestamp), sizeof(uint64_t));
+            //int32_t: Galileo/GPS Week Number associated to the rising edge of PPS signal
+            //            ppstimefile.write(reinterpret_cast<char *>(&tow.week), sizeof(int32_t));
+            //int32_t: Galileo/GPS TOW associated to the rising edge of PPS signal
+            //            ppstimefile.write(reinterpret_cast<char *>(&tow.tow_ms), sizeof(int32_t));
+            //record pps rise samplestamp associated to the absolute sample counter
+            //PPS rising edge must be associated with the corresponding uBlox time message (tx once a second)
+
+
+            if (GnssTime_queue->timed_wait_and_pop(tow, 2000) == false)
+                {
+                    if (receive_samples == true)
+                        {
+                            std::cout << "ERROR: uBlox time message not received, check uBlox GNSS signal quality!\n";
+                            LOG(INFO) << "ERROR: uBlox time message not received!";
+                        }
+                    break;
+                }
+            if (Pps_queue->timed_wait_and_pop(pps, 2000) == false)
+                {
+                    if (receive_samples == true)
+                        {
+                            std::cout << "ERROR: PPS time message not received, check uBlox GNSS signal quality!\n";
+                            LOG(INFO) << "ERROR: PPS time message not received!";
+                        }
+                    break;
+                }
+            if (pps.overflow_reg > 0)
+                {
+                    if (receive_samples == true)
+                        {
+                            fpga_overflow = true;
+                            std::cout << "ERROR: FPGA reported RX sample buffer overflow!\n";
+                            LOG(INFO) << "ERROR: FPGA reported RX sample buffer overflow!\n";
+                        }
+                    break;
+                }
+        }
+}
+bool ad936x_iio_custom::start_sample_rx(bool ppsmode)
+{
+    //using queues of smart pointers to preallocated buffers
+    free_buffers.clear();
+    used_buffers.clear();
+    //preallocate buffers and use queues
+    std::cerr << "Allocating memory..\n";
+    try
+        {
+            for (int n = 0; n < IIO_INPUTRAMFIFOSIZE; n++)
+                {
+                    free_buffers.push(std::make_shared<ad936x_iio_samples>());
+                }
+        }
+    catch (const std::exception &ex)
+        {
+            std::cout << "ERROR: Problem allocating RAM buffer: " << ex.what() << "\n";
+            return false;
+        }
+
+    //prepare capture channels
+    std::vector<std::string> channels;
+    switch (n_channels)
+        {
+        case 1:
+            channels.push_back("voltage0");  //Channel 0 I
+            channels.push_back("voltage1");  //Channel 0 Q
+            break;
+        case 2:
+            channels.push_back("voltage0");  //Channel 0 I
+            channels.push_back("voltage1");  //Channel 0 Q
+            channels.push_back("voltage2");  //Channel 1 I
+            channels.push_back("voltage3");  //Channel 1 Q
+            break;
+        default:
+            channels.push_back("voltage0");  //Channel 0 I
+            channels.push_back("voltage1");  //Channel 0 Q
+        }
+
+    receive_samples = true;
+    //start sample capture thread
+    capture_samples_thread = std::thread(&ad936x_iio_custom::capture, this, channels);
+    //start sample overflow detector
+    overflow_monitor_thread = std::thread(&ad936x_iio_custom::monitor_thread_fn, this);
+
+
+    //start PPS and GNSS Time capture thread
+
+    if (ppsmode == true)
+        {
+            capture_time_thread = std::thread(&ad936x_iio_custom::get_PPS_timestamp, this);
+        }
+    return true;
+}
+
+void ad936x_iio_custom::pop_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer)
+{
+    used_buffers.wait_and_pop(current_buffer);
+}
+
+void ad936x_iio_custom::push_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer)
+{
+    free_buffers.push(current_buffer);
+}
+
+void ad936x_iio_custom::capture(const std::vector<std::string> &channels)
+{
+    if (check_device() == false) return;
+
+    struct iio_buffer *rxbuf;
+
+    std::vector<struct iio_channel *> channel_list;
+
+    /* First disable all channels */
+    unsigned int nb_channels;
+    nb_channels = iio_device_get_channels_count(stream_dev);
+    for (unsigned int i = 0; i < nb_channels; i++)
+        {
+            iio_channel_disable(iio_device_get_channel(stream_dev, i));
+        }
+
+    // enable channels
+    if (channels.empty())
+        {
+            for (unsigned int i = 0; i < nb_channels; i++)
+                {
+                    struct iio_channel *chn =
+                        iio_device_get_channel(stream_dev, i);
+
+                    iio_channel_enable(chn);
+                    channel_list.push_back(chn);
+                }
+        }
+    else
+        {
+            for (std::vector<std::string>::const_iterator it =
+                     channels.begin();
+                 it != channels.end(); ++it)
+                {
+                    struct iio_channel *chn =
+                        iio_device_find_channel(stream_dev,
+                            it->c_str(), false);
+                    if (!chn)
+                        {
+                            std::cerr << "Channel " << it->c_str() << " not found\n";
+                            return;
+                        }
+                    else
+                        {
+                            iio_channel_enable(chn);
+                            channel_list.push_back(chn);
+                        }
+                }
+        }
+
+    const struct iio_data_format *format = iio_channel_get_data_format(channel_list[0]);
+
+    std::cerr << "Format: length " << format->length
+              << " bits " << format->bits
+              << " shift " << format->shift
+              << " is_signed " << format->is_signed
+              << " is_fully_defined " << format->is_fully_defined
+              << " is_be " << format->is_be
+              << " with_scale " << format->with_scale
+              << " scale " << format->scale
+              << " repeat " << format->repeat << "\n";
+
+    rxbuf = iio_device_create_buffer(stream_dev, IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES, false);
+    if (!rxbuf)
+        {
+            std::cout << "Could not create RX buffer. \n";
+            return;
+        }
+
+    std::shared_ptr<ad936x_iio_samples> current_buffer;
+    ad936x_iio_samples *current_samples;
+    unsigned long items_in_buffer;
+    std::cerr << "Enter capture loop...\n";
+    int ret;
+    int bytes_per_channel = static_cast<int>(channels.size()) * 2;  //each channel has two items in channels vector (I,Q). Each component has two bytes.
+    while (receive_samples == true)
+        {
+            free_buffers.wait_and_pop(current_buffer);
+            current_samples = current_buffer.get();
+            ret = iio_buffer_refill(rxbuf);
+            if (ret < 0)
+                {
+                    /* -EBADF happens when the buffer is cancelled */
+                    if (ret != -EBADF)
+                        {
+                            char err_buf[256];
+                            iio_strerror(-ret, err_buf, sizeof(err_buf));
+                            std::string error(err_buf);
+
+                            std::cerr << "Unable to refill buffer: " << error << std::endl;
+                            iio_buffer_destroy(rxbuf);
+                            return;
+                        }
+                }
+
+            // Demultiplex the samples of a given channel
+            int n_ch = 0;
+            for (auto it = std::begin(channel_list); it != std::end(channel_list); ++it)
+                {
+                    current_samples->n_bytes[n_ch] = iio_channel_read_raw(*it, rxbuf, &current_samples->buffer[n_ch][0], IIO_MAX_BYTES_PER_CHANNEL);
+                    current_samples->n_samples[n_ch] = current_samples->n_bytes[n_ch] / sizeof(short);
+                    n_ch++;
+                }
+
+            // old, valid only for one channel
+            //memcpy(&current_samples->buffer[0], iio_buffer_start(rxbuf), ret);
+
+            if (current_samples->n_bytes[0] == 0) return;
+
+            used_buffers.push(current_buffer);
+        }
+
+    iio_buffer_destroy(rxbuf);
+}
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.h b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
new file mode 100644
index 000000000..f8c1fe718
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
@@ -0,0 +1,144 @@
+/*!
+ * \file ad936x_iio_custom.h
+ * \brief A direct IIO custom front-end driver for the AD936x AD front-end family with special FPGA custom functionalities.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+
+#ifndef SRC_LIBS_ad936x_iio_custom_H_
+#define SRC_LIBS_ad936x_iio_custom_H_
+
+#include "concurrent_queue.h"
+#include "gnss_time.h"
+#include "pps_samplestamp.h"
+#include <boost/atomic.hpp>
+#include <memory>
+#include <string>
+
+#ifdef __APPLE__
+#include <iio/iio.h>
+#else
+#include <iio.h>
+#endif
+
+#include "ad936x_iio_samples.h"
+#include <ad9361.h>  //multichip sync and high level functions
+#include <thread>
+#include <vector>
+
+class ad936x_iio_custom
+{
+public:
+    ad936x_iio_custom(int debug_level_, int log_level_);
+    virtual ~ad936x_iio_custom();
+    bool initialize_device(std::string pluto_device_uri, std::string board_type);
+
+    bool init_config_ad9361_rx(long long bandwidth_,
+        long long sample_rate_,
+        long long freq_,
+        std::string rf_port_select_,
+        std::string rf_filter,
+        std::string gain_mode_rx0_,
+        std::string gain_mode_rx1_,
+        double rf_gain_rx0_,
+        double rf_gain_rx1_,
+        bool enable_ch0,
+        bool enable_ch1,
+        long long freq_2ch);
+
+    bool calibrate(int ch, double bw_hz);
+
+    double get_rx_gain(int ch_num);
+    bool setRXGain(int ch_num, std::string gain_mode, double gain_dB);
+
+    bool set_antenna_port(int ch, int antenna_idx);
+    double get_frequency(int ch);
+    bool set_frequency(int ch, double freq_hz);
+
+    bool start_sample_rx(bool ppsmode);
+    void stop_record();
+
+    void set_gnsstime_queue(std::shared_ptr<Concurrent_Queue<GnssTime>> queue);
+    void set_pps_samplestamp_queue(std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> queue);
+
+    bool get_rx_frequency(long long &freq_hz);
+    bool set_rx_frequency(long long freq_hz);
+    bool read_die_temp(double &temp_c);
+
+    void pop_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer);
+
+    void push_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer);
+
+    int n_channels;
+
+private:
+    std::shared_ptr<Concurrent_Queue<GnssTime>> GnssTime_queue;
+    std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> Pps_queue;
+    bool check_device();
+    bool get_iio_param(iio_device *dev, const std::string &param, std::string &value);
+    void configure_params(struct iio_device *phy,
+        const std::vector<std::string> &params);
+    void set_params_rx(struct iio_device *phy_device,
+        unsigned long long frequency,
+        unsigned long samplerate, unsigned long bandwidth,
+        bool quadrature, bool rfdc, bool bbdc,
+        std::string gain1, double gain1_value,
+        std::string gain2, double gain2_value,
+        std::string port_select);
+
+    bool config_ad9361_dds(uint64_t freq_rf_tx_hz_,
+        double tx_attenuation_db_,
+        int64_t freq_dds_tx_hz_,
+        double scale_dds_,
+        double phase_dds_deg_);
+
+    void get_PPS_timestamp();
+    void capture(const std::vector<std::string> &channels);
+
+    bool select_rf_filter(std::string rf_filter);
+
+    void monitor_thread_fn();
+
+    void PlutoTxEnable(bool txon);
+    void setPlutoGpo(int p);
+
+    //Device structure
+    struct iio_context *ctx;
+    struct iio_device *phy;
+    struct iio_device *stream_dev;
+    struct iio_device *dds_dev;
+
+    //stream
+
+    uint64_t sample_rate_sps;
+
+
+    int debug_level;
+    int log_level;
+    bool PPS_mode;
+
+    std::mutex mtx;
+    std::condition_variable cv;
+
+    boost::atomic<bool> receive_samples;
+
+    boost::atomic<bool> fpga_overflow;
+    //using queues of smart pointers to preallocated buffers
+    Concurrent_Queue<std::shared_ptr<ad936x_iio_samples>> free_buffers;
+    Concurrent_Queue<std::shared_ptr<ad936x_iio_samples>> used_buffers;
+
+    std::thread capture_samples_thread;
+    std::thread overflow_monitor_thread;
+    std::thread capture_time_thread;
+};
+
+#endif /* SRC_LIBS_ad936x_iio_custom_H_ */
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_samples.cc b/src/algorithms/signal_source/libs/ad936x_iio_samples.cc
new file mode 100644
index 000000000..7d1ef5681
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ad936x_iio_samples.cc
@@ -0,0 +1,26 @@
+/*!
+ * \file ad936x_iio_samples.cc
+ * \brief A class that holds a custom sample buffer for Analog Devices AD936x family front-ends.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "ad936x_iio_samples.h"
+
+ad936x_iio_samples::ad936x_iio_samples()
+{
+    for (int n = 0; n < IIO_MAX_CH; n++)
+        {
+            n_bytes[n] = 0;
+            n_samples[n] = 0;
+        }
+}
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_samples.h b/src/algorithms/signal_source/libs/ad936x_iio_samples.h
new file mode 100644
index 000000000..5e932075c
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ad936x_iio_samples.h
@@ -0,0 +1,41 @@
+/*!
+ * \file ad936x_iio_samples.h
+ * \brief A class that holds a custom sample buffer for Analog Devices AD936x family front-ends.
+ * \author Javier Arribas, jarribas(at)cttc.es
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2022  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+
+#ifndef SRC_LIBS_ad936x_iio_samples_H_
+#define SRC_LIBS_ad936x_iio_samples_H_
+
+#define IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES 32768 * 2
+
+#define IIO_INPUTRAMFIFOSIZE 512
+
+#define IIO_MAX_CH 4
+#define IIO_MAX_BYTES_PER_CHANNEL IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2 * 2  //(2-bytes per I + 2-bytes per Q)
+
+#include <memory>
+#include <stdint.h>
+#include <vector>
+
+class ad936x_iio_samples
+{
+public:
+    ad936x_iio_samples();
+    uint32_t n_bytes[IIO_MAX_CH];
+    uint32_t n_samples[IIO_MAX_CH];
+    int16_t buffer[IIO_MAX_CH][IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2];  //16 bits I,Q samples buffers
+};
+
+#endif
diff --git a/src/algorithms/signal_source/libs/pps_samplestamp.h b/src/algorithms/signal_source/libs/pps_samplestamp.h
new file mode 100644
index 000000000..cdbc1276f
--- /dev/null
+++ b/src/algorithms/signal_source/libs/pps_samplestamp.h
@@ -0,0 +1,20 @@
+/* -------------------------------------------------------------------------
+ *
+ * Copyright (C) 2022 (see AUTHORS file for a list of contributors)
+ *
+ *
+ */
+
+#ifndef IIOPPS_PPS_SAMPLESTAMP_H
+#define IIOPPS_PPS_SAMPLESTAMP_H
+
+#include <cstdint>
+
+class PpsSamplestamp
+{
+public:
+    uint64_t samplestamp;   //PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side
+    uint32_t overflow_reg;  // >0 indicates overflow situation in the FPGA RX buffer
+};
+
+#endif
diff --git a/src/algorithms/signal_source/libs/ppstcprx.cc b/src/algorithms/signal_source/libs/ppstcprx.cc
new file mode 100644
index 000000000..0a10d8545
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ppstcprx.cc
@@ -0,0 +1,154 @@
+/*
+ * ppstcprx.cc
+ *
+ *  Created on: 28 feb 2022
+ *      Author: javier
+ */
+
+#include "ppstcprx.h"
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+pps_tcp_rx::pps_tcp_rx()
+{
+    // TODO Auto-generated constructor stub
+    is_connected = false;
+    clientSd = -1;
+}
+
+pps_tcp_rx::~pps_tcp_rx()
+{
+    // TODO Auto-generated destructor stub
+}
+
+void pps_tcp_rx::set_pps_samplestamp_queue(std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> queue)
+{
+    Pps_queue = std::move(queue);
+}
+
+bool pps_tcp_rx::send_cmd(std::string cmd)
+{
+    if (is_connected == true)
+        {
+            // send call sends the data you specify as second param and it's length as 3rd param, also returns how many bytes were actually sent
+            auto bytes_sent = send(clientSd, cmd.data(), cmd.length(), 0);
+            if (bytes_sent <= 0)
+                {
+                    std::cerr << "Connection terminated...\n";
+                    return false;
+                }
+            else
+                {
+                    std::cout << "sent bytes..\n";
+                }
+        }
+    else
+        {
+            return false;
+        }
+    return true;
+}
+void pps_tcp_rx::receive_pps(std::string ip_address, int port)
+{
+    //create a message buffer
+    char buf[1500];
+    //setup a socket and connection tools
+    sockaddr_in sendSockAddr;
+    sendSockAddr.sin_family = AF_INET;
+    sendSockAddr.sin_addr.s_addr =
+        inet_addr(ip_address.c_str());
+    sendSockAddr.sin_port = htons(port);
+    clientSd = socket(AF_INET, SOCK_STREAM, 0);
+    //try to connect...
+    int status = connect(clientSd,
+        (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
+    if (status < 0)
+        {
+            std::cout << "pps_tcp_rx: Error connecting to PPS TCP server IP " << ip_address << " at port " << port << std::endl;
+            return;
+        }
+    std::string new_pps_line;
+
+    is_connected = true;
+    while (true)
+        {
+            int numBytesRead = recv(clientSd, buf, sizeof(buf), 0);
+            if (numBytesRead > 0)
+                {
+                    for (int i = 0; i < numBytesRead; i++)
+                        {
+                            char c = buf[i];
+                            if (c == '\n')
+                                {
+                                    if (new_pps_line.length() > 0)
+                                        {
+                                            //std::cout << "pps_tcp_rx debug: " << new_pps_line << "\n";
+                                            //parse string and push PPS data to the PPS queue
+                                            std::stringstream ss(new_pps_line);
+                                            std::vector<std::string> data;
+                                            while (ss.good())
+                                                {
+                                                    std::string substr;
+                                                    std::getline(ss, substr, ',');
+                                                    data.push_back(substr);
+                                                }
+                                            if (data.size() >= 2)
+                                                {
+                                                    PpsSamplestamp new_pps;
+                                                    //sample counter
+                                                    std::size_t found = data.at(0).find("sc=");
+                                                    if (found != std::string::npos)
+                                                        {
+                                                            try
+                                                                {
+                                                                    new_pps.samplestamp = std::strtoul(data.at(0).substr(found + 3).c_str(), NULL, 0);
+                                                                }
+                                                            catch (const std::exception &ex)
+                                                                {
+                                                                    std::cout << "pps_tcp_rx debug: sc parse error str " << data.at(0) << "\n";
+                                                                }
+                                                        }
+                                                    else
+                                                        {
+                                                            std::cout << "pps_tcp_rx debug: sc parse error str " << data.at(0) << "\n";
+                                                        }
+                                                    found = data.at(1).find("o=");
+                                                    if (found != std::string::npos)
+                                                        {
+                                                            try
+                                                                {
+                                                                    new_pps.overflow_reg = std::stoi(data.at(1).substr(found + 2).c_str(), NULL, 0);
+                                                                }
+                                                            catch (const std::exception &ex)
+                                                                {
+                                                                    std::cout << "pps_tcp_rx debug: o parse error str " << data.at(0) << "\n";
+                                                                }
+                                                        }
+                                                    else
+                                                        {
+                                                            std::cout << "pps_tcp_rx debug: o parse error str " << data.at(1) << "\n";
+                                                        }
+                                                    Pps_queue->push(new_pps);
+                                                    //std::cout << "pps_tcp_rx debug: pps pushed!\n";
+                                                }
+                                            else
+                                                {
+                                                    std::cout << "pps_tcp_rx debug: protocol error!\n";
+                                                }
+                                            new_pps_line = "";
+                                        }
+                                }
+                            else
+                                new_pps_line += c;
+                        }
+                }
+            else
+                {
+                    std::cout << "pps_tcp_rx: Socket disconnected!\n!";
+                    break;
+                }
+        }
+    is_connected = false;
+}
diff --git a/src/algorithms/signal_source/libs/ppstcprx.h b/src/algorithms/signal_source/libs/ppstcprx.h
new file mode 100644
index 000000000..37c8de92f
--- /dev/null
+++ b/src/algorithms/signal_source/libs/ppstcprx.h
@@ -0,0 +1,34 @@
+/*
+ * ppstcprx.h
+ *
+ *  Created on: 28 feb 2022
+ *      Author: javier
+ */
+
+#ifndef SRC_LIBS_PPSTCPRX_H_
+#define SRC_LIBS_PPSTCPRX_H_
+#include "concurrent_queue.h"
+#include "pps_samplestamp.h"
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/types.h>
+class pps_tcp_rx
+{
+private:
+    std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> Pps_queue;
+    int clientSd;
+
+public:
+    volatile bool is_connected;
+    pps_tcp_rx();
+    virtual ~pps_tcp_rx();
+
+    void receive_pps(std::string ip_address, int port);
+    bool send_cmd(std::string cmd);
+
+    void set_pps_samplestamp_queue(std::shared_ptr<Concurrent_Queue<PpsSamplestamp>> queue);
+};
+
+#endif /* SRC_LIBS_PPSTCPRX_H_ */
diff --git a/src/core/receiver/concurrent_queue.h b/src/core/receiver/concurrent_queue.h
index fc5c97053..ac7be0f17 100644
--- a/src/core/receiver/concurrent_queue.h
+++ b/src/core/receiver/concurrent_queue.h
@@ -55,6 +55,18 @@ public:
         return the_queue.empty();
     }
 
+    size_t size() const
+    {
+        std::unique_lock<std::mutex> lock(the_mutex);
+        return the_queue.size();
+    }
+
+    void clear()
+    {
+        std::unique_lock<std::mutex> lock(the_mutex);
+        the_queue = std::queue<Data>();
+    }
+
     bool try_pop(Data& popped_value)
     {
         std::unique_lock<std::mutex> lock(the_mutex);
diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc
index adba9c0df..d7bd03b97 100644
--- a/src/core/receiver/gnss_block_factory.cc
+++ b/src/core/receiver/gnss_block_factory.cc
@@ -153,6 +153,7 @@
 #endif
 
 #if PLUTOSDR_DRIVER
+#include "ad936x_custom_signal_source.h"
 #include "plutosdr_signal_source.h"
 #endif
 
@@ -775,6 +776,12 @@ std::unique_ptr<GNSSBlockInterface> GNSSBlockFactory::GetBlock(
                         out_streams, queue);
                     block = std::move(block_);
                 }
+            else if (implementation == "Ad936x_Custom_Signal_Source")
+                {
+                    std::unique_ptr<GNSSBlockInterface> block_ = std::make_unique<Ad936xCustomSignalSource>(configuration, role, in_streams,
+                        out_streams, queue);
+                    block = std::move(block_);
+                }
 #endif
 
 #if FMCOMMS2_DRIVER

From 0c5d38145f21beecc1938df95f1e41239ca596c0 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 30 Aug 2022 15:28:18 +0200
Subject: [PATCH 17/31] Adding extra control for the AD936x custom source
 external mixer

---
 .../adapters/ad936x_custom_signal_source.cc   |  55 ++++++---
 .../adapters/ad936x_custom_signal_source.h    |  10 +-
 .../gnuradio_blocks/ad936x_iio_source.cc      |  20 +++-
 .../gnuradio_blocks/ad936x_iio_source.h       |  12 +-
 .../signal_source/libs/ad936x_iio_custom.cc   | 108 +++++++++++-------
 .../signal_source/libs/ad936x_iio_custom.h    |   4 +-
 6 files changed, 138 insertions(+), 71 deletions(-)

diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
index a999d1025..5229405dd 100644
--- a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
@@ -56,12 +56,13 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
       PPS_mode_(configuration->property(role + ".PPS_mode", false)),
       fe_ip_(configuration->property(role + ".fe_ip", std::string("192.168.2.1"))),
       fe_ctlport_(configuration->property(role + ".fe_ctlport", int32_t(10000))),
-      ssize_(configuration->property(role + ".ssize", int32_t(16))),
+      ssize_(configuration->property(role + ".ssize", int32_t(12))),
       bshift_(configuration->property(role + ".bshift", int64_t(0))),
       spattern_(configuration->property(role + ".spattern", false)),
       inverted_spectrum_ch0_(configuration->property(role + ".inverted_spectrum_ch0", false)),
-      inverted_spectrum_ch1_(configuration->property(role + ".inverted_spectrum_ch1", false))
-
+      inverted_spectrum_ch1_(configuration->property(role + ".inverted_spectrum_ch1", false)),
+      lo_attenuation_db_(configuration->property(role + ".lo_attenuation_db", 6.0)),
+      high_side_lo_(configuration->property(role + ".high_side_lo", false))
 
 {
     if (item_type_ == "gr_complex")
@@ -69,7 +70,11 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
             item_size_ = sizeof(gr_complex);
             // 1. Make the driver instance
             bool customsamplesize = false;
-            if (ssize_ != 16 or spattern_ == true) customsamplesize = true;
+            if (ssize_ != 12 or spattern_ == true) customsamplesize = true;  // custom FPGA DMA firmware
+            if (ssize_ == 12)                                                // default original FPGA DMA firmware
+                {
+                    ssize_ = 16;  // set to 16 bits and do not try to change sample size
+                }
 
             ad936x_iio_source = ad936x_iio_make_source_sptr(
                 pluto_uri_,
@@ -92,7 +97,9 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
                 fe_ctlport_,
                 ssize_,
                 bshift_,
-                spattern_);
+                spattern_,
+                lo_attenuation_db_,
+                high_side_lo_);
 
             n_channels = 1;
             if (enable_ch0 == true and enable_ch1 == true)
@@ -100,26 +107,34 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
                     n_channels = 2;
                 }
 
+
+            for (int n = 0; n < n_channels; n++)
+                {
+                    if (n == 0) inverted_spectrum_vec.push_back(inverted_spectrum_ch0_);
+                    if (n == 1) inverted_spectrum_vec.push_back(inverted_spectrum_ch1_);
+                }
+
             for (int n = 0; n < n_channels; n++)
                 {
                     if (ssize_ == 16)
                         {
-                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make());
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, inverted_spectrum_vec.at(n)));
                         }
                     else if (ssize_ == 8)
                         {
-                            gr_interleaved_char_to_complex_.push_back(gr::blocks::interleaved_char_to_complex::make());
                             unpack_short_byte.push_back(make_unpack_short_byte_samples());
+                            gr_char_to_short_.push_back(gr::blocks::char_to_short::make());
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, inverted_spectrum_vec.at(n)));
                         }
                     else if (ssize_ == 4)
                         {
-                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false));
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, inverted_spectrum_vec.at(n)));
                             unpack_byte_fourbits.push_back(make_unpack_byte_4bit_samples());
                             unpack_short_byte.push_back(make_unpack_short_byte_samples());
                         }
                     else if (ssize_ == 2)
                         {
-                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false));
+                            gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, inverted_spectrum_vec.at(n)));
                             unpack_byte_twobits.push_back(make_unpack_byte_2bit_cpx_samples());
                             unpack_short_byte.push_back(make_unpack_short_byte_samples());
                         }
@@ -169,11 +184,12 @@ void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
             else if (ssize_ == 8)
                 {
                     top_block->connect(ad936x_iio_source, n, unpack_short_byte.at(n), 0);
-                    top_block->connect(unpack_short_byte.at(n), 0, gr_interleaved_char_to_complex_.at(n), 0);
-                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n;
+                    top_block->connect(unpack_short_byte.at(n), 0, gr_char_to_short_.at(n), 0);
+                    top_block->connect(gr_char_to_short_.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex_ for channel " << n;
                     if (dump_)
                         {
-                            top_block->connect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0);
+                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
                             DLOG(INFO) << "connected source to file sink";
                         }
                 }
@@ -231,13 +247,14 @@ void Ad936xCustomSignalSource::disconnect(gr::top_block_sptr top_block)
                 }
             else if (ssize_ == 8)
                 {
-                    top_block->connect(ad936x_iio_source, n, unpack_short_byte.at(n), 0);
-                    top_block->connect(unpack_short_byte.at(n), 0, unpack_byte_fourbits.at(n), 0);
-                    DLOG(INFO) << "disconnect ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n;
+                    top_block->disconnect(ad936x_iio_source, n, unpack_short_byte.at(n), 0);
+                    top_block->disconnect(unpack_short_byte.at(n), 0, gr_char_to_short_.at(n), 0);
+                    top_block->disconnect(gr_char_to_short_.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
+                    DLOG(INFO) << "disconnect ad936x_iio_source source to gr_interleaved_short_to_complex_ for channel " << n;
                     if (dump_)
                         {
-                            top_block->disconnect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "disconnect source to file sink";
+                            top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                            DLOG(INFO) << "connected source to file sink";
                         }
                 }
             else if (ssize_ == 4)
@@ -293,7 +310,7 @@ gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block()
         }
     else if (ssize_ == 8)
         {
-            return gr_interleaved_char_to_complex_.at(0);
+            return gr_interleaved_short_to_complex_.at(0);
         }
     else if (ssize_ == 4)
         {
@@ -317,7 +334,7 @@ gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block(int RF_channel)
         }
     else if (ssize_ == 8)
         {
-            return gr_interleaved_char_to_complex_.at(RF_channel);
+            return gr_interleaved_short_to_complex_.at(RF_channel);
         }
     else if (ssize_ == 4)
         {
diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
index 88bba68ef..11c5823a0 100644
--- a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
@@ -24,8 +24,9 @@
 #include "unpack_byte_2bit_cpx_samples.h"
 #include "unpack_byte_4bit_samples.h"
 #include "unpack_short_byte_samples.h"
+#include <gnuradio/blocks/char_to_short.h>
 #include <gnuradio/blocks/file_sink.h>
-#include <gnuradio/blocks/interleaved_char_to_complex.h>
+// #include <gnuradio/blocks/interleaved_char_to_complex.h>
 #include <gnuradio/blocks/interleaved_short_to_complex.h>
 #include <pmt/pmt.h>
 #include <cstdint>
@@ -73,8 +74,9 @@ private:
     std::vector<gr::blocks::file_sink::sptr> sink_;
     std::vector<std::string> filename_vec_;
 
+    std::vector<gr::blocks::char_to_short::sptr> gr_char_to_short_;
     std::vector<gr::blocks::interleaved_short_to_complex::sptr> gr_interleaved_short_to_complex_;
-    std::vector<gr::blocks::interleaved_char_to_complex::sptr> gr_interleaved_char_to_complex_;
+    //    std::vector<gr::blocks::interleaved_char_to_complex::sptr> gr_interleaved_char_to_complex_;
     std::vector<unpack_short_byte_samples_sptr> unpack_short_byte;
     std::vector<unpack_byte_4bit_samples_sptr> unpack_byte_fourbits;
     std::vector<unpack_byte_2bit_cpx_samples_sptr> unpack_byte_twobits;
@@ -109,8 +111,10 @@ private:
     bool spattern_;
     bool inverted_spectrum_ch0_;
     bool inverted_spectrum_ch1_;
+    double lo_attenuation_db_;
+    bool high_side_lo_;
 
-
+    std::vector<bool> inverted_spectrum_vec;
     int n_channels;
 };
 
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
index 77ebd64bc..a54a5a966 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -53,7 +53,9 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
     int fe_ctlport_,
     int ssize_,
     int bshift_,
-    bool spattern_)
+    bool spattern_,
+    double lo_attenuation_db_,
+    bool high_side_lo_)
 {
     return ad936x_iio_source_sptr(new ad936x_iio_source(
         pluto_uri_,
@@ -76,7 +78,9 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
         fe_ctlport_,
         ssize_,
         bshift_,
-        spattern_));
+        spattern_,
+        lo_attenuation_db_,
+        high_side_lo_));
 }
 
 void ad936x_iio_source::ad9361_channel_demux_and_record(ad936x_iio_samples *samples_in, int nchannels, std::vector<std::fstream> *files_out)
@@ -116,9 +120,11 @@ ad936x_iio_source::ad936x_iio_source(
     int fe_ctlport_,
     int ssize_,
     int bshift_,
-    bool spattern_) : gr::block("ad936x_iio_source",
-                          gr::io_signature::make(0, 0, 0),
-                          gr::io_signature::make(1, 4, sizeof(int16_t)))
+    bool spattern_,
+    double lo_attenuation_db_,
+    bool high_side_lo_) : gr::block("ad936x_iio_source",
+                              gr::io_signature::make(0, 0, 0),
+                              gr::io_signature::make(1, 4, sizeof(int16_t)))
 {
     ad936x_custom = std::make_unique<ad936x_iio_custom>(0, 0);
     try
@@ -137,7 +143,9 @@ ad936x_iio_source::ad936x_iio_source(
                             rf_gain_rx1_,
                             enable_ch0,
                             enable_ch1,
-                            freq_2ch) == true)
+                            freq_2ch,
+                            lo_attenuation_db_,
+                            high_side_lo_) == true)
                         {
                             std::cout << "ad936x_iio_source HW configured OK!\n";
 
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
index 7285ce15b..f3856b3d6 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
@@ -64,7 +64,9 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
     int fe_ctlport_,
     int ssize_,
     int bshift_,
-    bool spattern_);
+    bool spattern_,
+    double lo_attenuation_db_,
+    bool high_side_lo_);
 
 /*!
  * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband
@@ -107,7 +109,9 @@ private:
         int fe_ctlport_,
         int ssize_,
         int bshift_,
-        bool spattern_);
+        bool spattern_,
+        double lo_attenuation_db_,
+        bool high_side_lo_);
 
     ad936x_iio_source(
         std::string pluto_uri_,
@@ -130,7 +134,9 @@ private:
         int fe_ctlport_,
         int ssize_,
         int bshift_,
-        bool spattern_);
+        bool spattern_,
+        double lo_attenuation_db_,
+        bool high_side_lo_);
 
 
     void ad9361_channel_demux_to_buffer(ad936x_iio_samples *samples_in, int nchannels, gr_vector_void_star &output_items);
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index 92be896c4..3357a59d6 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -410,7 +410,9 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     double rf_gain_rx1_,
     bool enable_ch0,
     bool enable_ch1,
-    long long freq_2ch)
+    long long freq_2ch,
+    double lo_attenuation_db_,
+    bool high_side_lo_)
 
 {
     if (check_device() == false) return false;
@@ -510,17 +512,32 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     if (enable_ch1 == true and enable_ch0 == true and freq_ != freq_2ch)
         {
             std::cout << "Two channels enabled with different frequencies, enabling the external RF transverter board:\n";
-            long long int delta_freq_hz = freq_2ch - freq_;
-            if (delta_freq_hz < 0)
+            long long int lo_freq_hz = 0;
+            if (high_side_lo_ == false)
                 {
-                    std::cout << "Configuration problem: 2nd channel frequency is " << freq_2ch << " [Hz], must be higher than main channel (" << freq_ << " [Hz])\n";
-                    return false;
+                    std::cout << "Using LOW SIDE Local Oscillator (F_RF > F_LO)\n";
+                    lo_freq_hz = freq_2ch - freq_;
+                    if (lo_freq_hz < 0)
+                        {
+                            std::cout << "Configuration problem: 2nd channel frequency is " << freq_2ch << " [Hz], must be higher than main channel (" << freq_ << " [Hz])\n";
+                            return false;
+                        }
+                }
+            else
+                {
+                    std::cout << "Using HIGH SIDE Local Oscillator (F_RF < F_LO), consider baseband spectrum inversion.\n";
+                    lo_freq_hz = freq_2ch + freq_;
+                    if (lo_freq_hz < 0)
+                        {
+                            std::cout << "Configuration problem: 2nd channel frequency is " << freq_2ch << " [Hz], must be higher than main channel (" << freq_ << " [Hz])\n";
+                            return false;
+                        }
                 }
 
-            std::cout << "Configuring DDS Local Oscillator generation. LO Freq. is " << delta_freq_hz << " [Hz]\n";
+            std::cout << "Configuring DDS Local Oscillator generation. LO Freq. is " << lo_freq_hz << " [Hz]\n";
             PlutoTxEnable(true);
-            config_ad9361_dds(delta_freq_hz,
-                0,
+            config_ad9361_dds(lo_freq_hz,
+                lo_attenuation_db_,
                 0,
                 0.9,
                 0,
@@ -532,6 +549,30 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
             PlutoTxEnable(false);  //power down the TX LO to reduce interferences
         }
 
+
+    int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps);
+    if (set_filter_ret != 0)
+        {
+            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+        }
+
+    //testing: set manual RX filter chain
+    //    unsigned long RX_analog_bb_lpf_stop_hz = 1000000;
+    //    unsigned long TX_analog_bb_lpf_stop_hz = 1000000;
+    //
+    //    unsigned long FIR_lpf_passband_hz = 1000000;
+    //    unsigned long FIR_lpf_stopband_hz = 1200000;
+    //    int set_filter_ret = ad9361_set_bb_rate_custom_filter_manual(phy,
+    //        sample_rate_sps,
+    //        FIR_lpf_passband_hz,
+    //        FIR_lpf_stopband_hz,
+    //        RX_analog_bb_lpf_stop_hz,
+    //        TX_analog_bb_lpf_stop_hz);
+    //    if (set_filter_ret != 0)
+    //        {
+    //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+    //        }
+
     if (enable_ch0 == true)
         {
             n_channels++;
@@ -555,12 +596,12 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                     no_errors = false;
                 }
 
-            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
-            if (ret < 0)
-                {
-                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
-                    no_errors = false;
-                }
+            //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+            //            if (ret < 0)
+            //                {
+            //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+            //                    no_errors = false;
+            //                }
 
             long long set_rf_bw;
             ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
@@ -605,12 +646,24 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                     no_errors = false;
                 }
 
-            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+            //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+            //            if (ret < 0)
+            //                {
+            //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+            //                    no_errors = false;
+            //                }
+
+            long long set_rf_bw;
+            ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
             if (ret < 0)
                 {
-                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+                    std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
                     no_errors = false;
                 }
+            else
+                {
+                    std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
+                }
 
             if (setRXGain(1, gain_mode_rx1_, rf_gain_rx1_) == false)
                 {
@@ -619,29 +672,6 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                 }
         }
 
-    int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps);
-    if (set_filter_ret != 0)
-        {
-            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
-        }
-
-    //testing: set manual RX filter chain
-    //    unsigned long RX_analog_bb_lpf_stop_hz = 1000000;
-    //    unsigned long TX_analog_bb_lpf_stop_hz = 1000000;
-    //
-    //    unsigned long FIR_lpf_passband_hz = 1000000;
-    //    unsigned long FIR_lpf_stopband_hz = 1200000;
-    //  int set_filter_ret = ad9361_set_bb_rate_custom_filter_manual(phy,
-    //        sample_rate_sps,
-    //        FIR_lpf_passband_hz,
-    //        FIR_lpf_stopband_hz,
-    //        RX_analog_bb_lpf_stop_hz,
-    //        TX_analog_bb_lpf_stop_hz);
-    //    if (set_filter_ret != 0)
-    //        {
-    //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
-    //        }
-
     std::cout << "AD936x Front-end configuration summary: \n";
     std::cout << "RF frequency tunned in AD936x: " << freq_ << " [Hz]\n";
     std::cout << "Baseband sampling frequency: " << sample_rate_sps << " [SPS]\n";
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.h b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
index 4d95fb340..cbf5e65e7 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.h
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
@@ -53,7 +53,9 @@ public:
         double rf_gain_rx1_,
         bool enable_ch0,
         bool enable_ch1,
-        long long freq_2ch);
+        long long freq_2ch,
+        double lo_attenuation_db_,
+        bool high_side_lo_);
 
     bool calibrate(int ch, double bw_hz);
 

From db2addd9bc4e5433b20dcd560e1efc8ccdad29a5 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 30 Aug 2022 16:50:38 +0200
Subject: [PATCH 18/31] AD936x custom source switch from custom to default
 baseband filters

---
 .../signal_source/libs/ad936x_iio_custom.cc           | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index 3357a59d6..705180ef7 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -549,13 +549,18 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
             PlutoTxEnable(false);  //power down the TX LO to reduce interferences
         }
 
-
-    int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps);
+    int set_filter_ret = ad9361_set_bb_rate(phy, sample_rate_sps);
     if (set_filter_ret != 0)
         {
-            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+            std::cout << "Warning: Unable to set AD936x ad9361_set_bb_rate parameters!\n";
         }
 
+    //    int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps);
+    //    if (set_filter_ret != 0)
+    //        {
+    //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
+    //        }
+
     //testing: set manual RX filter chain
     //    unsigned long RX_analog_bb_lpf_stop_hz = 1000000;
     //    unsigned long TX_analog_bb_lpf_stop_hz = 1000000;

From dd1ce7893c0da4835be653ec6c8e5b3c2d7e41b7 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 13 Sep 2022 17:34:23 +0200
Subject: [PATCH 19/31] Adding ad936x custom source optional interchannel delay
 compensation

---
 .../adapters/ad936x_custom_signal_source.cc   | 297 +++++++++++++++---
 .../adapters/ad936x_custom_signal_source.h    |   6 +
 .../gnuradio_blocks/ad936x_iio_source.cc      |  12 +-
 .../gnuradio_blocks/ad936x_iio_source.h       |   9 +-
 .../signal_source/libs/ad936x_iio_custom.cc   |  88 +++---
 .../signal_source/libs/ad936x_iio_custom.h    |   4 +-
 6 files changed, 314 insertions(+), 102 deletions(-)

diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
index 5229405dd..067bf0e3d 100644
--- a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc
@@ -62,7 +62,10 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
       inverted_spectrum_ch0_(configuration->property(role + ".inverted_spectrum_ch0", false)),
       inverted_spectrum_ch1_(configuration->property(role + ".inverted_spectrum_ch1", false)),
       lo_attenuation_db_(configuration->property(role + ".lo_attenuation_db", 6.0)),
-      high_side_lo_(configuration->property(role + ".high_side_lo", false))
+      high_side_lo_(configuration->property(role + ".high_side_lo", false)),
+      tx_lo_channel_(configuration->property(role + ".tx_lo_channel", 1)),
+      rx0_to_rx1_delay_ns_(configuration->property(role + ".rx0_to_rx1_delay_ns", 0.0))
+
 
 {
     if (item_type_ == "gr_complex")
@@ -99,7 +102,8 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
                 bshift_,
                 spattern_,
                 lo_attenuation_db_,
-                high_side_lo_);
+                high_side_lo_,
+                tx_lo_channel_);
 
             n_channels = 1;
             if (enable_ch0 == true and enable_ch1 == true)
@@ -107,6 +111,42 @@ Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface*
                     n_channels = 2;
                 }
 
+            int delay_samples = 0;
+            if (n_channels == 2 and rx0_to_rx1_delay_ns_ != 0.0)
+                {
+                    double ts = 1.0 / static_cast<double>(sample_rate_);
+                    delay_samples = std::round(ts * rx0_to_rx1_delay_ns_ * 1e-9);
+                    if (delay_samples != 0)
+                        {
+                            delay_enabled = true;
+                            if (delay_samples > 0)
+                                {
+                                    apply_delay_on_rx0 = true;
+                                    LOG(INFO) << " Instantiating delay of rx0 equal to " << delay_samples << " samples.";
+                                }
+                            else
+                                {
+                                    // delay applied to rx1 instead.
+                                    apply_delay_on_rx0 = false;
+                                    delay_samples = -delay_samples;
+                                    LOG(INFO) << " Instantiating delay of rx1 equal to " << delay_samples << " samples.";
+                                }
+                        }
+                    else
+                        {
+                            LOG(INFO) << " Specified rx0_to_rx1 delay is smaller than the front-end sample period.";
+                        }
+                }
+            else
+                {
+                    apply_delay_on_rx0 = false;
+                    delay_enabled = false;
+                }
+
+            if (delay_enabled == true)
+                {
+                    gr_delay = gr::blocks::delay::make(sizeof(gr_complex), delay_samples);
+                }
 
             for (int n = 0; n < n_channels; n++)
                 {
@@ -175,10 +215,44 @@ void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
                 {
                     top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
                     DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
-                    if (dump_)
+                    if (delay_enabled == true)
                         {
-                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "connected source to file sink";
+                            if (n == 0 and apply_delay_on_rx0 == true)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else if (n == 1 and apply_delay_on_rx0 == false)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else
+                                {
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected source to file sink";
+                                        }
+                                }
+                        }
+                    else
+                        {
+                            if (dump_)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                    DLOG(INFO) << "connected source to file sink";
+                                }
                         }
                 }
             else if (ssize_ == 8)
@@ -186,11 +260,44 @@ void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
                     top_block->connect(ad936x_iio_source, n, unpack_short_byte.at(n), 0);
                     top_block->connect(unpack_short_byte.at(n), 0, gr_char_to_short_.at(n), 0);
                     top_block->connect(gr_char_to_short_.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
-                    DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex_ for channel " << n;
-                    if (dump_)
+                    if (delay_enabled == true)
                         {
-                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "connected source to file sink";
+                            if (n == 0 and apply_delay_on_rx0 == true)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else if (n == 1 and apply_delay_on_rx0 == false)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else
+                                {
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected source to file sink";
+                                        }
+                                }
+                        }
+                    else
+                        {
+                            if (dump_)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                    DLOG(INFO) << "connected source to file sink";
+                                }
                         }
                 }
             else if (ssize_ == 4)
@@ -199,10 +306,45 @@ void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
                     top_block->connect(unpack_short_byte.at(n), 0, unpack_byte_fourbits.at(n), 0);
                     top_block->connect(unpack_byte_fourbits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
                     DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
-                    if (dump_)
+
+                    if (delay_enabled == true)
                         {
-                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "connected source to file sink";
+                            if (n == 0 and apply_delay_on_rx0 == true)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else if (n == 1 and apply_delay_on_rx0 == false)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else
+                                {
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected source to file sink";
+                                        }
+                                }
+                        }
+                    else
+                        {
+                            if (dump_)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                    DLOG(INFO) << "connected source to file sink";
+                                }
                         }
                 }
             else if (ssize_ == 2)
@@ -211,20 +353,91 @@ void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block)
                     top_block->connect(unpack_short_byte.at(n), 0, unpack_byte_twobits.at(n), 0);
                     top_block->connect(unpack_byte_twobits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0);
                     DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n;
-                    if (dump_)
+
+                    if (delay_enabled == true)
                         {
-                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "connected source to file sink";
+                            if (n == 0 and apply_delay_on_rx0 == true)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else if (n == 1 and apply_delay_on_rx0 == false)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else
+                                {
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected source to file sink";
+                                        }
+                                }
+                        }
+                    else
+                        {
+                            if (dump_)
+
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                    DLOG(INFO) << "connected source to file sink";
+                                }
                         }
                 }
             else
                 {
                     top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0);
                     DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n;
-                    if (dump_)
+
+                    if (delay_enabled == true)
                         {
-                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
-                            DLOG(INFO) << "connected source to file sink";
+                            if (n == 0 and apply_delay_on_rx0 == true)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else if (n == 1 and apply_delay_on_rx0 == false)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, gr_delay, 0);
+                                    DLOG(INFO) << "connected gr_interleaved_short_to_complex to gr_delay for channel " << n;
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_delay, 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected delayed source to file sink";
+                                        }
+                                }
+                            else
+                                {
+                                    if (dump_)
+                                        {
+                                            top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                            DLOG(INFO) << "connected source to file sink";
+                                        }
+                                }
+                        }
+                    else
+                        {
+                            if (dump_)
+                                {
+                                    top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0);
+                                    DLOG(INFO) << "connected source to file sink";
+                                }
                         }
                 }
         }
@@ -304,45 +517,25 @@ gr::basic_block_sptr Ad936xCustomSignalSource::get_left_block()
 
 gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block()
 {
-    if (ssize_ == 16)
-        {
-            return gr_interleaved_short_to_complex_.at(0);
-        }
-    else if (ssize_ == 8)
-        {
-            return gr_interleaved_short_to_complex_.at(0);
-        }
-    else if (ssize_ == 4)
-        {
-            return gr_interleaved_short_to_complex_.at(0);
-        }
-    else if (ssize_ == 2)
-        {
-            return gr_interleaved_short_to_complex_.at(0);
-        }
-    else
-        {
-            return gr_interleaved_short_to_complex_.at(0);
-        }
+    return gr_interleaved_short_to_complex_.at(0);
 }
 
 gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block(int RF_channel)
 {
-    if (ssize_ == 16)
+    if (delay_enabled == true)
         {
-            return gr_interleaved_short_to_complex_.at(RF_channel);
-        }
-    else if (ssize_ == 8)
-        {
-            return gr_interleaved_short_to_complex_.at(RF_channel);
-        }
-    else if (ssize_ == 4)
-        {
-            return gr_interleaved_short_to_complex_.at(RF_channel);
-        }
-    else if (ssize_ == 2)
-        {
-            return gr_interleaved_short_to_complex_.at(RF_channel);
+            if (RF_channel == 0 and apply_delay_on_rx0 == true)
+                {
+                    return gr_delay;
+                }
+            else if (RF_channel == 1 and apply_delay_on_rx0 == false)
+                {
+                    return gr_delay;
+                }
+            else
+                {
+                    return gr_interleaved_short_to_complex_.at(RF_channel);
+                }
         }
     else
         {
diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
index 11c5823a0..4d4ae813c 100644
--- a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
+++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h
@@ -27,6 +27,7 @@
 #include <gnuradio/blocks/char_to_short.h>
 #include <gnuradio/blocks/file_sink.h>
 // #include <gnuradio/blocks/interleaved_char_to_complex.h>
+#include <gnuradio/blocks/delay.h>
 #include <gnuradio/blocks/interleaved_short_to_complex.h>
 #include <pmt/pmt.h>
 #include <cstdint>
@@ -74,6 +75,7 @@ private:
     std::vector<gr::blocks::file_sink::sptr> sink_;
     std::vector<std::string> filename_vec_;
 
+    gr::blocks::delay::sptr gr_delay;
     std::vector<gr::blocks::char_to_short::sptr> gr_char_to_short_;
     std::vector<gr::blocks::interleaved_short_to_complex::sptr> gr_interleaved_short_to_complex_;
     //    std::vector<gr::blocks::interleaved_char_to_complex::sptr> gr_interleaved_char_to_complex_;
@@ -113,6 +115,10 @@ private:
     bool inverted_spectrum_ch1_;
     double lo_attenuation_db_;
     bool high_side_lo_;
+    int tx_lo_channel_;
+    double rx0_to_rx1_delay_ns_;
+    bool delay_enabled;
+    bool apply_delay_on_rx0;
 
     std::vector<bool> inverted_spectrum_vec;
     int n_channels;
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
index a54a5a966..ffaae3e9c 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -55,7 +55,8 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
     int bshift_,
     bool spattern_,
     double lo_attenuation_db_,
-    bool high_side_lo_)
+    bool high_side_lo_,
+    int tx_lo_channel_)
 {
     return ad936x_iio_source_sptr(new ad936x_iio_source(
         pluto_uri_,
@@ -80,7 +81,8 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
         bshift_,
         spattern_,
         lo_attenuation_db_,
-        high_side_lo_));
+        high_side_lo_,
+        tx_lo_channel_));
 }
 
 void ad936x_iio_source::ad9361_channel_demux_and_record(ad936x_iio_samples *samples_in, int nchannels, std::vector<std::fstream> *files_out)
@@ -122,7 +124,8 @@ ad936x_iio_source::ad936x_iio_source(
     int bshift_,
     bool spattern_,
     double lo_attenuation_db_,
-    bool high_side_lo_) : gr::block("ad936x_iio_source",
+    bool high_side_lo_,
+    int tx_lo_channel_) : gr::block("ad936x_iio_source",
                               gr::io_signature::make(0, 0, 0),
                               gr::io_signature::make(1, 4, sizeof(int16_t)))
 {
@@ -145,7 +148,8 @@ ad936x_iio_source::ad936x_iio_source(
                             enable_ch1,
                             freq_2ch,
                             lo_attenuation_db_,
-                            high_side_lo_) == true)
+                            high_side_lo_,
+                            tx_lo_channel_) == true)
                         {
                             std::cout << "ad936x_iio_source HW configured OK!\n";
 
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
index f3856b3d6..411906a3e 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h
@@ -66,7 +66,8 @@ ad936x_iio_source_sptr ad936x_iio_make_source_sptr(
     int bshift_,
     bool spattern_,
     double lo_attenuation_db_,
-    bool high_side_lo_);
+    bool high_side_lo_,
+    int tx_lo_channel_);
 
 /*!
  * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband
@@ -111,7 +112,8 @@ private:
         int bshift_,
         bool spattern_,
         double lo_attenuation_db_,
-        bool high_side_lo_);
+        bool high_side_lo_,
+        int tx_lo_channel_);
 
     ad936x_iio_source(
         std::string pluto_uri_,
@@ -136,7 +138,8 @@ private:
         int bshift_,
         bool spattern_,
         double lo_attenuation_db_,
-        bool high_side_lo_);
+        bool high_side_lo_,
+        int tx_lo_channel_);
 
 
     void ad9361_channel_demux_to_buffer(ad936x_iio_samples *samples_in, int nchannels, gr_vector_void_star &output_items);
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index 705180ef7..c1bb56f5e 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -258,17 +258,39 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
     // ENABLE DDS on TX1
     // Configure LO channel
     std::vector<std::string> params_phy;
+    std::vector<std::string> params_dds;
 
     params_phy.push_back("out_altvoltage1_TX_LO_frequency=" +
                          std::to_string(freq_rf_tx_hz_));
     double disabled_tx_attenuation = 89.75;
-    if (channel == 1)
+    if (channel == 0)
         {
             params_phy.push_back("out_voltage0_hardwaregain=" +
                                  std::to_string(-tx_attenuation_db_));
             //disable the other TX
             params_phy.push_back("out_voltage1_hardwaregain=" +
                                  std::to_string(-disabled_tx_attenuation));
+
+            configure_params(phy, params_phy);
+
+            //DDS TX CH1 I (tone #1)
+            params_dds.push_back("out_altvoltage0_TX1_I_F1_frequency=" +
+                                 std::to_string(freq_dds_tx_hz_));
+            params_dds.push_back("out_altvoltage0_TX1_I_F1_phase=" +
+                                 std::to_string(phase_dds_deg_ * 1000.0));
+            params_dds.push_back("out_altvoltage0_TX1_I_F1_scale=" +
+                                 std::to_string(scale_dds_));
+            params_dds.push_back("out_altvoltage0_TX1_I_F1_raw=1");
+            //DDS TX CH1 Q (tone #1)
+            params_dds.push_back("out_altvoltage2_TX1_Q_F1_frequency=" +
+                                 std::to_string(freq_dds_tx_hz_));
+            params_dds.push_back("out_altvoltage2_TX1_Q_F1_phase=" +
+                                 std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
+            params_dds.push_back("out_altvoltage2_TX1_Q_F1_scale=" +
+                                 std::to_string(scale_dds_));
+            params_dds.push_back("out_altvoltage2_TX1_Q_F1_raw=1");
+
+            configure_params(dds_dev, params_dds);
         }
     else
         {
@@ -277,46 +299,29 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
             //disable the other TX
             params_phy.push_back("out_voltage0_hardwaregain=" +
                                  std::to_string(-disabled_tx_attenuation));
+
+            configure_params(phy, params_phy);
+
+            //DDS TX CH2 I (tone #1)
+            params_dds.push_back("out_altvoltage4_TX2_I_F1_frequency=" +
+                                 std::to_string(freq_dds_tx_hz_));
+            params_dds.push_back("out_altvoltage4_TX2_I_F1_phase=" +
+                                 std::to_string(phase_dds_deg_ * 1000.0));
+            params_dds.push_back("out_altvoltage4_TX2_I_F1_scale=" +
+                                 std::to_string(scale_dds_));
+            params_dds.push_back("out_altvoltage4_TX2_I_F1_raw=1");
+            //DDS TX CH2 Q (tone #1)
+            params_dds.push_back("out_altvoltage6_TX2_Q_F1_frequency=" +
+                                 std::to_string(freq_dds_tx_hz_));
+            params_dds.push_back("out_altvoltage6_TX2_Q_F1_phase=" +
+                                 std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
+            params_dds.push_back("out_altvoltage6_TX2_Q_F1_scale=" +
+                                 std::to_string(scale_dds_));
+            params_dds.push_back("out_altvoltage6_TX2_Q_F1_raw=1");
+
+            configure_params(dds_dev, params_dds);
         }
-    configure_params(phy, params_phy);
 
-    std::vector<std::string> params_dds;
-
-    //DDS TX CH1 I (tone #1)
-    //    params_dds.push_back("out_altvoltage0_TX1_I_F1_frequency=" +
-    //                         std::to_string(freq_dds_tx_hz_));
-    //    params_dds.push_back("out_altvoltage0_TX1_I_F1_phase=" +
-    //                         std::to_string(phase_dds_deg_ * 1000.0));
-    //    params_dds.push_back("out_altvoltage0_TX1_I_F1_scale=" +
-    //                         std::to_string(scale_dds_));
-    //    params_dds.push_back("out_altvoltage0_TX1_I_F1_raw=1");
-    //    //DDS TX CH1 Q (tone #1)
-    //    params_dds.push_back("out_altvoltage2_TX1_Q_F1_frequency=" +
-    //                         std::to_string(freq_dds_tx_hz_));
-    //    params_dds.push_back("out_altvoltage2_TX1_Q_F1_phase=" +
-    //                         std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
-    //    params_dds.push_back("out_altvoltage2_TX1_Q_F1_scale=" +
-    //                         std::to_string(scale_dds_));
-    //    params_dds.push_back("out_altvoltage2_TX1_Q_F1_raw=1");
-
-    //DDS TX CH2 I (tone #1)
-    params_dds.push_back("out_altvoltage4_TX2_I_F1_frequency=" +
-                         std::to_string(freq_dds_tx_hz_));
-    params_dds.push_back("out_altvoltage4_TX2_I_F1_phase=" +
-                         std::to_string(phase_dds_deg_ * 1000.0));
-    params_dds.push_back("out_altvoltage4_TX2_I_F1_scale=" +
-                         std::to_string(scale_dds_));
-    params_dds.push_back("out_altvoltage4_TX2_I_F1_raw=1");
-    //DDS TX CH2 Q (tone #1)
-    params_dds.push_back("out_altvoltage6_TX2_Q_F1_frequency=" +
-                         std::to_string(freq_dds_tx_hz_));
-    params_dds.push_back("out_altvoltage6_TX2_Q_F1_phase=" +
-                         std::to_string(phase_dds_deg_ * 1000.0 + 270000.0));
-    params_dds.push_back("out_altvoltage6_TX2_Q_F1_scale=" +
-                         std::to_string(scale_dds_));
-    params_dds.push_back("out_altvoltage6_TX2_Q_F1_raw=1");
-
-    configure_params(dds_dev, params_dds);
 
     return true;
 }
@@ -412,7 +417,8 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     bool enable_ch1,
     long long freq_2ch,
     double lo_attenuation_db_,
-    bool high_side_lo_)
+    bool high_side_lo_,
+    int tx_lo_channel_)
 
 {
     if (check_device() == false) return false;
@@ -541,7 +547,7 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                 0,
                 0.9,
                 0,
-                2);
+                tx_lo_channel_);
             std::cout << "Configuring DDS Local Oscillator generation DONE\n";
         }
     else
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.h b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
index cbf5e65e7..de36f5b5b 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.h
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
@@ -55,7 +55,8 @@ public:
         bool enable_ch1,
         long long freq_2ch,
         double lo_attenuation_db_,
-        bool high_side_lo_);
+        bool high_side_lo_,
+        int tx_lo_channel_);
 
     bool calibrate(int ch, double bw_hz);
 
@@ -79,7 +80,6 @@ public:
     void pop_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer);
 
     void push_sample_buffer(std::shared_ptr<ad936x_iio_samples> &current_buffer);
-
     int n_channels;
 
 private:

From 205583bb277de1d911ee8e91a1c9a4e85167acae Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Thu, 22 Sep 2022 15:15:07 +0200
Subject: [PATCH 20/31] Update ad936x iio custom lib

---
 .../signal_source/libs/ad936x_iio_custom.cc   | 122 +++++++++---------
 1 file changed, 60 insertions(+), 62 deletions(-)

diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index c1bb56f5e..efd67e778 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -512,9 +512,6 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
             no_errors = false;
         }
 
-
-    std::cout << "no_errors: " << no_errors << "\n";
-
     if (enable_ch1 == true and enable_ch0 == true and freq_ != freq_2ch)
         {
             std::cout << "Two channels enabled with different frequencies, enabling the external RF transverter board:\n";
@@ -583,10 +580,8 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     //        {
     //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
     //        }
-
     if (enable_ch0 == true)
         {
-            n_channels++;
             std::cerr << "* Get AD9361 Phy RX channel 0...\n";
             std::stringstream name;
             name.str("");
@@ -599,44 +594,45 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                     std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
                     no_errors = false;
                 }
-
-            ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
-            if (ret < 0)
-                {
-                    std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
-                    no_errors = false;
-                }
-
-            //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
-            //            if (ret < 0)
-            //                {
-            //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
-            //                    no_errors = false;
-            //                }
-
-            long long set_rf_bw;
-            ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
-            if (ret < 0)
-                {
-                    std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
-                    no_errors = false;
-                }
             else
                 {
-                    std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
-                }
+                    ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
+                    if (ret < 0)
+                        {
+                            std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
+                            no_errors = false;
+                        }
+
+                    //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+                    //            if (ret < 0)
+                    //                {
+                    //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+                    //                    no_errors = false;
+                    //                }
+
+                    long long set_rf_bw;
+                    ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
+                    if (ret < 0)
+                        {
+                            std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
+                            no_errors = false;
+                        }
+                    else
+                        {
+                            std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
+                        }
 
 
-            if (setRXGain(0, gain_mode_rx0_, rf_gain_rx0_) == false)
-                {
-                    std::cerr << "Info: setRXGain read returned false \n";
-                    no_errors = false;
+                    if (setRXGain(0, gain_mode_rx0_, rf_gain_rx0_) == false)
+                        {
+                            std::cerr << "Info: setRXGain read returned false \n";
+                            no_errors = false;
+                        }
                 }
         }
 
     if (enable_ch1 == true)
         {
-            n_channels++;
             std::cerr << "* Get AD9361 Phy RX channel 1...\n";
             std::stringstream name;
             name.str("");
@@ -649,37 +645,39 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
                     std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
                     no_errors = false;
                 }
-
-            ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
-            if (ret < 0)
-                {
-                    std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
-                    no_errors = false;
-                }
-
-            //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
-            //            if (ret < 0)
-            //                {
-            //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
-            //                    no_errors = false;
-            //                }
-
-            long long set_rf_bw;
-            ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
-            if (ret < 0)
-                {
-                    std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
-                    no_errors = false;
-                }
             else
                 {
-                    std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
-                }
+                    ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str());
+                    if (ret < 0)
+                        {
+                            std::cerr << "Warning: rf_port_select write returned: " << ret << "\n";
+                            no_errors = false;
+                        }
 
-            if (setRXGain(1, gain_mode_rx1_, rf_gain_rx1_) == false)
-                {
-                    std::cerr << "Info: setRXGain read returned false \n";
-                    no_errors = false;
+                    //            ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_);
+                    //            if (ret < 0)
+                    //                {
+                    //                    std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n";
+                    //                    no_errors = false;
+                    //                }
+
+                    long long set_rf_bw;
+                    ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw);
+                    if (ret < 0)
+                        {
+                            std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n";
+                            no_errors = false;
+                        }
+                    else
+                        {
+                            std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n";
+                        }
+
+                    if (setRXGain(1, gain_mode_rx1_, rf_gain_rx1_) == false)
+                        {
+                            std::cerr << "Info: setRXGain read returned false \n";
+                            no_errors = false;
+                        }
                 }
         }
 

From be2e5d8d5e495665f0d4519d6299df9a5b63b8cf Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Fri, 9 Jun 2023 14:09:18 +0200
Subject: [PATCH 21/31] Adding c2bits sample stream to UDP custom source

---
 .../gr_complex_ip_packet_source.cc            | 251 +++++++++++-------
 .../gr_complex_ip_packet_source.h             |   4 +-
 2 files changed, 163 insertions(+), 92 deletions(-)

diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
index 6cead4e46..20f829e6a 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
@@ -30,6 +30,11 @@
 const int FIFO_SIZE = 1472000;
 
 
+struct byte_2bit_struct
+{
+    signed two_bit_sample : 2;  // <- 2 bits wide only
+};
+
 /* 4 bytes IP address */
 typedef struct gr_ip_address
 {
@@ -120,6 +125,11 @@ Gr_Complex_Ip_Packet_Source::Gr_Complex_Ip_Packet_Source(std::string src_device,
             d_wire_sample_type = 1;
             d_bytes_per_sample = d_n_baseband_channels * 2;
         }
+    else if (wire_sample_type == "c2bits")
+        {
+            d_wire_sample_type = 5;
+            d_bytes_per_sample = d_n_baseband_channels;
+        }
     else if (wire_sample_type == "c4bits")
         {
             d_wire_sample_type = 2;
@@ -183,7 +193,8 @@ bool Gr_Complex_Ip_Packet_Source::stop()
 bool Gr_Complex_Ip_Packet_Source::open()
 {
     std::array<char, PCAP_ERRBUF_SIZE> errbuf{};
-    boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    gr::thread::scoped_lock guard(d_setlock);
     // open device for reading
     descr = pcap_open_live(d_src_device.c_str(), 1500, 1, 1000, errbuf.data());
     if (descr == nullptr)
@@ -239,7 +250,7 @@ void Gr_Complex_Ip_Packet_Source::static_pcap_callback(u_char *args, const struc
 void Gr_Complex_Ip_Packet_Source::pcap_callback(__attribute__((unused)) u_char *args, __attribute__((unused)) const struct pcap_pkthdr *pkthdr,
     const u_char *packet)
 {
-    boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
 
     const gr_ip_header *ih;
     const gr_udp_header *uh;
@@ -321,107 +332,164 @@ void Gr_Complex_Ip_Packet_Source::my_pcap_loop_thread(pcap_t *pcap_handle)
 
 void Gr_Complex_Ip_Packet_Source::demux_samples(const gr_vector_void_star &output_items, int num_samples_readed)
 {
-    for (int n = 0; n < num_samples_readed; n++)
+    if (d_wire_sample_type == 5)
         {
-            switch (d_wire_sample_type)
+            //interleaved 2-bit I 2-bit Q samples packed in bytes: 1 byte -> 2 complex samples
+            int nsample = 0;
+            byte_2bit_struct sample{};  // <- 2 bits wide only
+            int real;
+            int imag;
+            for (int nbyte = 0; nbyte < num_samples_readed / 2; nbyte++)
                 {
-                case 1:  // interleaved byte samples
                     for (const auto &output_item : output_items)
                         {
-                            int8_t real;
-                            int8_t imag;
-                            real = fifo_buff[fifo_read_ptr++];
-                            imag = fifo_buff[fifo_read_ptr++];
+                            // Read packed input sample (1 byte = 2 complex samples)
+                            // *     Packing Order
+                            // *     Most Significant Nibble  - Sample n
+                            // *     Least Significant Nibble - Sample n+1
+                            // *     Bit Packing order in Nibble Q1 Q0 I1 I0
+                            // normal
+                            int8_t c = fifo_buff[fifo_read_ptr++];
+
+                            // Q[n]
+                            sample.two_bit_sample = (c >> 6) & 3;
+                            imag = (2 * static_cast<int8_t>(sample.two_bit_sample) + 1);
+                            // I[n]
+                            sample.two_bit_sample = (c >> 4) & 3;
+                            real = (2 * static_cast<int8_t>(sample.two_bit_sample) + 1);
+
                             if (d_IQ_swap)
                                 {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                    static_cast<gr_complex *>(output_item)[nsample] = gr_complex(real, imag);
                                 }
                             else
                                 {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
-                                }
-                        }
-                    break;
-                case 2:  // 4-bit samples
-                    for (const auto &output_item : output_items)
-                        {
-                            int8_t real;
-                            int8_t imag;
-                            uint8_t tmp_char2;
-                            tmp_char2 = fifo_buff[fifo_read_ptr] & 0x0F;
-                            if (tmp_char2 >= 8)
-                                {
-                                    real = 2 * (tmp_char2 - 16) + 1;
-                                }
-                            else
-                                {
-                                    real = 2 * tmp_char2 + 1;
-                                }
-                            tmp_char2 = fifo_buff[fifo_read_ptr++] >> 4;
-                            tmp_char2 = tmp_char2 & 0x0F;
-                            if (tmp_char2 >= 8)
-                                {
-                                    imag = 2 * (tmp_char2 - 16) + 1;
-                                }
-                            else
-                                {
-                                    imag = 2 * tmp_char2 + 1;
+                                    static_cast<gr_complex *>(output_item)[nsample] = gr_complex(imag, real);
                                 }
+
+
+                            // Q[n+1]
+                            sample.two_bit_sample = (c >> 2) & 3;
+                            imag = (2 * static_cast<int8_t>(sample.two_bit_sample) + 1);
+                            // I[n+1]
+                            sample.two_bit_sample = c & 3;
+                            real = (2 * static_cast<int8_t>(sample.two_bit_sample) + 1);
+
                             if (d_IQ_swap)
                                 {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
+                                    static_cast<gr_complex *>(output_item)[nsample + 1] = gr_complex(real, imag);
                                 }
                             else
                                 {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                    static_cast<gr_complex *>(output_item)[nsample + 1] = gr_complex(imag, real);
                                 }
                         }
-                    break;
-                case 3:  // interleaved float samples
-                    for (const auto &output_item : output_items)
-                        {
-                            float real;
-                            float imag;
-                            memcpy(&real, &fifo_buff[fifo_read_ptr], sizeof(real));
-                            fifo_read_ptr += 4;  // Four bytes in float
-                            memcpy(&imag, &fifo_buff[fifo_read_ptr], sizeof(imag));
-                            fifo_read_ptr += 4;  // Four bytes in float
-                            if (d_IQ_swap)
-                                {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
-                                }
-                            else
-                                {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
-                                }
-                        }
-                    break;
-                case 4:  // interleaved short samples
-                    for (const auto &output_item : output_items)
-                        {
-                            int16_t real;
-                            int16_t imag;
-                            memcpy(&real, &fifo_buff[fifo_read_ptr], sizeof(real));
-                            fifo_read_ptr += 2;  // two bytes in short
-                            memcpy(&imag, &fifo_buff[fifo_read_ptr], sizeof(imag));
-                            fifo_read_ptr += 2;  // two bytes in short
-                            if (d_IQ_swap)
-                                {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
-                                }
-                            else
-                                {
-                                    static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
-                                }
-                        }
-                    break;
-                default:
-                    std::cout << "Unknown wire sample type\n";
-                    exit(0);
                 }
-            if (fifo_read_ptr == FIFO_SIZE)
+        }
+    else
+        {
+            for (int n = 0; n < num_samples_readed; n++)
                 {
-                    fifo_read_ptr = 0;
+                    switch (d_wire_sample_type)
+                        {
+                        case 1:  // interleaved byte samples
+                            for (const auto &output_item : output_items)
+                                {
+                                    int8_t real;
+                                    int8_t imag;
+                                    real = fifo_buff[fifo_read_ptr++];
+                                    imag = fifo_buff[fifo_read_ptr++];
+                                    if (d_IQ_swap)
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                        }
+                                    else
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
+                                        }
+                                }
+                            break;
+                        case 2:  // 4-bit samples
+                            for (const auto &output_item : output_items)
+                                {
+                                    int8_t real;
+                                    int8_t imag;
+                                    uint8_t tmp_char2;
+                                    tmp_char2 = fifo_buff[fifo_read_ptr] & 0x0F;
+                                    if (tmp_char2 >= 8)
+                                        {
+                                            real = 2 * (tmp_char2 - 16) + 1;
+                                        }
+                                    else
+                                        {
+                                            real = 2 * tmp_char2 + 1;
+                                        }
+                                    tmp_char2 = fifo_buff[fifo_read_ptr++] >> 4;
+                                    tmp_char2 = tmp_char2 & 0x0F;
+                                    if (tmp_char2 >= 8)
+                                        {
+                                            imag = 2 * (tmp_char2 - 16) + 1;
+                                        }
+                                    else
+                                        {
+                                            imag = 2 * tmp_char2 + 1;
+                                        }
+                                    if (d_IQ_swap)
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
+                                        }
+                                    else
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                        }
+                                }
+                            break;
+                        case 3:  // interleaved float samples
+                            for (const auto &output_item : output_items)
+                                {
+                                    float real;
+                                    float imag;
+                                    memcpy(&real, &fifo_buff[fifo_read_ptr], sizeof(real));
+                                    fifo_read_ptr += 4;  // Four bytes in float
+                                    memcpy(&imag, &fifo_buff[fifo_read_ptr], sizeof(imag));
+                                    fifo_read_ptr += 4;  // Four bytes in float
+                                    if (d_IQ_swap)
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                        }
+                                    else
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
+                                        }
+                                }
+                            break;
+                        case 4:  // interleaved short samples
+                            for (const auto &output_item : output_items)
+                                {
+                                    int16_t real;
+                                    int16_t imag;
+                                    memcpy(&real, &fifo_buff[fifo_read_ptr], sizeof(real));
+                                    fifo_read_ptr += 2;  // two bytes in short
+                                    memcpy(&imag, &fifo_buff[fifo_read_ptr], sizeof(imag));
+                                    fifo_read_ptr += 2;  // two bytes in short
+                                    if (d_IQ_swap)
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(real, imag);
+                                        }
+                                    else
+                                        {
+                                            static_cast<gr_complex *>(output_item)[n] = gr_complex(imag, real);
+                                        }
+                                }
+                            break;
+                        default:
+                            std::cout << "Unknown wire sample type\n";
+                            exit(0);
+                        }
+                    if (fifo_read_ptr == FIFO_SIZE)
+                        {
+                            fifo_read_ptr = 0;
+                        }
                 }
         }
 }
@@ -432,7 +500,7 @@ int Gr_Complex_Ip_Packet_Source::work(int noutput_items,
     gr_vector_void_star &output_items)
 {
     // send samples to next GNU Radio block
-    boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
     if (fifo_items == 0)
         {
             return 0;
@@ -446,21 +514,24 @@ int Gr_Complex_Ip_Packet_Source::work(int noutput_items,
     int num_samples_readed;
     int bytes_requested;
 
-    bytes_requested = noutput_items * d_bytes_per_sample;
+    bytes_requested = static_cast<int>(static_cast<float>(noutput_items) * d_bytes_per_sample);
     if (bytes_requested < fifo_items)
         {
             num_samples_readed = noutput_items;  // read all
+            // update fifo items
+            fifo_items = fifo_items - bytes_requested;
         }
     else
         {
-            num_samples_readed = fifo_items / d_bytes_per_sample;  // read what we have
+            num_samples_readed = static_cast<int>(static_cast<float>(fifo_items) / d_bytes_per_sample);  // read what we have
+            bytes_requested = fifo_items;
+            // update fifo items
+            fifo_items = 0;
         }
 
-    bytes_requested = num_samples_readed * d_bytes_per_sample;
+
     // read all in a single loop
     demux_samples(output_items, num_samples_readed);  // it also increases the fifo read pointer
-    // update fifo items
-    fifo_items = fifo_items - bytes_requested;
 
     for (uint64_t n = 0; n < output_items.size(); n++)
         {
diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
index c24ee5417..9f8939904 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
@@ -83,7 +83,7 @@ private:
     bool open();
 
     boost::thread *d_pcap_thread;
-    boost::mutex d_mutex;
+    //boost::mutex d_mutex;
     struct sockaddr_in si_me
     {
     };
@@ -98,7 +98,7 @@ private:
     int d_udp_port;
     int d_n_baseband_channels;
     int d_wire_sample_type;
-    int d_bytes_per_sample;
+    float d_bytes_per_sample;
     bool d_IQ_swap;
 };
 

From 36e709dda646484c44eb6591db1d9e429c624922 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 4 Jul 2023 13:09:45 +0200
Subject: [PATCH 22/31] Adding a simple PVT Holonomic Kalman filter for
 position and velocity

---
 src/algorithms/PVT/adapters/rtklib_pvt.cc     |   7 +
 .../PVT/gnuradio_blocks/rtklib_pvt_gs.cc      |   7 +-
 .../PVT/gnuradio_blocks/rtklib_pvt_gs.h       |   1 -
 src/algorithms/PVT/libs/CMakeLists.txt        |   2 +
 src/algorithms/PVT/libs/pvt_conf.h            |   7 +
 src/algorithms/PVT/libs/pvt_kf.cc             | 132 ++++++++++++++++++
 src/algorithms/PVT/libs/pvt_kf.h              |  66 +++++++++
 src/algorithms/PVT/libs/rtklib_solver.cc      |  51 ++++++-
 src/algorithms/PVT/libs/rtklib_solver.h       |   7 +-
 .../pvt/nmea_printer_test.cc                  |   5 +-
 .../pvt/rinex_printer_test.cc                 |  41 ++++--
 .../pvt/rtklib_solver_test.cc                 |   6 +-
 12 files changed, 305 insertions(+), 27 deletions(-)
 create mode 100644 src/algorithms/PVT/libs/pvt_kf.cc
 create mode 100644 src/algorithms/PVT/libs/pvt_kf.h

diff --git a/src/algorithms/PVT/adapters/rtklib_pvt.cc b/src/algorithms/PVT/adapters/rtklib_pvt.cc
index 9ce402bcc..9bf6174ab 100644
--- a/src/algorithms/PVT/adapters/rtklib_pvt.cc
+++ b/src/algorithms/PVT/adapters/rtklib_pvt.cc
@@ -74,6 +74,13 @@ Rtklib_Pvt::Rtklib_Pvt(const ConfigurationInterface* configuration,
     // display rate
     pvt_output_parameters.display_rate_ms = bc::lcm(pvt_output_parameters.output_rate_ms, configuration->property(role + ".display_rate_ms", 500));
 
+    // PVT KF settings
+    pvt_output_parameters.enable_pvt_kf = configuration->property(role + ".enable_pvt_kf", false);
+    pvt_output_parameters.measures_ecef_pos_sd_m = configuration->property(role + ".kf_measures_ecef_pos_sd_m", 1.0);
+    pvt_output_parameters.measures_ecef_vel_sd_ms = configuration->property(role + ".kf_measures_ecef_vel_sd_ms", 0.1);
+    pvt_output_parameters.system_ecef_pos_sd_m = configuration->property(role + ".kf_system_ecef_pos_sd_m", 0.01);
+    pvt_output_parameters.system_ecef_vel_sd_ms = configuration->property(role + ".kf_system_ecef_vel_sd_ms", 0.001);
+
     // NMEA Printer settings
     pvt_output_parameters.flag_nmea_tty_port = configuration->property(role + ".flag_nmea_tty_port", false);
     pvt_output_parameters.nmea_dump_filename = configuration->property(role + ".nmea_dump_filename", default_nmea_dump_filename);
diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
index 8f6f1648a..ab145aff4 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc
@@ -178,7 +178,6 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
       d_enable_rx_clock_correction(conf_.enable_rx_clock_correction),
       d_an_printer_enabled(conf_.an_output_enabled),
       d_log_timetag(conf_.log_source_timetag),
-      d_use_e6_for_pvt(conf_.use_e6_for_pvt),
       d_use_has_corrections(conf_.use_has_corrections),
       d_use_unhealthy_sats(conf_.use_unhealthy_sats)
 {
@@ -535,21 +534,21 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
         {
             // setup two PVT solvers: internal solver for rx clock and user solver
             // user PVT solver
-            d_user_pvt_solver = std::make_shared<Rtklib_Solver>(rtk, dump_ls_pvt_filename, d_type_of_rx, d_dump, d_dump_mat, d_use_e6_for_pvt);
+            d_user_pvt_solver = std::make_shared<Rtklib_Solver>(rtk, dump_ls_pvt_filename, d_type_of_rx, d_dump, d_dump_mat, conf_);
             d_user_pvt_solver->set_averaging_depth(1);
             d_user_pvt_solver->set_pre_2009_file(conf_.pre_2009_file);
 
             // internal PVT solver, mainly used to estimate the receiver clock
             rtk_t internal_rtk = rtk;
             internal_rtk.opt.mode = PMODE_SINGLE;  // use single positioning mode in internal PVT solver
-            d_internal_pvt_solver = std::make_shared<Rtklib_Solver>(internal_rtk, dump_ls_pvt_filename, d_type_of_rx, false, false, d_use_e6_for_pvt);
+            d_internal_pvt_solver = std::make_shared<Rtklib_Solver>(internal_rtk, dump_ls_pvt_filename, d_type_of_rx, false, false, conf_);
             d_internal_pvt_solver->set_averaging_depth(1);
             d_internal_pvt_solver->set_pre_2009_file(conf_.pre_2009_file);
         }
     else
         {
             // only one solver, customized by the user options
-            d_internal_pvt_solver = std::make_shared<Rtklib_Solver>(rtk, dump_ls_pvt_filename, d_type_of_rx, d_dump, d_dump_mat, d_use_e6_for_pvt);
+            d_internal_pvt_solver = std::make_shared<Rtklib_Solver>(rtk, dump_ls_pvt_filename, d_type_of_rx, d_dump, d_dump_mat, conf_);
             d_internal_pvt_solver->set_averaging_depth(1);
             d_internal_pvt_solver->set_pre_2009_file(conf_.pre_2009_file);
             d_user_pvt_solver = d_internal_pvt_solver;
diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
index 747c31c65..83d1f830d 100644
--- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
+++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h
@@ -276,7 +276,6 @@ private:
     bool d_enable_has_messages;
     bool d_an_printer_enabled;
     bool d_log_timetag;
-    bool d_use_e6_for_pvt;
     bool d_use_has_corrections;
     bool d_use_unhealthy_sats;
 };
diff --git a/src/algorithms/PVT/libs/CMakeLists.txt b/src/algorithms/PVT/libs/CMakeLists.txt
index 2460a3d4b..07e905a2c 100644
--- a/src/algorithms/PVT/libs/CMakeLists.txt
+++ b/src/algorithms/PVT/libs/CMakeLists.txt
@@ -23,6 +23,7 @@ set(PVT_LIB_SOURCES
     monitor_ephemeris_udp_sink.cc
     has_simple_printer.cc
     geohash.cc
+    pvt_kf.cc
 )
 
 set(PVT_LIB_HEADERS
@@ -45,6 +46,7 @@ set(PVT_LIB_HEADERS
     monitor_ephemeris_udp_sink.h
     has_simple_printer.h
     geohash.h
+    pvt_kf.h
 )
 
 list(SORT PVT_LIB_HEADERS)
diff --git a/src/algorithms/PVT/libs/pvt_conf.h b/src/algorithms/PVT/libs/pvt_conf.h
index d77a0b24c..26aac201a 100644
--- a/src/algorithms/PVT/libs/pvt_conf.h
+++ b/src/algorithms/PVT/libs/pvt_conf.h
@@ -94,6 +94,13 @@ public:
     bool use_e6_for_pvt = true;
     bool use_has_corrections = true;
     bool use_unhealthy_sats = false;
+
+    //PVT KF parameters
+    bool enable_pvt_kf = false;
+    double measures_ecef_pos_sd_m = 1.0;
+    double measures_ecef_vel_sd_ms = 0.1;
+    double system_ecef_pos_sd_m = 0.01;
+    double system_ecef_vel_sd_ms = 0.001;
 };
 
 
diff --git a/src/algorithms/PVT/libs/pvt_kf.cc b/src/algorithms/PVT/libs/pvt_kf.cc
new file mode 100644
index 000000000..be7871974
--- /dev/null
+++ b/src/algorithms/PVT/libs/pvt_kf.cc
@@ -0,0 +1,132 @@
+/*!
+ * \file Pvt_Kf.cc
+ * \brief Kalman Filter for Position and Velocity
+ * \author Javier Arribas, 2023. jarribas(at)cttc.es
+ *
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2023  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+#include "pvt_kf.h"
+#include <glog/logging.h>
+
+
+void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
+    double solver_interval_s,
+    double measures_ecef_pos_sd_m,
+    double measures_ecef_vel_sd_ms,
+    double system_ecef_pos_sd_m,
+    double system_ecef_vel_sd_ms)
+{
+    // Kalman Filter class variables
+    const double Ti = solver_interval_s;
+
+    std::cout << "Ti=" << Ti << "\n";
+    d_F = {{1.0, 0.0, 0.0, Ti, 0.0, 0.0},
+        {0.0, 1.0, 0.0, 0.0, Ti, 0.0},
+        {0.0, 0.0, 1.0, 0.0, 0.0, Ti},
+        {0.0, 0.0, 0.0, 1.0, 0.0, 0.0},
+        {0.0, 0.0, 0.0, 0.0, 1.0, 0.0},
+        {0.0, 0.0, 0.0, 0.0, 0.0, 1.0}};
+
+    d_H = arma::eye(6, 6);
+
+    //measurement matrix static covariances
+    d_R = {{pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
+        {0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
+        {0.0, 0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
+        {0.0, 0.0, 0.0, pow(measures_ecef_vel_sd_ms, 2.0), 0.0, 0.0},
+        {0.0, 0.0, 0.0, 0.0, pow(measures_ecef_vel_sd_ms, 2.0), 0.0},
+        {0.0, 0.0, 0.0, 0.0, 0.0, pow(measures_ecef_vel_sd_ms, 2.0)}};
+
+
+    // system covariance matrix (static)
+
+    d_Q = {{pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
+        {0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
+        {0.0, 0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
+        {0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0), 0.0, 0.0},
+        {0.0, 0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0), 0.0},
+        {0.0, 0.0, 0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0)}};
+
+    // initial Kalman covariance matrix
+    d_P_old_old = {{pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
+        {0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
+        {0.0, 0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
+        {0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0), 0.0, 0.0},
+        {0.0, 0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0), 0.0},
+        {0.0, 0.0, 0.0, 0.0, 0.0, pow(system_ecef_vel_sd_ms, 2.0)}};
+
+    // states: position ecef [m], velocity ecef [m/s]
+    d_x_old_old = arma::zeros(6);
+    d_x_old_old.subvec(0, 2) = p;
+    d_x_old_old.subvec(3, 5) = v;
+
+    initialized = true;
+
+    DLOG(INFO) << "F: " << d_F;
+    DLOG(INFO) << "H: " << d_H;
+    DLOG(INFO) << "R: " << d_R;
+    DLOG(INFO) << "Q: " << d_Q;
+    DLOG(INFO) << "P: " << d_P_old_old;
+    DLOG(INFO) << "x: " << d_x_old_old;
+}
+
+void Pvt_Kf::run_Kf(arma::vec p, arma::vec v)
+{
+    //
+    //    Pkp{1,i}=F*Pk{1,i-1}*F'+Q;
+    //    Xkp{1,i}=F*Xk{1,i-1}; %nuevo estado a partir del modelo de transición
+    //
+    //    %% Ganancia de Kalman
+    //
+    //    K=Pkp{1,i}*A'*inv(A*Pkp{1,i}*A'+R); %en base a lo que acaba de predecir
+    //
+    //    %% Corrección de la predicción y la covarianza, usando la info de las
+    //    %% observaciones
+    //
+    //    Xk{1,i}=Xkp{1,i}+K*(Z{1,i}-A*Xkp{1,i}); %correccion de lo predicho en base a la observación Z
+    //
+    //    Pk{1,i}=(eye(4)-K*A)*Pkp{1,i}; % Corrección de la covarianza
+    //    %pause(1)
+    //
+
+    //  Kalman loop
+    // Prediction
+    //    std::cout << "d_x_old_old=" << d_x_old_old << "\n";
+    d_x_new_old = d_F * d_x_old_old;
+    //    std::cout << "d_x_new_old=" << d_x_new_old << "\n";
+    d_P_new_old = d_F * d_P_old_old * d_F.t() + d_Q;
+
+    // Measurement update
+    arma::vec z = arma::join_cols(p, v);
+    arma::mat K = d_P_new_old * d_H.t() * arma::inv(d_H * d_P_new_old * d_H.t() + d_R);  // Kalman gain
+    d_x_new_new = d_x_new_old + K * (z - d_H * d_x_new_old);
+    //    std::cout << "z=" << z << "\n";
+    //    std::cout << "K=" << K << "\n";
+
+    //    std::cout << "d_x_new_new=" << d_x_new_new << "\n";
+    d_P_new_new = (arma::eye(6, 6) - K * d_H) * d_P_new_old;
+
+    // prepare data for next KF epoch
+    d_x_old_old = d_x_new_new;
+    d_P_old_old = d_P_new_new;
+}
+
+Pvt_Kf::Pvt_Kf()
+{
+    initialized = false;
+}
+
+void Pvt_Kf::get_pvt(arma::vec& p, arma::vec& v)
+{
+    p = d_x_new_new.subvec(0, 2);
+    v = d_x_new_new.subvec(3, 5);
+}
diff --git a/src/algorithms/PVT/libs/pvt_kf.h b/src/algorithms/PVT/libs/pvt_kf.h
new file mode 100644
index 000000000..ce1a3f8ba
--- /dev/null
+++ b/src/algorithms/PVT/libs/pvt_kf.h
@@ -0,0 +1,66 @@
+/*!
+ * \file Pvt_Kf.h
+ * \brief Kalman Filter for Position and Velocity
+ * \author Javier Arribas, 2023. jarribas(at)cttc.es
+ *
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2023  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#ifndef GNSS_SDR_PVT_KF_H
+#define GNSS_SDR_PVT_KF_H
+
+#include <armadillo>
+
+/** \addtogroup PVT
+ * \{ */
+/** \addtogroup PVT_libs
+ * \{ */
+
+
+/*!
+ * \brief Kalman Filter for Position and Velocity
+ *
+ */
+class Pvt_Kf
+{
+public:
+    Pvt_Kf();
+    virtual ~Pvt_Kf() = default;
+    void init_kf(arma::vec p, arma::vec v,
+        double solver_interval_s,
+        double measures_ecef_pos_sd_m,
+        double measures_ecef_vel_sd_ms,
+        double system_ecef_pos_sd_m,
+        double system_ecef_vel_sd_ms);
+
+    void run_Kf(arma::vec p, arma::vec v);
+    bool initialized;
+    void get_pvt(arma::vec &p, arma::vec &v);
+
+private:
+    // Kalman Filter class variables
+    arma::mat d_F;
+    arma::mat d_H;
+    arma::mat d_R;
+    arma::mat d_Q;
+    arma::mat d_P_old_old;
+    arma::mat d_P_new_old;
+    arma::mat d_P_new_new;
+    arma::vec d_x_old_old;
+    arma::vec d_x_new_old;
+    arma::vec d_x_new_new;
+};
+
+
+/** \} */
+/** \} */
+#endif  // GNSS_SDR_Pvt_Kf_H
diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc
index 8763cc12a..538de50df 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.cc
+++ b/src/algorithms/PVT/libs/rtklib_solver.cc
@@ -49,12 +49,13 @@ Rtklib_Solver::Rtklib_Solver(const rtk_t &rtk,
     uint32_t type_of_rx,
     bool flag_dump_to_file,
     bool flag_dump_to_mat,
-    bool use_e6_for_pvt) : d_dump_filename(dump_filename),
-                           d_rtk(rtk),
-                           d_type_of_rx(type_of_rx),
-                           d_flag_dump_enabled(flag_dump_to_file),
-                           d_flag_dump_mat_enabled(flag_dump_to_mat),
-                           d_use_e6_for_pvt(use_e6_for_pvt)
+    Pvt_Conf conf) : d_dump_filename(dump_filename),
+                     d_rtk(rtk),
+                     d_type_of_rx(type_of_rx),
+                     d_flag_dump_enabled(flag_dump_to_file),
+                     d_flag_dump_mat_enabled(flag_dump_to_mat),
+                     d_conf(conf)
+
 {
     this->set_averaging_flag(false);
 
@@ -1039,7 +1040,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                                         DLOG(INFO) << "No ephemeris data for SV " << gnss_observables_iter->second.PRN;
                                     }
                             }
-                        if (sig_ == "E6" && d_use_e6_for_pvt)
+                        if (sig_ == "E6" && d_conf.use_e6_for_pvt)
                             {
                                 galileo_ephemeris_iter = galileo_ephemeris_map.find(gnss_observables_iter->second.PRN);
                                 if (galileo_ephemeris_iter != galileo_ephemeris_map.cend())
@@ -1466,6 +1467,10 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                     d_rtk.neb = 0;                 // clear error buffer to avoid repeating the error message
                     this->set_time_offset_s(0.0);  // reset rx time estimation
                     this->set_num_valid_observations(0);
+                    if (d_conf.enable_pvt_kf == true)
+                        {
+                            d_pvt_kf.initialized = false;
+                        }
                 }
             else
                 {
@@ -1500,9 +1505,41 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
                         }
                     this->set_valid_position(true);
                     std::array<double, 4> rx_position_and_time{};
+
+                    if (d_conf.enable_pvt_kf == true)
+                        {
+                            if (d_pvt_kf.initialized == false)
+                                {
+                                    arma::vec p = {pvt_sol.rr[0], pvt_sol.rr[1], pvt_sol.rr[2]};
+                                    arma::vec v = {pvt_sol.rr[3], pvt_sol.rr[4], pvt_sol.rr[5]};
+
+                                    d_pvt_kf.init_kf(p,
+                                        v,
+                                        d_conf.observable_interval_ms / 1000.0,
+                                        d_conf.measures_ecef_pos_sd_m,
+                                        d_conf.measures_ecef_vel_sd_ms,
+                                        d_conf.system_ecef_pos_sd_m,
+                                        d_conf.system_ecef_vel_sd_ms);
+                                }
+                            else
+                                {
+                                    arma::vec p = {pvt_sol.rr[0], pvt_sol.rr[1], pvt_sol.rr[2]};
+                                    arma::vec v = {pvt_sol.rr[3], pvt_sol.rr[4], pvt_sol.rr[5]};
+                                    d_pvt_kf.run_Kf(p, v);
+                                    d_pvt_kf.get_pvt(p, v);
+                                    pvt_sol.rr[0] = p[0];  // [m]
+                                    pvt_sol.rr[1] = p[1];  // [m]
+                                    pvt_sol.rr[2] = p[2];  // [m]
+                                    pvt_sol.rr[3] = v[0];  // [ms]
+                                    pvt_sol.rr[4] = v[1];  // [ms]
+                                    pvt_sol.rr[5] = v[2];  // [ms]
+                                }
+                        }
+
                     rx_position_and_time[0] = pvt_sol.rr[0];  // [m]
                     rx_position_and_time[1] = pvt_sol.rr[1];  // [m]
                     rx_position_and_time[2] = pvt_sol.rr[2];  // [m]
+
                     // todo: fix this ambiguity in the RTKLIB units in receiver clock offset!
                     if (d_rtk.opt.mode == PMODE_SINGLE)
                         {
diff --git a/src/algorithms/PVT/libs/rtklib_solver.h b/src/algorithms/PVT/libs/rtklib_solver.h
index 0a7fc056b..b967cb02f 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.h
+++ b/src/algorithms/PVT/libs/rtklib_solver.h
@@ -56,6 +56,8 @@
 #include "gps_iono.h"
 #include "gps_utc_model.h"
 #include "monitor_pvt.h"
+#include "pvt_conf.h"
+#include "pvt_kf.h"
 #include "pvt_solution.h"
 #include "rtklib.h"
 #include "rtklib_conversions.h"
@@ -84,7 +86,7 @@ public:
         uint32_t type_of_rx,
         bool flag_dump_to_file,
         bool flag_dump_to_mat,
-        bool use_e6_for_pvt = true);
+        Pvt_Conf conf);
     ~Rtklib_Solver();
 
     bool get_PVT(const std::map<int, Gnss_Synchro>& gnss_observables_map, bool flag_averaging);
@@ -152,7 +154,8 @@ private:
     uint32_t d_type_of_rx;
     bool d_flag_dump_enabled;
     bool d_flag_dump_mat_enabled;
-    bool d_use_e6_for_pvt;
+    Pvt_Conf d_conf;
+    Pvt_Kf d_pvt_kf;
 };
 
 
diff --git a/src/tests/unit-tests/signal-processing-blocks/pvt/nmea_printer_test.cc b/src/tests/unit-tests/signal-processing-blocks/pvt/nmea_printer_test.cc
index 94c904f95..fcc0920be 100644
--- a/src/tests/unit-tests/signal-processing-blocks/pvt/nmea_printer_test.cc
+++ b/src/tests/unit-tests/signal-processing-blocks/pvt/nmea_printer_test.cc
@@ -17,6 +17,7 @@
 
 #include "gnss_sdr_filesystem.h"
 #include "nmea_printer.h"
+#include "pvt_conf.h"
 #include "rtklib_rtkpos.h"
 #include "rtklib_solver.h"
 #include <fstream>
@@ -144,7 +145,9 @@ void NmeaPrinterTest::conf()
 TEST_F(NmeaPrinterTest, PrintLine)
 {
     std::string filename("nmea_test.nmea");
-    std::shared_ptr<Rtklib_Solver> pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 1, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    std::shared_ptr<Rtklib_Solver> pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 1, false, false, conf);
 
     boost::posix_time::ptime pt(boost::gregorian::date(1994, boost::date_time::Nov, 19),
         boost::posix_time::hours(22) + boost::posix_time::minutes(54) + boost::posix_time::seconds(46));
diff --git a/src/tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc b/src/tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc
index 05f7d115e..a4a4fe90a 100644
--- a/src/tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc
+++ b/src/tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "gnss_sdr_filesystem.h"
+#include "pvt_conf.h"
 #include "rinex_printer.h"
 #include "rtklib_rtkpos.h"
 #include "rtklib_solver.h"
@@ -142,7 +143,9 @@ void RinexPrinterTest::conf()
 
 TEST_F(RinexPrinterTest, GalileoObsHeader)
 {
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 4, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 4, false, false, conf);
     auto eph = Galileo_Ephemeris();
     eph.PRN = 1;
     pvt_solution->galileo_ephemeris_map[1] = eph;
@@ -228,7 +231,9 @@ TEST_F(RinexPrinterTest, GalileoObsHeader)
 
 TEST_F(RinexPrinterTest, GlonassObsHeader)
 {
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 28, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 28, false, false, conf);
     auto eph = Glonass_Gnav_Ephemeris();
     eph.PRN = 1;
     pvt_solution->glonass_gnav_ephemeris_map[1] = eph;
@@ -288,7 +293,9 @@ TEST_F(RinexPrinterTest, MixedObsHeader)
     auto eph_gps = Gps_Ephemeris();
     eph_gal.PRN = 1;
     eph_gps.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 106, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 106, false, false, conf);
     pvt_solution->galileo_ephemeris_map[1] = eph_gal;
 
     pvt_solution->gps_ephemeris_map[1] = eph_gps;
@@ -358,7 +365,9 @@ TEST_F(RinexPrinterTest, MixedObsHeaderGpsGlo)
     auto eph_gps = Gps_Ephemeris();
     eph_glo.PRN = 1;
     eph_gps.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 26, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 26, false, false, conf);
     pvt_solution->glonass_gnav_ephemeris_map[1] = eph_glo;
 
     pvt_solution->gps_ephemeris_map[1] = eph_gps;
@@ -425,7 +434,9 @@ TEST_F(RinexPrinterTest, GalileoObsLog)
     bool no_more_finds = false;
     auto eph = Galileo_Ephemeris();
     eph.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 4, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 4, false, false, conf);
     pvt_solution->galileo_ephemeris_map[1] = eph;
     std::map<int, Gnss_Synchro> gnss_observables_map;
 
@@ -505,7 +516,9 @@ TEST_F(RinexPrinterTest, GlonassObsLog)
     bool no_more_finds = false;
     auto eph = Glonass_Gnav_Ephemeris();
     eph.PRN = 22;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 23, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 23, false, false, conf);
     pvt_solution->glonass_gnav_ephemeris_map[1] = eph;
     std::map<int, Gnss_Synchro> gnss_observables_map;
 
@@ -587,7 +600,9 @@ TEST_F(RinexPrinterTest, GpsObsLogDualBand)
     auto eph_cnav = Gps_CNAV_Ephemeris();
     eph.PRN = 1;
     eph_cnav.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 7, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 7, false, false, conf);
     pvt_solution->gps_ephemeris_map[1] = eph;
     pvt_solution->gps_cnav_ephemeris_map[1] = eph_cnav;
     std::map<int, Gnss_Synchro> gnss_observables_map;
@@ -675,7 +690,9 @@ TEST_F(RinexPrinterTest, GpsObsLogDualBand)
 
 TEST_F(RinexPrinterTest, GalileoObsLogDualBand)
 {
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 14, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 14, false, false, conf);
     auto eph = Galileo_Ephemeris();
     eph.PRN = 1;
     pvt_solution->galileo_ephemeris_map[1] = eph;
@@ -775,7 +792,9 @@ TEST_F(RinexPrinterTest, MixedObsLog)
     auto eph_gal = Galileo_Ephemeris();
     eph_gps.PRN = 1;
     eph_gal.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 9, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 9, false, false, conf);
     pvt_solution->gps_ephemeris_map[1] = eph_gps;
     pvt_solution->galileo_ephemeris_map[1] = eph_gal;
     std::map<int, Gnss_Synchro> gnss_observables_map;
@@ -899,7 +918,9 @@ TEST_F(RinexPrinterTest, MixedObsLogGpsGlo)
     auto eph_glo = Glonass_Gnav_Ephemeris();
     eph_gps.PRN = 1;
     eph_glo.PRN = 1;
-    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 26, false, false);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, "filename", 26, false, false, conf);
     pvt_solution->gps_ephemeris_map[1] = eph_gps;
     pvt_solution->glonass_gnav_ephemeris_map[1] = eph_glo;
     std::map<int, Gnss_Synchro> gnss_observables_map;
diff --git a/src/tests/unit-tests/signal-processing-blocks/pvt/rtklib_solver_test.cc b/src/tests/unit-tests/signal-processing-blocks/pvt/rtklib_solver_test.cc
index 4da572895..805ffdb9e 100644
--- a/src/tests/unit-tests/signal-processing-blocks/pvt/rtklib_solver_test.cc
+++ b/src/tests/unit-tests/signal-processing-blocks/pvt/rtklib_solver_test.cc
@@ -18,6 +18,7 @@
 #include "gnss_sdr_make_unique.h"
 #include "gnss_sdr_supl_client.h"
 #include "in_memory_configuration.h"
+#include "pvt_conf.h"
 #include "rtklib_solver.h"
 #include <armadillo>
 #include <boost/archive/xml_iarchive.hpp>
@@ -28,7 +29,6 @@
 #include <iostream>
 #include <string>
 
-
 rtk_t configure_rtklib_options()
 {
     std::shared_ptr<InMemoryConfiguration> configuration;
@@ -384,7 +384,9 @@ TEST(RTKLibSolverTest, test1)
     bool save_to_mat = false;
     rtk_t rtk = configure_rtklib_options();
 
-    auto d_ls_pvt = std::make_unique<Rtklib_Solver>(rtk, nchannels, dump_filename, 1, flag_dump_to_file, save_to_mat);
+    Pvt_Conf conf;
+    conf.use_e6_for_pvt = false;
+    auto d_ls_pvt = std::make_unique<Rtklib_Solver>(rtk, nchannels, dump_filename, 1, flag_dump_to_file, save_to_mat, conf);
     d_ls_pvt->set_averaging_depth(1);
 
     // load ephemeris

From 4c448251fae7a361ca31426a529ed71181383486 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 4 Jul 2023 17:19:17 +0200
Subject: [PATCH 23/31] Code cleaning

---
 src/algorithms/PVT/libs/pvt_conf.h |  2 +-
 src/algorithms/PVT/libs/pvt_kf.cc  | 26 ++------------------------
 2 files changed, 3 insertions(+), 25 deletions(-)

diff --git a/src/algorithms/PVT/libs/pvt_conf.h b/src/algorithms/PVT/libs/pvt_conf.h
index 26aac201a..bf4a3501a 100644
--- a/src/algorithms/PVT/libs/pvt_conf.h
+++ b/src/algorithms/PVT/libs/pvt_conf.h
@@ -95,7 +95,7 @@ public:
     bool use_has_corrections = true;
     bool use_unhealthy_sats = false;
 
-    //PVT KF parameters
+    // PVT KF parameters
     bool enable_pvt_kf = false;
     double measures_ecef_pos_sd_m = 1.0;
     double measures_ecef_vel_sd_ms = 0.1;
diff --git a/src/algorithms/PVT/libs/pvt_kf.cc b/src/algorithms/PVT/libs/pvt_kf.cc
index be7871974..fc06b8757 100644
--- a/src/algorithms/PVT/libs/pvt_kf.cc
+++ b/src/algorithms/PVT/libs/pvt_kf.cc
@@ -38,7 +38,7 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
 
     d_H = arma::eye(6, 6);
 
-    //measurement matrix static covariances
+    //   measurement matrix static covariances
     d_R = {{pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
         {0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
         {0.0, 0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
@@ -81,38 +81,16 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
 
 void Pvt_Kf::run_Kf(arma::vec p, arma::vec v)
 {
-    //
-    //    Pkp{1,i}=F*Pk{1,i-1}*F'+Q;
-    //    Xkp{1,i}=F*Xk{1,i-1}; %nuevo estado a partir del modelo de transición
-    //
-    //    %% Ganancia de Kalman
-    //
-    //    K=Pkp{1,i}*A'*inv(A*Pkp{1,i}*A'+R); %en base a lo que acaba de predecir
-    //
-    //    %% Corrección de la predicción y la covarianza, usando la info de las
-    //    %% observaciones
-    //
-    //    Xk{1,i}=Xkp{1,i}+K*(Z{1,i}-A*Xkp{1,i}); %correccion de lo predicho en base a la observación Z
-    //
-    //    Pk{1,i}=(eye(4)-K*A)*Pkp{1,i}; % Corrección de la covarianza
-    //    %pause(1)
-    //
-
     //  Kalman loop
     // Prediction
-    //    std::cout << "d_x_old_old=" << d_x_old_old << "\n";
     d_x_new_old = d_F * d_x_old_old;
-    //    std::cout << "d_x_new_old=" << d_x_new_old << "\n";
     d_P_new_old = d_F * d_P_old_old * d_F.t() + d_Q;
 
     // Measurement update
     arma::vec z = arma::join_cols(p, v);
     arma::mat K = d_P_new_old * d_H.t() * arma::inv(d_H * d_P_new_old * d_H.t() + d_R);  // Kalman gain
-    d_x_new_new = d_x_new_old + K * (z - d_H * d_x_new_old);
-    //    std::cout << "z=" << z << "\n";
-    //    std::cout << "K=" << K << "\n";
 
-    //    std::cout << "d_x_new_new=" << d_x_new_new << "\n";
+    d_x_new_new = d_x_new_old + K * (z - d_H * d_x_new_old);
     d_P_new_new = (arma::eye(6, 6) - K * d_H) * d_P_new_old;
 
     // prepare data for next KF epoch

From 1cf508ad200591f6b5543f5bb9b031deab47113d Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Tue, 4 Jul 2023 17:45:20 +0200
Subject: [PATCH 24/31] Code cleaning

---
 .../gnuradio_blocks/ad936x_iio_source.cc      | 22 ++---
 .../gr_complex_ip_packet_source.cc            |  6 +-
 .../gr_complex_ip_packet_source.h             |  2 +-
 .../signal_source/libs/ad936x_iio_custom.cc   | 92 +++++++++----------
 .../signal_source/libs/ad936x_iio_custom.h    |  8 +-
 .../signal_source/libs/ad936x_iio_samples.h   |  2 +-
 .../signal_source/libs/pps_samplestamp.h      |  2 +-
 src/algorithms/signal_source/libs/ppstcprx.cc | 14 +--
 8 files changed, 74 insertions(+), 74 deletions(-)

diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
index ffaae3e9c..45cc5046f 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -94,8 +94,8 @@ void ad936x_iio_source::ad9361_channel_demux_and_record(ad936x_iio_samples *samp
         {
             for (ch = 0; ch < nchannels; ch++)
                 {
-                    //std::cout << current_byte << " of " << samples_in->n_bytes << " test: " << (int)samples_in->buffer[current_byte] << "\n";
-                    (*files_out).at(ch).write(&samples_in->buffer[current_byte], 4);  //two bytes I + two bytes Q per channel
+                    // std::cout << current_byte << " of " << samples_in->n_bytes << " test: " << (int)samples_in->buffer[current_byte] << "\n";
+                    (*files_out).at(ch).write(&samples_in->buffer[current_byte], 4);  // two bytes I + two bytes Q per channel
                     current_byte += 4;
                 }
         }
@@ -134,7 +134,7 @@ ad936x_iio_source::ad936x_iio_source(
         {
             if (ad936x_custom->initialize_device(pluto_uri_, board_type_) == true)
                 {
-                    //configure channels
+                    // configure channels
                     if (ad936x_custom->init_config_ad9361_rx(bandwidth_,
                             sample_rate_,
                             freq_,
@@ -153,19 +153,19 @@ ad936x_iio_source::ad936x_iio_source(
                         {
                             std::cout << "ad936x_iio_source HW configured OK!\n";
 
-                            //PPS FPGA Samplestamp information from TCP server
+                            // PPS FPGA Samplestamp information from TCP server
                             pps_rx = std::make_shared<pps_tcp_rx>();
                             ppsqueue = std::shared_ptr<Concurrent_Queue<PpsSamplestamp>>(new Concurrent_Queue<PpsSamplestamp>());
 
                             pps_rx->set_pps_samplestamp_queue(ppsqueue);
                             ad936x_custom->set_pps_samplestamp_queue(ppsqueue);
 
-                            //start PPS RX thread
+                            // start PPS RX thread
                             if (ppsmode_ == true or customsamplesize_ == true)
                                 {
                                     pps_rx_thread = std::thread(&pps_tcp_rx::receive_pps, pps_rx, fe_ip_, fe_ctlport_);
                                     std::this_thread::sleep_for(std::chrono::milliseconds(500));
-                                    //configure custom FPGA options
+                                    // configure custom FPGA options
                                     switch (ssize_)
                                         {
                                         case 16:
@@ -251,13 +251,13 @@ ad936x_iio_source::ad936x_iio_source(
 
     set_min_noutput_items(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2);  // multiplexed I,Q, so, two samples per complex sample
     set_min_output_buffer(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * sizeof(int16_t) * 2);
-    //std::cout << "max_output_buffer " << min_output_buffer(0) << " min_noutput_items: " << min_noutput_items() << "\n";
+    // std::cout << "max_output_buffer " << min_output_buffer(0) << " min_noutput_items: " << min_noutput_items() << "\n";
 
     //    for (int n = 0; n < ad936x_custom->n_channels; n++)
     //        {
     //            std::string cap_file_root_name = "./debug_cap_ch";
     //            samplesfile.push_back(std::fstream(cap_file_root_name + std::to_string(n) + ".dat", std::ios::out | std::ios::binary));
-    //            //samplesfile.back().exceptions(std::ios_base::badbit | std::ios_base::failbit); //this will enable exceptions for debug
+    //            // samplesfile.back().exceptions(std::ios_base::badbit | std::ios_base::failbit); //this will enable exceptions for debug
     //
     //            if (samplesfile.back().is_open() == false)
     //                {
@@ -301,7 +301,7 @@ int ad936x_iio_source::general_work(int noutput_items,
     ad936x_custom->pop_sample_buffer(current_buffer);
     current_samples = current_buffer.get();
 
-    //I and Q samples are interleaved in buffer: IQIQIQ...
+    // I and Q samples are interleaved in buffer: IQIQIQ...
     int32_t n_interleaved_iq_samples_per_channel = current_samples->n_bytes / (ad936x_custom->n_channels * 2);
     if (noutput_items < n_interleaved_iq_samples_per_channel)
         {
@@ -310,12 +310,12 @@ int ad936x_iio_source::general_work(int noutput_items,
         }
     else
         {
-            //ad9361_channel_demux_and_record(current_samples, ad936x_custom->n_channels, &samplesfile);
+            // ad9361_channel_demux_and_record(current_samples, ad936x_custom->n_channels, &samplesfile);
 
             uint32_t current_byte = 0;
             uint32_t current_byte_in_gr = 0;
             int16_t ch = 0;
-            //std::cout << "nbytes: " << samples_in->n_bytes << " nsamples: " << samples_in->n_samples << " nch: " << nchannels << "\n";
+            // std::cout << "nbytes: " << samples_in->n_bytes << " nsamples: " << samples_in->n_samples << " nch: " << nchannels << "\n";
             if (ad936x_custom->n_channels == 1)
                 {
                     memcpy(&((char *)output_items[0])[0], &current_samples->buffer[current_byte], current_samples->n_bytes);
diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
index 20f829e6a..0153a11a2 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
@@ -193,7 +193,7 @@ bool Gr_Complex_Ip_Packet_Source::stop()
 bool Gr_Complex_Ip_Packet_Source::open()
 {
     std::array<char, PCAP_ERRBUF_SIZE> errbuf{};
-    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    // boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
     gr::thread::scoped_lock guard(d_setlock);
     // open device for reading
     descr = pcap_open_live(d_src_device.c_str(), 1500, 1, 1000, errbuf.data());
@@ -250,7 +250,7 @@ void Gr_Complex_Ip_Packet_Source::static_pcap_callback(u_char *args, const struc
 void Gr_Complex_Ip_Packet_Source::pcap_callback(__attribute__((unused)) u_char *args, __attribute__((unused)) const struct pcap_pkthdr *pkthdr,
     const u_char *packet)
 {
-    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    // boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
 
     const gr_ip_header *ih;
     const gr_udp_header *uh;
@@ -500,7 +500,7 @@ int Gr_Complex_Ip_Packet_Source::work(int noutput_items,
     gr_vector_void_star &output_items)
 {
     // send samples to next GNU Radio block
-    //boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
+    // boost::mutex::scoped_lock lock(d_mutex);  // hold mutex for duration of this function
     if (fifo_items == 0)
         {
             return 0;
diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
index 9f8939904..a7d37b5a0 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.h
@@ -83,7 +83,7 @@ private:
     bool open();
 
     boost::thread *d_pcap_thread;
-    //boost::mutex d_mutex;
+    // boost::mutex d_mutex;
     struct sockaddr_in si_me
     {
     };
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index efd67e778..c9d788bdd 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -40,10 +40,10 @@ ad936x_iio_custom::ad936x_iio_custom(int debug_level_, int log_level_)
 
 ad936x_iio_custom::~ad936x_iio_custom()
 {
-    //disable TX
+    // disable TX
     if (phy != NULL) PlutoTxEnable(false);
 
-    //Close device
+    // Close device
     if (ctx != NULL) iio_context_destroy(ctx);
 }
 
@@ -60,7 +60,7 @@ void ad936x_iio_custom::set_pps_samplestamp_queue(std::shared_ptr<Concurrent_Que
 
 bool ad936x_iio_custom::initialize_device(std::string pluto_device_uri, std::string board_type)
 {
-    //Find devices
+    // Find devices
     if (pluto_device_uri == "local")
         {
             struct iio_scan_context *tmp_ctx = iio_create_scan_context("usb", 0);
@@ -130,7 +130,7 @@ bool ad936x_iio_custom::initialize_device(std::string pluto_device_uri, std::str
 
     if (board_type.compare("fmcomms5") == 0)
         {
-            stream_dev = iio_context_find_device(ctx, "cf-ad9361-A");  //first ad9361 in FMCOMMS5
+            stream_dev = iio_context_find_device(ctx, "cf-ad9361-A");  // first ad9361 in FMCOMMS5
             if (stream_dev == NULL)
                 {
                     std::cout << "Unable to find cf-ad9361-A device from uri: " << pluto_device_uri << std::endl;
@@ -139,13 +139,13 @@ bool ad936x_iio_custom::initialize_device(std::string pluto_device_uri, std::str
         }
     else
         {
-            stream_dev = iio_context_find_device(ctx, "cf-ad9361-lpc");  //regular AD9361 stream device in single AD9361 boards
+            stream_dev = iio_context_find_device(ctx, "cf-ad9361-lpc");  // regular AD9361 stream device in single AD9361 boards
             if (stream_dev == NULL)
                 {
                     std::cout << "Unable to find cf-ad9361-lpc device from uri: " << pluto_device_uri << std::endl;
                     return false;
                 };
-            dds_dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");  //DDS core for LO oscillator (external transverter operation)
+            dds_dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");  // DDS core for LO oscillator (external transverter operation)
             if (stream_dev == NULL)
                 {
                     std::cout << "Warning: Unable to find cf-ad9361-dds-core-lpc device from uri: " << pluto_device_uri << std::endl;
@@ -267,13 +267,13 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
         {
             params_phy.push_back("out_voltage0_hardwaregain=" +
                                  std::to_string(-tx_attenuation_db_));
-            //disable the other TX
+            // disable the other TX
             params_phy.push_back("out_voltage1_hardwaregain=" +
                                  std::to_string(-disabled_tx_attenuation));
 
             configure_params(phy, params_phy);
 
-            //DDS TX CH1 I (tone #1)
+            // DDS TX CH1 I (tone #1)
             params_dds.push_back("out_altvoltage0_TX1_I_F1_frequency=" +
                                  std::to_string(freq_dds_tx_hz_));
             params_dds.push_back("out_altvoltage0_TX1_I_F1_phase=" +
@@ -281,7 +281,7 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
             params_dds.push_back("out_altvoltage0_TX1_I_F1_scale=" +
                                  std::to_string(scale_dds_));
             params_dds.push_back("out_altvoltage0_TX1_I_F1_raw=1");
-            //DDS TX CH1 Q (tone #1)
+            // DDS TX CH1 Q (tone #1)
             params_dds.push_back("out_altvoltage2_TX1_Q_F1_frequency=" +
                                  std::to_string(freq_dds_tx_hz_));
             params_dds.push_back("out_altvoltage2_TX1_Q_F1_phase=" +
@@ -296,13 +296,13 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
         {
             params_phy.push_back("out_voltage1_hardwaregain=" +
                                  std::to_string(-tx_attenuation_db_));
-            //disable the other TX
+            // disable the other TX
             params_phy.push_back("out_voltage0_hardwaregain=" +
                                  std::to_string(-disabled_tx_attenuation));
 
             configure_params(phy, params_phy);
 
-            //DDS TX CH2 I (tone #1)
+            // DDS TX CH2 I (tone #1)
             params_dds.push_back("out_altvoltage4_TX2_I_F1_frequency=" +
                                  std::to_string(freq_dds_tx_hz_));
             params_dds.push_back("out_altvoltage4_TX2_I_F1_phase=" +
@@ -310,7 +310,7 @@ bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_,
             params_dds.push_back("out_altvoltage4_TX2_I_F1_scale=" +
                                  std::to_string(scale_dds_));
             params_dds.push_back("out_altvoltage4_TX2_I_F1_raw=1");
-            //DDS TX CH2 Q (tone #1)
+            // DDS TX CH2 Q (tone #1)
             params_dds.push_back("out_altvoltage6_TX2_Q_F1_frequency=" +
                                  std::to_string(freq_dds_tx_hz_));
             params_dds.push_back("out_altvoltage6_TX2_Q_F1_phase=" +
@@ -549,7 +549,7 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
         }
     else
         {
-            PlutoTxEnable(false);  //power down the TX LO to reduce interferences
+            PlutoTxEnable(false);  // power down the TX LO to reduce interferences
         }
 
     int set_filter_ret = ad9361_set_bb_rate(phy, sample_rate_sps);
@@ -564,7 +564,7 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
     //        }
 
-    //testing: set manual RX filter chain
+    // testing: set manual RX filter chain
     //    unsigned long RX_analog_bb_lpf_stop_hz = 1000000;
     //    unsigned long TX_analog_bb_lpf_stop_hz = 1000000;
     //
@@ -588,7 +588,7 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
             name << "voltage";
             name << 0;
             struct iio_channel *phy_ch;
-            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  //false means RX
+            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  // false means RX
             if (!phy_ch)
                 {
                     std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
@@ -639,7 +639,7 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
             name << "voltage";
             name << 1;
             struct iio_channel *phy_ch;
-            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  //false means RX
+            phy_ch = iio_device_find_channel(phy, name.str().c_str(), false);  // false means RX
             if (!phy_ch)
                 {
                     std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n";
@@ -773,7 +773,7 @@ bool ad936x_iio_custom::setRXGain(int ch_num, std::string gain_mode, double gain
 double ad936x_iio_custom::get_rx_gain(int ch_num)
 {
     if (check_device() == false) return -1;
-    double gain_dB;  //gain in dB
+    double gain_dB;  // gain in dB
     int ret = 0;
     if (ch_num == 0)
         {
@@ -804,7 +804,7 @@ double ad936x_iio_custom::get_rx_gain(int ch_num)
 bool ad936x_iio_custom::calibrate(int ch, double bw_hz)
 {
     if (check_device() == false) return false;
-    //todo
+    // todo
     return true;
 }
 
@@ -889,7 +889,7 @@ void ad936x_iio_custom::PlutoTxEnable(bool txon)
             int ret;
             if (txon == false)
                 {
-                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", true);  //turn off TX LO
+                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", true);  // turn off TX LO
                     if (ret < 0)
                         {
                             std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl;
@@ -897,7 +897,7 @@ void ad936x_iio_custom::PlutoTxEnable(bool txon)
                 }
             else
                 {
-                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", false);  //turn on TX LO
+                    ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", false);  // turn on TX LO
                     if (ret < 0)
                         {
                             std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl;
@@ -909,10 +909,10 @@ void ad936x_iio_custom::PlutoTxEnable(bool txon)
 void ad936x_iio_custom::setPlutoGpo(int p)
 {
     char pins[11];
-    sprintf(pins, "0x27 0x%x0", p);  //direct access to AD9361 registers... WARNING!
+    sprintf(pins, "0x27 0x%x0", p);  // direct access to AD9361 registers... WARNING!
     pins[9] = 0;
     int ret;
-    //std::cout << "send: " << pins << " \n";
+    // std::cout << "send: " << pins << " \n";
     if (check_device())
         {
             ret = iio_device_debug_attr_write(phy, "direct_reg_access", pins);
@@ -932,7 +932,7 @@ bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
     // adi,gpo-manual-mode-enable-mask does not work...
     // some software use the direct_reg_access (see https://github.com/g4eml/Langstone/blob/master/LangstoneGUI.c)
 
-    //since plutosdr fw 31:
+    // since plutosdr fw 31:
     //    GPOs can be addressed individual 0..3 or altogether using 0xF as Identifier.
     //
     //    SYNTAX:
@@ -958,7 +958,7 @@ bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
 
     if (rf_filter.compare("E1") == 0)
         {
-            //set gpio0 to switch L1 filter
+            //  set gpio0 to switch L1 filter
             //            setPlutoGpo(plutoGpo);
             ret = iio_device_debug_attr_write(phy, "gpo_set", "0 0");
             if (ret < 0)
@@ -969,7 +969,7 @@ bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
         }
     else if (rf_filter.compare("E5E6") == 0)
         {
-            //set gpio0 to switch L5/L6 filter (GPO0)
+            // set gpio0 to switch L5/L6 filter (GPO0)
             //            plutoGpo = plutoGpo | 0x10;
             //            setPlutoGpo(plutoGpo);  //set the Pluto GPO Pin
             ret = iio_device_debug_attr_write(phy, "gpo_set", "0 1");
@@ -1018,20 +1018,20 @@ void ad936x_iio_custom::get_PPS_timestamp()
             return;
         }
 
-    //Get new PPS samplestamp and associate it to the corresponding uBlox TP message
+    // Get new PPS samplestamp and associate it to the corresponding uBlox TP message
     while (receive_samples == true)
         {
             std::cout << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n";
             LOG(INFO) << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n";
-            //write timestamp information to timestamp metadata file:
-            //uint64_t: absolute sample counter from the beginning of sample capture associated to the rising edge of the PPS signal
+            // write timestamp information to timestamp metadata file:
+            // uint64_t: absolute sample counter from the beginning of sample capture associated to the rising edge of the PPS signal
             //            ppstimefile.write(reinterpret_cast<char *>(&pps.samplestamp), sizeof(uint64_t));
-            //int32_t: Galileo/GPS Week Number associated to the rising edge of PPS signal
+            // int32_t: Galileo/GPS Week Number associated to the rising edge of PPS signal
             //            ppstimefile.write(reinterpret_cast<char *>(&tow.week), sizeof(int32_t));
-            //int32_t: Galileo/GPS TOW associated to the rising edge of PPS signal
+            // int32_t: Galileo/GPS TOW associated to the rising edge of PPS signal
             //            ppstimefile.write(reinterpret_cast<char *>(&tow.tow_ms), sizeof(int32_t));
-            //record pps rise samplestamp associated to the absolute sample counter
-            //PPS rising edge must be associated with the corresponding uBlox time message (tx once a second)
+            // record pps rise samplestamp associated to the absolute sample counter
+            // PPS rising edge must be associated with the corresponding uBlox time message (tx once a second)
 
 
             if (GnssTime_queue->timed_wait_and_pop(tow, 2000) == false)
@@ -1066,10 +1066,10 @@ void ad936x_iio_custom::get_PPS_timestamp()
 }
 bool ad936x_iio_custom::start_sample_rx(bool ppsmode)
 {
-    //using queues of smart pointers to preallocated buffers
+    // using queues of smart pointers to preallocated buffers
     free_buffers.clear();
     used_buffers.clear();
-    //preallocate buffers and use queues
+    // preallocate buffers and use queues
     std::cerr << "Allocating memory..\n";
     try
         {
@@ -1084,33 +1084,33 @@ bool ad936x_iio_custom::start_sample_rx(bool ppsmode)
             return false;
         }
 
-    //prepare capture channels
+    // prepare capture channels
     std::vector<std::string> channels;
     switch (n_channels)
         {
         case 1:
-            channels.push_back("voltage0");  //Channel 0 I
-            channels.push_back("voltage1");  //Channel 0 Q
+            channels.push_back("voltage0");  // Channel 0 I
+            channels.push_back("voltage1");  // Channel 0 Q
             break;
         case 2:
-            channels.push_back("voltage0");  //Channel 0 I
-            channels.push_back("voltage1");  //Channel 0 Q
-            channels.push_back("voltage2");  //Channel 1 I
-            channels.push_back("voltage3");  //Channel 1 Q
+            channels.push_back("voltage0");  // Channel 0 I
+            channels.push_back("voltage1");  // Channel 0 Q
+            channels.push_back("voltage2");  // Channel 1 I
+            channels.push_back("voltage3");  // Channel 1 Q
             break;
         default:
-            channels.push_back("voltage0");  //Channel 0 I
-            channels.push_back("voltage1");  //Channel 0 Q
+            channels.push_back("voltage0");  // Channel 0 I
+            channels.push_back("voltage1");  // Channel 0 Q
         }
 
     receive_samples = true;
-    //start sample capture thread
+    // start sample capture thread
     capture_samples_thread = std::thread(&ad936x_iio_custom::capture, this, channels);
-    //start sample overflow detector
+    // start sample overflow detector
     overflow_monitor_thread = std::thread(&ad936x_iio_custom::monitor_thread_fn, this);
 
 
-    //start PPS and GNSS Time capture thread
+    // start PPS and GNSS Time capture thread
 
     if (ppsmode == true)
         {
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.h b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
index de36f5b5b..83a61282a 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.h
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.h
@@ -31,7 +31,7 @@
 #endif
 
 #include "ad936x_iio_samples.h"
-#include <ad9361.h>  //multichip sync and high level functions
+#include <ad9361.h>  // multichip sync and high level functions
 #include <thread>
 #include <vector>
 
@@ -114,13 +114,13 @@ private:
     void PlutoTxEnable(bool txon);
     void setPlutoGpo(int p);
 
-    //Device structure
+    // Device structure
     struct iio_context *ctx;
     struct iio_device *phy;
     struct iio_device *stream_dev;
     struct iio_device *dds_dev;
 
-    //stream
+    // stream
 
     uint64_t sample_rate_sps;
 
@@ -135,7 +135,7 @@ private:
     boost::atomic<bool> receive_samples;
 
     boost::atomic<bool> fpga_overflow;
-    //using queues of smart pointers to preallocated buffers
+    // using queues of smart pointers to preallocated buffers
     Concurrent_Queue<std::shared_ptr<ad936x_iio_samples>> free_buffers;
     Concurrent_Queue<std::shared_ptr<ad936x_iio_samples>> used_buffers;
 
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_samples.h b/src/algorithms/signal_source/libs/ad936x_iio_samples.h
index 5156e89f0..96fd9e6ae 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_samples.h
+++ b/src/algorithms/signal_source/libs/ad936x_iio_samples.h
@@ -34,7 +34,7 @@ public:
     uint32_t n_interleaved_iq_samples;
     uint16_t n_channels;
     uint16_t step_bytes;
-    char buffer[IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 4 * 4];  //max 16 bits samples per buffer (4 channels, 2-bytes per I + 2-bytes per Q)
+    char buffer[IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 4 * 4];  // max 16 bits samples per buffer (4 channels, 2-bytes per I + 2-bytes per Q)
 };
 
 #endif
diff --git a/src/algorithms/signal_source/libs/pps_samplestamp.h b/src/algorithms/signal_source/libs/pps_samplestamp.h
index cdbc1276f..900388867 100644
--- a/src/algorithms/signal_source/libs/pps_samplestamp.h
+++ b/src/algorithms/signal_source/libs/pps_samplestamp.h
@@ -13,7 +13,7 @@
 class PpsSamplestamp
 {
 public:
-    uint64_t samplestamp;   //PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side
+    uint64_t samplestamp;   // PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side
     uint32_t overflow_reg;  // >0 indicates overflow situation in the FPGA RX buffer
 };
 
diff --git a/src/algorithms/signal_source/libs/ppstcprx.cc b/src/algorithms/signal_source/libs/ppstcprx.cc
index 0a10d8545..73ea791db 100644
--- a/src/algorithms/signal_source/libs/ppstcprx.cc
+++ b/src/algorithms/signal_source/libs/ppstcprx.cc
@@ -52,16 +52,16 @@ bool pps_tcp_rx::send_cmd(std::string cmd)
 }
 void pps_tcp_rx::receive_pps(std::string ip_address, int port)
 {
-    //create a message buffer
+    // create a message buffer
     char buf[1500];
-    //setup a socket and connection tools
+    // setup a socket and connection tools
     sockaddr_in sendSockAddr;
     sendSockAddr.sin_family = AF_INET;
     sendSockAddr.sin_addr.s_addr =
         inet_addr(ip_address.c_str());
     sendSockAddr.sin_port = htons(port);
     clientSd = socket(AF_INET, SOCK_STREAM, 0);
-    //try to connect...
+    // try to connect...
     int status = connect(clientSd,
         (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
     if (status < 0)
@@ -84,8 +84,8 @@ void pps_tcp_rx::receive_pps(std::string ip_address, int port)
                                 {
                                     if (new_pps_line.length() > 0)
                                         {
-                                            //std::cout << "pps_tcp_rx debug: " << new_pps_line << "\n";
-                                            //parse string and push PPS data to the PPS queue
+                                            // std::cout << "pps_tcp_rx debug: " << new_pps_line << "\n";
+                                            // parse string and push PPS data to the PPS queue
                                             std::stringstream ss(new_pps_line);
                                             std::vector<std::string> data;
                                             while (ss.good())
@@ -97,7 +97,7 @@ void pps_tcp_rx::receive_pps(std::string ip_address, int port)
                                             if (data.size() >= 2)
                                                 {
                                                     PpsSamplestamp new_pps;
-                                                    //sample counter
+                                                    // sample counter
                                                     std::size_t found = data.at(0).find("sc=");
                                                     if (found != std::string::npos)
                                                         {
@@ -131,7 +131,7 @@ void pps_tcp_rx::receive_pps(std::string ip_address, int port)
                                                             std::cout << "pps_tcp_rx debug: o parse error str " << data.at(1) << "\n";
                                                         }
                                                     Pps_queue->push(new_pps);
-                                                    //std::cout << "pps_tcp_rx debug: pps pushed!\n";
+                                                    // std::cout << "pps_tcp_rx debug: pps pushed!\n";
                                                 }
                                             else
                                                 {

From 1517bb1ab81c769a2d7c2bfb10c42f31b55c8c8d Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Wed, 5 Jul 2023 11:37:33 +0200
Subject: [PATCH 25/31] Make Clang-format happy

---
 .../signal_source/adapters/CMakeLists.txt       |  1 -
 .../gnuradio_blocks/ad936x_iio_source.cc        |  7 ++++---
 .../gr_complex_ip_packet_source.cc              |  2 +-
 .../signal_source/libs/ad936x_iio_custom.cc     | 17 +++++++++--------
 .../signal_source/libs/gnss_sdr_timestamp.h     |  2 +-
 .../signal_source/libs/pps_samplestamp.h        | 12 ++++++++++--
 src/algorithms/signal_source/libs/ppstcprx.cc   | 16 ++++++++++++----
 src/algorithms/signal_source/libs/ppstcprx.h    | 16 ++++++++++++----
 8 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt
index 1fd3d6ee8..afbf36296 100644
--- a/src/algorithms/signal_source/adapters/CMakeLists.txt
+++ b/src/algorithms/signal_source/adapters/CMakeLists.txt
@@ -18,7 +18,6 @@ if(ENABLE_PLUTOSDR)
     ##############################################
     set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} plutosdr_signal_source.cc)
     set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} plutosdr_signal_source.h)
-    
     ##############################################
     # CUSTOM AD936X IIO SOURCE
     ##############################################
diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
index 45cc5046f..0cf885fe2 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc
@@ -301,6 +301,7 @@ int ad936x_iio_source::general_work(int noutput_items,
     ad936x_custom->pop_sample_buffer(current_buffer);
     current_samples = current_buffer.get();
 
+
     // I and Q samples are interleaved in buffer: IQIQIQ...
     int32_t n_interleaved_iq_samples_per_channel = current_samples->n_bytes / (ad936x_custom->n_channels * 2);
     if (noutput_items < n_interleaved_iq_samples_per_channel)
@@ -311,14 +312,14 @@ int ad936x_iio_source::general_work(int noutput_items,
     else
         {
             // ad9361_channel_demux_and_record(current_samples, ad936x_custom->n_channels, &samplesfile);
-
+            auto **out = reinterpret_cast<int8_t **>(&output_items[0]);
             uint32_t current_byte = 0;
             uint32_t current_byte_in_gr = 0;
             int16_t ch = 0;
             // std::cout << "nbytes: " << samples_in->n_bytes << " nsamples: " << samples_in->n_samples << " nch: " << nchannels << "\n";
             if (ad936x_custom->n_channels == 1)
                 {
-                    memcpy(&((char *)output_items[0])[0], &current_samples->buffer[current_byte], current_samples->n_bytes);
+                    memcpy(out[0], &current_samples->buffer[current_byte], current_samples->n_bytes);
                 }
             else
                 {
@@ -327,7 +328,7 @@ int ad936x_iio_source::general_work(int noutput_items,
                         {
                             for (ch = 0; ch < ad936x_custom->n_channels; ch++)
                                 {
-                                    memcpy(&((char *)output_items[ch])[current_byte_in_gr], &current_samples->buffer[current_byte], 4);  // two bytes I + two bytes Q per channel: 4 bytes
+                                    memcpy(&out[ch][current_byte_in_gr], &current_samples->buffer[current_byte], 4);  // two bytes I + two bytes Q per channel: 4 bytes
                                     current_byte += 4;
                                 }
                             current_byte_in_gr += 4;
diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
index 0153a11a2..04840a799 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
@@ -334,7 +334,7 @@ void Gr_Complex_Ip_Packet_Source::demux_samples(const gr_vector_void_star &outpu
 {
     if (d_wire_sample_type == 5)
         {
-            //interleaved 2-bit I 2-bit Q samples packed in bytes: 1 byte -> 2 complex samples
+            // interleaved 2-bit I 2-bit Q samples packed in bytes: 1 byte -> 2 complex samples
             int nsample = 0;
             byte_2bit_struct sample{};  // <- 2 bits wide only
             int real;
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index c9d788bdd..1f7dfd823 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -21,6 +21,7 @@
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <utility>
 #include <vector>
 
 ad936x_iio_custom::ad936x_iio_custom(int debug_level_, int log_level_)
@@ -834,10 +835,10 @@ void ad936x_iio_custom::monitor_thread_fn()
                     continue;
                 }
 
-            //		if (device_is_tx) {
-            //			if (val & 1)
-            //				fprintf(stderr, "Underflow detected\n");
-            //		} else {
+            // if (device_is_tx) {
+            // if (val & 1)
+            // fprintf(stderr, "Underflow detected\n");
+            // } else {
             if (val & 4)
                 {
                     std::cout << "WARNING: IIO status register reported overflow!\n";
@@ -909,7 +910,7 @@ void ad936x_iio_custom::PlutoTxEnable(bool txon)
 void ad936x_iio_custom::setPlutoGpo(int p)
 {
     char pins[11];
-    sprintf(pins, "0x27 0x%x0", p);  // direct access to AD9361 registers... WARNING!
+    snprintf(pins, sizeof(pins), "0x27 0x%x0", p);  // direct access to AD9361 registers... WARNING!
     pins[9] = 0;
     int ret;
     // std::cout << "send: " << pins << " \n";
@@ -926,9 +927,9 @@ void ad936x_iio_custom::setPlutoGpo(int p)
 
 bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
 {
-    //	adi,gpo-manual-mode-enable		Enables GPO manual mode, this will conflict with automatic ENSM slave and eLNA mode
-    //	adi,gpo-manual-mode-enable-mask		Enable bit mask, setting or clearing bits will change the level of the corresponding output. Bit0 → GPO, Bit1 → GPO1, Bit2 → GPO2, Bit3 → GP03
-    //	adi,gpo-manual-mode-enable
+    // adi,gpo-manual-mode-enable Enables GPO manual mode, this will conflict with automatic ENSM slave and eLNA mode
+    // adi,gpo-manual-mode-enable-mask Enable bit mask, setting or clearing bits will change the level of the corresponding output. Bit0 → GPO, Bit1 → GPO1, Bit2 → GPO2, Bit3 → GP03
+    // adi,gpo-manual-mode-enable
     // adi,gpo-manual-mode-enable-mask does not work...
     // some software use the direct_reg_access (see https://github.com/g4eml/Langstone/blob/master/LangstoneGUI.c)
 
diff --git a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
index 11716ca82..6caafd8e5 100644
--- a/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
+++ b/src/algorithms/signal_source/libs/gnss_sdr_timestamp.h
@@ -71,8 +71,8 @@ private:
     double d_clock_offset_ms;
     double d_fraction_ms_offset;
     double d_integer_ms_offset;
-    uint64_t d_next_timetag_samplecount;
     int d_items_to_samples;
+    uint64_t d_next_timetag_samplecount;
     bool d_get_next_timetag;
 };
 
diff --git a/src/algorithms/signal_source/libs/pps_samplestamp.h b/src/algorithms/signal_source/libs/pps_samplestamp.h
index 900388867..d6891aa3e 100644
--- a/src/algorithms/signal_source/libs/pps_samplestamp.h
+++ b/src/algorithms/signal_source/libs/pps_samplestamp.h
@@ -1,8 +1,16 @@
-/* -------------------------------------------------------------------------
+/*!
+ * \file pps_samplestamp.h
+ * \brief A simple container for the sample count associated to PPS rising edge
+ * \author Javier Arribas, jarribas(at)cttc.es
+ * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2022 (see AUTHORS file for a list of contributors)
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
  *
+ * Copyright (C) 2010-2023  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
  *
+ * -----------------------------------------------------------------------------
  */
 
 #ifndef IIOPPS_PPS_SAMPLESTAMP_H
diff --git a/src/algorithms/signal_source/libs/ppstcprx.cc b/src/algorithms/signal_source/libs/ppstcprx.cc
index 73ea791db..93db26f61 100644
--- a/src/algorithms/signal_source/libs/ppstcprx.cc
+++ b/src/algorithms/signal_source/libs/ppstcprx.cc
@@ -1,8 +1,16 @@
-/*
- * ppstcprx.cc
+/*!
+ * \file ppstcprx.cc
+ * \brief TCP client class for front-end PPS samplestamp information reception
+ * \author Javier Arribas, jarribas(at)cttc.es
+ * -----------------------------------------------------------------------------
  *
- *  Created on: 28 feb 2022
- *      Author: javier
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2023  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
  */
 
 #include "ppstcprx.h"
diff --git a/src/algorithms/signal_source/libs/ppstcprx.h b/src/algorithms/signal_source/libs/ppstcprx.h
index 37c8de92f..653b2fdbd 100644
--- a/src/algorithms/signal_source/libs/ppstcprx.h
+++ b/src/algorithms/signal_source/libs/ppstcprx.h
@@ -1,8 +1,16 @@
-/*
- * ppstcprx.h
+/*!
+ * \file ppstcprx.h
+ * \brief TCP client class for front-end PPS samplestamp information reception
+ * \author Javier Arribas, jarribas(at)cttc.es
+ * -----------------------------------------------------------------------------
  *
- *  Created on: 28 feb 2022
- *      Author: javier
+ * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+ * This file is part of GNSS-SDR.
+ *
+ * Copyright (C) 2010-2023  (see AUTHORS file for a list of contributors)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
  */
 
 #ifndef SRC_LIBS_PPSTCPRX_H_

From 8e42b55f8d2fbc696ca77166cff17288552039f3 Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Wed, 5 Jul 2023 12:02:50 +0200
Subject: [PATCH 26/31] Code cleaning

---
 src/algorithms/signal_source/libs/ad936x_iio_custom.cc | 2 +-
 src/algorithms/signal_source/libs/pps_samplestamp.h    | 4 ++--
 src/algorithms/signal_source/libs/ppstcprx.cc          | 1 +
 src/algorithms/signal_source/libs/ppstcprx.h           | 2 ++
 4 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index 1f7dfd823..c99dba789 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -947,7 +947,7 @@ bool ad936x_iio_custom::select_rf_filter(std::string rf_filter)
 
 
     if (check_device() == false) return false;
-    int plutoGpo = 0;
+    // int plutoGpo = 0;
     int ret;
     ret = iio_device_debug_attr_write(phy, "adi,gpo-manual-mode-enable", "1");
 
diff --git a/src/algorithms/signal_source/libs/pps_samplestamp.h b/src/algorithms/signal_source/libs/pps_samplestamp.h
index d6891aa3e..df4b40929 100644
--- a/src/algorithms/signal_source/libs/pps_samplestamp.h
+++ b/src/algorithms/signal_source/libs/pps_samplestamp.h
@@ -21,8 +21,8 @@
 class PpsSamplestamp
 {
 public:
-    uint64_t samplestamp;   // PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side
-    uint32_t overflow_reg;  // >0 indicates overflow situation in the FPGA RX buffer
+    uint64_t samplestamp = 0;   // PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side
+    uint32_t overflow_reg = 0;  // >0 indicates overflow situation in the FPGA RX buffer
 };
 
 #endif
diff --git a/src/algorithms/signal_source/libs/ppstcprx.cc b/src/algorithms/signal_source/libs/ppstcprx.cc
index 93db26f61..0855ad0b6 100644
--- a/src/algorithms/signal_source/libs/ppstcprx.cc
+++ b/src/algorithms/signal_source/libs/ppstcprx.cc
@@ -17,6 +17,7 @@
 #include <cstring>
 #include <iostream>
 #include <sstream>
+#include <utility>
 #include <vector>
 
 pps_tcp_rx::pps_tcp_rx()
diff --git a/src/algorithms/signal_source/libs/ppstcprx.h b/src/algorithms/signal_source/libs/ppstcprx.h
index 653b2fdbd..3fb5878fb 100644
--- a/src/algorithms/signal_source/libs/ppstcprx.h
+++ b/src/algorithms/signal_source/libs/ppstcprx.h
@@ -18,10 +18,12 @@
 #include "concurrent_queue.h"
 #include "pps_samplestamp.h"
 #include <arpa/inet.h>
+#include <memory>
 #include <netinet/in.h>
 #include <string>
 #include <sys/socket.h>
 #include <sys/types.h>
+
 class pps_tcp_rx
 {
 private:

From 4f9a9068e97041550f34e6160e7b8643659cc01c Mon Sep 17 00:00:00 2001
From: Javier Arribas <javiarribas@gmail.com>
Date: Wed, 5 Jul 2023 15:51:01 +0200
Subject: [PATCH 27/31] Bug fix in iio signal source and ip packet source

---
 .../gnuradio_blocks/gr_complex_ip_packet_source.cc         | 5 ++++-
 src/algorithms/signal_source/libs/ad936x_iio_custom.cc     | 7 +++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
index 04840a799..d5659d0dc 100644
--- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
+++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc
@@ -19,6 +19,7 @@
 
 #include "gr_complex_ip_packet_source.h"
 #include <gnuradio/io_signature.h>
+#include <volk/volk.h>
 #include <array>
 #include <cstdint>
 #include <utility>
@@ -110,7 +111,7 @@ Gr_Complex_Ip_Packet_Source::Gr_Complex_Ip_Packet_Source(std::string src_device,
       d_pcap_thread(nullptr),
       d_src_device(std::move(src_device)),
       descr(nullptr),
-      fifo_buff(new char[FIFO_SIZE]),
+      fifo_buff(static_cast<char *>(volk_malloc(static_cast<int32_t>(FIFO_SIZE * sizeof(char)), volk_get_alignment()))),
       fifo_read_ptr(0),
       fifo_write_ptr(0),
       fifo_items(0),
@@ -297,6 +298,7 @@ void Gr_Complex_Ip_Packet_Source::pcap_callback(__attribute__((unused)) u_char *
                             if (aligned_write_items >= payload_length_bytes)
                                 {
                                     // write all in a single memcpy
+                                    gr::thread::scoped_lock guard(d_setlock);
                                     memcpy(&fifo_buff[fifo_write_ptr], &udp_payload[0], payload_length_bytes);  // size in bytes
                                     fifo_write_ptr += payload_length_bytes;
                                     if (fifo_write_ptr == FIFO_SIZE)
@@ -308,6 +310,7 @@ void Gr_Complex_Ip_Packet_Source::pcap_callback(__attribute__((unused)) u_char *
                             else
                                 {
                                     // two step wrap write
+                                    gr::thread::scoped_lock guard(d_setlock);
                                     memcpy(&fifo_buff[fifo_write_ptr], &udp_payload[0], aligned_write_items);  // size in bytes
                                     fifo_write_ptr = payload_length_bytes - aligned_write_items;
                                     memcpy(&fifo_buff[0], &udp_payload[aligned_write_items], fifo_write_ptr);  // size in bytes
diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
index c99dba789..e487d29b5 100644
--- a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
+++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc
@@ -581,9 +581,11 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
     //        {
     //            std::cout << "Warning: Unable to set AD936x RX filter parameters!\n";
     //        }
+    n_channels = 0;
     if (enable_ch0 == true)
         {
-            std::cerr << "* Get AD9361 Phy RX channel 0...\n";
+            n_channels++;
+            std::cout << "* Get AD9361 Phy RX channel 0...\n";
             std::stringstream name;
             name.str("");
             name << "voltage";
@@ -634,7 +636,8 @@ bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_,
 
     if (enable_ch1 == true)
         {
-            std::cerr << "* Get AD9361 Phy RX channel 1...\n";
+            n_channels++;
+            std::cout << "* Get AD9361 Phy RX channel 1...\n";
             std::stringstream name;
             name.str("");
             name << "voltage";

From a29f52e2e48b184e10d0cf3d5bc5f781f05a2980 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 5 Jul 2023 19:37:35 +0200
Subject: [PATCH 28/31] Make clang-tidy happy

---
 src/algorithms/PVT/libs/pvt_kf.cc             | 21 ++++++++----------
 src/algorithms/PVT/libs/pvt_kf.h              | 14 ++++++------
 src/algorithms/PVT/libs/rtklib_solver.cc      |  5 ++---
 src/algorithms/PVT/libs/rtklib_solver.h       |  4 ++--
 .../adapters/file_timestamp_signal_source.cc  | 22 ++++++++++++-------
 .../adapters/file_timestamp_signal_source.h   |  3 ++-
 6 files changed, 36 insertions(+), 33 deletions(-)

diff --git a/src/algorithms/PVT/libs/pvt_kf.cc b/src/algorithms/PVT/libs/pvt_kf.cc
index fc06b8757..a0fdda1d3 100644
--- a/src/algorithms/PVT/libs/pvt_kf.cc
+++ b/src/algorithms/PVT/libs/pvt_kf.cc
@@ -1,5 +1,5 @@
 /*!
- * \file Pvt_Kf.cc
+ * \file pvt_kf.cc
  * \brief Kalman Filter for Position and Velocity
  * \author Javier Arribas, 2023. jarribas(at)cttc.es
  *
@@ -14,11 +14,13 @@
  *
  * -----------------------------------------------------------------------------
  */
+
 #include "pvt_kf.h"
 #include <glog/logging.h>
 
 
-void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
+void Pvt_Kf::init_kf(const arma::vec& p,
+    const arma::vec& v,
     double solver_interval_s,
     double measures_ecef_pos_sd_m,
     double measures_ecef_vel_sd_ms,
@@ -28,7 +30,6 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
     // Kalman Filter class variables
     const double Ti = solver_interval_s;
 
-    std::cout << "Ti=" << Ti << "\n";
     d_F = {{1.0, 0.0, 0.0, Ti, 0.0, 0.0},
         {0.0, 1.0, 0.0, 0.0, Ti, 0.0},
         {0.0, 0.0, 1.0, 0.0, 0.0, Ti},
@@ -38,7 +39,7 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
 
     d_H = arma::eye(6, 6);
 
-    //   measurement matrix static covariances
+    // measurement matrix static covariances
     d_R = {{pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
         {0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
         {0.0, 0.0, pow(measures_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
@@ -46,9 +47,7 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
         {0.0, 0.0, 0.0, 0.0, pow(measures_ecef_vel_sd_ms, 2.0), 0.0},
         {0.0, 0.0, 0.0, 0.0, 0.0, pow(measures_ecef_vel_sd_ms, 2.0)}};
 
-
     // system covariance matrix (static)
-
     d_Q = {{pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0, 0.0},
         {0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0, 0.0},
         {0.0, 0.0, pow(system_ecef_pos_sd_m, 2.0), 0.0, 0.0, 0.0},
@@ -71,6 +70,7 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
 
     initialized = true;
 
+    DLOG(INFO) << "Ti: " << Ti;
     DLOG(INFO) << "F: " << d_F;
     DLOG(INFO) << "H: " << d_H;
     DLOG(INFO) << "R: " << d_R;
@@ -79,9 +79,10 @@ void Pvt_Kf::init_kf(arma::vec p, arma::vec v,
     DLOG(INFO) << "x: " << d_x_old_old;
 }
 
-void Pvt_Kf::run_Kf(arma::vec p, arma::vec v)
+
+void Pvt_Kf::run_Kf(const arma::vec& p, const arma::vec& v);
 {
-    //  Kalman loop
+    // Kalman loop
     // Prediction
     d_x_new_old = d_F * d_x_old_old;
     d_P_new_old = d_F * d_P_old_old * d_F.t() + d_Q;
@@ -98,10 +99,6 @@ void Pvt_Kf::run_Kf(arma::vec p, arma::vec v)
     d_P_old_old = d_P_new_new;
 }
 
-Pvt_Kf::Pvt_Kf()
-{
-    initialized = false;
-}
 
 void Pvt_Kf::get_pvt(arma::vec& p, arma::vec& v)
 {
diff --git a/src/algorithms/PVT/libs/pvt_kf.h b/src/algorithms/PVT/libs/pvt_kf.h
index ce1a3f8ba..26dc25a43 100644
--- a/src/algorithms/PVT/libs/pvt_kf.h
+++ b/src/algorithms/PVT/libs/pvt_kf.h
@@ -1,5 +1,5 @@
 /*!
- * \file Pvt_Kf.h
+ * \file pvt_kf.h
  * \brief Kalman Filter for Position and Velocity
  * \author Javier Arribas, 2023. jarribas(at)cttc.es
  *
@@ -33,18 +33,18 @@
 class Pvt_Kf
 {
 public:
-    Pvt_Kf();
+    Pvt_Kf() = default;
     virtual ~Pvt_Kf() = default;
-    void init_kf(arma::vec p, arma::vec v,
+    void init_kf(const arma::vec& p,
+        const arma::vec& v,
         double solver_interval_s,
         double measures_ecef_pos_sd_m,
         double measures_ecef_vel_sd_ms,
         double system_ecef_pos_sd_m,
         double system_ecef_vel_sd_ms);
-
-    void run_Kf(arma::vec p, arma::vec v);
-    bool initialized;
-    void get_pvt(arma::vec &p, arma::vec &v);
+    void run_Kf(const arma::vec& p, const arma::vec& v);
+    void get_pvt(arma::vec& p, arma::vec& v);
+    bool initialized{false};
 
 private:
     // Kalman Filter class variables
diff --git a/src/algorithms/PVT/libs/rtklib_solver.cc b/src/algorithms/PVT/libs/rtklib_solver.cc
index 70384d613..e17edfa14 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.cc
+++ b/src/algorithms/PVT/libs/rtklib_solver.cc
@@ -51,11 +51,10 @@ Rtklib_Solver::Rtklib_Solver(const rtk_t &rtk,
     bool flag_dump_to_mat,
     Pvt_Conf conf) : d_dump_filename(dump_filename),
                      d_rtk(rtk),
+                     d_conf(std::move(conf)),
                      d_type_of_rx(type_of_rx),
                      d_flag_dump_enabled(flag_dump_to_file),
-                     d_flag_dump_mat_enabled(flag_dump_to_mat),
-                     d_conf(conf)
-
+                     d_flag_dump_mat_enabled(flag_dump_to_mat)
 {
     this->set_averaging_flag(false);
 
diff --git a/src/algorithms/PVT/libs/rtklib_solver.h b/src/algorithms/PVT/libs/rtklib_solver.h
index b967cb02f..7bffa042d 100644
--- a/src/algorithms/PVT/libs/rtklib_solver.h
+++ b/src/algorithms/PVT/libs/rtklib_solver.h
@@ -151,11 +151,11 @@ private:
     rtk_t d_rtk{};
     nav_t d_nav_data{};
     Monitor_Pvt d_monitor_pvt{};
+    Pvt_Conf d_conf;
+    Pvt_Kf d_pvt_kf;
     uint32_t d_type_of_rx;
     bool d_flag_dump_enabled;
     bool d_flag_dump_mat_enabled;
-    Pvt_Conf d_conf;
-    Pvt_Kf d_pvt_kf;
 };
 
 
diff --git a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
index 8e17180f8..8a7c2958f 100644
--- a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
+++ b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.cc
@@ -1,6 +1,7 @@
 /*!
- * \file file_timestamp_signal_source.h
- * \brief This class reads samples stored in a file and generate stream tags with its timestamp information stored in separated file
+ * \file file_timestamp_signal_source.cc
+ * \brief This class reads samples stored in a file and generate stream tags
+ * with its timestamp information stored in separated file
  * \author Javier Arribas, jarribas(at)cttc.es
  *
  * -----------------------------------------------------------------------------
@@ -13,6 +14,7 @@
  *
  * -----------------------------------------------------------------------------
  */
+
 #include "file_timestamp_signal_source.h"
 #include "gnss_sdr_flags.h"
 #include "gnss_sdr_string_literals.h"
@@ -22,7 +24,9 @@
 using namespace std::string_literals;
 
 FileTimestampSignalSource::FileTimestampSignalSource(const ConfigurationInterface* configuration,
-    const std::string& role, unsigned int in_streams, unsigned int out_streams,
+    const std::string& role,
+    unsigned int in_streams,
+    unsigned int out_streams,
     Concurrent_Queue<pmt::pmt_t>* queue)
     : FileSourceBase(configuration, role, "File_Timestamp_Signal_Source"s, queue, "byte"s),
       timestamp_file_(configuration->property(role + ".timestamp_filename"s, "../data/example_capture_timestamp.dat"s)),
@@ -53,23 +57,23 @@ void FileTimestampSignalSource::create_file_source_hook()
     int source_items_to_samples = 1;
     bool is_complex = false;
 
-    if (item_type().compare("ibyte") == 0)
+    if (item_type() == "ibyte")
         {
             source_items_to_samples = 1;
         }
-    else if (item_type().compare("byte") == 0)
+    else if (item_type() == "byte")
         {
             source_items_to_samples = 1;
         }
-    else if (item_type().compare("short") == 0)
+    else if (item_type() == "short")
         {
             source_items_to_samples = 1;
         }
-    else if (item_type().compare("ishort") == 0)
+    else if (item_type() == "ishort")
         {
             source_items_to_samples = 1;
         }
-    else if (item_type().compare("gr_complex") == 0)
+    else if (item_type() == "gr_complex")
         {
             source_items_to_samples = 1;
             is_complex = true;
@@ -96,12 +100,14 @@ void FileTimestampSignalSource::create_file_source_hook()
     DLOG(INFO) << "timestamp_block_(" << timestamp_block_->unique_id() << ")";
 }
 
+
 void FileTimestampSignalSource::pre_connect_hook(gr::top_block_sptr top_block)
 {
     top_block->connect(file_source(), 0, timestamp_block_, 0);
     DLOG(INFO) << "connected file_source to timestamp_block_";
 }
 
+
 void FileTimestampSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block)
 {
     top_block->disconnect(file_source(), 0, timestamp_block_, 0);
diff --git a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.h b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.h
index f95b3622e..0c82cab86 100644
--- a/src/algorithms/signal_source/adapters/file_timestamp_signal_source.h
+++ b/src/algorithms/signal_source/adapters/file_timestamp_signal_source.h
@@ -1,6 +1,7 @@
 /*!
  * \file file_timestamp_signal_source.h
- * \brief This class reads samples stored in a file and generate stream tags with its timestamp information stored in separated file
+ * \brief This class reads samples stored in a file and generate stream tags
+ * with its timestamp information stored in separated file
  * \author Javier Arribas, jarribas(at)cttc.es
  *
  * -----------------------------------------------------------------------------

From 5759e0a38545abeaaae3a033c044affa9f6bdda7 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 5 Jul 2023 19:39:17 +0200
Subject: [PATCH 29/31] Bump Benchmark to 1.8.1

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 398c1f266..23e562631 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -344,7 +344,7 @@ set(GNSSSDR_PUGIXML_LOCAL_VERSION "1.13")
 set(GNSSSDR_GTEST_LOCAL_VERSION "1.13.0")
 set(GNSSSDR_GNSS_SIM_LOCAL_VERSION "master")
 set(GNSSSDR_GNSSTK_LOCAL_VERSION "14.0.0")
-set(GNSSSDR_BENCHMARK_LOCAL_VERSION "1.8.0")
+set(GNSSSDR_BENCHMARK_LOCAL_VERSION "1.8.1")
 set(GNSSSDR_MATHJAX_EXTERNAL_VERSION "2.7.7")
 
 # Downgrade versions if requirements are not met

From 4cc4a6cbb62e678acf2d6089ee12921e80777b6c Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 5 Jul 2023 20:03:22 +0200
Subject: [PATCH 30/31] Update changelog

---
 docs/CHANGELOG.md | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 5fd567b0c..422237dd3 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -12,6 +12,20 @@ SPDX-FileCopyrightText: 2011-2023 Carles Fernandez-Prades <carles.fernandez@cttc
 
 All notable changes to GNSS-SDR will be documented in this file.
 
+## [Unreleased](https://github.com/gnss-sdr/gnss-sdr/tree/next)
+
+### Improvements in Repeatability
+
+- A Kalman filter is now available in the PVT block, smoothing the outputs of a
+  simple Least Squares solution and improving the precision of delivered fixes.
+  It can be enabled by setting `PVT.enable_pvt_kf=true` in the configuration
+  file. The user can set values for the measurement and process noise
+  covariances with the following optional parameters (here with their default
+  values): `PVT.kf_measures_ecef_pos_sd_m=1.0`, in [m];
+  `PVT.kf_measures_ecef_vel_sd_ms=0.1`, in [m/s];
+  `PVT.kf_system_ecef_pos_sd_m=0.01`, in [m]; and
+  `PVT.kf_system_ecef_vel_sd_ms=0.001`, in [m/s].
+
 ## [GNSS-SDR v0.0.18](https://github.com/gnss-sdr/gnss-sdr/releases/tag/v0.0.18) - 2023-04-06
 
 [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7805514.svg)](https://doi.org/10.5281/zenodo.7805514)

From 7d475f9ffe4ddc9169a9184b849321af272adbea Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Wed, 5 Jul 2023 20:20:51 +0200
Subject: [PATCH 31/31] Add missing change

---
 src/algorithms/PVT/libs/pvt_kf.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/algorithms/PVT/libs/pvt_kf.cc b/src/algorithms/PVT/libs/pvt_kf.cc
index a0fdda1d3..10083b221 100644
--- a/src/algorithms/PVT/libs/pvt_kf.cc
+++ b/src/algorithms/PVT/libs/pvt_kf.cc
@@ -80,7 +80,7 @@ void Pvt_Kf::init_kf(const arma::vec& p,
 }
 
 
-void Pvt_Kf::run_Kf(const arma::vec& p, const arma::vec& v);
+void Pvt_Kf::run_Kf(const arma::vec& p, const arma::vec& v)
 {
     // Kalman loop
     // Prediction