c++gnuradiognuradio-companion

Manchester-L (Biphase-L) implementation with finite state machines in GNU Radio


I'm in the process of implementing line coding blocks as there are limited choices in GnuRadio. I have finished implementing and testing NRZ (L\M\S) using finite state machines (FSM) and they seem to work fine. Now, I'm trying to implement Manchester (L\M\S), starting with L as a baseline. The waveform can be seen in the diagram below Extracted from NASA's Deep Space Network Telemetry Data Decoding (208)

Modulation waveforms

A finite state machine was constructed as shown below. I have decided to use FSMs since it is, in my opinion, a very standardized way of implementing a line code. Manchester-L FSM

Below is the gnuradio flowgraph used to test the implementation. The tests are conducted by sending a BPSK signal (with CCSDS Reed-Solomon + Scrambler) to an external device capable of receiving the signal and processing the telemetry. This implementation has been tested successfully with NRZ-L\M\S. The CCSDS frames are read from a file, unpacked and sent to the OOT block debug_linecode_bp for Manchester encoding (Code 0 for Manchester-L). Following Manchester encoding is the OOT block debug_pulseshape_pam_2 block which takes filter taps and number of samples per symbol as arguments. Following that is an OOT block debug_bpsk_modulator which does simple BPSK mapping (Inphase = in[i], quadrature = 0). enter image description here The code for the header file is shown below

#ifndef INCLUDED_BASEBAND_DEBUG_LINECODE_BP_IMPL_H
#define INCLUDED_BASEBAND_DEBUG_LINECODE_BP_IMPL_H

#include <baseband/debug_linecode_bp.h>

namespace gr {
namespace baseband {

class debug_linecode_bp_impl : public debug_linecode_bp
{
 private:
  char last_state;
  int d_code;
  void fsm_decode_state(char state, unsigned char &bit0, unsigned char &bit1);
  void fsm_encode_state(int code, unsigned char input, char last_state, char &next_state);
 public:
  debug_linecode_bp_impl(int code);
  ~debug_linecode_bp_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 baseband
 } // namespace gr

#endif /* INCLUDED_BASEBAND_DEBUG_LINECODE_BP_IMPL_H */

And here is the implementation file

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

#include <gnuradio/io_signature.h>
#include "debug_linecode_bp_impl.h"
#include <iostream>
using namespace std;
namespace gr {
namespace baseband {

debug_linecode_bp::sptr
debug_linecode_bp::make(int code)
{
  return gnuradio::get_initial_sptr
    (new debug_linecode_bp_impl(code));
}

/*
 * The private constructor
 */
debug_linecode_bp_impl::debug_linecode_bp_impl(int code)
  : gr::sync_interpolator("debug_linecode_bp",
              gr::io_signature::make(1, 1, sizeof(unsigned char)),
              gr::io_signature::make(1, 1, sizeof(unsigned char)), 2),
d_code(code),last_state('a')
{}

/*
 * Our virtual destructor.
 */
debug_linecode_bp_impl::~debug_linecode_bp_impl()
{
}
void
debug_linecode_bp_impl::fsm_decode_state(char state, unsigned char &bit0, unsigned char &bit1)
{
  switch(state)
{
case 'a':
  bit0 = 0x00;
  bit1 = 0x00;
  break;
case 'b':
  bit0 = 0x00;
  bit1 = 0x01;
  break;
case 'c':
  bit0 = 0x01;
  bit1 = 0x01;
  break;
case 'd':
  bit0 = 0x01;
  bit1 = 0x00;
  break;
}
}

void
debug_linecode_bp_impl::fsm_encode_state(int code, unsigned char input, char last_state, char &next_state)
{
  switch(code)
{
case 0://Biphae-L
  switch(last_state)
    {
    case 'a': //Illegal state
      next_state = 'b';
      cout << "Illegal state [a] encountered" << endl;
      break;
    case 'b':
      next_state = (input & 0x01) ? 'd' : 'b';
      break;
    case 'c': //Illegal state
      next_state = 'b';
      cout << "Illegal state [b] encountered" << endl;
      break;
    case 'd':
      next_state = (input & 0x01) ? 'd' : 'b';
      break;
    }
  break;
case 1://Biphase-S
  switch(last_state)
    {
    case 'a':
      next_state = (input & 0x01) ? 'c' : 'd';
      break;
    case 'b':
      next_state = (input & 0x01) ? 'a' : 'b';
      break;
    case 'c': 
      next_state = (input & 0x01) ? 'a' : 'b';
      break;
    case 'd':
      next_state = (input & 0x01) ? 'c' : 'd';
      break;
    }
  break;
case 2://Biphase-M
  switch(last_state)
    {
    case 'a':
      next_state = (input & 0x01) ? 'd' : 'c';
      break;
    case 'b':
      next_state = (input & 0x01) ? 'b' : 'a';
      break;
    case 'c': 
      next_state = (input & 0x01) ? 'b' : 'a';
      break;
    case 'd':
      next_state = (input & 0x01) ? 'd' : 'c';
      break;
    }
  break;
}
}

int
debug_linecode_bp_impl::work(int noutput_items,
             gr_vector_const_void_star &input_items,
             gr_vector_void_star &output_items)
{
  const unsigned char *in = (const unsigned char *) input_items[0];
  unsigned char *out = (unsigned char *) output_items[0];

  char next_state;
  unsigned char bit0;
  unsigned char bit1;
  for (int i = 0; i < noutput_items/2; i++) {
fsm_encode_state(d_code,in[i],last_state, next_state);
fsm_decode_state(next_state, bit0, bit1);
for (int j = 0; j < 2; j++) {
  out[i + j]     = bit0;
  out[i + j + 1] = bit1;
}
last_state = next_state;
  }

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

} /* namespace baseband */
} /* namespace gr */

The tests haven't been a success so far. The device I use to receive signals from this flowgraph hasnt been able to pick even a single packet. My conclusion is that the error is coming from the Manchester encoder. Any thoughts on the code above is highly welcomed.

Thanks.


Solution

  • After some time, I have been able to find the bug in the code. Actually, the problem was with the way I was copying my output. All I had to do was removing the inner for loop and copy the output directly using memcpy. This is how the work function looks like now

     int
    debug_linecode_bp_impl::work(int noutput_items,
                 gr_vector_const_void_star &input_items,
                 gr_vector_void_star &output_items)
    {
      const unsigned char *in = (const unsigned char *) input_items[0];
      unsigned char *out = (unsigned char *) output_items[0];
    
      char next_state;
      unsigned char bit0;
      unsigned char bit1;
      vector<unsigned char> bits;
      for (int i = 0; i < noutput_items/2; i++) {
          fsm_encode_state(d_code,in[i],last_state, next_state);
          fsm_decode_state(next_state, bit0, bit1);
          bits.push_back(bit0);
          bits.push_back(bit1);
          memcpy(out,bits.data(),2);
          bits.clear();
          out+=2;
          last_state = next_state;
      }
    
      // Tell runtime system how many output items we produced.
      return noutput_items;
    }
    

    And this is the waveform the at the modulator output Manchester Pulse