diff options
Diffstat (limited to 'gnuradio-examples/python/gmsk2')
22 files changed, 3448 insertions, 0 deletions
diff --git a/gnuradio-examples/python/gmsk2/Makefile.am b/gnuradio-examples/python/gmsk2/Makefile.am new file mode 100644 index 0000000000..cf2ffb39ea --- /dev/null +++ b/gnuradio-examples/python/gmsk2/Makefile.am @@ -0,0 +1,38 @@ +# +# Copyright 2004 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +EXTRA_DIST = \ + benchmark_gmsk_rx.py \ + benchmark_gmsk_tx.py \ + benchmark_mpsk_rx.py \ + benchmark_mpsk_tx.py \ + fusb_options.py \ + gen_whitener.py \ + gmsk_test.py \ + pick_bitrate.py \ + receive_path.py \ + transmit_path.py \ + tunnel_ip_null_mac.py + + + + + diff --git a/gnuradio-examples/python/gmsk2/README b/gnuradio-examples/python/gmsk2/README new file mode 100644 index 0000000000..b3c343e4bb --- /dev/null +++ b/gnuradio-examples/python/gmsk2/README @@ -0,0 +1,81 @@ +Quick overview of what's here: + +* gmsk_test.py: stand-alone program that exercises the GMSK packet tx +and rx code. The two halves are connected with a simulated noisy +channel. It's easy to add extra instrumentation to log various internal +states. We used a variant of this code to get this working in the +first place. + +* benchmark_gmsk_tx.py: generates packets of the size you +specify and sends them across the air using the USRP. Known to work +well using the USRP with the Flex 400 transceiver daughterboard. +You can specify the bitrate to use with the -r <bitrate> command line +parameter. The default is 500k. Some machines will do 1M or more. + +* benchmark_gmsk_rx.py: the receiver half of benchmark_gmsk_tx.py. +Command line arguments are pretty much the same as tx. Works well +with a USRP and Flex 400 transceiver daughterboard. Will also work +with TVRX daugherboard, but you'll need to fiddle with the gain. See +below. Prints a summary of each packet received and keeps a running +total of packets received, and how many of them were error free. +There are two levels of error reporting going on. If the access code +(PN code) and header of a packet were properly detected, then you'll +get an output line. If the CRC32 of the payload was correct you get +"ok = True", else "ok = False". The "pktno" is extracted from the +received packet. If there are skipped numbers, you're missing some +packets. Be sure you've got a suitable antenna connected to the TX/RX +port on each board. A "70 cm" / 420 MHz antenna for a ham +handi-talkie works great. These are available at ham radio supplies, +etc. The boards need to be at least 3m apart. You can also try +experimenting with the rx gain (-g <gain> command line option). + +Generally speaking, I start the rx first on one machine, and then fire +up the tx on the other machine. The tx also supports a discontinous +transmission mode where it sends bursts of 5 packets and then waits 1 +second. This is useful for ensuring that all the receiver control +loops lock up fast enough. + +* tunnel.py: This program provides a framework for building your own +MACs. It creates a "TAP" interface in the kernel, typically gr0, +and sends and receives ethernet frames through it. See +/usr/src/linux/Documentation/networking/tuntap.txt and/or Google for +"universal tun tap". The Linux 2.6 kernel includes the tun module, you +don't have to build it. You may have to "modprobe tun" if it's not +loaded by default. If /dev/net/tun doesn't exist, try "modprobe tun". + +To run this program you'll need to be root or running with the +appropriate capability to open the tun interface. You'll need to fire +up two copies on different machines. Once each is running you'll need +to ifconfig the gr0 interface to set the IP address. + +This will allow two machines to talk, but anything beyond the two +machines depends on your networking setup. Left as an exercise... + +On machine A: + + $ su + # ./tunnel.py --freq 423.0M --bitrate 500k + # # in another window on A, also as root... + # ifconfig gr0 10.10.10.1 + + +On machine B: + + $ su + # ./tunnel.py --freq 423.0M --bitrate 500k + # # in another window on B, also as root... + # ifconfig gr0 10.10.10.2 + +Now, on machine A you shold be able to ping machine B: + + $ ping 10.10.10.2 + +and you should see some output for each packet in the +tunnel.py window if you used the -v option. + +Likewise, on machine B: + + $ ping 10.10.10.1 + +This now uses a carrier sense MAC, so you should be able to ssh +between the machines, web browse, etc. diff --git a/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py b/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py new file mode 100755 index 0000000000..d1f70c5462 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# Copyright 2005 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import struct + +# from current dir +from receive_path import receive_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# 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 = %r pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-g", "--rx-gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + demod_kwargs = { } # placeholder + + # build the graph + fg = my_graph(blks.gmsk2_demod, + options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, rx_callback, + options, demod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.rx_gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py b/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py new file mode 100755 index 0000000000..2994003a02 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import time +import struct +import sys + +# from current dir +from transmit_path import transmit_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, + help="set bandwidth-time product [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=100.0, + help="transmitter gain [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)") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + mod_kwargs = { + 'bt' : options.bt, + } + + # build the graph + fg = my_graph(blks.gmsk2_mod, options.tx_subdev_spec, + options.bitrate, options.interp, options.spb, options.gain, + options, mod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py b/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py new file mode 100755 index 0000000000..ad6218b698 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import struct + +# from current dir +from bpsk import bpsk_demod +from dbpsk import dbpsk_demod +from dqpsk import dqpsk_demod +from receive_path import receive_path +import fusb_options + +if 1: + import os + print os.getpid() + raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# 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 = %r pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-m", "--modulation", type="string", default='dbpsk', + help="modulation type (bpsk, dbpsk, dqpsk) [default=%default]") + parser.add_option("", "--excess-bw", type="float", default=0.3, + help="set RRC excess bandwith factor [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("","--log", action="store_true", default=False, + help="enable diagnostic logging") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + demod_kwargs = { + 'excess_bw' : options.excess_bw, + } + + #FIXME: Needs to be worked in to overall structure; this will be fixed + # once static class definitions for modulations are defined + if(options.modulation=='bpsk'): + modulation=bpsk_demod + elif(options.modulation=='dbpsk'): + modulation=dbpsk_demod + else: + modulation=dqpsk_demod + + # build the graph + fg = my_graph(modulation, + options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, + rx_callback, options, demod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py b/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py new file mode 100755 index 0000000000..7b37da7fef --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# Copyright 2005, 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, time, struct, sys + +# from current dir +from bpsk import bpsk_mod +from dbpsk import dbpsk_mod +from dqpsk import dqpsk_mod +from transmit_path import transmit_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("-m", "--modulation", type="string", default='dbpsk', + help="modulation type (bpsk, dbpsk, dqpsk) [default=%default]") + parser.add_option("", "--excess-bw", type="float", default=0.3, + help="set RRC excess bandwith factor [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=100.0, + help="transmitter gain [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)") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + mod_kwargs = { + 'excess_bw' : options.excess_bw, + } + + #FIXME: rework when static class defintions are ready for the modulation types + if(options.modulation=='bpsk'): + modulation=bpsk_mod + elif( options.modulation=='dbpsk'): + modulation=dbpsk_mod + else: + modulation=dqpsk_mod + + # build the graph + fg = my_graph(modulation, + options.tx_subdev_spec, options.bitrate, options.interp, + options.spb, options.gain, + options, mod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/bpsk.py b/gnuradio-examples/python/gmsk2/bpsk.py new file mode 100644 index 0000000000..14cea7b102 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/bpsk.py @@ -0,0 +1,256 @@ +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# mPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class bpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + Hierarchical block for RRC-filtered BPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + fg.connect(self.bytes2chunks, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +class bpsk_demod__coherent_detection_of_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +bpsk_demod = bpsk_demod__coherent_detection_of_psk + diff --git a/gnuradio-examples/python/gmsk2/dbpsk.py b/gnuradio-examples/python/gmsk2/dbpsk.py new file mode 100644 index 0000000000..213e90c611 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/dbpsk.py @@ -0,0 +1,282 @@ +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +differential BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class dbpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + 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. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + self.bits_per_symbol = bits_per_symbol + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + + +class dbpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1, 1000) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +dbpsk_demod = dbpsk_demod__coherent_detection_of_differentially_encoded_psk + diff --git a/gnuradio-examples/python/gmsk2/dqpsk.py b/gnuradio-examples/python/gmsk2/dqpsk.py new file mode 100644 index 0000000000..490dc6f739 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/dqpsk.py @@ -0,0 +1,280 @@ +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +differential QPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class dqpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + 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. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 2 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + + +class dqpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered QPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 2 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +dqpsk_demod = dqpsk_demod__coherent_detection_of_differentially_encoded_psk + diff --git a/gnuradio-examples/python/gmsk2/fusb_options.py b/gnuradio-examples/python/gmsk2/fusb_options.py new file mode 100644 index 0000000000..0d04e30c77 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/fusb_options.py @@ -0,0 +1,31 @@ +# +# 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +def add_options(parser): + """ + Add Fast USB specifc options to command line parser. + + @param parser: instance of OptionParser + """ + parser.add_option("-B", "--fusb-block-size", type="int", default=0, + help="specify fast usb block size [default=%default]") + parser.add_option("-N", "--fusb-nblocks", type="int", default=0, + help="specify number of fast usb blocks [default=%default]") diff --git a/gnuradio-examples/python/gmsk2/gen_whitener.py b/gnuradio-examples/python/gmsk2/gen_whitener.py new file mode 100755 index 0000000000..93aea2b821 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/gen_whitener.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + src = gr.lfsr_32k_source_s() + head = gr.head(gr.sizeof_short, 2048) + self.dst = gr.vector_sink_s() + self.connect(src, head, self.dst) + +if __name__ == '__main__': + try: + fg = my_graph() + fg.run() + f = sys.stdout + i = 0 + for s in fg.dst.data(): + f.write("%3d, " % (s & 0xff,)) + f.write("%3d, " % ((s >> 8) & 0xff,)) + i = i+2 + if i % 16 == 0: + f.write('\n') + + except KeyboardInterrupt: + pass + + diff --git a/gnuradio-examples/python/gmsk2/gmsk_test.py b/gnuradio-examples/python/gmsk2/gmsk_test.py new file mode 100755 index 0000000000..7ae69a0a94 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/gmsk_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +from gnuradio import gr, blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import random +import struct + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, rx_callback, spb, bt, SNR, freq_error): + gr.flow_graph.__init__(self) + + fg = self + + # Tuning Parameters + gain_mu = 0.002*spb + omega = spb*(1+freq_error) + + # transmitter + self.packet_transmitter = blks.gmsk2_mod_pkts(fg, spb=spb, bt=bt) + + # add some noise + add = gr.add_cc() + noise = gr.noise_source_c(gr.GR_GAUSSIAN, pow(10.0,-SNR/20.0)) + + # channel filter + rx_filt_taps = gr.firdes.low_pass(1,spb,0.8,0.1,gr.firdes.WIN_HANN) + rx_filt = gr.fir_filter_ccf(1,rx_filt_taps) + + # receiver + self.packet_receiver = blks.gmsk2_demod_pkts(fg, callback=rx_callback, + spb=spb, gain_mu=gain_mu, + freq_error=freq_error, omega=omega) + + fg.connect (self.packet_transmitter, (add,0)) + fg.connect (noise, (add,1)) + fg.connect(add, rx_filt, self.packet_receiver) + + +class stats(object): + def __init__(self): + self.npkts = 0 + self.nright = 0 + +def main(): + st = stats() + + def send_pkt(payload='', eof=False): + fg.packet_transmitter.send_pkt(payload, eof) + + def rx_callback(ok, payload): + st.npkts += 1 + if ok: + st.nright += 1 + if len(payload) <= 16: + print "ok = %5r payload = '%s' %d/%d" % (ok, payload, st.nright, st.npkts) + else: + (pktno,) = struct.unpack('!H', payload[0:2]) + print "ok = %5r pktno = %4d len(payload) = %4d %d/%d" % (ok, pktno, len(payload), + st.nright, st.npkts) + + + parser = OptionParser (option_class=eng_option) + parser.add_option("-M", "--megabytes", type="eng_float", default=1, + help="set megabytes to transmit [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("","--spb", type=int, default=4, + help="set samples per baud to SPB [default=%default]") + parser.add_option("", "--bt", type="eng_float", default=0.3, + help="set bandwidth time product for Gaussian filter [default=%default]") + parser.add_option("", "--snr", type="eng_float", default=20, + help="set SNR in dB for simulation [default=%default]") + parser.add_option("", "--freq-error", type="eng_float", default=0, + help="set frequency error for simulation [default=%default]") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + pkt_size = int(options.size) + + fg = my_graph(rx_callback, options.spb, options.bt, options.snr, options.freq_error) + fg.start() + + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + send_pkt('Hello World') + + # generate and send packets + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + pktno += 1 + + send_pkt('Goodbye World') + send_pkt(eof=True) # tell modulator we're not sending any more pkts + + fg.wait() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/mpsk_test.py b/gnuradio-examples/python/gmsk2/mpsk_test.py new file mode 100755 index 0000000000..1f257d9c5d --- /dev/null +++ b/gnuradio-examples/python/gmsk2/mpsk_test.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +from gnuradio import gr, blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import random +import struct +from mpsk_pkt import * +import cmath + +class my_graph(gr.flow_graph): + + def __init__(self, rx_callback, spb, excess_bw, SNR, freq_error, arity): + gr.flow_graph.__init__(self) + + fg = self + + # Tuning Parameters + gain_mu = 0.05/spb + + # transmitter + self.packet_transmitter = \ + mpsk_mod_pkts(fg, spb=spb, excess_bw=excess_bw, diff=True, arity=arity) + + # ---------------------------------------------------------------- + # Channel model + # ---------------------------------------------------------------- + + awgn = gr.noise_source_c(gr.GR_GAUSSIAN, pow(10.0,-SNR/20.0)) + add = gr.add_cc() # add some noise + fg.connect(awgn, (add,1)) + radians = 17*pi/180 + phase_rotate = gr.multiply_const_cc(cmath.exp(radians * 1j)) + + # ---------------------------------------------------------------- + + # channel filter + rx_filt_taps = gr.firdes.low_pass(1,spb,0.8,0.1,gr.firdes.WIN_HANN) + #rx_filt_taps = (1,) + rx_filt = gr.fir_filter_ccf(1,rx_filt_taps) + + # receiver + self.packet_receiver = \ + mpsk_demod_pkts(fg, callback=rx_callback, + excess_bw=excess_bw, arity=arity, diff=True, + costas_alpha=.005, gain_mu=gain_mu, spb=spb) + + fg.connect (self.packet_transmitter, (add,0)) + fg.connect(add, phase_rotate, rx_filt, self.packet_receiver) + + +class stats(object): + def __init__(self): + self.npkts = 0 + self.nright = 0 + +def main(): + st = stats() + + def send_pkt(payload='', eof=False): + fg.packet_transmitter.send_pkt(payload, eof) + + def rx_callback(ok, payload): + st.npkts += 1 + if ok: + st.nright += 1 + if len(payload) <= 16: + print "ok = %5r payload = '%s' %d/%d" % (ok, payload, st.nright, st.npkts) + else: + (pktno,) = struct.unpack('!H', payload[0:2]) + print "ok = %5r pktno = %4d len(payload) = %4d %d/%d" % (ok, pktno, len(payload), + st.nright, st.npkts) + + + parser = OptionParser (option_class=eng_option) + parser.add_option("-M", "--megabytes", type="eng_float", default=1, + help="set megabytes to transmit [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("","--spb", type=int, default=4, + help="set samples per baud to SPB [default=%default]") + parser.add_option("", "--excess-bw", type="eng_float", default=0.4, + help="set excess bandwidth for RRC filter [default=%default]") + parser.add_option("", "--snr", type="eng_float", default=40, + help="set SNR in dB for simulation [default=%default]") + parser.add_option("", "--m-arity", type=int, default=4, + help="PSK arity [default=%default]") + parser.add_option("", "--freq-error", type="eng_float", default=0, + help="set frequency error for simulation [default=%default]") + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + pkt_size = int(options.size) + + fg = my_graph(rx_callback, options.spb, options.excess_bw, options.snr, + options.freq_error, options.m_arity) + fg.start() + + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + send_pkt('Hello World') + + # generate and send packets + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + pktno += 1 + + send_pkt('Goodbye World') + send_pkt(eof=True) # tell modulator we're not sending any more pkts + + fg.wait() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/pick_bitrate.py b/gnuradio-examples/python/gmsk2/pick_bitrate.py new file mode 100644 index 0000000000..b35991caf3 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/pick_bitrate.py @@ -0,0 +1,143 @@ +# +# Copyright 2005 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +_default_bitrate = 500e3 + +_valid_spbs = (2,3,4,5,6,7) + +def _gen_tx_info(converter_rate): + results = [] + for spb in _valid_spbs: + for interp in range(16, 512 + 1, 4): + bitrate = converter_rate / interp / spb + results.append((bitrate, spb, interp)) + results.sort() + return results + +def _gen_rx_info(converter_rate): + results = [] + for spb in _valid_spbs: + for decim in range(8, 256 + 1, 2): + bitrate = converter_rate / decim / spb + results.append((bitrate, spb, decim)) + results.sort() + return results + +def _filter_info(info, samples_per_baud, xrate): + if samples_per_baud is not None: + info = [x for x in info if x[1] == samples_per_baud] + if xrate is not None: + info = [x for x in info if x[2] == xrate] + return info + +def _pick_best(target_bitrate, bits_per_symbol, info): + """ + @returns tuple (bitrate, samples_per_baud, interp_rate_or_decim_rate) + """ + if len(info) == 0: + raise RuntimeError, "info is zero length!" + + if target_bitrate is None: # return the fastest one + return info[-1] + + # convert bit rate to symbol rate + target_symbolrate = target_bitrate / bits_per_symbol + + # Find the closest matching symbol rate. + # In the event of a tie, the one with the lowest samples_per_baud wins. + # (We already sorted them, so the first one is the one we take) + + best = info[0] + best_delta = abs(target_symbolrate - best[0]) + for x in info[1:]: + delta = abs(target_symbolrate - x[0]) + if delta < best_delta: + best_delta = delta + best = x + + # convert symbol rate back to bit rate + return ((best[0] * bits_per_symbol),) + best[1:] + +def _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + xrate, converter_rate, gen_info): + """ + @returns tuple (bitrate, samples_per_baud, interp_rate_or_decim_rate) + """ + if not isinstance(bits_per_symbol, int) or bits_per_symbol < 1: + raise ValueError, "bits_per_symbol must be an int >= 1" + + if samples_per_baud is not None and xrate is not None: # completely determined + return (float(converter_rate) / xrate / samples_per_baud, + samples_per_baud, xrate) + + if bitrate is None and samples_per_baud is None and xrate is None: + bitrate = _default_bitrate + + # now we have a target bitrate and possibly an xrate or + # samples_per_baud constraint, but not both of them. + + return _pick_best(bitrate, bits_per_symbol, + _filter_info(gen_info(converter_rate), samples_per_baud, xrate)) + +# --------------------------------------------------------------------------------------- + +def pick_tx_bitrate(bitrate, bits_per_symbol, samples_per_baud, + interp_rate, converter_rate=128e6): + """ + Given the 4 input parameters, return at configuration that matches + + @param bitrate: desired bitrate or None + @type bitrate: number or None + @param bits_per_symbol: E.g., BPSK -> 1, QPSK -> 2, 8-PSK -> 3 + @type bits_per_symbol: integer >= 1 + @param samples_per_baud: samples/baud (aka samples/symbol) + @type samples_per_baud: number or None + @param interp_rate: USRP interpolation factor + @type interp_rate: integer or None + @param converter_rate: converter sample rate in Hz + @type converter_rate: number + + @returns tuple (bitrate, samples_per_baud, interp_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + interp_rate, converter_rate, _gen_tx_info) + + +def pick_rx_bitrate(bitrate, bits_per_symbol, samples_per_baud, + decim_rate, converter_rate=64e6): + """ + Given the 4 input parameters, return at configuration that matches + + @param bitrate: desired bitrate or None + @type bitrate: number or None + @param bits_per_symbol: E.g., BPSK -> 1, QPSK -> 2, 8-PSK -> 3 + @type bits_per_symbol: integer >= 1 + @param samples_per_baud: samples/baud (aka samples/symbol) + @type samples_per_baud: number or None + @param decim_rate: USRP decimation factor + @type decim_rate: integer or None + @param converter_rate: converter sample rate in Hz + @type converter_rate: number + + @returns tuple (bitrate, samples_per_baud, decim_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + decim_rate, converter_rate, _gen_rx_info) diff --git a/gnuradio-examples/python/gmsk2/qpsk.py b/gnuradio-examples/python/gmsk2/qpsk.py new file mode 100644 index 0000000000..6ca7f0e61a --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk.py @@ -0,0 +1,418 @@ +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +PSK and differential PSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# mPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class mpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, arity, excess_bw, diff=False): + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2, 4, or 8" + + ntaps = 11 * spb + + bits_per_symbol = int(gru.log2(arity)) + self.bits_per_symbol = bits_per_symbol + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True or arity > 2: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + if diff: + self.diffenc = gr.diff_encoder_bb(arity) + else: + self.diffenc = None + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + if diff: + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + else: + fg.connect(t, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + if diff: + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +class mpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered PSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2 or 4" + + if not diff and arity==4: + raise NotImplementedError, "non-differential QPSK not supported yet" + + bits_per_symbol = int(gru.log2(arity)) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + if arity == 2: + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + else: + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + if(diff): + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + if(diff): + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + else: + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + #fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + if(diff): + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.agc, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +######################################################################### + +class mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered PSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2 or 4" + + bits_per_symbol = int(gru.log2(arity)) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + if arity == 2: + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + else: + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.agc, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +mpsk_demod = mpsk_demod__coherent_detection_of_differentially_encoded_psk +#mpsk_demod = mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk diff --git a/gnuradio-examples/python/gmsk2/qpsk_tester.py b/gnuradio-examples/python/gmsk2/qpsk_tester.py new file mode 100644 index 0000000000..ea7bae2ebc --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk_tester.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +import random +from gnuradio import gr + +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)) + +code = string_to_1_0_list(default_access_code) +access_code = to_1_0_string(code) + +use_agc = 0 +use_rrc_tx = 1 +use_rrc_rx = 1 +use_sync_loop = 1 +use_clock_sync = 1 + +def main(): + fg = gr.flow_graph() + +# data = (1,2,3,4,5,6,7,8,9) + random.seed() + data = [random.randint(1,100) for i in range(20000)] + data[0] = 0 # you know, for the diff encoding stuff + bytes_src = gr.vector_source_b(data,False) + + k = 2 + spb = 50 + ntaps = 11*spb + excess_bw = 0.9 + threshold = 12 + constellation = (1+0j, 0+1j, -1+0j, 0-1j) + + bytes2chunks = gr.packed_to_unpacked_bb(k, gr.GR_MSB_FIRST) + diffenc = gr.diff_encoder_bb(4) + chunks2symbols = gr.chunks_to_symbols_bc(constellation) + + if use_rrc_tx: + rrc_taps_tx = gr.firdes.root_raised_cosine(spb, spb, 1.0, \ + excess_bw, ntaps) + rrc_tx = gr.interp_fir_filter_ccf(spb, rrc_taps_tx) + else: + rrc_tx = gr.interp_fir_filter_ccf(1, (1,)) + +################### CHANNEL MODEL ############################# + + phase_rotate = gr.multiply_const_cc(1-0.36j) + channel = gr.add_cc() + awgn = gr.noise_source_c(gr.GR_GAUSSIAN, 0.5) + fg.connect(awgn, (channel,1)) + +################### CHANNEL MODEL ############################# + + if use_agc: + agc = gr.agc_cc(1e-4, 1, 1) + else: + agc = gr.multiply_const_cc(1) + + # Downconverter + if use_sync_loop: + costas_alpha=0.005 + beta = costas_alpha*costas_alpha*0.25 + sync_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, 0) + else: + sync_loop = gr.multiply_const_cc(1) + + if use_rrc_rx: + rrc_taps_rx = gr.firdes.root_raised_cosine(1, spb, 1.0, \ + excess_bw, ntaps) + rrc_rx = gr.fir_filter_ccf(1, rrc_taps_rx) + else: + rrc_rx = gr.fir_filter_ccf(1, (1,)) + + # Sampler + if use_clock_sync: + mu = 0.05 + omega = spb + gain_mu = 0.05 + gain_omega = 0.25*gain_mu*gain_mu + omega_rel_limit = 0.5 + clock_sync = gr.clock_recovery_mm_cc(omega, gain_omega, mu, \ + gain_mu, omega_rel_limit) + #clock_sync.set_verbose(True); + else: + clock_sync = gr.fir_filter_ccf(1, (1,)) + + diff_phasor = gr.diff_phasor_cc() + slicer = gr.constellation_decoder_cb((constellation), (0,1,2,3)) + unpack = gr.unpack_k_bits_bb(k) + access = gr.correlate_access_code_bb(access_code,threshold) + + sink = gr.file_sink(gr.sizeof_char, 'output.dat') + + fg.connect(bytes_src, bytes2chunks, diffenc, chunks2symbols, rrc_tx) + fg.connect(rrc_tx, phase_rotate, channel, agc) + fg.connect(agc, sync_loop, rrc_rx, clock_sync, diff_phasor, slicer, sink) + + test = gr.file_sink(gr.sizeof_gr_complex, 'test.dat') + fg.connect(rrc_rx, test) + + fg.connect(chunks2symbols, gr.file_sink(gr.sizeof_gr_complex, 'rrc_tx.dat')) # into TX RRC + fg.connect(channel, gr.file_sink(gr.sizeof_gr_complex, 'channel.dat')) # Out of TX RRC + fg.connect(rrc_rx, gr.file_sink(gr.sizeof_gr_complex, 'rrc_rx.dat')) # Out of RX RRC -> clock_sync + fg.connect(clock_sync, gr.file_sink(gr.sizeof_gr_complex, 'clock_sync.dat')) # Out of M&M sync loop + fg.connect(bytes2chunks, gr.file_sink(gr.sizeof_char, 'source.dat')) + + fg.start() + fg.wait() + +if __name__ == "__main__": + main() + + + + diff --git a/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py b/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py new file mode 100644 index 0000000000..bf7698a2b2 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import random +from gnuradio import gr, gru, usrp + +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)) + +code = string_to_1_0_list(default_access_code) + +access_code = to_1_0_string(code) + +def main(): + + fg = gr.flow_graph() + f_rf = 5e6 + fs = 400e3 + sr = 100e3 + alpha = 0.5 + M = 4 + k = int(gru.log2(M)) + + # Source + src = usrp.source_c () + adc_rate = src.adc_rate() + usrp_decim = int(adc_rate / fs) + src.set_decim_rate(usrp_decim) + + subdev_spec = usrp.pick_rx_subdevice(src) + subdev = usrp.selected_subdev(src, subdev_spec) + print "Using RX d'board %s" % (subdev.side_and_name(),) + src.set_mux(usrp.determine_rx_mux_value(src, subdev_spec)) + src.tune(0, subdev, f_rf) + + g = subdev.gain_range() + subdev.set_gain(g[1]) + subdev.set_auto_tr(True) + + print "USRP Decimation Rate = %d" % usrp_decim + print "RF Frequency = %d" % f_rf + + agc = gr.multiply_const_cc(0.0025) + + # Downconverter + costas_alpha=0.005 + beta = costas_alpha*costas_alpha*0.25 + sync_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, 0) + + # Stage 2 +# fs2 = 200e3 +# D = int(fs / fs2) +# decimator = gr.keep_one_in_n(gr.sizeof_gr_complex, D) +# print "D = %d\nAfter decimator fs = %f" % (D, fs2) + + # Demodulator + taps = gr.firdes.root_raised_cosine(1, fs, sr, alpha, 45) + rrc = gr.fir_filter_ccf(1, taps) + + # Sampler + mu = 0.01 + omega = 4.3 + gain_mu = 0.05 + gain_omega = 0.25*gain_mu*gain_mu + omega_rel_limit = 0.5 + clock_sync = gr.clock_recovery_mm_cc(omega, gain_omega, mu, + gain_mu, omega_rel_limit) + clock_sync.set_verbose(False) + + diff_phasor = gr.diff_phasor_cc() + + threshold = 12 + constellation = (1+0j, 0+1j, -1+0j, 0-1j) + slicer = gr.constellation_decoder_cb((constellation), (0,1,2,3)) + unpack = gr.unpack_k_bits_bb(k) + access = gr.correlate_access_code_bb(access_code,threshold) + + test = gr.file_sink(gr.sizeof_gr_complex, 'test.dat') + sink = gr.file_sink(gr.sizeof_char, 'output.dat') + + fg.connect(src, agc, sync_loop, rrc, clock_sync) + fg.connect(clock_sync, diff_phasor, slicer, unpack, access, sink) + + fg.connect(slicer, gr.file_sink(gr.sizeof_char, 'chunks.dat')) + fg.connect(unpack, gr.file_sink(gr.sizeof_char, 'unpack.dat')) + fg.connect(clock_sync, gr.file_sink(gr.sizeof_gr_complex, 'phasor.dat')) + + fg.start() + fg.wait() + +if __name__ == "__main__": + main() diff --git a/gnuradio-examples/python/gmsk2/receive_path.py b/gnuradio-examples/python/gmsk2/receive_path.py new file mode 100644 index 0000000000..505ff7c4d3 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/receive_path.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp + +# from current dir +from pick_bitrate import pick_rx_bitrate + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block): + def __init__(self, fg, demod_class, rx_subdev_spec, + bitrate, decim, spb, + rx_callback, options, demod_kwargs): + + self.u = usrp.source_c (fusb_block_size=options.fusb_block_size, + fusb_nblocks=options.fusb_nblocks) + adc_rate = self.u.adc_rate() + + (self._bitrate, self._spb, self._decim) = \ + pick_rx_bitrate(bitrate, demod_class.bits_per_baud(), spb, decim, adc_rate) + + self.u.set_decim_rate(self._decim) + sw_decim = 1 + + if rx_subdev_spec is None: + rx_subdev_spec = usrp.pick_rx_subdevice(self.u) + self.subdev = usrp.selected_subdev(self.u, rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, rx_subdev_spec)) + + # Create filter to get actual channel we want + chan_coeffs = gr.firdes.low_pass (1.0, # gain + sw_decim * self._spb, # sampling rate + 1.0, # midpoint of trans. band + 0.1, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + print "len(rx_chan_coeffs) =", len(chan_coeffs) + + # Decimating Channel filter + # complex in and out, float taps + self.chan_filt = gr.fft_filter_ccc(sw_decim, chan_coeffs) + #self.chan_filt = gr.fir_filter_ccf(sw_decim, chan_coeffs) + + # receiver + self.packet_receiver = \ + blks.demod_pkts(fg, + demod_class(fg, spb=self._spb, **demod_kwargs), + access_code=None, + callback=rx_callback, + threshold=-1) + + fg.connect(self.u, self.chan_filt, self.packet_receiver) + gr.hier_block.__init__(self, fg, None, None) + + g = self.subdev.gain_range() + #self.set_gain((g[0] + g[1])/2) # set gain to midpoint + self.set_gain(g[1]) # set gain to max + self.set_auto_tr(True) # enable Auto Transmit/Receive switching + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + fg.connect(self.chan_filt, self.probe) + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital up converter. + """ + r = self.u.tune(0, self.subdev, target_freq) + if r: + return True + + return False + + def set_gain(self, gain): + if gain is None: + r = self.subdev.gain_range() + gain = (r[0] + r[1])/2 # set gain to midpoint + self.gain = gain + return self.subdev.set_gain(gain) + + def set_auto_tr(self, enable): + return self.subdev.set_auto_tr(enable) + + def bitrate(self): + return self._bitrate + + def spb(self): + return self._spb + + def decim(self): + return self._decim + + + 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) + diff --git a/gnuradio-examples/python/gmsk2/rx_voice.py b/gnuradio-examples/python/gmsk2/rx_voice.py new file mode 100755 index 0000000000..3f5cc3b476 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/rx_voice.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# +# Copyright 2005 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import audio +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio.vocoder import gsm_full_rate + +import random +import struct + +# from current dir +from receive_path import receive_path + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class audio_tx(gr.hier_block): + def __init__(self, fg): + self.packet_src = gr.message_source(33) + voice_decoder = gsm_full_rate.decode_ps() + s2f = gr.short_to_float () + sink_scale = gr.multiply_const_ff(1.0/32767.) + audio_sink = audio.sink(8000) + fg.connect(self.packet_src, voice_decoder, s2f, sink_scale, audio_sink) + gr.hier_block.__init__(self, fg, self.packet_src, audio_sink) + + def msgq(self): + return self.packet_src.msgq() + + +class my_graph(gr.flow_graph): + + def __init__(self, rx_subdev_spec, bitrate, decim_rate, spb, rx_callback, log): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, rx_subdev_spec, bitrate, decim_rate, + spb, rx_callback, log) + + self.audio_tx = audio_tx(self) + + +# ///////////////////////////////////////////////////////////////////////////// +# 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 + n_rcvd += 1 + if ok: + n_right += 1 + + fg.audio_tx.msgq().insert_tail(gr.message_from_string(payload)) + + print "ok = %r n_rcvd = %4d n_right = %4d" % ( + ok, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("","--log", action="store_true", default=False, + help="enable diagnostic logging") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + # build the graph + fg = my_graph(options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, rx_callback, options.log) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + fg.rxpath.set_freq(options.freq) + fg.rxpath.set_gain(options.gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/transmit_path.py b/gnuradio-examples/python/gmsk2/transmit_path.py new file mode 100644 index 0000000000..a1df1579fa --- /dev/null +++ b/gnuradio-examples/python/gmsk2/transmit_path.py @@ -0,0 +1,108 @@ +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp + +# from current dir +from pick_bitrate import pick_tx_bitrate + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block): + def __init__(self, fg, mod_class, tx_subdev_spec, + bitrate, interp, spb, gain, + options, mod_kwargs): + + self.normal_gain = gain + + self.u = usrp.sink_c (fusb_block_size=options.fusb_block_size, + fusb_nblocks=options.fusb_nblocks) + dac_rate = self.u.dac_rate(); + + print mod_class + print mod_class.bits_per_baud() + (self._bitrate, self._spb, self._interp) = \ + pick_tx_bitrate(bitrate, mod_class.bits_per_baud(), spb, interp, dac_rate) + + self.u.set_interp_rate(self._interp) + + # determine the daughterboard subdevice we're using + if tx_subdev_spec is None: + tx_subdev_spec = usrp.pick_tx_subdevice(self.u) + self.u.set_mux(usrp.determine_tx_mux_value(self.u, tx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, tx_subdev_spec) + print "Using TX d'board %s" % (self.subdev.side_and_name(),) + + # transmitter + self.packet_transmitter = \ + blks.mod_pkts(fg, + mod_class(fg, spb=self._spb, **mod_kwargs), + access_code=None, + msgq_limit=4, + pad_for_usrp=True) + + self.amp = gr.multiply_const_cc (self.normal_gain) + + fg.connect(self.packet_transmitter, self.amp, self.u) + gr.hier_block.__init__(self, fg, None, None) + + self.set_gain(self.subdev.gain_range()[1]) # set max Tx gain + self.set_auto_tr(True) # enable Auto Transmit/Receive switching + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital up converter. + """ + r = self.u.tune(self.subdev._which, self.subdev, target_freq) + if r: + return True + + return False + + def set_gain(self, gain): + self.gain = gain + self.subdev.set_gain(gain) + + def set_auto_tr(self, enable): + return self.subdev.set_auto_tr(enable) + + def send_pkt(self, payload='', eof=False): + return self.packet_transmitter.send_pkt(payload, eof) + + def bitrate(self): + return self._bitrate + + def spb(self): + return self._spb + + def interp(self): + return self._interp diff --git a/gnuradio-examples/python/gmsk2/tunnel.py b/gnuradio-examples/python/gmsk2/tunnel.py new file mode 100755 index 0000000000..bc10d3e34f --- /dev/null +++ b/gnuradio-examples/python/gmsk2/tunnel.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python +# +# Copyright 2005,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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + + +# ///////////////////////////////////////////////////////////////////////////// +# +# This code sets up up a virtual ethernet interface (typically gr0), +# and relays packets between the interface and the GNU Radio PHY+MAC +# +# What this means in plain language, is that if you've got a couple +# of USRPs on different machines, and if you run this code on those +# machines, you can talk between them using normal TCP/IP networking. +# +# ///////////////////////////////////////////////////////////////////////////// + + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import time +import struct +import sys +import os + +# from current dir +from transmit_path import transmit_path +from receive_path import receive_path +import fusb_options + +#print os.getpid() +#raw_input('Attach and press enter') + + +# ///////////////////////////////////////////////////////////////////////////// +# +# Use the Universal TUN/TAP device driver to move packets to/from kernel +# +# See /usr/src/linux/Documentation/networking/tuntap.txt +# +# ///////////////////////////////////////////////////////////////////////////// + +# Linux specific... +# TUNSETIFF ifr flags from <linux/tun_if.h> + +IFF_TUN = 0x0001 # tunnel IP packets +IFF_TAP = 0x0002 # tunnel ethernet frames +IFF_NO_PI = 0x1000 # don't pass extra packet info +IFF_ONE_QUEUE = 0x2000 # beats me ;) + +def open_tun_interface(tun_device_filename): + from fcntl import ioctl + + mode = IFF_TAP | IFF_NO_PI + TUNSETIFF = 0x400454ca + + tun = os.open(tun_device_filename, os.O_RDWR) + ifs = ioctl(tun, TUNSETIFF, struct.pack("16sH", "gr%d", mode)) + ifname = ifs[:16].strip("\x00") + return (tun, ifname) + + +# ///////////////////////////////////////////////////////////////////////////// +# the flow graph +# ///////////////////////////////////////////////////////////////////////////// + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, demod_class, tx_subdev_spec, rx_subdev_spec, + bitrate, decim_rate, interp_rate, spb, + bt, rx_callback, options): + + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, + bt, options) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options) + + def send_pkt(self, payload='', eof=False): + return self.txpath.send_pkt(payload, eof) + + def carrier_sensed(self): + """ + Return True if the receive path thinks there's carrier + """ + return self.rxpath.carrier_sensed() + + +# ///////////////////////////////////////////////////////////////////////////// +# Carrier Sense MAC +# ///////////////////////////////////////////////////////////////////////////// + +class cs_mac(object): + """ + Prototype carrier sense MAC + + Reads packets from the TUN/TAP interface, and sends them to the PHY. + Receives packets from the PHY via phy_rx_callback, and sends them + into the TUN/TAP interface. + + Of course, we're not restricted to getting packets via TUN/TAP, this + is just an example. + """ + def __init__(self, tun_fd, verbose=False): + self.tun_fd = tun_fd # file descriptor for TUN/TAP interface + self.verbose = verbose + self.fg = None # flow graph (access to PHY) + + def set_flow_graph(self, fg): + self.fg = fg + + def phy_rx_callback(self, ok, payload): + """ + Invoked by thread associated with PHY to pass received packet up. + + @param ok: bool indicating whether payload CRC was OK + @param payload: contents of the packet (string) + """ + if self.verbose: + print "Rx: ok = %r len(payload) = %4d" % (ok, len(payload)) + if ok: + os.write(self.tun_fd, payload) + + def main_loop(self): + """ + Main loop for MAC. + Only returns if we get an error reading from TUN. + + FIXME: may want to check for EINTR and EAGAIN and reissue read + """ + min_delay = 0.001 # seconds + + while 1: + payload = os.read(self.tun_fd, 10*1024) + if not payload: + self.fg.send_pkt(eof=True) + break + + if self.verbose: + print "Tx: len(payload) = %4d" % (len(payload),) + + delay = min_delay + while self.fg.carrier_sensed(): + sys.stderr.write('B') + time.sleep(delay) + if delay < 0.050: + delay = delay * 2 # exponential back-off + + self.fg.send_pkt(payload) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + parser = OptionParser (option_class=eng_option) + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-g", "--rx-gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-c", "--carrier-threshold", type="eng_float", default=30, + help="set carrier detect threshold (dB) [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, help="set bandwidth-time product [default=%default]") + parser.add_option("","--tun-device-filename", default="/dev/net/tun", + help="path to tun device file [default=%default]") + parser.add_option("-v","--verbose", action="store_true", default=False) + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + # open the TUN/TAP interface + (tun_fd, tun_ifname) = open_tun_interface(options.tun_device_filename) + + # Attempt to enable realtime scheduling + r = gr.enable_realtime_scheduling() + if r == gr.RT_OK: + realtime = True + else: + realtime = False + print "Note: failed to enable realtime scheduling" + + + # If the user hasn't set the fusb_* parameters on the command line, + # pick some values that will reduce latency. + + if options.fusb_block_size == 0 and options.fusb_nblocks == 0: + if realtime: # be more aggressive + options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024) + options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16) + else: + options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096) + options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16) + + print "fusb_block_size =", options.fusb_block_size + print "fusb_nblocks =", options.fusb_nblocks + + # instantiate the MAC + mac = cs_mac(tun_fd, verbose=True) + + + # build the graph (PHY) + fg = my_graph(blks.gmsk2_mod, blks.gmsk2_demod, + options.tx_subdev_spec, options.rx_subdev_spec, + options.bitrate, options.decim, options.interp, + options.spb, options.bt, mac.phy_rx_callback, + options) + + mac.set_flow_graph(fg) # give the MAC a handle for the PHY + + if fg.txpath.bitrate() != fg.rxpath.bitrate(): + print "WARNING: Transmit bitrate = %sb/sec, Receive bitrate = %sb/sec" % ( + eng_notation.num_to_str(fg.txpath.bitrate()), + eng_notation.num_to_str(fg.rxpath.bitrate())) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.rx_gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + fg.rxpath.set_carrier_threshold(options.carrier_threshold) + print "Carrier sense threshold:", options.carrier_threshold, "dB" + + print + print "Allocated virtual ethernet interface: %s" % (tun_ifname,) + print "You must now use ifconfig to set its IP address. E.g.," + print + print " $ sudo ifconfig %s 10.10.10.1" % (tun_ifname,) + print + print "Be sure to use a different address in the same subnet for each machine." + print + + + fg.start() # Start executing the flow graph (runs in separate threads) + + mac.main_loop() # don't expect this to return... + + fg.stop() # but if it does, tell flow graph to stop. + fg.wait() # wait for it to finish + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/tx_voice.py b/gnuradio-examples/python/gmsk2/tx_voice.py new file mode 100755 index 0000000000..9e274e5e09 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/tx_voice.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Copyright 2005 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 2, 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import audio +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio.vocoder import gsm_full_rate + +import random +import time +import struct +import sys + +# from current dir +from transmit_path import transmit_path + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, tx_subdev_spec, bitrate, interp_rate, spb, bt): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, tx_subdev_spec, bitrate, interp_rate, spb, bt) + + audio_input = "" + sample_rate = 8000 + src = audio.source(sample_rate, audio_input) + src_scale = gr.multiply_const_ff(32767) + f2s = gr.float_to_short() + voice_coder = gsm_full_rate.encode_sp() + self.packets_from_encoder = gr.msg_queue() + packet_sink = gr.message_sink(33, self.packets_from_encoder, False) + self.connect(src, src_scale, f2s, voice_coder, packet_sink) + + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, help="set bandwidth-time product [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)") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + # build the graph + fg = my_graph(options.tx_subdev_spec, options.bitrate, options.interp, + options.spb, options.bt) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + fg.txpath.set_freq(options.freq) + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + packet = fg.packets_from_encoder.delete_head() + s = packet.to_string() + send_pkt(s) + n += len(s) + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass |