GNU Radio Notes

From SpenchWiki
Jump to: navigation, search

My blocks

Contained in the gr-baz module.

My changes

gr-wxgui/constants.py:

SYMBOL_RATE_KEY = 'symbol_rate'

grc/blocks/wxgui_constellationsink2.xml:

<callback>set_symbol_rate($symbol_rate)</callback>

gr-wxgui/src/python/constsink_gl.py:

self.controller.subscribe(SYMBOL_RATE_KEY, lambda x: setter(self.controller, OMEGA_KEY, float(self.controller[SAMPLE_RATE_KEY])/float(x)))
self.controller.publish(SYMBOL_RATE_KEY, lambda: self.controller[SAMPLE_RATE_KEY] * self.controller[OMEGA])

Window creation:

symbol_rate_key=SYMBOL_RATE_KEY,

gr-wxgui/src/python/const_window.py:

In arguments:

symbol_rate_key,
#proxy the keys
self.proxy(SYMBOL_RATE_KEY, controller, symbol_rate_key)

gnuradio-core/src/python/gnuradio/blks2impl/cpm.py:

import Numeric

Line 123:

#self.sym_alphabet=numpy.arange(-(self.nsymbols-1),self.nsymbols,2)
self.sym_alphabet=Numeric.arrayrange(-(self.nsymbols-1),self.nsymbols,2)

Absent from 3.2 Doxygen class hierarchy

  • gr_fmdet_cf: Implements an IQ slope detector. This implements a limiting slope detector. The limiter is in the normalization by the magnitude of the sample.
  • gr_encode_ccsds_27_bb & gr_decode_ccsds_27_bb: A rate 1/2, k=7 convolutional encoder for the CCSDS standard. This block performs convolutional encoding using the CCSDS standard polynomial ("Voyager").
  • Polyphase arbitrary resampler, channelizer, clock sync (c & f), decimator, interpolator

gr_fft_vcc

gr_fft_vcc_fftw.cc: shift parameter swaps two halves of frequency-domain data. Enabled by default, so forward transform ends up having DC in centre of array (like in FFT Sink), which would otherwise be in the first element, and first negative would be in the last element.

gr_diff_phasor_cc

gr_diff_phasor_cc.cc:

 out[i] = in[i] * conj(in[i-1]);

gr_quadrature_demod_cf

gr_quadrature_demod_cf.cc:

 gr_complex product = in[i] * conj (in[i-1]);
 out[i] = d_gain * gr_fast_atan2f(imag(product), real(product));

Random Source

  • When Byte, full range (min/max) is 0-255.

GLFSR Source

  • Outputs unpacked bytes (1 bit per byte), so need to use Unpacked to Packed to re-assemble bytes.
  • Degree defines length of sequence before repeating as 2^degree-1. Also selects pre-defined polynomial bit-mask of same degree if Mask is 0.
  • Mask is XOR'd with shift register after each time LSBit that is shifted off is 1 (if 0, selects pre-defined polynomal based of Degree).
  • Seed sets initial state of shift register.
 0x00000000,
 0x00000001,			// x^1 + 1
 0x00000003,                    // x^2 + x^1 + 1
 0x00000005,                    // x^3 + x^1 + 1
 0x00000009,			// x^4 + x^1 + 1
 0x00000012,			// x^5 + x^2 + 1
 0x00000021,			// x^6 + x^1 + 1
 0x00000041,                    // x^7 + x^1 + 1
 0x0000008E,			// x^8 + x^4 + x^3 + x^2 + 1
 0x00000108,			// x^9 + x^4 + 1
 0x00000204,			// x^10 + x^4 + 1
 0x00000402,			// x^11 + x^2 + 1
 0x00000829,			// x^12 + x^6 + x^4 + x^1 + 1
 0x0000100D,			// x^13 + x^4 + x^3 + x^1 + 1
 0x00002015,			// x^14 + x^5 + x^3 + x^1 + 1
 0x00004001,			// x^15 + x^1 + 1
 0x00008016,			// x^16 + x^5 + x^3 + x^2 + 1
 0x00010004,			// x^17 + x^3 + 1
 0x00020013,			// x^18 + x^5 + x^2 + x^1 + 1
 0x00040013,			// x^19 + x^5 + x^2 + x^1 + 1
 0x00080004,			// x^20 + x^3 + 1
 0x00100002,			// x^21 + x^2 + 1
 0x00200001,			// x^22 + x^1 + 1
 0x00400010,			// x^23 + x^5 + 1
 0x0080000D,			// x^24 + x^4 + x^3 + x^1 + 1
 0x01000004,			// x^25 + x^3 + 1
 0x02000023,			// x^26 + x^6 + x^2 + x^1 + 1
 0x04000013,			// x^27 + x^5 + x^2 + x^1 + 1
 0x08000004,			// x^28 + x^3 + 1
 0x10000002,			// x^29 + x^2 + 1
 0x20000029,			// x^30 + x^4 + x^1 + 1
 0x40000004,			// x^31 + x^3 + 1
 0x80000057			// x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + 1

8psk_mod

8psk.py: Last block is

r.unpack_k_bits_bb(self.bits_per_symbol())

so each output symbol is unpacked into 1-bit per byte. Re-assembly with baz.unpacked_to_packed_bb(1, <bits per symbol>)

gr_cpfsk_bc

Expects 1-bit per byte input stream.

d_freq = k*M_PI/samples_per_sym;

for (int i = 0; i < noutput_items/d_samples_per_sym; i++) {
    for (int j = 0; j < d_samples_per_sym; j++) {
      if (in[i] == 1)
	d_phase += d_freq;
      else
	d_phase -= d_freq;
      
      while (d_phase > M_TWOPI)
	d_phase -= M_TWOPI;
      while (d_phase < -M_TWOPI)
	d_phase += M_TWOPI;
      
      *out++ = gr_expj(d_phase)*d_ampl;
    }
  }

gr_iqcomp_cc

What is this supposed to do?! No output!

  const gr_complex *iptr = (gr_complex *) input_items[0];
  // gr_complex *optr = (gr_complex *) output_items[0];

  for(int i = 0 ; i < noutput_items ; i++) {
    float i_out = iptr[i].real() - iptr[i].imag() * d_wq;
    float q_out = iptr[i].imag() - iptr[i].real() * d_wi;
    d_wi += d_mu * q_out * iptr[i].real();
    d_wq += d_mu * i_out * iptr[i].imag();
  }

gr_nop

Consumes input, doesn't produce any output.

gr_pa_2x2_phase_combiner

Antenna arrangement:

2  3
0  1

What does this mean: dx and dy are lambda/2

  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++){
    gr_complex	acc = 0;
    acc += in[0] * d_phase[0];
    acc += in[1] * d_phase[1];
    acc += in[2] * d_phase[2];
    acc += in[3] * d_phase[3];
    out[i] = acc;
    in += 4;
  }

gr_threshold_ff

  • Initial state & output is 1 or 0
  • hi is evaluated first, then lo. For binary threshold, make lo greater than hi.
    if (in[i] > d_hi) {
      out[i] = 1.0;
      d_last_state = 1.0;
    } else if (in[i] < d_lo) {
      out[i] = 0.0;
      d_last_state = 0.0;
    } else
      out[i] = d_last_state;

gr_channel_model

  d_timing_offset = gr_make_fractional_interpolator_cc(0, epsilon);
  
  d_multipath = gr_make_fir_filter_ccc(1, d_taps);
  
  d_noise_adder = gr_make_add_cc();
  d_noise = gr_make_noise_source_c(GR_GAUSSIAN, noise_voltage, noise_seed);
  d_freq_offset = gr_make_sig_source_c(1, GR_SIN_WAVE, frequency_offset, 1.0, 0.0);
  d_mixer_offset = gr_make_multiply_cc();
  
  connect(self(), 0, d_timing_offset, 0);
  connect(d_timing_offset, 0, d_multipath, 0);
  connect(d_multipath, 0, d_mixer_offset, 0);
  connect(d_freq_offset, 0, d_mixer_offset, 1);
  connect(d_mixer_offset, 0, d_noise_adder, 1);
  connect(d_noise, 0, d_noise_adder, 0);
  connect(d_noise_adder, 0, self(), 0);

gr_dpll_bb

Detect the peak of a signal. If a peak is detected, this block outputs a 1, or it outputs 0's.

Bits as bytes in & out: in/out = 0/1

  for (int i = 0; i < noutput_items; i++){
    optr[i]= 0;
    if(iptr[i] == 1) {
      if (d_restart == 0) {
	d_pulse_phase = 1;
      } else {
	if (d_pulse_phase > 0.5) d_pulse_phase += d_gain*(1.0-d_pulse_phase);
	else d_pulse_phase -= d_gain*d_pulse_phase;
      }
      d_restart = 3;
    }
    if (d_pulse_phase > d_decision_threshold) {
      d_pulse_phase -= 1.0;
      if (d_restart > 0) {
	d_restart -= 1;
	optr[i] = 1;
      }
    }
    d_pulse_phase += d_pulse_frequency;
  }

gr_simple_correlator & gr_simple_framer

Float input (sliced internally, monitors average signal level for slicing while looking for sync bits), bytes out (gr_simple_framer bytes in & out)

gr_pn_correlator

Sequential search, i.e. correlates by non-overlapping sample groups (decimates by PN code length)

  for (int i = 0; i < noutput_items; i++) {
    sum = 0.0;

    for (int j = 0; j < d_len; j++) {
      if (j != 0)		            // retard PN generator one sample per period
	d_pn = 2.0*d_reference->next_bit()-1.0; // no conditionals
      sum += *in++ * d_pn;
    }

    *out++ = sum*gr_complex(1.0/d_len, 0.0);
  }

gr_scrambler_bb & gr_descambler_bb

Both use gri_lfsr (Fibonacci Linear Feedback Shift Register using specified polynomial mask)

 next_bit_scramble(unsigned char input) - Scramble an input stream
 
     Perform one cycle of the LFSR.  The output bit is taken from
     the shift register LSB.  The shift register MSB is assigned from
     the modulo 2 sum of the masked shift register and the input LSB.

 next_bit_descramble(unsigned char input) - Descramble an input stream

     Perform one cycle of the LFSR.  The output bit is taken from 
     the modulo 2 sum of the masked shift register and the input LSB.
     The shift register MSB is assigned from the LSB of the input.

See http://en.wikipedia.org/wiki/Scrambler for operation of these
last two functions (see multiplicative scrambler.)

gr_phase_modulator_fc

  const float *in = (const float *) input_items[0];
  gr_complex *out = (gr_complex *) output_items[0];

  for (int i = 0; i < noutput_items; i++){
    d_phase = d_sensitivity * in[i];
    float oi, oq;
    gr_sincosf (d_phase, &oq, &oi);
    out[i] = gr_complex (oi, oq);
  }

gr_diff_encoder & gr_diff_decoder

Encoder: y[i] = (x[i] + y[i-1]) % M

  const unsigned char *in = (const unsigned char *) input_items[0];
  unsigned char *out = (unsigned char *) output_items[0];

  unsigned last_out = d_last_out;
  unsigned modulus = d_modulus;

  for (int i = 0; i < noutput_items; i++){
    out[i] = (in[i] + last_out) % modulus;
    last_out = out[i];
  }

  d_last_out = last_out;

Decoder: x[i] = (y[i] - y[i-1]) % M

  const unsigned char *in = (const unsigned char *) input_items[0];
  unsigned char *out = (unsigned char *) output_items[0];
  in += 1;	// ensure that in[-1] is valid

  unsigned modulus = d_modulus;

  for (int i = 0; i < noutput_items; i++){
    out[i] = (in[i] - in[i-1]) % modulus;
  }

PSK synchronisers & demodulators

  • Decimate by omega if reconstructed signal is output
  • Stream of bytes that represent each bit is output at symbol rate if full demodulator is used

gr_mpsk_receiver_cc

Constellation:

  • M modulation order of the M-PSK modulation
  • theta any constant phase rotation from the real axis of the constellation (radians)

Costas:

  • alpha gain parameter to adjust the phase in the Costas loop (~0.01)
  • beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4)
  • fmin minimum normalized frequency value the loop can achieve
  • fmax maximum normalized frequency value the loop can achieve

Clock recovery:

  • mu initial parameter for the interpolator [0,1]
  • gain_mu gain parameter of the M&M error signal to adjust mu (~0.05)
  • omega initial value for the number of symbols between samples (~number of samples/symbol)
  • gain_omega gain parameter to adjust omega based on the error (~omega^2/4)
  • omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005)

digital_clock_recovery_mm_xx

  • Omega
    • Samples per symbol
    • omega = bit_oversampling * (1 + freq_error)
  • Gain Mu (the most important parameter)
    • "This is the correction factor based on the timing difference between symbols" (0.05 - 0.15)
    • 0.175 (GMSK demod)
    • 0.05 (suggested above, 1200 baud AX.25 2FSK)
    • 0.01 (GSM scan, gr-smartnet)
    • 0.005 (too low?)
  • Mu (0.0 - 1.0)
    • "related to the adjustment of the sampling due to the error signal"
    • 0.5
  • Gain Omega
    • "gain_omega is used to adjust omega, but this should be very small since you don't want too much variation around it"
    • 0.25 * (Gain Mu)^2
  • Omega Relative Limit:
    • 0.001 (default in constructor documentation)
    • 0.005 (1200 baud AX.25 2FSK, GMSK demod)
    • 0.3 (gsm-scan, gr-smartnet)

From gsm-scan:

gain_mu = 0.01
gain_omega = .25 * gain_mu * gain_mu # critically damped
self.clocker = gr.clock_recovery_mm_ff(sps,
    gain_omega,
    0.5, #mu
    gain_mu,
    0.3) #omega_relative_limit

gr_fll_band_edge_cc

The frequency lock loop derives a band-edge filter that covers the upper and lower bandwidths
of a digitally-modulated signal. The bandwidth range is determined by the excess bandwidth
(e.g., rolloff factor) of the modulated signal. The placement in frequency of the band-edges
is determined by the oversampling ratio (number of samples per symbol) and the excess bandwidth.
The size of the filters should be fairly large so as to average over a number of symbols.

The FLL works by filtering the upper and lower band edges into x_u(t) and x_l(t), respectively.
These are combined to form cc(t) = x_u(t) + x_l(t) and ss(t) = x_u(t) - x_l(t). Combining
these to form the signal e(t) = Re{cc(t) \\times ss(t)^*} (where ^* is the complex conjugate)
provides an error signal at the DC term that is directly proportional to the carrier frequency.
We then make a second-order loop using the error signal that is the running average of e(t).

In theory, the band-edge filter is the derivative of the matched filter in frequency, 
(H_be(f) = \\frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point
of the matched filter's rolloff (if it's a raised-cosine, the derivative of a cosine is a sine).
Extend this sine by another quarter wave to make a half wave around the band-edges is equivalent
in time to the sum of two sinc functions. The baseband filter fot the band edges is therefore
derived from this sum of sincs. The band edge filters are then just the baseband signal
modulated to the correct place in frequency. All of these calculations are done in the
'design_filter' function.

Note: We use FIR filters here because the filters have to have a flat phase response over the
entire frequency range to allow their comparisons to be valid.

gr_encode_ccsds_27_bb & gr_decode_ccsds_27_bb

#define	POLYA	0x6d
#define	POLYB	0x4f

CHECK THIS: Apparently polynomials are reversed from common convention (AutoFEC handles this by trying to swap the streams before Viterbi)