summaryrefslogtreecommitdiff
path: root/gr-digital/lib/costas_loop_cc_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gr-digital/lib/costas_loop_cc_impl.cc')
-rw-r--r--gr-digital/lib/costas_loop_cc_impl.cc148
1 files changed, 45 insertions, 103 deletions
diff --git a/gr-digital/lib/costas_loop_cc_impl.cc b/gr-digital/lib/costas_loop_cc_impl.cc
index 930183c0c0..5f2cba275e 100644
--- a/gr-digital/lib/costas_loop_cc_impl.cc
+++ b/gr-digital/lib/costas_loop_cc_impl.cc
@@ -37,7 +37,8 @@ costas_loop_cc_impl::costas_loop_cc_impl(float loop_bw, unsigned int order, bool
blocks::control_loop(loop_bw, 1.0, -1.0),
d_error(0),
d_noise(1.0),
- d_phase_detector(choose_phase_detector(order, use_snr))
+ d_use_snr(use_snr),
+ d_order(order)
{
message_port_register_in(pmt::mp("noise"));
set_msg_handler(pmt::mp("noise"),
@@ -46,96 +47,6 @@ costas_loop_cc_impl::costas_loop_cc_impl(float loop_bw, unsigned int order, bool
costas_loop_cc_impl::~costas_loop_cc_impl() {}
-costas_loop_cc_impl::d_phase_detector_t
-costas_loop_cc_impl::choose_phase_detector(unsigned int order, bool use_snr)
-{
- switch (order) {
- case 2:
- if (use_snr) {
- return &costas_loop_cc_impl::phase_detector_snr_2;
- }
- return &costas_loop_cc_impl::phase_detector_2;
-
- case 4:
- if (use_snr) {
- return &costas_loop_cc_impl::phase_detector_snr_4;
- }
- return &costas_loop_cc_impl::phase_detector_4;
-
- case 8:
- if (use_snr) {
- return &costas_loop_cc_impl::phase_detector_snr_8;
- }
- return &costas_loop_cc_impl::phase_detector_8;
- }
- throw std::invalid_argument("order must be 2, 4, or 8");
-}
-
-float costas_loop_cc_impl::phase_detector_8(gr_complex sample) const
-{
- /* This technique splits the 8PSK constellation into 2 squashed
- QPSK constellations, one when I is larger than Q and one
- where Q is larger than I. The error is then calculated
- proportionally to these squashed constellations by the const
- K = sqrt(2)-1.
-
- The signal magnitude must be > 1 or K will incorrectly bias
- the error value.
-
- Ref: Z. Huang, Z. Yi, M. Zhang, K. Wang, "8PSK demodulation for
- new generation DVB-S2", IEEE Proc. Int. Conf. Communications,
- Circuits and Systems, Vol. 2, pp. 1447 - 1450, 2004.
- */
-
- const float K = (sqrtf(2.0) - 1);
- if (fabsf(sample.real()) >= fabsf(sample.imag())) {
- return ((sample.real() > 0 ? 1.0 : -1.0) * sample.imag() -
- (sample.imag() > 0 ? 1.0 : -1.0) * sample.real() * K);
- } else {
- return ((sample.real() > 0 ? 1.0 : -1.0) * sample.imag() * K -
- (sample.imag() > 0 ? 1.0 : -1.0) * sample.real());
- }
-}
-
-float costas_loop_cc_impl::phase_detector_4(gr_complex sample) const
-{
- return ((sample.real() > 0 ? 1.0 : -1.0) * sample.imag() -
- (sample.imag() > 0 ? 1.0 : -1.0) * sample.real());
-}
-
-float costas_loop_cc_impl::phase_detector_2(gr_complex sample) const
-{
- return (sample.real() * sample.imag());
-}
-
-float costas_loop_cc_impl::phase_detector_snr_8(gr_complex sample) const
-{
- const float K = (sqrtf(2.0) - 1);
- const float snr = std::norm(sample) / d_noise;
- if (fabsf(sample.real()) >= fabsf(sample.imag())) {
- return ((blocks::tanhf_lut(snr * sample.real()) * sample.imag()) -
- (blocks::tanhf_lut(snr * sample.imag()) * sample.real() * K));
- } else {
- return ((blocks::tanhf_lut(snr * sample.real()) * sample.imag() * K) -
- (blocks::tanhf_lut(snr * sample.imag()) * sample.real()));
- }
-}
-
-float costas_loop_cc_impl::phase_detector_snr_4(gr_complex sample) const
-{
- const float snr = std::norm(sample) / d_noise;
- return ((blocks::tanhf_lut(snr * sample.real()) * sample.imag()) -
- (blocks::tanhf_lut(snr * sample.imag()) * sample.real()));
-}
-
-float costas_loop_cc_impl::phase_detector_snr_2(gr_complex sample) const
-{
- const float snr = std::norm(sample) / d_noise;
- return blocks::tanhf_lut(snr * sample.real()) * sample.imag();
-}
-
-float costas_loop_cc_impl::error() const { return d_error; }
-
void costas_loop_cc_impl::handle_set_noise(pmt::pmt_t msg)
{
if (pmt::is_real(msg)) {
@@ -154,8 +65,6 @@ int costas_loop_cc_impl::work(int noutput_items,
float* phase_optr = output_items.size() >= 3 ? (float*)output_items[2] : NULL;
float* error_optr = output_items.size() >= 4 ? (float*)output_items[3] : NULL;
- gr_complex nco_out;
-
std::vector<tag_t> tags;
get_tags_in_range(tags,
0,
@@ -163,6 +72,15 @@ int costas_loop_cc_impl::work(int noutput_items,
nitems_read(0) + noutput_items,
pmt::intern("phase_est"));
+ // Get this out of the for loop if not used:
+ bool has_additional_outputs = false;
+ if (freq_optr)
+ has_additional_outputs = true;
+ else if (phase_optr)
+ has_additional_outputs = true;
+ else if (error_optr)
+ has_additional_outputs = true;
+
for (int i = 0; i < noutput_items; i++) {
if (!tags.empty()) {
if (tags[0].offset - nitems_read(0) == (size_t)i) {
@@ -171,22 +89,46 @@ int costas_loop_cc_impl::work(int noutput_items,
}
}
- nco_out = gr_expj(-d_phase);
- optr[i] = iptr[i] * nco_out;
-
- d_error = (*this.*d_phase_detector)(optr[i]);
+ const gr_complex nco_out = gr_expj(-d_phase);
+
+ gr::fast_cc_multiply(optr[i], iptr[i], nco_out);
+
+ // EXPENSIVE LINE with function pointer, switch was about 20% faster in testing.
+ // Left in for logic justification/reference. d_error = phase_detector_2(optr[i]);
+ switch (d_order) {
+ case 2:
+ if (d_use_snr)
+ d_error = phase_detector_snr_2(optr[i]);
+ else
+ d_error = phase_detector_2(optr[i]);
+ break;
+ case 4:
+ if (d_use_snr)
+ d_error = phase_detector_snr_4(optr[i]);
+ else
+ d_error = phase_detector_4(optr[i]);
+ break;
+ case 8:
+ if (d_use_snr)
+ d_error = phase_detector_snr_8(optr[i]);
+ else
+ d_error = phase_detector_8(optr[i]);
+ break;
+ }
d_error = gr::branchless_clip(d_error, 1.0);
advance_loop(d_error);
phase_wrap();
frequency_limit();
- if (freq_optr != NULL)
- freq_optr[i] = d_freq;
- if (phase_optr != NULL)
- phase_optr[i] = d_phase;
- if (error_optr != NULL)
- error_optr[i] = d_error;
+ if (has_additional_outputs) {
+ if (freq_optr)
+ freq_optr[i] = d_freq;
+ if (phase_optr)
+ phase_optr[i] = d_phase;
+ if (error_optr)
+ error_optr[i] = d_error;
+ }
}
return noutput_items;