diff options
Diffstat (limited to 'gnuradio-examples/python')
-rw-r--r-- | gnuradio-examples/python/Makefile.am | 4 | ||||
-rwxr-xr-x | gnuradio-examples/python/digital/benchmark_loopback.py | 25 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/Makefile.am | 32 | ||||
-rwxr-xr-x | gnuradio-examples/python/ofdm/benchmark_ofdm.py | 193 | ||||
-rwxr-xr-x | gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py | 204 | ||||
-rwxr-xr-x | gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py | 229 | ||||
-rwxr-xr-x | gnuradio-examples/python/ofdm/fftshift.py | 44 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/fusb_options.py | 31 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/ofdm.py | 161 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/ofdm_receiver.py | 147 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/pick_bitrate.py | 143 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/receive_path.py | 125 | ||||
-rw-r--r-- | gnuradio-examples/python/ofdm/transmit_path.py | 104 |
13 files changed, 1433 insertions, 9 deletions
diff --git a/gnuradio-examples/python/Makefile.am b/gnuradio-examples/python/Makefile.am index 0ebffb64e..e3aca7ef3 100644 --- a/gnuradio-examples/python/Makefile.am +++ b/gnuradio-examples/python/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2004 Free Software Foundation, Inc. +# Copyright 2004,2007 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -20,4 +20,4 @@ # SUBDIRS = apps audio channel-coding digital_voice digital multi-antenna \ - multi_usrp networking usrp hier + multi_usrp networking usrp hier ofdm diff --git a/gnuradio-examples/python/digital/benchmark_loopback.py b/gnuradio-examples/python/digital/benchmark_loopback.py index d036f63aa..2b1e965fa 100755 --- a/gnuradio-examples/python/digital/benchmark_loopback.py +++ b/gnuradio-examples/python/digital/benchmark_loopback.py @@ -1,7 +1,6 @@ #!/usr/bin/env python -#!/usr/bin/env python # -# Copyright 2005, 2006 Free Software Foundation, Inc. +# Copyright 2005, 2006, 2007 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -77,10 +76,23 @@ class my_graph(gr.flow_graph): self.rxpath = receive_path(self, demod_class, rx_callback, options) if channelon: - self.channel = awgn_channel(self, options.sample_rate, noise_voltage, frequency_offset, options.seed) + self.channel = awgn_channel(self, options.sample_rate, noise_voltage, + frequency_offset, options.seed) + + if options.discontinuous: + z = 20000*[0,] + self.zeros = gr.vector_source_c(z, True) + packet_size = 5*((4+8+4+1500+4) * 8) + self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(9e5)]) + + # Connect components + self.connect(self.txpath, (self.mux,0)) + self.connect(self.zeros, (self.mux,1)) + self.connect(self.mux, self.channel, self.rxpath) + + else: + self.connect(self.txpath, self.channel, self.rxpath) - # Connect components - self.connect(self.txpath, self.throttle, self.channel, self.rxpath) else: # Connect components self.connect(self.txpath, self.throttle, self.rxpath) @@ -106,6 +118,7 @@ def main(): print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % ( ok, pktno, n_rcvd, n_right) + # print payload[2:len(payload)] def send_pkt(payload='', eof=False): return fg.txpath.send_pkt(payload, eof) @@ -170,8 +183,6 @@ def main(): while n < nbytes: send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) n += pkt_size - if options.discontinuous and pktno % 5 == 4: - time.sleep(1) pktno += 1 send_pkt(eof=True) diff --git a/gnuradio-examples/python/ofdm/Makefile.am b/gnuradio-examples/python/ofdm/Makefile.am new file mode 100644 index 000000000..23df3f9d6 --- /dev/null +++ b/gnuradio-examples/python/ofdm/Makefile.am @@ -0,0 +1,32 @@ +# +# Copyright 2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +EXTRA_DIST = \ + benchmark_ofdm.py \ + benchmark_ofdm_rx.py \ + benchmark_ofdm_tx.py \ + fftshift.py \ + fusb_options.py \ + ofdm.py \ + ofdm_receiver.py \ + pick_bitrate.py \ + receive_path.py \ + transmit_path.py diff --git a/gnuradio-examples/python/ofdm/benchmark_ofdm.py b/gnuradio-examples/python/ofdm/benchmark_ofdm.py new file mode 100755 index 000000000..3b4761d5e --- /dev/null +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm.py @@ -0,0 +1,193 @@ +#!/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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru, modulation_utils +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, math, os + +# from current dir +from transmit_path import transmit_path +from receive_path import receive_path +import ofdm + +class awgn_channel(gr.hier_block): + def __init__(self, fg, sample_rate, noise_voltage, frequency_offset): + + self.input = gr.add_const_cc(0) + + self.noise_adder = gr.add_cc() + self.noise = gr.noise_source_c(gr.GR_GAUSSIAN,noise_voltage) + self.offset = gr.sig_source_c(1, gr.GR_SIN_WAVE, frequency_offset, 1.0, 0.0) + self.mixer_offset = gr.multiply_cc() + + fg.connect(self.input, (self.mixer_offset,0)) + fg.connect(self.offset,(self.mixer_offset,1)) + fg.connect(self.mixer_offset, (self.noise_adder,1)) + fg.connect(self.noise, (self.noise_adder,0)) + + gr.hier_block.__init__(self, fg, self.input, self.noise_adder) + +class multipath_channel(gr.hier_block): + def __init__(self, fg): + + self.taps = [1.0, .2, 0.0, .1, .08, -.4, .12, -.2, 0, 0, 0, .3] + self.chan = gr.fir_filter_ccc(1, self.taps) + + gr.hier_block.__init__(self, fg, self.chan, self.chan) + +class my_graph(gr.flow_graph): + def __init__(self, callback, options): + gr.flow_graph.__init__(self) + + channel_on = True + + SNR = 10.0**(options.snr/10.0) + frequency_offset = options.frequency_offset / options.fft_length + + power_in_signal = options.occupied_tones + noise_power_in_channel = power_in_signal/SNR + noise_power_required = noise_power_in_channel * options.fft_length / options.occupied_tones + noise_voltage = math.sqrt(noise_power_required) + + self.txpath = transmit_path(self, options) + self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate) + self.rxpath = receive_path(self, callback, options) + + if channel_on: + self.channel = awgn_channel(self, options.sample_rate, noise_voltage, frequency_offset) + self.multipath = multipath_channel(self) + + if options.discontinuous: + z = 20000*[0,] + self.zeros = gr.vector_source_c(z, True) + packet_size = 15*((4+8+4+1500+4) * 8) + self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(10e5)]) + + # Connect components + self.connect(self.txpath, (self.mux,0)) + self.connect(self.zeros, (self.mux,1)) + self.connect(self.mux, self.throttle, self.channel, self.rxpath) + self.connect(self.mux, gr.file_sink(gr.sizeof_gr_complex, "tx_ofdm.dat")) + + else: + #self.connect(self.txpath, self.throttle, self.multipath, self.channel) + self.connect(self.txpath, self.throttle, self.channel) + self.connect(self.channel, self.rxpath) + self.connect(self.txpath, gr.file_sink(gr.sizeof_gr_complex, "tx_ofdm.dat")) + + else: + self.connect(self.txpath, self.throttle, self.rxpath) + self.connect(self.txpath, gr.file_sink(gr.sizeof_gr_complex, "tx")) + self.connect(self.rxpath.ofdm_demod.ofdm_rx, gr.file_sink(options.fft_length*gr.sizeof_gr_complex, "rx")) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + global n_rcvd, n_right + n_rcvd += 1 + (pktno,) = struct.unpack('!H', payload[0:2]) + if ok: + n_right += 1 + print "ok: %r \t pktno: %d \t n_rcvd: %d \t n_right: %d" % (ok, pktno, n_rcvd, n_right) + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + parser.add_option("-s", "--size", type="eng_float", default=1450, + help="set packet size [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1e5, + help="set sample rate to RATE (%default)") + parser.add_option("", "--snr", type="eng_float", default=30, + help="set the SNR of the channel in dB [default=%default]") + parser.add_option("", "--frequency-offset", type="eng_float", default=0, + help="set frequency offset introduced by channel [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + + transmit_path.add_options(parser, expert_grp) + receive_path.add_options(parser, expert_grp) + ofdm.ofdm_mod.add_options(parser, expert_grp) + ofdm.ofdm_demod.add_options(parser, expert_grp) + + (options, args) = parser.parse_args () + + if(options.mtu < options.size): + sys.stderr.write("MTU (%.0f) must be larger than the packet size (%.0f)\n" + % (options.mtu, options.size)) + sys.exit(1) + + # build the graph + fg = my_graph(rx_callback, options) + + 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 + pkt_size = int(options.size) + + + + while n < nbytes: + r = ''.join([chr(random.randint(0,255)) for i in range(pkt_size-2)]) + #pkt_contents = struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff) + pkt_contents = struct.pack('!H', pktno) + r + send_pkt(pkt_contents) + 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 + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass + + diff --git a/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py b/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py new file mode 100755 index 000000000..f8ebb820d --- /dev/null +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py @@ -0,0 +1,204 @@ +#!/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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru, modulation_utils +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, math + +# from current dir +from receive_path import receive_path +import ofdm, fusb_options + +class usrp_graph(gr.flow_graph): + def __init__(self, callback, options): + gr.flow_graph.__init__(self) + + self._rx_freq = options.rx_freq # receiver's center frequency + self._rx_gain = options.rx_gain # receiver's gain + self._rx_subdev_spec = options.rx_subdev_spec # daughterboard to use + self._decim = options.decim # Decimating rate for the USRP (prelim) + self._fusb_block_size = options.fusb_block_size # usb info for USRP + self._fusb_nblocks = options.fusb_nblocks # usb info for USRP + + if self._rx_freq is None: + sys.stderr.write("-f FREQ or --freq FREQ or --rx-freq FREQ must be specified\n") + raise SystemExit + + # Set up USRP source + self._setup_usrp_source() + ok = self.set_freq(self._rx_freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(self._rx_freq)) + raise ValueError, eng_notation.num_to_str(self._rx_freq) + g = self.subdev.gain_range() + if options.show_rx_gain_range: + print "Rx Gain Range: minimum = %g, maximum = %g, step size = %g" \ + % (g[0], g[1], g[2]) + self.set_gain(options.rx_gain) + self.set_auto_tr(True) # enable Auto Transmit/Receive switching + + # Set up receive path + self.rxpath = receive_path(self, callback, options) + + self.connect(self.u, self.rxpath) + + def _setup_usrp_source(self): + self.u = usrp.source_c (fusb_block_size=self._fusb_block_size, + fusb_nblocks=self._fusb_nblocks) + adc_rate = self.u.adc_rate() + + self.u.set_decim_rate(self._decim) + + # determine the daughterboard subdevice we're using + if self._rx_subdev_spec is None: + self._rx_subdev_spec = usrp.pick_rx_subdevice(self.u) + self.subdev = usrp.selected_subdev(self.u, self._rx_subdev_spec) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, self._rx_subdev_spec)) + + 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): + """ + Sets the analog gain in the USRP + """ + 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 decim(self): + return self._decim + + def add_options(normal, expert): + """ + Adds usrp-specific options to the Options Parser + """ + add_freq_option(normal) + if not normal.has_option("--bitrate"): + normal.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. samples-per-symbol and interp/decim will be derived.") + normal.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + normal.add_option("", "--rx-gain", type="eng_float", default=None, metavar="GAIN", + help="set receiver gain in dB [default=midpoint]. See also --show-rx-gain-range") + normal.add_option("", "--show-rx-gain-range", action="store_true", default=False, + help="print min and max Rx gain available on selected daughterboard") + normal.add_option("-v", "--verbose", action="store_true", default=False) + + expert.add_option("-S", "--samples-per-symbol", type="int", default=None, + help="set samples/symbol [default=%default]") + expert.add_option("", "--rx-freq", type="eng_float", default=None, + help="set Rx frequency to FREQ [default=%default]", metavar="FREQ") + expert.add_option("-d", "--decim", type="intx", default=32, + help="set fpga decimation rate to DECIM [default=%default]") + expert.add_option("", "--snr", type="eng_float", default=30, + help="set the SNR of the channel in dB [default=%default]") + + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + +def add_freq_option(parser): + """ + Hackery that has the -f / --freq option set both tx_freq and rx_freq + """ + def freq_callback(option, opt_str, value, parser): + parser.values.rx_freq = value + parser.values.tx_freq = value + + if not parser.has_option('--freq'): + parser.add_option('-f', '--freq', type="eng_float", + action="callback", callback=freq_callback, + help="set Tx and/or Rx frequency to FREQ [default=%default]", + metavar="FREQ") + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +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 + (pktno,) = struct.unpack('!H', payload[0:2]) + if ok: + n_right += 1 + print "ok: %r \t pktno: %d \t n_rcvd: %d \t n_right: %d" % (ok, pktno, n_rcvd, n_right) + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1e5, + help="set sample rate to RATE (%default)") + + usrp_graph.add_options(parser, expert_grp) + receive_path.add_options(parser, expert_grp) + ofdm.ofdm_mod.add_options(parser, expert_grp) + fusb_options.add_options(expert_grp) + + (options, args) = parser.parse_args () + + # build the graph + fg = usrp_graph(rx_callback, options) + + 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/ofdm/benchmark_ofdm_tx.py b/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py new file mode 100755 index 000000000..bda638053 --- /dev/null +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py @@ -0,0 +1,229 @@ +#!/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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru, modulation_utils +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, math + +# from current dir +from transmit_path import transmit_path +from pick_bitrate import pick_tx_bitrate +import ofdm, fusb_options + +class usrp_graph(gr.flow_graph): + def __init__(self, options): + gr.flow_graph.__init__(self) + + self._tx_freq = options.tx_freq # tranmitter's center frequency + self._tx_subdev_spec = options.tx_subdev_spec # daughterboard to use + self._bitrate = options.bitrate # desired bit rate + self._interp = options.interp # interpolating rate for the USRP (prelim) + self._fusb_block_size = options.fusb_block_size # usb info for USRP + self._fusb_nblocks = options.fusb_nblocks # usb info for USRP + + if self._tx_freq is None: + sys.stderr.write("-f FREQ or --freq FREQ or --tx-freq FREQ must be specified\n") + raise SystemExit + + # Set up USRP sink; also adjusts interp, and bitrate + self._setup_usrp_sink() + + # copy the final answers back into options for use by modulator + #options.bitrate = self._bitrate + + self.txpath = transmit_path(self, options) + + self.connect(self.txpath, self.u) + + def _setup_usrp_sink(self): + """ + Creates a USRP sink, determines the settings for best bitrate, + and attaches to the transmitter's subdevice. + """ + self.u = usrp.sink_c(fusb_block_size=self._fusb_block_size, + fusb_nblocks=self._fusb_nblocks) + + self.u.set_interp_rate(self._interp) + + # determine the daughterboard subdevice we're using + if self._tx_subdev_spec is None: + self._tx_subdev_spec = usrp.pick_tx_subdevice(self.u) + self.u.set_mux(usrp.determine_tx_mux_value(self.u, self._tx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, self._tx_subdev_spec) + + # Set center frequency of USRP + ok = self.set_freq(self._tx_freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(self._tx_freq),) + raise ValueError + + # Set the USRP for maximum transmit gain + # (Note that on the RFX cards this is a nop.) + self.set_gain(self.subdev.gain_range()[0]) + + # enable Auto Transmit/Receive switching + self.set_auto_tr(True) + + 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): + """ + Sets the analog gain in the USRP + """ + self.gain = gain + self.subdev.set_gain(gain) + + def set_auto_tr(self, enable): + """ + Turns on auto transmit/receive of USRP daughterboard (if exits; else ignored) + """ + return self.subdev.set_auto_tr(enable) + + def interp(self): + return self._interp + + def add_options(normal, expert): + """ + Adds usrp-specific options to the Options Parser + """ + add_freq_option(normal) + if not normal.has_option('--bitrate'): + normal.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. samples-per-symbol and interp/decim will be derived.") + normal.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + normal.add_option("-v", "--verbose", action="store_true", default=False) + + expert.add_option("-S", "--samples-per-symbol", type="int", default=None, + help="set samples/symbol [default=%default]") + expert.add_option("", "--tx-freq", type="eng_float", default=None, + help="set transmit frequency to FREQ [default=%default]", metavar="FREQ") + expert.add_option("-i", "--interp", type="intx", default=64, + help="set fpga interpolation rate to INTERP [default=%default]") + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the transmit path + """ + print "Using TX d'board %s" % (self.subdev.side_and_name(),) + print "modulation: %s" % (self._modulator_class.__name__) + print "interp: %3d" % (self._interp) + print "Tx Frequency: %s" % (eng_notation.num_to_str(self._tx_freq)) + + +def add_freq_option(parser): + """ + Hackery that has the -f / --freq option set both tx_freq and rx_freq + """ + def freq_callback(option, opt_str, value, parser): + parser.values.rx_freq = value + parser.values.tx_freq = value + + if not parser.has_option('--freq'): + parser.add_option('-f', '--freq', type="eng_float", + action="callback", callback=freq_callback, + help="set Tx and/or Rx frequency to FREQ [default=%default]", + metavar="FREQ") + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + parser.add_option("-s", "--size", type="eng_float", default=1450, + help="set packet size [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1e5, + help="set sample rate to RATE (%default)") + + usrp_graph.add_options(parser, expert_grp) + transmit_path.add_options(parser, expert_grp) + ofdm.ofdm_mod.add_options(parser, expert_grp) + fusb_options.add_options(expert_grp) + + (options, args) = parser.parse_args () + + if(options.mtu < options.size): + sys.stderr.write("MTU (%.0f) must be larger than the packet size (%.0f)\n" + % (options.mtu, options.size)) + sys.exit(1) + + # build the graph + fg = usrp_graph(options) + + 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 + pkt_size = int(options.size) + + 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 + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/ofdm/fftshift.py b/gnuradio-examples/python/ofdm/fftshift.py new file mode 100755 index 000000000..6b355326c --- /dev/null +++ b/gnuradio-examples/python/ofdm/fftshift.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +from gnuradio import gr + +class my_graph(gr.flow_graph): + def __init__(self): + gr.flow_graph.__init__(self) + + length = 101 + + data_r = range(length) + data_i = range(length,2*length) + src_r = gr.vector_source_s(data_r, False) + src_i = gr.vector_source_s(data_i, False) + s2f_r = gr.short_to_float() + s2f_i = gr.short_to_float() + f2c = gr.float_to_complex() + s2v = gr.stream_to_vector(gr.sizeof_gr_complex, length) + + shift = True + ifft = gr.fft_vcc(length, False, [], shift) + fft = gr.fft_vcc(length, True, [], shift) + + v2s = gr.vector_to_stream(gr.sizeof_gr_complex, length) + snk_in = gr.file_sink(gr.sizeof_gr_complex, "fftshift.in") + snk_out = gr.file_sink(gr.sizeof_gr_complex, "fftshift.out") + + self.connect(src_r, s2f_r, (f2c,0)) + self.connect(src_i, s2f_i, (f2c,1)) + self.connect(f2c, snk_in) + self.connect(f2c, s2v, ifft, fft, v2s, snk_out) + + +def main(): + fg = my_graph() + fg.start() + fg.wait() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gnuradio-examples/python/ofdm/fusb_options.py b/gnuradio-examples/python/ofdm/fusb_options.py new file mode 100644 index 000000000..cdb15d72b --- /dev/null +++ b/gnuradio-examples/python/ofdm/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., 51 Franklin Street, +# Boston, MA 02110-1301, 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/ofdm/ofdm.py b/gnuradio-examples/python/ofdm/ofdm.py new file mode 100644 index 000000000..8b1e616f9 --- /dev/null +++ b/gnuradio-examples/python/ofdm/ofdm.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# +# Copyright 2004,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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from optparse import OptionParser +from ofdm_receiver import ofdm_receiver + +class ofdm_mod(gr.hier_block): + def __init__(self, fg, options): + self.fg = fg + self._occupied_tones = options.occupied_tones + self._fft_length = options.fft_length + self._cp_length = options.cp_length + self._mtu = options.mtu + + symbol_length = self._fft_length + self._cp_length + + if self._fft_length < self._occupied_tones: + sys.stderr.write("occupied tones must be less than FFT length\n") + raise SystemExit + if self._fft_length < self._cp_length: + sys.stderr.write("cyclic prefix length must be less than FFT length\n") + raise SystemExit + + win = [] #[1 for i in range(self._fft_length)] + + # hard-coded known symbol + #ks = self._occupied_tones*[1,] + ks1 = known_symbols_200_1 + ks2 = known_symbols_200_2 + + self.ofdm = gr.ofdm_bpsk_mapper(self._mtu, self._occupied_tones, self._fft_length, ks1, ks2) + self.ifft = gr.fft_vcc(self._fft_length, False, win, True) + self.cp_adder = gr.ofdm_cyclic_prefixer(self._fft_length, symbol_length) + + if options.verbose: + self._print_verbage() + + self.fg.connect(self.ofdm, self.ifft, self.cp_adder) + gr.hier_block.__init__(self, self.fg, self.ofdm, self.cp_adder) + + def samples_per_symbol(self): + return 2 + + def mtu(self): + return self._mtu + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def add_options(normal, expert): + """ + Adds OFDM-specific options to the Options Parser + """ + expert.add_option("", "--mtu", type="int", default=1500, + help="set maximum transmit unit [default=%default]") + expert.add_option("", "--fft-length", type="intx", default=512, + help="set the number of FFT bins [default=%default]") + expert.add_option("", "--occupied-tones", type="intx", default=200, + help="set the number of occupied FFT bins [default=%default]") + expert.add_option("", "--cp-length", type="intx", default=128, + help="set the number of bits in the cyclic prefix [default=%default]") + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the OFDM modulator + """ + print "\nOFDM Modulator:" + print "FFT length: %3d" % (self._fft_length) + print "Occupied Tones: %3d" % (self._occupied_tones) + print "CP length: %3d" % (self._cp_length) + + +class ofdm_demod(gr.hier_block): + def __init__(self, fg, options): + self.fg = fg + self._occupied_tones = options.occupied_tones + self._fft_length = options.fft_length + self._cp_length = options.cp_length + self._snr = options.snr + + symbol_length = self._fft_length + self._cp_length + + win = [1 for i in range(self._fft_length)] + + # hard-coded known symbol + ks1 = known_symbols_200_1 + ks2 = known_symbols_200_2 + + # ML Sync + self.ofdm_sync = ofdm_receiver(self.fg, self._fft_length, symbol_length, self._snr) + + # OFDM Demod + self.fft_demod = gr.fft_vcc(self._fft_length, True, win, True) + self.ofdm_corr = gr.ofdm_correlator(self._occupied_tones, self._fft_length, + self._cp_length, ks1, ks2) + self.ofdm_demod = gr.ofdm_bpsk_demapper(self._occupied_tones) + + + if options.verbose: + self._print_verbage() + + self.fg.connect(self.ofdm_sync, self.fft_demod, self.ofdm_corr, self.ofdm_demod) + gr.hier_block.__init__(self, self.fg, self.ofdm_sync, self.ofdm_demod) + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def add_options(normal, expert): + """ + Adds OFDM-specific options to the Options Parser + """ + expert.add_option("", "--fft-length", type="intx", default=512, + help="set the number of FFT bins [default=%default]") + expert.add_option("", "--occupied-tones", type="intx", default=200, + help="set the number of occupied FFT bins [default=%default]") + expert.add_option("", "--cp-length", type="intx", default=128, + help="set the number of bits in the cyclic prefix [default=%default]") + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the OFDM demodulator + """ + print "\nOFDM Demodulator:" + print "FFT length: %3d" % (self._fft_length) + print "Occupied Tones: %3d" % (self._occupied_tones) + print "CP length: %3d" % (self._cp_length) + + +# generated in python using: +# import random +# pn = [2.0*random.randint(0,1)-1.0 for i in range(self._occupied_tones)] + +known_symbols_200_1 = [1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0] + +known_symbols_200_2 = [-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0] diff --git a/gnuradio-examples/python/ofdm/ofdm_receiver.py b/gnuradio-examples/python/ofdm/ofdm_receiver.py new file mode 100644 index 000000000..467e3af05 --- /dev/null +++ b/gnuradio-examples/python/ofdm/ofdm_receiver.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# +# Copyright 2004,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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class ofdm_receiver(gr.hier_block): + def __init__(self, fg, fft_length, symbol_length, snr): + self.input = gr.add_const_cc(0) # Kluge that goes away with hier_block2 + + self.fg = fg + + cpsize = symbol_length - fft_length; + + SNR = 10.0**(snr/10.0) + rho = SNR / (SNR + 1.0) + + # ML Sync + + # Energy Detection from ML Sync + + # Create a delay line + delayline = [0.0 for i in range(fft_length+1)] + delayline[fft_length] = 1.0 + self.delay = gr.fir_filter_ccf(1,delayline) + self.fg.connect(self.input, self.delay) + + # magnitude squared blocks + self.magsqrd1 = gr.complex_to_mag_squared() + self.magsqrd2 = gr.complex_to_mag_squared() + self.adder = gr.add_ff() + + moving_sum_taps = [rho/2 for i in range(cpsize)] + self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) + + self.fg.connect(self.input,self.magsqrd1) + self.fg.connect(self.delay,self.magsqrd2) + self.fg.connect(self.magsqrd1,(self.adder,0)) + self.fg.connect(self.magsqrd2,(self.adder,1)) + self.fg.connect(self.adder,self.moving_sum_filter) + + + # Correlation from ML Sync + self.conjg = gr.conjugate_cc(); + self.mixer = gr.multiply_cc(); + + movingsum2_taps = [1.0 for i in range(cpsize)] + self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps) + + + # Correlator data handler + self.c2mag = gr.complex_to_mag() + self.angle = gr.complex_to_arg() + self.fg.connect(self.input,(self.mixer,1)) + self.fg.connect(self.delay,self.conjg,(self.mixer,0)) + self.fg.connect(self.mixer,self.movingsum2,self.c2mag) + self.fg.connect(self.movingsum2,self.angle) + + # ML Sync output arg, need to find maximum point of this + self.diff = gr.sub_ff() + self.fg.connect(self.c2mag,(self.diff,0)) + self.fg.connect(self.moving_sum_filter,(self.diff,1)) + + #ML measurements input to sampler block and detect + nco_sensitivity = 1.0/fft_length + self.f2c = gr.float_to_complex() + self.sampler = gr.ofdm_sampler(fft_length,symbol_length) + self.pkt_detect = gr.peak_detector_ff(0.2, 0.25, 30, 0.0001) + self.dpll = gr.dpll_ff(float(symbol_length),0.01) + self.sample_and_hold = gr.sample_and_hold_ff() + self.nco = gr.frequency_modulator_fc(nco_sensitivity) + self.inv = gr.multiply_const_ff(-1) + self.sigmix = gr.multiply_cc() + + # Mix the signal with an NCO controlled by the sync loop + self.fg.connect(self.input, (self.sigmix,0)) + self.fg.connect(self.nco, (self.sigmix,1)) + self.fg.connect(self.sigmix, (self.sampler,0)) + + sample_trigger = 0 + if sample_trigger: + # for testing + peak_null = gr.null_sink(gr.sizeof_float) + data = 640*[0,] + data[639] = 1 + peak_trigger = gr.vector_source_f(data, True) + + self.fg.connect(self.pkt_detect, peak_null) + self.fg.connect(peak_trigger, self.f2c, (self.sampler,1)) + self.fg.connect(peak_trigger, (self.sample_and_hold,1)) + + # use the sync loop values to set the sampler and the NCO + # self.diff = theta + # self.angle = epsilon + + self.fg.connect(self.diff, self.pkt_detect) + use_dpll = 1 + if not sample_trigger: + if use_dpll: + self.fg.connect(self.pkt_detect, self.dpll,self.f2c, (self.sampler,1)) + self.fg.connect(self.dpll, (self.sample_and_hold,1)) + if not use_dpll: + self.fg.connect(self.pkt_detect, self.f2c, (self.sampler,1)) + self.fg.connect(self.pkt_detect, (self.sample_and_hold,1)) + + self.fg.connect(self.angle, (self.sample_and_hold,0)) + self.fg.connect(self.sample_and_hold, self.inv, self.nco) + + + if 0: + self.fg.connect(self.diff, gr.file_sink(gr.sizeof_float, "theta_f.dat")) + self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "epsilon_f.dat")) + if use_dpll: + self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_float, "dpll_pulses.dat")) + if sample_trigger: + self.fg.connect(peak_trigger, gr.file_sink(gr.sizeof_float, "peaks_f.dat")) + else: + self.fg.connect(self.pkt_detect, gr.file_sink(gr.sizeof_float, "peaks_f.dat")) + + self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "sample_and_hold_f.dat")) + self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "nco_c.dat")) + self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "input_c.dat")) + self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "output_c.dat")) + + gr.hier_block.__init__(self, fg, self.input, self.sampler) diff --git a/gnuradio-examples/python/ofdm/pick_bitrate.py b/gnuradio-examples/python/ofdm/pick_bitrate.py new file mode 100644 index 000000000..42aefa94e --- /dev/null +++ b/gnuradio-examples/python/ofdm/pick_bitrate.py @@ -0,0 +1,143 @@ +# +# 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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +_default_bitrate = 500e3 + +_valid_samples_per_symbol = (2,3,4,5,6,7) + +def _gen_tx_info(converter_rate): + results = [] + for samples_per_symbol in _valid_samples_per_symbol: + for interp in range(16, 512 + 1, 4): + bitrate = converter_rate / interp / samples_per_symbol + results.append((bitrate, samples_per_symbol, interp)) + results.sort() + return results + +def _gen_rx_info(converter_rate): + results = [] + for samples_per_symbol in _valid_samples_per_symbol: + for decim in range(8, 256 + 1, 2): + bitrate = converter_rate / decim / samples_per_symbol + results.append((bitrate, samples_per_symbol, decim)) + results.sort() + return results + +def _filter_info(info, samples_per_symbol, xrate): + if samples_per_symbol is not None: + info = [x for x in info if x[1] == samples_per_symbol] + 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_symbol, 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_symbol 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_symbol, + xrate, converter_rate, gen_info): + """ + @returns tuple (bitrate, samples_per_symbol, 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_symbol is not None and xrate is not None: # completely determined + return (float(converter_rate) / xrate / samples_per_symbol, + samples_per_symbol, xrate) + + if bitrate is None and samples_per_symbol is None and xrate is None: + bitrate = _default_bitrate + + # now we have a target bitrate and possibly an xrate or + # samples_per_symbol constraint, but not both of them. + + return _pick_best(bitrate, bits_per_symbol, + _filter_info(gen_info(converter_rate), samples_per_symbol, xrate)) + +# --------------------------------------------------------------------------------------- + +def pick_tx_bitrate(bitrate, bits_per_symbol, samples_per_symbol, + 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_symbol: samples/baud (aka samples/symbol) + @type samples_per_symbol: 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_symbol, interp_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_symbol, + interp_rate, converter_rate, _gen_tx_info) + + +def pick_rx_bitrate(bitrate, bits_per_symbol, samples_per_symbol, + 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_symbol: samples/baud (aka samples/symbol) + @type samples_per_symbol: 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_symbol, decim_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_symbol, + decim_rate, converter_rate, _gen_rx_info) diff --git a/gnuradio-examples/python/ofdm/receive_path.py b/gnuradio-examples/python/ofdm/receive_path.py new file mode 100644 index 000000000..1c9c23844 --- /dev/null +++ b/gnuradio-examples/python/ofdm/receive_path.py @@ -0,0 +1,125 @@ +#!/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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +import copy +import sys + +# from current dir +from pick_bitrate import pick_rx_bitrate +import ofdm + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block): + def __init__(self, fg, rx_callback, options): + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._log = options.log + self._rx_callback = rx_callback # this callback is fired when there's a packet available + + if 0: + + chan_coeffs = gr.firdes.low_pass (1.0, # gain + 2.0, # sampling rate + bw, # midpoint of trans. band + 1*bw, # width of trans. band + gr.firdes.WIN_KAISER) # filter type + else: + chan_coeffs = [ 1.0, 0.0, 0 ] + self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) + + self.ofdm_demod = ofdm.ofdm_demod(fg, options) + + # receiver + self.packet_receiver = \ + blks.demod_ofdm_pkts(fg, + self.ofdm_demod, + access_code=None, + callback=self._rx_callback, + threshold=-1) + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + fg.connect(self.chan_filt, self.probe) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + fg.connect(self.chan_filt, self.packet_receiver) + #fg.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdmrx_chflt.dat")) + gr.hier_block.__init__(self, fg, self.chan_filt, None) + + def carrier_sensed(self): + """ + Return True if we think carrier is present. + """ + #return self.probe.level() > X + return self.probe.unmuted() + + def carrier_threshold(self): + """ + Return current setting in dB. + """ + return self.probe.threshold() + + def set_carrier_threshold(self, threshold_in_db): + """ + Set carrier threshold. + + @param threshold_in_db: set detection threshold + @type threshold_in_db: float (dB) + """ + self.probe.set_threshold(threshold_in_db) + + + def add_options(normal, expert): + """ + Adds receiver-specific options to the Options Parser + """ + normal.add_option("-v", "--verbose", action="store_true", default=False) + expert.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to files (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + + def _print_verbage(self): + """ + Prints information about the receive path + """ + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + print "Rx gain: %g" % (self.gain,) + print "modulation: %s" % (self._demod_class.__name__) + print "bitrate: %sb/s" % (eng_notation.num_to_str(self._bitrate)) + print "samples/symbol: %3d" % (self._samples_per_symbol) + print "decim: %3d" % (self._decim) diff --git a/gnuradio-examples/python/ofdm/transmit_path.py b/gnuradio-examples/python/ofdm/transmit_path.py new file mode 100644 index 000000000..308ec0851 --- /dev/null +++ b/gnuradio-examples/python/ofdm/transmit_path.py @@ -0,0 +1,104 @@ +# +# 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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation + +import copy +import sys + +# from current dir +import ofdm + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block): + def __init__(self, fg, options): + ''' + See below for what options should hold + ''' + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._tx_amplitude = options.tx_amplitude # digital amplitude sent to USRP + + self.ofdm_mod = ofdm.ofdm_mod(fg, options) + self.packet_transmitter = \ + blks.mod_ofdm_pkts(fg, + self.ofdm_mod, + access_code=None, + msgq_limit=4, + pad_for_usrp=True) + + self.amp = gr.multiply_const_cc(1) + self.set_tx_amplitude(self._tx_amplitude) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + # Create and setup transmit path flow graph + fg.connect(self.packet_transmitter, self.amp) + gr.hier_block.__init__(self, fg, None, self.amp) + + def set_tx_amplitude(self, ampl): + """ + Sets the transmit amplitude sent to the USRP + @param: ampl 0 <= ampl < 32768. Try 8000 + """ + self._tx_amplitude = max(0.0, min(ampl, 32767.0)) + self.amp.set_k(self._tx_amplitude) + + def send_pkt(self, payload='', eof=False): + """ + Calls the transmitter method to send a packet + """ + return self.packet_transmitter.send_pkt(payload, eof) + + def bitrate(self): + return self._bitrate + + def samples_per_symbol(self): + return self._samples_per_symbol + + def add_options(normal, expert): + """ + Adds transmitter-specific options to the Options Parser + """ + normal.add_option("", "--tx-amplitude", type="eng_float", default=12000, metavar="AMPL", + help="set transmitter digital amplitude: 0 <= AMPL < 32768 [default=%default]") + normal.add_option("-v", "--verbose", action="store_true", default=False) + expert.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to file (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the transmit path + """ + print "Tx amplitude %s" % (self._tx_amplitude) + |