summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorghostop14 <ghostop14@gmail.com>2020-02-04 18:37:41 -0500
committerMichael Dickens <michael.dickens@ettus.com>2020-02-15 19:20:26 -0500
commit012870af22ae873b3b998691de8e81752179d266 (patch)
treed294d2d83b4f94b11922cf2873ebaf473bb36df3
parentf6a346d8b59050b2da520c80f7fab9d85018e350 (diff)
Blocks: Add DC Spike Removal and IQ Swap Native Blocks
This block incorporates the OOT modules in correctiq that provide 3 different techniques to remove the DC spike inherent in IQ sampling. The first technique mirrors SDR GUI receivers with an IIR filter approach, the second provides a time-limited IIR approach where after a user-configurable number of seconds locks into a basic offset correction to eliminate the effect of the filter on the signal while maintaining the correction, and the last is manual I and Q configurable offsets. The Swap IQ block is a drop-in block to help correct for inverted spectrums and just swaps I<->Q.
-rw-r--r--gr-blocks/grc/blocks.tree.yml5
-rw-r--r--gr-blocks/grc/blocks_correctiq.block.yml20
-rw-r--r--gr-blocks/grc/blocks_correctiq_auto.block.yml56
-rw-r--r--gr-blocks/grc/blocks_correctiq_man.block.yml39
-rw-r--r--gr-blocks/grc/blocks_correctiq_swapiq.block.yml30
-rw-r--r--gr-blocks/include/gnuradio/blocks/CMakeLists.txt4
-rw-r--r--gr-blocks/include/gnuradio/blocks/correctiq.h44
-rw-r--r--gr-blocks/include/gnuradio/blocks/correctiq_auto.h55
-rw-r--r--gr-blocks/include/gnuradio/blocks/correctiq_man.h53
-rw-r--r--gr-blocks/include/gnuradio/blocks/correctiq_swapiq.h45
-rw-r--r--gr-blocks/lib/CMakeLists.txt4
-rw-r--r--gr-blocks/lib/correctiq_auto_impl.cc204
-rw-r--r--gr-blocks/lib/correctiq_auto_impl.h68
-rw-r--r--gr-blocks/lib/correctiq_impl.cc64
-rw-r--r--gr-blocks/lib/correctiq_impl.h38
-rw-r--r--gr-blocks/lib/correctiq_man_impl.cc169
-rw-r--r--gr-blocks/lib/correctiq_man_impl.h54
-rw-r--r--gr-blocks/lib/correctiq_swapiq_impl.cc96
-rw-r--r--gr-blocks/lib/correctiq_swapiq_impl.h41
-rw-r--r--gr-blocks/swig/blocks_swig0.i12
20 files changed, 1101 insertions, 0 deletions
diff --git a/gr-blocks/grc/blocks.tree.yml b/gr-blocks/grc/blocks.tree.yml
index 8fcdf26544..2c05555883 100644
--- a/gr-blocks/grc/blocks.tree.yml
+++ b/gr-blocks/grc/blocks.tree.yml
@@ -39,6 +39,11 @@
- blocks_file_meta_source
- blocks_file_meta_sink
- blocks_tagged_file_sink
+- IQ Correction:
+ - blocks_correctiq_auto
+ - blocks_correctiq_man
+ - blocks_swapiq
+ - blocks_correctiq
- Level Controllers:
- blocks_mute_xx
- blocks_sample_and_hold_xx
diff --git a/gr-blocks/grc/blocks_correctiq.block.yml b/gr-blocks/grc/blocks_correctiq.block.yml
new file mode 100644
index 0000000000..3a1051b07a
--- /dev/null
+++ b/gr-blocks/grc/blocks_correctiq.block.yml
@@ -0,0 +1,20 @@
+id: blocks_correctiq
+label: Remove DC Spike
+category: '[Core]/IQ Correction'
+
+templates:
+ imports: from gnuradio import blocks
+ make: blocks.correctiq(${})
+
+inputs:
+- domain: stream
+ dtype: complex
+
+outputs:
+- domain: stream
+ dtype: complex
+
+documentation: |-
+ This block to removes that center frequency IQ DC spike with an IIR filter.
+
+file_format: 1
diff --git a/gr-blocks/grc/blocks_correctiq_auto.block.yml b/gr-blocks/grc/blocks_correctiq_auto.block.yml
new file mode 100644
index 0000000000..f6f0a7a056
--- /dev/null
+++ b/gr-blocks/grc/blocks_correctiq_auto.block.yml
@@ -0,0 +1,56 @@
+id: blocks_correctiq_auto
+label: Remove DC Spike AutoSync
+category: '[Core]/IQ Correction'
+
+templates:
+ imports: from gnuradio import blocks
+ make: blocks.correctiq_auto(${samp_rate}, ${freq}, ${gain}, ${syncWindow})
+ callbacks:
+ - set_freq(${freq})
+ - set_gain(${gain})
+
+parameters:
+- id: samp_rate
+ label: Sample Rate
+ dtype: float
+ default: samp_rate
+- id: syncWindow
+ label: Sync Learning Period (sec)
+ dtype: float
+ default: '2'
+- id: freq
+ label: Frequency
+ dtype: float
+- id: gain
+ label: Upstream Gain
+ dtype: float
+
+inputs:
+- domain: stream
+ dtype: complex
+- domain: message
+ id: rsync
+ optional: true
+
+outputs:
+- domain: stream
+ dtype: complex
+- domain: message
+ id: sync_start
+ optional: true
+- domain: message
+ id: offsets
+ optional: true
+
+documentation: |-
+ This block to removes that center frequency IQ DC spike with a slight variation. It automatically calculates the offset then switches to straight DC offset mode to prevent any possible IIR filtering after it's been tuned. However, if frequency or upstream gain is changed, it must retune, so frequency and upstream gain are all taken as parameters and monitored for changes.
+
+ Notes:
+
+ 1. Any message received on the rsync port will trigger a resync process. So it could be a frequency or gain change, or any other desired condition.
+
+ 2. The optional sync_start output message will be sent whenever a resync is triggered. This can be used downstream to know the filter is calculating and adapting. Note that there is no initial syncing message (this is assumed on block start so downstream blocks that are interested should make the same assumption).
+
+ 3. When a sync is finished, the real and imag offsets will be sent in the optional offsets message. Note that the work loop calculates out[].real(in[].real() - real_offset) and same for imag. So offsets are subtractive from the input values.
+
+file_format: 1
diff --git a/gr-blocks/grc/blocks_correctiq_man.block.yml b/gr-blocks/grc/blocks_correctiq_man.block.yml
new file mode 100644
index 0000000000..7a465ed48d
--- /dev/null
+++ b/gr-blocks/grc/blocks_correctiq_man.block.yml
@@ -0,0 +1,39 @@
+id: blocks_correctiq_man
+label: IQ Correction Manual Offset
+category: '[Core]/IQ Correction'
+
+templates:
+ imports: from gnuradio import blocks
+ make: blocks.correctiq_man(${real}, ${imag})
+ callbacks:
+ - set_real(${real})
+ - set_imag(${imag})
+
+parameters:
+- id: real
+ label: Real (I)
+ dtype: float
+ default: '0.0'
+- id: imag
+ label: Imag (Q)
+ dtype: float
+ default: '0.0'
+
+inputs:
+- domain: stream
+ dtype: complex
+- domain: message
+ id: set_real
+ optional: true
+- domain: message
+ id: set_imag
+ optional: true
+
+outputs:
+- domain: stream
+ dtype: complex
+
+documentation: |-
+ This block provides a mechanism to manually provide a real and imaginary signal offset. Very similar to a complex add block, the block supports dynamic updating on the values.
+
+file_format: 1
diff --git a/gr-blocks/grc/blocks_correctiq_swapiq.block.yml b/gr-blocks/grc/blocks_correctiq_swapiq.block.yml
new file mode 100644
index 0000000000..f29ebb6015
--- /dev/null
+++ b/gr-blocks/grc/blocks_correctiq_swapiq.block.yml
@@ -0,0 +1,30 @@
+id: blocks_swapiq
+label: Swap IQ
+category: '[Core]/IQ Correction'
+
+templates:
+ imports: from gnuradio import blocks
+ make: blocks.swap_iq(${datatype.datatype}, ${datatype.datasize})
+
+parameters:
+- id: datatype
+ label: Input Type
+ dtype: enum
+ options: [complex, short, byte]
+ option_attributes:
+ datasize: [gr.sizeof_gr_complex, gr.sizeof_short, gr.sizeof_char]
+ datatype: ['1', '2', '3']
+ hide: part
+
+inputs:
+- domain: stream
+ dtype: ${ type }
+
+outputs:
+- domain: stream
+ dtype: ${ type }
+
+documentation: |-
+ This block will transpose the I and Q channels (Swap IQ) to correct for spectrally inverted inputs.
+
+file_format: 1
diff --git a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt
index 7a75fe1d8b..d7da574568 100644
--- a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt
+++ b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt
@@ -17,6 +17,10 @@ install(FILES
api.h
argmax.h
control_loop.h
+ correctiq.h
+ correctiq_auto.h
+ correctiq_man.h
+ correctiq_swapiq.h
count_bits.h
divide.h
file_sink_base.h
diff --git a/gr-blocks/include/gnuradio/blocks/correctiq.h b/gr-blocks/include/gnuradio/blocks/correctiq.h
new file mode 100644
index 0000000000..f7ebe497c4
--- /dev/null
+++ b/gr-blocks/include/gnuradio/blocks/correctiq.h
@@ -0,0 +1,44 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_H
+
+#include <gnuradio/blocks/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace blocks {
+
+/*!
+ * \brief This block to removes that center frequency IQ DC spike with an IIR filter.
+ * \ingroup iq_correction
+ *
+ */
+class BLOCKS_API correctiq : virtual public gr::sync_block
+{
+public:
+ typedef boost::shared_ptr<correctiq> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of correctiq::correctiq.
+ *
+ * To avoid accidental use of raw pointers, correctiq::correctiq's
+ * constructor is in a private implementation
+ * class. correctiq::correctiq::make is the public interface for
+ * creating new instances.
+ */
+ static sptr make();
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_H */
diff --git a/gr-blocks/include/gnuradio/blocks/correctiq_auto.h b/gr-blocks/include/gnuradio/blocks/correctiq_auto.h
new file mode 100644
index 0000000000..128c3cdf6b
--- /dev/null
+++ b/gr-blocks/include/gnuradio/blocks/correctiq_auto.h
@@ -0,0 +1,55 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_H
+
+#include <gnuradio/blocks/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace blocks {
+
+/*!
+ * \brief This block to removes that center frequency IQ DC spike with a slight variation.
+ * \p It automatically calculates the offset then switches to straight DC offset mode to
+ * \p prevent any possible IIR filtering after it's been tuned. However, if frequency
+ * \p or upstream gain is changed, it must retune, so frequency and upstream gain are
+ * \p all taken as parameters and monitored for changes.
+ * \ingroup iq_correction
+ *
+ */
+class BLOCKS_API correctiq_auto : virtual public gr::sync_block
+{
+public:
+ typedef boost::shared_ptr<correctiq_auto> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of correctiq::correctiq_auto.
+ *
+ * To avoid accidental use of raw pointers, correctiq::correctiq_auto's
+ * constructor is in a private implementation
+ * class. correctiq::correctiq_auto::make is the public interface for
+ * creating new instances.
+ */
+
+ virtual double get_freq() = 0;
+ virtual float get_gain() = 0;
+
+ virtual void set_freq(double newValue) = 0;
+ virtual void set_gain(float newValue) = 0;
+
+ static sptr make(double samp_rate, double freq, float gain, float sync_window);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_H */
diff --git a/gr-blocks/include/gnuradio/blocks/correctiq_man.h b/gr-blocks/include/gnuradio/blocks/correctiq_man.h
new file mode 100644
index 0000000000..0d46f0c0b9
--- /dev/null
+++ b/gr-blocks/include/gnuradio/blocks/correctiq_man.h
@@ -0,0 +1,53 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_MAN_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_MAN_H
+
+#include <gnuradio/blocks/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace blocks {
+
+/*!
+ * \brief This block provides a mechanism to manually provide a real and
+ * \p imaginary signal offset. Very similar to a complex add block,
+ * \p the block supports dynamic updating on the values.
+ * \ingroup iq_correction
+ *
+ */
+class BLOCKS_API correctiq_man : virtual public gr::sync_block
+{
+public:
+ typedef boost::shared_ptr<correctiq_man> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of correctiq::correctiq_man.
+ *
+ * To avoid accidental use of raw pointers, correctiq::correctiq_man's
+ * constructor is in a private implementation
+ * class. correctiq::correctiq_man::make is the public interface for
+ * creating new instances.
+ */
+
+ virtual float get_real() = 0;
+ virtual float get_imag() = 0;
+
+ virtual void set_real(float newValue) = 0;
+ virtual void set_imag(float newValue) = 0;
+
+ static sptr make(float real, float imag);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_MAN_H */
diff --git a/gr-blocks/include/gnuradio/blocks/correctiq_swapiq.h b/gr-blocks/include/gnuradio/blocks/correctiq_swapiq.h
new file mode 100644
index 0000000000..35a18649b4
--- /dev/null
+++ b/gr-blocks/include/gnuradio/blocks/correctiq_swapiq.h
@@ -0,0 +1,45 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_SWAPIQ_H
+#define INCLUDED_CORRECTIQ_SWAPIQ_H
+
+#include <gnuradio/blocks/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace blocks {
+
+/*!
+ * \brief This block will transpose the I and Q channels (Swap IQ) to
+ * \p correct for spectrally inverted inputs.
+ * \ingroup iq_correction
+ *
+ */
+class BLOCKS_API swap_iq : virtual public gr::sync_block
+{
+public:
+ typedef boost::shared_ptr<swap_iq> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of correctiq::swap_iq.
+ *
+ * To avoid accidental use of raw pointers, correctiq::swap_iq's
+ * constructor is in a private implementation
+ * class. correctiq::swap_iq::make is the public interface for
+ * creating new instances.
+ */
+ static sptr make(int datatype, int datasize);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_SWAPIQ_H */
diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt
index ddf60e1723..233a4e068d 100644
--- a/gr-blocks/lib/CMakeLists.txt
+++ b/gr-blocks/lib/CMakeLists.txt
@@ -20,6 +20,10 @@ add_library(gnuradio-blocks
and_blk_impl.cc
and_const_impl.cc
argmax_impl.cc
+ correctiq_auto_impl.cc
+ correctiq_impl.cc
+ correctiq_man_impl.cc
+ correctiq_swapiq_impl.cc
divide_impl.cc
integrate_impl.cc
max_blk_impl.cc
diff --git a/gr-blocks/lib/correctiq_auto_impl.cc b/gr-blocks/lib/correctiq_auto_impl.cc
new file mode 100644
index 0000000000..76f3ed5e7b
--- /dev/null
+++ b/gr-blocks/lib/correctiq_auto_impl.cc
@@ -0,0 +1,204 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "correctiq_auto_impl.h"
+#include <gnuradio/io_signature.h>
+#include <volk/volk.h>
+#include <boost/format.hpp>
+
+namespace gr {
+namespace blocks {
+
+correctiq_auto::sptr
+correctiq_auto::make(double samp_rate, double freq, float gain, float sync_window)
+{
+ return gnuradio::get_initial_sptr(
+ new correctiq_auto_impl(samp_rate, freq, gain, sync_window));
+}
+
+/*
+ * The private constructor
+ */
+correctiq_auto_impl::correctiq_auto_impl(double samp_rate,
+ double freq,
+ float gain,
+ float sync_window)
+ : gr::sync_block("correctiq_auto",
+ gr::io_signature::make(1, 1, sizeof(gr_complex)),
+ gr::io_signature::make(1, 1, sizeof(gr_complex))),
+ d_avg_real(0.0),
+ d_avg_img(0.0),
+ d_ratio(1e-05f),
+ d_samp_rate(samp_rate),
+ d_freq(freq),
+ d_gain(gain),
+ d_sync_window(sync_window),
+ d_synchronized(false),
+ d_buffer_size(8192),
+ d_volk_const_buffer(NULL)
+{
+ d_max_sync_samples = (long)(d_samp_rate * (double)d_sync_window);
+
+ set_const_buffer(d_buffer_size);
+ d_k = gr_complex(0.0, 0.0);
+ fill_const_buffer();
+
+ message_port_register_in(pmt::mp("rsync"));
+ set_msg_handler(pmt::mp("rsync"),
+ [this](pmt::pmt_t msg) { this->handle_resync(msg); });
+
+ message_port_register_out(pmt::mp("sync_start"));
+ message_port_register_out(pmt::mp("offsets"));
+
+ GR_LOG_INFO(d_logger, "Block start. Auto Synchronizing...");
+}
+
+void correctiq_auto_impl::trigger_resync(std::string reason)
+{
+ gr::thread::scoped_lock guard(d_setlock);
+
+ std::string d_resync_msg = reason + ". Auto Synchronizing...";
+
+ GR_LOG_INFO(d_logger, d_resync_msg);
+ d_synchronized = false;
+ d_sync_counter = 0;
+
+ // reset counters on an unsync
+ d_avg_real = 0.0;
+ d_avg_img = 0.0;
+ d_k = gr_complex(d_avg_real, d_avg_img);
+
+ send_syncing();
+}
+
+void correctiq_auto_impl::send_sync_values()
+{
+ pmt::pmt_t meta = pmt::make_dict();
+
+ meta = pmt::dict_add(meta, pmt::mp("real"), pmt::mp(d_avg_real));
+ meta = pmt::dict_add(meta, pmt::mp("imag"), pmt::mp(d_avg_img));
+
+ pmt::pmt_t pdu = pmt::cons(meta, pmt::PMT_NIL);
+ message_port_pub(pmt::mp("offsets"), pdu);
+}
+
+void correctiq_auto_impl::send_syncing()
+{
+ pmt::pmt_t pdu = pmt::cons(pmt::intern("syncing"), pmt::from_bool(true));
+
+ message_port_pub(pmt::mp("sync_start"), pdu);
+}
+
+void correctiq_auto_impl::handle_resync(pmt::pmt_t msg)
+{
+ trigger_resync("Resync message received.");
+}
+
+/*
+ * Our virtual destructor.
+ */
+correctiq_auto_impl::~correctiq_auto_impl()
+{
+ if (d_volk_const_buffer)
+ volk_free(d_volk_const_buffer);
+}
+
+void correctiq_auto_impl::set_const_buffer(int new_size)
+{
+ d_buffer_size = new_size;
+
+ if (d_volk_const_buffer) {
+ volk_free(d_volk_const_buffer);
+ }
+
+ d_volk_const_buffer = reinterpret_cast<gr_complex*>(
+ volk_malloc(sizeof(gr_complex) * d_buffer_size, volk_get_alignment()));
+
+ fill_const_buffer();
+}
+
+void correctiq_auto_impl::fill_const_buffer()
+{
+ gr_complex* tmp_ptr = d_volk_const_buffer;
+
+ for (int i = 0; i < d_buffer_size; i++) {
+ *tmp_ptr++ = d_k;
+ }
+}
+
+double correctiq_auto_impl::get_freq() { return d_freq; }
+
+float correctiq_auto_impl::get_gain() { return d_gain; }
+
+void correctiq_auto_impl::set_freq(double new_value)
+{
+ trigger_resync("Frequency change detected");
+}
+
+void correctiq_auto_impl::set_gain(float new_value)
+{
+ trigger_resync("Gain change detected");
+}
+
+int correctiq_auto_impl::work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items)
+{
+ gr::thread::scoped_lock guard(d_setlock);
+
+ if (!d_synchronized) {
+ const gr_complex* in = (const gr_complex*)input_items[0];
+ gr_complex* out = (gr_complex*)output_items[0];
+
+ for (int i = 0; i < noutput_items; i++) {
+ // Synchronizing. Behave just like normal correctiq.
+ d_avg_real = d_ratio * (in[i].real() - d_avg_real) + d_avg_real;
+ d_avg_img = d_ratio * (in[i].imag() - d_avg_img) + d_avg_img;
+ d_sync_counter++;
+
+ out[i].real(in[i].real() - d_avg_real);
+ out[i].imag(in[i].imag() - d_avg_img);
+ }
+ } else {
+ if (noutput_items > d_buffer_size)
+ set_const_buffer(noutput_items);
+
+ // Inputs are complex but we're casting as floats to leverage volk
+ const float* in = (const float*)input_items[0];
+ float* out = (float*)output_items[0];
+
+ volk_32f_x2_add_32f(
+ out, in, reinterpret_cast<float*>(d_volk_const_buffer), 2 * noutput_items);
+ }
+
+ if (!d_synchronized && (d_sync_counter >= d_max_sync_samples)) {
+ d_synchronized = true;
+ d_k = gr_complex(d_avg_real, d_avg_img);
+ fill_const_buffer();
+
+ GR_LOG_INFO(d_logger, "Auto offset now synchronized.");
+ GR_LOG_INFO(
+ d_logger,
+ boost::format{ "Applying these offsets (real-real_offset, "
+ "imag-imag_offset)... real_offset: %.6f, imag_offset: %.6f" } %
+ d_avg_real % d_avg_img);
+
+ send_sync_values();
+ }
+
+ return noutput_items;
+}
+
+} /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/lib/correctiq_auto_impl.h b/gr-blocks/lib/correctiq_auto_impl.h
new file mode 100644
index 0000000000..5ad6eea4f0
--- /dev/null
+++ b/gr-blocks/lib/correctiq_auto_impl.h
@@ -0,0 +1,68 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_IMPL_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_IMPL_H
+
+#include <gnuradio/blocks/correctiq_auto.h>
+
+namespace gr {
+namespace blocks {
+
+class correctiq_auto_impl : public correctiq_auto
+{
+private:
+ float d_avg_real;
+ float d_avg_img;
+ float d_ratio;
+ gr_complex d_k;
+
+ double d_samp_rate;
+ double d_freq;
+ float d_gain;
+ float d_sync_window;
+
+ long d_sync_counter;
+ bool d_synchronized;
+
+ long d_max_sync_samples;
+
+ void send_sync_values();
+ void send_syncing();
+
+ void trigger_resync(std::string reason);
+
+ int d_buffer_size;
+ gr_complex* d_volk_const_buffer;
+
+ void set_const_buffer(int new_size);
+ void fill_const_buffer();
+
+public:
+ correctiq_auto_impl(double samp_rate, double freq, float gain, float sync_window);
+ ~correctiq_auto_impl();
+
+ virtual double get_freq();
+ virtual float get_gain();
+
+ virtual void set_freq(double new_value);
+ virtual void set_gain(float new_value);
+
+ void handle_resync(pmt::pmt_t msg);
+
+ int work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_AUTO_IMPL_H */
diff --git a/gr-blocks/lib/correctiq_impl.cc b/gr-blocks/lib/correctiq_impl.cc
new file mode 100644
index 0000000000..d44b36ddc0
--- /dev/null
+++ b/gr-blocks/lib/correctiq_impl.cc
@@ -0,0 +1,64 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "correctiq_impl.h"
+#include <gnuradio/io_signature.h>
+
+namespace gr {
+namespace blocks {
+
+correctiq::sptr correctiq::make()
+{
+ return gnuradio::get_initial_sptr(new correctiq_impl());
+}
+
+/*
+ * The private constructor
+ */
+correctiq_impl::correctiq_impl()
+ : gr::sync_block("correctiq",
+ gr::io_signature::make(1, 1, sizeof(gr_complex)),
+ gr::io_signature::make(1, 1, sizeof(gr_complex))),
+ d_avg_real(0.0),
+ d_avg_img(0.0),
+ d_ratio(1e-05f)
+{
+}
+
+/*
+ * Our virtual destructor.
+ */
+correctiq_impl::~correctiq_impl() {}
+
+int correctiq_impl::work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items)
+{
+ const gr_complex* in = (const gr_complex*)input_items[0];
+ gr_complex* out = (gr_complex*)output_items[0];
+
+ for (int i = 0; i < noutput_items; i++) {
+ d_avg_real = d_ratio * (in[i].real() - d_avg_real) + d_avg_real;
+ d_avg_img = d_ratio * (in[i].imag() - d_avg_img) + d_avg_img;
+
+ out[i].real(in[i].real() - d_avg_real);
+ out[i].imag(in[i].imag() - d_avg_img);
+ }
+
+ // Tell runtime system how many output items we produced.
+ return noutput_items;
+}
+
+} /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/lib/correctiq_impl.h b/gr-blocks/lib/correctiq_impl.h
new file mode 100644
index 0000000000..901b706ea6
--- /dev/null
+++ b/gr-blocks/lib/correctiq_impl.h
@@ -0,0 +1,38 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_IMPL_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_IMPL_H
+
+#include <gnuradio/blocks/correctiq.h>
+
+namespace gr {
+namespace blocks {
+
+class correctiq_impl : public correctiq
+{
+private:
+ float d_avg_real = 0.0f;
+ float d_avg_img = 0.0f;
+ float d_ratio = 1e-05f;
+
+public:
+ correctiq_impl();
+ ~correctiq_impl();
+
+ int work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_IMPL_H */
diff --git a/gr-blocks/lib/correctiq_man_impl.cc b/gr-blocks/lib/correctiq_man_impl.cc
new file mode 100644
index 0000000000..ab82c72402
--- /dev/null
+++ b/gr-blocks/lib/correctiq_man_impl.cc
@@ -0,0 +1,169 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "correctiq_man_impl.h"
+#include <gnuradio/io_signature.h>
+#include <volk/volk.h>
+
+namespace gr {
+namespace blocks {
+
+correctiq_man::sptr correctiq_man::make(float real, float imag)
+{
+ return gnuradio::get_initial_sptr(new correctiq_man_impl(real, imag));
+}
+
+/*
+ * The private constructor
+ */
+correctiq_man_impl::correctiq_man_impl(float real, float imag)
+ : gr::sync_block("correctiq_man",
+ gr::io_signature::make(1, 1, sizeof(gr_complex)),
+ gr::io_signature::make(1, 1, sizeof(gr_complex))),
+ d_avg_real(real),
+ d_avg_img(imag),
+ d_buffer_size(8192),
+ d_volk_const_buffer(NULL)
+{
+ d_k = gr_complex(d_avg_real, d_avg_img);
+
+ set_const_buffer(d_buffer_size);
+
+ message_port_register_in(pmt::mp("set_real"));
+ set_msg_handler(pmt::mp("set_real"),
+ [this](pmt::pmt_t msg) { this->handle_real(msg); });
+ message_port_register_in(pmt::mp("set_imag"));
+ set_msg_handler(pmt::mp("set_imag"),
+ [this](pmt::pmt_t msg) { this->handle_imag(msg); });
+}
+
+/*
+ * Our virtual destructor.
+ */
+correctiq_man_impl::~correctiq_man_impl()
+{
+ if (d_volk_const_buffer)
+ volk_free(d_volk_const_buffer);
+}
+
+void correctiq_man_impl::set_const_buffer(int new_size)
+{
+ d_buffer_size = new_size;
+
+ if (d_volk_const_buffer) {
+ volk_free(d_volk_const_buffer);
+ }
+
+ d_volk_const_buffer = reinterpret_cast<gr_complex*>(
+ volk_malloc(sizeof(gr_complex) * d_buffer_size, volk_get_alignment()));
+
+ fill_const_buffer();
+}
+
+void correctiq_man_impl::fill_const_buffer()
+{
+ gr_complex* tmp_ptr = d_volk_const_buffer;
+
+ for (int i = 0; i < d_buffer_size; i++) {
+ *tmp_ptr++ = d_k;
+ }
+}
+
+float correctiq_man_impl::get_real() { return d_avg_real; }
+float correctiq_man_impl::get_imag() { return d_avg_img; }
+
+void correctiq_man_impl::set_real(float new_value)
+{
+ gr::thread::scoped_lock guard(d_setlock);
+
+ d_avg_real = new_value;
+ d_k = gr_complex(d_avg_real, d_avg_img);
+ fill_const_buffer();
+}
+
+void correctiq_man_impl::set_imag(float new_value)
+{
+ gr::thread::scoped_lock guard(d_setlock);
+
+ d_avg_img = new_value;
+ d_k = gr_complex(d_avg_real, d_avg_img);
+ fill_const_buffer();
+}
+
+void correctiq_man_impl::handle_real(pmt::pmt_t msg)
+{
+ if (pmt::is_pair(msg)) {
+ pmt::pmt_t data = pmt::cdr(msg);
+ if (pmt::is_real(data)) {
+ float new_value = pmt::to_float(data);
+
+ set_real(new_value);
+ } else {
+ GR_LOG_WARN(d_logger, "Non-float real value received. Ignoring.");
+ }
+ } else {
+ if (pmt::is_real(msg)) {
+ float new_value = pmt::to_float(msg);
+
+ set_real(new_value);
+ } else {
+ GR_LOG_WARN(d_logger, "Non-float real value received. Ignoring.");
+ }
+ }
+}
+
+void correctiq_man_impl::handle_imag(pmt::pmt_t msg)
+{
+ if (pmt::is_pair(msg)) {
+ pmt::pmt_t data = pmt::cdr(msg);
+ if (pmt::is_real(data)) {
+ float new_value = pmt::to_float(data);
+
+ set_imag(new_value);
+ } else {
+ GR_LOG_WARN(d_logger, "Non-float imag value received. Ignoring.");
+ }
+ } else {
+ if (pmt::is_real(msg)) {
+ float new_value = pmt::to_float(msg);
+
+ set_imag(new_value);
+ } else {
+ GR_LOG_WARN(d_logger, "Non-float imag value received. Ignoring.");
+ }
+ }
+}
+
+int correctiq_man_impl::work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items)
+{
+ gr::thread::scoped_lock guard(d_setlock);
+
+ if (noutput_items > d_buffer_size)
+ set_const_buffer(noutput_items);
+
+ // Inputs are complex but we're casting as floats to leverage volk
+ const float* in = (const float*)input_items[0];
+ float* out = (float*)output_items[0];
+
+ volk_32f_x2_add_32f(
+ out, in, reinterpret_cast<float*>(d_volk_const_buffer), 2 * noutput_items);
+
+ // Tell runtime system how many output items we produced.
+ return noutput_items;
+}
+
+} /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/lib/correctiq_man_impl.h b/gr-blocks/lib/correctiq_man_impl.h
new file mode 100644
index 0000000000..edaaf2ff0e
--- /dev/null
+++ b/gr-blocks/lib/correctiq_man_impl.h
@@ -0,0 +1,54 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_CORRECTIQ_MAN_IMPL_H
+#define INCLUDED_CORRECTIQ_CORRECTIQ_MAN_IMPL_H
+
+#include <gnuradio/blocks/correctiq_man.h>
+
+namespace gr {
+namespace blocks {
+
+class correctiq_man_impl : public correctiq_man
+{
+private:
+ float d_avg_real;
+ float d_avg_img;
+
+ gr_complex d_k;
+
+ int d_buffer_size;
+ gr_complex* d_volk_const_buffer;
+
+ void set_const_buffer(int new_size);
+ void fill_const_buffer();
+
+public:
+ correctiq_man_impl(float real, float imag);
+ ~correctiq_man_impl();
+
+ virtual float get_real();
+ virtual float get_imag();
+
+ virtual void set_real(float new_value);
+ virtual void set_imag(float new_value);
+
+ void handle_real(pmt::pmt_t msg);
+ void handle_imag(pmt::pmt_t msg);
+
+ int work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_CORRECTIQ_MAN_IMPL_H */
diff --git a/gr-blocks/lib/correctiq_swapiq_impl.cc b/gr-blocks/lib/correctiq_swapiq_impl.cc
new file mode 100644
index 0000000000..dac0fc10fe
--- /dev/null
+++ b/gr-blocks/lib/correctiq_swapiq_impl.cc
@@ -0,0 +1,96 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "correctiq_swapiq_impl.h"
+#include <gnuradio/io_signature.h>
+
+namespace gr {
+namespace blocks {
+
+swap_iq::sptr swap_iq::make(int datatype, int datasize)
+{
+ return gnuradio::get_initial_sptr(new swap_iq_impl(datatype, datasize));
+}
+
+/*
+ * The private constructor
+ */
+swap_iq_impl::swap_iq_impl(int datatype, int datasize)
+ : gr::sync_block("swap_iq",
+ gr::io_signature::make(1, 1, datasize),
+ gr::io_signature::make(1, 1, datasize))
+{
+ d_datatype = datatype;
+
+ if (d_datatype != SWAPTYPE_FLOATCOMPLEX) {
+ gr::block::set_output_multiple(2); // Make sure we work with pairs
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+swap_iq_impl::~swap_iq_impl() {}
+
+int swap_iq_impl::work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items)
+{
+ // Constructor guarantees we'll have pairs.
+
+ long i;
+ long noi;
+
+ switch (d_datatype) {
+ case SWAPTYPE_FLOATCOMPLEX: {
+ noi = noutput_items * 2; // Each complex is 2 floats
+ const float* in_float = (const float*)input_items[0];
+ float* out_float = (float*)output_items[0];
+
+ for (i = 0; i < noi; i += 2) {
+ *out_float++ = in_float[i + 1];
+ *out_float++ = in_float[i];
+ }
+ } break;
+ case SWAPTYPE_SHORTCOMPLEX: {
+ noi = noutput_items;
+ const int16_t* in_short = (const int16_t*)input_items[0];
+ int16_t* out_short = (int16_t*)output_items[0];
+
+ for (i = 0; i < noi; i += 2) {
+ *out_short++ = in_short[i + 1];
+ *out_short++ = in_short[i];
+ }
+ } break;
+ case SWAPTYPE_BYTECOMPLEX: {
+ noi = noutput_items;
+ const char* in_byte = (const char*)input_items[0];
+ char* out_byte = (char*)output_items[0];
+
+ for (i = 0; i < noi; i += 2) {
+ *out_byte++ = in_byte[i + 1];
+ *out_byte++ = in_byte[i];
+ }
+ } break;
+ default: {
+ GR_LOG_ERROR(d_logger, "Unknown data type. Bytes not swapped.");
+ return WORK_DONE;
+ }
+ }
+
+ // Tell runtime system how many output items we produced.
+ return noutput_items;
+}
+} /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/lib/correctiq_swapiq_impl.h b/gr-blocks/lib/correctiq_swapiq_impl.h
new file mode 100644
index 0000000000..5995caa884
--- /dev/null
+++ b/gr-blocks/lib/correctiq_swapiq_impl.h
@@ -0,0 +1,41 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_CORRECTIQ_SWAPIQ_IMPL_H
+#define INCLUDED_CORRECTIQ_SWAPIQ_IMPL_H
+
+#include <gnuradio/blocks/correctiq_swapiq.h>
+
+#define SWAPTYPE_FLOATCOMPLEX 1
+#define SWAPTYPE_SHORTCOMPLEX 2
+#define SWAPTYPE_BYTECOMPLEX 3
+
+namespace gr {
+namespace blocks {
+
+class swap_iq_impl : public swap_iq
+{
+private:
+ int d_datatype;
+
+public:
+ swap_iq_impl(int datatype, int datasize);
+ ~swap_iq_impl();
+
+ // Where all the action really happens
+ int work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items);
+};
+
+} // namespace blocks
+} // namespace gr
+
+#endif /* INCLUDED_CORRECTIQ_SWAPIQ_IMPL_H */
diff --git a/gr-blocks/swig/blocks_swig0.i b/gr-blocks/swig/blocks_swig0.i
index 76d7057f35..47a15f6e8f 100644
--- a/gr-blocks/swig/blocks_swig0.i
+++ b/gr-blocks/swig/blocks_swig0.i
@@ -19,6 +19,10 @@
#include "gnuradio/blocks/annotator_1to1.h"
#include "gnuradio/blocks/annotator_alltoall.h"
#include "gnuradio/blocks/annotator_raw.h"
+#include "gnuradio/blocks/correctiq.h"
+#include "gnuradio/blocks/correctiq_man.h"
+#include "gnuradio/blocks/correctiq_auto.h"
+#include "gnuradio/blocks/correctiq_swapiq.h"
#include "gnuradio/blocks/control_loop.h"
#include "gnuradio/blocks/copy.h"
#include "gnuradio/blocks/delay.h"
@@ -42,6 +46,10 @@
%include "gnuradio/blocks/annotator_1to1.h"
%include "gnuradio/blocks/annotator_alltoall.h"
%include "gnuradio/blocks/annotator_raw.h"
+%include "gnuradio/blocks/correctiq.h"
+%include "gnuradio/blocks/correctiq_man.h"
+%include "gnuradio/blocks/correctiq_auto.h"
+%include "gnuradio/blocks/correctiq_swapiq.h"
%include "gnuradio/blocks/control_loop.h"
%include "gnuradio/blocks/copy.h"
%include "gnuradio/blocks/delay.h"
@@ -64,6 +72,10 @@
GR_SWIG_BLOCK_MAGIC2(blocks, annotator_1to1);
GR_SWIG_BLOCK_MAGIC2(blocks, annotator_alltoall);
GR_SWIG_BLOCK_MAGIC2(blocks, annotator_raw);
+GR_SWIG_BLOCK_MAGIC2(blocks, correctiq);
+GR_SWIG_BLOCK_MAGIC2(blocks, correctiq_man);
+GR_SWIG_BLOCK_MAGIC2(blocks, correctiq_auto);
+GR_SWIG_BLOCK_MAGIC2(blocks, swap_iq);
GR_SWIG_BLOCK_MAGIC2(blocks, copy);
GR_SWIG_BLOCK_MAGIC2(blocks, delay);
GR_SWIG_BLOCK_MAGIC2(blocks, endian_swap);