diff options
Diffstat (limited to 'gr-digital/lib/costas_loop_cc_impl.cc')
-rw-r--r-- | gr-digital/lib/costas_loop_cc_impl.cc | 148 |
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; |