summaryrefslogtreecommitdiff
path: root/gnuradio-core
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core')
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc33
-rw-r--r--gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h1
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc104
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h40
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i1
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/dbpsk2.py64
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/dqpsk2.py56
7 files changed, 183 insertions, 116 deletions
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc
index d4b14c594..48eb849ab 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc
+++ b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.cc
@@ -47,6 +47,8 @@ gr_pfb_arb_resampler_ccf::gr_pfb_arb_resampler_ccf (float rate,
gr_make_io_signature (1, 1, sizeof(gr_complex))),
d_updated (false)
{
+ d_acc = 0; // start accumulator at 0
+
/* The number of filters is specified by the user as the filter size;
this is also the interpolation rate of the filter. We use it and the
rate provided to determine the decimation rate. This acts as a
@@ -131,23 +133,15 @@ void
gr_pfb_arb_resampler_ccf::create_diff_taps(const std::vector<float> &newtaps,
std::vector<float> &difftaps)
{
- float maxtap = 1e-20;
+ // Calculate the differential taps (derivative filter) by taking the difference
+ // between two taps. Duplicate the last one to make both filters the same length.
+ float tap;
difftaps.clear();
- difftaps.push_back(0); //newtaps[0]);
- for(unsigned int i = 1; i < newtaps.size()-1; i++) {
- float tap = newtaps[i+1] - newtaps[i-1];
+ for(unsigned int i = 0; i < newtaps.size()-1; i++) {
+ tap = newtaps[i+1] - newtaps[i];
difftaps.push_back(tap);
- if(tap > maxtap) {
- maxtap = tap;
- }
- }
- difftaps.push_back(0);//-newtaps[newtaps.size()-1]);
-
- // Scale the differential taps; helps scale error term to better update state
- // FIXME: should this be scaled this way or use the same gain as the taps?
- for(unsigned int i = 0; i < difftaps.size(); i++) {
- difftaps[i] /= maxtap;
}
+ difftaps.push_back(tap);
}
void
@@ -188,14 +182,17 @@ gr_pfb_arb_resampler_ccf::general_work (int noutput_items,
// start j by wrapping around mod the number of channels
while((j < d_int_rate) && (i < noutput_items)) {
- // Take the current filter output
+ // Take the current filter and derivative filter output
o0 = d_filters[j]->filter(&in[count]);
o1 = d_diff_filters[j]->filter(&in[count]);
- out[i] = o0 + o1*d_flt_rate; // linearly interpolate between samples
+ out[i] = o0 + o1*d_acc; // linearly interpolate between samples
i++;
-
- j += d_dec_rate;
+
+ // Adjust accumulator and index into filterbank
+ d_acc += d_flt_rate;
+ j += d_dec_rate + (int)floor(d_acc);
+ d_acc = fmodf(d_acc, 1.0);
}
if(i < noutput_items) { // keep state for next entry
float ss = (int)(j / d_int_rate); // number of items to skip ahead by
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h
index 531e9726f..b99ad286b 100644
--- a/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h
+++ b/gnuradio-core/src/lib/filter/gr_pfb_arb_resampler_ccf.h
@@ -118,6 +118,7 @@ class gr_pfb_arb_resampler_ccf : public gr_block
unsigned int d_int_rate; // the number of filters (interpolation rate)
unsigned int d_dec_rate; // the stride through the filters (decimation rate)
float d_flt_rate; // residual rate for the linear interpolation
+ float d_acc;
unsigned int d_last_filter;
int d_start_index;
unsigned int d_taps_per_filter;
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
index 0f3d85c83..030e45ddf 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
@@ -53,11 +53,13 @@ gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float ro
}
+static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)};
+static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int));
gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
int filter_size, float alpha, float beta)
: gr_sync_block ("fll_band_edge_cc",
gr_make_io_signature (1, 1, sizeof(gr_complex)),
- gr_make_io_signature (1, 1, sizeof(gr_complex))),
+ gr_make_io_signaturev (1, 4, iosig)),
d_alpha(alpha), d_beta(beta), d_updated (false)
{
// base this on the number of samples per symbol
@@ -67,6 +69,29 @@ gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
d_freq = 0;
d_phase = 0;
+ set_alpha(alpha);
+
+ design_filter(samps_per_sym, rolloff, filter_size);
+}
+
+gr_fll_band_edge_cc::~gr_fll_band_edge_cc ()
+{
+ delete d_filter_lower;
+ delete d_filter_upper;
+}
+
+void
+gr_fll_band_edge_cc::set_alpha(float alpha)
+{
+ float eta = sqrt(2.0)/2.0;
+ float theta = alpha;
+ d_alpha = (4*eta*theta) / (1.0 + 2.0*eta*theta + theta*theta);
+ d_beta = (4*theta*theta) / (1.0 + 2.0*eta*theta + theta*theta);
+}
+
+void
+gr_fll_band_edge_cc::design_filter(float samps_per_sym, float rolloff, int filter_size)
+{
int M = rint(filter_size / samps_per_sym);
float power = 0;
std::vector<float> bb_taps;
@@ -81,11 +106,11 @@ gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
int N = (bb_taps.size() - 1.0)/2.0;
std::vector<gr_complex> taps_lower;
std::vector<gr_complex> taps_upper;
- for(int i = 0; i < bb_taps.size(); i++) {
+ for(unsigned int i = 0; i < bb_taps.size(); i++) {
float tap = bb_taps[i] / power;
- float k = (-N + i)/(2.0*samps_per_sym); //rng = scipy.arange(-nn2, nn2+1) / (2*spb);
-
+ float k = (-N + (int)i)/(2.0*samps_per_sym);
+
gr_complex t1 = tap * gr_expj(-2*M_PI*(1+rolloff)*k);
gr_complex t2 = tap * gr_expj(2*M_PI*(1+rolloff)*k);
@@ -97,61 +122,32 @@ gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
d_filter_upper = gr_fir_util::create_gr_fir_ccc(vtaps);
d_filter_lower = gr_fir_util::create_gr_fir_ccc(vtaps);
- set_taps_lower(taps_lower);
- set_taps_upper(taps_upper);
-}
-
-gr_fll_band_edge_cc::~gr_fll_band_edge_cc ()
-{
-}
-
-void
-gr_fll_band_edge_cc::set_taps_lower (const std::vector<gr_complex> &taps)
-{
- unsigned int i;
-
- for(i = 0; i < taps.size(); i++) {
- d_taps_lower.push_back(taps[i]);
- }
-
- d_filter_lower->set_taps(d_taps_lower);
-
- // Set the history to ensure enough input items for each filter
- set_history(d_taps_lower.size()+1);
+ d_filter_lower->set_taps(taps_lower);
+ d_filter_upper->set_taps(taps_upper);
d_updated = true;
-}
-
-void
-gr_fll_band_edge_cc::set_taps_upper (const std::vector<gr_complex> &taps)
-{
- unsigned int i;
-
- for(i = 0; i < taps.size(); i++) {
- d_taps_upper.push_back(taps[i]);
- }
-
- d_filter_upper->set_taps(d_taps_upper);
// Set the history to ensure enough input items for each filter
- set_history(d_taps_upper.size()+1);
+ set_history(filter_size+1);
- d_updated = true;
}
void
gr_fll_band_edge_cc::print_taps()
{
unsigned int i;
+ std::vector<gr_complex> taps_upper = d_filter_upper->get_taps();
+ std::vector<gr_complex> taps_lower = d_filter_lower->get_taps();
+
printf("Upper Band-edge: [");
- for(i = 0; i < d_taps_upper.size(); i++) {
- printf(" %.4e + %.4ej,", d_taps_upper[i].real(), d_taps_upper[i].imag());
+ for(i = 0; i < taps_upper.size(); i++) {
+ printf(" %.4e + %.4ej,", taps_upper[i].real(), taps_upper[i].imag());
}
printf("]\n\n");
printf("Lower Band-edge: [");
- for(i = 0; i < d_taps_lower.size(); i++) {
- printf(" %.4e + %.4ej,", d_taps_lower[i].real(), d_taps_lower[i].imag());
+ for(i = 0; i < taps_lower.size(); i++) {
+ printf(" %.4e + %.4ej,", taps_lower[i].real(), taps_lower[i].imag());
}
printf("]\n\n");
}
@@ -164,6 +160,13 @@ gr_fll_band_edge_cc::work (int noutput_items,
const gr_complex *in = (const gr_complex *) input_items[0];
gr_complex *out = (gr_complex *) output_items[0];
+ float *frq, *phs, *err;
+ if(output_items.size() > 2) {
+ frq = (float *) output_items[1];
+ phs = (float *) output_items[2];
+ err = (float *) output_items[3];
+ }
+
if (d_updated) {
d_updated = false;
return 0; // history requirements may have changed.
@@ -180,21 +183,28 @@ gr_fll_band_edge_cc::work (int noutput_items,
out_upper = norm(d_filter_upper->filter(&out[i]));
out_lower = norm(d_filter_lower->filter(&out[i]));
error = out_lower - out_upper;
- d_error = 0.1*error + 0.9*d_error; // average error
+ d_error = 0.01*error + 0.99*d_error; // average error
- d_freq = d_freq + d_beta * error;
- d_phase = d_phase + d_freq + d_alpha * error;
+ d_freq = d_freq + d_beta * d_error;
+ d_phase = d_phase + d_freq + d_alpha * d_error;
if(d_phase > M_PI)
d_phase -= M_TWOPI;
else if(d_phase < -M_PI)
d_phase += M_TWOPI;
-
+
if (d_freq > d_max_freq)
d_freq = d_max_freq;
else if (d_freq < d_min_freq)
d_freq = d_min_freq;
+
+ if(output_items.size() > 2) {
+ frq[i] = d_freq;
+ phs[i] = d_phase;
+ err[i] = d_error;
+ }
}
+
return noutput_items;
}
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
index 86e69df16..09baf7fde 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
@@ -39,6 +39,26 @@ class gri_fft_complex;
* \brief Frequency Lock Loop using band-edge filters
*
* \ingroup general
+ *
+ * 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 calculating the power in both the upper and lower bands and comparing them. The
+ * difference in power between the filters is proportional to the frequency offset.
+ *
+ * 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.
*/
class gr_fll_band_edge_cc : public gr_sync_block
@@ -46,7 +66,11 @@ class gr_fll_band_edge_cc : public gr_sync_block
private:
/*!
* Build the FLL
- * \param taps (vector/list of gr_complex) The taps of the band-edge filter
+ * \param samps_per_sym (float) Number of samples per symbol of signal
+ * \param rolloff (float) Rolloff factor of signal
+ * \param filter_size (int) Size (in taps) of the filter
+ * \param alpha (float) Loop gain 1
+ * \param beta (float) Loop gain 2
*/
friend gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
int filter_size, float alpha, float beta);
@@ -58,8 +82,6 @@ class gr_fll_band_edge_cc : public gr_sync_block
gr_fir_ccc* d_filter_upper;
gr_fir_ccc* d_filter_lower;
- std::vector<gr_complex> d_taps_upper;
- std::vector<gr_complex> d_taps_lower;
bool d_updated;
float d_error;
float d_freq;
@@ -76,17 +98,19 @@ public:
~gr_fll_band_edge_cc ();
/*!
- * Resets the filter taps with the new prototype filter
- * \param taps (vector/list of gr_complex) The band-edge filter
+ * Design the band-edge filter based on the number of samples per symbol,
+ * filter rolloff factor, and the filter size
+ * \param samps_per_sym (float) Number of samples per symbol of signal
+ * \param rolloff (float) Rolloff factor of signal
+ * \param filter_size (int) Size (in taps) of the filter
*/
- void set_taps_lower (const std::vector<gr_complex> &taps);
- void set_taps_upper (const std::vector<gr_complex> &taps);
+ void design_filter(float samps_per_sym, float rolloff, int filter_size);
/*!
* Set the alpha gainvalue
* \param alpha (float) new gain value
*/
- void set_alpha(float alpha) { d_alpha = alpha; }
+ void set_alpha(float alpha);
/*!
* Set the beta gain value
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
index 545bad4f6..c9c792c8a 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
@@ -36,5 +36,6 @@ class gr_fll_band_edge_cc : public gr_sync_block
void set_alpha (float alpha);
void set_beta (float beta);
+ void design_filter(float samps_per_sym, float rolloff, int filter_size);
void print_taps();
};
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk2.py b/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk2.py
index cd9a207c8..e9fb3df89 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk2.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk2.py
@@ -26,7 +26,7 @@ differential BPSK modulation and demodulation.
"""
from gnuradio import gr, gru, modulation_utils
-from math import pi, sqrt
+from math import pi, sqrt, ceil
import psk
import cmath
from pprint import pprint
@@ -38,6 +38,7 @@ _def_gray_code = True
_def_verbose = False
_def_log = False
+_def_freq_alpha = 4e-3
_def_costas_alpha = 0.1
_def_timing_alpha = 0.100
_def_timing_beta = 0.010
@@ -85,8 +86,6 @@ class dbpsk2_mod(gr.hier_block2):
if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2:
raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol)
- ntaps = 11 * self._samples_per_symbol
-
arity = pow(2,self.bits_per_symbol())
# turn bytes into k-bit vectors
@@ -103,15 +102,15 @@ class dbpsk2_mod(gr.hier_block2):
self.chunks2symbols = gr.chunks_to_symbols_bc(psk.constellation[arity])
# pulse shaping filter
- self.rrc_taps = gr.firdes.root_raised_cosine(
- self._samples_per_symbol, # gain (samples_per_symbol since we're
- # interpolating by samples_per_symbol)
- self._samples_per_symbol, # sampling rate
- 1.0, # symbol rate
- self._excess_bw, # excess bandwidth (roll-off factor)
+ nfilts = 32
+ ntaps = nfilts * 11 * self._samples_per_symbol # make nfilts filters of ntaps each
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ nfilts, # gain
+ nfilts, # sampling rate based on 32 filters in resampler
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
ntaps)
- self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol,
- self.rrc_taps)
+ self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps)
# Connect
self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
@@ -182,6 +181,7 @@ class dbpsk2_demod(gr.hier_block2):
def __init__(self,
samples_per_symbol=_def_samples_per_symbol,
excess_bw=_def_excess_bw,
+ freq_alpha=_def_freq_alpha,
costas_alpha=_def_costas_alpha,
timing_alpha=_def_timing_alpha,
timing_max_dev=_def_timing_max_dev,
@@ -199,9 +199,11 @@ class dbpsk2_demod(gr.hier_block2):
@type samples_per_symbol: float
@param excess_bw: Root-raised cosine filter excess bandwidth
@type excess_bw: float
- @param costas_alpha: loop filter gain
+ @param freq_alpha: loop filter gain for frequency recovery
+ @type freq_alpha: float
+ @param costas_alpha: loop filter gain for phase/fine frequency recovery
@type costas_alpha: float
- @param timing_alpha: timing loop alpha gain
+ @param timing_alpha: loop alpha gain for timing recovery
@type timing_alpha: float
@param timing_max: timing loop maximum rate deviations
@type timing_max: float
@@ -223,6 +225,8 @@ class dbpsk2_demod(gr.hier_block2):
self._samples_per_symbol = samples_per_symbol
self._excess_bw = excess_bw
+ self._freq_alpha = freq_alpha
+ self._freq_beta = 0.25*self._freq_alpha**2
self._costas_alpha = costas_alpha
self._timing_alpha = timing_alpha
self._timing_beta = _def_timing_beta
@@ -238,24 +242,32 @@ class dbpsk2_demod(gr.hier_block2):
self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100)
#self.agc = gr.feedforward_agc_cc(16, 1.0)
+ # Frequency correction
+ self.freq_recov = gr.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw,
+ 11*int(self._samples_per_symbol),
+ self._freq_alpha, self._freq_beta)
+
+ # symbol timing recovery with RRC data filter
+ nfilts = 32
+ ntaps = 11 * int(self._samples_per_symbol*nfilts)
+ taps = gr.firdes.root_raised_cosine(nfilts, nfilts,
+ 1.0/float(self._samples_per_symbol),
+ self._excess_bw, ntaps)
+ self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol,
+ self._timing_alpha,
+ taps, nfilts, nfilts/2, self._timing_max_dev)
+ self.time_recov.set_beta(self._timing_beta)
+
+ # Perform phase / fine frequency correction
self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
# Allow a frequency swing of +/- half of the sample rate
fmin = -0.5
fmax = 0.5
- self.clock_recov = gr.costas_loop_cc(self._costas_alpha,
+ self.phase_recov = gr.costas_loop_cc(self._costas_alpha,
self._costas_beta,
fmax, fmin, arity)
- # symbol timing recovery with RRC data filter
- nfilts = 32
- ntaps = 11 * samples_per_symbol*nfilts
- taps = gr.firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(self._samples_per_symbol), self._excess_bw, ntaps)
- self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol,
- self._timing_alpha,
- taps, nfilts, nfilts/2, self._timing_max_dev)
- self.time_recov.set_beta(self._timing_beta)
-
# Do differential decoding based on phase change of symbols
self.diffdec = gr.diff_phasor_cc()
@@ -280,8 +292,7 @@ class dbpsk2_demod(gr.hier_block2):
# Connect
self.connect(self, self.agc,
- self.clock_recov,
- self.time_recov,
+ self.freq_recov, self.time_recov, self.phase_recov,
self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self)
if sync_out: self.connect(self.time_recov, (self, 1))
@@ -297,6 +308,7 @@ class dbpsk2_demod(gr.hier_block2):
print "bits per symbol: %d" % self.bits_per_symbol()
print "Gray code: %s" % self._gray_code
print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "FLL gain: %.2f" % self._freq_alpha
print "Costas Loop alpha: %.2f" % self._costas_alpha
print "Costas Loop beta: %.2f" % self._costas_beta
print "Timing alpha gain: %.2f" % self._timing_alpha
@@ -333,6 +345,8 @@ class dbpsk2_demod(gr.hier_block2):
parser.add_option("", "--no-gray-code", dest="gray_code",
action="store_false", default=_def_gray_code,
help="disable gray coding on modulated bits (PSK)")
+ parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha,
+ help="set frequency lock loop alpha gain value [default=%default] (PSK)")
parser.add_option("", "--costas-alpha", type="float", default=None,
help="set Costas loop alpha value [default=%default] (PSK)")
parser.add_option("", "--gain-alpha", type="float", default=_def_timing_alpha,
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk2.py b/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk2.py
index fd1e9f0ef..9fae6acca 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk2.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk2.py
@@ -38,6 +38,7 @@ _def_gray_code = True
_def_verbose = False
_def_log = False
+_def_freq_alpha = 4e-3
_def_costas_alpha = 0.01
_def_timing_alpha = 0.100
_def_timing_beta = 0.010
@@ -105,14 +106,15 @@ class dqpsk2_mod(gr.hier_block2):
self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
# pulse shaping filter
- self.rrc_taps = gr.firdes.root_raised_cosine(
- self._samples_per_symbol, # gain (sps since we're interpolating by sps)
- self._samples_per_symbol, # sampling rate
- 1.0, # symbol rate
- self._excess_bw, # excess bandwidth (roll-off factor)
+ nfilts = 32
+ ntaps = nfilts * 11 * self._samples_per_symbol # make nfilts filters of ntaps each
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ nfilts, # gain
+ nfilts, # sampling rate based on 32 filters in resampler
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
ntaps)
-
- self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+ self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps)
if verbose:
self._print_verbage()
@@ -182,6 +184,7 @@ class dqpsk2_demod(gr.hier_block2):
def __init__(self,
samples_per_symbol=_def_samples_per_symbol,
excess_bw=_def_excess_bw,
+ freq_alpha=_def_freq_alpha,
costas_alpha=_def_costas_alpha,
timing_alpha=_def_timing_alpha,
timing_max_dev=_def_timing_max_dev,
@@ -199,6 +202,8 @@ class dqpsk2_demod(gr.hier_block2):
@type samples_per_symbol: float
@param excess_bw: Root-raised cosine filter excess bandwidth
@type excess_bw: float
+ @param freq_alpha: loop filter gain for frequency recovery
+ @type freq_alpha: float
@param costas_alpha: loop filter gain
@type costas_alphas: float
@param timing_alpha: timing loop alpha gain
@@ -223,6 +228,8 @@ class dqpsk2_demod(gr.hier_block2):
self._samples_per_symbol = samples_per_symbol
self._excess_bw = excess_bw
+ self._freq_alpha = freq_alpha
+ self._freq_beta = 0.25*self._freq_alpha**2
self._costas_alpha = costas_alpha
self._timing_alpha = timing_alpha
self._timing_beta = _def_timing_beta
@@ -238,24 +245,35 @@ class dqpsk2_demod(gr.hier_block2):
self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100)
#self.agc = gr.feedforward_agc_cc(16, 2.0)
- self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
- # Allow a frequency swing of +/- half of the sample rate
- fmin = -0.5
- fmax = 0.5
+ # Frequency correction
+ self.freq_recov = gr.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw,
+ 11*int(self._samples_per_symbol),
+ self._freq_alpha, self._freq_beta)
- self.clock_recov = gr.costas_loop_cc(self._costas_alpha,
- self._costas_beta,
- fmax, fmin, arity)
# symbol timing recovery with RRC data filter
nfilts = 32
- ntaps = 11 * samples_per_symbol*nfilts
- taps = gr.firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(self._samples_per_symbol), self._excess_bw, ntaps)
+ ntaps = 11 * int(samples_per_symbol*nfilts)
+ taps = gr.firdes.root_raised_cosine(nfilts, nfilts,
+ 1.0/float(self._samples_per_symbol),
+ self._excess_bw, ntaps)
self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol,
self._timing_alpha,
taps, nfilts, nfilts/2, self._timing_max_dev)
self.time_recov.set_beta(self._timing_beta)
+
+ # Perform phase / fine frequency correction
+ self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
+ # Allow a frequency swing of +/- half of the sample rate
+ fmin = -0.5
+ fmax = 0.5
+
+ self.phase_recov = gr.costas_loop_cc(self._costas_alpha,
+ self._costas_beta,
+ fmax, fmin, arity)
+
+
# Perform Differential decoding on the constellation
self.diffdec = gr.diff_phasor_cc()
@@ -280,8 +298,7 @@ class dqpsk2_demod(gr.hier_block2):
# Connect
self.connect(self, self.agc,
- self.clock_recov,
- self.time_recov,
+ self.freq_recov, self.time_recov, self.phase_recov,
self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self)
if sync_out: self.connect(self.time_recov, (self, 1))
@@ -297,6 +314,7 @@ class dqpsk2_demod(gr.hier_block2):
print "bits per symbol: %d" % self.bits_per_symbol()
print "Gray code: %s" % self._gray_code
print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "FLL gain: %.2f" % self._freq_alpha
print "Costas Loop alpha: %.2e" % self._costas_alpha
print "Costas Loop beta: %.2e" % self._costas_beta
print "Timing alpha gain: %.2f" % self._timing_alpha
@@ -333,6 +351,8 @@ class dqpsk2_demod(gr.hier_block2):
parser.add_option("", "--no-gray-code", dest="gray_code",
action="store_false", default=_def_gray_code,
help="disable gray coding on modulated bits (PSK)")
+ parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha,
+ help="set frequency lock loop alpha gain value [default=%default] (PSK)")
parser.add_option("", "--costas-alpha", type="float", default=_def_costas_alpha,
help="set Costas loop alpha value [default=%default] (PSK)")
parser.add_option("", "--gain-alpha", type="float", default=_def_timing_alpha,