3
$\begingroup$

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. flowgraph 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;
    }

tx

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;
    } 

rx

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,

constellation

$\endgroup$
8
  • $\begingroup$ 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? $\endgroup$ Commented May 7, 2019 at 19:08
  • 1
    $\begingroup$ 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. $\endgroup$ Commented May 8, 2019 at 5:38
  • $\begingroup$ @robertbristow-johnson The plots are accurate. $\endgroup$ Commented May 8, 2019 at 10:51
  • $\begingroup$ @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. $\endgroup$ Commented May 8, 2019 at 10:54
  • 1
    $\begingroup$ 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. $\endgroup$ Commented May 8, 2019 at 11:57

1 Answer 1

1
$\begingroup$

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 <gnuradio/io_signature.h>
#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 &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)
    {
      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 &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,

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.