I am trying to make a signal processing block which implements the costas loop to stabilize an 8-PSK signal. Here is the flowgraph with "costas_loop" being my block. enter image description here

My workflow is the following -

in0 = input_items[0]
out = output_items[0]
# <+signal processing here+>
feedback = np.ones(in0.shape, dtype=np.complex64)
on_first_mul = np.ones(in0.shape, dtype=np.complex64)
op_thresh_imag = np.ones(in0.shape, dtype=np.float64)
op_thresh_real = np.ones(in0.shape, dtype=np.float64)
a = np.array([op_thresh_imag])
b = np.array([op_thresh_real])
in_iir = np.zeros(in0.shape, dtype=np.float64)
out_iir = np.ones(in0.shape, dtype=np.float64)
out_vco = np.ones(in0.shape, dtype=np.complex64)
in0 = in0/math.sqrt(2)

for i in xrange(0,self.iter):

  # Multiply input signal with feedback of that iteration
  on_first_mul = in0*feedback
  # Phase error detector
  real = on_first_mul.real
  imag = on_first_mul.imag
  a = self.threshold(imag)
  b = self.threshold(real)
  in_iir = np.arcsin(imag*b - real*a)

  # IIR filter implementing (1.0001 - z)/(1 - z) (old style)
  # self.prev_input[i] contains last input value from previous in0 chunk
  in_iir_delay = np.concatenate([[self.prev_input[i]],in_iir[0:-1]])
  # Update this for next in0 chunk
  self.prev_input[i] = in_iir[-1]
  out_temp = in_iir*1.0001 - in_iir_delay
  # Workaround for adding y[n-1]
  out_temp[0] += self.prev_output[i]
  out_iir = np.cumsum(out_temp)
  self.prev_output[i] = out_iir[-1]

  # VCO implementation 1/(1 - z) (old style)
  out_iir[0] += self.prev_phase[i]
  in_vco = np.cumsum(out_iir)
  self.prev_phase[i] = in_vco[-1]
  real_part = np.cos(self.k_factor*in_vco)
  imag_part = np.sin(self.k_factor*in_vco)
  out_vco = real_part + 1j*imag_part
  feedback = out_vco

Also, here is the rest of the code -

def __init__(self, samp_rate, iter):
  self.samp_rate = samp_rate;
  self.iter = iter;
  self.call = 0

  self.prev_input = np.zeros(self.iter, dtype=np.float64)
  self.prev_output = np.zeros(self.iter, dtype=np.float64)
  self.prev_phase = np.zeros(self.iter, dtype=np.float64)

  self.k_factor = -5/samp_rate

def threshold(self, in0):
    output = 0
    if in0 <= math.cos(7*math.pi/8):
      output = -1
    elif in0 > math.cos(7*math.pi/8) and in0 <= math.cos(5*math.pi/8):
      output = -1/(2**0.5)
    elif in0 > math.cos(5*math.pi/8) and in0 <= math.cos(3*math.pi/8):
      output = 0
    elif in0 > math.cos(3*math.pi/8) and in0 <= math.cos(math.pi/8):
      output = 1/(2**0.5)
    elif in0 > math.cos(math.pi/8):
      output = 1
    return output

This code unfortunately "almost" works. I get a stabilized 8-PSK output in the beginning,

enter image description here

Unfortunately, after about 160 calls to the costas_loop's work(), the 8-PSK output starts to move and destabilizes. What could be going wrong?

  • $\begingroup$ I'll be happy to provide more information, but unfortunately I don't know what information will be crucial here $\endgroup$ – martianwars Nov 2 '16 at 15:56
  • $\begingroup$ It looks like you're using a root raised cosine matched filter. Are you using a root raised cosine pulse shape to transmit? (It isn't clear in the figure). $\endgroup$ – MBaz Nov 2 '16 at 16:23
  • $\begingroup$ Yes, it's a part of the Polyphase Arbitrary Resampler $\endgroup$ – martianwars Nov 2 '16 at 16:28
  • 1
    $\begingroup$ Can you double-check the frequency of the sine carrier? I don't have gnuradio-companion access right now but I seem to recall that 'm' stands for 1e-3 and 'M' for 1e6. Also, why are you multiplying the signal by this carrier? $\endgroup$ – MBaz Nov 2 '16 at 17:06
  • $\begingroup$ It's supposed to represent "milli". I'm doing that to begin rotating the constellation at a frequency of 0.1 Hz. The goal of the costas loop is to stabilise the constellation output. $\endgroup$ – martianwars Nov 2 '16 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.