# 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 &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++)
out[i] = gr_complex(real(in[i]),imag(in[i - d_delay]));

// Do <+signal processing+>

// 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 &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+=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,

• looks to me that you have top and bottom constellations mixed up. ain't the top constellation your Rx and the bottom your Tx constellation? – robert bristow-johnson May 7 '19 at 19:08
• in your for-loop, for i=0, you're getting the -1. input item – that's not legal! In your previous call to work you already returned noutput_items, so you told GNU Radio that you don't need any of the input items you've gotten again. So, the upstream block might have already overwritten "-1th" input item with new output data. – Marcus Müller May 8 '19 at 5:38
• @robertbristow-johnson The plots are accurate. – Moses Browne Mwakyanjala May 8 '19 at 10:51
• @MarcusMüller That's absolutely correct. But, since the delay used is 2, then there will be -2-th and -1th, which as you said, are illegal values. I have tried a number of initial values for i including for i = 1,2, etc without any luck. I was wondering how which index value will be appropriate to start with. – Moses Browne Mwakyanjala May 8 '19 at 10:54
• you could simply not consume all input! i.e. instead of return noutput_items, you return noutput_items - 1, so that you produced one less, but also didn't consume the last input item. – Marcus Müller May 8 '19 at 11:57

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 "oqpskIQMap_impl.h"

namespace gr {
namespace oqpsk {

oqpskIQMap::sptr
oqpskIQMap::make(int sps)
{
(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 &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 = d_delay; i < noutput_items; i++)
out[i] = gr_complex(real(in[i]),imag(in[i - d_delay]));

// Do <+signal processing+>

// 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 <gnuradio/io_signature.h>
#include "oqpskIQDemap_impl.h"

namespace gr {
namespace oqpsk {

oqpskIQDemap::sptr
oqpskIQDemap::make(int delay)
{
(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 &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+=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,