1

I'm currently implementing symbol time recovery blocks. The idea is to be able to choose different TEDs (Gardner, Zero-crossing, Early-Late, Maximum-likelihood etc). In blocks like M&M recovery, the gain parameters of the loop are expressed explicitly (gain_omega and gain_mu) which can be difficult to get right. The contro_loop class is, however, more convenient (loop characteristics can be specified by "loop bandwidth" and "damping factor"(zeta)). So my first test started with the re-implementation of the MM Clock Recovery with a control loop. The work function of this block is shown below (Comments are mine)

clock_recovery_mm_ff_impl::general_work(int noutput_items,
                    gr_vector_int &ninput_items,
                    gr_vector_const_void_star &input_items,
                    gr_vector_void_star &output_items)
{
  const float *in = (const float *)input_items[0];
  float *out = (float *)output_items[0];

  int ii = 0; // input index
  int oo = 0; // output index
  int ni = ninput_items[0] - d_interp->ntaps(); // don't use more input than this
  float mm_val;

  while(oo < noutput_items && ii < ni ) {
// produce output sample
out[oo] = d_interp->interpolate(&in[ii], d_mu); //Interpolation
mm_val = slice(d_last_sample) * out[oo] - slice(out[oo]) * d_last_sample; // Error calculation
d_last_sample = out[oo];
//Loop filtering
d_omega = d_omega + d_gain_omega * mm_val; //Frequency
d_omega = d_omega_mid + gr::branchless_clip(d_omega-d_omega_mid, d_omega_lim); //Bound the frequency
d_mu = d_mu + d_omega + d_gain_mu * mm_val; //Phase

ii += (int)floor(d_mu); // Basepoint index
d_mu = d_mu - floor(d_mu); // Fractional interval
oo++;
  }

  consume_each(ii);
  return oo;
}

Here is my code. First, the control loop is initialized the constructor

loop(new gr::blocks::control_loop(0.02,(1 + d_omega_relative_limit)*omega,
(1 - d_omega_relative_limit)*omega))

First of all I would like to eliminate a couple of doubts that I have regarding pll (the control_loop above) in symbol timing recovery particularly phase and frequency ranges (that are in turn used for wrapping). Taking an analogy from Costas loop : carrier phase is wrapped between -2pi and +2pi and the frequency offset is tracked between -1 and +1. It is quite straightforward to see why. Unfortunately I can't get my head around phase and frequency tracking in symbol recovery. From the m&m block, frequency is tracked between (1+omega_relative_limit) and (1 - omega_relative_limit)*omega where omega is simply the number of samples per symbol. Phase is tracked between 0 and omega. I dont understand why this is so and why the m&m block doesn't wrap it. Any ideas here will be appreciated. And here is my work function

debug_time_recovery_pam_test_1_impl::general_work (int noutput_items,
                           gr_vector_int &ninput_items,
                           gr_vector_const_void_star &input_items,
                           gr_vector_void_star &output_items)
{
  // Tell runtime system how many output items we produced.
  const float *in = (const float *)input_items[0];
  float *out = (float *)output_items[0];

  int ii = 0; // input index
  int oo = 0; // output index
  int ni = ninput_items[0] - d_interp->ntaps(); // don't use more input than this
  float mm_val;

  while(oo < noutput_items && ii < ni ) {
// produce output sample
out[oo] = d_interp->interpolate(&in[ii], d_mu);

//Calculating error
mm_val = slice(d_last_sample) * out[oo] - slice(out[oo]) * d_last_sample;
d_last_sample = out[oo];

//Loop filtering
loop->advance_loop(mm_val); // Filter the error
loop->frequency_limit();    //Stop frequency from wandering too far

//Loop phase and frequency
d_omega = loop->get_frequency();
d_mu    = loop->get_phase();


//d_omega = d_omega + d_gain_omega * mm_val;
//d_omega = d_omega_mid + gr::branchless_clip(d_omega-d_omega_mid, d_omega_lim);
//d_mu = d_mu + d_omega + d_gain_mu * mm_val;

ii += (int)floor(d_mu);   // Basepoint index
d_mu = d_mu - floor(d_mu);//Fractional interval
oo++;
  }

  consume_each(ii);
  return oo;
}

I have tried to use the block in a GFSK demodulator and I got this error

python: /build/gnuradio-bJXzXK/gnuradio-3.7.9.1/gnuradio-runtime/include/gnuradio/buffer.h:177: unsigned int gr::buffer::index_add(unsigned int, unsigned int): Assertion `s < d_bufsize' failed.

The first google search regarding this error suggests that im somehow "abusing" the scheduler since this error comes somewhere below the API. I think my calculation of d_omega and d_mu from the control loop is a bit naive but unfortunately I don't know any other way of doing so. Another alternative will be to use a modulo-1 counter (incrementing or decrementing) but I want to explore this option first.

  • Hm, we won't get far here if you don't use a debugger. It should be fairly easy for you to figure out whether you trigger this behaviour directly in your block or whether it's emergent behaviour. – Marcus Müller Nov 22 '17 at 20:51
  • @MarcusMüller I will try a debugger and see how things go. But before I do so I would like to sort out a few issues regarding pll in symbol timing recovery particularly phase and frequency ranges (that are in turn used for wrapping). Taking an analogy from Costas loop : phase is carrier phase is wrapped between -2pi and +2pi and the frequency offset is tracked between -1 and +1. It is quite straightforward to see why . – Moses Browne Mwakyanjala Nov 24 '17 at 00:02
  • Unfortunately I can't get my head around phase and frequency tracking in symbol recovery. From the m&m block, frequency is tracked between (1+omega_relative_limit) and (1 - omega_relative_limit)*omega where omega is simply the number of samples per symbol. Phase is tracked between 0 and omega but the m&m block doesn't wrap it. Could you shed some light? – Moses Browne Mwakyanjala Nov 24 '17 at 00:04
  • I'm sorry, but I think we should separate the software problem from the control problem. Not answering new questions in the comments! – Marcus Müller Nov 24 '17 at 08:46
  • @MarcusMüller fair enough. But actually it's not a new question. It is implicitly part and parcel of the whole post. I was hoping someone could have picked up that this is the essence of the whole post. I guess I will have to edit the whole post and make it more explicit. – Moses Browne Mwakyanjala Nov 24 '17 at 10:41

0 Answers0