diff options
Diffstat (limited to 'gr-digital')
57 files changed, 4543 insertions, 98 deletions
diff --git a/gr-digital/Makefile.am b/gr-digital/Makefile.am index 62c40f2df..f1409793f 100644 --- a/gr-digital/Makefile.am +++ b/gr-digital/Makefile.am @@ -24,7 +24,7 @@ include $(top_srcdir)/Makefile.common SUBDIRS = lib if PYTHON -SUBDIRS += swig python apps grc +SUBDIRS += swig python apps grc examples endif pkgconfigdir = $(libdir)/pkgconfig diff --git a/gr-digital/examples/.gitignore b/gr-digital/examples/.gitignore new file mode 100644 index 000000000..b336cc7ce --- /dev/null +++ b/gr-digital/examples/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/gr-digital/examples/Makefile.am b/gr-digital/examples/Makefile.am new file mode 100644 index 000000000..8ed53fe41 --- /dev/null +++ b/gr-digital/examples/Makefile.am @@ -0,0 +1,30 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +ourdatadir = $(exampledir)/digital + +dist_ourdata_SCRIPTS = \ + transmit_path.py \ + receive_path.py + + diff --git a/gr-digital/examples/benchmark_rx.py b/gr-digital/examples/benchmark_rx.py new file mode 100755 index 000000000..9d8734e3d --- /dev/null +++ b/gr-digital/examples/benchmark_rx.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright 2010,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, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# From gr-digital +from gnuradio import digital + +# from current dir +from receive_path import receive_path + +import random +import struct +import sys + +#import os +#print os.getpid() +#raw_input('Attach and press enter: ') + +class my_top_block(gr.top_block): + def __init__(self, demodulator, rx_callback, options): + gr.top_block.__init__(self) + + # Set up receive path + self.rxpath = receive_path(demodulator, rx_callback, options) + + if(options.from_file is not None): + self.source = gr.file_source(gr.sizeof_gr_complex, options.from_file) + else: + self.source = gr.null_source(gr.sizeof_gr_complex) + + self.connect(self.source, self.rxpath) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +global n_rcvd, n_right + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def rx_callback(ok, payload): + global n_rcvd, n_right + (pktno,) = struct.unpack('!H', payload[0:2]) + n_rcvd += 1 + if ok: + n_right += 1 + + print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + demods = digital.modulation_utils2.type_1_demods() + + # Create Options Parser: + parser = OptionParser (option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(demods.keys()),)) + parser.add_option("","--from-file", default=None, + help="input file of samples to demod") + + receive_path.add_options(parser, expert_grp) + + for mod in demods.values(): + mod.add_options(expert_grp) + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help(sys.stderr) + sys.exit(1) + + if options.from_file is None: + if options.rx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + + # build the graph + tb = my_top_block(demods[options.modulation], rx_callback, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/benchmark_tx.py b/gr-digital/examples/benchmark_tx.py new file mode 100755 index 000000000..01902c0e3 --- /dev/null +++ b/gr-digital/examples/benchmark_tx.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# +# Copyright 2010 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, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# From gr-digital +from gnuradio import digital + +# from current dir +from transmit_path import transmit_path + +import random, time, struct, sys + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + +class my_top_block(gr.top_block): + def __init__(self, modulator, options): + gr.top_block.__init__(self) + + self.txpath = transmit_path(modulator, options) + + if(options.to_file is not None): + self.sink = gr.file_sink(gr.sizeof_gr_complex, options.to_file) + else: + self.sink = gr.null_sink(gr.sizeof_gr_complex) + + self.connect(self.txpath, self.sink) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return tb.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + mods = digital.modulation_utils2.type_1_mods() + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(mods.keys()),)) + + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + parser.add_option("","--from-file", default=None, + help="use intput file for packet contents") + parser.add_option("","--to-file", default=None, + help="Output file for modulated samples") + + transmit_path.add_options(parser, expert_grp) + + for mod in mods.values(): + mod.add_options(expert_grp) + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.to_file is None: + if options.tx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + if options.from_file is not None: + source_file = open(options.from_file, 'r') + + # build the graph + tb = my_top_block(mods[options.modulation], options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + tb.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + pkt_size = int(options.size) + + while n < nbytes: + if options.from_file is None: + data = (pkt_size - 2) * chr(pktno & 0xff) + else: + data = source_file.read(pkt_size - 2) + if data == '': + break; + + payload = struct.pack('!H', pktno & 0xffff) + data + send_pkt(payload) + n += len(payload) + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/receive_path.py b/gr-digital/examples/receive_path.py new file mode 100644 index 000000000..9bc5f7b8f --- /dev/null +++ b/gr-digital/examples/receive_path.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2007 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, gru +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block2): + def __init__(self, demod_class, rx_callback, options): + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._bitrate = options.bitrate # desired bit rate + self._samples_per_symbol = options.samples_per_symbol # desired samples/symbol + + self._rx_callback = rx_callback # this callback is fired when there's a packet available + self._demod_class = demod_class # the demodulator_class we're using + + # Get demod_kwargs + demod_kwargs = self._demod_class.extract_kwargs_from_options(options) + + # Design filter to get actual channel we want + sw_decim = 1 + chan_coeffs = gr.firdes.low_pass (1.0, # gain + sw_decim * self._samples_per_symbol, # sampling rate + 1.0, # midpoint of trans. band + 0.5, # width of trans. band + gr.firdes.WIN_HANN) # filter type + self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) + + # receiver + self.packet_receiver = \ + digital.demod_pkts(self._demod_class(**demod_kwargs), + access_code=None, + callback=self._rx_callback, + threshold=-1) + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + # connect block input to channel filter + self.connect(self, self.channel_filter) + + # connect the channel input filter to the carrier power detector + self.connect(self.channel_filter, self.probe) + + # connect channel filter to the packet receiver + self.connect(self.channel_filter, self.packet_receiver) + + def bitrate(self): + return self._bitrate + + def samples_per_symbol(self): + return self._samples_per_symbol + + def carrier_sensed(self): + """ + Return True if we think carrier is present. + """ + #return self.probe.level() > X + return self.probe.unmuted() + + def carrier_threshold(self): + """ + Return current setting in dB. + """ + return self.probe.threshold() + + def set_carrier_threshold(self, threshold_in_db): + """ + Set carrier threshold. + + @param threshold_in_db: set detection threshold + @type threshold_in_db: float (dB) + """ + self.probe.set_threshold(threshold_in_db) + + + def add_options(normal, expert): + """ + Adds receiver-specific options to the Options Parser + """ + if not normal.has_option("--bitrate"): + normal.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate [default=%default].") + normal.add_option("-v", "--verbose", action="store_true", default=False) + expert.add_option("-S", "--samples-per-symbol", type="float", default=None, + help="set samples/symbol [default=%default]") + expert.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to files (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + + def _print_verbage(self): + """ + Prints information about the receive path + """ + print "\nReceive Path:" + print "modulation: %s" % (self._demod_class.__name__) + print "bitrate: %sb/s" % (eng_notation.num_to_str(self._bitrate)) + print "samples/symbol: %.4f" % (self._samples_per_symbol) diff --git a/gr-digital/examples/transmit_path.py b/gr-digital/examples/transmit_path.py new file mode 100644 index 000000000..91869ad9a --- /dev/null +++ b/gr-digital/examples/transmit_path.py @@ -0,0 +1,119 @@ +# +# Copyright 2005-2007,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, gru +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block2): + def __init__(self, modulator_class, options): + ''' + See below for what options should hold + ''' + gr.hier_block2.__init__(self, "transmit_path", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._tx_amplitude = options.tx_amplitude # digital amplitude sent to USRP + self._bitrate = options.bitrate # desired bit rate + self._samples_per_symbol = options.samples_per_symbol # desired samples/baud + + self._modulator_class = modulator_class # the modulator_class we are using + + # Get mod_kwargs + mod_kwargs = self._modulator_class.extract_kwargs_from_options(options) + + # transmitter + modulator = self._modulator_class(**mod_kwargs) + self.packet_transmitter = \ + digital.mod_pkts(modulator, + access_code=None, + msgq_limit=4, + pad_for_usrp=True) + + self.amp = gr.multiply_const_cc(1) + self.set_tx_amplitude(self._tx_amplitude) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + # Connect components in the flowgraph + self.connect(self.packet_transmitter, self.amp, self) + + def set_tx_amplitude(self, ampl): + """ + Sets the transmit amplitude sent to the USRP in volts + @param: ampl 0 <= ampl < 1. + """ + self._tx_amplitude = max(0.0, min(ampl, 1)) + self.amp.set_k(self._tx_amplitude) + + def send_pkt(self, payload='', eof=False): + """ + Calls the transmitter method to send a packet + """ + return self.packet_transmitter.send_pkt(payload, eof) + + def bitrate(self): + return self._bitrate + + def samples_per_symbol(self): + return self._samples_per_symbol + + def add_options(normal, expert): + """ + Adds transmitter-specific options to the Options Parser + """ + if not normal.has_option('--bitrate'): + normal.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate [default=%default].") + normal.add_option("", "--tx-amplitude", type="eng_float", default=0.250, metavar="AMPL", + help="set transmitter digital amplitude: 0 <= AMPL < 1 [default=%default]") + normal.add_option("-v", "--verbose", action="store_true", default=False) + + expert.add_option("-S", "--samples-per-symbol", type="float", default=None, + help="set samples/symbol [default=%default]") + expert.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to file (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the transmit path + """ + print "Tx amplitude %s" % (self._tx_amplitude) + print "modulation: %s" % (self._modulator_class.__name__) + print "bitrate: %sb/s" % (eng_notation.num_to_str(self._bitrate)) + print "samples/symbol: %.4f" % (self._samples_per_symbol) + diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt index 86e082b31..b7d93da6b 100644 --- a/gr-digital/lib/CMakeLists.txt +++ b/gr-digital/lib/CMakeLists.txt @@ -32,13 +32,19 @@ LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) # Setup library ######################################################################## LIST(APPEND gr_digital_sources + digital_binary_slicer_fb.cc + digital_clock_recovery_mm_cc.cc + digital_clock_recovery_mm_ff.cc digital_constellation.cc digital_constellation_receiver_cb.cc digital_constellation_decoder_cb.cc + digital_correlate_access_code_bb.cc digital_costas_loop_cc.cc digital_cma_equalizer_cc.cc + digital_crc32.cc digital_lms_dd_equalizer_cc.cc digital_kurtotic_equalizer_cc.cc + digital_mpsk_receiver_cc.cc ) LIST(APPEND digital_libs @@ -61,15 +67,20 @@ INSTALL(TARGETS gnuradio-digital # Install header files ######################################################################## INSTALL(FILES - digital_api.h + digital_binary_slicer_fb.h + digital_clock_recovery_mm_cc.h + digital_clock_recovery_mm_ff.h digital_constellation.h digital_constellation_receiver_cb.h digital_constellation_decoder_cb.h + digital_correlate_access_code_bb.h digital_costas_loop_cc.h digital_cma_equalizer_cc.h + digital_crc32.h digital_lms_dd_equalizer_cc.h digital_kurtotic_equalizer_cc.h digital_metric_type.h + digital_mpsk_receiver_cc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio COMPONENT "digital_devel" ) diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am index 1cdb27a38..6b14988ac 100644 --- a/gr-digital/lib/Makefile.am +++ b/gr-digital/lib/Makefile.am @@ -26,25 +26,37 @@ AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) # These headers get installed in ${prefix}/include/gnuradio grinclude_HEADERS = \ digital_api.h \ + digital_binary_slicer_fb.h \ + digital_clock_recovery_mm_cc.h \ + digital_clock_recovery_mm_ff.h \ digital_constellation.h \ digital_constellation_receiver_cb.h \ digital_constellation_decoder_cb.h \ + digital_correlate_access_code_bb.h \ digital_costas_loop_cc.h \ digital_cma_equalizer_cc.h \ + digital_crc32.h \ digital_lms_dd_equalizer_cc.h \ digital_kurtotic_equalizer_cc.h \ - digital_metric_type.h + digital_metric_type.h \ + digital_mpsk_receiver_cc.h lib_LTLIBRARIES = libgnuradio-digital.la libgnuradio_digital_la_SOURCES = \ + digital_binary_slicer_fb.cc \ + digital_clock_recovery_mm_cc.cc \ + digital_clock_recovery_mm_ff.cc \ digital_constellation.cc \ digital_constellation_receiver_cb.cc \ digital_constellation_decoder_cb.cc \ + digital_correlate_access_code_bb.cc \ digital_costas_loop_cc.cc \ digital_cma_equalizer_cc.cc \ + digital_crc32.cc \ digital_lms_dd_equalizer_cc.cc \ - digital_kurtotic_equalizer_cc.cc + digital_kurtotic_equalizer_cc.cc \ + digital_mpsk_receiver_cc.cc libgnuradio_digital_la_LIBADD = \ $(GNURADIO_CORE_LA) diff --git a/gr-digital/lib/digital_binary_slicer_fb.cc b/gr-digital/lib/digital_binary_slicer_fb.cc new file mode 100644 index 000000000..fcdb4291f --- /dev/null +++ b/gr-digital/lib/digital_binary_slicer_fb.cc @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2010,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_binary_slicer_fb.h> +#include <gr_io_signature.h> +#include <gr_math.h> +#include <stdexcept> + +digital_binary_slicer_fb_sptr +digital_make_binary_slicer_fb () +{ + return gnuradio::get_initial_sptr(new digital_binary_slicer_fb ()); +} + +digital_binary_slicer_fb::digital_binary_slicer_fb () + : gr_sync_block ("binary_slicer_fb", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (unsigned char))) +{ +} + +int +digital_binary_slicer_fb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + + for (int i = 0; i < noutput_items; i++){ + out[i] = gr_binary_slicer(in[i]); + } + + return noutput_items; +} diff --git a/gr-digital/lib/digital_binary_slicer_fb.h b/gr-digital/lib/digital_binary_slicer_fb.h new file mode 100644 index 000000000..9da776012 --- /dev/null +++ b/gr-digital/lib/digital_binary_slicer_fb.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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_BINARY_SLICER_FB_H +#define INCLUDED_DIGITAL_BINARY_SLICER_FB_H + +#include <digital_api.h> +#include <gr_sync_block.h> + +class digital_binary_slicer_fb; +typedef boost::shared_ptr<digital_binary_slicer_fb> digital_binary_slicer_fb_sptr; + +DIGITAL_API digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + +/*! + * \brief slice float binary symbol outputting 1 bit output + * \ingroup converter_blk + * + * x < 0 --> 0 + * x >= 0 --> 1 + */ +class DIGITAL_API digital_binary_slicer_fb : public gr_sync_block +{ + friend DIGITAL_API digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + digital_binary_slicer_fb (); + + public: + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/lib/digital_clock_recovery_mm_cc.cc b/gr-digital/lib/digital_clock_recovery_mm_cc.cc new file mode 100644 index 000000000..2984afd6c --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_cc.cc @@ -0,0 +1,217 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2010,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 <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_clock_recovery_mm_cc.h> +#include <gri_mmse_fir_interpolator_cc.h> +#include <stdexcept> +#include <cstdio> + + +// Public constructor +static const int FUDGE = 16; + +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_cc (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_cc::digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 2, sizeof (gr_complex))), + d_mu (mu), d_omega(omega), d_gain_omega(gain_omega), + d_omega_relative_limit(omega_relative_limit), + d_gain_mu(gain_mu), d_last_sample(0), d_interp(new gri_mmse_fir_interpolator_cc()), + d_verbose(gr_prefs::singleton()->get_bool("clock_recovery_mm_cc", "verbose", false)), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + set_history(3); // ensure 2 extra input sample is available +} + +digital_clock_recovery_mm_cc::~digital_clock_recovery_mm_cc () +{ + delete d_interp; +} + +void +digital_clock_recovery_mm_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()) + FUDGE; +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_0deg (gr_complex sample) +{ + float real=0, imag=0; + + if(sample.real() > 0) + real = 1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_45deg (gr_complex sample) +{ + float real= -1, imag = -1; + if(sample.real() > 0) + real=1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +/* + Modified Mueller and Muller clock recovery circuit + Based: + G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. +*/ + +int +digital_clock_recovery_mm_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + gr_complex *foptr = (gr_complex *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps() - FUDGE; // don't use more input than this + + assert(d_mu >= 0.0); + assert(d_mu <= 1.0); + + float mm_val=0; + gr_complex u, x, y; + + // This loop writes the error to the second output, if it exists + if (write_foptr) { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,1.0); + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + // write the error signal to the second output + foptr[oo-1] = gr_complex(d_mu,0); + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + // This loop does not write to the second output (ugly, but faster) + else { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,1.0); + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + if(d_verbose) { + printf("%f\t%f\n", d_omega, d_mu); + } + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + + if (ii > 0){ + if (ii > ninput_items[0]){ + fprintf(stderr, "gr_clock_recovery_mm_cc: ii > ninput_items[0] (%d > %d)\n", + ii, ninput_items[0]); + assert(0); + } + consume_each (ii); + } + + return oo; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_cc.h b/gr-digital/lib/digital_clock_recovery_mm_cc.h new file mode 100644 index 000000000..e45b79229 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_cc.h @@ -0,0 +1,113 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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_CLOCK_RECOVERY_MM_CC_H +#define INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_CC_H + +#include <digital_api.h> +#include <gr_block.h> +#include <gr_complex.h> +#include <gr_math.h> + +class gri_mmse_fir_interpolator_cc; + +class digital_clock_recovery_mm_cc; +typedef boost::shared_ptr<digital_clock_recovery_mm_cc> digital_clock_recovery_mm_cc_sptr; + +// public constructor +DIGITAL_API digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001); + +/*! + * \brief Mueller and Müller (M&M) based clock recovery block with complex input, complex output. + * \ingroup sync_blk + * + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * The complex version here is based on: + * Modified Mueller and Muller clock recovery circuit + * Based: + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + * algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. + */ +class DIGITAL_API digital_clock_recovery_mm_cc : public gr_block +{ + public: + ~digital_clock_recovery_mm_cc (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + void set_verbose (bool verbose) { d_verbose = verbose; } + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + +protected: + digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limi); + + private: + float d_mu; + float d_omega; + float d_gain_omega; + float d_min_omega; // minimum allowed omega + float d_max_omega; // maximum allowed omeg + float d_omega_relative_limit; // used to compute min and max omega + float d_omega_mid; + float d_gain_mu; + gr_complex d_last_sample; + gri_mmse_fir_interpolator_cc *d_interp; + bool d_verbose; + + gr_complex d_p_2T; + gr_complex d_p_1T; + gr_complex d_p_0T; + + gr_complex d_c_2T; + gr_complex d_c_1T; + gr_complex d_c_0T; + + gr_complex slicer_0deg (gr_complex sample); + gr_complex slicer_45deg (gr_complex sample); + + friend DIGITAL_API digital_clock_recovery_mm_cc_sptr + digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); +}; + +#endif diff --git a/gr-digital/lib/digital_clock_recovery_mm_ff.cc b/gr-digital/lib/digital_clock_recovery_mm_ff.cc new file mode 100644 index 000000000..04057f0e9 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_ff.cc @@ -0,0 +1,139 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,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 <gr_io_signature.h> +#include <digital_clock_recovery_mm_ff.h> +#include <gri_mmse_fir_interpolator.h> +#include <stdexcept> + +#define DEBUG_CR_MM_FF 0 // must be defined as 0 or 1 + +// Public constructor + +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_ff (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_ff::digital_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_ff", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (float))), + d_mu (mu), d_gain_omega(gain_omega), d_gain_mu(gain_mu), + d_last_sample(0), d_interp(new gri_mmse_fir_interpolator()), + d_logfile(0), d_omega_relative_limit(omega_relative_limit) +{ + if (omega < 1) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + + if (DEBUG_CR_MM_FF) + d_logfile = fopen("cr_mm_ff.dat", "wb"); +} + +digital_clock_recovery_mm_ff::~digital_clock_recovery_mm_ff () +{ + delete d_interp; + + if (DEBUG_CR_MM_FF && d_logfile){ + fclose(d_logfile); + d_logfile = 0; + } +} + +void +digital_clock_recovery_mm_ff::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +static inline float +slice(float x) +{ + return x < 0 ? -1.0F : 1.0F; +} + +/* + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * + * See "Digital Communication Receivers: Synchronization, Channel + * Estimation and Signal Processing" by Heinrich Meyr, Marc Moeneclaey, & Stefan Fechtel. + * ISBN 0-471-50275-8. + */ +int +digital_clock_recovery_mm_ff::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps(); // don't use more input than this + float mm_val; + + while (oo < noutput_items && ii < ni ){ + + // produce output sample + out[oo] = d_interp->interpolate (&in[ii], d_mu); + mm_val = slice(d_last_sample) * out[oo] - slice(out[oo]) * d_last_sample; + d_last_sample = out[oo]; + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + + ii += (int) floor(d_mu); + d_mu = d_mu - floor(d_mu); + oo++; + + if (DEBUG_CR_MM_FF && d_logfile){ + fwrite(&d_omega, sizeof(d_omega), 1, d_logfile); + } + } + + consume_each (ii); + + return oo; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_ff.h b/gr-digital/lib/digital_clock_recovery_mm_ff.h new file mode 100644 index 000000000..6f88a4dcd --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_ff.h @@ -0,0 +1,99 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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_CLOCK_RECOVERY_MM_FF_H +#define INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_FF_H + +#include <digital_api.h> +#include <gr_block.h> +#include <gr_math.h> +#include <stdio.h> + +class gri_mmse_fir_interpolator; + +class digital_clock_recovery_mm_ff; +typedef boost::shared_ptr<digital_clock_recovery_mm_ff> digital_clock_recovery_mm_ff_sptr; + +// public constructor +DIGITAL_API digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001); + +/*! + * \brief Mueller and Müller (M&M) based clock recovery block with float input, float output. + * \ingroup sync_blk + * + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * + * See "Digital Communication Receivers: Synchronization, Channel + * Estimation and Signal Processing" by Heinrich Meyr, Marc Moeneclaey, & Stefan Fechtel. + * ISBN 0-471-50275-8. + */ +class DIGITAL_API digital_clock_recovery_mm_ff : public gr_block +{ + public: + ~digital_clock_recovery_mm_ff (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega){ + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + +protected: + digital_clock_recovery_mm_ff (float omega, float gain_omega, float mu, float gain_mu, + float omega_relative_limit); + + private: + float d_mu; // fractional sample position [0.0, 1.0] + float d_omega; // nominal frequency + float d_min_omega; // minimum allowed omega + float d_omega_mid; // average omega + float d_max_omega; // maximum allowed omega + float d_gain_omega; // gain for adjusting omega + float d_gain_mu; // gain for adjusting mu + float d_last_sample; + gri_mmse_fir_interpolator *d_interp; + FILE *d_logfile; + float d_omega_relative_limit; // used to compute min and max omega + + friend DIGITAL_API digital_clock_recovery_mm_ff_sptr + digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); +}; + +#endif diff --git a/gr-digital/lib/digital_correlate_access_code_bb.cc b/gr-digital/lib/digital_correlate_access_code_bb.cc new file mode 100644 index 000000000..f21b57d92 --- /dev/null +++ b/gr-digital/lib/digital_correlate_access_code_bb.cc @@ -0,0 +1,134 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,2010,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_correlate_access_code_bb.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <gr_count_bits.h> +#include <cstdio> + + +#define VERBOSE 0 + + +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold) +{ + return gnuradio::get_initial_sptr(new digital_correlate_access_code_bb + (access_code, threshold)); +} + + +digital_correlate_access_code_bb::digital_correlate_access_code_bb ( + const std::string &access_code, int threshold) + : gr_sync_block ("correlate_access_code_bb", + gr_make_io_signature (1, 1, sizeof(char)), + gr_make_io_signature (1, 1, sizeof(char))), + d_data_reg(0), d_flag_reg(0), d_flag_bit(0), d_mask(0), + d_threshold(threshold) + +{ + if (!set_access_code(access_code)){ + fprintf(stderr, "digital_correlate_access_code_bb: access_code is > 64 bits\n"); + throw std::out_of_range ("access_code is > 64 bits"); + } +} + +digital_correlate_access_code_bb::~digital_correlate_access_code_bb () +{ +} + +bool +digital_correlate_access_code_bb::set_access_code( + const std::string &access_code) +{ + unsigned len = access_code.length(); // # of bytes in string + if (len > 64) + return false; + + // set len top bits to 1. + d_mask = ((~0ULL) >> (64 - len)) << (64 - len); + + d_flag_bit = 1LL << (64 - len); // Where we or-in new flag values. + // new data always goes in 0x0000000000000001 + d_access_code = 0; + for (unsigned i=0; i < 64; i++){ + d_access_code <<= 1; + if (i < len) + d_access_code |= access_code[i] & 1; // look at LSB only + } + + return true; +} + +int +digital_correlate_access_code_bb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for (int i = 0; i < noutput_items; i++){ + + // compute output value + unsigned int t = 0; + + t |= ((d_data_reg >> 63) & 0x1) << 0; + t |= ((d_flag_reg >> 63) & 0x1) << 1; // flag bit + out[i] = t; + + // compute hamming distance between desired access code and current data + unsigned long long wrong_bits = 0; + unsigned int nwrong = d_threshold+1; + int new_flag = 0; + + wrong_bits = (d_data_reg ^ d_access_code) & d_mask; + nwrong = gr_count_bits64(wrong_bits); + + // test for access code with up to threshold errors + new_flag = (nwrong <= d_threshold); + +#if VERBOSE + if(new_flag) { + fprintf(stderr, "access code found: %llx\n", d_access_code); + } + else { + fprintf(stderr, "%llx ==> %llx\n", d_access_code, d_data_reg); + } +#endif + + // shift in new data and new flag + d_data_reg = (d_data_reg << 1) | (in[i] & 0x1); + d_flag_reg = (d_flag_reg << 1); + if (new_flag) { + d_flag_reg |= d_flag_bit; + } + } + + return noutput_items; +} + diff --git a/gr-digital/lib/digital_correlate_access_code_bb.h b/gr-digital/lib/digital_correlate_access_code_bb.h new file mode 100644 index 000000000..c4cb60428 --- /dev/null +++ b/gr-digital/lib/digital_correlate_access_code_bb.h @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,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_CORRELATE_ACCESS_CODE_BB_H +#define INCLUDED_DIGITAL_CORRELATE_ACCESS_CODE_BB_H + +#include <digital_api.h> +#include <gr_sync_block.h> +#include <string> + +class digital_correlate_access_code_bb; +typedef boost::shared_ptr<digital_correlate_access_code_bb> digital_correlate_access_code_bb_sptr; + +/*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + * \param threshold maximum number of bits that may be wrong + */ +DIGITAL_API digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + +/*! + * \brief Examine input for specified access code, one bit at a time. + * \ingroup sync_blk + * + * input: stream of bits, 1 bit per input byte (data in LSB) + * output: stream of bits, 2 bits per output byte (data in LSB, flag in next higher bit) + * + * Each output byte contains two valid bits, the data bit, and the + * flag bit. The LSB (bit 0) is the data bit, and is the original + * input data, delayed 64 bits. Bit 1 is the + * flag bit and is 1 if the corresponding data bit is the first data + * bit following the access code. Otherwise the flag bit is 0. + */ +class DIGITAL_API digital_correlate_access_code_bb : public gr_sync_block +{ + friend DIGITAL_API digital_correlate_access_code_bb_sptr + digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + private: + unsigned long long d_access_code; // access code to locate start of packet + // access code is left justified in the word + unsigned long long d_data_reg; // used to look for access_code + unsigned long long d_flag_reg; // keep track of decisions + unsigned long long d_flag_bit; // mask containing 1 bit which is location of new flag + unsigned long long d_mask; // masks access_code bits (top N bits are set where + // N is the number of bits in the access code) + unsigned int d_threshold; // how many bits may be wrong in sync vector + + protected: + digital_correlate_access_code_bb(const std::string &access_code, int threshold); + + public: + ~digital_correlate_access_code_bb(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + /*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + */ + bool set_access_code (const std::string &access_code); +}; + +#endif /* INCLUDED_DIGITAL_CORRELATE_ACCESS_CODE_BB_H */ diff --git a/gr-digital/lib/digital_crc32.cc b/gr-digital/lib/digital_crc32.cc new file mode 100644 index 000000000..8806d6e9c --- /dev/null +++ b/gr-digital/lib/digital_crc32.cc @@ -0,0 +1,130 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,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. + */ + +/* + * See also ISO 3309 [ISO-3309] or ITU-T V.42 [ITU-V42] for a formal specification. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <digital_crc32.h> + + +// Automatically generated CRC function +// polynomial: 0x104C11DB7 +unsigned int +digital_update_crc32(unsigned int crc, const unsigned char *data, size_t len) +{ + static const unsigned int table[256] = { + 0x00000000U,0x04C11DB7U,0x09823B6EU,0x0D4326D9U, + 0x130476DCU,0x17C56B6BU,0x1A864DB2U,0x1E475005U, + 0x2608EDB8U,0x22C9F00FU,0x2F8AD6D6U,0x2B4BCB61U, + 0x350C9B64U,0x31CD86D3U,0x3C8EA00AU,0x384FBDBDU, + 0x4C11DB70U,0x48D0C6C7U,0x4593E01EU,0x4152FDA9U, + 0x5F15ADACU,0x5BD4B01BU,0x569796C2U,0x52568B75U, + 0x6A1936C8U,0x6ED82B7FU,0x639B0DA6U,0x675A1011U, + 0x791D4014U,0x7DDC5DA3U,0x709F7B7AU,0x745E66CDU, + 0x9823B6E0U,0x9CE2AB57U,0x91A18D8EU,0x95609039U, + 0x8B27C03CU,0x8FE6DD8BU,0x82A5FB52U,0x8664E6E5U, + 0xBE2B5B58U,0xBAEA46EFU,0xB7A96036U,0xB3687D81U, + 0xAD2F2D84U,0xA9EE3033U,0xA4AD16EAU,0xA06C0B5DU, + 0xD4326D90U,0xD0F37027U,0xDDB056FEU,0xD9714B49U, + 0xC7361B4CU,0xC3F706FBU,0xCEB42022U,0xCA753D95U, + 0xF23A8028U,0xF6FB9D9FU,0xFBB8BB46U,0xFF79A6F1U, + 0xE13EF6F4U,0xE5FFEB43U,0xE8BCCD9AU,0xEC7DD02DU, + 0x34867077U,0x30476DC0U,0x3D044B19U,0x39C556AEU, + 0x278206ABU,0x23431B1CU,0x2E003DC5U,0x2AC12072U, + 0x128E9DCFU,0x164F8078U,0x1B0CA6A1U,0x1FCDBB16U, + 0x018AEB13U,0x054BF6A4U,0x0808D07DU,0x0CC9CDCAU, + 0x7897AB07U,0x7C56B6B0U,0x71159069U,0x75D48DDEU, + 0x6B93DDDBU,0x6F52C06CU,0x6211E6B5U,0x66D0FB02U, + 0x5E9F46BFU,0x5A5E5B08U,0x571D7DD1U,0x53DC6066U, + 0x4D9B3063U,0x495A2DD4U,0x44190B0DU,0x40D816BAU, + 0xACA5C697U,0xA864DB20U,0xA527FDF9U,0xA1E6E04EU, + 0xBFA1B04BU,0xBB60ADFCU,0xB6238B25U,0xB2E29692U, + 0x8AAD2B2FU,0x8E6C3698U,0x832F1041U,0x87EE0DF6U, + 0x99A95DF3U,0x9D684044U,0x902B669DU,0x94EA7B2AU, + 0xE0B41DE7U,0xE4750050U,0xE9362689U,0xEDF73B3EU, + 0xF3B06B3BU,0xF771768CU,0xFA325055U,0xFEF34DE2U, + 0xC6BCF05FU,0xC27DEDE8U,0xCF3ECB31U,0xCBFFD686U, + 0xD5B88683U,0xD1799B34U,0xDC3ABDEDU,0xD8FBA05AU, + 0x690CE0EEU,0x6DCDFD59U,0x608EDB80U,0x644FC637U, + 0x7A089632U,0x7EC98B85U,0x738AAD5CU,0x774BB0EBU, + 0x4F040D56U,0x4BC510E1U,0x46863638U,0x42472B8FU, + 0x5C007B8AU,0x58C1663DU,0x558240E4U,0x51435D53U, + 0x251D3B9EU,0x21DC2629U,0x2C9F00F0U,0x285E1D47U, + 0x36194D42U,0x32D850F5U,0x3F9B762CU,0x3B5A6B9BU, + 0x0315D626U,0x07D4CB91U,0x0A97ED48U,0x0E56F0FFU, + 0x1011A0FAU,0x14D0BD4DU,0x19939B94U,0x1D528623U, + 0xF12F560EU,0xF5EE4BB9U,0xF8AD6D60U,0xFC6C70D7U, + 0xE22B20D2U,0xE6EA3D65U,0xEBA91BBCU,0xEF68060BU, + 0xD727BBB6U,0xD3E6A601U,0xDEA580D8U,0xDA649D6FU, + 0xC423CD6AU,0xC0E2D0DDU,0xCDA1F604U,0xC960EBB3U, + 0xBD3E8D7EU,0xB9FF90C9U,0xB4BCB610U,0xB07DABA7U, + 0xAE3AFBA2U,0xAAFBE615U,0xA7B8C0CCU,0xA379DD7BU, + 0x9B3660C6U,0x9FF77D71U,0x92B45BA8U,0x9675461FU, + 0x8832161AU,0x8CF30BADU,0x81B02D74U,0x857130C3U, + 0x5D8A9099U,0x594B8D2EU,0x5408ABF7U,0x50C9B640U, + 0x4E8EE645U,0x4A4FFBF2U,0x470CDD2BU,0x43CDC09CU, + 0x7B827D21U,0x7F436096U,0x7200464FU,0x76C15BF8U, + 0x68860BFDU,0x6C47164AU,0x61043093U,0x65C52D24U, + 0x119B4BE9U,0x155A565EU,0x18197087U,0x1CD86D30U, + 0x029F3D35U,0x065E2082U,0x0B1D065BU,0x0FDC1BECU, + 0x3793A651U,0x3352BBE6U,0x3E119D3FU,0x3AD08088U, + 0x2497D08DU,0x2056CD3AU,0x2D15EBE3U,0x29D4F654U, + 0xC5A92679U,0xC1683BCEU,0xCC2B1D17U,0xC8EA00A0U, + 0xD6AD50A5U,0xD26C4D12U,0xDF2F6BCBU,0xDBEE767CU, + 0xE3A1CBC1U,0xE760D676U,0xEA23F0AFU,0xEEE2ED18U, + 0xF0A5BD1DU,0xF464A0AAU,0xF9278673U,0xFDE69BC4U, + 0x89B8FD09U,0x8D79E0BEU,0x803AC667U,0x84FBDBD0U, + 0x9ABC8BD5U,0x9E7D9662U,0x933EB0BBU,0x97FFAD0CU, + 0xAFB010B1U,0xAB710D06U,0xA6322BDFU,0xA2F33668U, + 0xBCB4666DU,0xB8757BDAU,0xB5365D03U,0xB1F740B4U, + }; + + while (len > 0) + { + crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8); + data++; + len--; + } + return crc; +} + +unsigned int +digital_update_crc32(unsigned int crc, const std::string s) +{ + return digital_update_crc32(crc, (const unsigned char *) s.data(), s.size()); +} + +unsigned int +digital_crc32(const unsigned char *buf, size_t len) +{ + return digital_update_crc32(0xffffffff, buf, len) ^ 0xffffffff; +} + +unsigned int +digital_crc32(const std::string s) +{ + return digital_crc32((const unsigned char *) s.data(), s.size()); +} diff --git a/gr-digital/lib/digital_crc32.h b/gr-digital/lib/digital_crc32.h new file mode 100644 index 000000000..852d06f49 --- /dev/null +++ b/gr-digital/lib/digital_crc32.h @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,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_CRC32_H +#define INCLUDED_DIGITAL_CRC32_H + +#include <digital_api.h> +#include <string> +#include <gr_types.h> + +/*! + * \brief update running CRC-32 + * \ingroup misc + * + * Update a running CRC with the bytes buf[0..len-1] The CRC should be + * initialized to all 1's, and the transmitted value is the 1's + * complement of the final running CRC. The resulting CRC should be + * transmitted in big endian order. + */ +DIGITAL_API unsigned int +digital_update_crc32(unsigned int crc, const unsigned char *buf, size_t len); + +DIGITAL_API unsigned int +digital_update_crc32(unsigned int crc, const std::string buf); + +DIGITAL_API unsigned int +digital_crc32(const unsigned char *buf, size_t len); + +DIGITAL_API unsigned int +digital_crc32(const std::string buf); + +#endif /* INCLUDED_CRC32_H */ diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc new file mode 100644 index 000000000..3b2ea9840 --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.cc @@ -0,0 +1,322 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007,2010,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 <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_mpsk_receiver_cc.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> +#include <gri_mmse_fir_interpolator_cc.h> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc(unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel) +{ + return gnuradio::get_initial_sptr(new digital_mpsk_receiver_cc (M, theta, + alpha, beta, + fmin, fmax, + mu, gain_mu, + omega, gain_omega, omega_rel)); +} + +digital_mpsk_receiver_cc::digital_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel) + : gr_block ("mpsk_receiver_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 1, sizeof (gr_complex))), + d_M(M), d_theta(theta), + d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), + d_current_const_point(0), + d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), + d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + d_interp = new gri_mmse_fir_interpolator_cc(); + d_dl_idx = 0; + + set_omega(omega); + + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + assert(d_interp->ntaps() <= DLLEN); + + // zero double length delay line. + for (unsigned int i = 0; i < 2 * DLLEN; i++) + d_dl[i] = gr_complex(0.0,0.0); + + // build the constellation vector from M + make_constellation(); + + // Select a phase detector and a decision maker for the modulation order + switch(d_M) { + case 2: // optimized algorithms for BPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_bpsk; //bpsk; + d_decision = &digital_mpsk_receiver_cc::decision_bpsk; + break; + + case 4: // optimized algorithms for QPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_qpsk; //qpsk; + d_decision = &digital_mpsk_receiver_cc::decision_qpsk; + break; + + default: // generic algorithms for any M (power of 2?) but not pretty + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_generic; + d_decision = &digital_mpsk_receiver_cc::decision_generic; + break; + } +} + +digital_mpsk_receiver_cc::~digital_mpsk_receiver_cc () +{ + delete d_interp; +} + +void +digital_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +// FIXME add these back in an test difference in performance +float +digital_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const +{ + float phase_error = 0; + if(fabsf(sample.real()) > fabsf(sample.imag())) { + if(sample.real() > 0) + phase_error = -sample.imag(); + else + phase_error = sample.imag(); + } + else { + if(sample.imag() > 0) + phase_error = sample.real(); + else + phase_error = -sample.real(); + } + + return phase_error; +} + +float +digital_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const +{ + return -(sample.real()*sample.imag()); +} + +float digital_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const +{ + //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point])); + return -arg(sample*conj(d_constellation[d_current_const_point])); +} + +unsigned int +digital_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const +{ + return (gr_branchless_binary_slicer(sample.real()) ^ 1); + //return gr_binary_slicer(sample.real()) ^ 1; +} + +unsigned int +digital_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const +{ + unsigned int index; + + //index = gr_branchless_quad_0deg_slicer(sample); + index = gr_quad_0deg_slicer(sample); + return index; +} + +unsigned int +digital_mpsk_receiver_cc::decision_generic(gr_complex sample) const +{ + unsigned int min_m = 0; + float min_s = 65535; + + // Develop all possible constellation points and find the one that minimizes + // the Euclidean distance (error) with the sample + for(unsigned int m=0; m < d_M; m++) { + gr_complex diff = norm(d_constellation[m] - sample); + + if(fabs(diff.real()) < min_s) { + min_s = fabs(diff.real()); + min_m = m; + } + } + // Return the index of the constellation point that minimizes the error + return min_m; +} + + +void +digital_mpsk_receiver_cc::make_constellation() +{ + for(unsigned int m=0; m < d_M; m++) { + d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m)); + } +} + +void +digital_mpsk_receiver_cc::mm_sampler(const gr_complex symbol) +{ + gr_complex sample, nco; + + d_mu--; // skip a number of symbols between sampling + d_phase += d_freq; // increment the phase based on the frequency of the rotation + + // Keep phase clamped and not walk to infinity + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the current sample + sample = nco*symbol; // get the downconverted symbol + + // Fill up the delay line for the interpolator + d_dl[d_dl_idx] = sample; + d_dl[(d_dl_idx + DLLEN)] = sample; // put this in the second half of the buffer for overflows + d_dl_idx = (d_dl_idx+1) % DLLEN; // Keep the delay line index in bounds +} + +void +digital_mpsk_receiver_cc::mm_error_tracking(gr_complex sample) +{ + gr_complex u, x, y; + float mm_error = 0; + + // Make sample timing corrections + + // set the delayed samples + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = sample; + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + + d_current_const_point = (*this.*d_decision)(d_p_0T); // make a decision on the sample value + d_c_0T = d_constellation[d_current_const_point]; + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_error = u.real(); // the error signal is in the real part + mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val + + d_omega = d_omega + d_gain_omega * mm_error; // update omega based on loop error + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away + + d_mu += d_omega + d_gain_mu * mm_error; // update mu based on loop error + +#if VERBOSE_MM + printf("mm: mu: %f omega: %f mm_error: %f sample: %f+j%f constellation: %f+j%f\n", + d_mu, d_omega, mm_error, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + + +void +digital_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) +{ + float phase_error = 0; + + // Make phase and frequency corrections based on sampled value + phase_error = (*this.*d_phase_error_detector)(sample); + + d_freq += d_beta*phase_error; // adjust frequency based on error + d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error + + // Make sure we stay within +-2pi + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + // Limit the frequency range + d_freq = gr_branchless_clip(d_freq, d_max_freq); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + +int +digital_mpsk_receiver_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + int i=0, o=0; + + while((o < noutput_items) && (i < ninput_items[0])) { + while((d_mu > 1) && (i < ninput_items[0])) { + mm_sampler(in[i]); // puts symbols into a buffer and adjusts d_mu + i++; + } + + if(i < ninput_items[0]) { + gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu); + + mm_error_tracking(interp_sample); // corrects M&M sample time + phase_error_tracking(interp_sample); // corrects phase and frequency offsets + + out[o++] = interp_sample; + } + } + + #if 0 + printf("ninput_items: %d noutput_items: %d consuming: %d returning: %d\n", + ninput_items[0], noutput_items, i, o); + #endif + + consume_each(i); + return o; +} diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.h b/gr-digital/lib/digital_mpsk_receiver_cc.h new file mode 100644 index 000000000..8a6352ec7 --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.h @@ -0,0 +1,317 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,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_RECEIVER_CC_H +#define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H + +#include <digital_api.h> +#include <gruel/attributes.h> +#include <gr_block.h> +#include <gr_complex.h> +#include <fstream> + +class gri_mmse_fir_interpolator_cc; + +class digital_mpsk_receiver_cc; +typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_sptr; + +// public constructor +DIGITAL_API digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + +/*! + * \brief This block takes care of receiving M-PSK modulated signals through phase, frequency, and symbol + * synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * + * This block takes care of receiving M-PSK modulated signals through phase, frequency, and symbol + * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. + * It works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It should also work for OQPSK and + * PI/4 DQPSK. + * + * The phase and frequency synchronization are based on a Costas loop that finds the error of the incoming + * signal point compared to its nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. There are optimized phase error detectors for BPSK and QPSK, but 8PSK + * is done using a brute-force computation of the constellation points to find the minimum. + * + * The symbol synchronization is done using a modified Mueller and Muller circuit from the paper: + * + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + * algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. + * + * This circuit interpolates the downconverted sample (using the NCO developed by the Costas loop) + * every mu samples, then it finds the sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are optimized decision algorithms for BPSK + * and QPKS, but 8PSK uses another brute force computation against all possible symbols. The modifications + * to the M&M used here reduce self-noise. + * + */ + +class DIGITAL_API digital_mpsk_receiver_cc : public gr_block +{ + public: + ~digital_mpsk_receiver_cc (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + // Member functions related to the symbol tracking portion of the receiver + //! (M&M) Returns current value of mu + float mu() const { return d_mu;} + + //! (M&M) Returns current value of omega + float omega() const { return d_omega;} + + //! (M&M) Returns mu gain factor + float gain_mu() const { return d_gain_mu;} + + //! (M&M) Returns omega gain factor + float gain_omega() const { return d_gain_omega;} + + //! (M&M) Sets value of mu + void set_mu (float mu) { d_mu = mu; } + + //! (M&M) Sets value of omega and its min and max values + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_rel); + d_max_omega = omega*(1.0 + d_omega_rel); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + + //! (M&M) Sets value for mu gain factor + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + + //! (M&M) Sets value for omega gain factor + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + + + + // Member function related to the phase/frequency tracking portion of the receiver + //! (CL) Returns the value for alpha (the phase gain term) + float alpha() const { return d_alpha; } + + //! (CL) Returns the value of beta (the frequency gain term) + float beta() const { return d_beta; } + + //! (CL) Returns the current value of the frequency of the NCO in the Costas loop + float freq() const { return d_freq; } + + //! (CL) Returns the current value of the phase of the NCO in the Costal loop + float phase() const { return d_phase; } + + //! (CL) Sets the value for alpha (the phase gain term) + void set_alpha(float alpha) { d_alpha = alpha; } + + //! (CL) Setss the value of beta (the frequency gain term) + void set_beta(float beta) { d_beta = beta; } + + //! (CL) Sets the current value of the frequency of the NCO in the Costas loop + void set_freq(float freq) { d_freq = freq; } + + //! (CL) Setss the current value of the phase of the NCO in the Costal loop + void set_phase(float phase) { d_phase = phase; } + + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param M modulation order of the M-PSK modulation + * \param theta any constant phase rotation from the real axis of the constellation + * \param alpha gain parameter to adjust the phase in the Costas loop (~0.01) + * \param beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4) + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * \param mu initial parameter for the interpolator [0,1] + * \param gain_mu gain parameter of the M&M error signal to adjust mu (~0.05) + * \param omega initial value for the number of symbols between samples (~number of samples/symbol) + * \param gain_omega gain parameter to adjust omega based on the error (~omega^2/4) + * \param omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005) + * + * The constructor also chooses which phase detector and decision maker to use in the work loop based on the + * value of M. + */ + digital_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + + void make_constellation(); + void mm_sampler(const gr_complex symbol); + void mm_error_tracking(gr_complex sample); + void phase_error_tracking(gr_complex sample); + + +/*! + * \brief Phase error detector for MPSK modulations. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error for any MPSK signal by creating a set of PSK constellation points + * and doing a brute-force search to see which point minimizes the Euclidean distance. This point is then used + * to derotate the sample to the real-axis and a atan (using the fast approximation function) to determine the + * phase difference between the incoming sample and the real constellation point + * + * This should be cleaned up and made more efficient. + * + * \returns the approximated phase error. + */ + float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly + + /*! + * \brief Phase error detector for BPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using a simple BPSK phase error detector by multiplying the real + * and imaginary (the error signal) components together. As the imaginary part goes to 0, so does this error. + * + * \returns the approximated phase error. + */ + float phase_error_detector_bpsk(gr_complex sample) const; // optimized for BPSK + + /*! + * \brief Phase error detector for QPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using the limiter approach in a standard 4th order Costas loop + * + * \returns the approximated phase error. + */ + float phase_error_detector_qpsk(gr_complex sample) const; + + + + /*! + * \brief Decision maker for a generic MPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a generic implementation that does a brute-force search + * for the constellation point that minimizes the error between it and the incoming signal. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_generic(gr_complex sample) const; + + + /*! + * \brief Decision maker for BPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a decision on the symbol based on its + * placement on the real axis of greater than 0 or less than 0; the quadrature component is always 0. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_bpsk(gr_complex sample) const; + + + /*! + * \brief Decision maker for QPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a decision on the symbol based on its + * placement versus both axes and returns which quadrant the symbol is in. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_qpsk(gr_complex sample) const; + + private: + unsigned int d_M; + float d_theta; + + // Members related to carrier and phase tracking + float d_alpha; + float d_beta; + float d_freq, d_max_freq, d_min_freq; + float d_phase; + +/*! + * \brief Decision maker function pointer + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This is a function pointer that is set in the constructor to point to the proper decision function + * for the specified constellation order. + * + * \return index into d_constellation point that is the closest to the recieved sample + */ + unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function + + + std::vector<gr_complex> d_constellation; + unsigned int d_current_const_point; + + // Members related to symbol timing + float d_mu, d_gain_mu; + float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid; + gr_complex d_p_2T, d_p_1T, d_p_0T; + gr_complex d_c_2T, d_c_1T, d_c_0T; + + /*! + * \brief Phase error detector function pointer + * + * \param sample the I&Q sample from which to determine the phase error + * + * This is a function pointer that is set in the constructor to point to the proper phase error detector + * function for the specified constellation order. + */ + float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const; + + + //! get interpolated value + gri_mmse_fir_interpolator_cc *d_interp; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; + + //! index to delay line + unsigned int d_dl_idx; + + friend DIGITAL_API digital_mpsk_receiver_cc_sptr + digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +}; + +#endif diff --git a/gr-digital/python/CMakeLists.txt b/gr-digital/python/CMakeLists.txt index 194894c73..2d09a4945 100644 --- a/gr-digital/python/CMakeLists.txt +++ b/gr-digital/python/CMakeLists.txt @@ -25,18 +25,19 @@ INCLUDE(GrPython) GR_PYTHON_INSTALL( FILES __init__.py + bpsk.py + cpm.py + crc.py + generic_mod_demod.py + gmsk.py + modulation_utils.py + modulation_utils2.py + packet_utils.py + pkt.py psk.py - dbpsk.py - dqpsk.py - d8psk.py psk2.py - generic_mod_demod.py qam.py - bpsk.py qpsk.py - ofdm.py - pkt.py - modulation_utils2.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/digital COMPONENT "digital_python" ) diff --git a/gr-digital/python/Makefile.am b/gr-digital/python/Makefile.am index 392ce45c1..87752e9ae 100644 --- a/gr-digital/python/Makefile.am +++ b/gr-digital/python/Makefile.am @@ -32,23 +32,31 @@ digitaldir = $(grpythondir)/digital noinst_PYTHON = \ qa_digital.py \ + qa_binary_slicer_fb.py \ + qa_clock_recovery_mm.py \ + qa_cma_equalizer.py \ qa_constellation.py \ qa_constellation_receiver.py \ - qa_costas_loop_cc.py + qa_constellation_decoder_cb.py \ + qa_correlate_access_code.py \ + qa_costas_loop_cc.py \ + qa_crc32.py \ + qa_lms_equalizer.py \ + qa_mpsk_receiver.py digital_PYTHON = \ - __init__.py \ - psk.py \ - dbpsk.py \ - dqpsk.py \ - d8psk.py \ - psk2.py \ + __init__.py \ + bpsk.py \ + cpm.py \ + crc.py \ generic_mod_demod.py \ - qam.py \ - bpsk.py \ - qpsk.py \ - ofdm.py \ - pkt.py \ - modulation_utils2.py - + gmsk.py \ + modulation_utils.py \ + modulation_utils2.py \ + packet_utils.py \ + pkt.py \ + psk.py \ + psk2.py \ + qam.py \ + qpsk.py endif diff --git a/gr-digital/python/__init__.py b/gr-digital/python/__init__.py index 3d0be3865..4046f7faf 100644 --- a/gr-digital/python/__init__.py +++ b/gr-digital/python/__init__.py @@ -22,11 +22,10 @@ # The presence of this file turns this directory into a Python package from digital_swig import * -from dbpsk import * -from dqpsk import * -from d8psk import * from psk2 import * +from bpsk import * +from qpsk import * from qam import * -from ofdm import * from pkt import * -from modulation_utils2 import * +from packet_utils import * +from crc import * diff --git a/gr-digital/python/bpsk.py b/gr-digital/python/bpsk.py index 6d2eb5d6d..51de3ce08 100644 --- a/gr-digital/python/bpsk.py +++ b/gr-digital/python/bpsk.py @@ -26,8 +26,10 @@ BPSK modulation and demodulation. from math import pi, log from cmath import exp -from gnuradio import gr, modulation_utils2 +from gnuradio import gr from gnuradio.digital.generic_mod_demod import generic_mod, generic_demod +import digital_swig +import modulation_utils2 # Default number of points in constellation. _def_constellation_points = 2 @@ -41,7 +43,7 @@ _def_differential = True def bpsk_constellation(m=_def_constellation_points): if m != _def_constellation_points: raise ValueError("BPSK can only have 2 constellation points.") - return gr.constellation_bpsk() + return digital_swig.constellation_bpsk() # ///////////////////////////////////////////////////////////////////////////// # BPSK modulator @@ -61,7 +63,8 @@ class bpsk_mod(generic_mod): See generic_mod block for list of parameters. """ - constellation = gr.constellation_bpsk() + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() if constellation_points != 2: raise ValueError('Number of constellation points must be 2 for BPSK.') super(bpsk_mod, self).__init__(constellation, *args, **kwargs) @@ -85,7 +88,8 @@ class bpsk_demod(generic_demod): See generic_demod block for list of parameters. """ - constellation = gr.constellation_bpsk() + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() if constellation_points != 2: raise ValueError('Number of constellation points must be 2 for BPSK.') super(bpsk_demod, self).__init__(constellation, *args, **kwargs) diff --git a/gr-digital/python/cpm.py b/gr-digital/python/cpm.py new file mode 100644 index 000000000..8f593cd51 --- /dev/null +++ b/gr-digital/python/cpm.py @@ -0,0 +1,249 @@ +# +# CPM modulation and demodulation. +# +# +# Copyright 2005,2006,2007 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. +# + +# See gnuradio-examples/python/digital for examples + +from gnuradio import gr +from gnuradio import modulation_utils +from math import pi +import numpy +from pprint import pprint +import inspect + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_bits_per_symbol = 1 +_def_h_numerator = 1 +_def_h_denominator = 2 +_def_cpm_type = 0 # 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL +_def_bt = 0.35 +_def_symbols_per_pulse = 1 +_def_generic_taps = numpy.empty(1) +_def_verbose = False +_def_log = False + + +# ///////////////////////////////////////////////////////////////////////////// +# CPM modulator +# ///////////////////////////////////////////////////////////////////////////// + +class cpm_mod(gr.hier_block2): + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bits_per_symbol=_def_bits_per_symbol, + h_numerator=_def_h_numerator, + h_denominator=_def_h_denominator, + cpm_type=_def_cpm_type, + bt=_def_bt, + symbols_per_pulse=_def_symbols_per_pulse, + generic_taps=_def_generic_taps, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Continuous Phase + modulation. + + The input is a byte stream (unsigned char) + representing packed bits and the + output is the complex modulated signal at baseband. + + See Proakis for definition of generic CPM signals: + s(t)=exp(j phi(t)) + phi(t)= 2 pi h int_0^t f(t') dt' + f(t)=sum_k a_k g(t-kT) + (normalizing assumption: int_0^infty g(t) dt = 1/2) + + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param bits_per_symbol: bits per symbol + @type bits_per_symbol: integer + @param h_numerator: numerator of modulation index + @type h_numerator: integer + @param h_denominator: denominator of modulation index (numerator and denominator must be relative primes) + @type h_denominator: integer + @param cpm_type: supported types are: 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL + @type cpm_type: integer + @param bt: bandwidth symbol time product for GMSK + @type bt: float + @param symbols_per_pulse: shaping pulse duration in symbols + @type symbols_per_pulse: integer + @param generic_taps: define a generic CPM pulse shape (sum = samples_per_symbol/2) + @type generic_taps: array of floats + + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modulation data to files? + @type debug: bool + """ + + gr.hier_block2.__init__("cpm_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bits_per_symbol = bits_per_symbol + self._h_numerator = h_numerator + self._h_denominator = h_denominator + self._cpm_type = cpm_type + self._bt=bt + if cpm_type == 0 or cpm_type == 2 or cpm_type == 3: # CPFSK, RC, Generic + self._symbols_per_pulse = symbols_per_pulse + elif cpm_type == 1: # GMSK + self._symbols_per_pulse = 4 + else: + raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,)) + + self._generic_taps=numpy.array(generic_taps) + + if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,)) + + self.nsymbols = 2**bits_per_symbol + self.sym_alphabet=numpy.arange(-(self.nsymbols-1),self.nsymbols,2) + + + self.ntaps = self._symbols_per_pulse * samples_per_symbol + sensitivity = 2 * pi * h_numerator / h_denominator / samples_per_symbol + + # Unpack Bytes into bits_per_symbol groups + self.B2s = gr.packed_to_unpacked_bb(bits_per_symbol,gr.GR_MSB_FIRST) + + + # Turn it into symmetric PAM data. + self.pam = gr.chunks_to_symbols_bf(self.sym_alphabet,1) + + # Generate pulse (sum of taps = samples_per_symbol/2) + if cpm_type == 0: # CPFSK + self.taps= (1.0/self._symbols_per_pulse/2,) * self.ntaps + elif cpm_type == 1: # GMSK + gaussian_taps = gr.firdes.gaussian( + 1.0/2, # gain + samples_per_symbol, # symbol_rate + bt, # bandwidth * symbol time + self.ntaps # number of taps + ) + sqwave = (1,) * samples_per_symbol # rectangular window + self.taps = numpy.convolve(numpy.array(gaussian_taps),numpy.array(sqwave)) + elif cpm_type == 2: # Raised Cosine + # generalize it for arbitrary roll-off factor + self.taps = (1-numpy.cos(2*pi*numpy.arange(0,self.ntaps)/samples_per_symbol/self._symbols_per_pulse))/(2*self._symbols_per_pulse) + elif cpm_type == 3: # Generic CPM + self.taps = generic_taps + else: + raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,)) + + self.filter = gr.interp_fir_filter_fff(samples_per_symbol, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect + self.connect(self, self.B2s, self.pam, self.filter, self.fmmod, self) + + #def samples_per_symbol(self): + #return self._samples_per_symbol + + #def bits_per_symbol(self): + #return self._bits_per_symbol + + #def h_numerator(self): + #return self._h_numerator + + #def h_denominator(self): + #return self._h_denominator + + #def cpm_type(self): + #return self._cpm_type + + #def bt(self): + #return self._bt + + #def symbols_per_pulse(self): + #return self._symbols_per_pulse + + + def _print_verbage(self): + print "Samples per symbol = %d" % self._samples_per_symbol + print "Bits per symbol = %d" % self._bits_per_symbol + print "h = " , self._h_numerator , " / " , self._h_denominator + print "Symbol alphabet = " , self.sym_alphabet + print "Symbols per pulse = %d" % self._symbols_per_pulse + print "taps = " , self.taps + + print "CPM type = %d" % self._cpm_type + if self._cpm_type == 1: + print "Gaussian filter BT = %.2f" % self._bt + + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.B2s, + gr.file_sink(gr.sizeof_float, "symbols.dat")) + self.connect(self.pam, + gr.file_sink(gr.sizeof_float, "pam.dat")) + self.connect(self.filter, + gr.file_sink(gr.sizeof_float, "filter.dat")) + self.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat")) + + + def add_options(parser): + """ + Adds CPM modulation-specific options to the standard parser + """ + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(cpm_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# CPM demodulator +# ///////////////////////////////////////////////////////////////////////////// +# +# Not yet implemented +# + + + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('cpm', cpm_mod) +#modulation_utils.add_type_1_demod('cpm', cpm_demod) diff --git a/gr-digital/python/crc.py b/gr-digital/python/crc.py new file mode 100644 index 000000000..198ab059f --- /dev/null +++ b/gr-digital/python/crc.py @@ -0,0 +1,38 @@ +# +# Copyright 2005,2007,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 gru +import digital_swig +import struct + +def gen_and_append_crc32(s): + crc = digital_swig.crc32(s) + return s + struct.pack(">I", gru.hexint(crc) & 0xFFFFFFFF) + +def check_crc32(s): + if len(s) < 4: + return (False, '') + msg = s[:-4] + #print "msg = '%s'" % (msg,) + actual = digital_swig.crc32(msg) + (expected,) = struct.unpack(">I", s[-4:]) + # print "actual =", hex(actual), "expected =", hex(expected) + return (actual == expected, msg) diff --git a/gr-digital/python/d8psk.py b/gr-digital/python/d8psk.py index 8bed395a7..46290faed 100644 --- a/gr-digital/python/d8psk.py +++ b/gr-digital/python/d8psk.py @@ -310,8 +310,9 @@ class d8psk_demod(gr.hier_block2): print "Timing alpha gain: %.2f" % self._timing_alpha print "Timing beta gain: %.2f" % self._timing_beta print "Timing max dev: %.2f" % self._timing_max_dev - print "Phase track alpha: %.2e" % self._phase_alpha - print "Phase track beta: %.2e" % self._phase_beta + print "Phase damping fact: %.2e" % self._phase_damping + print "Phase natural freq: %.2e" % self._phase_natfreq + def _setup_logging(self): print "Modulation logging turned on." @@ -343,8 +344,10 @@ class d8psk_demod(gr.hier_block2): help="disable gray coding on modulated bits (PSK)") parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, help="set frequency lock loop alpha gain value [default=%default] (PSK)") - parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, - help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--phase-natfreq", type="float", default=_def_phase_natfreq, + help="set natural frequency of phase tracking loop [default=%default] (PSK)") + parser.add_option("", "--phase-damping", type="float", default=_def_phase_damping, + help="set damping factor of phase tracking loop [default=%default] (PSK)") parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, diff --git a/gr-digital/python/dbpsk.py b/gr-digital/python/dbpsk.py index 2e9b756e6..9e065263f 100644 --- a/gr-digital/python/dbpsk.py +++ b/gr-digital/python/dbpsk.py @@ -100,6 +100,7 @@ class dbpsk_mod(gr.hier_block2): self.diffenc = gr.diff_encoder_bb(arity) + self.chunks2symbols = gr.chunks_to_symbols_bc(psk.constellation[arity]) # pulse shaping filter @@ -272,9 +273,8 @@ class dbpsk_demod(gr.hier_block2): self.diffdec = gr.diff_phasor_cc() # find closest constellation point - rot = 1 - rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) - self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + const = digital_swig.constellation_bpsk() + self.slicer = digital_swig.constellation_decoder_cb(const.base()) if self._gray_code: self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) @@ -312,8 +312,8 @@ class dbpsk_demod(gr.hier_block2): print "Timing alpha gain: %.2e" % self._timing_alpha print "Timing beta gain: %.2e" % self._timing_beta print "Timing max dev: %.2f" % self._timing_max_dev - print "Phase track alpha: %.2e" % self._phase_alpha - print "Phase track beta: %.2e" % self._phase_beta + print "Phase damping fact: %.2e" % self._phase_damping + print "Phase natural freq: %.2e" % self._phase_natfreq def _setup_logging(self): print "Modulation logging turned on." @@ -345,8 +345,10 @@ class dbpsk_demod(gr.hier_block2): help="disable gray coding on modulated bits (PSK)") parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, help="set frequency lock loop alpha gain value [default=%default] (PSK)") - parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, - help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--phase-natfreq", type="float", default=_def_phase_natfreq, + help="set natural frequency of phase tracking loop [default=%default] (PSK)") + parser.add_option("", "--phase-damping", type="float", default=_def_phase_damping, + help="set damping factor of phase tracking loop [default=%default] (PSK)") parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, @@ -366,5 +368,5 @@ class dbpsk_demod(gr.hier_block2): # # Add these to the mod/demod registry # -modulation_utils2.add_type_1_mod('dbpsk3', dbpsk_mod) -modulation_utils2.add_type_1_demod('dbpsk3', dbpsk_demod) +modulation_utils2.add_type_1_mod('dbpsk', dbpsk_mod) +modulation_utils2.add_type_1_demod('dbpsk', dbpsk_demod) diff --git a/gr-digital/python/dqpsk.py b/gr-digital/python/dqpsk.py index 29afd5530..5e17d24bc 100644 --- a/gr-digital/python/dqpsk.py +++ b/gr-digital/python/dqpsk.py @@ -315,8 +315,8 @@ class dqpsk_demod(gr.hier_block2): print "Timing alpha gain: %.2f" % self._timing_alpha print "Timing beta gain: %.2f" % self._timing_beta print "Timing max dev: %.2f" % self._timing_max_dev - print "Phase track alpha: %.2e" % self._phase_alpha - print "Phase track beta: %.2e" % self._phase_beta + print "Phase damping fact: %.2e" % self._phase_damping + print "Phase natural freq: %.2e" % self._phase_natfreq def _setup_logging(self): print "Modulation logging turned on." @@ -348,8 +348,10 @@ class dqpsk_demod(gr.hier_block2): help="disable gray coding on modulated bits (PSK)") parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, help="set frequency lock loop alpha gain value [default=%default] (PSK)") - parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, - help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--phase-natfreq", type="float", default=_def_phase_natfreq, + help="set natural frequency of phase tracking loop [default=%default] (PSK)") + parser.add_option("", "--phase-damping", type="float", default=_def_phase_damping, + help="set damping factor of phase tracking loop [default=%default] (PSK)") parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, diff --git a/gr-digital/python/generic_mod_demod.py b/gr-digital/python/generic_mod_demod.py index f8051db0a..1b8603fea 100644 --- a/gr-digital/python/generic_mod_demod.py +++ b/gr-digital/python/generic_mod_demod.py @@ -27,7 +27,6 @@ Generic modulation and demodulation. from gnuradio import gr from modulation_utils2 import extract_kwargs_from_options_for_class -#from gnuradio.digital.utils import mod_codes from utils import mod_codes import digital_swig @@ -106,12 +105,10 @@ class generic_mod(gr.hier_block2): self._samples_per_symbol = samples_per_symbol self._excess_bw = excess_bw self._differential = differential - - if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol) - - ntaps = 11 * self._samples_per_symbol + if self._samples_per_symbol < 2: + raise TypeError, ("sbp must be >= 2, is %d" % self._samples_per_symbol) + arity = pow(2,self.bits_per_symbol()) # turn bytes into k-bit vectors @@ -127,14 +124,15 @@ class generic_mod(gr.hier_block2): self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.points()) # pulse shaping filter - self.rrc_taps = gr.firdes.root_raised_cosine( - self._samples_per_symbol, # gain (samples_per_symbol since we're - # interpolating by samples_per_symbol) - self._samples_per_symbol, # sampling rate - 1.0, # symbol rate - self._excess_bw, # excess bandwidth (roll-off factor) + nfilts = 32 + ntaps = nfilts * 11 * int(self._samples_per_symbol) # make nfilts filters of ntaps each + self.rrc_taps = gr.firdes.root_raised_cosine( + nfilts, # gain + nfilts, # sampling rate based on 32 filters in resampler + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) ntaps) - self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, + self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps) # Connect @@ -255,8 +253,8 @@ class generic_demod(gr.hier_block2): self._timing_max_dev=timing_max_dev self._differential = differential - if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol) + if self._samples_per_symbol < 2: + raise TypeError, ("sbp must be >= 2, is %d" % self._samples_per_symbol) arity = pow(2,self.bits_per_symbol()) @@ -279,7 +277,6 @@ class generic_demod(gr.hier_block2): taps, nfilts, nfilts/2, self._timing_max_dev) self.time_recov.set_beta(self._timing_beta) - #self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha fmin = -0.25 fmax = 0.25 diff --git a/gr-digital/python/gmsk.py b/gr-digital/python/gmsk.py new file mode 100644 index 000000000..3b6c016a0 --- /dev/null +++ b/gr-digital/python/gmsk.py @@ -0,0 +1,292 @@ +# +# GMSK modulation and demodulation. +# +# +# Copyright 2005,2006,2007 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. +# + +# See gnuradio-examples/python/digital for examples + +from gnuradio import gr +from gnuradio import modulation_utils +from math import pi +import numpy +from pprint import pprint +import inspect + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_bt = 0.35 +_def_verbose = False +_def_log = False + +_def_gain_mu = None +_def_mu = 0.5 +_def_freq_error = 0.0 +_def_omega_relative_limit = 0.005 + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_mod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bt=_def_bt, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param bt: Gaussian filter bandwidth * symbol time + @type bt: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "gmsk_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bt = bt + + if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,)) + + ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once + sensitivity = (pi / 2) / samples_per_symbol # phase change per bit = pi / 2 + + # Turn it into NRZ data. + self.nrz = gr.bytes_to_syms() + + # Form Gaussian filter + # Generate Gaussian response (Needs to be convolved with window below). + self.gaussian_taps = gr.firdes.gaussian( + 1, # gain + samples_per_symbol, # symbol_rate + bt, # bandwidth * symbol time + ntaps # number of taps + ) + + self.sqwave = (1,) * samples_per_symbol # rectangular window + self.taps = numpy.convolve(numpy.array(self.gaussian_taps),numpy.array(self.sqwave)) + self.gaussian_filter = gr.interp_fir_filter_fff(samples_per_symbol, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.nrz, self.gaussian_filter, self.fmmod, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gaussian filter bt = %.2f" % self._bt + + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.nrz, + gr.file_sink(gr.sizeof_float, "nrz.dat")) + self.connect(self.gaussian_filter, + gr.file_sink(gr.sizeof_float, "gaussian_filter.dat")) + self.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat")) + + + def add_options(parser): + """ + Adds GMSK modulation-specific options to the standard parser + """ + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK demodulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + gain_mu=_def_gain_mu, + mu=_def_mu, + omega_relative_limit=_def_omega_relative_limit, + freq_error=_def_freq_error, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (the LSB) + + @param samples_per_symbol: samples per baud + @type samples_per_symbol: integer + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Print modualtion data to files? + @type log: bool + + Clock recovery parameters. These all have reasonble defaults. + + @param gain_mu: controls rate of mu adjustment + @type gain_mu: float + @param mu: fractional delay [0.0, 1.0] + @type mu: float + @param omega_relative_limit: sets max variation in omega + @type omega_relative_limit: float, typically 0.000200 (200 ppm) + @param freq_error: bit rate error as a fraction + @param float + """ + + gr.hier_block2.__init__(self, "gmsk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._gain_mu = gain_mu + self._mu = mu + self._omega_relative_limit = omega_relative_limit + self._freq_error = freq_error + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol + + self._omega = samples_per_symbol*(1+self._freq_error) + + if not self._gain_mu: + self._gain_mu = 0.175 + + self._gain_omega = .25 * self._gain_mu * self._gain_mu # critically damped + + # Demodulate FM + sensitivity = (pi / 2) / samples_per_symbol + self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) + + # the clock recovery block tracks the symbol clock and resamples as needed. + # the output of the block is a stream of soft symbols (float) + self.clock_recovery = gr.clock_recovery_mm_ff(self._omega, self._gain_omega, + self._mu, self._gain_mu, + self._omega_relative_limit) + + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample + self.slicer = gr.binary_slicer_fb() + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "M&M clock recovery omega = %f" % self._omega + print "M&M clock recovery gain mu = %f" % self._gain_mu + print "M&M clock recovery mu = %f" % self._mu + print "M&M clock recovery omega rel. limit = %f" % self._omega_relative_limit + print "frequency error = %f" % self._freq_error + + + def _setup_logging(self): + print "Demodulation logging turned on." + self.connect(self.fmdemod, + gr.file_sink(gr.sizeof_float, "fmdemod.dat")) + self.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_float, "clock_recovery.dat")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + + def add_options(parser): + """ + Adds GMSK demodulation-specific options to the standard parser + """ + parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu, + help="M&M clock recovery gain mu [default=%default] (GMSK/PSK)") + parser.add_option("", "--mu", type="float", default=_def_mu, + help="M&M clock recovery mu [default=%default] (GMSK/PSK)") + parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit, + help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)") + parser.add_option("", "--freq-error", type="float", default=_def_freq_error, + help="M&M clock recovery frequency error [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('gmsk', gmsk_mod) +modulation_utils.add_type_1_demod('gmsk', gmsk_demod) diff --git a/gr-digital/python/modulation_utils.py b/gr-digital/python/modulation_utils.py new file mode 100644 index 000000000..71ba77389 --- /dev/null +++ b/gr-digital/python/modulation_utils.py @@ -0,0 +1,81 @@ +# +# Copyright 2006 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Miscellaneous utilities for managing mods and demods, as well as other items +useful in dealing with generalized handling of different modulations and demods. +""" + +import inspect + + +# Type 1 modulators accept a stream of bytes on their input and produce complex baseband output +_type_1_modulators = {} + +def type_1_mods(): + return _type_1_modulators + +def add_type_1_mod(name, mod_class): + _type_1_modulators[name] = mod_class + + +# Type 1 demodulators accept complex baseband input and produce a stream of bits, packed +# 1 bit / byte as their output. Their output is completely unambiguous. There is no need +# to resolve phase or polarity ambiguities. +_type_1_demodulators = {} + +def type_1_demods(): + return _type_1_demodulators + +def add_type_1_demod(name, demod_class): + _type_1_demodulators[name] = demod_class + + +def extract_kwargs_from_options(function, excluded_args, options): + """ + Given a function, a list of excluded arguments and the result of + parsing command line options, create a dictionary of key word + arguments suitable for passing to the function. The dictionary + will be populated with key/value pairs where the keys are those + that are common to the function's argument list (minus the + excluded_args) and the attributes in options. The values are the + corresponding values from options unless that value is None. + In that case, the corresponding dictionary entry is not populated. + + (This allows different modulations that have the same parameter + names, but different default values to coexist. The downside is + that --help in the option parser will list the default as None, + but in that case the default provided in the __init__ argument + list will be used since there is no kwargs entry.) + + @param function: the function whose parameter list will be examined + @param excluded_args: function arguments that are NOT to be added to the dictionary + @type excluded_args: sequence of strings + @param options: result of command argument parsing + @type options: optparse.Values + """ + # Try this in C++ ;) + args, varargs, varkw, defaults = inspect.getargspec(function) + d = {} + for kw in [a for a in args if a not in excluded_args]: + if hasattr(options, kw): + if getattr(options, kw) is not None: + d[kw] = getattr(options, kw) + return d diff --git a/gr-digital/python/packet_utils.py b/gr-digital/python/packet_utils.py new file mode 100644 index 000000000..2e216ff50 --- /dev/null +++ b/gr-digital/python/packet_utils.py @@ -0,0 +1,457 @@ +# +# Copyright 2005,2006,2007 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. +# + +import struct +import numpy +from gnuradio import gru +import crc + +def conv_packed_binary_string_to_1_0_string(s): + """ + '\xAF' --> '10101111' + """ + r = [] + for ch in s: + x = ord(ch) + for i in range(7,-1,-1): + t = (x >> i) & 0x1 + r.append(t) + + return ''.join(map(lambda x: chr(x + ord('0')), r)) + +def conv_1_0_string_to_packed_binary_string(s): + """ + '10101111' -> ('\xAF', False) + + Basically the inverse of conv_packed_binary_string_to_1_0_string, + but also returns a flag indicating if we had to pad with leading zeros + to get to a multiple of 8. + """ + if not is_1_0_string(s): + raise ValueError, "Input must be a string containing only 0's and 1's" + + # pad to multiple of 8 + padded = False + rem = len(s) % 8 + if rem != 0: + npad = 8 - rem + s = '0' * npad + s + padded = True + + assert len(s) % 8 == 0 + + r = [] + i = 0 + while i < len(s): + t = 0 + for j in range(8): + t = (t << 1) | (ord(s[i + j]) - ord('0')) + r.append(chr(t)) + i += 8 + return (''.join(r), padded) + + +default_access_code = \ + conv_packed_binary_string_to_1_0_string('\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC') +preamble = \ + conv_packed_binary_string_to_1_0_string('\xA4\xF2') + +def is_1_0_string(s): + if not isinstance(s, str): + return False + for ch in s: + if not ch in ('0', '1'): + return False + return True + +def string_to_hex_list(s): + return map(lambda x: hex(ord(x)), s) + + +def whiten(s, o): + sa = numpy.fromstring(s, numpy.uint8) + z = sa ^ random_mask_vec8[o:len(sa)+o] + return z.tostring() + +def dewhiten(s, o): + return whiten(s, o) # self inverse + + +def make_header(payload_len, whitener_offset=0): + # Upper nibble is offset, lower 12 bits is len + val = ((whitener_offset & 0xf) << 12) | (payload_len & 0x0fff) + #print "offset =", whitener_offset, " len =", payload_len, " val=", val + return struct.pack('!HH', val, val) + +def make_packet(payload, samples_per_symbol, bits_per_symbol, + access_code=default_access_code, pad_for_usrp=True, + whitener_offset=0, whitening=True): + """ + Build a packet, given access code, payload, and whitener offset + + @param payload: packet payload, len [0, 4096] + @param samples_per_symbol: samples per symbol (needed for padding calculation) + @type samples_per_symbol: int + @param bits_per_symbol: (needed for padding calculation) + @type bits_per_symbol: int + @param access_code: string of ascii 0's and 1's + @param whitener_offset offset into whitener string to use [0-16) + + Packet will have access code at the beginning, followed by length, payload + and finally CRC-32. + """ + if not is_1_0_string(access_code): + raise ValueError, "access_code must be a string containing only 0's and 1's (%r)" % (access_code,) + + if not whitener_offset >=0 and whitener_offset < 16: + raise ValueError, "whitener_offset must be between 0 and 15, inclusive (%i)" % (whitener_offset,) + + (packed_access_code, padded) = conv_1_0_string_to_packed_binary_string(access_code) + (packed_preamble, ignore) = conv_1_0_string_to_packed_binary_string(preamble) + + payload_with_crc = crc.gen_and_append_crc32(payload) + #print "outbound crc =", string_to_hex_list(payload_with_crc[-4:]) + + L = len(payload_with_crc) + MAXLEN = len(random_mask_tuple) + if L > MAXLEN: + raise ValueError, "len(payload) must be in [0, %d]" % (MAXLEN,) + + if whitening: + pkt = ''.join((packed_preamble, packed_access_code, make_header(L, whitener_offset), + whiten(payload_with_crc, whitener_offset), '\x55')) + else: + pkt = ''.join((packed_preamble, packed_access_code, make_header(L, whitener_offset), + (payload_with_crc), '\x55')) + + if pad_for_usrp: + pkt = pkt + (_npadding_bytes(len(pkt), int(samples_per_symbol), bits_per_symbol) * '\x55') + + #print "make_packet: len(pkt) =", len(pkt) + return pkt + +def _npadding_bytes(pkt_byte_len, samples_per_symbol, bits_per_symbol): + """ + Generate sufficient padding such that each packet ultimately ends + up being a multiple of 512 bytes when sent across the USB. We + send 4-byte samples across the USB (16-bit I and 16-bit Q), thus + we want to pad so that after modulation the resulting packet + is a multiple of 128 samples. + + @param ptk_byte_len: len in bytes of packet, not including padding. + @param samples_per_symbol: samples per bit (1 bit / symbolwidth GMSK) + @type samples_per_symbol: int + @param bits_per_symbol: bits per symbol (log2(modulation order)) + @type bits_per_symbol: int + + @returns number of bytes of padding to append. + """ + modulus = 128 + byte_modulus = gru.lcm(modulus/8, samples_per_symbol) * bits_per_symbol / samples_per_symbol + r = pkt_byte_len % byte_modulus + if r == 0: + return 0 + return byte_modulus - r + + +def unmake_packet(whitened_payload_with_crc, whitener_offset=0, dewhitening=True): + """ + Return (ok, payload) + + @param whitened_payload_with_crc: string + """ + + if dewhitening: + payload_with_crc = dewhiten(whitened_payload_with_crc, whitener_offset) + else: + payload_with_crc = (whitened_payload_with_crc) + + ok, payload = crc.check_crc32(payload_with_crc) + + if 0: + print "payload_with_crc =", string_to_hex_list(payload_with_crc) + print "ok = %r, len(payload) = %d" % (ok, len(payload)) + print "payload =", string_to_hex_list(payload) + + return ok, payload + + +# FYI, this PN code is the output of a 15-bit LFSR +random_mask_tuple = ( + 255, 63, 0, 16, 0, 12, 0, 5, 192, 3, 16, 1, 204, 0, 85, 192, + 63, 16, 16, 12, 12, 5, 197, 195, 19, 17, 205, 204, 85, 149, 255, 47, + 0, 28, 0, 9, 192, 6, 208, 2, 220, 1, 153, 192, 106, 208, 47, 28, + 28, 9, 201, 198, 214, 210, 222, 221, 152, 89, 170, 186, 255, 51, 0, 21, + 192, 15, 16, 4, 12, 3, 69, 193, 243, 16, 69, 204, 51, 21, 213, 207, + 31, 20, 8, 15, 70, 132, 50, 227, 85, 137, 255, 38, 192, 26, 208, 11, + 28, 7, 73, 194, 182, 209, 182, 220, 118, 217, 230, 218, 202, 219, 23, 27, + 78, 139, 116, 103, 103, 106, 170, 175, 63, 60, 16, 17, 204, 12, 85, 197, + 255, 19, 0, 13, 192, 5, 144, 3, 44, 1, 221, 192, 89, 144, 58, 236, + 19, 13, 205, 197, 149, 147, 47, 45, 220, 29, 153, 201, 170, 214, 255, 30, + 192, 8, 80, 6, 188, 2, 241, 193, 132, 80, 99, 124, 41, 225, 222, 200, + 88, 86, 186, 190, 243, 48, 69, 212, 51, 31, 85, 200, 63, 22, 144, 14, + 236, 4, 77, 195, 117, 145, 231, 44, 74, 157, 247, 41, 134, 158, 226, 232, + 73, 142, 182, 228, 118, 203, 102, 215, 106, 222, 175, 24, 124, 10, 161, 199, + 56, 82, 146, 189, 173, 177, 189, 180, 113, 183, 100, 118, 171, 102, 255, 106, + 192, 47, 16, 28, 12, 9, 197, 198, 211, 18, 221, 205, 153, 149, 170, 239, + 63, 12, 16, 5, 204, 3, 21, 193, 207, 16, 84, 12, 63, 69, 208, 51, + 28, 21, 201, 207, 22, 212, 14, 223, 68, 88, 51, 122, 149, 227, 47, 9, + 220, 6, 217, 194, 218, 209, 155, 28, 107, 73, 239, 118, 204, 38, 213, 218, + 223, 27, 24, 11, 74, 135, 119, 34, 166, 153, 186, 234, 243, 15, 5, 196, + 3, 19, 65, 205, 240, 85, 132, 63, 35, 80, 25, 252, 10, 193, 199, 16, + 82, 140, 61, 165, 209, 187, 28, 115, 73, 229, 246, 203, 6, 215, 66, 222, + 177, 152, 116, 106, 167, 111, 58, 172, 19, 61, 205, 209, 149, 156, 111, 41, + 236, 30, 205, 200, 85, 150, 191, 46, 240, 28, 68, 9, 243, 70, 197, 242, + 211, 5, 157, 195, 41, 145, 222, 236, 88, 77, 250, 181, 131, 55, 33, 214, + 152, 94, 234, 184, 79, 50, 180, 21, 183, 79, 54, 180, 22, 247, 78, 198, + 180, 82, 247, 125, 134, 161, 162, 248, 121, 130, 162, 225, 185, 136, 114, 230, + 165, 138, 251, 39, 3, 90, 129, 251, 32, 67, 88, 49, 250, 148, 67, 47, + 113, 220, 36, 89, 219, 122, 219, 99, 27, 105, 203, 110, 215, 108, 94, 173, + 248, 125, 130, 161, 161, 184, 120, 114, 162, 165, 185, 187, 50, 243, 85, 133, + 255, 35, 0, 25, 192, 10, 208, 7, 28, 2, 137, 193, 166, 208, 122, 220, + 35, 25, 217, 202, 218, 215, 27, 30, 139, 72, 103, 118, 170, 166, 255, 58, + 192, 19, 16, 13, 204, 5, 149, 195, 47, 17, 220, 12, 89, 197, 250, 211, + 3, 29, 193, 201, 144, 86, 236, 62, 205, 208, 85, 156, 63, 41, 208, 30, + 220, 8, 89, 198, 186, 210, 243, 29, 133, 201, 163, 22, 249, 206, 194, 212, + 81, 159, 124, 104, 33, 238, 152, 76, 106, 181, 239, 55, 12, 22, 133, 206, + 227, 20, 73, 207, 118, 212, 38, 223, 90, 216, 59, 26, 147, 75, 45, 247, + 93, 134, 185, 162, 242, 249, 133, 130, 227, 33, 137, 216, 102, 218, 170, 219, + 63, 27, 80, 11, 124, 7, 97, 194, 168, 81, 190, 188, 112, 113, 228, 36, + 75, 91, 119, 123, 102, 163, 106, 249, 239, 2, 204, 1, 149, 192, 111, 16, + 44, 12, 29, 197, 201, 147, 22, 237, 206, 205, 148, 85, 175, 127, 60, 32, + 17, 216, 12, 90, 133, 251, 35, 3, 89, 193, 250, 208, 67, 28, 49, 201, + 212, 86, 223, 126, 216, 32, 90, 152, 59, 42, 147, 95, 45, 248, 29, 130, + 137, 161, 166, 248, 122, 194, 163, 17, 185, 204, 114, 213, 229, 159, 11, 40, + 7, 94, 130, 184, 97, 178, 168, 117, 190, 167, 48, 122, 148, 35, 47, 89, + 220, 58, 217, 211, 26, 221, 203, 25, 151, 74, 238, 183, 12, 118, 133, 230, + 227, 10, 201, 199, 22, 210, 142, 221, 164, 89, 187, 122, 243, 99, 5, 233, + 195, 14, 209, 196, 92, 83, 121, 253, 226, 193, 137, 144, 102, 236, 42, 205, + 223, 21, 152, 15, 42, 132, 31, 35, 72, 25, 246, 138, 198, 231, 18, 202, + 141, 151, 37, 174, 155, 60, 107, 81, 239, 124, 76, 33, 245, 216, 71, 26, + 178, 139, 53, 167, 87, 58, 190, 147, 48, 109, 212, 45, 159, 93, 168, 57, + 190, 146, 240, 109, 132, 45, 163, 93, 185, 249, 178, 194, 245, 145, 135, 44, + 98, 157, 233, 169, 142, 254, 228, 64, 75, 112, 55, 100, 22, 171, 78, 255, + 116, 64, 39, 112, 26, 164, 11, 59, 71, 83, 114, 189, 229, 177, 139, 52, + 103, 87, 106, 190, 175, 48, 124, 20, 33, 207, 88, 84, 58, 191, 83, 48, + 61, 212, 17, 159, 76, 104, 53, 238, 151, 12, 110, 133, 236, 99, 13, 233, + 197, 142, 211, 36, 93, 219, 121, 155, 98, 235, 105, 143, 110, 228, 44, 75, + 93, 247, 121, 134, 162, 226, 249, 137, 130, 230, 225, 138, 200, 103, 22, 170, + 142, 255, 36, 64, 27, 112, 11, 100, 7, 107, 66, 175, 113, 188, 36, 113, + 219, 100, 91, 107, 123, 111, 99, 108, 41, 237, 222, 205, 152, 85, 170, 191, + 63, 48, 16, 20, 12, 15, 69, 196, 51, 19, 85, 205, 255, 21, 128, 15, + 32, 4, 24, 3, 74, 129, 247, 32, 70, 152, 50, 234, 149, 143, 47, 36, + 28, 27, 73, 203, 118, 215, 102, 222, 170, 216, 127, 26, 160, 11, 56, 7, + 82, 130, 189, 161, 177, 184, 116, 114, 167, 101, 186, 171, 51, 63, 85, 208, + 63, 28, 16, 9, 204, 6, 213, 194, 223, 17, 152, 12, 106, 133, 239, 35, + 12, 25, 197, 202, 211, 23, 29, 206, 137, 148, 102, 239, 106, 204, 47, 21, + 220, 15, 25, 196, 10, 211, 71, 29, 242, 137, 133, 166, 227, 58, 201, 211, + 22, 221, 206, 217, 148, 90, 239, 123, 12, 35, 69, 217, 243, 26, 197, 203, + 19, 23, 77, 206, 181, 148, 119, 47, 102, 156, 42, 233, 223, 14, 216, 4, + 90, 131, 123, 33, 227, 88, 73, 250, 182, 195, 54, 209, 214, 220, 94, 217, + 248, 90, 194, 187, 17, 179, 76, 117, 245, 231, 7, 10, 130, 135, 33, 162, + 152, 121, 170, 162, 255, 57, 128, 18, 224, 13, 136, 5, 166, 131, 58, 225, + 211, 8, 93, 198, 185, 146, 242, 237, 133, 141, 163, 37, 185, 219, 50, 219, + 85, 155, 127, 43, 96, 31, 104, 8, 46, 134, 156, 98, 233, 233, 142, 206, + 228, 84, 75, 127, 119, 96, 38, 168, 26, 254, 139, 0, 103, 64, 42, 176, + 31, 52, 8, 23, 70, 142, 178, 228, 117, 139, 103, 39, 106, 154, 175, 43, + 60, 31, 81, 200, 60, 86, 145, 254, 236, 64, 77, 240, 53, 132, 23, 35, + 78, 153, 244, 106, 199, 111, 18, 172, 13, 189, 197, 177, 147, 52, 109, 215, + 109, 158, 173, 168, 125, 190, 161, 176, 120, 116, 34, 167, 89, 186, 186, 243, + 51, 5, 213, 195, 31, 17, 200, 12, 86, 133, 254, 227, 0, 73, 192, 54, + 208, 22, 220, 14, 217, 196, 90, 211, 123, 29, 227, 73, 137, 246, 230, 198, + 202, 210, 215, 29, 158, 137, 168, 102, 254, 170, 192, 127, 16, 32, 12, 24, + 5, 202, 131, 23, 33, 206, 152, 84, 106, 191, 111, 48, 44, 20, 29, 207, + 73, 148, 54, 239, 86, 204, 62, 213, 208, 95, 28, 56, 9, 210, 134, 221, + 162, 217, 185, 154, 242, 235, 5, 143, 67, 36, 49, 219, 84, 91, 127, 123, + 96, 35, 104, 25, 238, 138, 204, 103, 21, 234, 143, 15, 36, 4, 27, 67, + 75, 113, 247, 100, 70, 171, 114, 255, 101, 128, 43, 32, 31, 88, 8, 58, + 134, 147, 34, 237, 217, 141, 154, 229, 171, 11, 63, 71, 80, 50, 188, 21, + 177, 207, 52, 84, 23, 127, 78, 160, 52, 120, 23, 98, 142, 169, 164, 126, + 251, 96, 67, 104, 49, 238, 148, 76, 111, 117, 236, 39, 13, 218, 133, 155, + 35, 43, 89, 223, 122, 216, 35, 26, 153, 203, 42, 215, 95, 30, 184, 8, + 114, 134, 165, 162, 251, 57, 131, 82, 225, 253, 136, 65, 166, 176, 122, 244, + 35, 7, 89, 194, 186, 209, 179, 28, 117, 201, 231, 22, 202, 142, 215, 36, + 94, 155, 120, 107, 98, 175, 105, 188, 46, 241, 220, 68, 89, 243, 122, 197, + 227, 19, 9, 205, 198, 213, 146, 223, 45, 152, 29, 170, 137, 191, 38, 240, + 26, 196, 11, 19, 71, 77, 242, 181, 133, 183, 35, 54, 153, 214, 234, 222, + 207, 24, 84, 10, 191, 71, 48, 50, 148, 21, 175, 79, 60, 52, 17, 215, + 76, 94, 181, 248, 119, 2, 166, 129, 186, 224, 115, 8, 37, 198, 155, 18, + 235, 77, 143, 117, 164, 39, 59, 90, 147, 123, 45, 227, 93, 137, 249, 166, + 194, 250, 209, 131, 28, 97, 201, 232, 86, 206, 190, 212, 112, 95, 100, 56, + 43, 82, 159, 125, 168, 33, 190, 152, 112, 106, 164, 47, 59, 92, 19, 121, + 205, 226, 213, 137, 159, 38, 232, 26, 206, 139, 20, 103, 79, 106, 180, 47, + 55, 92, 22, 185, 206, 242, 212, 69, 159, 115, 40, 37, 222, 155, 24, 107, + 74, 175, 119, 60, 38, 145, 218, 236, 91, 13, 251, 69, 131, 115, 33, 229, + 216, 75, 26, 183, 75, 54, 183, 86, 246, 190, 198, 240, 82, 196, 61, 147, + 81, 173, 252, 125, 129, 225, 160, 72, 120, 54, 162, 150, 249, 174, 194, 252, + 81, 129, 252, 96, 65, 232, 48, 78, 148, 52, 111, 87, 108, 62, 173, 208, + 125, 156, 33, 169, 216, 126, 218, 160, 91, 56, 59, 82, 147, 125, 173, 225, + 189, 136, 113, 166, 164, 122, 251, 99, 3, 105, 193, 238, 208, 76, 92, 53, + 249, 215, 2, 222, 129, 152, 96, 106, 168, 47, 62, 156, 16, 105, 204, 46, + 213, 220, 95, 25, 248, 10, 194, 135, 17, 162, 140, 121, 165, 226, 251, 9, + 131, 70, 225, 242, 200, 69, 150, 179, 46, 245, 220, 71, 25, 242, 138, 197, + 167, 19, 58, 141, 211, 37, 157, 219, 41, 155, 94, 235, 120, 79, 98, 180, + 41, 183, 94, 246, 184, 70, 242, 178, 197, 181, 147, 55, 45, 214, 157, 158, + 233, 168, 78, 254, 180, 64, 119, 112, 38, 164, 26, 251, 75, 3, 119, 65, + 230, 176, 74, 244, 55, 7, 86, 130, 190, 225, 176, 72, 116, 54, 167, 86, + 250, 190, 195, 48, 81, 212, 60, 95, 81, 248, 60, 66, 145, 241, 172, 68, + 125, 243, 97, 133, 232, 99, 14, 169, 196, 126, 211, 96, 93, 232, 57, 142, + 146, 228, 109, 139, 109, 167, 109, 186, 173, 179, 61, 181, 209, 183, 28, 118, + 137, 230, 230, 202, 202, 215, 23, 30, 142, 136, 100, 102, 171, 106, 255, 111, + 0, 44, 0, 29, 192, 9, 144, 6, 236, 2, 205, 193, 149, 144, 111, 44, + 44, 29, 221, 201, 153, 150, 234, 238, 207, 12, 84, 5, 255, 67, 0, 49, + 192, 20, 80, 15, 124, 4, 33, 195, 88, 81, 250, 188, 67, 49, 241, 212, + 68, 95, 115, 120, 37, 226, 155, 9, 171, 70, 255, 114, 192, 37, 144, 27, + 44, 11, 93, 199, 121, 146, 162, 237, 185, 141, 178, 229, 181, 139, 55, 39, + 86, 154, 190, 235, 48, 79, 84, 52, 63, 87, 80, 62, 188, 16, 113, 204, + 36, 85, 219, 127, 27, 96, 11, 104, 7, 110, 130, 172, 97, 189, 232, 113, + 142, 164, 100, 123, 107, 99, 111, 105, 236, 46, 205, 220, 85, 153, 255, 42, + 192, 31, 16, 8, 12, 6, 133, 194, 227, 17, 137, 204, 102, 213, 234, 223, + 15, 24, 4, 10, 131, 71, 33, 242, 152, 69, 170, 179, 63, 53, 208, 23, + 28, 14, 137, 196, 102, 211, 106, 221, 239, 25, 140, 10, 229, 199, 11, 18, + 135, 77, 162, 181, 185, 183, 50, 246, 149, 134, 239, 34, 204, 25, 149, 202, + 239, 23, 12, 14, 133, 196, 99, 19, 105, 205, 238, 213, 140, 95, 37, 248, + 27, 2, 139, 65, 167, 112, 122, 164, 35, 59, 89, 211, 122, 221, 227, 25, + 137, 202, 230, 215, 10, 222, 135, 24, 98, 138, 169, 167, 62, 250, 144, 67, + 44, 49, 221, 212, 89, 159, 122, 232, 35, 14, 153, 196, 106, 211, 111, 29, + 236, 9, 141, 198, 229, 146, 203, 45, 151, 93, 174, 185, 188, 114, 241, 229, + 132, 75, 35, 119, 89, 230, 186, 202, 243, 23, 5, 206, 131, 20, 97, 207, + 104, 84, 46, 191, 92, 112, 57, 228, 18, 203, 77, 151, 117, 174, 167, 60, + 122, 145, 227, 44, 73, 221, 246, 217, 134, 218, 226, 219, 9, 155, 70, 235, + 114, 207, 101, 148, 43, 47, 95, 92, 56, 57, 210, 146, 221, 173, 153, 189, + 170, 241, 191, 4, 112, 3, 100, 1, 235, 64, 79, 112, 52, 36, 23, 91, + 78, 187, 116, 115, 103, 101, 234, 171, 15, 63, 68, 16, 51, 76, 21, 245, + 207, 7, 20, 2, 143, 65, 164, 48, 123, 84, 35, 127, 89, 224, 58, 200, + 19, 22, 141, 206, 229, 148, 75, 47, 119, 92, 38, 185, 218, 242, 219, 5, + 155, 67, 43, 113, 223, 100, 88, 43, 122, 159, 99, 40, 41, 222, 158, 216, + 104, 90, 174, 187, 60, 115, 81, 229, 252, 75, 1, 247, 64, 70, 176, 50, + 244, 21, 135, 79, 34, 180, 25, 183, 74, 246, 183, 6, 246, 130, 198, 225, + 146, 200, 109, 150, 173, 174, 253, 188, 65, 177, 240, 116, 68, 39, 115, 90, + 165, 251, 59, 3, 83, 65, 253, 240, 65, 132, 48, 99, 84, 41, 255, 94, + 192, 56, 80, 18, 188, 13, 177, 197, 180, 83, 55, 125, 214, 161, 158, 248, + 104, 66, 174, 177, 188, 116, 113, 231, 100, 74, 171, 119, 63, 102, 144, 42, + 236, 31, 13, 200, 5, 150, 131, 46, 225, 220, 72, 89, 246, 186, 198, 243, + 18, 197, 205, 147, 21, 173, 207, 61, 148, 17, 175, 76, 124, 53, 225, 215, + 8, 94, 134, 184, 98, 242, 169, 133, 190, 227, 48, 73, 212, 54, 223, 86, + 216, 62, 218, 144, 91, 44, 59, 93, 211, 121, 157, 226, 233, 137, 142, 230, + 228, 74, 203, 119, 23, 102, 142, 170, 228, 127, 11, 96, 7, 104, 2, 174, + 129, 188, 96, 113, 232, 36, 78, 155, 116, 107, 103, 111, 106, 172, 47, 61, + 220, 17, 153, 204, 106, 213, 239, 31, 12, 8, 5, 198, 131, 18, 225, 205, + 136, 85, 166, 191, 58, 240, 19, 4, 13, 195, 69, 145, 243, 44, 69, 221, + 243, 25, 133, 202, 227, 23, 9, 206, 134, 212, 98, 223, 105, 152, 46, 234, + 156, 79, 41, 244, 30, 199, 72, 82, 182, 189, 182, 241, 182, 196, 118, 211, + 102, 221, 234, 217, 143, 26, 228, 11, 11, 71, 71, 114, 178, 165, 181, 187, + 55, 51, 86, 149, 254, 239, 0, 76, 0, 53, 192, 23, 16, 14, 140, 4, + 101, 195, 107, 17, 239, 76, 76, 53, 245, 215, 7, 30, 130, 136, 97, 166, + 168, 122, 254, 163, 0, 121, 192, 34, 208, 25, 156, 10, 233, 199, 14, 210, + 132, 93, 163, 121, 185, 226, 242, 201, 133, 150, 227, 46, 201, 220, 86, 217, + 254, 218, 192, 91, 16, 59, 76, 19, 117, 205, 231, 21, 138, 143, 39, 36, + 26, 155, 75, 43, 119, 95, 102, 184, 42, 242, 159, 5, 168, 3, 62, 129, + 208, 96, 92, 40, 57, 222, 146, 216, 109, 154, 173, 171, 61, 191, 81, 176, + 60, 116, 17, 231, 76, 74, 181, 247, 55, 6, 150, 130, 238, 225, 140, 72, + 101, 246, 171, 6, 255, 66, 192, 49, 144, 20, 108, 15, 109, 196, 45, 147, + 93, 173, 249, 189, 130, 241, 161, 132, 120, 99, 98, 169, 233, 190, 206, 240, + 84, 68, 63, 115, 80, 37, 252, 27, 1, 203, 64, 87, 112, 62, 164, 16, + 123, 76, 35, 117, 217, 231, 26, 202, 139, 23, 39, 78, 154, 180, 107, 55, + 111, 86, 172, 62, 253, 208, 65, 156, 48, 105, 212, 46, 223, 92, 88, 57, + 250, 146, 195, 45, 145, 221, 172, 89, 189, 250, 241, 131, 4, 97, 195, 104, + 81, 238, 188, 76, 113, 245, 228, 71, 11, 114, 135, 101, 162, 171, 57, 191, + 82, 240, 61, 132, 17, 163, 76, 121, 245, 226, 199, 9, 146, 134, 237, 162, + 205, 185, 149, 178, 239, 53, 140, 23, 37, 206, 155, 20, 107, 79, 111, 116, + 44, 39, 93, 218, 185, 155, 50, 235, 85, 143, 127, 36, 32, 27, 88, 11, + 122, 135, 99, 34, 169, 217, 190, 218, 240, 91, 4, 59, 67, 83, 113, 253, + 228, 65, 139, 112, 103, 100, 42, 171, 95, 63, 120, 16, 34, 140, 25, 165, + 202, 251, 23, 3, 78, 129, 244, 96, 71, 104, 50, 174, 149, 188, 111, 49, + 236, 20, 77, 207, 117, 148, 39, 47, 90, 156, 59, 41, 211, 94, 221, 248, + 89, 130, 186, 225, 179, 8, 117, 198, 167, 18, 250, 141, 131, 37, 161, 219, + 56, 91, 82, 187, 125, 179, 97, 181, 232, 119, 14, 166, 132, 122, 227, 99, + 9, 233, 198, 206, 210, 212, 93, 159, 121, 168, 34, 254, 153, 128, 106, 224, + 47, 8, 28, 6, 137, 194, 230, 209, 138, 220, 103, 25, 234, 138, 207, 39, + 20, 26, 143, 75, 36, 55, 91, 86, 187, 126, 243, 96, 69, 232, 51, 14, + 149, 196, 111, 19, 108, 13, 237, 197, 141, 147, 37, 173, 219, 61, 155, 81, + 171, 124, 127, 97, 224, 40, 72, 30, 182, 136, 118, 230, 166, 202, 250, 215, + 3, 30, 129, 200, 96, 86, 168, 62, 254, 144, 64, 108, 48, 45, 212, 29, + 159, 73, 168, 54, 254, 150, 192, 110, 208, 44, 92, 29, 249, 201, 130, 214, + 225, 158, 200, 104, 86, 174, 190, 252, 112, 65, 228, 48, 75, 84, 55, 127, + 86, 160, 62, 248, 16, 66, 140, 49, 165, 212, 123, 31, 99, 72, 41, 246, + 158, 198, 232, 82, 206, 189, 148, 113, 175, 100, 124, 43, 97, 223, 104, 88, + 46, 186, 156, 115, 41, 229, 222, 203, 24, 87, 74, 190, 183, 48, 118, 148, + 38, 239, 90, 204, 59, 21, 211, 79, 29, 244, 9, 135, 70, 226, 178, 201, + 181, 150, 247, 46, 198, 156, 82, 233, 253, 142, 193, 164, 80, 123, 124, 35, + 97, 217, 232, 90, 206, 187, 20, 115, 79, 101, 244, 43, 7, 95, 66, 184, + 49, 178, 148, 117, 175, 103, 60, 42, 145, 223, 44, 88, 29, 250, 137, 131, + 38, 225, 218, 200, 91, 22, 187, 78, 243, 116, 69, 231, 115, 10, 165, 199, + 59, 18, 147, 77, 173, 245, 189, 135, 49, 162, 148, 121, 175, 98, 252, 41, + 129, 222, 224, 88, 72, 58, 182, 147, 54, 237, 214, 205, 158, 213, 168, 95, + 62, 184, 16, 114, 140, 37, 165, 219, 59, 27, 83, 75, 125, 247, 97, 134, + 168, 98, 254, 169, 128, 126, 224, 32, 72, 24, 54, 138, 150, 231, 46, 202, + 156, 87, 41, 254, 158, 192, 104, 80, 46, 188, 28, 113, 201, 228, 86, 203, + 126, 215, 96, 94, 168, 56, 126, 146, 160, 109, 184, 45, 178, 157, 181, 169, + 183, 62, 246, 144, 70, 236, 50, 205, 213, 149, 159, 47, 40, 28, 30, 137, + 200, 102, 214, 170, 222, 255, 24, 64, 10, 176, 7, 52, 2, 151, 65, 174, + 176, 124, 116, 33, 231, 88, 74, 186, 183, 51, 54, 149, 214, 239, 30, 204, + 8, 85, 198, 191, 18, 240, 13, 132, 5, 163, 67, 57, 241, 210, 196, 93, + 147, 121, 173, 226, 253, 137, 129, 166, 224, 122, 200, 35, 22, 153, 206, 234, + 212, 79, 31, 116, 8, 39, 70, 154, 178, 235, 53, 143, 87, 36, 62, 155, + 80, 107, 124, 47, 97, 220, 40, 89, 222, 186, 216, 115, 26, 165, 203, 59, + 23, 83, 78, 189, 244, 113, 135, 100, 98, 171, 105, 191, 110, 240, 44, 68, + 29, 243, 73, 133, 246, 227, 6, 201, 194, 214, 209, 158, 220, 104, 89, 238, + 186, 204, 115, 21, 229, 207, 11, 20, 7, 79, 66, 180, 49, 183, 84, 118, + 191, 102, 240, 42, 196, 31, 19, 72, 13, 246, 133, 134, 227, 34, 201, 217, + 150, 218, 238, 219, 12, 91, 69, 251, 115, 3, 101, 193, 235, 16, 79, 76, + 52, 53, 215, 87, 30, 190, 136, 112, 102, 164, 42, 251, 95, 3, 120, 1, + 226, 128, 73, 160, 54, 248, 22, 194, 142, 209, 164, 92, 123, 121, 227, 98, + 201, 233, 150, 206, 238, 212, 76, 95, 117, 248, 39, 2, 154, 129, 171, 32, + 127, 88, 32, 58, 152, 19, 42, 141, 223, 37, 152, 27, 42, 139, 95, 39, + 120, 26, 162, 139, 57, 167, 82, 250, 189, 131, 49, 161, 212, 120, 95, 98, + 184, 41, 178, 158, 245, 168, 71, 62, 178, 144, 117, 172, 39, 61, 218, 145, + 155, 44, 107, 93, 239, 121, 140, 34, 229, 217, 139, 26, 231, 75, 10, 183, + 71, 54, 178, 150, 245, 174, 199, 60, 82, 145, 253, 172, 65, 189, 240, 113, + 132, 36, 99, 91, 105, 251, 110, 195, 108, 81, 237, 252, 77, 129, 245, 160, + 71, 56, 50, 146, 149, 173, 175, 61, 188, 17, 177, 204, 116, 85, 231, 127, + 10, 160, 7, 56, 2, 146, 129, 173, 160, 125, 184, 33, 178, 152, 117, 170, + 167, 63, 58, 144, 19, 44, 13, 221, 197, 153, 147, 42, 237, 223, 13, 152, + 5, 170, 131, 63, 33, 208, 24, 92, 10, 185, 199, 50, 210, 149, 157, 175, + 41, 188, 30, 241, 200, 68, 86, 179, 126, 245, 224, 71, 8, 50, 134, 149, + 162, 239, 57, 140, 18, 229, 205, 139, 21, 167, 79, 58, 180, 19, 55, 77, + 214, 181, 158, 247, 40, 70, 158, 178, 232, 117, 142, 167, 36, 122, 155, 99, + 43, 105, 223, 110, 216, 44, 90, 157, 251, 41, 131, 94, 225, 248, 72, 66, + 182, 177, 182, 244, 118, 199, 102, 210, 170, 221, 191, 25, 176, 10, 244, 7, + 7, 66, 130, 177, 161, 180, 120, 119, 98, 166, 169, 186, 254, 243, 0, 69, + 192, 51, 16, 21, 204, 15, 21, 196, 15, 19, 68, 13, 243, 69, 133, 243, + 35, 5, 217, 195, 26, 209, 203, 28, 87, 73, 254, 182, 192, 118, 208, 38, + 220, 26, 217, 203, 26, 215, 75, 30, 183, 72, 118, 182, 166, 246, 250, 198, + 195, 18, 209, 205, 156, 85, 169, 255, 62, 192, 16, 80, 12, 60, 5, 209, + 195, 28, 81, 201, 252, 86, 193, 254, 208, 64, 92, 48, 57, 212, 18, 223, + 77, 152, 53, 170, 151, 63, 46, 144, 28, 108, 9, 237, 198, 205, 146, 213, + 173, 159, 61, 168, 17, 190, 140, 112, 101, 228, 43, 11, 95, 71, 120, 50, + 162, 149, 185, 175, 50, 252, 21, 129, 207, 32, 84, 24, 63, 74, 144, 55, + 44, 22, 157, 206, 233, 148, 78, 239, 116, 76, 39, 117, 218, 167, 27, 58, + 139, 83, 39, 125, 218, 161, 155, 56, 107, 82, 175, 125, 188, 33, 177, 216, + 116, 90, 167, 123, 58, 163, 83, 57, 253, 210, 193, 157, 144, 105, 172, 46, + 253, 220, 65, 153, 240, 106, 196, 47, 19, 92, 13, 249, 197, 130, 211, 33, + 157, 216, 105, 154, 174, 235, 60, 79, 81, 244, 60, 71, 81, 242, 188, 69, + 177, 243, 52, 69, 215, 115, 30, 165, 200, 123, 22, 163, 78, 249, 244, 66, + 199, 113, 146, 164, 109, 187, 109, 179, 109, 181, 237, 183, 13, 182, 133, 182, + 227, 54, 201, 214, 214, 222, 222, 216, 88, 90, 186, 187, 51, 51, 255, 63 ) + +random_mask_vec8 = numpy.array(random_mask_tuple, numpy.uint8) + diff --git a/gr-digital/python/pkt.py b/gr-digital/python/pkt.py index aa720d1a5..c9b29bd51 100644 --- a/gr-digital/python/pkt.py +++ b/gr-digital/python/pkt.py @@ -20,8 +20,10 @@ # from math import pi -from gnuradio import gr, packet_utils +from gnuradio import gr import gnuradio.gr.gr_threading as _threading +import packet_utils +import digital_swig # ///////////////////////////////////////////////////////////////////////////// @@ -34,8 +36,8 @@ class mod_pkts(gr.hier_block2): Send packets by calling send_pkt """ - def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False, - modulate=True): + def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, + use_whitener_offset=False, modulate=True): """ Hierarchical block for sending packets @@ -154,7 +156,7 @@ class demod_pkts(gr.hier_block2): threshold = 12 # FIXME raise exception self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY - self.correlator = gr.correlate_access_code_bb(access_code, threshold) + self.correlator = digital_swig.correlate_access_code_bb(access_code, threshold) self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) if self._demodulator is not None: diff --git a/gr-digital/python/psk2.py b/gr-digital/python/psk2.py index 778c1e5e5..82781e63b 100644 --- a/gr-digital/python/psk2.py +++ b/gr-digital/python/psk2.py @@ -26,7 +26,6 @@ PSK modulation and demodulation. from math import pi, log from cmath import exp -from gnuradio import gr import digital_swig import modulation_utils2 from utils import mod_codes, gray_code diff --git a/gr-digital/python/qa_binary_slicer_fb.py b/gr-digital/python/qa_binary_slicer_fb.py new file mode 100755 index 000000000..944dc1b5a --- /dev/null +++ b/gr-digital/python/qa_binary_slicer_fb.py @@ -0,0 +1,55 @@ +#!/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 +import math, random + +class test_constellation_decoder (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_binary_slicer_fb (self): + expected_result = ( 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1) + src_data = (-1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1) + src_data = [s + (1 - random.random()) for s in src_data] # add some noise + src = gr.vector_source_f (src_data) + op = digital_swig.binary_slicer_fb () + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation_decoder, "test_constellation_decoder.xml") + diff --git a/gr-digital/python/qa_clock_recovery_mm.py b/gr-digital/python/qa_clock_recovery_mm.py new file mode 100755 index 000000000..5ef86eda0 --- /dev/null +++ b/gr-digital/python/qa_clock_recovery_mm.py @@ -0,0 +1,176 @@ +#!/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, psk2 +import random, cmath + +class test_clock_recovery_mm(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # Test complex/complex version + omega = 2 + gain_omega = 0.001 + mu = 0.5 + gain_mu = 0.01 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 100*[complex(1, 1),] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 100*[complex(0.99972, 0.99972)] # doesn't quite get to 1.0 + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 30 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + + def test02 (self): + # Test float/float version + omega = 2 + gain_omega = 0.01 + mu = 0.5 + gain_mu = 0.01 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_ff(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 100*[1,] + self.src = gr.vector_source_f(data, False) + self.snk = gr.vector_sink_f() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 100*[0.99972, ] # doesn't quite get to 1.0 + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 30 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5) + + + def test03 (self): + # Test complex/complex version with varying input + omega = 2 + gain_omega = 0.01 + mu = 0.25 + gain_mu = 0.1 + omega_rel_lim = 0.0001 + + self.test = digital_swig.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 1000*[complex(1, 1), complex(1, 1), complex(-1, -1), complex(-1, -1)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(-1.2, -1.2), complex(1.2, 1.2)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + + + def test04 (self): + # Test float/float version + omega = 2 + gain_omega = 0.01 + mu = 0.25 + gain_mu = 0.1 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_ff(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 1000*[1, 1, -1, -1] + self.src = gr.vector_source_f(data, False) + self.snk = gr.vector_sink_f() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[-1.31, 1.31] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 1) + + +if __name__ == '__main__': + gr_unittest.run(test_clock_recovery_mm, "test_clock_recovery_mm.xml") diff --git a/gr-digital/python/qa_cma_equalizer.py b/gr-digital/python/qa_cma_equalizer.py new file mode 100755 index 000000000..75fb0f05e --- /dev/null +++ b/gr-digital/python/qa_cma_equalizer.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,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 + +class test_cma_equalizer_fir(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def transform(self, src_data): + SRC = gr.vector_source_c(src_data, False) + EQU = digital_swig.cma_equalizer_cc(4, 1.0, .001, 1) + DST = gr.vector_sink_c() + self.tb.connect(SRC, EQU, DST) + self.tb.run() + return DST.data() + + def test_001_identity(self): + # Constant modulus signal so no adjustments + src_data = (1+0j, 0+1j, -1+0j, 0-1j)*1000 + expected_data = src_data + result = self.transform(src_data) + self.assertComplexTuplesAlmostEqual(expected_data, result) + +if __name__ == "__main__": + gr_unittest.run(test_cma_equalizer_fir, "test_cma_equalizer_fir.xml") diff --git a/gr-digital/python/qa_constellation_decoder_cb.py b/gr-digital/python/qa_constellation_decoder_cb.py new file mode 100755 index 000000000..5401a07fc --- /dev/null +++ b/gr-digital/python/qa_constellation_decoder_cb.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010,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 +import math + +class test_constellation_decoder (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_constellation_decoder_cb_bpsk (self): + cnst = digital_swig.constellation_bpsk() + src_data = (0.5 + 0.5j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, + 0.8 + 1.0j, -0.5 + 0.1j, 0.1 - 1.2j) + expected_result = ( 1, 1, 0, 0, + 1, 0, 1) + src = gr.vector_source_c (src_data) + op = digital_swig.constellation_decoder_cb (cnst.base()) + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + def test_constellation_decoder_cb_qpsk (self): + cnst = digital_swig.constellation_qpsk() + src_data = (0.5 + 0.5j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, + 0.8 + 1.0j, -0.5 + 0.1j, 0.1 - 1.2j) + expected_result = ( 3, 1, 0, 2, + 3, 2, 1) + src = gr.vector_source_c (src_data) + op = digital_swig.constellation_decoder_cb (cnst.base()) + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation_decoder, "test_constellation_decoder.xml") + diff --git a/gr-digital/python/qa_constellation_receiver.py b/gr-digital/python/qa_constellation_receiver.py index 70b62c7aa..cb4a0c10e 100755 --- a/gr-digital/python/qa_constellation_receiver.py +++ b/gr-digital/python/qa_constellation_receiver.py @@ -22,9 +22,9 @@ import random -from gnuradio import gr, blks2, packet_utils, gr_unittest +from gnuradio import gr, blks2, gr_unittest from utils import mod_codes, alignment -import digital_swig +import digital_swig, packet_utils from generic_mod_demod import generic_mod, generic_demod from qa_constellation import tested_constellations, twod_constell diff --git a/gr-digital/python/qa_correlate_access_code.py b/gr-digital/python/qa_correlate_access_code.py new file mode 100755 index 000000000..6b6f25051 --- /dev/null +++ b/gr-digital/python/qa_correlate_access_code.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,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 +import math + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +class test_correlate_access_code(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + pad = (0,) * 64 + # 0 0 0 1 0 0 0 1 + src_data = (1, 0, 1, 1, 1, 1, 0, 1, 1) + pad + (0,) * 7 + expected_result = pad + (1, 0, 1, 1, 3, 1, 0, 1, 1, 2) + (0,) * 6 + src = gr.vector_source_b (src_data) + op = digital_swig.correlate_access_code_bb("1011", 0) + dst = gr.vector_sink_b () + self.tb.connect (src, op, dst) + self.tb.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + def test_002(self): + code = tuple(string_to_1_0_list(default_access_code)) + access_code = to_1_0_string(code) + pad = (0,) * 64 + #print code + #print access_code + src_data = code + (1, 0, 1, 1) + pad + expected_result = pad + code + (3, 0, 1, 1) + src = gr.vector_source_b (src_data) + op = digital_swig.correlate_access_code_bb(access_code, 0) + dst = gr.vector_sink_b () + self.tb.connect (src, op, dst) + self.tb.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + +if __name__ == '__main__': + gr_unittest.run(test_correlate_access_code, "test_correlate_access_code.xml") + diff --git a/gr-digital/python/qa_costas_loop_cc.py b/gr-digital/python/qa_costas_loop_cc.py index 368704093..3f5eaa141 100755 --- a/gr-digital/python/qa_costas_loop_cc.py +++ b/gr-digital/python/qa_costas_loop_cc.py @@ -20,11 +20,11 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks2, gr_unittest -import digital_swig +from gnuradio import gr, gr_unittest +import digital_swig, psk2 import random, cmath -class test_digital(gr_unittest.TestCase): +class test_costas_loop_cc(gr_unittest.TestCase): def setUp (self): self.tb = gr.top_block () @@ -34,8 +34,8 @@ class test_digital(gr_unittest.TestCase): def test01 (self): # test basic functionality by setting all gains to 0 - damp = 0.4 - natfreq = 0.25 + damp = 0.0 + natfreq = 0.0 order = 2 self.test = digital_swig.costas_loop_cc(damp, natfreq, order) @@ -130,9 +130,9 @@ class test_digital(gr_unittest.TestCase): self.test = digital_swig.costas_loop_cc(damp, natfreq, order) rot = cmath.exp(-cmath.pi/8.0j) # rotate to match Costas rotation - const = blks2.psk.make_constellation(order) + const = psk2.psk_constellation(order) data = [random.randint(0,7) for i in xrange(100)] - data = [2*rot*const[d] for d in data] + data = [2*rot*const.points()[d] for d in data] N = 40 # settling time expected_result = data[N:] @@ -153,4 +153,4 @@ class test_digital(gr_unittest.TestCase): self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) if __name__ == '__main__': - gr_unittest.run(test_digital, "test_digital.xml") + gr_unittest.run(test_costas_loop_cc, "test_costas_loop_cc.xml") diff --git a/gr-digital/python/qa_crc32.py b/gr-digital/python/qa_crc32.py new file mode 100644 index 000000000..f86813f3f --- /dev/null +++ b/gr-digital/python/qa_crc32.py @@ -0,0 +1,60 @@ +#!/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 +import random, cmath + +class test_crc32(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + data = 100*"0" + expected_result = 2943744955 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + + def test02 (self): + data = 100*"1" + expected_result = 2326594156 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + + def test03 (self): + data = 10*"0123456789" + expected_result = 3774345973 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + +if __name__ == '__main__': + gr_unittest.run(test_crc32, "test_crc32.xml") diff --git a/gr-digital/python/qa_lms_equalizer.py b/gr-digital/python/qa_lms_equalizer.py new file mode 100644 index 000000000..025c785aa --- /dev/null +++ b/gr-digital/python/qa_lms_equalizer.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,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 + +class test_lms_dd_equalizer(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def transform(self, src_data, gain, const): + SRC = gr.vector_source_c(src_data, False) + EQU = digital_swig.lms_dd_equalizer_cc(4, gain, 1, const.base()) + DST = gr.vector_sink_c() + self.tb.connect(SRC, EQU, DST) + self.tb.run() + return DST.data() + + def test_001_identity(self): + # Constant modulus signal so no adjustments + const = digital_swig.constellation_qpsk() + src_data = const.points()*1000 + + N = 100 # settling time + expected_data = src_data[N:] + result = self.transform(src_data, 0.1, const)[N:] + self.assertComplexTuplesAlmostEqual(expected_data, result, 5) + +if __name__ == "__main__": + gr_unittest.run(test_lms_dd_equalizer, "test_lms_dd_equalizer.xml") diff --git a/gr-digital/python/qa_mpsk_receiver.py b/gr-digital/python/qa_mpsk_receiver.py new file mode 100644 index 000000000..7e9a76e1f --- /dev/null +++ b/gr-digital/python/qa_mpsk_receiver.py @@ -0,0 +1,123 @@ +#!/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, psk2 +import random, cmath + +class test_mpsk_receiver(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # Test BPSK sync + M = 2 + theta = 0 + alpha = 0.1 + beta = 0.25*alpha*alpha + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex(1,0), complex(1,0), complex(-1,0), complex(-1,0)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(-0.5,0), complex(0.5,0)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + + + def test02 (self): + # Test QPSK sync + M = 4 + theta = 0 + alpha = 0.1 + beta = 0.25*alpha*alpha + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex( 0.707, 0.707), complex( 0.707, 0.707), + complex(-0.707, 0.707), complex(-0.707, 0.707), + complex(-0.707, -0.707), complex(-0.707, -0.707), + complex( 0.707, -0.707), complex( 0.707, -0.707)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(1.2, 0), complex(0, 1.2), + complex(-1.2, 0), complex(0, -1.2)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + +if __name__ == '__main__': + gr_unittest.run(test_mpsk_receiver, "test_mpsk_receiver.xml") diff --git a/gr-digital/python/qam.py b/gr-digital/python/qam.py index ee2b1e416..f29291ce8 100644 --- a/gr-digital/python/qam.py +++ b/gr-digital/python/qam.py @@ -26,10 +26,10 @@ QAM modulation and demodulation. from math import pi, sqrt, log from gnuradio import gr -import modulation_utils2 from generic_mod_demod import generic_mod, generic_demod from utils.gray_code import gray_code from utils import mod_codes +import modulation_utils2 # Default number of points in constellation. _def_constellation_points = 16 diff --git a/gr-digital/python/qpsk.py b/gr-digital/python/qpsk.py index ea1724424..91e8b196f 100644 --- a/gr-digital/python/qpsk.py +++ b/gr-digital/python/qpsk.py @@ -26,9 +26,10 @@ Demodulation is not included since the generic_mod_demod doesn't work for non-differential encodings. """ -from gnuradio import gr, modulation_utils2 -from gnuradio.digital.generic_mod_demod import generic_mod - +from gnuradio import gr +from gnuradio.digital.generic_mod_demod import generic_mod, generic_demod +import digital_swig +import modulation_utils2 # Default number of points in constellation. _def_constellation_points = 4 @@ -43,7 +44,7 @@ _def_gray_coded = True def qpsk_constellation(m=_def_constellation_points): if m != _def_constellation_points: raise ValueError("QPSK can only have 4 constellation points.") - return gr.constellation_qpsk() + return digital_swig.constellation_qpsk() # ///////////////////////////////////////////////////////////////////////////// # QPSK modulator @@ -65,15 +66,45 @@ class qpsk_mod(generic_mod): See generic_mod block for list of parameters. """ - constellation = gr.constellation_qpsk() + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_qpsk() if constellation_points != 4: raise ValueError("QPSK can only have 4 constellation points.") if differential or not gray_coded: raise ValueError("This QPSK mod/demod works only for gray-coded, non-differential.") super(qpsk_mod, self).__init__(constellation, differential, gray_coded, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class qpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_qpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for QPSK.') + super(qpsk_demod, self).__init__(constellation, *args, **kwargs) + + # # Add these to the mod/demod registry # modulation_utils2.add_type_1_mod('qpsk', qpsk_mod) +modulation_utils2.add_type_1_demod('qpsk', qpsk_demod) modulation_utils2.add_type_1_constellation('qpsk', qpsk_constellation) + diff --git a/gr-digital/swig/CMakeLists.txt b/gr-digital/swig/CMakeLists.txt index a7374f0f0..037c0a3f1 100644 --- a/gr-digital/swig/CMakeLists.txt +++ b/gr-digital/swig/CMakeLists.txt @@ -40,13 +40,19 @@ GR_SWIG_INSTALL( INSTALL( FILES + digital_binary_slicer_fb.i + digital_clock_recovery_mm_cc.i + digital_clock_recovery_mm_ff.i digital_constellation.i digital_constellation_receiver_cb.i digital_constellation_decoder_cb.i + digital_correlate_access_code_bb.i digital_costas_loop_cc.i digital_cma_equalizer_cc.i + digital_crc32.i digital_lms_dd_equalizer_cc.i digital_kurtotic_equalizer_cc.i + digital_mpsk_receiver_cc.i DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig COMPONENT "digital_swig" ) diff --git a/gr-digital/swig/Makefile.am b/gr-digital/swig/Makefile.am index b9ebf4cc5..08fb140ac 100644 --- a/gr-digital/swig/Makefile.am +++ b/gr-digital/swig/Makefile.am @@ -58,13 +58,19 @@ digital_swig_la_swig_libadd = \ # additional SWIG files to be installed digital_swig_swiginclude_headers = \ + digital_binary_slicer_fb.i \ + digital_clock_recovery_mm_cc.i \ + digital_clock_recovery_mm_ff.i \ digital_constellation.i \ digital_constellation_receiver_cb.i \ digital_constellation_decoder_cb.i \ + digital_correlate_access_code_bb.i \ digital_costas_loop_cc.i \ digital_cma_equalizer_cc.i \ + digital_crc32.i \ digital_lms_dd_equalizer_cc.i \ - digital_kurtotic_equalizer_cc.i + digital_kurtotic_equalizer_cc.i \ + digital_mpsk_receiver_cc.i digital_swig_swig_args = \ -I$(abs_top_srcdir)/gr-digital/lib \ diff --git a/gr-digital/swig/digital_binary_slicer_fb.i b/gr-digital/swig/digital_binary_slicer_fb.i new file mode 100644 index 000000000..30603748b --- /dev/null +++ b/gr-digital/swig/digital_binary_slicer_fb.i @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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,binary_slicer_fb); + +digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + +class digital_binary_slicer_fb : public gr_sync_block +{ + private: + digital_binary_slicer_fb (); + + public: +}; diff --git a/gr-digital/swig/digital_clock_recovery_mm_cc.i b/gr-digital/swig/digital_clock_recovery_mm_cc.i new file mode 100644 index 000000000..4ce9a9725 --- /dev/null +++ b/gr-digital/swig/digital_clock_recovery_mm_cc.i @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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,clock_recovery_mm_cc); + +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) throw(std::exception); + +class digital_clock_recovery_mm_cc : public gr_sync_block +{ + private: + digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); + +public: + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float omega) { d_mu = mu; } + void set_omega (float omega) { d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + } + void set_verbose (bool verbose) { d_verbose = verbose; } +}; diff --git a/gr-digital/swig/digital_clock_recovery_mm_ff.i b/gr-digital/swig/digital_clock_recovery_mm_ff.i new file mode 100644 index 000000000..054ef9ebf --- /dev/null +++ b/gr-digital/swig/digital_clock_recovery_mm_ff.i @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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,clock_recovery_mm_ff); + +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001) throw(std::exception); + +class digital_clock_recovery_mm_ff : public gr_sync_block +{ + private: + digital_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); + +public: + float mu() const; + float omega() const; + float gain_mu() const; + float gain_omega() const; + + void set_gain_mu (float gain_mu); + void set_gain_omega (float gain_omega); + void set_mu (float omega); + void set_omega (float omega); +}; diff --git a/gr-digital/swig/digital_correlate_access_code_bb.i b/gr-digital/swig/digital_correlate_access_code_bb.i new file mode 100644 index 000000000..01087b8e9 --- /dev/null +++ b/gr-digital/swig/digital_correlate_access_code_bb.i @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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,correlate_access_code_bb); + +/*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + * \param threshold maximum number of bits that may be wrong + */ +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold) + throw(std::out_of_range); + +/*! + * \brief Examine input for specified access code, one bit at a time. + * \ingroup block + * + * input: stream of bits, 1 bit per input byte (data in LSB) + * output: stream of bits, 2 bits per output byte (data in LSB, flag in next higher bit) + * + * Each output byte contains two valid bits, the data bit, and the + * flag bit. The LSB (bit 0) is the data bit, and is the original + * input data, delayed 64 bits. Bit 1 is the + * flag bit and is 1 if the corresponding data bit is the first data + * bit following the access code. Otherwise the flag bit is 0. + */ +class digital_correlate_access_code_bb : public gr_sync_block +{ + friend digital_correlate_access_code_bb_sptr + digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + protected: + digital_correlate_access_code_bb(const std::string &access_code, int threshold); + + public: + ~digital_correlate_access_code_bb(); + + /*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + */ + bool set_access_code (const std::string &access_code); +}; diff --git a/gr-digital/swig/digital_crc32.i b/gr-digital/swig/digital_crc32.i new file mode 100644 index 000000000..806bfad6a --- /dev/null +++ b/gr-digital/swig/digital_crc32.i @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,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. + */ + +%rename(update_crc32) digital_update_crc32; +%rename(crc32) digital_crc32; + +unsigned int digital_update_crc32(unsigned int crc, const std::string buf); +unsigned int digital_crc32(const std::string buf); diff --git a/gr-digital/swig/digital_mpsk_receiver_cc.i b/gr-digital/swig/digital_mpsk_receiver_cc.i new file mode 100644 index 000000000..cdc9f661b --- /dev/null +++ b/gr-digital/swig/digital_mpsk_receiver_cc.i @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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_receiver_cc); + +digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, + float omega_rel); +class digital_mpsk_receiver_cc : public gr_block +{ + private: + digital_mpsk_receiver_cc (unsigned int M,float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +public: + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_rel); + d_max_omega = omega*(1.0 + d_omega_rel); + } + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + float alpha() const { return d_alpha; } + float beta() const { return d_beta; } + float freq() const { return d_freq; } + float phase() const { return d_phase; } + void set_alpha(float alpha) { d_alpha = alpha; } + void set_beta(float beta) { d_beta = beta; } + void set_freq(float freq) { d_freq = freq; } + void set_phase(float phase) { d_phase = phase; } +}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index 26a9dd130..cfaadcaea 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -22,22 +22,34 @@ %include "gnuradio.i" %{ +#include "digital_binary_slicer_fb.h" +#include "digital_clock_recovery_mm_cc.h" +#include "digital_clock_recovery_mm_ff.h" +#include "digital_cma_equalizer_cc.h" #include "digital_constellation.h" +#include "digital_constellation_decoder_cb.h" +#include "digital_constellation_receiver_cb.h" +#include "digital_correlate_access_code_bb.h" #include "digital_costas_loop_cc.h" -#include "digital_cma_equalizer_cc.h" -#include "digital_lms_dd_equalizer_cc.h" +#include "digital_crc32.h" #include "digital_kurtotic_equalizer_cc.h" -#include "digital_constellation_receiver_cb.h" -#include "digital_constellation_decoder_cb.h" +#include "digital_lms_dd_equalizer_cc.h" +#include "digital_mpsk_receiver_cc.h" %} +%include "digital_binary_slicer_fb.i" +%include "digital_clock_recovery_mm_cc.i" +%include "digital_clock_recovery_mm_ff.i" +%include "digital_cma_equalizer_cc.i" %include "digital_constellation.i" +%include "digital_constellation_decoder_cb.i" +%include "digital_constellation_receiver_cb.i" +%include "digital_correlate_access_code_bb.i" %include "digital_costas_loop_cc.i" -%include "digital_cma_equalizer_cc.i" -%include "digital_lms_dd_equalizer_cc.i" +%include "digital_crc32.i" %include "digital_kurtotic_equalizer_cc.i" -%include "digital_constellation_receiver_cb.i" -%include "digital_constellation_decoder_cb.i" +%include "digital_lms_dd_equalizer_cc.i" +%include "digital_mpsk_receiver_cc.i" #if SWIGGUILE %scheme %{ |