diff options
35 files changed, 1650 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 62eae906b..b6b5eb496 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,10 +124,20 @@ add_custom_target(uninstall ######################################################################## find_package(PythonLibs) find_package(SWIG) + +if(SWIG_FOUND) + message(STATUS "Minimum SWIG version required is 1.3.31") + set(SWIG_VERSION_CHECK FALSE) + if("${SWIG_VERSION}" VERSION_GREATER "1.3.30") + set(SWIG_VERSION_CHECK TRUE) + endif() +endif(SWIG_FOUND) + include(GrComponent) GR_REGISTER_COMPONENT("python-support" ENABLE_PYTHON PYTHONLIBS_FOUND SWIG_FOUND + SWIG_VERSION_CHECK ) find_package(CppUnit) diff --git a/cmake/Modules/GrSwig.cmake b/cmake/Modules/GrSwig.cmake index f49fc731c..ced8b16c8 100644 --- a/cmake/Modules/GrSwig.cmake +++ b/cmake/Modules/GrSwig.cmake @@ -72,10 +72,18 @@ function(GR_SWIG_MAKE_DOCS output_file) COMMENT "Generating doxygen xml for ${name} docs" ) + #call sync if we can to flush the doxygen writes to file before python reads + find_program(SYNC_EXECUTABLE sync) + unset(sync_command) + if(SYNC_EXECUTABLE) + set(sync_command COMMAND ${SYNC_EXECUTABLE}) + endif() + #call the swig_doc script on the xml files add_custom_command( OUTPUT ${output_file} DEPENDS ${input_files} ${OUTPUT_DIRECTORY}/xml/index.xml + ${sync_command} COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} ${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py ${OUTPUT_DIRECTORY}/xml diff --git a/docs/doxygen/other/group_defs.dox b/docs/doxygen/other/group_defs.dox index 6288d1f0a..facdc2338 100644 --- a/docs/doxygen/other/group_defs.dox +++ b/docs/doxygen/other/group_defs.dox @@ -32,6 +32,7 @@ /*! \defgroup uhd_blk UHD Interface */ /*! \defgroup audio_blk Audio Interface */ /*! \defgroup pfb_blk Polyphase Filterbank */ +/*! \defgroup snr_blk SNR estimators */ /*! * \defgroup base_blk Base classes for GR Blocks diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.cc b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.cc index 02bfaf105..9fa98cc69 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.cc +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.cc @@ -62,6 +62,7 @@ gr_fft_filter_ccc::gr_fft_filter_ccc (int decimation, const std::vector<gr_compl #else d_filter = new gri_fft_filter_ccc_sse(decimation, taps); #endif + d_new_taps = taps; d_nsamples = d_filter->set_taps(taps); set_output_multiple(d_nsamples); } @@ -78,6 +79,12 @@ gr_fft_filter_ccc::set_taps (const std::vector<gr_complex> &taps) d_updated = true; } +std::vector<gr_complex> +gr_fft_filter_ccc::taps () const +{ + return d_new_taps; +} + int gr_fft_filter_ccc::work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.h b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.h index 721a44a83..1b72a1c00 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.h +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.h @@ -62,6 +62,7 @@ class GR_CORE_API gr_fft_filter_ccc : public gr_sync_decimator ~gr_fft_filter_ccc (); void set_taps (const std::vector<gr_complex> &taps); + std::vector<gr_complex> taps () const; int work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.i b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.i index aa7564f54..812920d8b 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.i +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_ccc.i @@ -36,4 +36,5 @@ class gr_fft_filter_ccc : public gr_sync_decimator ~gr_fft_filter_ccc (); void set_taps (const std::vector<gr_complex> &taps); + std::vector<gr_complex> taps () const; }; diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.cc b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.cc index e5b218f20..c0a9b3483 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.cc +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.cc @@ -55,7 +55,7 @@ gr_fft_filter_fff::gr_fft_filter_fff (int decimation, const std::vector<float> & #else d_filter = new gri_fft_filter_fff_sse(decimation, taps); #endif - + d_new_taps = taps; d_nsamples = d_filter->set_taps(taps); set_output_multiple(d_nsamples); } @@ -72,6 +72,12 @@ gr_fft_filter_fff::set_taps (const std::vector<float> &taps) d_updated = true; } +std::vector<float> +gr_fft_filter_fff::taps () const +{ + return d_new_taps; +} + int gr_fft_filter_fff::work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.h b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.h index b0dc74883..ddd8dcac2 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.h +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.h @@ -62,6 +62,7 @@ class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator ~gr_fft_filter_fff (); void set_taps (const std::vector<float> &taps); + std::vector<float> taps () const; int work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.i b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.i index bbe84f99f..7e2cde977 100644 --- a/gnuradio-core/src/lib/filter/gr_fft_filter_fff.i +++ b/gnuradio-core/src/lib/filter/gr_fft_filter_fff.i @@ -36,4 +36,5 @@ class gr_fft_filter_fff : public gr_sync_decimator ~gr_fft_filter_fff (); void set_taps (const std::vector<float> &taps); + std::vector<float> taps () const; }; diff --git a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.cc.t b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.cc.t index 29e351925..f7458e743 100644 --- a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.cc.t +++ b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.cc.t @@ -63,6 +63,12 @@ void d_updated = true; } +std::vector<@TAP_TYPE@> +@NAME@::taps () const +{ + return d_new_taps; +} + int @NAME@::work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.h.t b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.h.t index db0625504..f638e7bb5 100644 --- a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.h.t +++ b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.h.t @@ -59,6 +59,7 @@ class GR_CORE_API @NAME@ : public gr_sync_decimator ~@NAME@ (); void set_taps (const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps () const; int work (int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.i.t b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.i.t index 0cbe8cbcc..fb4ff95af 100644 --- a/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.i.t +++ b/gnuradio-core/src/lib/filter/gr_fir_filter_XXX.i.t @@ -38,4 +38,5 @@ class @NAME@ : public gr_sync_decimator ~@NAME@ (); void set_taps (const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps () const; }; diff --git a/gnuradio-core/src/lib/gengen/gr_noise_source_X.h.t b/gnuradio-core/src/lib/gengen/gr_noise_source_X.h.t index 9dd92c8f5..ab5992257 100644 --- a/gnuradio-core/src/lib/gengen/gr_noise_source_X.h.t +++ b/gnuradio-core/src/lib/gengen/gr_noise_source_X.h.t @@ -55,6 +55,9 @@ class GR_CORE_API @NAME@ : public gr_sync_block { void set_type (gr_noise_type_t type) { d_type = type; } void set_amplitude (float ampl) { d_ampl = ampl; } + gr_noise_type_t type () const { return d_type; } + float amplitude () const { return d_ampl; } + virtual int work (int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); diff --git a/gnuradio-core/src/lib/gengen/gr_noise_source_X.i.t b/gnuradio-core/src/lib/gengen/gr_noise_source_X.i.t index 27261502d..179dc0343 100644 --- a/gnuradio-core/src/lib/gengen/gr_noise_source_X.i.t +++ b/gnuradio-core/src/lib/gengen/gr_noise_source_X.i.t @@ -34,4 +34,7 @@ class @NAME@ : public gr_block { public: void set_type (gr_noise_type_t type) { d_type = type; } void set_amplitude (float ampl) { d_ampl = ampl; } + + gr_noise_type_t type () const { return d_type; } + float amplitude () const { return d_ampl; } }; diff --git a/gnuradio-core/src/lib/hier/gr_channel_model.cc b/gnuradio-core/src/lib/hier/gr_channel_model.cc index fb57e808a..5f190e972 100644 --- a/gnuradio-core/src/lib/hier/gr_channel_model.cc +++ b/gnuradio-core/src/lib/hier/gr_channel_model.cc @@ -99,3 +99,28 @@ gr_channel_model::set_timing_offset(double epsilon) { d_timing_offset->set_interp_ratio(epsilon); } + + +double +gr_channel_model::noise_voltage() const +{ + return d_noise->amplitude(); +} + +double +gr_channel_model::frequency_offset() const +{ + return d_freq_offset->frequency(); +} + +std::vector<gr_complex> +gr_channel_model::taps() const +{ + return d_multipath->taps(); +} + +double +gr_channel_model::timing_offset() const +{ + return d_timing_offset->interp_ratio(); +} diff --git a/gnuradio-core/src/lib/hier/gr_channel_model.h b/gnuradio-core/src/lib/hier/gr_channel_model.h index 07c0c76b6..c5d06ce11 100644 --- a/gnuradio-core/src/lib/hier/gr_channel_model.h +++ b/gnuradio-core/src/lib/hier/gr_channel_model.h @@ -71,4 +71,9 @@ class GR_CORE_API gr_channel_model : public gr_hier_block2 void set_frequency_offset(double frequency_offset); void set_taps(const std::vector<gr_complex> &taps); void set_timing_offset(double epsilon); + + double noise_voltage() const; + double frequency_offset() const; + std::vector<gr_complex> taps() const; + double timing_offset() const; }; diff --git a/gnuradio-core/src/lib/hier/gr_channel_model.i b/gnuradio-core/src/lib/hier/gr_channel_model.i index ff9ab466d..2e0cb7bdf 100644 --- a/gnuradio-core/src/lib/hier/gr_channel_model.i +++ b/gnuradio-core/src/lib/hier/gr_channel_model.i @@ -42,4 +42,9 @@ class gr_channel_model : public gr_hier_block2 void set_frequency_offset(double frequency_offset); void set_taps(const std::vector<gr_complex> &taps); void set_timing_offset(double epsilon); + + double noise_voltage() const; + double frequency_offset() const; + std::vector<gr_complex> taps() const; + double timing_offset() const; }; diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py index b3124ad29..f02f700a6 100755 --- a/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py +++ b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py @@ -273,6 +273,30 @@ class test_fft_filter(gr_unittest.TestCase): self.assert_fft_float_ok2(expected_result, result_data) + def test_fff_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + + op = gr.fft_filter_fff(1, taps) + result_data = op.taps() + print result_data + + self.assertEqual(taps, result_data) + + def test_ccc_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + + op = gr.fft_filter_ccc(1, taps) + result_data = op.taps() + print result_data + + self.assertComplexTuplesAlmostEqual(taps, result_data, 4) + if __name__ == '__main__': gr_unittest.run(test_fft_filter, "test_fft_filter.xml") diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_noise.py b/gnuradio-core/src/python/gnuradio/gr/qa_noise.py index 4a575f5d6..d7750cfe2 100755 --- a/gnuradio-core/src/python/gnuradio/gr/qa_noise.py +++ b/gnuradio-core/src/python/gnuradio/gr/qa_noise.py @@ -34,6 +34,18 @@ class test_noise_source(gr_unittest.TestCase): # Just confirm that we can instantiate a noise source op = gr.noise_source_f(gr.GR_GAUSSIAN, 10, 10) + def test_002(self): + # Test get methods + set_type = gr.GR_GAUSSIAN + set_ampl = 10 + op = gr.noise_source_f(set_type, set_ampl, 10) + get_type = op.type() + get_ampl = op.amplitude() + + self.assertEqual(get_type, set_type) + self.assertEqual(get_ampl, set_ampl) + + if __name__ == '__main__': gr_unittest.run(test_noise_source, "test_noise_source.xml") diff --git a/gr-digital/examples/CMakeLists.txt b/gr-digital/examples/CMakeLists.txt index 2645557cc..7b94f745c 100644 --- a/gr-digital/examples/CMakeLists.txt +++ b/gr-digital/examples/CMakeLists.txt @@ -25,6 +25,7 @@ GR_PYTHON_INSTALL(PROGRAMS example_timing.py run_length.py gen_whitener.py + snr_estimators.py DESTINATION ${GR_PKG_DATA_DIR}/examples/digital COMPONENT "digital_python" ) diff --git a/gr-digital/examples/snr_estimators.py b/gr-digital/examples/snr_estimators.py new file mode 100755 index 000000000..432abd455 --- /dev/null +++ b/gr-digital/examples/snr_estimators.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python + +import sys + +try: + import scipy + from scipy import stats +except ImportError: + print "Error: Program requires scipy (www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires Matplotlib (matplotlib.sourceforge.net)." + sys.exit(1) + +from gnuradio import gr, digital +from optparse import OptionParser +from gnuradio.eng_option import eng_option + +''' +This example program uses Python and GNU Radio to calculate SNR of a +noise BPSK signal to compare them. + +For an explination of the online algorithms, see: +http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics +''' + +def online_skewness(data, alpha): + n = 0 + mean = 0 + M2 = 0 + M3 = 0 + d_M3 = 0 + + for n in xrange(len(data)): + delta = data[n] - mean + delta_n = delta / (n+1) + term1 = delta * delta_n * (n) + mean = mean + delta_n + M3 = term1 * delta_n * (n - 1) - 3 * delta_n * M2 + M2 = M2 + term1 + d_M3 = (0.001)*M3 + (1-0.001)*d_M3; + + return d_M3 + +def snr_est_simple(signal): + y1 = scipy.mean(abs(signal)) + y2 = scipy.real(scipy.mean(signal**2)) + y3 = (y1*y1 - y2) + snr_rat = y1*y1/y3 + return 10.0*scipy.log10(snr_rat), snr_rat + +def snr_est_skew(signal): + y1 = scipy.mean(abs(signal)) + y2 = scipy.mean(scipy.real(signal**2)) + y3 = (y1*y1 - y2) + y4 = online_skewness(abs(signal.real), 0.001) + + skw = y4*y4 / (y2*y2*y2); + snr_rat = y1*y1 / (y3 + skw*y1*y1) + return 10.0*scipy.log10(snr_rat), snr_rat + +def snr_est_m2m4(signal): + M2 = scipy.mean(abs(signal)**2) + M4 = scipy.mean(abs(signal)**4) + snr_rat = 2*scipy.sqrt(2*M2*M2 - M4) / (M2 - scipy.sqrt(2*M2*M2 - M4)) + return 10.0*scipy.log10(snr_rat), snr_rat + +def snr_est_svr(signal): + N = len(signal) + ssum = 0 + msum = 0 + for i in xrange(1, N): + ssum += (abs(signal[i])**2)*(abs(signal[i-1])**2) + msum += (abs(signal[i])**4) + savg = (1.0/(float(N)-1.0))*ssum + mavg = (1.0/(float(N)-1.0))*msum + beta = savg / (mavg - savg) + + snr_rat = 2*((beta - 1) + scipy.sqrt(beta*(beta-1))) + return 10.0*scipy.log10(snr_rat), snr_rat + + +def main(): + gr_estimators = {"simple": digital.SNR_EST_SIMPLE, + "skew": digital.SNR_EST_SKEW, + "m2m4": digital.SNR_EST_M2M4, + "svr": digital.SNR_EST_SVR} + py_estimators = {"simple": snr_est_simple, + "skew": snr_est_skew, + "m2m4": snr_est_m2m4, + "svr": snr_est_svr} + + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Set the number of samples to process [default=%default]") + parser.add_option("", "--snr-min", type="float", default=-5, + help="Minimum SNR [default=%default]") + parser.add_option("", "--snr-max", type="float", default=20, + help="Maximum SNR [default=%default]") + parser.add_option("", "--snr-step", type="float", default=0.5, + help="SNR step amount [default=%default]") + parser.add_option("-t", "--type", type="choice", + choices=gr_estimators.keys(), default="simple", + help="Estimator type {0} [default=%default]".format( + gr_estimators.keys())) + (options, args) = parser.parse_args () + + N = options.nsamples + xx = scipy.random.randn(N) + xy = scipy.random.randn(N) + bits = 2*scipy.complex64(scipy.random.randint(0, 2, N)) - 1 + + snr_known = list() + snr_python = list() + snr_gr = list() + + # when to issue an SNR tag; can be ignored in this example. + ntag = 10000 + + n_cpx = xx + 1j*xy + + py_est = py_estimators[options.type] + gr_est = gr_estimators[options.type] + + SNR_min = options.snr_min + SNR_max = options.snr_max + SNR_step = options.snr_step + SNR_dB = scipy.arange(SNR_min, SNR_max+SNR_step, SNR_step) + for snr in SNR_dB: + SNR = 10.0**(snr/10.0) + scale = scipy.sqrt(SNR) + yy = bits + n_cpx/scale + print "SNR: ", snr + + Sknown = scipy.mean(yy**2) + Nknown = scipy.var(n_cpx/scale)/2 + snr0 = Sknown/Nknown + snr0dB = 10.0*scipy.log10(snr0) + snr_known.append(snr0dB) + + snrdB, snr = py_est(yy) + snr_python.append(snrdB) + + gr_src = gr.vector_source_c(bits.tolist(), False) + gr_snr = digital.mpsk_snr_est_cc(gr_est, ntag, 0.001) + gr_chn = gr.channel_model(1.0/scale) + gr_snk = gr.null_sink(gr.sizeof_gr_complex) + tb = gr.top_block() + tb.connect(gr_src, gr_chn, gr_snr, gr_snk) + tb.run() + + snr_gr.append(gr_snr.snr()) + + f1 = pylab.figure(1) + s1 = f1.add_subplot(1,1,1) + s1.plot(SNR_dB, snr_known, "k-o", linewidth=2, label="Known") + s1.plot(SNR_dB, snr_python, "b-o", linewidth=2, label="Python") + s1.plot(SNR_dB, snr_gr, "g-o", linewidth=2, label="GNU Radio") + s1.grid(True) + s1.set_title('SNR Estimators') + s1.set_xlabel('SNR (dB)') + s1.set_ylabel('Estimated SNR') + s1.legend() + + pylab.show() + + +if __name__ == "__main__": + main() + diff --git a/gr-digital/grc/digital_dxpsk_demod.xml b/gr-digital/grc/digital_dxpsk_demod.xml index d5e742097..cfd474f68 100644 --- a/gr-digital/grc/digital_dxpsk_demod.xml +++ b/gr-digital/grc/digital_dxpsk_demod.xml @@ -33,6 +33,7 @@ <make>digital.$(type)_demod( samples_per_symbol=$samples_per_symbol, excess_bw=$excess_bw, + freq_bw=$freq_bw, phase_bw=$phase_bw, timing_bw=$timing_bw, gray_coded=$gray_coded, @@ -67,6 +68,12 @@ <type>real</type> </param> <param> + <name>FLL Bandwidth</name> + <key>freq_bw</key> + <value>6.28/100.0</value> + <type>real</type> + </param> + <param> <name>Phase Loop Bandwidth</name> <key>phase_bw</key> <value>6.28/100.0</value> diff --git a/gr-digital/include/CMakeLists.txt b/gr-digital/include/CMakeLists.txt index cf20bd1e7..81ed8d368 100644 --- a/gr-digital/include/CMakeLists.txt +++ b/gr-digital/include/CMakeLists.txt @@ -22,6 +22,7 @@ ######################################################################## install(FILES digital_api.h + digital_impl_mpsk_snr_est.h digital_binary_slicer_fb.h digital_clock_recovery_mm_cc.h digital_clock_recovery_mm_ff.h @@ -37,12 +38,14 @@ install(FILES digital_kurtotic_equalizer_cc.h digital_metric_type.h digital_mpsk_receiver_cc.h + digital_mpsk_snr_est_cc.h digital_ofdm_cyclic_prefixer.h digital_ofdm_frame_acquisition.h digital_ofdm_frame_sink.h digital_ofdm_insert_preamble.h digital_ofdm_mapper_bcv.h digital_ofdm_sampler.h + digital_probe_mpsk_snr_est_c.h digital_gmskmod_bc.h digital_cpmmod_bc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio diff --git a/gr-digital/include/digital_impl_mpsk_snr_est.h b/gr-digital/include/digital_impl_mpsk_snr_est.h new file mode 100644 index 000000000..df7dbadec --- /dev/null +++ b/gr-digital/include/digital_impl_mpsk_snr_est.h @@ -0,0 +1,279 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_DIGITAL_IMPL_MPSK_SNR_EST_H +#define INCLUDED_DIGITAL_IMPL_MPSK_SNR_EST_H + +#include <digital_api.h> +#include <gr_sync_block.h> + +//! Enum for the type of SNR estimator to select +/*! \ingroup snr_blk + * \anchor ref_snr_est_types + * + * Below are some ROUGH estimates of what values of SNR each of these + * types of estimators is good for. In general, these offer a + * trade-off between accuracy and performance. + * + * \li SNR_EST_SIMPLE: Simple estimator (>= 7 dB) + * \li SNR_EST_SKEW: Skewness-base est (>= 5 dB) + * \li SNR_EST_M2M4: 2nd & 4th moment est (>= 1 dB) + * \li SNR_EST_SVR: SVR-based est (>= 0dB) +*/ +enum snr_est_type_t { + SNR_EST_SIMPLE = 0, // Simple estimator (>= 7 dB) + SNR_EST_SKEW, // Skewness-base est (>= 5 dB) + SNR_EST_M2M4, // 2nd & 4th moment est (>= 1 dB) + SNR_EST_SVR // SVR-based est (>= 0dB) +}; + +/*! \brief A parent class for SNR estimators, specifically for M-PSK + * signals in AWGN channels. + * \ingroup snr_blk + */ +class DIGITAL_API digital_impl_mpsk_snr_est +{ + protected: + double d_alpha, d_beta; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + */ + digital_impl_mpsk_snr_est(double alpha); + virtual ~digital_impl_mpsk_snr_est(); + + //! Get the running-average coefficient + double alpha() const; + + //! Set the running-average coefficient + void set_alpha(double alpha); + + //! Update the current registers + virtual int update(int noutput_items, + const gr_complex *in); + + //! Use the register values to compute a new estimate + virtual double snr(); +}; + + +//! \brief SNR Estimator using simple mean/variance estimates. +/*! \ingroup snr_blk + * + * A very simple SNR estimator that just uses mean and variance + * estimates of an M-PSK constellation. This esimator is quick and + * cheap and accurate for high SNR (above 7 dB or so) but quickly + * starts to overestimate the SNR at low SNR. + */ +class DIGITAL_API digital_impl_mpsk_snr_est_simple : + public digital_impl_mpsk_snr_est +{ + private: + double d_y1, d_y2; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + */ + digital_impl_mpsk_snr_est_simple(double alpha); + ~digital_impl_mpsk_snr_est_simple() {} + + int update(int noutput_items, + const gr_complex *in); + double snr(); +}; + + +//! \brief SNR Estimator using skewness correction. +/*! \ingroup snr_blk + * + * This is an estimator that came from a discussion between Tom + * Rondeau and fred harris with no known paper reference. The idea is + * that at low SNR, the variance estimations will be affected because + * of fold-over around the decision boundaries, which results in a + * skewness to the samples. We estimate the skewness and use this as + * a correcting term. + */ +class DIGITAL_API digital_impl_mpsk_snr_est_skew : + public digital_impl_mpsk_snr_est +{ + private: + double d_y1, d_y2, d_y3; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + */ + digital_impl_mpsk_snr_est_skew(double alpha); + ~digital_impl_mpsk_snr_est_skew() {} + + int update(int noutput_items, + const gr_complex *in); + double snr(); +}; + + +//! \brief SNR Estimator using 2nd and 4th-order moments. +/*! \ingroup snr_blk + * + * An SNR estimator for M-PSK signals that uses 2nd (M2) and 4th (M4) + * order moments. This estimator uses knowledge of the kurtosis of + * the signal (k_a) and noise (k_w) to make its estimation. We use + * Beaulieu's approximations here to M-PSK signals and AWGN channels + * such that k_a=1 and k_w=2. These approximations significantly + * reduce the complexity of the calculations (and computations) + * required. + * + * Reference: + * D. R. Pauluzzi and N. C. Beaulieu, "A comparison of SNR + * estimation techniques for the AWGN channel," IEEE + * Trans. Communications, Vol. 48, No. 10, pp. 1681-1691, 2000. + */ +class DIGITAL_API digital_impl_mpsk_snr_est_m2m4 : + public digital_impl_mpsk_snr_est +{ + private: + double d_y1, d_y2; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + */ + digital_impl_mpsk_snr_est_m2m4(double alpha); + ~digital_impl_mpsk_snr_est_m2m4() {} + + int update(int noutput_items, + const gr_complex *in); + double snr(); +}; + + +//! \brief SNR Estimator using 2nd and 4th-order moments. +/*! \ingroup snr_blk + * + * An SNR estimator for M-PSK signals that uses 2nd (M2) and 4th (M4) + * order moments. This estimator uses knowledge of the kurtosis of + * the signal (k_a) and noise (k_w) to make its estimation. In this + * case, you can set your own estimations for k_a and k_w, the + * kurtosis of the signal and noise, to fit this estimation better to + * your signal and channel conditions. + * + * A word of warning: this estimator has not been fully tested or + * proved with any amount of rigor. The estimation for M4 in + * particular might be ignoring effectf of when k_a and k_w are + * different. Use this estimator with caution and a copy of the + * reference on hand. + * + * The digital_mpsk_snr_est_m2m4 assumes k_a and k_w to simplify the + * computations for M-PSK and AWGN channels. Use that estimator + * unless you have a way to guess or estimate these values here. + * + * Original paper: + * R. Matzner, "An SNR estimation algorithm for complex baseband + * signal using higher order statistics," Facta Universitatis + * (Nis), no. 6, pp. 41-52, 1993. + * + * Reference used in derivation: + * D. R. Pauluzzi and N. C. Beaulieu, "A comparison of SNR + * estimation techniques for the AWGN channel," IEEE + * Trans. Communications, Vol. 48, No. 10, pp. 1681-1691, 2000. + */ +class DIGITAL_API digital_impl_snr_est_m2m4 : + public digital_impl_mpsk_snr_est +{ + private: + double d_y1, d_y2; + double d_ka, d_kw; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + * \param ka: estimate of the signal kurtosis (1 for PSK) + * \param kw: estimate of the channel noise kurtosis (2 for AWGN) + */ + digital_impl_snr_est_m2m4(double alpha, double ka, double kw); + ~digital_impl_snr_est_m2m4() {} + + int update(int noutput_items, + const gr_complex *in); + double snr(); +}; + + +//! \brief Signal-to-Variation Ratio SNR Estimator. +/*! \ingroup snr_blk + * + * This estimator actually comes from an SNR estimator for M-PSK + * signals in fading channels, but this implementation is + * specifically for AWGN channels. The math was simplified to assume + * a signal and noise kurtosis (k_a and k_w) for M-PSK signals in + * AWGN. These approximations significantly reduce the complexity of + * the calculations (and computations) required. + * + * Original paper: + * A. L. Brandao, L. B. Lopes, and D. C. McLernon, "In-service + * monitoring of multipath delay and cochannel interference for + * indoor mobile communication systems," Proc. IEEE + * Int. Conf. Communications, vol. 3, pp. 1458-1462, May 1994. + * + * Reference: + * D. R. Pauluzzi and N. C. Beaulieu, "A comparison of SNR + * estimation techniques for the AWGN channel," IEEE + * Trans. Communications, Vol. 48, No. 10, pp. 1681-1691, 2000. + */ +class DIGITAL_API digital_impl_mpsk_snr_est_svr : + public digital_impl_mpsk_snr_est +{ + private: + double d_y1, d_y2; + + public: + /*! Constructor + * + * Parameters: + * \param alpha: the update rate of internal running average + * calculations. + */ + digital_impl_mpsk_snr_est_svr(double alpha); + ~digital_impl_mpsk_snr_est_svr() {} + + int update(int noutput_items, + const gr_complex *in); + double snr(); +}; + +#endif /* INCLUDED_DIGITAL_IMPL_MPSK_SNR_EST_H */ diff --git a/gr-digital/include/digital_mpsk_snr_est_cc.h b/gr-digital/include/digital_mpsk_snr_est_cc.h new file mode 100644 index 000000000..2cbd98bab --- /dev/null +++ b/gr-digital/include/digital_mpsk_snr_est_cc.h @@ -0,0 +1,115 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_DIGITAL_MPSK_SNR_EST_CC_H +#define INCLUDED_DIGITAL_MPSK_SNR_EST_CC_H + +#include <digital_api.h> +#include <gr_sync_block.h> +#include <digital_impl_mpsk_snr_est.h> + +class digital_mpsk_snr_est_cc; +typedef boost::shared_ptr<digital_mpsk_snr_est_cc> digital_mpsk_snr_est_cc_sptr; + +DIGITAL_API digital_mpsk_snr_est_cc_sptr +digital_make_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples=10000, + double alpha=0.001); + +//! \brief A block for computing SNR of a signal. +/*! \ingroup snr_blk + * + * This block can be used to monitor and retrieve estimations of the + * signal SNR. It is designed to work in a flowgraph and passes all + * incoming data along to its output. + * + * The block is designed for use with M-PSK signals especially. The + * type of estimator is specified as the \p type parameter in the + * constructor. The estimators tend to trade off performance for + * accuracy, although experimentation should be done to figure out + * the right approach for a given implementation. Further, the + * current set of estimators are designed and proven theoretically + * under AWGN conditions; some amount of error should be assumed + * and/or estimated for real channel conditions. + */ +class DIGITAL_API digital_mpsk_snr_est_cc : public gr_sync_block +{ + private: + snr_est_type_t d_type; + int d_nsamples, d_count; + double d_alpha; + digital_impl_mpsk_snr_est *d_snr_est; + + //d_key is the tag name, 'snr', d_me is the block name + unique ID + pmt::pmt_t d_key, d_me; + + /*! Factory function returning shared pointer of this class + * + * Parameters: + * + * \param type: the type of estimator to use \ref ref_snr_est_types + * "snr_est_type_t" for details about the available types. + * \param tag_nsamples: after this many samples, a tag containing + * the SNR (key='snr') will be sent + * \param alpha: the update rate of internal running average + * calculations. + */ + friend DIGITAL_API digital_mpsk_snr_est_cc_sptr + digital_make_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples, + double alpha); + + // Private constructor + digital_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples, + double alpha); + +public: + + ~digital_mpsk_snr_est_cc(); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + //! Return the estimated signal-to-noise ratio in decibels + double snr(); + + //! Return the type of estimator in use + snr_est_type_t type() const; + + //! Return how many samples between SNR tags + int tag_nsample() const; + + //! Get the running-average coefficient + double alpha() const; + + //! Set type of estimator to use + void set_type(snr_est_type_t t); + + //! Set the number of samples between SNR tags + void set_tag_nsample(int n); + + //! Set the running-average coefficient + void set_alpha(double alpha); +}; + +#endif /* INCLUDED_DIGITAL_MPSK_SNR_EST_CC_H */ diff --git a/gr-digital/include/digital_probe_mpsk_snr_est_c.h b/gr-digital/include/digital_probe_mpsk_snr_est_c.h new file mode 100644 index 000000000..a78e90412 --- /dev/null +++ b/gr-digital/include/digital_probe_mpsk_snr_est_c.h @@ -0,0 +1,113 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_DIGITAL_PROBE_MPSK_SNR_EST_C_H +#define INCLUDED_DIGITAL_PROBE_MPSK_SNR_EST_C_H + +#include <digital_api.h> +#include <gr_sync_block.h> +#include <digital_impl_mpsk_snr_est.h> + +class digital_probe_mpsk_snr_est_c; +typedef boost::shared_ptr<digital_probe_mpsk_snr_est_c> digital_probe_mpsk_snr_est_c_sptr; + +DIGITAL_API digital_probe_mpsk_snr_est_c_sptr +digital_make_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples=10000, + double alpha=0.001); + +//! \brief A probe for computing SNR of a signal. +/*! \ingroup snr_blk + * + * This is a probe block (a sink) that can be used to monitor and + * retrieve estimations of the signal SNR. This probe is designed for + * use with M-PSK signals especially. The type of estimator is + * specified as the \p type parameter in the constructor. The + * estimators tend to trade off performance for accuracy, although + * experimentation should be done to figure out the right approach + * for a given implementation. Further, the current set of estimators + * are designed and proven theoretically under AWGN conditions; some + * amount of error should be assumed and/or estimated for real + * channel conditions. + */ +class DIGITAL_API digital_probe_mpsk_snr_est_c : public gr_sync_block +{ + private: + snr_est_type_t d_type; + int d_nsamples, d_count; + double d_alpha; + digital_impl_mpsk_snr_est *d_snr_est; + + //d_key is the message name, 'snr' + pmt::pmt_t d_key; + + /*! Factory function returning shared pointer of this class + * + * Parameters: + * + * \param type: the type of estimator to use \ref ref_snr_est_types + * "snr_est_type_t" for details about the available types. + * \param msg_nsamples: [not implemented yet] after this many + * samples, a message containing the SNR (key='snr') will be sent + * \param alpha: the update rate of internal running average + * calculations. + */ + friend DIGITAL_API digital_probe_mpsk_snr_est_c_sptr + digital_make_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples, + double alpha); + + //! Private constructor + digital_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples, + double alpha); + +public: + + ~digital_probe_mpsk_snr_est_c(); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + //! Return the estimated signal-to-noise ratio in decibels + double snr(); + + //! Return the type of estimator in use + snr_est_type_t type() const; + + //! Return how many samples between SNR messages + int msg_nsample() const; + + //! Get the running-average coefficient + double alpha() const; + + //! Set type of estimator to use + void set_type(snr_est_type_t t); + + //! Set the number of samples between SNR messages + void set_msg_nsample(int n); + + //! Set the running-average coefficient + void set_alpha(double alpha); +}; + +#endif /* INCLUDED_DIGITAL_PROBE_MPSK_SNR_EST_C_H */ diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt index b90757111..779972ff3 100644 --- a/gr-digital/lib/CMakeLists.txt +++ b/gr-digital/lib/CMakeLists.txt @@ -32,6 +32,7 @@ link_directories(${Boost_LIBRARY_DIRS}) # Setup library ######################################################################## list(APPEND gr_digital_sources + digital_impl_mpsk_snr_est.cc digital_binary_slicer_fb.cc digital_clock_recovery_mm_cc.cc digital_clock_recovery_mm_ff.cc @@ -46,12 +47,14 @@ list(APPEND gr_digital_sources digital_lms_dd_equalizer_cc.cc digital_kurtotic_equalizer_cc.cc digital_mpsk_receiver_cc.cc + digital_mpsk_snr_est_cc.cc digital_ofdm_cyclic_prefixer.cc digital_ofdm_frame_acquisition.cc digital_ofdm_frame_sink.cc digital_ofdm_insert_preamble.cc digital_ofdm_mapper_bcv.cc digital_ofdm_sampler.cc + digital_probe_mpsk_snr_est_c.cc digital_gmskmod_bc.cc digital_cpmmod_bc.cc ) diff --git a/gr-digital/lib/digital_impl_mpsk_snr_est.cc b/gr-digital/lib/digital_impl_mpsk_snr_est.cc new file mode 100644 index 000000000..38177083f --- /dev/null +++ b/gr-digital/lib/digital_impl_mpsk_snr_est.cc @@ -0,0 +1,256 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <digital_impl_mpsk_snr_est.h> +#include <cstdio> + +digital_impl_mpsk_snr_est::digital_impl_mpsk_snr_est(double alpha) +{ + set_alpha(alpha); +} + +digital_impl_mpsk_snr_est::~digital_impl_mpsk_snr_est() +{} + +void +digital_impl_mpsk_snr_est::set_alpha(double alpha) +{ + d_alpha = alpha; + d_beta = 1.0-alpha; +} + +double +digital_impl_mpsk_snr_est::alpha() const +{ + return d_alpha; +} + +int +digital_impl_mpsk_snr_est::update(int noutput_items, + const gr_complex *in) +{ + throw std::runtime_error("digital_impl_mpsk_snr_est: Unimplemented"); +} + +double +digital_impl_mpsk_snr_est::snr() +{ + throw std::runtime_error("digital_impl_mpsk_snr_est: Unimplemented"); +} + + +/********************************************************************/ + + +digital_impl_mpsk_snr_est_simple::digital_impl_mpsk_snr_est_simple( + double alpha) : + digital_impl_mpsk_snr_est(alpha) +{ + d_y1 = 0; + d_y2 = 0; +} + +int +digital_impl_mpsk_snr_est_simple::update( + int noutput_items, + const gr_complex *in) +{ + for (int i = 0; i < noutput_items; i++){ + double y1 = abs(in[i]); + d_y1 = d_alpha*y1 + d_beta*d_y1; + + double y2 = real(in[i]*in[i]); + d_y2 = d_alpha*y2 + d_beta*d_y2; + } + return noutput_items; +} + +double +digital_impl_mpsk_snr_est_simple::snr() +{ + double y1_2 = d_y1*d_y1; + double y3 = y1_2 - d_y2 + 1e-20; + return 10.0*log10(y1_2/y3); +} + + +/********************************************************************/ + + +digital_impl_mpsk_snr_est_skew::digital_impl_mpsk_snr_est_skew( + double alpha) : + digital_impl_mpsk_snr_est(alpha) +{ + d_y1 = 0; + d_y2 = 0; + d_y3 = 0; +} + + +int +digital_impl_mpsk_snr_est_skew::update( + int noutput_items, + const gr_complex *in) +{ + for (int i = 0; i < noutput_items; i++){ + double y1 = abs(in[i]); + d_y1 = d_alpha*y1 + d_beta*d_y1; + + double y2 = real(in[i]*in[i]); + d_y2 = d_alpha*y2 + d_beta*d_y2; + + // online algorithm for calculating skewness + // See: + // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics + double d = abs(in[i]) - d_y1; + double d_i = d / (i+1); + double y3 = (d*d_i*i)*d_i*(i-1) - 3.0*d_i*d_y2; + d_y3 = d_alpha*y3 + d_beta*d_y3; + } + return noutput_items; +} + +double +digital_impl_mpsk_snr_est_skew::snr() +{ + double y3 = d_y3*d_y3 / (d_y2*d_y2*d_y2); + double y1_2 = d_y1*d_y1; + double x = y1_2 - d_y2; + return 10.0*log10(y1_2 / (x + y3*y1_2)); +} + + +/********************************************************************/ + + +digital_impl_mpsk_snr_est_m2m4::digital_impl_mpsk_snr_est_m2m4( + double alpha) : + digital_impl_mpsk_snr_est(alpha) +{ + d_y1 = 0; + d_y2 = 0; +} + +int +digital_impl_mpsk_snr_est_m2m4::update( + int noutput_items, + const gr_complex *in) +{ + for (int i = 0; i < noutput_items; i++){ + double y1 = abs(in[i])*abs(in[i]); + d_y1 = d_alpha*y1 + d_beta*d_y1; + + double y2 = abs(in[i])*abs(in[i])*abs(in[i])*abs(in[i]); + d_y2 = d_alpha*y2 + d_beta*d_y2; + } + return noutput_items; +} + +double +digital_impl_mpsk_snr_est_m2m4::snr() +{ + double y1_2 = d_y1*d_y1; + return 10.0*log10(2.0*sqrt(2*y1_2 - d_y2) / + (d_y1 - sqrt(2*y1_2 - d_y2))); +} + + +/********************************************************************/ + + +digital_impl_snr_est_m2m4::digital_impl_snr_est_m2m4( + double alpha, double ka, double kw) : + digital_impl_mpsk_snr_est(alpha) +{ + d_y1 = 0; + d_y2 = 0; + d_ka = ka; + d_kw = kw; +} + +int +digital_impl_snr_est_m2m4::update( + int noutput_items, + const gr_complex *in) +{ + for (int i = 0; i < noutput_items; i++) { + double y1 = abs(in[i])*abs(in[i]); + d_y1 = d_alpha*y1 + d_beta*d_y1; + + double y2 = abs(in[i])*abs(in[i])*abs(in[i])*abs(in[i]); + d_y2 = d_alpha*y2 + d_beta*d_y2; + } + return noutput_items; +} + +double +digital_impl_snr_est_m2m4::snr() +{ + double M2 = d_y1; + double M4 = d_y2; + double s = M2*(d_kw - 2) + + sqrt((4.0-d_ka*d_kw)*M2*M2 + M4*(d_ka+d_kw-4.0)) / + (d_ka + d_kw - 4.0); + double n = M2 - s; + + return 10.0*log10(s / n); +} + + +/********************************************************************/ + + +digital_impl_mpsk_snr_est_svr::digital_impl_mpsk_snr_est_svr( + double alpha) : + digital_impl_mpsk_snr_est(alpha) +{ + d_y1 = 0; + d_y2 = 0; +} + +int +digital_impl_mpsk_snr_est_svr::update( + int noutput_items, + const gr_complex *in) +{ + for (int i = 0; i < noutput_items; i++){ + double x = abs(in[i]); + double x1 = abs(in[i-1]); + double y1 = (x*x)*(x1*x1); + d_y1 = d_alpha*y1 + d_beta*d_y1; + + double y2 = x*x*x*x; + d_y2 = d_alpha*y2 + d_beta*d_y2; + } + return noutput_items; +} + +double +digital_impl_mpsk_snr_est_svr::snr() +{ + double x = d_y1 / (d_y2 - d_y1); + return 10.0*log10(2.*((x-1) + sqrt(x*(x-1)))); +} diff --git a/gr-digital/lib/digital_mpsk_snr_est_cc.cc b/gr-digital/lib/digital_mpsk_snr_est_cc.cc new file mode 100644 index 000000000..b5a60f0d3 --- /dev/null +++ b/gr-digital/lib/digital_mpsk_snr_est_cc.cc @@ -0,0 +1,186 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <digital_mpsk_snr_est_cc.h> +#include <gr_io_signature.h> +#include <cstdio> + +digital_mpsk_snr_est_cc_sptr +digital_make_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples, + double alpha) +{ + return gnuradio::get_initial_sptr(new digital_mpsk_snr_est_cc( + type, tag_nsamples, alpha)); +} + +digital_mpsk_snr_est_cc::digital_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples, + double alpha) + : gr_sync_block ("mpsk_snr_est_cc", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))) +{ + d_snr_est = NULL; + + d_type = type; + d_nsamples = tag_nsamples; + d_count = 0; + set_alpha(alpha); + + set_type(type); + + // at least 1 estimator has to look back + set_history(2); + + std::stringstream str; + str << name() << unique_id(); + d_me = pmt::pmt_string_to_symbol(str.str()); + d_key = pmt::pmt_string_to_symbol("snr"); +} + +digital_mpsk_snr_est_cc::~digital_mpsk_snr_est_cc() +{ + if(d_snr_est) + delete d_snr_est; +} + +int +digital_mpsk_snr_est_cc::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + // This is a pass-through block; copy input to output + memcpy(output_items[0], input_items[0], + noutput_items * sizeof(gr_complex)); + + const gr_complex *in = (const gr_complex*)input_items[0]; + + // Update, calculate, and issue an SNR tag every d_nsamples + int index = 0, x = 0; + int64_t nwritten = nitems_written(0); + while(index + (d_nsamples-d_count) <= noutput_items) { + x = d_nsamples - d_count; + nwritten += x; + + // Update the SNR estimate registers from the current input + d_snr_est->update(x, &in[index]); + + // Issue a tag with the SNR data + pmt::pmt_t pmt_snr = pmt::pmt_from_double(d_snr_est->snr()); + add_item_tag(0, // stream ID + nwritten, // tag's sample number + d_key, // snr key + pmt_snr, // SNR + d_me); // block src id + + index += x; + d_count = 0; + } + + // Keep track of remaining items and update estimators + x = noutput_items - index; + d_count += x; + d_snr_est->update(x, &in[index]); + + return noutput_items; +} + +double +digital_mpsk_snr_est_cc::snr() +{ + if(d_snr_est) + return d_snr_est->snr(); + else + throw std::runtime_error("digital_mpsk_snr_est_cc:: No SNR estimator defined.\n"); +} + +snr_est_type_t +digital_mpsk_snr_est_cc::type() const +{ + return d_type; +} + +int +digital_mpsk_snr_est_cc::tag_nsample() const +{ + return d_nsamples; +} + +double +digital_mpsk_snr_est_cc::alpha() const +{ + return d_alpha; +} + +void +digital_mpsk_snr_est_cc::set_type(snr_est_type_t t) +{ + d_type = t; + + if(d_snr_est) + delete d_snr_est; + + switch (d_type) { + case(SNR_EST_SIMPLE): + d_snr_est = new digital_impl_mpsk_snr_est_simple(d_alpha); + break; + case(SNR_EST_SKEW): + d_snr_est = new digital_impl_mpsk_snr_est_skew(d_alpha); + break; + case(SNR_EST_M2M4): + d_snr_est = new digital_impl_mpsk_snr_est_m2m4(d_alpha); + break; + case(SNR_EST_SVR): + d_snr_est = new digital_impl_mpsk_snr_est_svr(d_alpha); + break; + default: + throw std::invalid_argument("digital_mpsk_snr_est_cc: unknown type specified.\n"); + } +} + +void +digital_mpsk_snr_est_cc::set_tag_nsample(int n) +{ + if(n > 0) { + d_nsamples = n; + d_count = 0; // reset state + } + else + throw std::invalid_argument("digital_mpsk_snr_est_cc: tag_nsamples can't be <= 0\n"); +} + +void +digital_mpsk_snr_est_cc::set_alpha(double alpha) +{ + if((alpha >= 0) && (alpha <= 1.0)) { + d_alpha = alpha; + if(d_snr_est) + d_snr_est->set_alpha(d_alpha); + } + else + throw std::invalid_argument("digital_mpsk_snr_est_cc: alpha must be in [0,1]\n"); +} diff --git a/gr-digital/lib/digital_probe_mpsk_snr_est_c.cc b/gr-digital/lib/digital_probe_mpsk_snr_est_c.cc new file mode 100644 index 000000000..5cdfea96d --- /dev/null +++ b/gr-digital/lib/digital_probe_mpsk_snr_est_c.cc @@ -0,0 +1,152 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <digital_probe_mpsk_snr_est_c.h> +#include <gr_io_signature.h> +#include <cstdio> + +digital_probe_mpsk_snr_est_c_sptr +digital_make_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples, + double alpha) +{ + return gnuradio::get_initial_sptr( + new digital_probe_mpsk_snr_est_c(type, msg_nsamples, alpha)); +} + +digital_probe_mpsk_snr_est_c::digital_probe_mpsk_snr_est_c( + snr_est_type_t type, + int msg_nsamples, + double alpha) + : gr_sync_block ("probe_mpsk_snr_est_c", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)) +{ + d_snr_est = NULL; + + d_type = type; + d_nsamples = msg_nsamples; + d_count = 0; + set_alpha(alpha); + + set_type(type); + + // at least 1 estimator has to look back + set_history(2); + + d_key = pmt::pmt_string_to_symbol("snr"); +} + +digital_probe_mpsk_snr_est_c::~digital_probe_mpsk_snr_est_c() +{ + if(d_snr_est) + delete d_snr_est; +} + +int +digital_probe_mpsk_snr_est_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex*)input_items[0]; + return d_snr_est->update(noutput_items, in); +} + +double +digital_probe_mpsk_snr_est_c::snr() +{ + if(d_snr_est) + return d_snr_est->snr(); + else + throw std::runtime_error("digital_probe_mpsk_snr_est_c:: No SNR estimator defined.\n"); +} + +snr_est_type_t +digital_probe_mpsk_snr_est_c::type() const +{ + return d_type; +} + +int +digital_probe_mpsk_snr_est_c::msg_nsample() const +{ + return d_nsamples; +} + +double +digital_probe_mpsk_snr_est_c::alpha() const +{ + return d_alpha; +} + +void +digital_probe_mpsk_snr_est_c::set_type(snr_est_type_t t) +{ + d_type = t; + + if(d_snr_est) + delete d_snr_est; + + switch (d_type) { + case(SNR_EST_SIMPLE): + d_snr_est = new digital_impl_mpsk_snr_est_simple(d_alpha); + break; + case(SNR_EST_SKEW): + d_snr_est = new digital_impl_mpsk_snr_est_skew(d_alpha); + break; + case(SNR_EST_M2M4): + d_snr_est = new digital_impl_mpsk_snr_est_m2m4(d_alpha); + break; + case(SNR_EST_SVR): + d_snr_est = new digital_impl_mpsk_snr_est_svr(d_alpha); + break; + default: + throw std::invalid_argument("digital_probe_mpsk_snr_est_c: unknown type specified.\n"); + } +} + +void +digital_probe_mpsk_snr_est_c::set_msg_nsample(int n) +{ + if(n > 0) { + d_nsamples = n; + d_count = 0; // reset state + } + else + throw std::invalid_argument("digital_probe_mpsk_snr_est_c: msg_nsamples can't be <= 0\n"); +} + +void +digital_probe_mpsk_snr_est_c::set_alpha(double alpha) +{ + if((alpha >= 0) && (alpha <= 1.0)) { + d_alpha = alpha; + if(d_snr_est) + d_snr_est->set_alpha(d_alpha); + } + else + throw std::invalid_argument("digital_probe_mpsk_snr_est_c: alpha must be in [0,1]\n"); +} diff --git a/gr-digital/python/qa_mpsk_snr_est.py b/gr-digital/python/qa_mpsk_snr_est.py new file mode 100755 index 000000000..d392567bf --- /dev/null +++ b/gr-digital/python/qa_mpsk_snr_est.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig as digital +import math, random + +def get_cplx(): + return complex(2*random.randint(0,1) - 1, 0) +def get_n_cplx(): + return complex(random.random()-0.5, random.random()-0.5) + +class test_mpsk_snr_est (gr_unittest.TestCase): + def setUp (self): + self.tb = gr.top_block () + + random.seed(0) # make repeatable + N = 10000 + self._noise = [get_n_cplx() for i in xrange(N)] + self._bits = [get_cplx() for i in xrange(N)] + + def tearDown (self): + self.tb = None + + def mpsk_snr_est_setup (self, op): + result = [] + for i in xrange(1,6): + src_data = [b+(i*n) for b,n in zip(self._bits, self._noise)] + + src = gr.vector_source_c (src_data) + dst = gr.null_sink (gr.sizeof_gr_complex) + + tb = gr.top_block () + tb.connect (src, op) + tb.connect (op, dst) + tb.run () # run the graph and wait for it to finish + + result.append(op.snr()) + return result + + def test_mpsk_snr_est_simple (self): + expected_result = [11.48, 5.91, 3.30, 2.08, 1.46] + + N = 10000 + alpha = 0.001 + op = digital.mpsk_snr_est_cc (digital.SNR_EST_SIMPLE, N, alpha) + + actual_result = self.mpsk_snr_est_setup(op) + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 2) + + def test_mpsk_snr_est_skew (self): + expected_result = [11.48, 5.91, 3.30, 2.08, 1.46] + + N = 10000 + alpha = 0.001 + op = digital.mpsk_snr_est_cc (digital.SNR_EST_SKEW, N, alpha) + + actual_result = self.mpsk_snr_est_setup(op) + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 2) + + def test_mpsk_snr_est_m2m4 (self): + expected_result = [11.02, 6.20, 4.98, 5.16, 5.66] + + N = 10000 + alpha = 0.001 + op = digital.mpsk_snr_est_cc (digital.SNR_EST_M2M4, N, alpha) + + actual_result = self.mpsk_snr_est_setup(op) + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 2) + + def test_mpsk_snr_est_svn (self): + expected_result = [10.90, 6.00, 4.76, 4.97, 5.49] + + N = 10000 + alpha = 0.001 + op = digital.mpsk_snr_est_cc (digital.SNR_EST_SVR, N, alpha) + + actual_result = self.mpsk_snr_est_setup(op) + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 2) + + def test_probe_mpsk_snr_est_m2m4 (self): + expected_result = [11.02, 6.20, 4.98, 5.16, 5.66] + + actual_result = [] + for i in xrange(1,6): + src_data = [b+(i*n) for b,n in zip(self._bits, self._noise)] + + src = gr.vector_source_c (src_data) + + N = 10000 + alpha = 0.001 + op = digital.probe_mpsk_snr_est_c (digital.SNR_EST_M2M4, N, alpha) + + tb = gr.top_block () + tb.connect (src, op) + tb.run () # run the graph and wait for it to finish + + actual_result.append(op.snr()) + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 2) + + +if __name__ == '__main__': + # Test various SNR estimators; we're not using a Gaussian + # noise source, so these estimates have no real meaning; + # just a sanity check. + gr_unittest.run(test_mpsk_snr_est, "test_mpsk_snr_est.xml") + diff --git a/gr-digital/swig/CMakeLists.txt b/gr-digital/swig/CMakeLists.txt index dd6097286..6f2c2251a 100644 --- a/gr-digital/swig/CMakeLists.txt +++ b/gr-digital/swig/CMakeLists.txt @@ -59,12 +59,14 @@ install( digital_lms_dd_equalizer_cc.i digital_kurtotic_equalizer_cc.i digital_mpsk_receiver_cc.i + digital_mpsk_snr_est_cc.i digital_ofdm_cyclic_prefixer.i digital_ofdm_frame_acquisition.i digital_ofdm_frame_sink.i digital_ofdm_insert_preamble.i digital_ofdm_mapper_bcv.i digital_ofdm_sampler.i + digital_probe_mpsk_snr_est_c.i digital_gmskmod_bc.i digital_cpmmod_bc.i DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig diff --git a/gr-digital/swig/digital_mpsk_snr_est_cc.i b/gr-digital/swig/digital_mpsk_snr_est_cc.i new file mode 100644 index 000000000..f0ca13f87 --- /dev/null +++ b/gr-digital/swig/digital_mpsk_snr_est_cc.i @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,mpsk_snr_est_cc); + +digital_mpsk_snr_est_cc_sptr +digital_make_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples=10000, + double alpha=0.001); + +class digital_mpsk_snr_est_cc : public gr_sync_block +{ +private: + void digital_mpsk_snr_est_cc(snr_est_type_t type, + int tag_nsamples, + double alpha); + +public: + double snr(); + snr_est_type_t type() const; + int tag_nsample() const; + double alpha() const; + void set_type(snr_est_type_t t); + void set_tag_nsample(int n); + void set_alpha(double alpha); +}; diff --git a/gr-digital/swig/digital_probe_mpsk_snr_est_c.i b/gr-digital/swig/digital_probe_mpsk_snr_est_c.i new file mode 100644 index 000000000..93db4127a --- /dev/null +++ b/gr-digital/swig/digital_probe_mpsk_snr_est_c.i @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,probe_mpsk_snr_est_c); + +digital_probe_mpsk_snr_est_c_sptr +digital_make_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples=10000, + double alpha=0.001); + +class digital_probe_mpsk_snr_est_c : public gr_sync_block +{ +private: + void digital_probe_mpsk_snr_est_c(snr_est_type_t type, + int msg_nsamples, + double alpha); + +public: + double snr(); + snr_est_type_t type() const; + int msg_nsample() const; + double alpha() const; + void set_type(snr_est_type_t t); + void set_msg_nsample(int n); + void set_alpha(double alpha); +}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index 86b5cab13..a39ef9ab7 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -24,6 +24,15 @@ //load generated python docstrings %include "digital_swig_doc.i" +#if SWIGPYTHON +enum snr_est_type_t { + SNR_EST_SIMPLE = 0, // Simple estimator (>= 7 dB) + SNR_EST_SKEW, // Skewness-base est (>= 5 dB) + SNR_EST_M2M4, // 2nd & 4th moment est (>= 1 dB) + SNR_EST_SVR // SVR-based est (>= 0dB) +}; +#endif + %include <gri_control_loop.i> %{ @@ -41,12 +50,14 @@ #include "digital_kurtotic_equalizer_cc.h" #include "digital_lms_dd_equalizer_cc.h" #include "digital_mpsk_receiver_cc.h" +#include "digital_mpsk_snr_est_cc.h" #include "digital_ofdm_cyclic_prefixer.h" #include "digital_ofdm_frame_acquisition.h" #include "digital_ofdm_frame_sink.h" #include "digital_ofdm_insert_preamble.h" #include "digital_ofdm_mapper_bcv.h" #include "digital_ofdm_sampler.h" +#include "digital_probe_mpsk_snr_est_c.h" #include "digital_cpmmod_bc.h" #include "digital_gmskmod_bc.h" %} @@ -65,16 +76,26 @@ %include "digital_kurtotic_equalizer_cc.i" %include "digital_lms_dd_equalizer_cc.i" %include "digital_mpsk_receiver_cc.i" +%include "digital_mpsk_snr_est_cc.i" %include "digital_ofdm_cyclic_prefixer.i" %include "digital_ofdm_frame_acquisition.i" %include "digital_ofdm_frame_sink.i" %include "digital_ofdm_insert_preamble.i" %include "digital_ofdm_mapper_bcv.i" %include "digital_ofdm_sampler.i" +%include "digital_probe_mpsk_snr_est_c.i" %include "digital_cpmmod_bc.i" %include "digital_gmskmod_bc.i" #if SWIGGUILE + +enum snr_est_type_t { + SNR_EST_SIMPLE = 0, // Simple estimator (>= 7 dB) + SNR_EST_SKEW, // Skewness-base est (>= 5 dB) + SNR_EST_M2M4, // 2nd & 4th moment est (>= 1 dB) + SNR_EST_SVR // SVR-based est (>= 0dB) +}; + %scheme %{ (load-extension-global "libguile-gnuradio-digital_swig" "scm_init_gnuradio_digital_swig_module") %} |