GNU Radio Notes
Contents
- 1 My blocks
- 2 My changes
- 3 Absent from 3.2 Doxygen class hierarchy
- 4 gr_fft_vcc
- 5 gr_diff_phasor_cc
- 6 gr_quadrature_demod_cf
- 7 Random Source
- 8 GLFSR Source
- 9 8psk_mod
- 10 gr_cpfsk_bc
- 11 gr_iqcomp_cc
- 12 gr_nop
- 13 gr_pa_2x2_phase_combiner
- 14 gr_threshold_ff
- 15 gr_channel_model
- 16 gr_dpll_bb
- 17 gr_simple_correlator & gr_simple_framer
- 18 gr_pn_correlator
- 19 gr_scrambler_bb & gr_descambler_bb
- 20 gr_phase_modulator_fc
- 21 gr_diff_encoder & gr_diff_decoder
- 22 PSK synchronisers & demodulators
- 23 gr_fll_band_edge_cc
- 24 gr_encode_ccsds_27_bb & gr_decode_ccsds_27_bb
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)
|