diff options
-rw-r--r-- | cmake/Packaging/Ubuntu-15.10.cmake | 13 | ||||
-rw-r--r-- | gr-dtv/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gr-dtv/examples/CMakeLists.txt | 25 | ||||
-rwxr-xr-x | gr-dtv/examples/atsc_ctrlport_monitor.py | 149 | ||||
-rw-r--r-- | gr-dtv/include/gnuradio/dtv/atsc_equalizer.h | 3 | ||||
-rw-r--r-- | gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h | 15 | ||||
-rw-r--r-- | gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h | 6 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_equalizer_impl.cc | 89 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_equalizer_impl.h | 7 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc | 89 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_rs_decoder_impl.h | 13 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_single_viterbi.cc | 65 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_single_viterbi.h | 17 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc | 25 | ||||
-rw-r--r-- | gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h | 4 | ||||
-rw-r--r-- | gr-uhd/include/gnuradio/uhd/usrp_source.h | 5 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.cc | 6 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.cc | 45 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.h | 4 |
19 files changed, 482 insertions, 100 deletions
diff --git a/cmake/Packaging/Ubuntu-15.10.cmake b/cmake/Packaging/Ubuntu-15.10.cmake new file mode 100644 index 0000000000..ed14b1ab22 --- /dev/null +++ b/cmake/Packaging/Ubuntu-15.10.cmake @@ -0,0 +1,13 @@ +SET(PACKAGE_DEPENDS_GRUEL_RUNTIME "libboost-all-dev" "libc6") +SET(PACKAGE_DEPENDS_UTILS "python-mako") +SET(PACKAGE_DEPENDS_GRUEL_PYTHON "python" "python-numpy") +SET(PACKAGE_DEPENDS_CORE_RUNTIME "libfftw3-3") +SET(PACKAGE_DEPENDS_QTGUI_RUNTIME "libqtcore4" "libqwt6") +SET(PACKAGE_DEPENDS_QTGUI_PYTHON "python-qt4" "python-qwt5-qt4") +SET(PACKAGE_DEPENDS_GRC "python" "python-numpy" "python-gtk2" "python-lxml" "python-cheetah") +SET(PACKAGE_DEPENDS_WXGUI "python-wxgtk2.8" "python-opengl") +SET(PACKAGE_DEPENDS_VIDEO_SDL_RUNTIME "libsdl1.2debian") +SET(PACKAGE_DEPENDS_UHD_RUNTIME "uhd") +SET(PACKAGE_DEPENDS_AUDIO_RUNTIME "libpulse0" "alsa-base" "libjack0") +SET(PACKAGE_DEPENDS_WAVELET_RUNTIME "libgsl0ldbl") +SET(PACKAGE_DEPENDS_WAVELET_PYTHON "python" "python-numpy") diff --git a/gr-dtv/CMakeLists.txt b/gr-dtv/CMakeLists.txt index fc7ab56bef..5a23482b26 100644 --- a/gr-dtv/CMakeLists.txt +++ b/gr-dtv/CMakeLists.txt @@ -41,6 +41,8 @@ GR_SET_GLOBAL(GR_DTV_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/lib ) +SET(GR_PKG_DTV_EXAMPLES_DIR ${GR_PKG_DATA_DIR}/examples/dtv) + ######################################################################## # Begin conditional configuration ######################################################################## diff --git a/gr-dtv/examples/CMakeLists.txt b/gr-dtv/examples/CMakeLists.txt index 82b663d924..315cb33de9 100644 --- a/gr-dtv/examples/CMakeLists.txt +++ b/gr-dtv/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2014 Free Software Foundation, Inc. +# Copyright 2014-2015 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -21,6 +21,29 @@ include(GrPython) GR_PYTHON_INSTALL( PROGRAMS + atsc_ctrlport_monitor.py + DESTINATION ${GR_PKG_DTV_EXAMPLES_DIR} + COMPONENT "dtv_python" +) + +install( + FILES + README.dvbs + README.dvbs2 + README.dvbt + README.dvbt2 + dvbs2_tx.grc + dvbs_tx.grc + dvbt_rx_8k.grc + dvbt_tx_2k.grc + dvbt_tx_8k.grc + file_atsc_rx.grc + file_atsc_tx.grc + uhd_atsc_capture.grc + uhd_rx_atsc.grc + vv003-cr23.grc + vv009-4kfft.grc + vv018-miso.grc DESTINATION ${GR_PKG_DTV_EXAMPLES_DIR} COMPONENT "dtv_python" ) diff --git a/gr-dtv/examples/atsc_ctrlport_monitor.py b/gr-dtv/examples/atsc_ctrlport_monitor.py new file mode 100755 index 0000000000..7c43aebb77 --- /dev/null +++ b/gr-dtv/examples/atsc_ctrlport_monitor.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Copyright 2015 Free Software Foundation +# +# This program 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. +# +# This program 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 this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +import matplotlib +matplotlib.use("QT4Agg") +import matplotlib.pyplot as plt +import matplotlib.animation as animation +from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient +import scipy +from scipy import fftpack + +""" +If a host is running the ATSC receiver chain with ControlPort +turned on, this script will connect to the host using the hostname and +port pair of the ControlPort instance and display metrics of the +receiver. The ATSC publishes information about the succes of the +Reed-Solomon decoder and Viterbi metrics for use here in displaying +the link quality. This also gets the equalizer taps of the receiver +and displays the frequency response. +""" + +class atsc_ctrlport_monitor: + def __init__(self, host, port): + argv = [None, host, port] + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + self.radio = radiosys.client + print self.radio + + + vt_init_key = 'dtv_atsc_viterbi_decoder0::decoder_metrics' + data = self.radio.getKnobs([vt_init_key])[vt_init_key] + init_metric = scipy.mean(data.value) + self._viterbi_metric = 100*[init_metric,] + + table_col_labels = ('Num Packets', 'Error Rate', 'Packet Error Rate', + 'Viterbi Metric', 'SNR') + + self._fig = plt.figure(1, figsize=(12,12), facecolor='w') + self._sp0 = self._fig.add_subplot(4,1,1) + self._sp1 = self._fig.add_subplot(4,1,2) + self._sp2 = self._fig.add_subplot(4,1,3) + self._plot_taps = self._sp0.plot([], [], 'k', linewidth=2) + self._plot_psd = self._sp1.plot([], [], 'k', linewidth=2) + self._plot_data = self._sp2.plot([], [], 'ok', linewidth=2, markersize=4, alpha=0.05) + + self._ax2 = self._fig.add_subplot(4,1,4) + self._table = self._ax2.table(cellText=[len(table_col_labels)*['0']], + colLabels=table_col_labels, + loc='center') + self._ax2.axis('off') + cells = self._table.properties()['child_artists'] + for c in cells: + c.set_lw(0.1) # set's line width + c.set_ls('solid') + c.set_height(0.2) + + ani = animation.FuncAnimation(self._fig, self.update_data, frames=200, + fargs=(self._plot_taps[0], self._plot_psd[0], + self._plot_data[0], self._table), + init_func=self.init_function, + blit=True) + plt.show() + + def update_data(self, x, taps, psd, syms, table): + try: + eqdata_key = 'dtv_atsc_equalizer0::taps' + symdata_key = 'dtv_atsc_equalizer0::data' + rs_nump_key = 'dtv_atsc_rs_decoder0::num_packets' + rs_numbp_key = 'dtv_atsc_rs_decoder0::num_bad_packets' + rs_numerrs_key = 'dtv_atsc_rs_decoder0::num_errors_corrected' + vt_metrics_key = 'dtv_atsc_viterbi_decoder0::decoder_metrics' + snr_key = 'probe2_f0::SNR' + + data = self.radio.getKnobs([]) + eqdata = data[eqdata_key] + symdata = data[symdata_key] + rs_num_packets = data[rs_nump_key] + rs_num_bad_packets = data[rs_numbp_key] + rs_num_errors_corrected = data[rs_numerrs_key] + vt_decoder_metrics = data[vt_metrics_key] + snr_est = data[snr_key] + + vt_decoder_metrics = scipy.mean(vt_decoder_metrics.value) + self._viterbi_metric.pop() + self._viterbi_metric.insert(0, vt_decoder_metrics) + + except: + sys.stderr.write("Lost connection, exiting") + sys.exit(1) + + ntaps = len(eqdata.value) + taps.set_ydata(eqdata.value) + taps.set_xdata(xrange(ntaps)) + self._sp0.set_xlim(0, ntaps) + self._sp0.set_ylim(min(eqdata.value), max(eqdata.value)) + + fs = 6.25e6 + freq = scipy.linspace(-fs/2, fs/2, 10000) + H = fftpack.fftshift(fftpack.fft(eqdata.value, 10000)) + HdB = 20.0*scipy.log10(abs(H)) + psd.set_ydata(HdB) + psd.set_xdata(freq) + self._sp1.set_xlim(0, fs/2) + self._sp1.set_ylim([min(HdB), max(HdB)]) + self._sp1.set_yticks([min(HdB), max(HdB)]) + self._sp1.set_yticklabels(["min", "max"]) + + nsyms = len(symdata.value) + syms.set_ydata(symdata.value) + syms.set_xdata(nsyms*[0,]) + self._sp2.set_xlim([-1, 1]) + self._sp2.set_ylim([-10, 10]) + + per = float(rs_num_bad_packets.value) / float(rs_num_packets.value) + ber = float(rs_num_errors_corrected.value) / float(187*rs_num_packets.value) + + table._cells[(1,0)]._text.set_text("{0}".format(rs_num_packets.value)) + table._cells[(1,1)]._text.set_text("{0:.2g}".format(ber)) + table._cells[(1,2)]._text.set_text("{0:.2g}".format(per)) + table._cells[(1,3)]._text.set_text("{0:.1f}".format(scipy.mean(self._viterbi_metric))) + table._cells[(1,4)]._text.set_text("{0:.4f}".format(snr_est.value[0])) + + return (taps, psd, syms, table) + + def init_function(self): + return self._plot_taps + self._plot_psd + self._plot_data + +if __name__ == "__main__": + host = sys.argv[1] + port = sys.argv[2] + m = atsc_ctrlport_monitor(host, port) diff --git a/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h b/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h index 3fe101ac25..17f2e8f768 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h @@ -45,6 +45,9 @@ namespace gr { * \brief Make a new instance of gr::dtv::atsc_equalizer. */ static sptr make(); + + virtual std::vector<float> taps() const = 0; + virtual std::vector<float> data() const = 0; }; } /* namespace dtv */ diff --git a/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h b/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h index 92eb263e5d..c2b7d0defa 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h @@ -42,6 +42,21 @@ namespace gr { typedef boost::shared_ptr<atsc_rs_decoder> sptr; /*! + * Returns the number of errors corrected by the decoder. + */ + virtual int num_errors_corrected() const = 0; + + /*! + * Returns the number of bad packets rejected by the decoder. + */ + virtual int num_bad_packets() const = 0; + + /*! + * Returns the total number of packets seen by the decoder. + */ + virtual int num_packets() const = 0; + + /*! * \brief Make a new instance of gr::dtv::atsc_rs_decoder. */ static sptr make(); diff --git a/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h b/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h index a4c18c39da..8efece4c43 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h @@ -45,6 +45,12 @@ namespace gr { * \brief Make a new instance of gr::dtv::atsc_viterbi_decoder. */ static sptr make(); + + /*! + * For each decoder, returns the current best state of the + * decoding metrics. + */ + virtual std::vector<float> decoder_metrics() const = 0; }; } /* namespace dtv */ diff --git a/gr-dtv/lib/atsc/atsc_equalizer_impl.cc b/gr-dtv/lib/atsc/atsc_equalizer_impl.cc index c804be6dc7..de01cea6f2 100644 --- a/gr-dtv/lib/atsc/atsc_equalizer_impl.cc +++ b/gr-dtv/lib/atsc/atsc_equalizer_impl.cc @@ -28,6 +28,7 @@ #include "atsc_types.h" #include "atsc_pnXXX_impl.h" #include <gnuradio/io_signature.h> +#include <volk/volk.h> namespace gr { namespace dtv { @@ -76,23 +77,42 @@ namespace gr { init_field_sync_common(training_sequence1, 0); init_field_sync_common(training_sequence2, 1); - for (int i = 0; i < NTAPS; i++) - d_taps[i] = 0.0; + d_taps.resize(NTAPS, 0.0f); d_buff_not_filled = true; + + const int alignment_multiple = + volk_get_alignment() / sizeof(float); + set_alignment(std::max(1, alignment_multiple)); } atsc_equalizer_impl::~atsc_equalizer_impl() { } + std::vector<float> + atsc_equalizer_impl::taps() const + { + return d_taps; + } + + std::vector<float> + atsc_equalizer_impl::data() const + { + std::vector<float> ret(&data_mem2[0], &data_mem2[ATSC_DATA_SEGMENT_LENGTH-1]); + return ret; + } + void - atsc_equalizer_impl::filterN(const float *input_samples, float *output_samples, int nsamples) + atsc_equalizer_impl::filterN(const float *input_samples, + float *output_samples, + int nsamples) { for (int j = 0; j < nsamples; j++) { output_samples[j] = 0; - for(int i = 0; i < NTAPS; i++) - output_samples[j] += d_taps[i] * input_samples[j + i]; + volk_32f_x2_dot_prod_32f(&output_samples[j], + &input_samples[j], + &d_taps[0], NTAPS); } } @@ -107,14 +127,16 @@ namespace gr { for(int j = 0; j < nsamples; j++) { output_samples[j] = 0; - for( int i = 0; i < NTAPS; i++ ) - output_samples[j] += d_taps[i] * input_samples[j + i]; + volk_32f_x2_dot_prod_32f(&output_samples[j], + &input_samples[j], + &d_taps[0], NTAPS); - double e = output_samples[j] - training_pattern[j]; + float e = output_samples[j] - training_pattern[j]; // update taps... - for( int i = 0; i < NTAPS; i++ ) - d_taps[i] -= BETA * e * (double)(input_samples[j + i]); + float tmp_taps[NTAPS]; + volk_32f_s32f_multiply_32f(tmp_taps, &input_samples[j], BETA*e, NTAPS); + volk_32f_x2_subtract_32f(&d_taps[0], &d_taps[0], tmp_taps, NTAPS); } } @@ -131,8 +153,8 @@ namespace gr { int i = 0; if(d_buff_not_filled) { - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - data_mem[NPRETAPS + j] = in[i].data[j]; + memcpy(&data_mem[NPRETAPS], in[i].data, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); d_flags = in[i].pli._flags; d_segno = in[i].pli._segno; d_buff_not_filled = false; @@ -141,8 +163,8 @@ namespace gr { for (; i < noutput_items; i++) { - for(int j = 0; j < NTAPS - NPRETAPS; j++) - data_mem[ATSC_DATA_SEGMENT_LENGTH + NPRETAPS + j] = in[i].data[j]; + memcpy(&data_mem[ATSC_DATA_SEGMENT_LENGTH + NPRETAPS], in[i].data, + (NTAPS - NPRETAPS)*sizeof(float)); if(d_segno == -1) { if(d_flags & 0x0010) { @@ -157,19 +179,18 @@ namespace gr { else { filterN(data_mem, data_mem2, ATSC_DATA_SEGMENT_LENGTH); - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - out[output_produced].data[j] = data_mem2[j]; + memcpy(out[output_produced].data, data_mem2, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); out[output_produced].pli._flags = d_flags; out[output_produced].pli._segno = d_segno; output_produced++; } - for( int j = 0; j < NPRETAPS; j++ ) - data_mem[j] = data_mem[ATSC_DATA_SEGMENT_LENGTH + j]; - - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - data_mem[NPRETAPS + j] = in[i].data[j]; + memcpy(data_mem, &data_mem[ATSC_DATA_SEGMENT_LENGTH], + NPRETAPS*sizeof(float)); + memcpy(&data_mem[NPRETAPS], in[i].data, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); d_flags = in[i].pli._flags; d_segno = in[i].pli._segno; @@ -179,5 +200,31 @@ namespace gr { return output_produced; } + void + atsc_equalizer_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float> >( + alias(), "taps", + &atsc_equalizer::taps, + pmt::make_f32vector(1,-10), + pmt::make_f32vector(1,10), + pmt::make_f32vector(1,0), + "", "Equalizer Taps", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float> >( + alias(), "data", + &atsc_equalizer::data, + pmt::make_f32vector(1,-10), + pmt::make_f32vector(1,10), + pmt::make_f32vector(1,0), + "", "Post-equalizer Data", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_equalizer_impl.h b/gr-dtv/lib/atsc/atsc_equalizer_impl.h index 6ff89ca646..75862f6408 100644 --- a/gr-dtv/lib/atsc/atsc_equalizer_impl.h +++ b/gr-dtv/lib/atsc/atsc_equalizer_impl.h @@ -46,7 +46,7 @@ namespace gr { void adaptN(const float *input_samples, const float *training_pattern, float *output_samples, int nsamples); - float d_taps[NTAPS]; + std::vector<float> d_taps; float data_mem[ATSC_DATA_SEGMENT_LENGTH + NTAPS]; // Buffer for previous data packet float data_mem2[ATSC_DATA_SEGMENT_LENGTH]; @@ -59,6 +59,11 @@ namespace gr { atsc_equalizer_impl(); ~atsc_equalizer_impl(); + void setup_rpc(); + + std::vector<float> taps() const; + std::vector<float> data() const; + virtual int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, diff --git a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc index 7a950e716e..b8ee91a52d 100644 --- a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc +++ b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc @@ -56,9 +56,9 @@ namespace gr { { d_rs = init_rs_char(rs_init_symsize, rs_init_gfpoly, rs_init_fcr, rs_init_prim, rs_init_nroots); assert (d_rs != 0); - nerrors_corrrected_count = 0; - bad_packet_count = 0; - total_packets = 0; + d_nerrors_corrrected_count = 0; + d_bad_packet_count = 0; + d_total_packets = 0; } int atsc_rs_decoder_impl::decode (atsc_mpeg_packet_no_sync &out, const atsc_mpeg_packet_rs_encoded &in) @@ -89,6 +89,24 @@ namespace gr { } int + atsc_rs_decoder_impl::num_errors_corrected() const + { + return d_nerrors_corrrected_count; + } + + int + atsc_rs_decoder_impl::num_bad_packets() const + { + return d_bad_packet_count; + } + + int + atsc_rs_decoder_impl::num_packets() const + { + return d_total_packets; + } + + int atsc_rs_decoder_impl::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) @@ -102,24 +120,20 @@ namespace gr { int nerrors_corrrected = decode(out[i], in[i]); out[i].pli.set_transport_error(nerrors_corrrected == -1); - if (nerrors_corrrected == -1) - bad_packet_count++; - else - nerrors_corrrected_count += nerrors_corrrected; + if (nerrors_corrrected == -1) { + d_bad_packet_count++; + d_nerrors_corrrected_count += 10; // lower bound estimate; most this RS can fix + } + else { + d_nerrors_corrrected_count += nerrors_corrrected; + } - total_packets++; + d_total_packets++; #if 0 - if (total_packets > 1000) { - // FIXME: convert to logger - std::cout << "Error rate: " - << (float)nerrors_corrrected_count/total_packets - << "\tPacket error rate: " - << (float)bad_packet_count/total_packets - << std::endl; - - nerrors_corrrected_count = 0; - bad_packet_count = 0; - total_packets = 0; + if (d_total_packets > 1000) { + GR_LOG_INFO(d_logger, boost::format("Error rate: %1%\tPacket error rate: %2%") \ + % ((float)d_nerrors_corrrected_count/(ATSC_MPEG_DATA_LENGTH*d_total_packets)) + % ((float)d_bad_packet_count/d_total_packets)); } #endif } @@ -127,5 +141,42 @@ namespace gr { return noutput_items; } + + void + atsc_rs_decoder_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_errors_corrected", + &atsc_rs_decoder::num_errors_corrected, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of errors corrected", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_bad_packets", + &atsc_rs_decoder::num_bad_packets, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of bad packets", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_packets", + &atsc_rs_decoder::num_packets, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of packets", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h index 57460128dc..adbc4879a9 100644 --- a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h +++ b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h @@ -36,15 +36,22 @@ namespace gr { class atsc_rs_decoder_impl : public atsc_rs_decoder { private: - int nerrors_corrrected_count; - int bad_packet_count; - int total_packets; + int d_nerrors_corrrected_count; + int d_bad_packet_count; + int d_total_packets; + int d_total_bits; void *d_rs; public: atsc_rs_decoder_impl(); ~atsc_rs_decoder_impl(); + void setup_rpc(); + + int num_errors_corrected() const; + int num_bad_packets() const; + int num_packets() const; + /*! * Decode RS encoded packet. * \returns a count of corrected symbols, or -1 if the block was uncorrectible. diff --git a/gr-dtv/lib/atsc/atsc_single_viterbi.cc b/gr-dtv/lib/atsc/atsc_single_viterbi.cc index 385940e453..011cc0fe96 100644 --- a/gr-dtv/lib/atsc/atsc_single_viterbi.cc +++ b/gr-dtv/lib/atsc/atsc_single_viterbi.cc @@ -28,7 +28,7 @@ namespace gr { /* was_sent is a table of what symbol we get given what bit pair was sent and what state we where in [state][pair] */ - const int atsc_single_viterbi::was_sent[4][4] = { + const int atsc_single_viterbi::d_was_sent[4][4] = { {0,2,4,6}, {0,2,4,6}, {1,3,5,7}, @@ -37,7 +37,7 @@ namespace gr { /* transition_table is a table of what state we were in given current state and bit pair sent [state][pair] */ - const int atsc_single_viterbi::transition_table[4][4] = { + const int atsc_single_viterbi::d_transition_table[4][4] = { {0,2,0,2}, {2,0,2,0}, {1,3,1,3}, @@ -49,11 +49,12 @@ namespace gr { { for (unsigned int i = 0; i<2; i++) for (unsigned int j = 0; j<4; j++) { - path_metrics[i][j] = 0; - traceback[i][j] = 0; + d_path_metrics[i][j] = 0; + d_traceback[i][j] = 0; } - post_coder_state = 0; - phase = 0; + d_post_coder_state = 0; + d_phase = 0; + d_best_state_metric = 100000; } atsc_single_viterbi::atsc_single_viterbi() @@ -61,17 +62,24 @@ namespace gr { reset(); } + float + atsc_single_viterbi::best_state_metric() const + { + return d_best_state_metric; + } + char atsc_single_viterbi::decode(float input) { unsigned int best_state = 0; - float best_state_metric = 100000; + //float best_state_metric = 100000; + d_best_state_metric = 100000; /* Precompute distances from input to each possible symbol */ - float distances[8] = { (float)fabs( input + 7 ), (float)fabs( input + 5 ), - (float)fabs( input + 3 ), (float)fabs( input + 1 ), - (float)fabs( input - 1 ), (float)fabs( input - 3 ), - (float)fabs( input - 5 ), (float)fabs( input - 7 ) }; + float distances[8] = { fabsf( input + 7 ), fabsf( input + 5 ), + fabsf( input + 3 ), fabsf( input + 1 ), + fabsf( input - 1 ), fabsf( input - 3 ), + fabsf( input - 5 ), fabsf( input - 7 ) }; /* We start by iterating over all possible states */ for (unsigned int state = 0; state < 4; state++) { @@ -79,15 +87,20 @@ namespace gr { states to the state we are testing, we only need to look at the 4 paths that can be taken given the 2-bit input */ int min_metric_symb = 0; - float min_metric = distances[was_sent[state][0]] + path_metrics[phase][transition_table[state][0]]; + float min_metric = distances[d_was_sent[state][0]] + + d_path_metrics[d_phase][d_transition_table[state][0]]; + for (unsigned int symbol_sent = 1; symbol_sent < 4; symbol_sent++) - if( (distances[was_sent[state][symbol_sent]] + path_metrics[phase][transition_table[state][symbol_sent]]) < min_metric) { - min_metric = distances[was_sent[state][symbol_sent]] + path_metrics[phase][transition_table[state][symbol_sent]]; + if( (distances[d_was_sent[state][symbol_sent]] + + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]) < min_metric) { + min_metric = distances[d_was_sent[state][symbol_sent]] + + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]; min_metric_symb = symbol_sent; } - path_metrics[phase^1][state] = min_metric; - traceback[phase^1][state] = (((unsigned long long)min_metric_symb) << 62) | (traceback[phase][transition_table[state][min_metric_symb]] >> 2); + d_path_metrics[d_phase^1][state] = min_metric; + d_traceback[d_phase^1][state] = (((unsigned long long)min_metric_symb) << 62) | + (d_traceback[d_phase][d_transition_table[state][min_metric_symb]] >> 2); /* If this is the most probable state so far remember it, this only needs to be checked when we are about to output a path @@ -98,26 +111,24 @@ namespace gr { head state path will tend towards the optimal path with a probability approaching 1 in just 8 or so transitions */ - if(min_metric <= best_state_metric) { - best_state_metric = min_metric; + if(min_metric <= d_best_state_metric) { + d_best_state_metric = min_metric; best_state = state; } } - if(best_state_metric > 10000) { + if(d_best_state_metric > 10000) { for(unsigned int state = 0; state < 4; state++) - path_metrics[phase^1][state] -= best_state_metric; + d_path_metrics[d_phase^1][state] -= d_best_state_metric; } - phase ^= 1; + d_phase ^= 1; - int y2 = (0x2 & traceback[phase][best_state]) >> 1; - int x2 = y2 ^ post_coder_state; - post_coder_state = y2; + int y2 = (0x2 & d_traceback[d_phase][best_state]) >> 1; + int x2 = y2 ^ d_post_coder_state; + d_post_coder_state = y2; - return ( x2 << 1 ) | (0x1 & traceback[phase][best_state]); + return ( x2 << 1 ) | (0x1 & d_traceback[d_phase][best_state]); } } /* namespace dtv */ } /* namespace gr */ - - diff --git a/gr-dtv/lib/atsc/atsc_single_viterbi.h b/gr-dtv/lib/atsc/atsc_single_viterbi.h index 3c756c7690..9522e2eb1b 100644 --- a/gr-dtv/lib/atsc/atsc_single_viterbi.h +++ b/gr-dtv/lib/atsc/atsc_single_viterbi.h @@ -44,14 +44,17 @@ namespace gr { //! internal delay of decoder static int delay () { return TB_LEN - 1; } - protected: - static const int transition_table[4][4]; - static const int was_sent[4][4]; + float best_state_metric() const; - float path_metrics [2][4]; - unsigned long long traceback [2][4]; - unsigned char phase; - int post_coder_state; + protected: + static const int d_transition_table[4][4]; + static const int d_was_sent[4][4]; + + float d_path_metrics [2][4]; + unsigned long long d_traceback [2][4]; + unsigned char d_phase; + int d_post_coder_state; + float d_best_state_metric; }; } /* namespace dtv */ diff --git a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc index 430eb55a17..2284372dfb 100644 --- a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc +++ b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc @@ -77,6 +77,15 @@ namespace gr { fifo[i]->reset(); } + std::vector<float> + atsc_viterbi_decoder_impl::decoder_metrics() const + { + std::vector<float> metrics(NCODERS); + for (int i = 0; i < NCODERS; i++) + metrics[i] = viterbi[i].best_state_metric(); + return metrics; + } + int atsc_viterbi_decoder_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -135,5 +144,21 @@ namespace gr { return noutput_items; } + void + atsc_viterbi_decoder_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_viterbi_decoder, std::vector<float> >( + alias(), "decoder_metrics", + &atsc_viterbi_decoder::decoder_metrics, + pmt::make_f32vector(1,0), + pmt::make_f32vector(1,100000), + pmt::make_f32vector(1,0), + "", "Viterbi decoder metrics", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h index b4fbbd1033..ef4faab313 100644 --- a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h +++ b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h @@ -63,8 +63,12 @@ namespace gr { atsc_viterbi_decoder_impl(); ~atsc_viterbi_decoder_impl(); + void setup_rpc(); + void reset(); + std::vector<float> decoder_metrics() const; + virtual int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); diff --git a/gr-uhd/include/gnuradio/uhd/usrp_source.h b/gr-uhd/include/gnuradio/uhd/usrp_source.h index 19b3feb61f..19201c031c 100644 --- a/gr-uhd/include/gnuradio/uhd/usrp_source.h +++ b/gr-uhd/include/gnuradio/uhd/usrp_source.h @@ -96,10 +96,13 @@ namespace gr { /*! * \param device_addr the address to identify the hardware * \param stream_args the IO format and channel specification + * \param issue_stream_cmd_on_start enable or disable continuous streaming when flowgraph + * starts (default true) * \return a new USRP source block object */ static sptr make(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args); + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start = true); /*! * Set the start time for incoming samples. diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index 53050051f9..e2e4d334b6 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -321,7 +321,8 @@ namespace gr { { _update_stream_args(stream_args); #ifdef GR_UHD_USE_STREAM_API - _tx_stream.reset(); + if(_tx_stream) + _tx_stream.reset(); #else throw std::runtime_error("not implemented in this version"); #endif @@ -609,7 +610,8 @@ namespace gr { _nitems_to_send = 0; #ifdef GR_UHD_USE_STREAM_API - _tx_stream->send(gr_vector_const_void_star(_nchan), 0, _metadata, 1.0); + if(_tx_stream) + _tx_stream->send(gr_vector_const_void_star(_nchan), 0, _metadata, 1.0); #else _dev->get_device()->send (gr_vector_const_void_star(_nchan), 0, _metadata, diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc index fbeb992192..7ce8633597 100644 --- a/gr-uhd/lib/usrp_source_impl.cc +++ b/gr-uhd/lib/usrp_source_impl.cc @@ -53,20 +53,23 @@ namespace gr { usrp_source::sptr usrp_source::make(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args) + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start) { check_abi(); return usrp_source::sptr - (new usrp_source_impl(device_addr, stream_args_ensure(stream_args))); + (new usrp_source_impl(device_addr, stream_args_ensure(stream_args), issue_stream_cmd_on_start)); } usrp_source_impl::usrp_source_impl(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args): + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start): usrp_block("usrp_source", io_signature::make(0, 0, 0), args_to_io_sig(stream_args)), usrp_block_impl(device_addr, stream_args, ""), - _tag_now(false) + _tag_now(false), + _issue_stream_cmd_on_start(issue_stream_cmd_on_start) { std::stringstream str; str << name() << unique_id(); @@ -348,7 +351,8 @@ namespace gr { { _update_stream_args(stream_args); #ifdef GR_UHD_USE_STREAM_API - _rx_stream.reset(); + if(_rx_stream) + _rx_stream.reset(); #else throw std::runtime_error("not implemented in this version"); #endif @@ -381,18 +385,20 @@ namespace gr { _samps_per_packet = _rx_stream->get_max_num_samps(); } #endif - //setup a stream command that starts streaming slightly in the future - static const double reasonable_delay = 0.1; //order of magnitude over RTT - ::uhd::stream_cmd_t stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - stream_cmd.stream_now = _stream_now; - if(_start_time_set) { - _start_time_set = false; //cleared for next run - stream_cmd.time_spec = _start_time; - } - else { - stream_cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); + if(_issue_stream_cmd_on_start){ + //setup a stream command that starts streaming slightly in the future + static const double reasonable_delay = 0.1; //order of magnitude over RTT + ::uhd::stream_cmd_t stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.stream_now = _stream_now; + if(_start_time_set) { + _start_time_set = false; //cleared for next run + stream_cmd.time_spec = _start_time; + } + else { + stream_cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); + } + this->issue_stream_cmd(stream_cmd); } - this->issue_stream_cmd(stream_cmd); _tag_now = true; return true; } @@ -409,7 +415,12 @@ namespace gr { while(true) { #ifdef GR_UHD_USE_STREAM_API const size_t bpi = ::uhd::convert::get_bytes_per_item(_stream_args.cpu_format); - _rx_stream->recv(outputs, nbytes/bpi, _metadata, 0.0); + if(_rx_stream) + // get the remaining samples out of the buffers + _rx_stream->recv(outputs, nbytes/bpi, _metadata, 0.0); + else + // no rx streamer -- nothing to flush + break; #else _dev->get_device()->recv (outputs, nbytes/_type->size, _metadata, *_type, diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h index 98af816c02..b43df4dab3 100644 --- a/gr-uhd/lib/usrp_source_impl.h +++ b/gr-uhd/lib/usrp_source_impl.h @@ -55,7 +55,8 @@ namespace gr { { public: usrp_source_impl(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args); + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start = true); ~usrp_source_impl(); // Get Commands @@ -119,6 +120,7 @@ namespace gr { bool _tag_now; ::uhd::rx_metadata_t _metadata; pmt::pmt_t _id; + bool _issue_stream_cmd_on_start; //tag shadows double _samp_rate; |