Difference between revisions of "GNU Radio Notes"

From SpenchWiki
Jump to: navigation, search
(Created page with '* gr_fft_vcc (gr_fft_vcc_fftw): '''shift''' parameter swaps two halves of frequency-domain data. Enabled by default, so forward transform ends up having DC in centre of array (li...')
 
(= digital_clock_recovery_mm_xx)
 
(25 intermediate revisions by the same user not shown)
Line 1: Line 1:
* gr_fft_vcc (gr_fft_vcc_fftw): '''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.
+
== My blocks ==
  
* gr_diff_phasor_cc.cc:
+
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:
 +
 +
<syntaxhighlight lang="cpp">
 
  out[i] = in[i] * conj(in[i-1]);
 
  out[i] = in[i] * conj(in[i-1]);
 +
</syntaxhighlight>
  
* gr_quadrature_demod_cf.cc:
+
== gr_quadrature_demod_cf ==
  
 +
gr_quadrature_demod_cf.cc:
 +
 +
<syntaxhighlight lang="cpp">
 
  gr_complex product = in[i] * conj (in[i-1]);
 
  gr_complex product = in[i] * conj (in[i-1]);
 
  out[i] = d_gain * gr_fast_atan2f(imag(product), real(product));
 
  out[i] = d_gain * gr_fast_atan2f(imag(product), real(product));
 +
</syntaxhighlight>
 +
 +
== 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.
 +
 +
<syntaxhighlight lang="cpp">
 +
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;
 +
    }
 +
  }
 +
</syntaxhighlight>
 +
 +
== gr_iqcomp_cc ==
 +
 +
What is this supposed to do?! No output!
 +
 +
<syntaxhighlight lang="cpp">
 +
  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();
 +
  }
 +
</syntaxhighlight>
 +
 +
== 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
 +
 +
<syntaxhighlight lang="cpp">
 +
  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;
 +
  }
 +
</syntaxhighlight>
 +
 +
== gr_threshold_ff ==
 +
 +
* Initial state & output is 1 or 0
 +
* '''hi''' is evaluated first, then '''lo'''. For binary threshold, make '''lo''' greater than '''hi'''.
 +
 +
<syntaxhighlight lang="cpp">
 +
    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;
 +
</syntaxhighlight>
 +
 +
== gr_channel_model ==
 +
 +
<syntaxhighlight lang="cpp">
 +
  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);
 +
</syntaxhighlight>
 +
 +
== 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
 +
 +
<syntaxhighlight lang="cpp">
 +
  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;
 +
  }
 +
</syntaxhighlight>
 +
 +
== 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)
 +
 +
<syntaxhighlight lang="cpp">
 +
  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);
 +
  }
 +
</syntaxhighlight>
 +
 +
== 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 ==
 +
 +
<syntaxhighlight lang="cpp">
 +
  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);
 +
  }
 +
</syntaxhighlight>
 +
 +
== gr_diff_encoder & gr_diff_decoder ==
 +
 +
Encoder: y[i] = (x[i] + y[i-1]) % M
 +
 +
<syntaxhighlight lang="cpp">
 +
  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;
 +
</syntaxhighlight>
 +
 +
Decoder: x[i] = (y[i] - y[i-1]) % M
 +
 +
<syntaxhighlight lang="cpp">
 +
  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;
 +
  }
 +
</syntaxhighlight>
 +
 +
== 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:
 +
<pre>
 +
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
 +
</pre>
 +
 +
== 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 ==
 +
 +
<syntaxhighlight lang="cpp">
 +
#define POLYA 0x6d
 +
#define POLYB 0x4f
 +
</syntaxhighlight>
  
* Random Source:
+
'''CHECK THIS''': Apparently polynomials are reversed from common convention (AutoFEC handles this by trying to swap the streams before Viterbi)
** When '''Byte''', full range (min/max) is 0-255.
 
  
* GLFSR Source:
+
{{RF}}
** 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.
 

Latest revision as of 12:36, 4 March 2013

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)