Offset QPSK detection in GNU Radio - Sample delay An OQPSK detector is being tested in GNU Radio. The architecture was obtained from Michael Rice's Digital Communications - A discrete-time approach. The flowgraph is shown below.

The modulator architecture is basically QPSK delayed by half the sample rate, as shown below. In the flowgraph, the block oqpskIQMap maps the I/Q symbols from QPSK to OQPSK. It does so by delaying the Q-channel by d_delay, which is equal to the number of samples per symbol divided by two. 

oqpskIQMap_impl::oqpskIQMap_impl(int sps)
      : gr::sync_block("oqpskIQMap",
              gr::io_signature::make(1, 1, sizeof(gr_complex)),
              gr::io_signature::make(1, 1, sizeof(gr_complex))),
        d_delay(sps/2)
    {
        //set_history(d_delay);
    }

    /*
     * Our virtual destructor.
     */
    oqpskIQMap_impl::~oqpskIQMap_impl()
    {
    }

    int
    oqpskIQMap_impl::work(int noutput_items,
        gr_vector_const_void_star &amp;input_items,
        gr_vector_void_star &amp;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 &lt; noutput_items; i++)
          out[i] = gr_complex(real(in[i]),imag(in[i - d_delay]));

      // Do &lt;+signal processing+&gt;

      // Tell runtime system how many output items we produced.
      return noutput_items;
    }




The receiver architecture from Rice's book is shown below. In this architecture, the match-filter produces 2 samples per symbol. The oqpskIQDemap block processes the output samples (X(KTs), X(KTs + Ts/2), Y(KTs) and Y(KTs + Ts/2) into a constellation point (X(KTs) ,Y(KTs + Ts/2)), effectively reducing the sample rate to 1 sample per symbol.

oqpskIQDemap_impl::oqpskIQDemap_impl(int delay)
      : gr::sync_decimator("oqpskIQDemap",
              gr::io_signature::make(1, 1, sizeof(gr_complex)),
              gr::io_signature::make(1, 1, sizeof(gr_complex)), 2),
        d_delay(delay)
    {}

    /*
     * Our virtual destructor.
     */
    oqpskIQDemap_impl::~oqpskIQDemap_impl()
    {
    }

    int
    oqpskIQDemap_impl::work(int noutput_items,
        gr_vector_const_void_star &amp;input_items,
        gr_vector_void_star &amp;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 &lt; noutput_items; i+=2)
          out[i] = gr_complex(real(in[i]),imag(in[i + 1]));

      // Tell runtime system how many output items we produced.
      return noutput_items;
    } 




The transmitted constellation (top) looks pretty okay. The Rx constellation (bottom), on the other hand, seems to have some points crossing the boundaries unexpectedly, given that the SNR is 25 dBs. I suspect the problem is with the way I introduce the delay in the two IQ map/demap blocks. 

Please do let me know what you think. 

Regards,
Thanks to Marcus' answer, I was able to resolve the problem. See below for the updated code:

IQ Mapping

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include &lt;gnuradio/io_signature.h&gt;
#include "oqpskIQMap_impl.h"

namespace gr {
  namespace oqpsk {

    oqpskIQMap::sptr
    oqpskIQMap::make(int sps)
    {
      return gnuradio::get_initial_sptr
        (new oqpskIQMap_impl(sps));
    }

    /*
     * The private constructor
     */
    oqpskIQMap_impl::oqpskIQMap_impl(int sps)
      : gr::sync_block("oqpskIQMap",
              gr::io_signature::make(1, 1, sizeof(gr_complex)),
              gr::io_signature::make(1, 1, sizeof(gr_complex))),
        d_delay(sps/2)
    {
        //set_history(1);
    }

    /*
     * Our virtual destructor.
     */
    oqpskIQMap_impl::~oqpskIQMap_impl()
    {
    }

    int
    oqpskIQMap_impl::work(int noutput_items,
        gr_vector_const_void_star &amp;input_items,
        gr_vector_void_star &amp;output_items)
    {
      const gr_complex *in = (const gr_complex *) input_items[0];
      gr_complex *out = (gr_complex *) output_items[0];

      for(int i = d_delay; i &lt; noutput_items; i++)
          out[i] = gr_complex(real(in[i]),imag(in[i - d_delay]));


      // Do &lt;+signal processing+&gt;

      // Tell runtime system how many output items we produced.
      return noutput_items - d_delay;
    }

  } /* namespace oqpsk */
} /* namespace gr */


IQ Demapping
Please note that this is a downsampling block and which expects 2 samples per symbol and produces 1 sample per symbol (a single constellation point). 
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif

#include &lt;gnuradio/io_signature.h&gt;
#include "oqpskIQDemap_impl.h"

namespace gr {
  namespace oqpsk {

    oqpskIQDemap::sptr
    oqpskIQDemap::make(int delay)
    {
      return gnuradio::get_initial_sptr
        (new oqpskIQDemap_impl(delay));
    }

    /*
     * The private constructor
     */
    oqpskIQDemap_impl::oqpskIQDemap_impl(int delay)
      : gr::sync_decimator("oqpskIQDemap",
              gr::io_signature::make(1, 1, sizeof(gr_complex)),
              gr::io_signature::make(1, 1, sizeof(gr_complex)), 2),
        d_delay(delay)
    {
        set_output_multiple(2);
    }

    /*
     * Our virtual destructor.
     */
    oqpskIQDemap_impl::~oqpskIQDemap_impl()
    {
    }

    int
    oqpskIQDemap_impl::work(int noutput_items,
        gr_vector_const_void_star &amp;input_items,
        gr_vector_void_star &amp;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 &lt; noutput_items; i+=2)
          //out[i] = gr_complex(real(in[i]),imag(in[i + 1]));
      out[0] = gr_complex(real(in[0]),imag(in[1]));

      // Tell runtime system how many output items we produced.
      return 1;
    }

  } /* namespace oqpsk */
} /* namespace gr */


Regards,