diff options
Diffstat (limited to 'gr-digital')
189 files changed, 20156 insertions, 0 deletions
diff --git a/gr-digital/.gitignore b/gr-digital/.gitignore new file mode 100644 index 000000000..37c287f40 --- /dev/null +++ b/gr-digital/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +gnuradio-digital.pc diff --git a/gr-digital/Makefile.am b/gr-digital/Makefile.am new file mode 100644 index 000000000..85a9bbbde --- /dev/null +++ b/gr-digital/Makefile.am @@ -0,0 +1,31 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = include lib doc + +if PYTHON +SUBDIRS += swig python apps grc examples +endif + +pkgconfigdir = $(libdir)/pkgconfig +dist_pkgconfig_DATA = gnuradio-digital.pc diff --git a/gr-digital/README b/gr-digital/README new file mode 100644 index 000000000..af2005c97 --- /dev/null +++ b/gr-digital/README @@ -0,0 +1,4 @@ +This GNU Radio component for implementing digitial modulators and demodulators. + + +FIXME: just fixme.
\ No newline at end of file diff --git a/gr-digital/apps/.gitignore b/gr-digital/apps/.gitignore new file mode 100644 index 000000000..282522db0 --- /dev/null +++ b/gr-digital/apps/.gitignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/gr-digital/apps/Makefile.am b/gr-digital/apps/Makefile.am new file mode 100644 index 000000000..7373a0719 --- /dev/null +++ b/gr-digital/apps/Makefile.am @@ -0,0 +1,33 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +if PYTHON + +dist_bin_SCRIPTS = + +noinst_PYTHON = + +endif + +EXTRA_DIST += + diff --git a/gr-digital/doc/.gitignore b/gr-digital/doc/.gitignore new file mode 100644 index 000000000..b336cc7ce --- /dev/null +++ b/gr-digital/doc/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/gr-digital/doc/Makefile.am b/gr-digital/doc/Makefile.am new file mode 100644 index 000000000..dbfe1d5a2 --- /dev/null +++ b/gr-digital/doc/Makefile.am @@ -0,0 +1,27 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = + +dist_gr_doc_DATA = \ + README.digital diff --git a/gr-digital/doc/README.digital b/gr-digital/doc/README.digital new file mode 100644 index 000000000..f4d40f3a0 --- /dev/null +++ b/gr-digital/doc/README.digital @@ -0,0 +1,13 @@ +This is the gr-digital package. It contains all of the digital +modulation blocks, utilities, and examples. To use the digital blocks, +the Python namespaces is in gnuradio.digital, which would be normally +imported as: + + from gnuradio import digital + +See the Doxygen documentation for details about the blocks available +in this package. A quick listing of the details can be found in Python +after importing by using: + + help(digital) + diff --git a/gr-digital/doc/digital.dox b/gr-digital/doc/digital.dox new file mode 100644 index 000000000..110dcb677 --- /dev/null +++ b/gr-digital/doc/digital.dox @@ -0,0 +1,23 @@ +/*! \page page_digital Digital Modulation + +\section Introduction +This is the gr-digital package. It contains all of the digital +modulation blocks, utilities, and examples. To use the digital blocks, +the Python namespaces is in gnuradio.digital, which would be normally +imported as: + +\code + from gnuradio import digital +\endcode + +See the Doxygen documentation for details about the blocks available +in this package. The relevant blocks are listed in the \ref digital group. + +A quick listing of the details can be found in Python after importing +by using: + +\code + help(digital) +\endcode + +*/ diff --git a/gr-digital/examples/.gitignore b/gr-digital/examples/.gitignore new file mode 100644 index 000000000..d7028d40d --- /dev/null +++ b/gr-digital/examples/.gitignore @@ -0,0 +1,8 @@ +/Makefile +/Makefile.in +/*.8b +/*.16s +/*.16i +/*.32f +/*.32fc +/*.16sc diff --git a/gr-digital/examples/Makefile.am b/gr-digital/examples/Makefile.am new file mode 100644 index 000000000..3841bbd0e --- /dev/null +++ b/gr-digital/examples/Makefile.am @@ -0,0 +1,46 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +dig_examples_prefix= $(exampledir)/digital + +basicdatadir = $(dig_examples_prefix) +dist_basicdata_SCRIPTS = \ + example_costas.py \ + example_fll.py \ + example_timing.py \ + run_length.py \ + gen_whitener.py + +nbdatadir = $(dig_examples_prefix)/narrowband +dist_nbdata_SCRIPTS = \ + narrowband/transmit_path.py \ + narrowband/receive_path.py \ + narrowband/uhd_interface.py \ + narrowband/benchmark_tx.py \ + narrowband/benchmark_rx.py \ + narrowband/tx_voice.py \ + narrowband/rx_voice.py \ + narrowband/digital_bert_rx.py \ + narrowband/digital_bert_tx.py \ + narrowband/tunnel.py + diff --git a/gr-digital/examples/README b/gr-digital/examples/README new file mode 100644 index 000000000..1c50ad69b --- /dev/null +++ b/gr-digital/examples/README @@ -0,0 +1,153 @@ +Quick overview of what's here: + +* benchmark_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 RFX transceiver daughterboards. +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. +You can select the modulation to use with the -m <modulation> command +line argument. The legal values for <modulation> are gmsk, dbpsk and dqpsk. + +* benchmark_rx.py: the receiver half of benchmark_tx.py. +Command line arguments are pretty much the same as rx. Works well +with a USRP and RFX transceiver daughterboards. 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. For the RFX-400, "70 cm" / 420 MHz antennas for ham +handi-talkies work 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 192.168.200.1 + + +On machine B: + + $ su + # ./tunnel.py --freq 423.0M --bitrate 500k + # # in another window on B, also as root... + # ifconfig gr0 192.168.200.2 + +Now, on machine A you shold be able to ping machine B: + + $ ping 192.168.200.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 192.168.200.1 + +This now uses a carrier sense MAC, so you should be able to ssh +between the machines, web browse, etc. + +* run_length.py: This program takes a single argument '-f FILE' and +outputs the number of runs of similar bits within the file. It is +useful as a diagnostic tool when experimenting with line coding or +whitening algorithms. + + + +********************************************************************** +********************************************************************** + + +BERT testing example scripts + +benchmark_tx.py + +This sets up a BPSK transmitter that is modulated with a pseudorandom +sequence of bits. The PN code is generated by sending an all 1s +sequence through a 7-bit scrambler. The transmitter performs the BPSK +modulation, then passes the complex baseband waveform through a +root-raised-cosine filter and onto the USRP. + +The --sps parameter controls how many baseband samples per symbol +are created and passed through the RRC filter, prior to going to the +USRP over the USB for interpolation to the final DAC rate. + +The baseband bit rate is controlled by -r or --rate. This value, when +multiplied by the --sps parameter, must result in valid interpolation +rate for the USRP. For example, if the baseband rate is 250k bits/sec, +and the samples per symbol is 4, then the final rate is 1M samples/sec, +which results in an interpolation rate of 128. The valid interpolation +rates for the USRP are multiples of 4 between 16 and 512. + +Finally, the RRC excess bandwidth may be specified by --excess-bw. +(See ./benchmark_tx.py -h for additional parameters.) + + +benchmark_rx.py + +This sets up a BPSK receiver to demodulate the received waveform. It +accepts a similar set of parameters as the transmitter, except that one +specifies the USRP decimation rate desired. The resulting sample stream +rate must be an integral number of baseband symbols. For example, the +parameters corresponding to the above transmitter would be to use a +decimation rate of 8 (32 sps), 16 (16 sps), 32 (8 sps), 64, (4 sps), or +128 (2 sps). The lower the USRP decimation, the more CPU is required to +demodulate the signal, so not all valid decimation rates will work. + +The baseband signal from the USRP is first passed through an AGC to +establish an average power of 1.0. It is then passed through a matched +filter (another RRC), a Costas phase-locked loop, and a Mueller and +Muller bit timing recovery loop. The resulting constellation has an SNR +estimation probe attached, and is then sliced into a bit stream. + +The recovered bits are then passed through a 7-bit descrambler. If +there are no channel errors, the all 1s sequence is recovered. In the +event of a channel error, there will be a 0 in the bit stream for each +feedback tap in the descrambler. In this case, the CCSDS descrambler is +using 3 feedback taps. + +Finally, the signal is passed into a bit density measurement probe. The +channel BER is measured by dividing the 0s density by three. This +measurement is inaccurate at high BER rates (>10%) as the error 0s +begin to overlap. + +The benchmark script will, once per second, output the Costas loop +frequency offset, the recovered timing error, the estimated SNR, and the +average BER. + +NOTE: The particular SNR estimator used is inaccurate below about 7dB, +and will report erroneously high values even for random noise. + +There are a variety of Costas and M&M loop parameters one can adjust. +See ./benchmark_rx.py -h for the full set. + + diff --git a/gr-digital/examples/example_costas.py b/gr-digital/examples/example_costas.py new file mode 100755 index 000000000..aef0196cc --- /dev/null +++ b/gr-digital/examples/example_costas.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +from gnuradio import gr, digital +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_costas(gr.top_block): + def __init__(self, N, sps, rolloff, ntaps, bw, noise, foffset, toffset, poffset): + gr.top_block.__init__(self) + + rrc_taps = gr.firdes.root_raised_cosine( + sps, sps, 1.0, rolloff, ntaps) + + data = 2.0*scipy.random.randint(0, 2, N) - 1.0 + data = scipy.exp(1j*poffset) * data + + self.src = gr.vector_source_c(data.tolist(), False) + self.rrc = gr.interp_fir_filter_ccf(sps, rrc_taps) + self.chn = gr.channel_model(noise, foffset, toffset) + self.cst = digital.costas_loop_cc(bw, 2) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_cst = gr.vector_sink_c() + self.vsnk_frq = gr.vector_sink_f() + + self.connect(self.src, self.rrc, self.chn, self.cst, self.vsnk_cst) + self.connect(self.rrc, self.vsnk_src) + self.connect((self.cst,1), self.vsnk_frq) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=2000, + help="Set the number of samples to process [default=%default]") + parser.add_option("-S", "--sps", type="int", default=4, + help="Set the samples per symbol [default=%default]") + parser.add_option("-r", "--rolloff", type="eng_float", default=0.35, + help="Set the rolloff factor [default=%default]") + parser.add_option("-W", "--bandwidth", type="eng_float", default=2*scipy.pi/100.0, + help="Set the loop bandwidth [default=%default]") + parser.add_option("-n", "--ntaps", type="int", default=45, + help="Set the number of taps in the filters [default=%default]") + parser.add_option("", "--noise", type="eng_float", default=0.0, + help="Set the simulation noise voltage [default=%default]") + parser.add_option("-f", "--foffset", type="eng_float", default=0.0, + help="Set the simulation's normalized frequency offset (in Hz) [default=%default]") + parser.add_option("-t", "--toffset", type="eng_float", default=1.0, + help="Set the simulation's timing offset [default=%default]") + parser.add_option("-p", "--poffset", type="eng_float", default=0.707, + help="Set the simulation's phase offset [default=%default]") + (options, args) = parser.parse_args () + + # Adjust N for the interpolation by sps + options.nsamples = options.nsamples // options.sps + + # Set up the program-under-test + put = example_costas(options.nsamples, options.sps, options.rolloff, + options.ntaps, options.bandwidth, options.noise, + options.foffset, options.toffset, options.poffset) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + + # Convert the FLL's LO frequency from rads/sec to Hz + data_frq = scipy.array(put.vsnk_frq.data()) / (2.0*scipy.pi) + + # adjust this to align with the data. + data_cst = scipy.array(3*[0,]+list(put.vsnk_cst.data())) + + # Plot the Costas loop's LO frequency + f1 = pylab.figure(1, figsize=(12,10), facecolor='w') + s1 = f1.add_subplot(2,2,1) + s1.plot(data_frq) + s1.set_title("Costas LO") + s1.set_xlabel("Samples") + s1.set_ylabel("Frequency (normalized Hz)") + + # Plot the IQ symbols + s3 = f1.add_subplot(2,2,2) + s3.plot(data_src.real, data_src.imag, "o") + s3.plot(data_cst.real, data_cst.imag, "rx") + s3.set_title("IQ") + s3.set_xlabel("Real part") + s3.set_ylabel("Imag part") + s3.set_xlim([-2, 2]) + s3.set_ylim([-2, 2]) + + # Plot the symbols in time + s4 = f1.add_subplot(2,2,3) + s4.set_position([0.125, 0.05, 0.775, 0.4]) + s4.plot(data_src.real, "o-") + s4.plot(data_cst.real, "rx-") + s4.set_title("Symbols") + s4.set_xlabel("Samples") + s4.set_ylabel("Real Part of Signals") + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-digital/examples/example_fll.py b/gr-digital/examples/example_fll.py new file mode 100755 index 000000000..3b75b5a75 --- /dev/null +++ b/gr-digital/examples/example_fll.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +from gnuradio import gr, digital +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fll(gr.top_block): + def __init__(self, N, sps, rolloff, ntaps, bw, noise, foffset, toffset, poffset): + gr.top_block.__init__(self) + + rrc_taps = gr.firdes.root_raised_cosine( + sps, sps, 1.0, rolloff, ntaps) + + data = 2.0*scipy.random.randint(0, 2, N) - 1.0 + data = scipy.exp(1j*poffset) * data + + self.src = gr.vector_source_c(data.tolist(), False) + self.rrc = gr.interp_fir_filter_ccf(sps, rrc_taps) + self.chn = gr.channel_model(noise, foffset, toffset) + self.fll = digital.fll_band_edge_cc(sps, rolloff, ntaps, bw) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_fll = gr.vector_sink_c() + self.vsnk_frq = gr.vector_sink_f() + self.vsnk_phs = gr.vector_sink_f() + self.vsnk_err = gr.vector_sink_f() + + self.connect(self.src, self.rrc, self.chn, self.fll, self.vsnk_fll) + self.connect(self.rrc, self.vsnk_src) + self.connect((self.fll,1), self.vsnk_frq) + self.connect((self.fll,2), self.vsnk_phs) + self.connect((self.fll,3), self.vsnk_err) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=2000, + help="Set the number of samples to process [default=%default]") + parser.add_option("-S", "--sps", type="int", default=4, + help="Set the samples per symbol [default=%default]") + parser.add_option("-r", "--rolloff", type="eng_float", default=0.35, + help="Set the rolloff factor [default=%default]") + parser.add_option("-W", "--bandwidth", type="eng_float", default=2*scipy.pi/100.0, + help="Set the loop bandwidth [default=%default]") + parser.add_option("-n", "--ntaps", type="int", default=45, + help="Set the number of taps in the filters [default=%default]") + parser.add_option("", "--noise", type="eng_float", default=0.0, + help="Set the simulation noise voltage [default=%default]") + parser.add_option("-f", "--foffset", type="eng_float", default=0.2, + help="Set the simulation's normalized frequency offset (in Hz) [default=%default]") + parser.add_option("-t", "--toffset", type="eng_float", default=1.0, + help="Set the simulation's timing offset [default=%default]") + parser.add_option("-p", "--poffset", type="eng_float", default=0.0, + help="Set the simulation's phase offset [default=%default]") + (options, args) = parser.parse_args () + + # Adjust N for the interpolation by sps + options.nsamples = options.nsamples // options.sps + + # Set up the program-under-test + put = example_fll(options.nsamples, options.sps, options.rolloff, + options.ntaps, options.bandwidth, options.noise, + options.foffset, options.toffset, options.poffset) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_err = scipy.array(put.vsnk_err.data()) + + # Convert the FLL's LO frequency from rads/sec to Hz + data_frq = scipy.array(put.vsnk_frq.data()) / (2.0*scipy.pi) + + # adjust this to align with the data. There are 2 filters of + # ntaps long and the channel introduces another 4 sample delay. + data_fll = scipy.array(put.vsnk_fll.data()[2*options.ntaps-4:]) + + # Plot the FLL's LO frequency + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(2,2,1) + s1.plot(data_frq) + s1.set_title("FLL LO") + s1.set_xlabel("Samples") + s1.set_ylabel("Frequency (normalized Hz)") + + # Plot the FLL's error + s2 = f1.add_subplot(2,2,2) + s2.plot(data_err) + s2.set_title("FLL Error") + s2.set_xlabel("Samples") + s2.set_ylabel("FLL Loop error") + + # Plot the IQ symbols + s3 = f1.add_subplot(2,2,3) + s3.plot(data_src.real, data_src.imag, "o") + s3.plot(data_fll.real, data_fll.imag, "rx") + s3.set_title("IQ") + s3.set_xlabel("Real part") + s3.set_ylabel("Imag part") + + # Plot the symbols in time + s4 = f1.add_subplot(2,2,4) + s4.plot(data_src.real, "o-") + s4.plot(data_fll.real, "rx-") + s4.set_title("Symbols") + s4.set_xlabel("Samples") + s4.set_ylabel("Real Part of Signals") + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-digital/examples/example_timing.py b/gr-digital/examples/example_timing.py new file mode 100755 index 000000000..fd86acfb1 --- /dev/null +++ b/gr-digital/examples/example_timing.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +from gnuradio import gr, digital +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +from scipy import fftpack + +class example_timing(gr.top_block): + def __init__(self, N, sps, rolloff, ntaps, bw, noise, + foffset, toffset, poffset, mode=0): + gr.top_block.__init__(self) + + rrc_taps = gr.firdes.root_raised_cosine( + sps, sps, 1.0, rolloff, ntaps) + + gain = 2*scipy.pi/100.0 + nfilts = 32 + rrc_taps_rx = gr.firdes.root_raised_cosine( + nfilts, sps*nfilts, 1.0, rolloff, ntaps*nfilts) + + data = 2.0*scipy.random.randint(0, 2, N) - 1.0 + data = scipy.exp(1j*poffset) * data + + self.src = gr.vector_source_c(data.tolist(), False) + self.rrc = gr.interp_fir_filter_ccf(sps, rrc_taps) + self.chn = gr.channel_model(noise, foffset, toffset) + self.off = gr.fractional_interpolator_cc(0.20, 1.0) + + if mode == 0: + self.clk = gr.pfb_clock_sync_ccf(sps, gain, rrc_taps_rx, + nfilts, nfilts//2, 3.5) + self.taps = self.clk.get_taps() + self.dtaps = self.clk.get_diff_taps() + + self.vsnk_err = gr.vector_sink_f() + self.vsnk_rat = gr.vector_sink_f() + self.vsnk_phs = gr.vector_sink_f() + + self.connect((self.clk,1), self.vsnk_err) + self.connect((self.clk,2), self.vsnk_rat) + self.connect((self.clk,3), self.vsnk_phs) + + else: # mode == 1 + mu = 0.5 + gain_mu = 0.1 + gain_omega = 0.25*gain_mu*gain_mu + omega_rel_lim = 0.02 + self.clk = digital.clock_recovery_mm_cc(sps, gain_omega, + mu, gain_mu, + omega_rel_lim) + + self.vsnk_err = gr.vector_sink_f() + + self.connect((self.clk,1), self.vsnk_err) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_clk = gr.vector_sink_c() + + self.connect(self.src, self.rrc, self.chn, self.off, self.clk, self.vsnk_clk) + self.connect(self.off, self.vsnk_src) + + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=2000, + help="Set the number of samples to process [default=%default]") + parser.add_option("-S", "--sps", type="int", default=4, + help="Set the samples per symbol [default=%default]") + parser.add_option("-r", "--rolloff", type="eng_float", default=0.35, + help="Set the rolloff factor [default=%default]") + parser.add_option("-W", "--bandwidth", type="eng_float", default=2*scipy.pi/100.0, + help="Set the loop bandwidth [default=%default]") + parser.add_option("-n", "--ntaps", type="int", default=45, + help="Set the number of taps in the filters [default=%default]") + parser.add_option("", "--noise", type="eng_float", default=0.0, + help="Set the simulation noise voltage [default=%default]") + parser.add_option("-f", "--foffset", type="eng_float", default=0.0, + help="Set the simulation's normalized frequency offset (in Hz) [default=%default]") + parser.add_option("-t", "--toffset", type="eng_float", default=1.0, + help="Set the simulation's timing offset [default=%default]") + parser.add_option("-p", "--poffset", type="eng_float", default=0.0, + help="Set the simulation's phase offset [default=%default]") + parser.add_option("-M", "--mode", type="int", default=0, + help="Set the recovery mode (0: polyphase, 1: M&M) [default=%default]") + (options, args) = parser.parse_args () + + # Adjust N for the interpolation by sps + options.nsamples = options.nsamples // options.sps + + # Set up the program-under-test + put = example_timing(options.nsamples, options.sps, options.rolloff, + options.ntaps, options.bandwidth, options.noise, + options.foffset, options.toffset, options.poffset, + options.mode) + put.run() + + if options.mode == 0: + data_src = scipy.array(put.vsnk_src.data()[20:]) + data_clk = scipy.array(put.vsnk_clk.data()[20:]) + + data_err = scipy.array(put.vsnk_err.data()[20:]) + data_rat = scipy.array(put.vsnk_rat.data()[20:]) + data_phs = scipy.array(put.vsnk_phs.data()[20:]) + + f1 = pylab.figure(1, figsize=(12,10), facecolor='w') + + # Plot the IQ symbols + s1 = f1.add_subplot(2,2,1) + s1.plot(data_src.real, data_src.imag, "bo") + s1.plot(data_clk.real, data_clk.imag, "ro") + s1.set_title("IQ") + s1.set_xlabel("Real part") + s1.set_ylabel("Imag part") + s1.set_xlim([-2, 2]) + s1.set_ylim([-2, 2]) + + # Plot the symbols in time + s2 = f1.add_subplot(2,2,2) + s2.plot(data_src.real, "bo-") + s2.plot(data_clk.real, "ro") + s2.set_title("Symbols") + s2.set_xlabel("Samples") + s2.set_ylabel("Real Part of Signals") + + # Plot the clock recovery loop's error + s3 = f1.add_subplot(2,2,3) + s3.plot(data_err) + s3.set_title("Clock Recovery Loop Error") + s3.set_xlabel("Samples") + s3.set_ylabel("Error") + + # Plot the clock recovery loop's error + s4 = f1.add_subplot(2,2,4) + s4.plot(data_phs) + s4.set_title("Clock Recovery Loop Filter Phase") + s4.set_xlabel("Samples") + s4.set_ylabel("Filter Phase") + + + diff_taps = put.dtaps + ntaps = len(diff_taps[0]) + nfilts = len(diff_taps) + t = scipy.arange(0, ntaps*nfilts) + + f3 = pylab.figure(3, figsize=(12,10), facecolor='w') + s31 = f3.add_subplot(2,1,1) + s32 = f3.add_subplot(2,1,2) + s31.set_title("Differential Filters") + s32.set_title("FFT of Differential Filters") + + for i,d in enumerate(diff_taps): + D = 20.0*scipy.log10(abs(fftpack.fftshift(fftpack.fft(d, 10000)))) + s31.plot(t[i::nfilts].real, d, "-o") + s32.plot(D) + + # If testing the M&M clock recovery loop + else: + data_src = scipy.array(put.vsnk_src.data()[20:]) + data_clk = scipy.array(put.vsnk_clk.data()[20:]) + + data_err = scipy.array(put.vsnk_err.data()[20:]) + + f1 = pylab.figure(1, figsize=(12,10), facecolor='w') + + # Plot the IQ symbols + s1 = f1.add_subplot(2,2,1) + s1.plot(data_src.real, data_src.imag, "o") + s1.plot(data_clk.real, data_clk.imag, "ro") + s1.set_title("IQ") + s1.set_xlabel("Real part") + s1.set_ylabel("Imag part") + s1.set_xlim([-2, 2]) + s1.set_ylim([-2, 2]) + + # Plot the symbols in time + s2 = f1.add_subplot(2,2,2) + s2.plot(data_src.real, "o-") + s2.plot(data_clk.real, "ro") + s2.set_title("Symbols") + s2.set_xlabel("Samples") + s2.set_ylabel("Real Part of Signals") + + # Plot the clock recovery loop's error + s3 = f1.add_subplot(2,2,3) + s3.plot(data_err) + s3.set_title("Clock Recovery Loop Error") + s3.set_xlabel("Samples") + s3.set_ylabel("Error") + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-digital/examples/gen_whitener.py b/gr-digital/examples/gen_whitener.py new file mode 100755 index 000000000..9a81e4eaa --- /dev/null +++ b/gr-digital/examples/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.top_block): + + def __init__(self): + gr.top_block.__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: + tb = my_graph() + tb.run() + f = sys.stdout + i = 0 + for s in tb.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/gr-digital/examples/narrowband/benchmark_add_channel.py b/gr-digital/examples/narrowband/benchmark_add_channel.py new file mode 100755 index 000000000..841833a08 --- /dev/null +++ b/gr-digital/examples/narrowband/benchmark_add_channel.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, math, sys + +class my_top_block(gr.top_block): + def __init__(self, ifile, ofile, options): + gr.top_block.__init__(self) + + SNR = 10.0**(options.snr/10.0) + frequency_offset = options.frequency_offset + time_offset = options.time_offset + phase_offset = options.phase_offset*(math.pi/180.0) + + # calculate noise voltage from SNR + power_in_signal = abs(options.tx_amplitude)**2 + noise_power = power_in_signal/SNR + noise_voltage = math.sqrt(noise_power) + + self.src = gr.file_source(gr.sizeof_gr_complex, ifile) + #self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate) + self.channel = gr.channel_model(noise_voltage, frequency_offset, + time_offset, noise_seed=random.randint(0,100000)) + self.phase = gr.multiply_const_cc(complex(math.cos(phase_offset), + math.sin(phase_offset))) + self.snk = gr.file_sink(gr.sizeof_gr_complex, ofile) + + self.connect(self.src, self.channel, self.phase, self.snk) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + # Create Options Parser: + usage = "benchmack_add_channel.py [options] <input file> <output file>" + parser = OptionParser (usage=usage, option_class=eng_option, conflict_handler="resolve") + parser.add_option("-n", "--snr", type="eng_float", default=30, + help="set the SNR of the channel in dB [default=%default]") + parser.add_option("", "--seed", action="store_true", default=False, + help="use a random seed for AWGN noise [default=%default]") + parser.add_option("-f", "--frequency-offset", type="eng_float", default=0, + help="set frequency offset introduced by channel [default=%default]") + parser.add_option("-t", "--time-offset", type="eng_float", default=1.0, + help="set timing offset between Tx and Rx [default=%default]") + parser.add_option("-p", "--phase-offset", type="eng_float", default=0, + help="set phase offset (in degrees) between Tx and Rx [default=%default]") + parser.add_option("-m", "--use-multipath", action="store_true", default=False, + help="Use a multipath channel [default=%default]") + parser.add_option("", "--tx-amplitude", type="eng_float", + default=1.0, + help="tell the simulator the signal amplitude [default=%default]") + + (options, args) = parser.parse_args () + + if len(args) != 2: + parser.print_help(sys.stderr) + sys.exit(1) + + ifile = args[0] + ofile = args[1] + + # build the graph + tb = my_top_block(ifile, ofile, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/benchmark_rx.py b/gr-digital/examples/narrowband/benchmark_rx.py new file mode 100755 index 000000000..65aac3638 --- /dev/null +++ b/gr-digital/examples/narrowband/benchmark_rx.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# From gr-digital +from gnuradio import digital + +# from current dir +from receive_path import receive_path +from uhd_interface import uhd_receiver + +import struct +import sys + +#import os +#print os.getpid() +#raw_input('Attach and press enter: ') + +class my_top_block(gr.top_block): + def __init__(self, demodulator, rx_callback, options): + gr.top_block.__init__(self) + + if(options.rx_freq is not None): + self.source = uhd_receiver(options.address, options.bitrate, + options.samples_per_symbol, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self.source._sps + + elif(options.from_file is not None): + sys.stderr.write(("Reading samples from '%s'.\n\n" % (options.from_file))) + self.source = gr.file_source(gr.sizeof_gr_complex, options.from_file) + else: + sys.stderr.write("No source defined, pulling samples from null source.\n\n") + self.source = gr.null_source(gr.sizeof_gr_complex) + + # Set up receive path + # do this after for any adjustments to the options that may + # occur in the sinks (specifically the UHD sink) + self.rxpath = receive_path(demodulator, rx_callback, options) + + self.connect(self.source, self.rxpath) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +global n_rcvd, n_right + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def rx_callback(ok, payload): + global n_rcvd, n_right + (pktno,) = struct.unpack('!H', payload[0:2]) + n_rcvd += 1 + if ok: + n_right += 1 + + print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + demods = digital.modulation_utils.type_1_demods() + + # Create Options Parser: + parser = OptionParser (option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(demods.keys()),)) + parser.add_option("","--from-file", default=None, + help="input file of samples to demod") + + receive_path.add_options(parser, expert_grp) + uhd_receiver.add_options(parser) + + for mod in demods.values(): + mod.add_options(expert_grp) + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help(sys.stderr) + sys.exit(1) + + if options.from_file is None: + if options.rx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + + # build the graph + tb = my_top_block(demods[options.modulation], rx_callback, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/benchmark_tx.py b/gr-digital/examples/narrowband/benchmark_tx.py new file mode 100755 index 000000000..1fd881981 --- /dev/null +++ b/gr-digital/examples/narrowband/benchmark_tx.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# From gr-digital +from gnuradio import digital + +# from current dir +from transmit_path import transmit_path +from uhd_interface import uhd_transmitter + +import time, struct, sys + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + +class my_top_block(gr.top_block): + def __init__(self, modulator, options): + gr.top_block.__init__(self) + + if(options.tx_freq is not None): + self.sink = uhd_transmitter(options.address, options.bitrate, + options.samples_per_symbol, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self.sink._sps + + elif(options.to_file is not None): + sys.stderr.write(("Saving samples to '%s'.\n\n" % (options.to_file))) + self.sink = gr.file_sink(gr.sizeof_gr_complex, options.to_file) + else: + sys.stderr.write("No sink defined, dumping samples to null sink.\n\n") + self.sink = gr.null_sink(gr.sizeof_gr_complex) + + # do this after for any adjustments to the options that may + # occur in the sinks (specifically the UHD sink) + self.txpath = transmit_path(modulator, options) + + self.connect(self.txpath, self.sink) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return tb.txpath.send_pkt(payload, eof) + + mods = digital.modulation_utils.type_1_mods() + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(mods.keys()),)) + + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + parser.add_option("","--from-file", default=None, + help="use intput file for packet contents") + parser.add_option("","--to-file", default=None, + help="Output file for modulated samples") + + transmit_path.add_options(parser, expert_grp) + uhd_transmitter.add_options(parser) + + for mod in mods.values(): + mod.add_options(expert_grp) + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.from_file is not None: + source_file = open(options.from_file, 'r') + + # build the graph + tb = my_top_block(mods[options.modulation], options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + tb.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + pkt_size = int(options.size) + + while n < nbytes: + if options.from_file is None: + data = (pkt_size - 2) * chr(pktno & 0xff) + else: + data = source_file.read(pkt_size - 2) + if data == '': + break; + + payload = struct.pack('!H', pktno & 0xffff) + data + send_pkt(payload) + n += len(payload) + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/digital_bert_rx.py b/gr-digital/examples/narrowband/digital_bert_rx.py new file mode 100755 index 000000000..9878f55e1 --- /dev/null +++ b/gr-digital/examples/narrowband/digital_bert_rx.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# Copyright 2008,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, eng_notation +from optparse import OptionParser +from gnuradio.eng_option import eng_option +import gnuradio.gr.gr_threading as _threading +import sys, time, math + +from gnuradio import digital + +# from current dir +from uhd_interface import uhd_receiver + +n2s = eng_notation.num_to_str + +class status_thread(_threading.Thread): + def __init__(self, tb): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.tb = tb + self.done = False + self.start() + + def run(self): + while not self.done: + print "Freq. Offset: {0:5.0f} Hz Timing Offset: {1:10.1f} ppm Estimated SNR: {2:4.1f} dB BER: {3:g}".format( + tb.frequency_offset(), tb.timing_offset()*1e6, tb.snr(), tb.ber()) + try: + time.sleep(1.0) + except KeyboardInterrupt: + self.done = True + + + +class bert_receiver(gr.hier_block2): + def __init__(self, bitrate, + constellation, samples_per_symbol, + differential, excess_bw, gray_coded, + freq_bw, timing_bw, phase_bw, + verbose, log): + + gr.hier_block2.__init__(self, "bert_receive", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self._bitrate = bitrate + + self._demod = digital.generic_demod(constellation, samples_per_symbol, + differential, excess_bw, gray_coded, + freq_bw, timing_bw, phase_bw, + verbose, log) + + self._symbol_rate = self._bitrate * self._demod.bits_per_symbol() + self._sample_rate = self._symbol_rate * samples_per_symbol + + # Add an SNR probe on the demodulated constellation + self._snr_probe = gr.probe_mpsk_snr_c(10.0/self._symbol_rate) + self.connect(self._demod.time_recov, self._snr_probe) + + # Descramble BERT sequence. A channel error will create 3 incorrect bits + self._descrambler = gr.descrambler_bb(0x8A, 0x7F, 7) # CCSDS 7-bit descrambler + + # Measure BER by the density of 0s in the stream + self._ber = gr.probe_density_b(1.0/self._symbol_rate) + + self.connect(self, self._demod, self._descrambler, self._ber) + + def frequency_offset(self): + return self._demod.freq_recov.get_frequency()*self._sample_rate/(2*math.pi) + + def timing_offset(self): + return self._demod.time_recov.get_clock_rate() + + def snr(self): + return self._snr_probe.snr() + + def ber(self): + return (1.0-self._ber.density())/3.0 + + + +class rx_psk_block(gr.top_block): + def __init__(self, demod, options): + + gr.top_block.__init__(self, "rx_mpsk") + + self._demodulator_class = demod + + # Get demod_kwargs + demod_kwargs = self._demodulator_class.extract_kwargs_from_options(options) + + # demodulator + self._demodulator = self._demodulator_class(**demod_kwargs) + + if(options.rx_freq is not None): + self._source = uhd_receiver(options.address, options.bitrate, + options.samples_per_symbol, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self._source._sps + + elif(options.from_file is not None): + self._source = gr.file_source(gr.sizeof_gr_complex, options.from_file) + else: + self._source = gr.null_source(gr.sizeof_gr_complex) + + # Create the BERT receiver + self._receiver = bert_receiver(options.bitrate, + self._demodulator._constellation, + options.samples_per_symbol, + options.differential, + options.excess_bw, + gray_coded=True, + freq_bw=options.freq_bw, + timing_bw=options.timing_bw, + phase_bw=options.phase_bw, + verbose=options.verbose, + log=options.log) + + self.connect(self._source, self._receiver) + + def snr(self): + return self._receiver.snr() + + def mag(self): + return self._receiver.signal_mean() + + def var(self): + return self._receiver.noise_variance() + + def ber(self): + return self._receiver.ber() + + def frequency_offset(self): + return self._receiver.frequency_offset() + + def timing_offset(self): + return self._receiver.timing_offset() + + +def get_options(demods): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("","--from-file", default=None, + help="input file of samples to demod") + parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(demods.keys()),)) + parser.add_option("-r", "--bitrate", type="eng_float", default=250e3, + help="Select modulation bit rate (default=%default)") + parser.add_option("-S", "--samples-per-symbol", type="float", default=2, + help="set samples/symbol [default=%default]") + if not parser.has_option("--verbose"): + parser.add_option("-v", "--verbose", action="store_true", default=False) + if not parser.has_option("--log"): + parser.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to files (CAUTION: lots of data)") + + uhd_receiver.add_options(parser) + + demods = digital.modulation_utils.type_1_demods() + for mod in demods.values(): + mod.add_options(parser) + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + return (options, args) + + +if __name__ == "__main__": + demods = digital.modulation_utils.type_1_demods() + + (options, args) = get_options(demods) + + demod = demods[options.modulation] + tb = rx_psk_block(demod, options) + + print "\n*** SNR estimator is inaccurate below about 7dB" + print "*** BER estimator is inaccurate above about 10%\n" + updater = status_thread(tb) + + try: + tb.run() + except KeyboardInterrupt: + updater.done = True + updater = None diff --git a/gr-digital/examples/narrowband/digital_bert_tx.py b/gr-digital/examples/narrowband/digital_bert_tx.py new file mode 100755 index 000000000..96cb338fe --- /dev/null +++ b/gr-digital/examples/narrowband/digital_bert_tx.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# +# Copyright 2008,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +from gnuradio import digital + +# from current dir +from uhd_interface import uhd_transmitter + +n2s = eng_notation.num_to_str + +class bert_transmit(gr.hier_block2): + def __init__(self, constellation, samples_per_symbol, + differential, excess_bw, gray_coded, + verbose, log): + + gr.hier_block2.__init__(self, "bert_transmit", + gr.io_signature(0, 0, 0), # Output signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Input signature + + # Create BERT data bit stream + self._bits = gr.vector_source_b([1,], True) # Infinite stream of ones + self._scrambler = gr.scrambler_bb(0x8A, 0x7F, 7) # CCSDS 7-bit scrambler + + self._mod = digital.generic_mod(constellation, samples_per_symbol, + differential, excess_bw, gray_coded, + verbose, log) + + self._pack = gr.unpacked_to_packed_bb(self._mod.bits_per_symbol(), gr.GR_MSB_FIRST) + + self.connect(self._bits, self._scrambler, self._pack, self._mod, self) + + +class tx_psk_block(gr.top_block): + def __init__(self, mod, options): + gr.top_block.__init__(self, "tx_mpsk") + + self._modulator_class = mod + + # Get mod_kwargs + mod_kwargs = self._modulator_class.extract_kwargs_from_options(options) + + # transmitter + self._modulator = self._modulator_class(**mod_kwargs) + + if(options.tx_freq is not None): + self._sink = uhd_transmitter(options.address, options.bitrate, + options.samples_per_symbol, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self._sink._sps + + elif(options.to_file is not None): + self._sink = gr.file_sink(gr.sizeof_gr_complex, options.to_file) + else: + self._sink = gr.null_sink(gr.sizeof_gr_complex) + + + self._transmitter = bert_transmit(self._modulator._constellation, + options.samples_per_symbol, + options.differential, + options.excess_bw, + gray_coded=True, + verbose=options.verbose, + log=options.log) + + self.connect(self._transmitter, self._sink) + + +def get_options(mods): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(), + default='psk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(mods.keys()),)) + parser.add_option("", "--amplitude", type="eng_float", default=0.2, + help="set Tx amplitude (0-1) (default=%default)") + parser.add_option("-r", "--bitrate", type="eng_float", default=250e3, + help="Select modulation bit rate (default=%default)") + parser.add_option("-S", "--samples-per-symbol", type="float", default=2, + help="set samples/symbol [default=%default]") + parser.add_option("","--to-file", default=None, + help="Output file for modulated samples") + if not parser.has_option("--verbose"): + parser.add_option("-v", "--verbose", action="store_true", default=False) + if not parser.has_option("--log"): + parser.add_option("", "--log", action="store_true", default=False) + + uhd_transmitter.add_options(parser) + + for mod in mods.values(): + mod.add_options(parser) + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + return (options, args) + +if __name__ == "__main__": + mods = digital.modulation_utils.type_1_mods() + + (options, args) = get_options(mods) + + mod = mods[options.modulation] + tb = tx_psk_block(mod, options) + + try: + tb.run() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/receive_path.py b/gr-digital/examples/narrowband/receive_path.py new file mode 100644 index 000000000..1c5e58963 --- /dev/null +++ b/gr-digital/examples/narrowband/receive_path.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block2): + def __init__(self, demod_class, rx_callback, options): + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(0, 0, 0)) + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._bitrate = options.bitrate # desired bit rate + + self._rx_callback = rx_callback # this callback is fired when a packet arrives + self._demod_class = demod_class # the demodulator_class we're using + + # Get demod_kwargs + demod_kwargs = self._demod_class.extract_kwargs_from_options(options) + + # Build the demodulator + self.demodulator = self._demod_class(**demod_kwargs) + + # Design filter to get actual channel we want + sw_decim = 1 + chan_coeffs = gr.firdes.low_pass (1.0, # gain + sw_decim * self.samples_per_symbol(), # sampling rate + 1.0, # midpoint of trans. band + 0.5, # width of trans. band + gr.firdes.WIN_HANN) # filter type + self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) + + # receiver + self.packet_receiver = \ + digital.demod_pkts(self.demodulator, + access_code=None, + callback=self._rx_callback, + threshold=-1) + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + # connect block input to channel filter + self.connect(self, self.channel_filter) + + # connect the channel input filter to the carrier power detector + self.connect(self.channel_filter, self.probe) + + # connect channel filter to the packet receiver + self.connect(self.channel_filter, self.packet_receiver) + + def bitrate(self): + return self._bitrate + + def samples_per_symbol(self): + return self.demodulator._samples_per_symbol + + def differential(self): + return self.demodulator._differential + + def carrier_sensed(self): + """ + Return True if we think carrier is present. + """ + #return self.probe.level() > X + return self.probe.unmuted() + + def carrier_threshold(self): + """ + Return current setting in dB. + """ + return self.probe.threshold() + + def set_carrier_threshold(self, threshold_in_db): + """ + Set carrier threshold. + + @param threshold_in_db: set detection threshold + @type threshold_in_db: float (dB) + """ + self.probe.set_threshold(threshold_in_db) + + + def add_options(normal, expert): + """ + Adds receiver-specific options to the Options Parser + """ + if not normal.has_option("--bitrate"): + normal.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate [default=%default].") + normal.add_option("-v", "--verbose", action="store_true", default=False) + expert.add_option("-S", "--samples-per-symbol", type="float", default=2, + help="set samples/symbol [default=%default]") + expert.add_option("", "--log", action="store_true", default=False, + help="Log all parts of flow graph to files (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + + def _print_verbage(self): + """ + Prints information about the receive path + """ + print "\nReceive Path:" + print "modulation: %s" % (self._demod_class.__name__) + print "bitrate: %sb/s" % (eng_notation.num_to_str(self._bitrate)) + print "samples/symbol: %.4f" % (self.samples_per_symbol()) + print "Differential: %s" % (self.differential()) diff --git a/gr-digital/examples/narrowband/rx_voice.py b/gr-digital/examples/narrowband/rx_voice.py new file mode 100755 index 000000000..42d7b893b --- /dev/null +++ b/gr-digital/examples/narrowband/rx_voice.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2009,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2, audio, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio import digital +from gnuradio import vocoder + +import random +import struct +import sys + +# from current dir +from receive_path import receive_path +from uhd_interface import uhd_receiver + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class audio_tx(gr.hier_block2): + def __init__(self, audio_output_dev): + gr.hier_block2.__init__(self, "audio_tx", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self.sample_rate = sample_rate = 8000 + self.packet_src = gr.message_source(33) + voice_decoder = vocoder.gsm_fr_decode_ps() + s2f = gr.short_to_float () + sink_scale = gr.multiply_const_ff(1.0/32767.) + audio_sink = audio.sink(sample_rate, audio_output_dev) + self.connect(self.packet_src, voice_decoder, s2f, sink_scale, audio_sink) + + def msgq(self): + return self.packet_src.msgq() + + +class my_top_block(gr.top_block): + def __init__(self, demod_class, rx_callback, options): + gr.top_block.__init__(self) + self.rxpath = receive_path(demod_class, rx_callback, options) + self.audio_tx = audio_tx(options.audio_output) + + if(options.rx_freq is not None): + self.source = uhd_receiver(options.address, options.bitrate, + options.samples_per_symbol, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self.source._sps + + audio_rate = self.audio_tx.sample_rate + usrp_rate = self.source.get_sample_rate() + rrate = audio_rate / usrp_rate + self.resampler = blks2.pfb_arb_resampler_ccf(rrate) + + self.connect(self.source, self.resampler, self.rxpath) + + elif(options.from_file is not None): + self.thr = gr.throttle(gr.sizeof_gr_complex, options.bitrate) + self.source = gr.file_source(gr.sizeof_gr_complex, options.from_file) + self.connect(self.source, self.thr, self.rxpath) + + else: + self.thr = gr.throttle(gr.sizeof_gr_complex, 1e6) + self.source = gr.null_source(gr.sizeof_gr_complex) + self.connect(self.source, self.thr, self.rxpath) + + self.connect(self.audio_tx) + +# ///////////////////////////////////////////////////////////////////////////// +# 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 + + tb.audio_tx.msgq().insert_tail(gr.message_from_string(payload)) + + print "ok = %r n_rcvd = %4d n_right = %4d" % ( + ok, n_rcvd, n_right) + + demods = digital.modulation_utils.type_1_demods() + + # Create Options Parser: + parser = OptionParser (option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), + default='gmsk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(demods.keys()),)) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("","--from-file", default=None, + help="input file of samples to demod") + receive_path.add_options(parser, expert_grp) + uhd_receiver.add_options(parser) + + for mod in demods.values(): + mod.add_options(expert_grp) + + parser.set_defaults(bitrate=50e3) # override default bitrate default + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help(sys.stderr) + sys.exit(1) + + if options.from_file is None: + if options.rx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + + # build the graph + tb = my_top_block(demods[options.modulation], rx_callback, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.run() + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/transmit_path.py b/gr-digital/examples/narrowband/transmit_path.py new file mode 100644 index 000000000..4d6162ed6 --- /dev/null +++ b/gr-digital/examples/narrowband/transmit_path.py @@ -0,0 +1,126 @@ +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block2): + def __init__(self, modulator_class, options): + ''' + See below for what options should hold + ''' + gr.hier_block2.__init__(self, "transmit_path", + gr.io_signature(0,0,0), + gr.io_signature(1,1,gr.sizeof_gr_complex)) + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose + self._tx_amplitude = options.tx_amplitude # digital amplitude sent to USRP + self._bitrate = options.bitrate # desired bit rate + self._modulator_class = modulator_class # the modulator_class we are using + + # Get mod_kwargs + mod_kwargs = self._modulator_class.extract_kwargs_from_options(options) + + # transmitter + self.modulator = self._modulator_class(**mod_kwargs) + + self.packet_transmitter = \ + digital.mod_pkts(self.modulator, + access_code=None, + msgq_limit=4, + pad_for_usrp=True) + + self.amp = gr.multiply_const_cc(1) + self.set_tx_amplitude(self._tx_amplitude) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + # Connect components in the flowgraph + self.connect(self.packet_transmitter, self.amp, self) + + def set_tx_amplitude(self, ampl): + """ + Sets the transmit amplitude sent to the USRP in volts + @param: ampl 0 <= ampl < 1. + """ + self._tx_amplitude = max(0.0, min(ampl, 1)) + self.amp.set_k(self._tx_amplitude) + + def send_pkt(self, payload='', eof=False): + """ + Calls the transmitter method to send a packet + """ + return self.packet_transmitter.send_pkt(payload, eof) + + def bitrate(self): + return self._bitrate + + def samples_per_symbol(self): + return self.modulator._samples_per_symbol + + def differential(self): + return self.modulator._differential + + def add_options(normal, expert): + """ + Adds transmitter-specific options to the Options Parser + """ + if not normal.has_option('--bitrate'): + normal.add_option("-r", "--bitrate", type="eng_float", + default=100e3, + help="specify bitrate [default=%default].") + normal.add_option("", "--tx-amplitude", type="eng_float", + default=0.250, metavar="AMPL", + help="set transmitter digital amplitude: 0 <= AMPL < 1 [default=%default]") + normal.add_option("-v", "--verbose", action="store_true", + default=False) + + expert.add_option("-S", "--samples-per-symbol", type="float", + default=2, + help="set samples/symbol [default=%default]") + expert.add_option("", "--log", action="store_true", + default=False, + help="Log all parts of flow graph to file (CAUTION: lots of data)") + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the transmit path + """ + print "Tx amplitude %s" % (self._tx_amplitude) + print "modulation: %s" % (self._modulator_class.__name__) + print "bitrate: %sb/s" % (eng_notation.num_to_str(self._bitrate)) + print "samples/symbol: %.4f" % (self.samples_per_symbol()) + print "Differential: %s" % (self.differential()) diff --git a/gr-digital/examples/narrowband/tunnel.py b/gr-digital/examples/narrowband/tunnel.py new file mode 100755 index 000000000..7f40bb1c3 --- /dev/null +++ b/gr-digital/examples/narrowband/tunnel.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2009,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + + +# //////////////////////////////////////////////////////////////////// +# +# 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, digital +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# from current dir +from receive_path import receive_path +from transmit_path import transmit_path +from uhd_interface import uhd_transmitter +from uhd_interface import uhd_receiver + +import os, sys +import random, time, struct + +#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_top_block(gr.top_block): + + def __init__(self, mod_class, demod_class, + rx_callback, options): + + gr.top_block.__init__(self) + + self.source = uhd_receiver(options.address, options.bitrate, + options.samples_per_symbol, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + + self.sink = uhd_transmitter(options.address, options.bitrate, + options.samples_per_symbol, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + + options.samples_per_symbol = self.source._sps + + self.txpath = transmit_path(mod_class, options) + self.rxpath = receive_path(demod_class, rx_callback, options) + self.connect(self.txpath, self.sink) + self.connect(self.source, self.rxpath) + + 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() + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + """ + + self.sink.set_freq(target_freq) + self.source.set_freq(target_freq) + + +# //////////////////////////////////////////////////////////////////// +# 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.tb = None # top block (access to PHY) + + def set_top_block(self, tb): + self.tb = tb + + 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.tb.send_pkt(eof=True) + break + + if self.verbose: + print "Tx: len(payload) = %4d" % (len(payload),) + + delay = min_delay + while self.tb.carrier_sensed(): + sys.stderr.write('B') + time.sleep(delay) + if delay < 0.050: + delay = delay * 2 # exponential back-off + + self.tb.send_pkt(payload) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + mods = digital.modulation_utils.type_1_mods() + demods = digital.modulation_utils.type_1_demods() + + parser = OptionParser (option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(), + default='gmsk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(mods.keys()),)) + + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("-v","--verbose", action="store_true", default=False) + expert_grp.add_option("-c", "--carrier-threshold", type="eng_float", default=30, + help="set carrier detect threshold (dB) [default=%default]") + expert_grp.add_option("","--tun-device-filename", default="/dev/net/tun", + help="path to tun device file [default=%default]") + + transmit_path.add_options(parser, expert_grp) + receive_path.add_options(parser, expert_grp) + uhd_receiver.add_options(parser) + uhd_transmitter.add_options(parser) + + for mod in mods.values(): + mod.add_options(expert_grp) + + for demod in demods.values(): + demod.add_options(expert_grp) + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help(sys.stderr) + sys.exit(1) + + # 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" + + # instantiate the MAC + mac = cs_mac(tun_fd, verbose=True) + + # build the graph (PHY) + tb = my_top_block(mods[options.modulation], + demods[options.modulation], + mac.phy_rx_callback, + options) + + mac.set_top_block(tb) # give the MAC a handle for the PHY + + if tb.txpath.bitrate() != tb.rxpath.bitrate(): + print "WARNING: Transmit bitrate = %sb/sec, Receive bitrate = %sb/sec" % ( + eng_notation.num_to_str(tb.txpath.bitrate()), + eng_notation.num_to_str(tb.rxpath.bitrate())) + + print "modulation: %s" % (options.modulation,) + print "freq: %s" % (eng_notation.num_to_str(options.tx_freq)) + print "bitrate: %sb/sec" % (eng_notation.num_to_str(tb.txpath.bitrate()),) + print "samples/symbol: %3d" % (tb.txpath.samples_per_symbol(),) + + tb.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 192.168.200.1" % (tun_ifname,) + print + print "Be sure to use a different address in the same subnet for each machine." + print + + + tb.start() # Start executing the flow graph (runs in separate threads) + + mac.main_loop() # don't expect this to return... + + tb.stop() # but if it does, tell flow graph to stop. + tb.wait() # wait for it to finish + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/tx_voice.py b/gr-digital/examples/narrowband/tx_voice.py new file mode 100755 index 000000000..3d767a077 --- /dev/null +++ b/gr-digital/examples/narrowband/tx_voice.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2009,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2, audio, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio import digital +from gnuradio import vocoder + +import random +import time +import struct +import sys + +# from current dir +from transmit_path import transmit_path +from uhd_interface import uhd_transmitter + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class audio_rx(gr.hier_block2): + def __init__(self, audio_input_dev): + gr.hier_block2.__init__(self, "audio_rx", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + self.sample_rate = sample_rate = 8000 + src = audio.source(sample_rate, audio_input_dev) + src_scale = gr.multiply_const_ff(32767) + f2s = gr.float_to_short() + voice_coder = vocoder.gsm_fr_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) + + def get_encoded_voice_packet(self): + return self.packets_from_encoder.delete_head() + + +class my_top_block(gr.top_block): + + def __init__(self, modulator_class, options): + gr.top_block.__init__(self) + self.txpath = transmit_path(modulator_class, options) + self.audio_rx = audio_rx(options.audio_input) + + if(options.tx_freq is not None): + self.sink = uhd_transmitter(options.address, options.bitrate, + options.samples_per_symbol, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + options.samples_per_symbol = self.sink._sps + audio_rate = self.audio_rx.sample_rate + usrp_rate = self.sink.get_sample_rate() + rrate = usrp_rate / audio_rate + + elif(options.to_file is not None): + self.sink = gr.file_sink(gr.sizeof_gr_complex, options.to_file) + rrate = 1 + else: + self.sink = gr.null_sink(gr.sizeof_gr_complex) + rrate = 1 + + self.resampler = blks2.pfb_arb_resampler_ccf(rrate) + + self.connect(self.audio_rx) + self.connect(self.txpath, self.resampler, self.sink) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return tb.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + mods = digital.modulation_utils.type_1_mods() + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(), + default='gmsk', + help="Select modulation from: %s [default=%%default]" + % (', '.join(mods.keys()),)) + parser.add_option("-M", "--megabytes", type="eng_float", default=0, + help="set megabytes to transmit [default=inf]") + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("","--to-file", default=None, + help="Output file for modulated samples") + + transmit_path.add_options(parser, expert_grp) + uhd_transmitter.add_options(parser) + + for mod in mods.values(): + mod.add_options(expert_grp) + + parser.set_defaults(bitrate=50e3) # override default bitrate default + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.to_file is None: + if options.tx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + # build the graph + tb = my_top_block(mods[options.modulation], options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + + tb.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while nbytes == 0 or n < nbytes: + packet = tb.audio_rx.get_encoded_voice_packet() + s = packet.to_string() + send_pkt(s) + n += len(s) + sys.stderr.write('.') + pktno += 1 + + send_pkt(eof=True) + tb.wait() # wait for it to finish + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/narrowband/uhd_interface.py b/gr-digital/examples/narrowband/uhd_interface.py new file mode 100644 index 000000000..8420f3eec --- /dev/null +++ b/gr-digital/examples/narrowband/uhd_interface.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import sys + +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") + +class uhd_interface: + def __init__(self, istx, address, bitrate, sps, freq=None, + gain=None, antenna=None): + + if(istx): + self.u = uhd.usrp_sink(device_addr=address, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=1) + else: + self.u = uhd.usrp_source(device_addr=address, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=1) + + self._addr = address + self._ant = antenna + self._gain = self.set_gain(gain) + self._freq = self.set_freq(freq) + + self._rate, self._sps = self.set_sample_rate(bitrate, sps) + + if(antenna): + self.u.set_antenna(antenna, 0) + + def set_sample_rate(self, bitrate, req_sps): + start_sps = req_sps + while(True): + asked_samp_rate = bitrate * req_sps + self.u.set_samp_rate(asked_samp_rate) + actual_samp_rate = self.u.get_samp_rate() + + sps = actual_samp_rate/bitrate + if(sps < 2): + req_sps +=1 + else: + actual_sps = sps + break + + if(sps != req_sps): + print "\nBit Rate: %f" % (bitrate) + print "Requested sps: %f" % (start_sps) + print "Given sample rate: %f" % (actual_samp_rate) + print "Actual sps for rate: %f" % (actual_sps) + + if(actual_samp_rate != asked_samp_rate): + print "\nRequested sample rate: %f" % (asked_samp_rate) + print "Actual sample rate: %f" % (actual_samp_rate) + + return (actual_samp_rate, actual_sps) + + def get_sample_rate(self): + return self.u.get_samp_rate() + + def set_gain(self, gain=None): + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + gain = float(g.start()+g.stop())/2 + print "\nNo gain specified." + print "Setting gain to %f (from [%f, %f])" % \ + (gain, g.start(), g.stop()) + + self.u.set_gain(gain, 0) + return gain + + def set_freq(self, freq=None): + if(freq is None): + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + sys.exit(1) + + r = self.u.set_center_freq(freq, 0) + if r: + return freq + else: + frange = self.u.get_freq_range() + sys.stderr.write(("\nRequested frequency (%f) out or range [%f, %f]\n") % \ + (freq, frange.start(), frange.stop())) + sys.exit(1) + +#-------------------------------------------------------------------# +# TRANSMITTER +#-------------------------------------------------------------------# + +class uhd_transmitter(uhd_interface, gr.hier_block2): + def __init__(self, address, bitrate, sps, freq=None, gain=None, + antenna=None, verbose=False): + gr.hier_block2.__init__(self, "uhd_transmitter", + gr.io_signature(1,1,gr.sizeof_gr_complex), + gr.io_signature(0,0,0)) + + # Set up the UHD interface as a transmitter + uhd_interface.__init__(self, True, address, bitrate, sps, + freq, gain, antenna) + + self.connect(self, self.u) + + if(verbose): + self._print_verbage() + + def add_options(parser): + add_freq_option(parser) + parser.add_option("-a", "--address", type="string", default="addr=192.168.10.2", + help="Address of UHD device, [default=%default]") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("", "--tx-freq", type="eng_float", default=None, + help="set transmit frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("", "--tx-gain", type="eng_float", default=None, + help="set transmit gain in dB (default is midpoint)") + parser.add_option("-v", "--verbose", action="store_true", default=False) + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the UHD transmitter + """ + print "\nUHD Transmitter:" + print "Address: %s" % (self._addr) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + + + +#-------------------------------------------------------------------# +# RECEIVER +#-------------------------------------------------------------------# + + +class uhd_receiver(uhd_interface, gr.hier_block2): + def __init__(self, address, bitrate, sps, freq=None, gain=None, + antenna=None, verbose=False): + gr.hier_block2.__init__(self, "uhd_receiver", + gr.io_signature(0,0,0), + gr.io_signature(1,1,gr.sizeof_gr_complex)) + + # Set up the UHD interface as a receiver + uhd_interface.__init__(self, False, address, bitrate, sps, + freq, gain, antenna) + + self.connect(self.u, self) + + if(verbose): + self._print_verbage() + + def add_options(parser): + add_freq_option(parser) + parser.add_option("-a", "--address", type="string", default="addr=192.168.10.2", + help="Address of UHD device, [default=%default]") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("", "--rx-freq", type="eng_float", default=None, + help="set receive frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("", "--rx-gain", type="eng_float", default=None, + help="set receive gain in dB (default is midpoint)") + if not parser.has_option("--verbose"): + parser.add_option("-v", "--verbose", action="store_true", default=False) + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the UHD transmitter + """ + print "\nUHD Receiver:" + print "Address: %s" % (self._addr) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + diff --git a/gr-digital/examples/ofdm/benchmark_add_channel.py b/gr-digital/examples/ofdm/benchmark_add_channel.py new file mode 100755 index 000000000..01776d209 --- /dev/null +++ b/gr-digital/examples/ofdm/benchmark_add_channel.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, math, sys + +class my_top_block(gr.top_block): + def __init__(self, ifile, ofile, options): + gr.top_block.__init__(self) + + SNR = 10.0**(options.snr/10.0) + time_offset = options.time_offset + phase_offset = options.phase_offset*(math.pi/180.0) + + # calculate noise voltage from SNR + power_in_signal = abs(options.tx_amplitude)**2 + noise_power = power_in_signal/SNR + noise_voltage = math.sqrt(noise_power) + print "Noise voltage: ", noise_voltage + + frequency_offset = options.frequency_offset / options.fft_length + + self.src = gr.file_source(gr.sizeof_gr_complex, ifile) + #self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate) + self.channel = gr.channel_model(noise_voltage, frequency_offset, + time_offset, noise_seed=random.randint(0,100000)) + self.phase = gr.multiply_const_cc(complex(math.cos(phase_offset), + math.sin(phase_offset))) + self.snk = gr.file_sink(gr.sizeof_gr_complex, ofile) + + self.connect(self.src, self.channel, self.phase, self.snk) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + # Create Options Parser: + usage = "benchmack_add_channel.py [options] <input file> <output file>" + parser = OptionParser (usage=usage, option_class=eng_option, conflict_handler="resolve") + parser.add_option("-n", "--snr", type="eng_float", default=30, + help="set the SNR of the channel in dB [default=%default]") + parser.add_option("", "--seed", action="store_true", default=False, + help="use a random seed for AWGN noise [default=%default]") + parser.add_option("-f", "--frequency-offset", type="eng_float", default=0, + help="set frequency offset introduced by channel [default=%default]") + parser.add_option("-t", "--time-offset", type="eng_float", default=1.0, + help="set timing offset between Tx and Rx [default=%default]") + parser.add_option("-p", "--phase-offset", type="eng_float", default=0, + help="set phase offset (in degrees) between Tx and Rx [default=%default]") + parser.add_option("-m", "--use-multipath", action="store_true", default=False, + help="Use a multipath channel [default=%default]") + parser.add_option("", "--fft-length", type="intx", default=None, + help="set the number of FFT bins [default=%default]") + parser.add_option("", "--tx-amplitude", type="eng_float", + default=1.0, + help="tell the simulator the signal amplitude [default=%default]") + + (options, args) = parser.parse_args () + + if len(args) != 2: + parser.print_help(sys.stderr) + sys.exit(1) + + if options.fft_length is None: + sys.stderr.write("Please enter the FFT length of the OFDM signal.\n") + sys.exit(1) + + ifile = args[0] + ofile = args[1] + + # build the graph + tb = my_top_block(ifile, ofile, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/ofdm/benchmark_rx.py b/gr-digital/examples/ofdm/benchmark_rx.py new file mode 100755 index 000000000..f2ed3b8ff --- /dev/null +++ b/gr-digital/examples/ofdm/benchmark_rx.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2 +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio import digital + +# from current dir +from receive_path import receive_path +from uhd_interface import uhd_receiver + +import struct, sys + +class my_top_block(gr.top_block): + def __init__(self, callback, options): + gr.top_block.__init__(self) + + if(options.rx_freq is not None): + self.source = uhd_receiver(options.address, + options.bandwidth, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + elif(options.from_file is not None): + self.source = gr.file_source(gr.sizeof_gr_complex, options.from_file) + else: + self.source = gr.null_source(gr.sizeof_gr_complex) + + # Set up receive path + # do this after for any adjustments to the options that may + # occur in the sinks (specifically the UHD sink) + self.rxpath = receive_path(callback, options) + + self.connect(self.source, self.rxpath) + + +# ///////////////////////////////////////////////////////////////////////////// +# 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) + + if 0: + printlst = list() + for x in payload[2:]: + t = hex(ord(x)).replace('0x', '') + if(len(t) == 1): + t = '0' + t + printlst.append(t) + printable = ''.join(printlst) + + print printable + print "\n" + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinuous") + parser.add_option("","--from-file", default=None, + help="input file of samples to demod") + + receive_path.add_options(parser, expert_grp) + uhd_receiver.add_options(parser) + digital.ofdm_demod.add_options(parser, expert_grp) + + (options, args) = parser.parse_args () + + if options.from_file is None: + if options.rx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + # build the graph + tb = my_top_block(rx_callback, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/ofdm/benchmark_tx.py b/gr-digital/examples/ofdm/benchmark_tx.py new file mode 100755 index 000000000..ad3609b52 --- /dev/null +++ b/gr-digital/examples/ofdm/benchmark_tx.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import time, struct, sys + +from gnuradio import digital + +# from current dir +from transmit_path import transmit_path +from uhd_interface import uhd_transmitter + +class my_top_block(gr.top_block): + def __init__(self, options): + gr.top_block.__init__(self) + + if(options.tx_freq is not None): + self.sink = uhd_transmitter(options.address, + options.bandwidth, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + elif(options.to_file is not None): + self.sink = gr.file_sink(gr.sizeof_gr_complex, options.to_file) + else: + self.sink = gr.null_sink(gr.sizeof_gr_complex) + + # do this after for any adjustments to the options that may + # occur in the sinks (specifically the UHD sink) + self.txpath = transmit_path(options) + + self.connect(self.txpath, self.sink) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return tb.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=400, + help="set packet size [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinuous mode") + parser.add_option("","--from-file", default=None, + help="use intput file for packet contents") + parser.add_option("","--to-file", default=None, + help="Output file for modulated samples") + + transmit_path.add_options(parser, expert_grp) + digital.ofdm_mod.add_options(parser, expert_grp) + uhd_transmitter.add_options(parser) + + (options, args) = parser.parse_args () + + # build the graph + tb = my_top_block(options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + tb.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + pkt_size = int(options.size) + + while n < nbytes: + if options.from_file is None: + data = (pkt_size - 2) * chr(pktno & 0xff) + else: + data = source_file.read(pkt_size - 2) + if data == '': + break; + + payload = struct.pack('!H', pktno & 0xffff) + data + send_pkt(payload) + n += len(payload) + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/ofdm/gr_plot_ofdm.py b/gr-digital/examples/ofdm/gr_plot_ofdm.py new file mode 100755 index 000000000..b24855148 --- /dev/null +++ b/gr-digital/examples/ofdm/gr_plot_ofdm.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# +# 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math, struct, sys +from optparse import OptionParser +from math import log10 + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + from pylab import * + from matplotlib.font_manager import fontManager, FontProperties +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_constellation: + def __init__(self, options): + derot_file = "ofdm_frame_sink_c.dat" + acq_file = "ofdm_frame_acq_c.dat" + fft_file = "ofdm_receiver-fft_out_c.dat" + + self.h_derot_file = open(derot_file, "r") + self.h_acq_file = open(acq_file, "r") + self.h_fft_file = open(fft_file, "r") + + self.occ_tones = options.occ_tones + self.fft_size = options.fft_size + self.symbol = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(14, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_sym = figtext(0.05, 0.95, ("Symbol: %s" % self.symbol), weight="heavy", size=self.text_size) + + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_eq.get_xlim() + + self.manager = get_current_fig_manager() + #connect('draw_event', self.zoom) + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_sym.set_text("Symbol: %d" % (self.symbol)) + + derot_data = scipy.fromfile(self.h_derot_file, dtype=scipy.complex64, count=self.occ_tones) + acq_data = scipy.fromfile(self.h_acq_file, dtype=scipy.complex64, count=self.occ_tones) + fft_data = scipy.fromfile(self.h_fft_file, dtype=scipy.complex64, count=self.fft_size) + if(len(acq_data) == 0): + print "End of File" + else: + self.acq_data_reals = [r.real for r in acq_data] + self.acq_data_imags = [i.imag for i in acq_data] + self.derot_data_reals = [r.real for r in derot_data] + self.derot_data_imags = [i.imag for i in derot_data] + + self.unequalized_angle = [math.atan2(x.imag, x.real) for x in fft_data] + self.equalized_angle = [math.atan2(x.imag, x.real) for x in acq_data] + self.derot_equalized_angle = [math.atan2(x.imag, x.real) for x in derot_data] + + self.time = [i*(1/self.sample_rate) for i in range(len(acq_data))] + ffttime = [i*(1/self.sample_rate) for i in range(len(fft_data))] + + self.freq = self.get_freq(ffttime, self.sample_rate) + + for i in range(len(fft_data)): + if(abs(fft_data[i]) == 0.0): + fft_data[i] = complex(1e-6,1e-6) + self.fft_data = [20*log10(abs(f)) for f in fft_data] + + def get_freq(self, time, sample_rate, T=1): + N = len(time) + Fs = 1.0 / (max(time) - min(time)) + Fn = 0.5 * sample_rate + freq = [-Fn + i*Fs for i in range(N)] + return freq + + def make_plots(self): + self.h_acq_file.seek(8*self.symbol*self.occ_tones, 0) + self.h_fft_file.seek(8*self.symbol*self.fft_size, 0) + self.h_derot_file.seek(8*self.symbol*self.occ_tones, 0) + + self.get_data() + + # Subplot: constellation of rotated symbols + self.sp_const = self.fig.add_subplot(4,1,1, position=[0.15, 0.55, 0.3, 0.35]) + self.sp_const.set_title(("Constellation"), fontsize=self.title_font_size, fontweight="bold") + self.sp_const.set_xlabel("Inphase", fontsize=self.label_font_size, fontweight="bold") + self.sp_const.set_ylabel("Qaudrature", fontsize=self.label_font_size, fontweight="bold") + self.plot_const = plot(self.acq_data_reals, self.acq_data_imags, 'bo') + self.plot_const += plot(self.derot_data_reals, self.derot_data_imags, 'ro') + self.sp_const.axis([-2, 2, -2, 2]) + + # Subplot: unequalized angle + self.sp_uneq = self.fig.add_subplot(4,2,1, position=[0.575, 0.55, 0.3, 0.35]) + self.sp_uneq.set_title(("Unequalized Angle"), fontsize=self.title_font_size, fontweight="bold") + self.sp_uneq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_uneq.set_ylabel("Angle", fontsize=self.label_font_size, fontweight="bold") + uneqscale = range(len(self.unequalized_angle)) + self.plot_uneq = plot(uneqscale, self.unequalized_angle, 'bo') + + # Subplot: equalized angle + self.sp_eq = self.fig.add_subplot(4,1,2, position=[0.15, 0.1, 0.3, 0.35]) + self.sp_eq.set_title(("Equalized Angle"), fontsize=self.title_font_size, fontweight="bold") + self.sp_eq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_eq.set_ylabel("Angle", fontsize=self.label_font_size, fontweight="bold") + eqscale = range(len(self.equalized_angle)) + self.plot_eq = plot(eqscale, self.equalized_angle, 'bo') + self.plot_eq += plot(eqscale, self.derot_equalized_angle, 'ro', markersize=4) + + # Subplot: FFT + self.sp_fft = self.fig.add_subplot(4,2,2, position=[0.575, 0.1, 0.3, 0.35]) + self.sp_fft.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp_fft.set_xlabel("Frequency (MHz)", fontsize=self.label_font_size, fontweight="bold") + self.sp_fft.set_ylabel("Power (dBm)", fontsize=self.label_font_size, fontweight="bold") + self.plot_fft = plot(self.freq, self.fft_data, '-bo') + + draw() + + def update_plots(self): + eqscale = range(len(self.equalized_angle)) + uneqscale = range(len(self.unequalized_angle)) + self.plot_eq[0].set_data([eqscale, self.equalized_angle]) + self.plot_eq[1].set_data([eqscale, self.derot_equalized_angle]) + self.plot_uneq[0].set_data([uneqscale, self.unequalized_angle]) + self.sp_eq.set_ylim([-4, 4]) + self.sp_uneq.set_ylim([-4, 4]) + + #self.sp_iq.axis([min(self.time), max(self.time), + # 1.5*min([min(self.acq_data_reals), min(self.acq_data_imags)]), + # 1.5*max([max(self.acq_data_reals), max(self.acq_data_imags)])]) + + self.plot_const[0].set_data([self.acq_data_reals, self.acq_data_imags]) + self.plot_const[1].set_data([self.derot_data_reals, self.derot_data_imags]) + self.sp_const.axis([-2, 2, -2, 2]) + + self.plot_fft[0].set_data([self.freq, self.fft_data]) + + draw() + + def zoom(self, event): + newxlim = self.sp_eq.get_xlim() + if(newxlim != self.xlim): + self.xlim = newxlim + r = self.reals[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] + i = self.imags[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] + + self.plot_const[0].set_data(r, i) + self.sp_const.axis([-2, 2, -2, 2]) + self.manager.canvas.draw() + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.symbol += 1 + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + self.symbol -= 1 + if(self.h_acq_file.tell() >= 16*self.occ_tones): + self.h_acq_file.seek(-16*self.occ_tones, 1) + else: + self.symbol = 0 + self.h_acq_file.seek(-self.h_acq_file.tell(),1) + + + if(self.h_derot_file.tell() >= 16*self.occ_tones): + self.h_derot_file.seek(-16*self.occ_tones, 1) + else: + self.symbol = 0 + self.h_derot_file.seek(-self.h_derot_file.tell(),1) + + + if(self.h_fft_file.tell() >= 16*self.fft_size): + self.h_fft_file.seek(-16*self.fft_size, 1) + else: + self.symbol = 0 + self.h_fft_file.seek(-self.h_fft_file.tell(),1) + + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options]" + + parser = OptionParser(conflict_handler="resolve", usage=usage) + parser.add_option("", "--fft-size", type="int", default=512, + help="Specify the size of the FFT [default=%default]") + parser.add_option("", "--occ-tones", type="int", default=200, + help="Specify the number of occupied tones [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify the starting symbol to plot [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + + dc = draw_constellation(options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + + + diff --git a/gr-digital/examples/ofdm/ofdm_mod_demod_test.py b/gr-digital/examples/ofdm/ofdm_mod_demod_test.py new file mode 100755 index 000000000..b1521da6d --- /dev/null +++ b/gr-digital/examples/ofdm/ofdm_mod_demod_test.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, ofdm_packet_utils +import gnuradio.gr.gr_threading as _threading +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, time, struct, sys, math, os + +class my_top_block(gr.top_block): + def __init__(self, callback, options): + gr.top_block.__init__(self) + + # hard-coded known symbol + ks1 = known_symbols_4512_1[0:options.occupied_tones] + ks2 = known_symbols_4512_2[0:options.occupied_tones] + + self._rcvd_pktq = gr.msg_queue() + + # accepts messages from the outside world + self.ofdm_mapper = gr.ofdm_bpsk_mapper(4, options.occupied_tones, options.fft_length, ks1, ks2) + self.ofdm_corr = gr.ofdm_correlator(options.occupied_tones, options.fft_length, 0, ks1, ks2) + self.ofdm_framer = gr.ofdm_frame_sink(self._rcvd_pktq, options.occupied_tones) + + if 0: # set to 1 to put the correlator in the path to take over the signalling + self.connect((self.ofdm_mapper, 0), (self.ofdm_corr, 0)) + self.connect((self.ofdm_corr, 0), (self.ofdm_framer, 0)) + self.connect((self.ofdm_corr, 1), (self.ofdm_framer, 1)) + + self.connect((self.ofdm_mapper,0), gr.file_sink(gr.sizeof_gr_complex*options.fft_length, "ofdm_mapper.dat")) + self.connect((self.ofdm_corr,0), gr.file_sink(gr.sizeof_gr_complex*options.occupied_tones, "ofdm_corr.dat")) + self.connect((self.ofdm_corr,1), gr.file_sink(gr.sizeof_char, "ofdm_sig.dat")) + + else: + self.connect((self.ofdm_mapper, 0), (self.ofdm_framer, 0)) + self.connect((self.ofdm_mapper, 1), (self.ofdm_framer, 1)) + + self.connect((self.ofdm_mapper,0), gr.file_sink(gr.sizeof_gr_complex*options.fft_length, "ofdm_mapper.dat")) + self.connect((self.ofdm_mapper,1), gr.file_sink(gr.sizeof_char, "ofdm_sig.dat")) + + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + def send_pkt(self, payload='', eof=False): + if eof: + msg = gr.message(1) + else: + pkt = ofdm_packet_utils.make_packet(payload, 1, 1, False, whiten=False) + msg = gr.message_from_string(pkt) + self.ofdm_mapper.msgq().insert_tail(msg) + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = ofdm_packet_utils.unmake_packet(msg.to_string(), whiten=False) + if self.callback: + self.callback(ok, payload) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + ''' Use this program to tie the OFDM modulators straight into the frame sink, with or without + the correlator in between. This is for testing of the modulators and demodulators only without + receiver and sync functionality.''' + + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def send_pkt(payload='', eof=False): + return fg.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) + + printlst = list() + for x in payload[2:]: + t = hex(ord(x)).replace('0x', '') + if(len(t) == 1): + t = '0' + t + printlst.append(t) + printable = ''.join(printlst) + + print printable + print "\n" + + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + expert = 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]") + 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]") + 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]") + + (options, args) = parser.parse_args () + + # build the graph + tb = my_top_block(rx_callback, options) + + tb.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + pkt_size = int(options.size) + + while n < nbytes: + #r = ''.join([chr(random.randint(0,255)) for i in range(pkt_size-2)]) + #pkt_contents = struct.pack('!H', pktno) + r + + pkt_contents = struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff) + + send_pkt(pkt_contents) + n += pkt_size + pktno += 1 + + send_pkt(eof=True) + tb.wait() # wait for it to finish + +known_symbols_4512_1 = [-1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1] + +known_symbols_4512_2 = [1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1] + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-digital/examples/ofdm/ofdm_sync.m b/gr-digital/examples/ofdm/ofdm_sync.m new file mode 100644 index 000000000..d5df42137 --- /dev/null +++ b/gr-digital/examples/ofdm/ofdm_sync.m @@ -0,0 +1,28 @@ +% + +function [theta, g, g1, k, h] = ofdm_sync(signal,SNR,FFTSIZE,CPLEN) + + rho = SNR/(SNR+1); + + d = abs(signal).^2; + g = [ zeros(1,FFTSIZE) signal(1:max(size(signal))-FFTSIZE) ]; + g1 = conj(g); + f = abs(g).^2; + c = d + f; + moving_sum_taps = rho/2 * ones(1,CPLEN); + b = conv(c,moving_sum_taps); + b = b(1:max(size(signal))); + %b = b(CPLEN:max(size(b))); + + k = g1 .* signal; + + moving_sum_taps2 = ones(1, CPLEN); + h = conv(k,moving_sum_taps2); + h = h(1:max(size(signal))); + %h = h(CPLEN:max(size(h))); + + a = abs(h); + + theta = a-b; + +endfunction diff --git a/gr-digital/examples/ofdm/ofdm_sync_pn.m b/gr-digital/examples/ofdm/ofdm_sync_pn.m new file mode 100644 index 000000000..d93c0ca92 --- /dev/null +++ b/gr-digital/examples/ofdm/ofdm_sync_pn.m @@ -0,0 +1,21 @@ +mf = read_float_binary('ofdm_sync_pn-mf_f.dat'); +theta_pn = read_float_binary('ofdm_sync_pn-theta_f.dat'); +peaks_pn = read_char_binary('ofdm_sync_pn-peaks_b.dat'); +regen_pn = read_char_binary('ofdm_sync_pn-regen_b.dat'); +angle_pn = read_float_binary('ofdm_sync_pn-epsilon_f.dat'); + +plot(mf, 'k') +hold +plot(theta_pn, 'g'); +plot(peaks_pn, 'r'); +plot(regen_pn, 'b'); +xlim([100, 50000]); +ylim([0, 1]) +i = find(peaks_pn); +i(100:200) +hold off + +ipeaks = find(peaks_pn); +dpeaks = diff(ipeaks); +hist(dpeaks, 30) +set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); diff --git a/gr-digital/examples/ofdm/plot_ofdm.m b/gr-digital/examples/ofdm/plot_ofdm.m new file mode 100755 index 000000000..2a649b5f5 --- /dev/null +++ b/gr-digital/examples/ofdm/plot_ofdm.m @@ -0,0 +1,74 @@ +function plot_ofdm(fft_size, occ_tones) + +ofdm = read_complex_binary('ofdm_frame_acq_c.dat'); +ofdm_split = split_vect(ofdm, occ_tones); + +ofdm_derot = read_complex_binary('ofdm_frame_sink_c.dat'); +ofdm_derot_split = split_vect(ofdm_derot, occ_tones); + +fftc = read_complex_binary('fft_out_c.dat'); +fftc_split = split_vect(fftc, fft_size); + +size(ofdm_split) +size(ofdm_derot_split) +disp "DEROTATED SPLIT" +ofdm_derot(1:100) + + +figure(1) +#set(gcf, 'Position', [50 50 1000 600]); + +a = size(ofdm_split); +if nargin == 3 + maxcount = count; + if maxcount > a(1) + error('plot_ofdm:tolong', 'Requested count size exceeds size of vectors'); + end +else + maxcount = a(1); +end + +for i = 1:size(ofdm_split)[0] + x = ofdm_split(i,:); + y = fftc_split(i+1,:); + + subplot(2,2,1); + plot(real(x), imag(x), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + axis([-1.5, 1.5, -1.5, 1.5]) + #title('I&Q Constellation', 'FontSize', 36); + #xlabel('Inphase', 'FontSize', 32); + #ylabel('Quadrature', 'FontSize', 32); + + subplot(2,2,3); + plot(angle(x*j), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + axis([0, occ_tones, -3.5, 3.5]) + #title('Equalized Symbol Angle', 'FontSize', 36); + #xlabel('Bin Number (Occ. Tones)', 'FontSize', 32); + #ylabel('Symbol Angle', 'FontSize', 32); + + subplot(2,2,2); + plot(angle(y*j), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + axis([0, fft_size, -3.5, 3.5]) + #title('Unequalized Symbol Angle', 'FontSize', 36); + #xlabel('Bin Number (FFT Size)', 'FontSize', 32); + #ylabel('Symbol Angle', 'FontSize', 32); + + Y = 20*log10(abs(y) ./ max(abs(y))); + subplot(2,2,4); + plot(Y, 'b-'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + axis([0, fft_size, -50, 1]); + #title('Frequency Domain of Unequalized Rx', 'FontSize', 36); + #xlabel('Bin Number (FFT Size)', 'FontSize', 32); + #ylabel('Power (dB)', 'FontSize', 32); + + #N = 20*log10(var(abs(x)-1)); + + disp(sprintf('Symbol Number: %d\n', i)) + #disp(sprintf('\tFreq Error: %f\n', anglesh_pn(1+(i-1)*fft_size))) + pause + +end diff --git a/gr-digital/examples/ofdm/receive_path.py b/gr-digital/examples/ofdm/receive_path.py new file mode 100644 index 000000000..e1c7868a0 --- /dev/null +++ b/gr-digital/examples/ofdm/receive_path.py @@ -0,0 +1,105 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block2): + def __init__(self, rx_callback, options): + + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(0, 0, 0)) + + + 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 + + # receiver + self.ofdm_rx = digital.ofdm_demod(options, + callback=self._rx_callback) + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + + self.connect(self, self.ofdm_rx) + self.connect(self.ofdm_rx, self.probe) + + # Display some information about the setup + if self._verbose: + self._print_verbage() + + 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("-W", "--bandwidth", type="eng_float", + default=500e3, + help="set symbol bandwidth [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 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 + """ + pass diff --git a/gr-digital/examples/ofdm/transmit_path.py b/gr-digital/examples/ofdm/transmit_path.py new file mode 100644 index 000000000..ec357d617 --- /dev/null +++ b/gr-digital/examples/ofdm/transmit_path.py @@ -0,0 +1,100 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio import digital + +import copy +import sys + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block2): + def __init__(self, options): + ''' + See below for what options should hold + ''' + + gr.hier_block2.__init__(self, "transmit_path", + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + options = copy.copy(options) # make a copy so we can destructively modify + + self._verbose = options.verbose # turn verbose mode on/off + self._tx_amplitude = options.tx_amplitude # digital amp sent to radio + + self.ofdm_tx = digital.ofdm_mod(options, + msgq_limit=4, + pad_for_usrp=False) + + 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 + self.connect(self.ofdm_tx, self.amp, self) + + def set_tx_amplitude(self, ampl): + """ + Sets the transmit amplitude sent to the USRP + @param: ampl 0 <= ampl < 1.0. Try 0.10 + """ + self._tx_amplitude = max(0.0, min(ampl, 1)) + self.amp.set_k(self._tx_amplitude) + + def send_pkt(self, payload='', eof=False): + """ + Calls the transmitter method to send a packet + """ + return self.ofdm_tx.send_pkt(payload, eof) + + def add_options(normal, expert): + """ + Adds transmitter-specific options to the Options Parser + """ + normal.add_option("", "--tx-amplitude", type="eng_float", + default=0.1, metavar="AMPL", + help="set transmitter digital amplitude: 0 <= AMPL < 1.0 [default=%default]") + normal.add_option("-W", "--bandwidth", type="eng_float", + default=500e3, + help="set symbol bandwidth [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) + diff --git a/gr-digital/examples/ofdm/tunnel.py b/gr-digital/examples/ofdm/tunnel.py new file mode 100755 index 000000000..74f2667fa --- /dev/null +++ b/gr-digital/examples/ofdm/tunnel.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 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. +# + + +# ///////////////////////////////////////////////////////////////////////////// +# +# 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, digital +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# from current dir +from receive_path import receive_path +from transmit_path import transmit_path +from uhd_interface import uhd_transmitter +from uhd_interface import uhd_receiver + +import os, sys +import random, time, struct + +#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_top_block(gr.top_block): + def __init__(self, callback, options): + gr.top_block.__init__(self) + + self.source = uhd_receiver(options.address, + options.bandwidth, + options.rx_freq, options.rx_gain, + options.antenna, options.verbose) + + self.sink = uhd_transmitter(options.address, + options.bandwidth, + options.tx_freq, options.tx_gain, + options.antenna, options.verbose) + + self.txpath = transmit_path(options) + self.rxpath = receive_path(callback, options) + + self.connect(self.txpath, self.sink) + self.connect(self.source, self.rxpath) + + def carrier_sensed(self): + """ + Return True if the receive path thinks there's carrier + """ + return self.rxpath.carrier_sensed() + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + """ + self.u_snk.set_freq(target_freq) + self.u_src.set_freq(target_freq) + + +# ///////////////////////////////////////////////////////////////////////////// +# 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.tb = None # top block (access to PHY) + + def set_flow_graph(self, tb): + self.tb = tb + + 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.tb.txpath.send_pkt(eof=True) + break + + if self.verbose: + print "Tx: len(payload) = %4d" % (len(payload),) + + delay = min_delay + while self.tb.carrier_sensed(): + sys.stderr.write('B') + time.sleep(delay) + if delay < 0.050: + delay = delay * 2 # exponential back-off + + self.tb.txpath.send_pkt(payload) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + parser = OptionParser (option_class=eng_option, conflict_handler="resolve") + expert_grp = parser.add_option_group("Expert") + + parser.add_option("-m", "--modulation", type="choice", choices=['bpsk', 'qpsk'], + default='bpsk', + help="Select modulation from: bpsk, qpsk [default=%%default]") + + parser.add_option("-v","--verbose", action="store_true", default=False) + expert_grp.add_option("-c", "--carrier-threshold", type="eng_float", default=30, + help="set carrier detect threshold (dB) [default=%default]") + expert_grp.add_option("","--tun-device-filename", default="/dev/net/tun", + help="path to tun device file [default=%default]") + + digital.ofdm_mod.add_options(parser, expert_grp) + digital.ofdm_demod.add_options(parser, expert_grp) + transmit_path.add_options(parser, expert_grp) + receive_path.add_options(parser, expert_grp) + uhd_receiver.add_options(parser) + uhd_transmitter.add_options(parser) + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help(sys.stderr) + sys.exit(1) + + if options.rx_freq is None or options.tx_freq is None: + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + parser.print_help(sys.stderr) + sys.exit(1) + + # 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" + + # instantiate the MAC + mac = cs_mac(tun_fd, verbose=True) + + + # build the graph (PHY) + tb = my_top_block(mac.phy_rx_callback, options) + + mac.set_flow_graph(tb) # give the MAC a handle for the PHY + + print "modulation: %s" % (options.modulation,) + print "freq: %s" % (eng_notation.num_to_str(options.tx_freq)) + + tb.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 192.168.200.1" % (tun_ifname,) + print + print "Be sure to use a different address in the same subnet for each machine." + print + + + tb.start() # Start executing the flow graph (runs in separate threads) + + mac.main_loop() # don't expect this to return... + + tb.stop() # but if it does, tell flow graph to stop. + tb.wait() # wait for it to finish + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/examples/ofdm/uhd_interface.py b/gr-digital/examples/ofdm/uhd_interface.py new file mode 100644 index 000000000..d03bd7f46 --- /dev/null +++ b/gr-digital/examples/ofdm/uhd_interface.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import sys + +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") + +class uhd_interface: + def __init__(self, istx, address, bandwidth, freq=None, + gain=None, antenna=None): + + if(istx): + self.u = uhd.usrp_sink(device_addr=address, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=1) + else: + self.u = uhd.usrp_source(device_addr=address, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=1) + + self._addr = address + self._ant = antenna + self._gain = self.set_gain(gain) + self._freq = self.set_freq(freq) + + self._rate = self.set_sample_rate(bandwidth) + + if(antenna): + self.u.set_antenna(antenna, 0) + + def set_sample_rate(self, bandwidth): + self.u.set_samp_rate(bandwidth) + actual_bw = self.u.get_samp_rate() + + return actual_bw + + def get_sample_rate(self): + return self.u.get_samp_rate() + + def set_gain(self, gain=None): + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + gain = float(g.start()+g.stop())/2 + print "\nNo gain specified." + print "Setting gain to %f (from [%f, %f])" % \ + (gain, g.start(), g.stop()) + + self.u.set_gain(gain, 0) + return gain + + def set_freq(self, freq=None): + if(freq is None): + sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") + sys.exit(1) + + r = self.u.set_center_freq(freq, 0) + if r: + return freq + else: + frange = self.u.get_freq_range() + sys.stderr.write(("\nRequested frequency (%f) out or range [%f, %f]\n") % \ + (freq, frange.start(), frange.stop())) + sys.exit(1) + +#-------------------------------------------------------------------# +# TRANSMITTER +#-------------------------------------------------------------------# + +class uhd_transmitter(uhd_interface, gr.hier_block2): + def __init__(self, address, bandwidth, freq=None, gain=None, + antenna=None, verbose=False): + gr.hier_block2.__init__(self, "uhd_transmitter", + gr.io_signature(1,1,gr.sizeof_gr_complex), + gr.io_signature(0,0,0)) + + # Set up the UHD interface as a transmitter + uhd_interface.__init__(self, True, address, bandwidth, + freq, gain, antenna) + + self.connect(self, self.u) + + if(verbose): + self._print_verbage() + + def add_options(parser): + add_freq_option(parser) + parser.add_option("-a", "--address", type="string", default="addr=192.168.10.2", + help="Address of UHD device, [default=%default]") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("", "--tx-freq", type="eng_float", default=None, + help="set transmit frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("", "--tx-gain", type="eng_float", default=None, + help="set transmit gain in dB (default is midpoint)") + parser.add_option("-v", "--verbose", action="store_true", default=False) + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the UHD transmitter + """ + print "\nUHD Transmitter:" + print "Address: %s" % (self._addr) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + + + +#-------------------------------------------------------------------# +# RECEIVER +#-------------------------------------------------------------------# + + +class uhd_receiver(uhd_interface, gr.hier_block2): + def __init__(self, address, bandwidth, freq=None, gain=None, + antenna=None, verbose=False): + gr.hier_block2.__init__(self, "uhd_receiver", + gr.io_signature(0,0,0), + gr.io_signature(1,1,gr.sizeof_gr_complex)) + + # Set up the UHD interface as a receiver + uhd_interface.__init__(self, False, address, bandwidth, + freq, gain, antenna) + + self.connect(self.u, self) + + if(verbose): + self._print_verbage() + + def add_options(parser): + add_freq_option(parser) + parser.add_option("-a", "--address", type="string", default="addr=192.168.10.2", + help="Address of UHD device, [default=%default]") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("", "--rx-freq", type="eng_float", default=None, + help="set receive frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("", "--rx-gain", type="eng_float", default=None, + help="set receive gain in dB (default is midpoint)") + if not parser.has_option("--verbose"): + parser.add_option("-v", "--verbose", action="store_true", default=False) + + # Make a static method to call before instantiation + add_options = staticmethod(add_options) + + def _print_verbage(self): + """ + Prints information about the UHD transmitter + """ + print "\nUHD Receiver:" + print "Address: %s" % (self._addr) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + diff --git a/gr-digital/examples/run_length.py b/gr-digital/examples/run_length.py new file mode 100755 index 000000000..5020655db --- /dev/null +++ b/gr-digital/examples/run_length.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# +# 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from optparse import OptionParser +import sys + +def main(): + parser = OptionParser() + parser.add_option("-f", "--file", default=None, + help="Choose file to read data from.") + (options, args) = parser.parse_args() + + if options.file == None: + print "Must specify file to read from using '-f'." + sys.exit(1) + print "Using", options.file, "for data." + + f = open(options.file, 'r') + runs = [] + count = 0 + current = 0 + bytes = 0 + bits = 0 + + for ch in f.read(): + x = ord(ch) + bytes = bytes + 1 + for i in range(7,-1,-1): + bits = bits + 1 + t = (x >> i) & 0x1 + if t == current: + count = count + 1 + else: + if count > 0: + if len(runs) < count: + for j in range(count - len(runs)): + runs.append(0); + runs[count-1] = runs[count-1] + 1 + + current = 1-current; + count = 1 + + # Deal with last run at EOF + if len(runs) < count and count > 0: + for j in range(count - len(runs)): + runs.append(0); + runs[count-1] = runs[count-1] + 1 + + chk = 0 + print "Bytes read: ", bytes + print "Bits read: ", bits + print + for i in range(len(runs)): + chk = chk + runs[i]*(i+1) + print "Runs of length", i+1, ":", runs[i] + print + print "Sum of runs:", chk, "bits" + print + print "Maximum run length is", len(runs), "bits" + +if __name__ == "__main__": + main() + + diff --git a/gr-digital/gnuradio-digital.pc.in b/gr-digital/gnuradio-digital.pc.in new file mode 100644 index 000000000..6c0a7ccf8 --- /dev/null +++ b/gr-digital/gnuradio-digital.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: gnuradio-digital +Description: GNU Radio blocks for digital communications +Requires: gnuradio-core +Version: @LIBVER@ +Libs: -L${libdir} -lgnuradio-digital +Cflags: -I${includedir} diff --git a/gr-digital/grc/.gitignore b/gr-digital/grc/.gitignore new file mode 100644 index 000000000..3dda72986 --- /dev/null +++ b/gr-digital/grc/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/gr-digital/grc/Makefile.am b/gr-digital/grc/Makefile.am new file mode 100644 index 000000000..ef4e38600 --- /dev/null +++ b/gr-digital/grc/Makefile.am @@ -0,0 +1,50 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +grcblocksdir = $(grc_blocksdir) + +dist_grcblocks_DATA = \ + digital_block_tree.xml \ + digital_binary_slicer_fb.xml \ + digital_clock_recovery_mm_xx.xml \ + digital_constellation_decoder_cb.xml \ + digital_correlate_access_code_bb.xml \ + digital_costas_loop_cc.xml \ + digital_cma_equalizer_cc.xml \ + digital_fll_band_edge_cc.xml \ + digital_kurtotic_equalizer_cc.xml \ + digital_lms_dd_equalizer_cc.xml \ + digital_mpsk_receiver_cc.xml \ + digital_dxpsk_mod.xml \ + digital_dxpsk_demod.xml \ + digital_psk_mod.xml \ + digital_psk_demod.xml \ + digital_qam_mod.xml \ + digital_qam_demod.xml \ + digital_ofdm_mod.xml \ + digital_ofdm_demod.xml \ + digital_ofdm_cyclic_prefixer.xml \ + digital_ofdm_frame_acquisition.xml \ + digital_ofdm_insert_preamble.xml \ + digital_ofdm_sampler.xml \ + digital_ofdm_sync_pn.xml diff --git a/gr-digital/grc/digital_binary_slicer_fb.xml b/gr-digital/grc/digital_binary_slicer_fb.xml new file mode 100644 index 000000000..3187d13f9 --- /dev/null +++ b/gr-digital/grc/digital_binary_slicer_fb.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Binary Slicer +################################################### + --> +<block> + <name>Binary Slicer</name> + <key>digital_binary_slicer_fb</key> + <import>from gnuradio import digital</import> + <make>digital.binary_slicer_fb()</make> + <sink> + <name>in</name> + <type>float</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml new file mode 100644 index 000000000..816df7f45 --- /dev/null +++ b/gr-digital/grc/digital_block_tree.xml @@ -0,0 +1,63 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##Block Tree for GR Digital blocks. +################################################### + --> +<cat> + <name></name> <!-- Blank for Root Name --> + <cat> + <name>Digital</name> + <block>digital_binary_slicer_fb</block> + <block>digital_clock_recovery_mm_xx</block> + <block>digital_cma_equalizer_cc</block> + <block>digital_constellation_decoder_cb</block> + <block>digital_correlate_access_code_bb</block> + <block>digital_costas_loop_cc</block> + <block>digital_fll_band_edge_cc</block> + <block>digital_kurtotic_equalizer_cc</block> + <block>digital_lms_dd_equalizer_cc</block> + <block>digital_mpsk_receiver_cc</block> + </cat> + <cat> + <name>Digital Modulators</name> + <block>digital_dxpsk_mod</block> + <block>digital_dxpsk_demod</block> + <block>digital_psk_mod</block> + <block>digital_psk_demod</block> + <block>digital_qam_mod</block> + <block>digital_qam_demod</block> + </cat> + <cat> + <name>OFDM</name> + <block>digital_ofdm_mod</block> + <block>digital_ofdm_demod</block> + <block>digital_ofdm_cyclic_prefixer</block> + <block>digital_ofdm_frame_acquisition</block> + <block>digital_ofdm_insert_preamble</block> + <block>digital_ofdm_sampler</block> + <block>digital_ofdm_sync_pn</block> + </cat> +</cat> diff --git a/gr-digital/grc/digital_clock_recovery_mm_xx.xml b/gr-digital/grc/digital_clock_recovery_mm_xx.xml new file mode 100644 index 000000000..d9c5ea4ff --- /dev/null +++ b/gr-digital/grc/digital_clock_recovery_mm_xx.xml @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Clock Recovery MM +################################################### + --> +<block> + <name>Clock Recovery MM</name> + <key>digital_clock_recovery_mm_xx</key> + <import>from gnuradio import digital</import> + <make>digital.clock_recovery_mm_$(type.fcn)($omega, $gain_omega, $mu, $gain_mu, $omega_relative_limit)</make> + <callback>set_omega($omega)</callback> + <callback>set_gain_omega($gain_omega)</callback> + <callback>set_mu($mu)</callback> + <callback>set_gain_mu($gain_mu)</callback> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:cc</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:ff</opt> + </option> + </param> + <param> + <name>Omega</name> + <key>omega</key> + <type>real</type> + </param> + <param> + <name>Gain Omega</name> + <key>gain_omega</key> + <type>real</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <param> + <name>Gain Mu</name> + <key>gain_mu</key> + <type>real</type> + </param> + <param> + <name>Omega Relative Limit</name> + <key>omega_relative_limit</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>$type</type> + </sink> + <source> + <name>out</name> + <type>$type</type> + </source> +</block> diff --git a/gr-digital/grc/digital_cma_equalizer_cc.xml b/gr-digital/grc/digital_cma_equalizer_cc.xml new file mode 100644 index 000000000..118c18e29 --- /dev/null +++ b/gr-digital/grc/digital_cma_equalizer_cc.xml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<!-- +################################################### +## CMA Equalizer +################################################### + --> +<block> + <name>CMA Equalizer</name> + <key>digital_cma_equalizer_cc</key> + <import>from gnuradio import digital</import> + <make>digital.cma_equalizer_cc($num_taps, $modulus, $mu, $sps)</make> + <callback>set_gain($mu)</callback> + <callback>set_modulus($modulus)</callback> + <param> + <name>Num. Taps</name> + <key>num_taps</key> + <type>int</type> + </param> + <param> + <name>Modulus</name> + <key>modulus</key> + <type>real</type> + </param> + <param> + <name>Gain</name> + <key>mu</key> + <type>real</type> + </param> + <param> + <name>Samples per Symbol</name> + <key>sps</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_constellation_decoder_cb.xml b/gr-digital/grc/digital_constellation_decoder_cb.xml new file mode 100644 index 000000000..c7353e288 --- /dev/null +++ b/gr-digital/grc/digital_constellation_decoder_cb.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Constellation Decoder +################################################### + --> +<block> + <name>Constellation Decoder</name> + <key>digital_constellation_decoder_cb</key> + <import>from gnuradio import digital</import> + <make>digital.constellation_decoder_cb($constellation)</make> + <param> + <name>Constellation Object</name> + <key>constellation</key> + <type>raw</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_correlate_access_code_bb.xml b/gr-digital/grc/digital_correlate_access_code_bb.xml new file mode 100644 index 000000000..3941834c4 --- /dev/null +++ b/gr-digital/grc/digital_correlate_access_code_bb.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Correlate Access Code +################################################### + --> +<block> + <name>Correlate Access Code</name> + <key>digital_correlate_access_code_bb</key> + <import>from gnuradio import digital</import> + <make>digital.correlate_access_code_bb($access_code, $threshold)</make> + <param> + <name>Access Code</name> + <key>access_code</key> + <value>101010</value> + <type>string</type> + </param> + <param> + <name>Threshold</name> + <key>threshold</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_costas_loop_cc.xml b/gr-digital/grc/digital_costas_loop_cc.xml new file mode 100644 index 000000000..668c43dec --- /dev/null +++ b/gr-digital/grc/digital_costas_loop_cc.xml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Costas Loop +################################################### + --> +<block> + <name>Costas Loop</name> + <key>digital_costas_loop_cc</key> + <import>from gnuradio import digital</import> + <make>digital.costas_loop_cc($w, $order)</make> + <callback>set_loop_bandwidth($w)</callback> + <param> + <name>Loop Bandwidth</name> + <key>w</key> + <type>real</type> + </param> + <param> + <name>Order</name> + <key>order</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> + + <!-- Optional Outputs --> + <source> + <name>frequency</name> + <type>float</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_dxpsk_demod.xml b/gr-digital/grc/digital_dxpsk_demod.xml new file mode 100644 index 000000000..d5e742097 --- /dev/null +++ b/gr-digital/grc/digital_dxpsk_demod.xml @@ -0,0 +1,153 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##DPSK2 Mod - 2, 4, 8 +################################################### + --> +<block> + <name>DPSK Demod</name> + <key>digital_dxpsk_demod</key> + <import>from gnuradio import digital</import> + <make>digital.$(type)_demod( + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + phase_bw=$phase_bw, + timing_bw=$timing_bw, + gray_coded=$gray_coded, + verbose=$verbose, + log=$log +)</make> + <callback>clock_recov.set_loop_bandwidth($phase_bw)</callback> + <callback>time_recov.set_loop_bandwidth($timing_bw)</callback> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>DBPSK</name> + <key>dbpsk</key> + </option> + <option> + <name>DQPSK</name> + <key>dqpsk</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Phase Loop Bandwidth</name> + <key>phase_bw</key> + <value>6.28/100.0</value> + <type>real</type> + </param> + <param> + <name>Timing Bandwidth</name> + <key>timing_bw</key> + <value>6.28/100.0</value> + <type>real</type> + </param> + <param> + <name>Omega Relative Limit</name> + <key>omega_relative_limit</key> + <value>0.005</value> + <type>real</type> + </param> + <param> + <name>Gray Code</name> + <key>gray_coded</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Sync Out</name> + <key>sync_out</key> + <value>False</value> + <type>bool</type> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_dxpsk_mod.xml b/gr-digital/grc/digital_dxpsk_mod.xml new file mode 100644 index 000000000..fbda9fb1f --- /dev/null +++ b/gr-digital/grc/digital_dxpsk_mod.xml @@ -0,0 +1,121 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +## DPSK Mod - 2, 4, 8 +################################################### + --> +<block> + <name>DPSK Mod</name> + <key>digital_dxpsk_mod</key> + <import>from gnuradio import digital</import> + <make>digital.$(type)_mod( + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + gray_coded=$gray_coded, + verbose=$verbose, + log=$log) + </make> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>DBPSK</name> + <key>dbpsk</key> + </option> + <option> + <name>DQPSK</name> + <key>dqpsk</key> + </option> + <option> + <name>D8PSK</name> + <key>d8psk</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Gray Code</name> + <key>gray_coded</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_fll_band_edge_cc.xml b/gr-digital/grc/digital_fll_band_edge_cc.xml new file mode 100644 index 000000000..e4da773db --- /dev/null +++ b/gr-digital/grc/digital_fll_band_edge_cc.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- +################################################### +## FLL using Band-Edge Filters +################################################### + --> +<block> + <name>FLL Band-Edge</name> + <key>digital_fll_band_edge_cc</key> + <import>from gnuradio import digital</import> + <make>digital.fll_band_edge_cc($samps_per_sym, $rolloff, $filter_size, $w)</make> + <callback>set_loop_bandwidth($w)</callback> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex->Complex</name> + <key>cc</key> + <opt>input:complex</opt> + <opt>output:complex</opt> + </option> + </param> + + <param> + <name>Samples Per Symbol</name> + <key>samps_per_sym</key> + <type>real</type> + </param> + <param> + <name>Filter Rolloff Factor</name> + <key>rolloff</key> + <type>real</type> + </param> + <param> + <name>Prototype Filter Size</name> + <key>filter_size</key> + <type>int</type> + </param> + + <param> + <name>Loop Bandwidth</name> + <key>w</key> + <type>real</type> + </param> + + <sink> + <name>in</name> + <type>$type.input</type> + </sink> + <source> + <name>out</name> + <type>$type.output</type> + </source> + <source> + <name>freq</name> + <type>float</type> + <optional>1</optional> + </source> + <source> + <name>phase</name> + <type>float</type> + <optional>1</optional> + </source> + <source> + <name>error</name> + <type>float</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_kurtotic_equalizer_cc.xml b/gr-digital/grc/digital_kurtotic_equalizer_cc.xml new file mode 100644 index 000000000..8c4a2012d --- /dev/null +++ b/gr-digital/grc/digital_kurtotic_equalizer_cc.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Kurtotic Equalizer +################################################### + --> +<block> + <name>Kurtotic Equalizer</name> + <key>digital_kurtotic_equalizer_cc</key> + <import>from gnuradio import digital</import> + <make>digital.kurtotic_equalizer_cc($num_taps, $mu)</make> + <callback>set_gain($mu)</callback> + <param> + <name>Num. Taps</name> + <key>num_taps</key> + <type>int</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_lms_dd_equalizer_cc.xml b/gr-digital/grc/digital_lms_dd_equalizer_cc.xml new file mode 100644 index 000000000..0fd7d523b --- /dev/null +++ b/gr-digital/grc/digital_lms_dd_equalizer_cc.xml @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<!-- +################################################### +## LMS DD Equalizer +################################################### + --> +<block> + <name>LMS DD Equalizer</name> + <key>digital_lms_dd_equalizer_cc</key> + <import>from gnuradio import digital</import> + <make>digital.lms_dd_equalizer_cc($num_taps, $mu, $sps, $cnst)</make> + <callback>set_gain($mu)</callback> + <param> + <name>Gain</name> + <key>mu</key> + <type>real</type> + </param> + <param> + <name>Num. Taps</name> + <key>num_taps</key> + <type>int</type> + </param> + <param> + <name>Samples per Symbol</name> + <key>sps</key> + <type>int</type> + </param> + <param> + <name>Constellation Object</name> + <key>cnst</key> + <type>raw</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_mpsk_receiver_cc.xml b/gr-digital/grc/digital_mpsk_receiver_cc.xml new file mode 100644 index 000000000..ab7e5c209 --- /dev/null +++ b/gr-digital/grc/digital_mpsk_receiver_cc.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- +################################################### +##MPSK Receiver +################################################### + --> +<block> + <name>MPSK Receiver</name> + <key>digital_mpsk_receiver_cc</key> + <import>from gnuradio import digital</import> + <make>digital.mpsk_receiver_cc($M, $theta, $w, $fmin, $fmax, $mu, $gain_mu, $omega, $gain_omega, $omega_relative_limit)</make> + <callback>set_loop_bandwidth($w)</callback> + <callback>set_mu($mu)</callback> + <callback>set_gain_mu($gain_mu)</callback> + <callback>set_omega($omega)</callback> + <callback>set_gain_omega($gain_omega)</callback> + <param> + <name>M</name> + <key>M</key> + <type>int</type> + </param> + <param> + <name>Theta</name> + <key>theta</key> + <type>real</type> + </param> + <param> + <name>Loop Bandwidth</name> + <key>w</key> + <type>real</type> + </param> + <param> + <name>Min Freq</name> + <key>fmin</key> + <type>real</type> + </param> + <param> + <name>Max Freq</name> + <key>fmax</key> + <type>real</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <param> + <name>Gain Mu</name> + <key>gain_mu</key> + <type>real</type> + </param> + <param> + <name>Omega</name> + <key>omega</key> + <type>real</type> + </param> + <param> + <name>Gain Omega</name> + <key>gain_omega</key> + <type>real</type> + </param> + <param> + <name>Omega Relative Limit</name> + <key>omega_relative_limit</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_ofdm_cyclic_prefixer.xml b/gr-digital/grc/digital_ofdm_cyclic_prefixer.xml new file mode 100644 index 000000000..d5e5d3894 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_cyclic_prefixer.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Cyclic Prefixer +################################################### + --> +<block> + <name>OFDM Cyclic Prefixer</name> + <key>digital_ofdm_cyclic_prefixer</key> + <import>from gnuradio import digital</import> + <make>digital.ofdm_cyclic_prefixer($input_size, $output_size)</make> + <param> + <name>Input Size</name> + <key>input_size</key> + <type>int</type> + </param> + <param> + <name>Output Size</name> + <key>output_size</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + <vlen>$input_size</vlen> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_ofdm_demod.xml b/gr-digital/grc/digital_ofdm_demod.xml new file mode 100644 index 000000000..9f3a83715 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_demod.xml @@ -0,0 +1,143 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Demod +################################################### + --> +<block> + <name>OFDM Demod</name> + <key>digital_ofdm_demod</key> + <import>from grc_gnuradio import blks2 as grc_blks2</import> + <import>from gnuradio import digital</import> + <make>grc_blks2.packet_demod_$(type.fcn)(digital.ofdm_demod( + options=grc_blks2.options( + modulation="$modulation", + fft_length=$fft_length, + occupied_tones=$occupied_tones, + cp_length=$cp_length, + snr=$snr, + log=None, + verbose=None, + ), + callback=lambda ok, payload: self.$(id).recv_pkt(ok, payload), + ), +)</make> + <param> + <name>Output Type</name> + <key>type</key> + <value>float</value> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:c</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:f</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>fcn:i</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>fcn:s</opt> + </option> + <option> + <name>Byte</name> + <key>byte</key> + <opt>fcn:b</opt> + </option> + </param> + <param> + <name>Modulation</name> + <key>modulation</key> + <type>enum</type> + <option> + <name>BPSK</name> + <key>bpsk</key> + </option> + <option> + <name>QPSK</name> + <key>qpsk</key> + </option> + <option> + <name>8PSK</name> + <key>8psk</key> + </option> + <option> + <name>QAM8</name> + <key>qam8</key> + </option> + <option> + <name>QAM16</name> + <key>qam16</key> + </option> + <option> + <name>QAM64</name> + <key>qam64</key> + </option> + <option> + <name>QAM256</name> + <key>qam256</key> + </option> + </param> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <value>512</value> + <type>int</type> + </param> + <param> + <name>Occupied Tones</name> + <key>occupied_tones</key> + <value>200</value> + <type>int</type> + </param> + <param> + <name>Cyclic Prefix Length</name> + <key>cp_length</key> + <value>128</value> + <type>int</type> + </param> + <param> + <name>SNR</name> + <key>snr</key> + <value>10</value> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>$type</type> + </source> + <doc>Payload Length: 0 for automatic.</doc> +</block> diff --git a/gr-digital/grc/digital_ofdm_frame_acquisition.xml b/gr-digital/grc/digital_ofdm_frame_acquisition.xml new file mode 100644 index 000000000..2545f18e6 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_frame_acquisition.xml @@ -0,0 +1,77 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Frame Acquisition +################################################### + --> +<block> + <name>OFDM Frame Acquisition</name> + <key>digital_ofdm_frame_acquisition</key> + <import>from gnuradio import digital</import> + <make>digital.ofdm_frame_acquisition($occupied_carriers, $fft_length, $cplen, $known_symbol, $max_fft_shift_len)</make> + <param> + <name>Occupied Carriers</name> + <key>occupied_carriers</key> + <type>int</type> + </param> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <type>int</type> + </param> + <param> + <name>CP Length</name> + <key>cplen</key> + <type>int</type> + </param> + <param> + <name>Preamble</name> + <key>known_symbol</key> + <type>complex_vector</type> + </param> + <param> + <name>Max FFT Shift</name> + <key>max_fft_shift_len</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + <vlen>$fft_length</vlen> + </sink> + <sink> + <name>flag</name> + <type>byte</type> + <vlen>$fft_length</vlen> + </sink> + <source> + <name>out</name> + <type>complex</type> + <vlen>$occupied_carriers</vlen> + </source> + <source> + <name>flag</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_ofdm_insert_preamble.xml b/gr-digital/grc/digital_ofdm_insert_preamble.xml new file mode 100644 index 000000000..33a93058f --- /dev/null +++ b/gr-digital/grc/digital_ofdm_insert_preamble.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Insert Preamble +################################################### + --> +<block> + <name>OFDM Insert Preamble</name> + <key>digital_ofdm_insert_preamble</key> + <import>from gnuradio import digital</import> + <make>digital.ofdm_insert_preamble($fft_length, $preamble)</make> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <type>int</type> + </param> + <param> + <name>Preamble</name> + <key>preamble</key> + <type>raw</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + <vlen>$fft_length</vlen> + </sink> + <sink> + <name>flag</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + <vlen>$fft_length</vlen> + </source> +</block> diff --git a/gr-digital/grc/digital_ofdm_mod.xml b/gr-digital/grc/digital_ofdm_mod.xml new file mode 100644 index 000000000..24cb2aa33 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_mod.xml @@ -0,0 +1,156 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Mod +################################################### + --> +<block> + <name>OFDM Mod</name> + <key>digital_ofdm_mod</key> + <import>from grc_gnuradio import blks2 as grc_blks2</import> + <import>from gnuradio import digital</import> + <make>grc_blks2.packet_mod_$(type.fcn)(digital.ofdm_mod( + options=grc_blks2.options( + modulation="$modulation", + fft_length=$fft_length, + occupied_tones=$occupied_tones, + cp_length=$cp_length, + pad_for_usrp=$pad_for_usrp, + log=None, + verbose=None, + ), + ), + payload_length=$payload_length, +)</make> + <param> + <name>Input Type</name> + <key>type</key> + <value>float</value> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:c</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:f</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>fcn:i</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>fcn:s</opt> + </option> + <option> + <name>Byte</name> + <key>byte</key> + <opt>fcn:b</opt> + </option> + </param> + <param> + <name>Modulation</name> + <key>modulation</key> + <type>enum</type> + <option> + <name>BPSK</name> + <key>bpsk</key> + </option> + <option> + <name>QPSK</name> + <key>qpsk</key> + </option> + <option> + <name>8PSK</name> + <key>8psk</key> + </option> + <option> + <name>QAM8</name> + <key>qam8</key> + </option> + <option> + <name>QAM16</name> + <key>qam16</key> + </option> + <option> + <name>QAM64</name> + <key>qam64</key> + </option> + <option> + <name>QAM256</name> + <key>qam256</key> + </option> + </param> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <value>512</value> + <type>int</type> + </param> + <param> + <name>Occupied Tones</name> + <key>occupied_tones</key> + <value>200</value> + <type>int</type> + </param> + <param> + <name>Cyclic Prefix Length</name> + <key>cp_length</key> + <value>128</value> + <type>int</type> + </param> + <param> + <name>Pad for USRP</name> + <key>pad_for_usrp</key> + <type>enum</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Payload Length</name> + <key>payload_length</key> + <value>0</value> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>$type</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> + <doc>Payload Length: 0 for automatic.</doc> +</block> diff --git a/gr-digital/grc/digital_ofdm_sampler.xml b/gr-digital/grc/digital_ofdm_sampler.xml new file mode 100644 index 000000000..1be0d11f3 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_sampler.xml @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Sampler +################################################### + --> +<block> + <name>OFDM Sampler</name> + <key>digital_ofdm_sampler</key> + <import>from gnuradio import digital</import> + <make>digital.ofdm_sampler($fft_length, $symbol_length, $timeout)</make> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <type>int</type> + </param> + <param> + <name>Symbol Length</name> + <key>symbol_length</key> + <type>int</type> + </param> + <param> + <name>Timeout</name> + <key>timeout</key> + <type>int</type> + <value>1000</value> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <sink> + <name>flag</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + <vlen>$fft_length</vlen> + </source> + <source> + <name>flag</name> + <type>byte</type> + <vlen>$fft_length</vlen> + </source> +</block> diff --git a/gr-digital/grc/digital_ofdm_sync_pn.xml b/gr-digital/grc/digital_ofdm_sync_pn.xml new file mode 100644 index 000000000..ac0bbfe88 --- /dev/null +++ b/gr-digital/grc/digital_ofdm_sync_pn.xml @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<!-- + Copyright 2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##OFDM Synchronizer - PN based +################################################### + --> +<block> + <name>OFDM Sync PN</name> + <key>digital_ofdm_sync_pn</key> + <import>from gnuradio import digital</import> + <make>digital.ofdm_sync_pn($fft_length, $cp_length, $logging)</make> + <param> + <name>FFT Length</name> + <key>fft_length</key> + <type>int</type> + </param> + <param> + <name>CP Length</name> + <key>cp_length</key> + <type>int</type> + </param> + <param> + <name>Logging</name> + <key>logging</key> + <type>bool</type> + <value>False</value> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>fine freq</name> + <type>float</type> + </source> + <source> + <name>timing sig</name> + <type>byte</type> + </source> +</block> diff --git a/gr-digital/grc/digital_psk_demod.xml b/gr-digital/grc/digital_psk_demod.xml new file mode 100644 index 000000000..b2628ac88 --- /dev/null +++ b/gr-digital/grc/digital_psk_demod.xml @@ -0,0 +1,158 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##PSK Demod +################################################### + --> +<block> + <name>PSK Demod</name> + <key>digital_psk_demod</key> + <import>from gnuradio import digital</import> + <make>digital.psk.psk_demod( + constellation_points=$constellation_points, + mod_code=$mod_code, + differential=$differential, + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + freq_alpha=$freq_alpha, + timing_alpha=$timing_alpha, + timing_max_dev=$timing_max_dev, + phase_alpha=$phase_alpha, + verbose=$verbose, + log=$log, + )</make> + <param> + <name>Number of Constellation Points</name> + <key>constellation_points</key> + <value>8</value> + <type>int</type> + </param> + <param> + <name>Gray Code</name> + <key>mod_code</key> + <type>enum</type> + <option> + <name>Yes</name> + <key>gray</key> + </option> + <option> + <name>No</name> + <key>none</key> + </option> + </param> + <param> + <name>Differential Encoding</name> + <key>differential</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Frequency Alpha</name> + <key>freq_alpha</key> + <value>0.01</value> + <type>real</type> + </param> + <param> + <name>Timing Alpha</name> + <key>timing_alpha</key> + <value>0.100</value> + <type>real</type> + </param> + <param> + <name>Timing Max Dev</name> + <key>timing_max_dev</key> + <value>1.5</value> + <type>real</type> + </param> + <param> + <name>Phase Alpha</name> + <key>phase_alpha</key> + <value>0.1</value> + <type>real</type> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> + <source> + <name>sync</name> + <type>complex</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_psk_mod.xml b/gr-digital/grc/digital_psk_mod.xml new file mode 100644 index 000000000..cafcf4e50 --- /dev/null +++ b/gr-digital/grc/digital_psk_mod.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##PSK Mod +################################################### + --> +<block> + <name>PSK Mod</name> + <key>digital_psk_mod</key> + <import>from gnuradio import digital</import> + <make>digital.psk.psk_mod( + constellation_points=$constellation_points, + mod_code=$mod_code, + differential=$differential, + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + verbose=$verbose, + log=$log, + )</make> + <param> + <name>Number of Constellation Points</name> + <key>constellation_points</key> + <value>8</value> + <type>int</type> + </param> + <param> + <name>Gray Code</name> + <key>mod_code</key> + <type>enum</type> + <option> + <name>Yes</name> + <key>"gray"</key> + </option> + <option> + <name>No</name> + <key>"none"</key> + </option> + </param> + <param> + <name>Differential Encoding</name> + <key>differential</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_qam_demod.xml b/gr-digital/grc/digital_qam_demod.xml new file mode 100644 index 000000000..88b20293e --- /dev/null +++ b/gr-digital/grc/digital_qam_demod.xml @@ -0,0 +1,158 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##QAM Demod +################################################### + --> +<block> + <name>QAM Demod</name> + <key>digital_qam_demod</key> + <import>from gnuradio import digital</import> + <make>digital.qam.qam_demod( + constellation_points=$constellation_points, + mod_code=$mod_code, + differential=$differential, + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + freq_alpha=$freq_alpha, + timing_alpha=$timing_alpha, + timing_max_dev=$timing_max_dev, + phase_alpha=$phase_alpha, + verbose=$verbose, + log=$log, + )</make> + <param> + <name>Number of Constellation Points</name> + <key>constellation_points</key> + <value>16</value> + <type>int</type> + </param> + <param> + <name>Gray Code</name> + <key>mod_code</key> + <type>enum</type> + <option> + <name>Yes</name> + <key>gray</key> + </option> + <option> + <name>No</name> + <key>none</key> + </option> + </param> + <param> + <name>Differential Encoding</name> + <key>differential</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Frequency Alpha</name> + <key>freq_alpha</key> + <value>0.01</value> + <type>real</type> + </param> + <param> + <name>Timing Alpha</name> + <key>timing_alpha</key> + <value>0.100</value> + <type>real</type> + </param> + <param> + <name>Timing Max Dev</name> + <key>timing_max_dev</key> + <value>1.5</value> + <type>real</type> + </param> + <param> + <name>Phase Alpha</name> + <key>phase_alpha</key> + <value>0.1</value> + <type>real</type> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> + <source> + <name>sync</name> + <type>complex</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_qam_mod.xml b/gr-digital/grc/digital_qam_mod.xml new file mode 100644 index 000000000..4d73d9a68 --- /dev/null +++ b/gr-digital/grc/digital_qam_mod.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,2010,2011 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<!-- +################################################### +##QAM Mod +################################################### + --> +<block> + <name>QAM Mod</name> + <key>digital_qam_mod</key> + <import>from gnuradio import digital</import> + <make>digital.qam.qam_mod( + constellation_points=$constellation_points, + mod_code=$mod_code, + differential=$differential, + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + verbose=$verbose, + log=$log, + )</make> + <param> + <name>Number of Constellation Points</name> + <key>constellation_points</key> + <value>16</value> + <type>int</type> + </param> + <param> + <name>Gray Code</name> + <key>mod_code</key> + <type>enum</type> + <option> + <name>Yes</name> + <key>"gray"</key> + </option> + <option> + <name>No</name> + <key>"none"</key> + </option> + </param> + <param> + <name>Differential Encoding</name> + <key>differential</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/include/.gitignore b/gr-digital/include/.gitignore new file mode 100644 index 000000000..b336cc7ce --- /dev/null +++ b/gr-digital/include/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/gr-digital/include/Makefile.am b/gr-digital/include/Makefile.am new file mode 100644 index 000000000..ce9e67efb --- /dev/null +++ b/gr-digital/include/Makefile.am @@ -0,0 +1,50 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + digital_binary_slicer_fb.h \ + digital_clock_recovery_mm_cc.h \ + digital_clock_recovery_mm_ff.h \ + digital_constellation.h \ + digital_constellation_receiver_cb.h \ + digital_constellation_decoder_cb.h \ + digital_correlate_access_code_bb.h \ + digital_costas_loop_cc.h \ + digital_cma_equalizer_cc.h \ + digital_crc32.h \ + digital_fll_band_edge_cc.h \ + digital_lms_dd_equalizer_cc.h \ + digital_kurtotic_equalizer_cc.h \ + digital_metric_type.h \ + digital_mpsk_receiver_cc.h \ + digital_ofdm_cyclic_prefixer.h \ + digital_ofdm_frame_acquisition.h \ + digital_ofdm_frame_sink.h \ + digital_ofdm_insert_preamble.h \ + digital_ofdm_mapper_bcv.h \ + digital_ofdm_sampler.h \ + digital_gmskmod_bc.h \ + digital_cpmmod_bc.h + +libgnuradio_digital_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS) diff --git a/gr-digital/include/digital_binary_slicer_fb.h b/gr-digital/include/digital_binary_slicer_fb.h new file mode 100644 index 000000000..4e6717de6 --- /dev/null +++ b/gr-digital/include/digital_binary_slicer_fb.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_BINARY_SLICER_FB_H +#define INCLUDED_DIGITAL_BINARY_SLICER_FB_H + +#include <gr_sync_block.h> + +class digital_binary_slicer_fb; +typedef boost::shared_ptr<digital_binary_slicer_fb> digital_binary_slicer_fb_sptr; + +digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + +/*! + * \brief slice float binary symbol outputting 1 bit output + * \ingroup converter_blk + * \ingroup digital + * + * x < 0 --> 0 + * x >= 0 --> 1 + */ +class digital_binary_slicer_fb : public gr_sync_block +{ + friend digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + digital_binary_slicer_fb (); + + public: + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/include/digital_clock_recovery_mm_cc.h b/gr-digital/include/digital_clock_recovery_mm_cc.h new file mode 100644 index 000000000..5d9c8c5e5 --- /dev/null +++ b/gr-digital/include/digital_clock_recovery_mm_cc.h @@ -0,0 +1,116 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_CC_H +#define INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_CC_H + +#include <gr_block.h> +#include <gr_complex.h> +#include <gr_math.h> + +class gri_mmse_fir_interpolator_cc; + +class digital_clock_recovery_mm_cc; +typedef boost::shared_ptr<digital_clock_recovery_mm_cc> digital_clock_recovery_mm_cc_sptr; + +// public constructor +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001); + +/*! + * \brief Mueller and Müller (M&M) based clock recovery block with complex input, complex output. + * \ingroup sync_blk + * \ingroup digital + * + * This implements the Mueller and Müller (M&M) discrete-time + * error-tracking synchronizer. + * + * The complex version here is based on: + * Modified Mueller and Muller clock recovery circuit + * Based: + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller + * and Muller algorithm," Electronics Letters, Vol. 31, no. 13, 22 + * June 1995, pp. 1032 - 1033. + */ +class digital_clock_recovery_mm_cc : public gr_block +{ + public: + ~digital_clock_recovery_mm_cc (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + void set_verbose (bool verbose) { d_verbose = verbose; } + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + +protected: + digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limi); + + private: + float d_mu; + float d_omega; + float d_gain_omega; + float d_min_omega; // minimum allowed omega + float d_max_omega; // maximum allowed omeg + float d_omega_relative_limit; // used to compute min and max omega + float d_omega_mid; + float d_gain_mu; + gr_complex d_last_sample; + gri_mmse_fir_interpolator_cc *d_interp; + bool d_verbose; + + gr_complex d_p_2T; + gr_complex d_p_1T; + gr_complex d_p_0T; + + gr_complex d_c_2T; + gr_complex d_c_1T; + gr_complex d_c_0T; + + gr_complex slicer_0deg (gr_complex sample); + gr_complex slicer_45deg (gr_complex sample); + + friend digital_clock_recovery_mm_cc_sptr + digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); +}; + +#endif diff --git a/gr-digital/include/digital_clock_recovery_mm_ff.h b/gr-digital/include/digital_clock_recovery_mm_ff.h new file mode 100644 index 000000000..5a2206fb2 --- /dev/null +++ b/gr-digital/include/digital_clock_recovery_mm_ff.h @@ -0,0 +1,99 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_FF_H +#define INCLUDED_DIGITAL_CLOCK_RECOVERY_MM_FF_H + +#include <gr_block.h> +#include <gr_math.h> +#include <stdio.h> + +class gri_mmse_fir_interpolator; + +class digital_clock_recovery_mm_ff; +typedef boost::shared_ptr<digital_clock_recovery_mm_ff> digital_clock_recovery_mm_ff_sptr; + +// public constructor +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001); + +/*! + * \brief Mueller and Müller (M&M) based clock recovery block with float input, float output. + * \ingroup sync_blk + * \ingroup digital + * + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * + * See "Digital Communication Receivers: Synchronization, Channel + * Estimation and Signal Processing" by Heinrich Meyr, Marc Moeneclaey, & Stefan Fechtel. + * ISBN 0-471-50275-8. + */ +class digital_clock_recovery_mm_ff : public gr_block +{ + public: + ~digital_clock_recovery_mm_ff (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega){ + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + +protected: + digital_clock_recovery_mm_ff (float omega, float gain_omega, float mu, float gain_mu, + float omega_relative_limit); + + private: + float d_mu; // fractional sample position [0.0, 1.0] + float d_omega; // nominal frequency + float d_min_omega; // minimum allowed omega + float d_omega_mid; // average omega + float d_max_omega; // maximum allowed omega + float d_gain_omega; // gain for adjusting omega + float d_gain_mu; // gain for adjusting mu + float d_last_sample; + gri_mmse_fir_interpolator *d_interp; + FILE *d_logfile; + float d_omega_relative_limit; // used to compute min and max omega + + friend digital_clock_recovery_mm_ff_sptr + digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); +}; + +#endif diff --git a/gr-digital/include/digital_cma_equalizer_cc.h b/gr-digital/include/digital_cma_equalizer_cc.h new file mode 100644 index 000000000..93771dca4 --- /dev/null +++ b/gr-digital/include/digital_cma_equalizer_cc.h @@ -0,0 +1,102 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H +#define INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H + +#include <gr_adaptive_fir_ccc.h> +#include <gr_math.h> +#include <iostream> + +class digital_cma_equalizer_cc; +typedef boost::shared_ptr<digital_cma_equalizer_cc> digital_cma_equalizer_cc_sptr; + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps); + +/*! + * \brief Implements constant modulus adaptive filter on complex stream + * \ingroup eq_blk + * \ingroup digital + * + * The error value and tap update equations (for p=2) can be found in: + * + * D. Godard, "Self-Recovering Equalization and Carrier Tracking in + * Two-Dimensional Data Communication Systems," IEEE Transactions on + * Communications, Vol. 28, No. 11, pp. 1867 - 1875, 1980, + */ +class digital_cma_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + float d_modulus; + float d_mu; + + friend digital_cma_equalizer_cc_sptr digital_make_cma_equalizer_cc(int num_taps, + float modulus, + float mu, + int sps); + digital_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps); + +protected: + + virtual gr_complex error(const gr_complex &out) + { + gr_complex error = out*(norm(out) - d_modulus); + float re = gr_clip(error.real(), 1.0); + float im = gr_clip(error.imag(), 1.0); + return gr_complex(re, im); + } + + virtual void update_tap(gr_complex &tap, const gr_complex &in) + { + // Hn+1 = Hn - mu*conj(Xn)*zn*(|zn|^2 - 1) + tap -= d_mu*conj(in)*d_error; + } + +public: + float get_gain() + { + return d_mu; + } + + void set_gain(float mu) + { + if(mu < 0.0f || mu > 1.0f) { + throw std::out_of_range("digital_cma_equalizer::set_gain: Gain value must be in [0,1]"); + } + d_mu = mu; + } + + float get_modulus() + { + return d_modulus; + } + + void set_modulus(float mod) + { + if(mod < 0) + throw std::out_of_range("digital_cma_equalizer::set_modulus: Modulus value must be >= 0"); + d_modulus = mod; + } +}; + +#endif diff --git a/gr-digital/include/digital_constellation.h b/gr-digital/include/digital_constellation.h new file mode 100644 index 000000000..b6da7ff9b --- /dev/null +++ b/gr-digital/include/digital_constellation.h @@ -0,0 +1,448 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CONSTELLATION_H +#define INCLUDED_DIGITAL_CONSTELLATION_H + +#include <vector> +#include <math.h> +#include <gr_complex.h> +#include <boost/enable_shared_from_this.hpp> +#include <digital_metric_type.h> + +/************************************************************/ +/* digital_constellation */ +/* */ +/* Base class defining interface. */ +/************************************************************/ + +class digital_constellation; +typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; + +/*! + * \brief An abstracted constellation object + * \ingroup digital + * + * The constellation objects hold the necessary information to pass + * around constellation information for modulators and + * demodulators. These objects contain the mapping between the bits + * and the constellation points used to represent them as well as + * methods for slicing the symbol space. Various implementations are + * possible for efficiency and ease of use. + * + * Standard constellations (BPSK, QPSK, QAM, etc) can be inherited + * from this class and overloaded to perform optimized slicing and + * constellation mappings. + */ +class digital_constellation : public boost::enable_shared_from_this<digital_constellation> +{ +public: + digital_constellation (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + digital_constellation (); + + //! Returns the constellation points for a symbol value + void map_to_points(unsigned int value, gr_complex *points); + std::vector<gr_complex> map_to_points_v(unsigned int value); + + //! Returns the constellation point that matches best. + virtual unsigned int decision_maker (const gr_complex *sample) = 0; + //! Takes a vector rather than a pointer. Better for SWIG wrapping. + unsigned int decision_maker_v (std::vector<gr_complex> sample); + //! Also calculates the phase error. + unsigned int decision_maker_pe (const gr_complex *sample, float *phase_error); + //! Calculates distance. + unsigned int decision_maker_e (const gr_complex *sample, float *error); + + //! Calculates metrics for all points in the constellation. + //! For use with the viterbi algorithm. + virtual void calc_metric(const gr_complex *sample, float *metric, trellis_metric_type_t type); + virtual void calc_euclidean_metric(const gr_complex *sample, float *metric); + virtual void calc_hard_symbol_metric(const gr_complex *sample, float *metric); + + //! Returns the set of points in this constellation. + std::vector<gr_complex> points() { return d_constellation;} + //! Returns the vector of points in this constellation. + //! Raise error if dimensionality is not one. + std::vector<gr_complex> s_points(); + //! Returns a vector of vectors of points. + std::vector<std::vector<gr_complex> > v_points(); + //! Whether to apply an encoding before doing differential encoding. (e.g. gray coding) + bool apply_pre_diff_code() { return d_apply_pre_diff_code;} + //! Whether to apply an encoding before doing differential encoding. (e.g. gray coding) + void set_pre_diff_code(bool a) { d_apply_pre_diff_code = a;} + //! Returns the encoding to apply before differential encoding. + std::vector<unsigned int> pre_diff_code() { return d_pre_diff_code;} + //! Returns the order of rotational symmetry. + unsigned int rotational_symmetry() { return d_rotational_symmetry;} + //! Returns the number of complex numbers in a single symbol. + unsigned int dimensionality() {return d_dimensionality;} + + unsigned int bits_per_symbol () { + return floor(log(double(d_constellation.size()))/d_dimensionality/log(2.0)); + } + + unsigned int arity () { + return d_arity; + } + + digital_constellation_sptr base() { + return shared_from_this(); + } + + protected: + + std::vector<gr_complex> d_constellation; + std::vector<unsigned int> d_pre_diff_code; + bool d_apply_pre_diff_code; + unsigned int d_rotational_symmetry; + unsigned int d_dimensionality; + unsigned int d_arity; + + float get_distance(unsigned int index, const gr_complex *sample); + unsigned int get_closest_point(const gr_complex *sample); + void calc_arity (); +}; + +/************************************************************/ +/* digital_constellation_calcdist */ +/* */ +/************************************************************/ + +class digital_constellation_calcdist; +typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_calcdist_sptr; + +// public constructor +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + + +/*! \brief Calculate Euclidian distance for any constellation + * \ingroup digital + * + * Constellation which calculates the distance to each point in the + * constellation for decision making. Inefficient for large + * constellations. + */ +class digital_constellation_calcdist : public digital_constellation +{ + public: + digital_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + unsigned int decision_maker (const gr_complex *sample); + // void calc_metric(gr_complex *sample, float *metric, trellis_metric_type_t type); + // void calc_euclidean_metric(gr_complex *sample, float *metric); + // void calc_hard_symbol_metric(gr_complex *sample, float *metric); + + private: + friend digital_constellation_calcdist_sptr + digital_make_constellation_calcdist (std::vector<gr_complex> constellation); +}; + + +/************************************************************/ +/*! digital_constellation_sector */ +/************************************************************/ + +/*! + * \brief Sectorized digital constellation + * \ingroup digital + * + * Constellation space is divided into sectors. Each sector is + * associated with the nearest constellation point. + * + */ +class digital_constellation_sector : public digital_constellation +{ + public: + + digital_constellation_sector (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality, + unsigned int n_sectors); + + unsigned int decision_maker (const gr_complex *sample); + + protected: + + virtual unsigned int get_sector (const gr_complex *sample) = 0; + virtual unsigned int calc_sector_value (unsigned int sector) = 0; + void find_sector_values (); + + unsigned int n_sectors; + + private: + + std::vector<unsigned int> sector_values; + +}; + +/************************************************************/ +/* digital_constellation_rect */ +/************************************************************/ + +/*! + * \brief Rectangular digital constellation + * \ingroup digital + * + * Only implemented for 1-(complex)dimensional constellation. + * + * Constellation space is divided into rectangular sectors. Each + * sector is associated with the nearest constellation point. + * + * Works well for square QAM. + * + * Works for any generic constellation provided sectors are not too + * large. + */ + +class digital_constellation_rect; +typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect_sptr; + +// public constructor +digital_constellation_rect_sptr +digital_make_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); + +class digital_constellation_rect : public digital_constellation_sector +{ + public: + + digital_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); + + protected: + + unsigned int get_sector (const gr_complex *sample); + + unsigned int calc_sector_value (unsigned int sector); + + private: + + unsigned int n_real_sectors; + unsigned int n_imag_sectors; + float d_width_real_sectors; + float d_width_imag_sectors; + + friend digital_constellation_rect_sptr + digital_make_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); + +}; + + +/************************************************************/ +/* digital_constellation_psk */ +/************************************************************/ + +class digital_constellation_psk; +typedef boost::shared_ptr<digital_constellation_psk> digital_constellation_psk_sptr; + +// public constructor +digital_constellation_psk_sptr +digital_make_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +/*! + * \brief digital_constellation_psk + * \ingroup digital + * + * Constellation space is divided into pie slices sectors. + * + * Each slice is associated with the nearest constellation point. + * + * Works well for PSK but nothing else. + * + * Assumes that there is a constellation point at 1.x + */ +class digital_constellation_psk : public digital_constellation_sector +{ + public: + + digital_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + + protected: + + unsigned int get_sector (const gr_complex *sample); + + unsigned int calc_sector_value (unsigned int sector); + + private: + + friend digital_constellation_psk_sptr + digital_make_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +}; + + +/************************************************************/ +/* digital_constellation_bpsk */ +/* */ +/* Only works for BPSK. */ +/* */ +/************************************************************/ + +class digital_constellation_bpsk; +typedef boost::shared_ptr<digital_constellation_bpsk> digital_constellation_bpsk_sptr; + +// public constructor +digital_constellation_bpsk_sptr +digital_make_constellation_bpsk (); + +/*! + * \brief Digital constellation for BPSK + * \ingroup digital + */ +class digital_constellation_bpsk : public digital_constellation +{ + public: + + digital_constellation_bpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_bpsk_sptr + digital_make_constellation_bpsk (); + +}; + + +/************************************************************/ +/* digital_constellation_qpsk */ +/* */ +/* Only works for QPSK. */ +/* */ +/************************************************************/ + +class digital_constellation_qpsk; +typedef boost::shared_ptr<digital_constellation_qpsk> digital_constellation_qpsk_sptr; + +// public constructor +digital_constellation_qpsk_sptr +digital_make_constellation_qpsk (); + +/*! + * \brief Digital constellation for QPSK + * \ingroup digital + */ +class digital_constellation_qpsk : public digital_constellation +{ + public: + + digital_constellation_qpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_qpsk_sptr + digital_make_constellation_qpsk (); + +}; + + +/************************************************************/ +/* digital_constellation_dqpsk */ +/* */ +/* Works with differential encoding; slower decisions. */ +/* */ +/************************************************************/ + +class digital_constellation_dqpsk; +typedef boost::shared_ptr<digital_constellation_dqpsk> digital_constellation_dqpsk_sptr; + +// public constructor +digital_constellation_dqpsk_sptr +digital_make_constellation_dqpsk (); + +/*! + * \brief Digital constellation for DQPSK + * \ingroup digital + */ +class digital_constellation_dqpsk : public digital_constellation +{ + public: + + digital_constellation_dqpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_dqpsk_sptr + digital_make_constellation_dqpsk (); + +}; + + +/************************************************************/ +/* digital_constellation_8psk */ +/* */ +/* Only works for 8PSK. */ +/* */ +/************************************************************/ + +class digital_constellation_8psk; +typedef boost::shared_ptr<digital_constellation_8psk> digital_constellation_8psk_sptr; + +// public constructor +digital_constellation_8psk_sptr +digital_make_constellation_8psk (); + +/*! + * \brief Digital constellation for 8PSK + * \ingroup digital + */ +class digital_constellation_8psk : public digital_constellation +{ + public: + + digital_constellation_8psk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_8psk_sptr + digital_make_constellation_8psk (); + +}; + +#endif diff --git a/gr-digital/include/digital_constellation_decoder_cb.h b/gr-digital/include/digital_constellation_decoder_cb.h new file mode 100644 index 000000000..d587f6bc4 --- /dev/null +++ b/gr-digital/include/digital_constellation_decoder_cb.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CONSTELLATION_DECODER_CB_H +#define INCLUDED_DIGITAL_CONSTELLATION_DECODER_CB_H + +#include <gr_block.h> +#include <digital_constellation.h> +#include <vector> + +class digital_constellation_decoder_cb; +typedef boost::shared_ptr<digital_constellation_decoder_cb>digital_constellation_decoder_cb_sptr; + +digital_constellation_decoder_cb_sptr +digital_make_constellation_decoder_cb (digital_constellation_sptr constellation); + +/*! + * \brief Constellation Decoder + * \ingroup coding_blk + * \ingroup digital + * + */ +class digital_constellation_decoder_cb : public gr_block +{ + + private: + digital_constellation_sptr d_constellation; + unsigned int d_dim; + + friend digital_constellation_decoder_cb_sptr + digital_make_constellation_decoder_cb (digital_constellation_sptr constellation); + + digital_constellation_decoder_cb (digital_constellation_sptr constellation); + + public: + + void forecast (int noutput_items, + gr_vector_int &ninput_items_required); + + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/include/digital_constellation_receiver_cb.h b/gr-digital/include/digital_constellation_receiver_cb.h new file mode 100644 index 000000000..a67f67082 --- /dev/null +++ b/gr-digital/include/digital_constellation_receiver_cb.h @@ -0,0 +1,121 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CONSTELLATION_RECEIVER_CB_H +#define INCLUDED_DIGITAL_CONSTELLATION_RECEIVER_CB_H + +#include <gr_block.h> +#include <digital_constellation.h> +#include <gri_control_loop.h> +#include <gr_complex.h> +#include <math.h> +#include <fstream> + +class digital_constellation_receiver_cb; +typedef boost::shared_ptr<digital_constellation_receiver_cb> digital_constellation_receiver_cb_sptr; + +// public constructor +digital_constellation_receiver_cb_sptr +digital_make_constellation_receiver_cb (digital_constellation_sptr constellation, + float loop_bw, float fmin, float fmax); + +/*! + * \brief This block takes care of receiving generic modulated signals + * through phase, frequency, and symbol synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * \ingroup digital + * + * This block takes care of receiving generic modulated signals + * through phase, frequency, and symbol synchronization. It performs + * carrier frequency and phase locking as well as symbol timing + * recovery. + * + * The phase and frequency synchronization are based on a Costas loop + * that finds the error of the incoming signal point compared to its + * nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. + * + * The symbol synchronization is done using a modified Mueller and + * Muller circuit from the paper: + * + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller + * and Muller algorithm," Electronics Letters, Vol. 31, no. 13, 22 + * June 1995, pp. 1032 - 1033. + * + * This circuit interpolates the downconverted sample (using the NCO + * developed by the Costas loop) every mu samples, then it finds the + * sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are + * optimized decision algorithms for BPSK and QPKS, but 8PSK uses + * another brute force computation against all possible symbols. The + * modifications to the M&M used here reduce self-noise. + * + */ + +class digital_constellation_receiver_cb : public gr_block, public gri_control_loop +{ +public: + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param constellation constellation of points for generic modulation + * \param loop_bw Loop bandwidth of the Costas Loop (~ 2pi/100) + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * + * The constructor also chooses which phase detector and decision maker to use in the + * work loop based on the value of M. + */ + digital_constellation_receiver_cb (digital_constellation_sptr constellation, + float loop_bw, float fmin, float fmax); + + void phase_error_tracking(float phase_error); + +private: + unsigned int d_M; + + digital_constellation_sptr d_constellation; + unsigned int d_current_const_point; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8))); + + //! index to delay line + unsigned int d_dl_idx; + + friend digital_constellation_receiver_cb_sptr + digital_make_constellation_receiver_cb (digital_constellation_sptr constell, + float loop_bw, float fmin, float fmax); +}; + +#endif diff --git a/gr-digital/include/digital_correlate_access_code_bb.h b/gr-digital/include/digital_correlate_access_code_bb.h new file mode 100644 index 000000000..c21875f0e --- /dev/null +++ b/gr-digital/include/digital_correlate_access_code_bb.h @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CORRELATE_ACCESS_CODE_BB_H +#define INCLUDED_DIGITAL_CORRELATE_ACCESS_CODE_BB_H + +#include <gr_sync_block.h> +#include <string> + +class digital_correlate_access_code_bb; +typedef boost::shared_ptr<digital_correlate_access_code_bb> digital_correlate_access_code_bb_sptr; + +/*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + * \param threshold maximum number of bits that may be wrong + */ +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + +/*! + * \brief Examine input for specified access code, one bit at a time. + * \ingroup sync_blk + * \ingroup digital + * + * input: stream of bits, 1 bit per input byte (data in LSB) + * output: stream of bits, 2 bits per output byte (data in LSB, flag in next higher bit) + * + * Each output byte contains two valid bits, the data bit, and the + * flag bit. The LSB (bit 0) is the data bit, and is the original + * input data, delayed 64 bits. Bit 1 is the + * flag bit and is 1 if the corresponding data bit is the first data + * bit following the access code. Otherwise the flag bit is 0. + */ +class digital_correlate_access_code_bb : public gr_sync_block +{ + friend digital_correlate_access_code_bb_sptr + digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + private: + unsigned long long d_access_code; // access code to locate start of packet + // access code is left justified in the word + unsigned long long d_data_reg; // used to look for access_code + unsigned long long d_flag_reg; // keep track of decisions + unsigned long long d_flag_bit; // mask containing 1 bit which is location of new flag + unsigned long long d_mask; // masks access_code bits (top N bits are set where + // N is the number of bits in the access code) + unsigned int d_threshold; // how many bits may be wrong in sync vector + + protected: + digital_correlate_access_code_bb(const std::string &access_code, int threshold); + + public: + ~digital_correlate_access_code_bb(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + /*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + */ + bool set_access_code (const std::string &access_code); +}; + +#endif /* INCLUDED_DIGITAL_CORRELATE_ACCESS_CODE_BB_H */ diff --git a/gr-digital/include/digital_costas_loop_cc.h b/gr-digital/include/digital_costas_loop_cc.h new file mode 100644 index 000000000..3811825dd --- /dev/null +++ b/gr-digital/include/digital_costas_loop_cc.h @@ -0,0 +1,117 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_DIGITAL_COSTAS_LOOP_CC_H +#define INCLUDED_DIGITAL_COSTAS_LOOP_CC_H + +#include <gr_sync_block.h> +#include <gri_control_loop.h> +#include <stdexcept> +#include <fstream> + + +/*! + * \brief A Costas loop carrier recovery module. + * \ingroup sync_blk + * \ingroup digital + * + * The Costas loop locks to the center frequency of a signal and + * downconverts it to baseband. The second (order=2) order loop is + * used for BPSK where the real part of the output signal is the + * baseband BPSK signal and the imaginary part is the error + * signal. When order=4, it can be used for quadrature modulations + * where both I and Q (real and imaginary) are outputted. + * + * More details can be found online: + * + * J. Feigin, "Practical Costas loop design: Designing a simple and + * inexpensive BPSK Costas loop carrier recovery circuit," RF signal + * processing, pp. 20-36, 2002. + * + * http://rfdesign.com/images/archive/0102Feigin20.pdf + * + * \param alpha the loop gain used for phase adjustment + * \param beta the loop gain for frequency adjustments + * \param max_freq the maximum frequency deviation (radians/sample) the loop can handle + * \param min_freq the minimum frequency deviation (radians/sample) the loop can handle + * \param order the loop order, either 2 or 4 + */ +class digital_costas_loop_cc; +typedef boost::shared_ptr<digital_costas_loop_cc> digital_costas_loop_cc_sptr; + + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument); + + +/*! + * \brief Carrier tracking PLL for QPSK + * \ingroup sync_blk + * input: complex; output: complex + * <br>The Costas loop can have two output streams: + * stream 1 is the baseband I and Q; + * stream 2 is the normalized frequency of the loop + * + * \p order must be 2 or 4. + */ +class digital_costas_loop_cc : public gr_sync_block, public gri_control_loop +{ + friend digital_costas_loop_cc_sptr + digital_make_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument); + + int d_order; + + digital_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument); + + /*! \brief the phase detector circuit for 8th-order PSK loops + * \param sample complex sample + * \return the phase error + */ + float phase_detector_8(gr_complex sample) const; // for 8PSK + + /*! \brief the phase detector circuit for fourth-order loops + * \param sample complex sample + * \return the phase error + */ + float phase_detector_4(gr_complex sample) const; // for QPSK + + /*! \brief the phase detector circuit for second-order loops + * \param sample a complex sample + * \return the phase error + */ + float phase_detector_2(gr_complex sample) const; // for BPSK + + + float (digital_costas_loop_cc::*d_phase_detector)(gr_complex sample) const; + +public: + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/include/digital_cpmmod_bc.h b/gr-digital/include/digital_cpmmod_bc.h new file mode 100644 index 000000000..85b901ba2 --- /dev/null +++ b/gr-digital/include/digital_cpmmod_bc.h @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CPMMOD_BC_H +#define INCLUDED_DIGITAL_CPMMOD_BC_H + +#include <gr_hier_block2.h> +#include <gr_char_to_float.h> +#include <gr_interp_fir_filter_fff.h> +#include <gr_frequency_modulator_fc.h> +#include <gr_cpm.h> + + +class digital_cpmmod_bc; +typedef boost::shared_ptr<digital_cpmmod_bc> digital_cpmmod_bc_sptr; + + +digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta=0.3); + +/*! + * \brief Generic CPM modulator + * + * \ingroup modulation_blk + * \ingroup digital + * + * \param type The modulation type. Can be one of LREC, LRC, LSRC, TFM + * or GAUSSIAN. See gr_cpm::phase_response() for a + * detailed description. + * \param h The modulation index. \f$ h \cdot \pi\f$ is the maximum + * phase change that can occur between two symbols, i.e., if + * you only send ones, the phase will increase by \f$ h \cdot + * \pi\f$ every \p samples_per_sym samples. Set this to 0.5 + * for Minimum Shift Keying variants. + * \param samples_per_sym Samples per symbol. + * \param L The length of the phase duration in symbols. For L=1, this + * yields full- response CPM symbols, for L > 1, + * partial-response. + * \param beta For LSRC, this is the rolloff factor. For Gaussian + * pulses, this is the 3 dB time-bandwidth product. + * + * Examples: + * - Setting h = 0.5, L = 1, type = LREC yields MSK. + * - Setting h = 0.5, type = GAUSSIAN and beta = 0.3 yields GMSK + * as used in GSM. + * + * The input of this block are symbols from an M-ary alphabet + * +/-1, +/-3, ..., +/-(M-1). Usually, M = 2 and therefore, the + * valid inputs are +/-1. + * The modulator will silently accept any other inputs, though. + * The output is the phase-modulated signal. + */ +class digital_cpmmod_bc : public gr_hier_block2 +{ + friend digital_cpmmod_bc_sptr digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + + std::vector<float> d_taps; + gr_char_to_float_sptr d_char_to_float; + gr_interp_fir_filter_fff_sptr d_pulse_shaper; + gr_frequency_modulator_fc_sptr d_fm; + +protected: + digital_cpmmod_bc(gr_cpm::cpm_type type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + +public: + //! Return the phase response FIR taps + std::vector<float> get_taps() { return d_taps; }; +}; + +#endif /* INCLUDED_DIGITAL_CPMMOD_BC_H */ + diff --git a/gr-digital/include/digital_crc32.h b/gr-digital/include/digital_crc32.h new file mode 100644 index 000000000..869cf7f45 --- /dev/null +++ b/gr-digital/include/digital_crc32.h @@ -0,0 +1,50 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CRC32_H +#define INCLUDED_DIGITAL_CRC32_H + +#include <string> +#include <gr_types.h> + +/*! + * \brief update running CRC-32 + * \ingroup digital + * + * Update a running CRC with the bytes buf[0..len-1] The CRC should be + * initialized to all 1's, and the transmitted value is the 1's + * complement of the final running CRC. The resulting CRC should be + * transmitted in big endian order. + */ +unsigned int +digital_update_crc32(unsigned int crc, const unsigned char *buf, size_t len); + +unsigned int +digital_update_crc32(unsigned int crc, const std::string buf); + +unsigned int +digital_crc32(const unsigned char *buf, size_t len); + +unsigned int +digital_crc32(const std::string buf); + +#endif /* INCLUDED_CRC32_H */ diff --git a/gr-digital/include/digital_fll_band_edge_cc.h b/gr-digital/include/digital_fll_band_edge_cc.h new file mode 100644 index 000000000..576dfab87 --- /dev/null +++ b/gr-digital/include/digital_fll_band_edge_cc.h @@ -0,0 +1,213 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_DIGITAL_FLL_BAND_EDGE_CC_H +#define INCLUDED_DIGITAL_FLL_BAND_EDGE_CC_H + +#include <gr_sync_block.h> +#include <gri_control_loop.h> + +class digital_fll_band_edge_cc; +typedef boost::shared_ptr<digital_fll_band_edge_cc> digital_fll_band_edge_cc_sptr; +digital_fll_band_edge_cc_sptr digital_make_fll_band_edge_cc (float samps_per_sym, + float rolloff, + int filter_size, + float bandwidth); + +/*! + * \class digital_fll_band_edge_cc + * \brief Frequency Lock Loop using band-edge filters + * + * \ingroup general + * \ingroup digital + * + * The frequency lock loop derives a band-edge filter that covers the + * upper and lower bandwidths of a digitally-modulated signal. The + * bandwidth range is determined by the excess bandwidth (e.g., + * rolloff factor) of the modulated signal. The placement in frequency + * of the band-edges is determined by the oversampling ratio (number + * of samples per symbol) and the excess bandwidth. The size of the + * filters should be fairly large so as to average over a number of + * symbols. + * + * The FLL works by filtering the upper and lower band edges into + * x_u(t) and x_l(t), respectively. These are combined to form cc(t) + * = x_u(t) + x_l(t) and ss(t) = x_u(t) - x_l(t). Combining these to + * form the signal e(t) = Re{cc(t) \\times ss(t)^*} (where ^* is the + * complex conjugate) provides an error signal at the DC term that is + * directly proportional to the carrier frequency. We then make a + * second-order loop using the error signal that is the running + * average of e(t). + * + * In practice, the above equation can be simplified by just comparing + * the absolute value squared of the output of both filters: + * abs(x_l(t))^2 - abs(x_u(t))^2 = norm(x_l(t)) - norm(x_u(t)). + * + * In theory, the band-edge filter is the derivative of the matched + * filter in frequency, (H_be(f) = \\frac{H(f)}{df}. In practice, this + * comes down to a quarter sine wave at the point of the matched + * filter's rolloff (if it's a raised-cosine, the derivative of a + * cosine is a sine). Extend this sine by another quarter wave to + * make a half wave around the band-edges is equivalent in time to the + * sum of two sinc functions. The baseband filter fot the band edges + * is therefore derived from this sum of sincs. The band edge filters + * are then just the baseband signal modulated to the correct place in + * frequency. All of these calculations are done in the + * 'design_filter' function. + * + * Note: We use FIR filters here because the filters have to have a + * flat phase response over the entire frequency range to allow their + * comparisons to be valid. + * + * It is very important that the band edge filters be the derivatives + * of the pulse shaping filter, and that they be linear + * phase. Otherwise, the variance of the error will be very large. + * + */ + +class digital_fll_band_edge_cc : public gr_sync_block, public gri_control_loop +{ + private: + /*! + * Build the FLL + * \param samps_per_sym (float) Number of samples per symbol of signal + * \param rolloff (float) Rolloff factor of signal + * \param filter_size (int) Size (in taps) of the filter + * \param bandwidth (float) Loop bandwidth + */ + friend digital_fll_band_edge_cc_sptr digital_make_fll_band_edge_cc (float samps_per_sym, + float rolloff, + int filter_size, + float bandwidth); + + float d_sps; + float d_rolloff; + int d_filter_size; + + std::vector<gr_complex> d_taps_lower; + std::vector<gr_complex> d_taps_upper; + bool d_updated; + + /*! + * Build the FLL + * \param samps_per_sym (float) number of samples per symbol + * \param rolloff (float) Rolloff (excess bandwidth) of signal filter + * \param filter_size (int) number of filter taps to generate + * \param bandwidth (float) Loop bandwidth + */ + digital_fll_band_edge_cc(float samps_per_sym, float rolloff, + int filter_size, float bandwidth); + + /*! + * Design the band-edge filter based on the number of samples per symbol, + * filter rolloff factor, and the filter size + * + * \param samps_per_sym (float) Number of samples per symbol of signal + * \param rolloff (float) Rolloff factor of signal + * \param filter_size (int) Size (in taps) of the filter + */ + void design_filter(float samps_per_sym, float rolloff, int filter_size); + +public: + ~digital_fll_band_edge_cc (); + + /******************************************************************* + SET FUNCTIONS + *******************************************************************/ + + /*! + * \brief Set the number of samples per symbol + * + * Set's the number of samples per symbol the system should + * use. This value is uesd to calculate the filter taps and will + * force a recalculation. + * + * \param sps (float) new samples per symbol + * + */ + void set_samples_per_symbol(float sps); + + /*! + * \brief Set the rolloff factor of the shaping filter + * + * This sets the rolloff factor that is used in the pulse shaping + * filter and is used to calculate the filter taps. Changing this + * will force a recalculation of the filter taps. + * + * This should be the same value that is used in the transmitter's + * pulse shaping filter. It must be between 0 and 1 and is usually + * between 0.2 and 0.5 (where 0.22 and 0.35 are commonly used + * values). + * + * \param rolloff (float) new shaping filter rolloff factor [0,1] + * + */ + void set_rolloff(float rolloff); + + /*! + * \brief Set the number of taps in the filter + * + * This sets the number of taps in the band-edge filters. Setting + * this will force a recalculation of the filter taps. + * + * This should be about the same number of taps used in the + * transmitter's shaping filter and also not very large. A large + * number of taps will result in a large delay between input and + * frequency estimation, and so will not be as accurate. Between 30 + * and 70 taps is usual. + * + * \param filter_size (float) number of taps in the filters + * + */ + void set_filter_size(int filter_size); + + /******************************************************************* + GET FUNCTIONS + *******************************************************************/ + + /*! + * \brief Returns the number of sampler per symbol used for the filter + */ + float get_samples_per_symbol() const; + + /*! + * \brief Returns the rolloff factor used for the filter + */ + float get_rolloff() const; + + /*! + * \brief Returns the number of taps of the filter + */ + int get_filter_size() const; + + /*! + * Print the taps to screen. + */ + void print_taps(); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/include/digital_gmskmod_bc.h b/gr-digital/include/digital_gmskmod_bc.h new file mode 100644 index 000000000..4b0952401 --- /dev/null +++ b/gr-digital/include/digital_gmskmod_bc.h @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_GMSKMOD_BC_H +#define INCLUDED_DIGITAL_GMSKMOD_BC_H + +#include <digital_cpmmod_bc.h> + +class digital_gmskmod_bc; +typedef boost::shared_ptr<digital_gmskmod_bc> digital_gmskmod_bc_sptr; + + +digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym=2, + double bt=0.3, unsigned L=4); + +/*! + * \brief GMSK modulator + * + * \ingroup modulation_blk + * \ingroup digital + * + * \param samples_per_sym Samples per symbol. + * \param bt The 3 dB time-bandwidth product. + * \param L The length of the phase duration in symbols. The Gaussian + * pulse is truncated after L symbols. + * + * The input of this block are symbols from an M-ary alphabet + * +/-1, +/-3, ..., +/-(M-1). Usually, M = 2 and therefore, the + * valid inputs are +/-1. + * The modulator will silently accept any other inputs, though. + * The output is the phase-modulated signal. + */ +class digital_gmskmod_bc : public digital_cpmmod_bc +{ + friend digital_gmskmod_bc_sptr digital_make_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L); + digital_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L); +}; + +#endif /* INCLUDED_DIGITAL_GMSKMOD_BC_H */ + diff --git a/gr-digital/include/digital_kurtotic_equalizer_cc.h b/gr-digital/include/digital_kurtotic_equalizer_cc.h new file mode 100644 index 000000000..018a906b0 --- /dev/null +++ b/gr-digital/include/digital_kurtotic_equalizer_cc.h @@ -0,0 +1,112 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H +#define INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H + +#include <gr_adaptive_fir_ccc.h> +#include <gr_math.h> +#include <iostream> + +class digital_kurtotic_equalizer_cc; +typedef boost::shared_ptr<digital_kurtotic_equalizer_cc> digital_kurtotic_equalizer_cc_sptr; + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, float mu); + +/*! + * \brief Implements a kurtosis-based adaptive equalizer on complex stream + * \ingroup eq_blk + * \ingroup digital + * + * Y. Guo, J. Zhao, Y. Sun, "Sign kurtosis maximization based blind + * equalization algorithm," IEEE Conf. on Control, Automation, + * Robotics and Vision, Vol. 3, Dec. 2004, pp. 2052 - 2057. + */ +class digital_kurtotic_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + float d_mu; + float d_p, d_m; + gr_complex d_q, d_u; + float d_alpha_p, d_alpha_q, d_alpha_m; + + friend digital_kurtotic_equalizer_cc_sptr digital_make_kurtotic_equalizer_cc(int num_taps, + float mu); + digital_kurtotic_equalizer_cc(int num_taps, float mu); + + gr_complex sign(gr_complex x) + { + float re = (float)(x.real() >= 0.0f); + float im = (float)(x.imag() >= 0.0f); + return gr_complex(re, im); + } + +protected: + + virtual gr_complex error(const gr_complex &out) + { + + // p = E[|z|^2] + // q = E[z^2] + // m = E[|z|^4] + // u = E[kurtosis(z)] + + float nrm = norm(out); + gr_complex cnj = conj(out); + float epsilon_f = 1e-12; + gr_complex epsilon_c = gr_complex(1e-12, 1e-12); + + + d_p = (1-d_alpha_p)*d_p + (d_alpha_p)*nrm + epsilon_f; + d_q = (1-d_alpha_q)*d_q + (d_alpha_q)*out*out + epsilon_c; + d_m = (1-d_alpha_m)*d_m + (d_alpha_m)*nrm*nrm + epsilon_f; + d_u = d_m - 2.0f*(d_p*d_p) - d_q*d_q; + + gr_complex F = (1.0f / (d_p*d_p*d_p)) * + (sign(d_u) * (nrm*cnj - 2.0f*d_p*cnj - conj(d_q)*out) - + abs(d_u)*cnj); + + //std::cout << "out: " << out << " p: " << d_p << " q: " << d_q; + //std::cout << " m: " << d_m << " u: " << d_u << std::endl; + //std::cout << "error: " << F << std::endl; + + float re = gr_clip(F.real(), 1.0); + float im = gr_clip(F.imag(), 1.0); + return gr_complex(re, im); + } + + virtual void update_tap(gr_complex &tap, const gr_complex &in) + { + tap += d_mu*in*d_error; + } + +public: + void set_gain(float mu) + { + if(mu < 0) + throw std::out_of_range("digital_kurtotic_equalizer::set_gain: Gain value must be >= 0"); + d_mu = mu; + } +}; + +#endif diff --git a/gr-digital/include/digital_lms_dd_equalizer_cc.h b/gr-digital/include/digital_lms_dd_equalizer_cc.h new file mode 100644 index 000000000..050d8781d --- /dev/null +++ b/gr-digital/include/digital_lms_dd_equalizer_cc.h @@ -0,0 +1,117 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_LMS_DD_EQUALIZER_CC_H +#define INCLUDED_DIGITAL_LMS_DD_EQUALIZER_CC_H + +#include <gr_adaptive_fir_ccc.h> +#include <digital_constellation.h> + +class digital_lms_dd_equalizer_cc; +typedef boost::shared_ptr<digital_lms_dd_equalizer_cc> digital_lms_dd_equalizer_cc_sptr; + +digital_lms_dd_equalizer_cc_sptr digital_make_lms_dd_equalizer_cc (int num_taps, + float mu, int sps, + digital_constellation_sptr cnst); + +/*! + * \brief Least-Mean-Square Decision Directed Equalizer (complex in/out) + * \ingroup eq_blk + * \ingroup digital + * + * This block implements an LMS-based decision-directed equalizer. + * It uses a set of weights, w, to correlate against the inputs, u, + * and a decisions is then made from this output. The error + * in the decision is used to update teh weight vector. + * + * y[n] = conj(w[n]) u[n] + * d[n] = decision(y[n]) + * e[n] = d[n] - y[n] + * w[n+1] = w[n] + mu u[n] conj(e[n]) + * + * Where mu is a gain value (between 0 and 1 and usualy small, + * around 0.001 - 0.01. + * + * This block uses the digital_constellation object for making + * the decision from y[n]. Create the constellation object for + * whatever constellation is to be used and pass in the object. + * In Python, you can use something like: + * self.constellation = digital.constellation_qpsk() + * To create a QPSK constellation (see the digital_constellation + * block for more details as to what constellations are available + * or how to create your own). You then pass the object to this + * block as an sptr, or using "self.constellation.base()". + * + * The theory for this algorithm can be found in Chapter 9 of: + * S. Haykin, Adaptive Filter Theory, Upper Saddle River, NJ: + * Prentice Hall, 1996. + * + */ +class digital_lms_dd_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + friend digital_lms_dd_equalizer_cc_sptr digital_make_lms_dd_equalizer_cc (int num_taps, + float mu, int sps, + digital_constellation_sptr cnst); + + float d_mu; + std::vector<gr_complex> d_taps; + digital_constellation_sptr d_cnst; + + digital_lms_dd_equalizer_cc (int num_taps, + float mu, int sps, + digital_constellation_sptr cnst); + +protected: + + virtual gr_complex error(const gr_complex &out) + { + gr_complex decision, error; + d_cnst->map_to_points(d_cnst->decision_maker(&out), &decision); + error = decision - out; + return error; + } + + virtual void update_tap(gr_complex &tap, const gr_complex &in) + { + tap += d_mu*conj(in)*d_error; + } + +public: + float get_gain() + { + return d_mu; + } + + void set_gain(float mu) + { + if(mu < 0.0f || mu > 1.0f) { + throw std::out_of_range("digital_lms_dd_equalizer::set_mu: Gain value must in [0, 1]"); + } + else { + d_mu = mu; + } + } + +}; + +#endif diff --git a/gr-digital/include/digital_metric_type.h b/gr-digital/include/digital_metric_type.h new file mode 100644 index 000000000..83de166f0 --- /dev/null +++ b/gr-digital/include/digital_metric_type.h @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_METRIC_TYPE_H +#define INCLUDED_DIGITAL_METRIC_TYPE_H + +typedef enum { + TRELLIS_EUCLIDEAN = 200, TRELLIS_HARD_SYMBOL, TRELLIS_HARD_BIT +} trellis_metric_type_t; + +#endif + diff --git a/gr-digital/include/digital_mpsk_receiver_cc.h b/gr-digital/include/digital_mpsk_receiver_cc.h new file mode 100644 index 000000000..74aca5649 --- /dev/null +++ b/gr-digital/include/digital_mpsk_receiver_cc.h @@ -0,0 +1,302 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H +#define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H + +#include <gruel/attributes.h> +#include <gri_control_loop.h> +#include <gr_block.h> +#include <gr_complex.h> +#include <fstream> + +class gri_mmse_fir_interpolator_cc; + +class digital_mpsk_receiver_cc; +typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_sptr; + +// public constructor +digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + +/*! + * \brief This block takes care of receiving M-PSK modulated signals + * through phase, frequency, and symbol synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * \ingroup digital + * + * This block takes care of receiving M-PSK modulated signals through + * phase, frequency, and symbol synchronization. It performs carrier + * frequency and phase locking as well as symbol timing recovery. It + * works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It + * should also work for OQPSK and PI/4 DQPSK. + * + * The phase and frequency synchronization are based on a Costas loop + * that finds the error of the incoming signal point compared to its + * nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. There are optimized phase error + * detectors for BPSK and QPSK, but 8PSK is done using a brute-force + * computation of the constellation points to find the minimum. + * + * The symbol synchronization is done using a modified Mueller and + * Muller circuit from the paper: + * + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller + * and Muller algorithm," Electronics Letters, Vol. 31, no. 13, 22 + * June 1995, pp. 1032 - 1033. + * + * This circuit interpolates the downconverted sample (using the NCO + * developed by the Costas loop) every mu samples, then it finds the + * sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are + * optimized decision algorithms for BPSK and QPKS, but 8PSK uses + * another brute force computation against all possible symbols. The + * modifications to the M&M used here reduce self-noise. + * + */ + +class digital_mpsk_receiver_cc : public gr_block, public gri_control_loop +{ + public: + ~digital_mpsk_receiver_cc (); + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + // Member functions related to the symbol tracking portion of the receiver + //! (M&M) Returns current value of mu + float mu() const { return d_mu;} + + //! (M&M) Returns current value of omega + float omega() const { return d_omega;} + + //! (M&M) Returns mu gain factor + float gain_mu() const { return d_gain_mu;} + + //! (M&M) Returns omega gain factor + float gain_omega() const { return d_gain_omega;} + + //! (M&M) Sets value of mu + void set_mu (float mu) { d_mu = mu; } + + //! (M&M) Sets value of omega and its min and max values + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_rel); + d_max_omega = omega*(1.0 + d_omega_rel); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + + //! (M&M) Sets value for mu gain factor + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + + //! (M&M) Sets value for omega gain factor + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param M modulation order of the M-PSK modulation + * \param theta any constant phase rotation from the real axis of the constellation + * \param loop_bw Loop bandwidth to set gains of phase/freq tracking loop + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * \param mu initial parameter for the interpolator [0,1] + * \param gain_mu gain parameter of the M&M error signal to adjust mu (~0.05) + * \param omega initial value for the number of symbols between samples (~number of samples/symbol) + * \param gain_omega gain parameter to adjust omega based on the error (~omega^2/4) + * \param omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005) + * + * The constructor also chooses which phase detector and decision maker to use in the work loop based on the + * value of M. + */ + digital_mpsk_receiver_cc (unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + + void make_constellation(); + void mm_sampler(const gr_complex symbol); + void mm_error_tracking(gr_complex sample); + void phase_error_tracking(gr_complex sample); + + + /*! + * \brief Phase error detector for MPSK modulations. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error for any MPSK signal by + * creating a set of PSK constellation points and doing a + * brute-force search to see which point minimizes the Euclidean + * distance. This point is then used to derotate the sample to the + * real-axis and a atan (using the fast approximation function) to + * determine the phase difference between the incoming sample and + * the real constellation point + * + * This should be cleaned up and made more efficient. + * + * \returns the approximated phase error. + */ + float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly + + /*! + * \brief Phase error detector for BPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using a simple BPSK + * phase error detector by multiplying the real and imaginary (the + * error signal) components together. As the imaginary part goes to + * 0, so does this error. + * + * \returns the approximated phase error. + */ + float phase_error_detector_bpsk(gr_complex sample) const; // optimized for BPSK + + /*! + * \brief Phase error detector for QPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using the limiter + * approach in a standard 4th order Costas loop + * + * \returns the approximated phase error. + */ + float phase_error_detector_qpsk(gr_complex sample) const; + + + + /*! + * \brief Decision maker for a generic MPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a generic implementation that does a + * brute-force search for the constellation point that minimizes the + * error between it and the incoming signal. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_generic(gr_complex sample) const; + + + /*! + * \brief Decision maker for BPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement on the real axis of + * greater than 0 or less than 0; the quadrature component is always + * 0. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_bpsk(gr_complex sample) const; + + + /*! + * \brief Decision maker for QPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement versus both axes + * and returns which quadrant the symbol is in. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_qpsk(gr_complex sample) const; + +private: + unsigned int d_M; + float d_theta; + + /*! + * \brief Decision maker function pointer + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This is a function pointer that is set in the constructor to + * point to the proper decision function for the specified + * constellation order. + * + * \return index into d_constellation point that is the closest to the recieved sample + */ + unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function + + + std::vector<gr_complex> d_constellation; + unsigned int d_current_const_point; + + // Members related to symbol timing + float d_mu, d_gain_mu; + float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid; + gr_complex d_p_2T, d_p_1T, d_p_0T; + gr_complex d_c_2T, d_c_1T, d_c_0T; + + /*! + * \brief Phase error detector function pointer + * + * \param sample the I&Q sample from which to determine the phase error + * + * This is a function pointer that is set in the constructor to + * point to the proper phase error detector function for the + * specified constellation order. + */ + float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const; + + + //! get interpolated value + gri_mmse_fir_interpolator_cc *d_interp; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; + + //! index to delay line + unsigned int d_dl_idx; + + friend digital_mpsk_receiver_cc_sptr + digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +}; + +#endif diff --git a/gr-digital/include/digital_ofdm_cyclic_prefixer.h b/gr-digital/include/digital_ofdm_cyclic_prefixer.h new file mode 100644 index 000000000..a3c32125b --- /dev/null +++ b/gr-digital/include/digital_ofdm_cyclic_prefixer.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_CYCLIC_PREFIXER_H +#define INCLUDED_DIGITAL_OFDM_CYCLIC_PREFIXER_H + +#include <gr_sync_interpolator.h> +#include <stdio.h> + +class digital_ofdm_cyclic_prefixer; +typedef boost::shared_ptr<digital_ofdm_cyclic_prefixer> digital_ofdm_cyclic_prefixer_sptr; + +digital_ofdm_cyclic_prefixer_sptr +digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size); + + +/*! + * \brief adds a cyclic prefix vector to an input size long ofdm + * symbol(vector) and converts vector to a stream output_size long. + * \ingroup ofdm_blk + */ +class digital_ofdm_cyclic_prefixer : public gr_sync_interpolator +{ + friend digital_ofdm_cyclic_prefixer_sptr + digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size); + + protected: + digital_ofdm_cyclic_prefixer (size_t input_size, size_t output_size); + + public: + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + private: + size_t d_input_size; + size_t d_output_size; +}; + +#endif /* INCLUDED_DIGITAL_OFDM_CYCLIC_PREFIXER_H */ diff --git a/gr-digital/include/digital_ofdm_frame_acquisition.h b/gr-digital/include/digital_ofdm_frame_acquisition.h new file mode 100644 index 000000000..971202099 --- /dev/null +++ b/gr-digital/include/digital_ofdm_frame_acquisition.h @@ -0,0 +1,116 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_FRAME_ACQUISITION_H +#define INCLUDED_DIGITAL_OFDM_FRAME_ACQUISITION_H + + +#include <gr_block.h> +#include <vector> + +class digital_ofdm_frame_acquisition; +typedef boost::shared_ptr<digital_ofdm_frame_acquisition> digital_ofdm_frame_acquisition_sptr; + +digital_ofdm_frame_acquisition_sptr +digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len=10); + +/*! + * \brief take a vector of complex constellation points in from an FFT + * and performs a correlation and equalization. + * \ingroup demodulation_blk + * \ingroup ofdm_blk + * + * This block takes the output of an FFT of a received OFDM symbol and finds the + * start of a frame based on two known symbols. It also looks at the surrounding + * bins in the FFT output for the correlation in case there is a large frequency + * shift in the data. This block assumes that the fine frequency shift has already + * been corrected and that the samples fall in the middle of one FFT bin. + * + * It then uses one of those known + * symbols to estimate the channel response over all subcarriers and does a simple + * 1-tap equalization on all subcarriers. This corrects for the phase and amplitude + * distortion caused by the channel. + */ + +class digital_ofdm_frame_acquisition : public gr_block +{ + /*! + * \brief Build an OFDM correlator and equalizer. + * \param occupied_carriers The number of subcarriers with data in the received symbol + * \param fft_length The size of the FFT vector (occupied_carriers + unused carriers) + * \param cplen The length of the cycle prefix + * \param known_symbol A vector of complex numbers representing a known symbol at the + * start of a frame (usually a BPSK PN sequence) + * \param max_fft_shift_len Set's the maximum distance you can look between bins for correlation + */ + friend digital_ofdm_frame_acquisition_sptr + digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); + +protected: + digital_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); + + private: + unsigned char slicer(gr_complex x); + void correlate(const gr_complex *symbol, int zeros_on_left); + void calculate_equalizer(const gr_complex *symbol, int zeros_on_left); + gr_complex coarse_freq_comp(int freq_delta, int count); + + unsigned int d_occupied_carriers; // !< \brief number of subcarriers with data + unsigned int d_fft_length; // !< \brief length of FFT vector + unsigned int d_cplen; // !< \brief length of cyclic prefix in samples + unsigned int d_freq_shift_len; // !< \brief number of surrounding bins to look at for correlation + std::vector<gr_complex> d_known_symbol; // !< \brief known symbols at start of frame + std::vector<float> d_known_phase_diff; // !< \brief factor used in correlation from known symbol + std::vector<float> d_symbol_phase_diff; // !< \brief factor used in correlation from received symbol + std::vector<gr_complex> d_hestimate; // !< channel estimate + int d_coarse_freq; // !< \brief search distance in number of bins + unsigned int d_phase_count; // !< \brief accumulator for coarse freq correction + float d_snr_est; // !< an estimation of the signal to noise ratio + + gr_complex *d_phase_lut; // !< look-up table for coarse frequency compensation + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + public: + /*! + * \brief Return an estimate of the SNR of the channel + */ + float snr() { return d_snr_est; } + + ~digital_ofdm_frame_acquisition(void); + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + + +#endif diff --git a/gr-digital/include/digital_ofdm_frame_sink.h b/gr-digital/include/digital_ofdm_frame_sink.h new file mode 100644 index 000000000..3575ee35b --- /dev/null +++ b/gr-digital/include/digital_ofdm_frame_sink.h @@ -0,0 +1,126 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_FRAME_SINK_H +#define INCLUDED_DIGITAL_OFDM_FRAME_SINK_H + +#include <gr_sync_block.h> +#include <gr_msg_queue.h> + +class digital_ofdm_frame_sink; +typedef boost::shared_ptr<digital_ofdm_frame_sink> digital_ofdm_frame_sink_sptr; + +digital_ofdm_frame_sink_sptr +digital_make_ofdm_frame_sink (const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain=0.25, float freq_gain=0.25*0.25/4.0); + +/*! + * \brief Takes an OFDM symbol in, demaps it into bits of 0's and 1's, packs + * them into packets, and sends to to a message queue sink. + * \ingroup sink_blk + * \ingroup ofdm_blk + * + * NOTE: The mod input parameter simply chooses a pre-defined demapper/slicer. Eventually, + * we want to be able to pass in a reference to an object to do the demapping and slicing + * for a given modulation type. + */ +class digital_ofdm_frame_sink : public gr_sync_block +{ + friend digital_ofdm_frame_sink_sptr + digital_make_ofdm_frame_sink (const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); + + private: + enum state_t {STATE_SYNC_SEARCH, STATE_HAVE_SYNC, STATE_HAVE_HEADER}; + + static const int MAX_PKT_LEN = 4096; + static const int HEADERBYTELEN = 4; + + gr_msg_queue_sptr d_target_queue; // where to send the packet when received + state_t d_state; + unsigned int d_header; // header bits + int d_headerbytelen_cnt; // how many so far + + unsigned char *d_bytes_out; // hold the current bytes produced by the demapper + + unsigned int d_occupied_carriers; + unsigned int d_byte_offset; + unsigned int d_partial_byte; + + unsigned char d_packet[MAX_PKT_LEN]; // assembled payload + int d_packetlen; // length of packet + int d_packet_whitener_offset; // offset into whitener string to use + int d_packetlen_cnt; // how many so far + + gr_complex * d_derotated_output; // Pointer to output stream to send deroated symbols out + + std::vector<gr_complex> d_sym_position; + std::vector<unsigned char> d_sym_value_out; + std::vector<gr_complex> d_dfe; + unsigned int d_nbits; + + unsigned char d_resid; + unsigned int d_nresid; + float d_phase; + float d_freq; + float d_phase_gain; + float d_freq_gain; + float d_eq_gain; + + std::vector<int> d_subcarrier_map; + + protected: + digital_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); + + void enter_search(); + void enter_have_sync(); + void enter_have_header(); + + bool header_ok() + { + // confirm that two copies of header info are identical + return ((d_header >> 16) ^ (d_header & 0xffff)) == 0; + } + + unsigned char slicer(const gr_complex x); + unsigned int demapper(const gr_complex *in, + unsigned char *out); + + bool set_sym_value_out(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out); + + public: + ~digital_ofdm_frame_sink(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_OFDM_FRAME_SINK_H */ diff --git a/gr-digital/include/digital_ofdm_insert_preamble.h b/gr-digital/include/digital_ofdm_insert_preamble.h new file mode 100644 index 000000000..a7d87d42f --- /dev/null +++ b/gr-digital/include/digital_ofdm_insert_preamble.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_INSERT_PREAMBLE_H +#define INCLUDED_DIGITAL_OFDM_INSERT_PREAMBLE_H + +#include <gr_block.h> +#include <vector> + +class digital_ofdm_insert_preamble; +typedef boost::shared_ptr<digital_ofdm_insert_preamble> digital_ofdm_insert_preamble_sptr; + +digital_ofdm_insert_preamble_sptr +digital_make_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble); + +/*! + * \brief insert "pre-modulated" preamble symbols before each payload. + * \ingroup sync_blk + * \ingroup ofdm_blk + * + * <pre> + * input 1: stream of vectors of gr_complex [fft_length] + * These are the modulated symbols of the payload. + * + * input 2: stream of char. The LSB indicates whether the corresponding + * symbol on input 1 is the first symbol of the payload or not. + * It's a 1 if the corresponding symbol is the first symbol, + * otherwise 0. + * + * N.B., this implies that there must be at least 1 symbol in the payload. + * + * + * output 1: stream of vectors of gr_complex [fft_length] + * These include the preamble symbols and the payload symbols. + * + * output 2: stream of char. The LSB indicates whether the corresponding + * symbol on input 1 is the first symbol of a packet (i.e., the + * first symbol of the preamble.) It's a 1 if the corresponding + * symbol is the first symbol, otherwise 0. + * </pre> + * + * \param fft_length length of each symbol in samples. + * \param preamble vector of symbols that represent the pre-modulated preamble. + */ + +class digital_ofdm_insert_preamble : public gr_block +{ + friend digital_ofdm_insert_preamble_sptr + digital_make_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble); + +protected: + digital_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble); + +private: + enum state_t { + ST_IDLE, + ST_PREAMBLE, + ST_FIRST_PAYLOAD, + ST_PAYLOAD + }; + + int d_fft_length; + const std::vector<std::vector<gr_complex> > d_preamble; + state_t d_state; + int d_nsymbols_output; + int d_pending_flag; + + void enter_idle(); + void enter_preamble(); + void enter_first_payload(); + void enter_payload(); + + +public: + ~digital_ofdm_insert_preamble(); + + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_DIGITAL_OFDM_INSERT_PREAMBLE_H */ diff --git a/gr-digital/include/digital_ofdm_mapper_bcv.h b/gr-digital/include/digital_ofdm_mapper_bcv.h new file mode 100644 index 000000000..e9b47eb9d --- /dev/null +++ b/gr-digital/include/digital_ofdm_mapper_bcv.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_MAPPER_BCV_H +#define INCLUDED_DIGITAL_OFDM_MAPPER_BCV_H + +#include <gr_sync_block.h> +#include <gr_message.h> +#include <gr_msg_queue.h> + +class digital_ofdm_mapper_bcv; +typedef boost::shared_ptr<digital_ofdm_mapper_bcv> digital_ofdm_mapper_bcv_sptr; + +digital_ofdm_mapper_bcv_sptr +digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned msgq_limit, + unsigned occupied_carriers, unsigned int fft_length); + +/*! + * \brief take a stream of bytes in and map to a vector of complex + * constellation points suitable for IFFT input to be used in an ofdm + * modulator. Abstract class must be subclassed with specific mapping. + * \ingroup modulation_blk + * \ingroup ofdm_blk + */ + +class digital_ofdm_mapper_bcv : public gr_sync_block +{ + friend digital_ofdm_mapper_bcv_sptr + digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned msgq_limit, + unsigned occupied_carriers, unsigned int fft_length); +protected: + digital_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned msgq_limit, + unsigned occupied_carriers, unsigned int fft_length); + + private: + std::vector<gr_complex> d_constellation; + gr_msg_queue_sptr d_msgq; + gr_message_sptr d_msg; + unsigned d_msg_offset; + bool d_eof; + + unsigned int d_occupied_carriers; + unsigned int d_fft_length; + unsigned int d_bit_offset; + int d_pending_flag; + + unsigned long d_nbits; + unsigned char d_msgbytes; + + unsigned char d_resid; + unsigned int d_nresid; + + std::vector<int> d_subcarrier_map; + + int randsym(); + + public: + ~digital_ofdm_mapper_bcv(void); + + gr_msg_queue_sptr msgq() const { return d_msgq; } + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +}; + +#endif diff --git a/gr-digital/include/digital_ofdm_sampler.h b/gr-digital/include/digital_ofdm_sampler.h new file mode 100644 index 000000000..01aa40d46 --- /dev/null +++ b/gr-digital/include/digital_ofdm_sampler.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_OFDM_SAMPLER_H +#define INCLUDED_DIGITAL_OFDM_SAMPLER_H + +#include <gr_sync_block.h> + +class digital_ofdm_sampler; +typedef boost::shared_ptr<digital_ofdm_sampler> digital_ofdm_sampler_sptr; + +digital_ofdm_sampler_sptr digital_make_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout=1000); + +/*! + * \brief does the rest of the OFDM stuff + * \ingroup ofdm_blk + */ +class digital_ofdm_sampler : public gr_block +{ + friend digital_ofdm_sampler_sptr digital_make_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout); + + digital_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout); + + private: + enum state_t {STATE_NO_SIG, STATE_PREAMBLE, STATE_FRAME}; + + state_t d_state; + unsigned int d_timeout_max; + unsigned int d_timeout; + unsigned int d_fft_length; + unsigned int d_symbol_length; + + public: + void forecast (int noutput_items, gr_vector_int &ninput_items_required); + + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gr-digital/lib/.gitignore b/gr-digital/lib/.gitignore new file mode 100644 index 000000000..1b6114c39 --- /dev/null +++ b/gr-digital/lib/.gitignore @@ -0,0 +1,4 @@ +/.libs +/.deps +/Makefile +/Makefile.in diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am new file mode 100644 index 000000000..2860974ca --- /dev/null +++ b/gr-digital/lib/Makefile.am @@ -0,0 +1,56 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) \ + $(GR_DIGITAL_INCLUDES) $(WITH_INCLUDES) + +lib_LTLIBRARIES = libgnuradio-digital.la + +libgnuradio_digital_la_SOURCES = \ + digital_binary_slicer_fb.cc \ + digital_clock_recovery_mm_cc.cc \ + digital_clock_recovery_mm_ff.cc \ + digital_constellation.cc \ + digital_constellation_receiver_cb.cc \ + digital_constellation_decoder_cb.cc \ + digital_correlate_access_code_bb.cc \ + digital_costas_loop_cc.cc \ + digital_cma_equalizer_cc.cc \ + digital_crc32.cc \ + digital_fll_band_edge_cc.cc \ + digital_lms_dd_equalizer_cc.cc \ + digital_kurtotic_equalizer_cc.cc \ + digital_mpsk_receiver_cc.cc \ + digital_ofdm_cyclic_prefixer.cc \ + digital_ofdm_frame_acquisition.cc \ + digital_ofdm_frame_sink.cc \ + digital_ofdm_insert_preamble.cc \ + digital_ofdm_mapper_bcv.cc \ + digital_ofdm_sampler.cc \ + digital_gmskmod_bc.cc \ + digital_cpmmod_bc.cc + +libgnuradio_digital_la_LIBADD = \ + $(GNURADIO_CORE_LA) + +libgnuradio_digital_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS) diff --git a/gr-digital/lib/digital_binary_slicer_fb.cc b/gr-digital/lib/digital_binary_slicer_fb.cc new file mode 100644 index 000000000..fcdb4291f --- /dev/null +++ b/gr-digital/lib/digital_binary_slicer_fb.cc @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_binary_slicer_fb.h> +#include <gr_io_signature.h> +#include <gr_math.h> +#include <stdexcept> + +digital_binary_slicer_fb_sptr +digital_make_binary_slicer_fb () +{ + return gnuradio::get_initial_sptr(new digital_binary_slicer_fb ()); +} + +digital_binary_slicer_fb::digital_binary_slicer_fb () + : gr_sync_block ("binary_slicer_fb", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (unsigned char))) +{ +} + +int +digital_binary_slicer_fb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + + for (int i = 0; i < noutput_items; i++){ + out[i] = gr_binary_slicer(in[i]); + } + + return noutput_items; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_cc.cc b/gr-digital/lib/digital_clock_recovery_mm_cc.cc new file mode 100644 index 000000000..198eb4b89 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_cc.cc @@ -0,0 +1,217 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_clock_recovery_mm_cc.h> +#include <gri_mmse_fir_interpolator_cc.h> +#include <stdexcept> +#include <cstdio> + + +// Public constructor +static const int FUDGE = 16; + +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_cc (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_cc::digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature2 (1, 2, sizeof (gr_complex), sizeof(float))), + d_mu (mu), d_omega(omega), d_gain_omega(gain_omega), + d_omega_relative_limit(omega_relative_limit), + d_gain_mu(gain_mu), d_last_sample(0), d_interp(new gri_mmse_fir_interpolator_cc()), + d_verbose(gr_prefs::singleton()->get_bool("clock_recovery_mm_cc", "verbose", false)), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + set_history(3); // ensure 2 extra input sample is available +} + +digital_clock_recovery_mm_cc::~digital_clock_recovery_mm_cc () +{ + delete d_interp; +} + +void +digital_clock_recovery_mm_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()) + FUDGE; +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_0deg (gr_complex sample) +{ + float real=0, imag=0; + + if(sample.real() > 0) + real = 1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_45deg (gr_complex sample) +{ + float real= -1, imag = -1; + if(sample.real() > 0) + real=1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +/* + Modified Mueller and Muller clock recovery circuit + Based: + G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. +*/ + +int +digital_clock_recovery_mm_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + float *foptr = (float *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps() - FUDGE; // don't use more input than this + + assert(d_mu >= 0.0); + assert(d_mu <= 1.0); + + float mm_val=0; + gr_complex u, x, y; + + // This loop writes the error to the second output, if it exists + if (write_foptr) { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,4.0); + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + // write the error signal to the second output + foptr[oo-1] = mm_val; + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + // This loop does not write to the second output (ugly, but faster) + else { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,1.0); + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + if(d_verbose) { + printf("%f\t%f\n", d_omega, d_mu); + } + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + + if (ii > 0){ + if (ii > ninput_items[0]){ + fprintf(stderr, "gr_clock_recovery_mm_cc: ii > ninput_items[0] (%d > %d)\n", + ii, ninput_items[0]); + assert(0); + } + consume_each (ii); + } + + return oo; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_ff.cc b/gr-digital/lib/digital_clock_recovery_mm_ff.cc new file mode 100644 index 000000000..04057f0e9 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_ff.cc @@ -0,0 +1,139 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <digital_clock_recovery_mm_ff.h> +#include <gri_mmse_fir_interpolator.h> +#include <stdexcept> + +#define DEBUG_CR_MM_FF 0 // must be defined as 0 or 1 + +// Public constructor + +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_ff (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_ff::digital_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_ff", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (float))), + d_mu (mu), d_gain_omega(gain_omega), d_gain_mu(gain_mu), + d_last_sample(0), d_interp(new gri_mmse_fir_interpolator()), + d_logfile(0), d_omega_relative_limit(omega_relative_limit) +{ + if (omega < 1) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + + if (DEBUG_CR_MM_FF) + d_logfile = fopen("cr_mm_ff.dat", "wb"); +} + +digital_clock_recovery_mm_ff::~digital_clock_recovery_mm_ff () +{ + delete d_interp; + + if (DEBUG_CR_MM_FF && d_logfile){ + fclose(d_logfile); + d_logfile = 0; + } +} + +void +digital_clock_recovery_mm_ff::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +static inline float +slice(float x) +{ + return x < 0 ? -1.0F : 1.0F; +} + +/* + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * + * See "Digital Communication Receivers: Synchronization, Channel + * Estimation and Signal Processing" by Heinrich Meyr, Marc Moeneclaey, & Stefan Fechtel. + * ISBN 0-471-50275-8. + */ +int +digital_clock_recovery_mm_ff::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps(); // don't use more input than this + float mm_val; + + while (oo < noutput_items && ii < ni ){ + + // produce output sample + out[oo] = d_interp->interpolate (&in[ii], d_mu); + mm_val = slice(d_last_sample) * out[oo] - slice(out[oo]) * d_last_sample; + d_last_sample = out[oo]; + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + + ii += (int) floor(d_mu); + d_mu = d_mu - floor(d_mu); + oo++; + + if (DEBUG_CR_MM_FF && d_logfile){ + fwrite(&d_omega, sizeof(d_omega), 1, d_logfile); + } + } + + consume_each (ii); + + return oo; +} diff --git a/gr-digital/lib/digital_cma_equalizer_cc.cc b/gr-digital/lib/digital_cma_equalizer_cc.cc new file mode 100644 index 000000000..c6c46c2d8 --- /dev/null +++ b/gr-digital/lib/digital_cma_equalizer_cc.cc @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cma_equalizer_cc.h> +#include <cstdio> + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps) +{ + return gnuradio::get_initial_sptr(new digital_cma_equalizer_cc(num_taps, modulus, + mu, sps)); +} + +digital_cma_equalizer_cc::digital_cma_equalizer_cc(int num_taps, float modulus, + float mu, int sps) + : gr_adaptive_fir_ccc("cma_equalizer_cc", sps, + std::vector<gr_complex>(num_taps, gr_complex(0,0))) +{ + set_modulus(modulus); + set_gain(mu); + if (num_taps > 0) + d_taps[0] = 1.0; +} diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc new file mode 100644 index 000000000..0c100f38e --- /dev/null +++ b/gr-digital/lib/digital_constellation.cc @@ -0,0 +1,554 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <digital_constellation.h> +#include <digital_metric_type.h> +#include <gr_math.h> +#include <gr_complex.h> +#include <math.h> +#include <iostream> +#include <stdlib.h> +#include <float.h> +#include <stdexcept> + +#define M_TWOPI (2*M_PI) +#define SQRT_TWO 0.707107 + +// Base Constellation Class + +digital_constellation::digital_constellation (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) : + d_constellation(constellation), + d_pre_diff_code(pre_diff_code), + d_rotational_symmetry(rotational_symmetry), + d_dimensionality(dimensionality) +{ + if (pre_diff_code.size() == 0) + d_apply_pre_diff_code = false; + else if (pre_diff_code.size() != constellation.size()) + throw std::runtime_error ("The constellation and pre-diff code must be of the same length."); + else + d_apply_pre_diff_code = true; + calc_arity(); +} + +digital_constellation::digital_constellation () : + d_apply_pre_diff_code(false), + d_rotational_symmetry(0), + d_dimensionality(1) +{ + calc_arity(); +} + +//! Returns the constellation points for a symbol value +void +digital_constellation::map_to_points(unsigned int value, gr_complex *points) +{ + for (unsigned int i=0; i<d_dimensionality; i++) + points[i] = d_constellation[value*d_dimensionality + i]; +} + +std::vector<gr_complex> +digital_constellation::map_to_points_v(unsigned int value) +{ + std::vector<gr_complex> points_v; + points_v.resize(d_dimensionality); + map_to_points(value, &(points_v[0])); + return points_v; +} + +float +digital_constellation::get_distance(unsigned int index, const gr_complex *sample) +{ + float dist = 0; + for (unsigned int i=0; i<d_dimensionality; i++) { + dist += norm(sample[i] - d_constellation[index*d_dimensionality + i]); + } + return dist; +} + +unsigned int +digital_constellation::get_closest_point(const gr_complex *sample) +{ + unsigned int min_index = 0; + float min_euclid_dist; + float euclid_dist; + + min_euclid_dist = get_distance(0, sample); + min_index = 0; + for (unsigned int j = 1; j < d_arity; j++){ + euclid_dist = get_distance(j, sample); + if (euclid_dist < min_euclid_dist){ + min_euclid_dist = euclid_dist; + min_index = j; + } + } + return min_index; +} + +unsigned int +digital_constellation::decision_maker_pe(const gr_complex *sample, float *phase_error) +{ + unsigned int index = decision_maker(sample); + *phase_error = 0; + for (unsigned int d=0; d<d_dimensionality; d++) + *phase_error += -arg(sample[d]*conj(d_constellation[index+d])); + return index; +} + +/* +unsigned int digital_constellation::decision_maker_e(const gr_complex *sample, float *error) +{ + unsigned int index = decision_maker(sample); + *error = 0; + for (unsigned int d=0; d<d_dimensionality; d++) + *error += sample[d]*conj(d_constellation[index+d]); + return index; +} +*/ + +std::vector<gr_complex> digital_constellation::s_points () { + if (d_dimensionality != 1) + throw std::runtime_error ("s_points only works for dimensionality 1 constellations."); + else + return d_constellation; +} + +std::vector<std::vector<gr_complex> > +digital_constellation::v_points () +{ + std::vector<std::vector<gr_complex> > vv_const; + vv_const.resize(d_arity); + for (unsigned int p=0; p<d_arity; p++) { + std::vector<gr_complex> v_const; + v_const.resize(d_dimensionality); + for (unsigned int d=0; d<d_dimensionality; d++) { + v_const[d] = d_constellation[p*d_dimensionality+d]; + } + vv_const[p] = v_const; + } + return vv_const; +} + +void +digital_constellation::calc_metric(const gr_complex *sample, float *metric, + trellis_metric_type_t type) +{ + switch (type){ + case TRELLIS_EUCLIDEAN: + calc_euclidean_metric(sample, metric); + break; + case TRELLIS_HARD_SYMBOL: + calc_hard_symbol_metric(sample, metric); + break; + case TRELLIS_HARD_BIT: + throw std::runtime_error ("Invalid metric type (not yet implemented)."); + break; + default: + throw std::runtime_error ("Invalid metric type."); + } +} + +void +digital_constellation::calc_euclidean_metric(const gr_complex *sample, float *metric) +{ + for (unsigned int o=0; o<d_arity; o++) { + metric[o] = get_distance(o, sample); + } +} + +void +digital_constellation::calc_hard_symbol_metric(const gr_complex *sample, float *metric) +{ + float minm = FLT_MAX; + unsigned int minmi = 0; + for (unsigned int o=0; o<d_arity; o++) { + float dist = get_distance(o, sample); + if (dist < minm) { + minm = dist; + minmi = o; + } + } + for(unsigned int o=0; o<d_arity; o++) { + metric[o] = (o==minmi?0.0:1.0); + } +} + +void +digital_constellation::calc_arity () +{ + if (d_constellation.size() % d_dimensionality != 0) + throw std::runtime_error ("Constellation vector size must be a multiple of the dimensionality."); + d_arity = d_constellation.size()/d_dimensionality; +} + +unsigned int +digital_constellation::decision_maker_v (std::vector<gr_complex> sample) +{ + assert(sample.size() == d_dimensionality); + return decision_maker (&(sample[0])); +} + +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) +{ + return digital_constellation_calcdist_sptr(new digital_constellation_calcdist + (constellation, pre_diff_code, + rotational_symmetry, dimensionality)); +} + +digital_constellation_calcdist::digital_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) : + digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality) +{} + +// Chooses points base on shortest distance. +// Inefficient. +unsigned int +digital_constellation_calcdist::decision_maker(const gr_complex *sample) +{ + return get_closest_point(sample); +} + +digital_constellation_sector::digital_constellation_sector (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality, + unsigned int n_sectors) : + digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality), + n_sectors(n_sectors) +{ +} + +unsigned int +digital_constellation_sector::decision_maker (const gr_complex *sample) +{ + unsigned int sector; + sector = get_sector(sample); + return sector_values[sector]; +} + +void +digital_constellation_sector::find_sector_values () +{ + unsigned int i; + sector_values.clear(); + for (i=0; i<n_sectors; i++) { + sector_values.push_back(calc_sector_value(i)); + } +} + +digital_constellation_rect_sptr +digital_make_constellation_rect(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) +{ + return digital_constellation_rect_sptr(new digital_constellation_rect + (constellation, pre_diff_code, + rotational_symmetry, + real_sectors, imag_sectors, + width_real_sectors, + width_imag_sectors)); + } + +digital_constellation_rect::digital_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) : + digital_constellation_sector(constellation, pre_diff_code, rotational_symmetry, 1, real_sectors * imag_sectors), + n_real_sectors(real_sectors), n_imag_sectors(imag_sectors), + d_width_real_sectors(width_real_sectors), d_width_imag_sectors(width_imag_sectors) +{ + find_sector_values(); +} + +unsigned int +digital_constellation_rect::get_sector (const gr_complex *sample) +{ + int real_sector, imag_sector; + unsigned int sector; + + real_sector = int(real(*sample)/d_width_real_sectors + n_real_sectors/2.0); + if(real_sector < 0) + real_sector = 0; + if(real_sector >= (int)n_real_sectors) + real_sector = n_real_sectors-1; + + imag_sector = int(imag(*sample)/d_width_imag_sectors + n_imag_sectors/2.0); + if(imag_sector < 0) + imag_sector = 0; + if(imag_sector >= (int)n_imag_sectors) + imag_sector = n_imag_sectors-1; + + sector = real_sector * n_imag_sectors + imag_sector; + return sector; +} + +unsigned int +digital_constellation_rect::calc_sector_value (unsigned int sector) +{ + unsigned int real_sector, imag_sector; + gr_complex sector_center; + unsigned int closest_point; + real_sector = float(sector)/n_imag_sectors; + imag_sector = sector - real_sector * n_imag_sectors; + sector_center = gr_complex((real_sector + 0.5 - n_real_sectors/2.0) * d_width_real_sectors, + (imag_sector + 0.5 - n_imag_sectors/2.0) * d_width_imag_sectors); + closest_point = get_closest_point(§or_center); + return closest_point; +} + + +digital_constellation_psk_sptr +digital_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) +{ + return digital_constellation_psk_sptr(new digital_constellation_psk + (constellation, pre_diff_code, + n_sectors)); +} + +digital_constellation_psk::digital_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) : + digital_constellation_sector(constellation, pre_diff_code, constellation.size(), 1, n_sectors) +{ + find_sector_values(); +} + +unsigned int +digital_constellation_psk::get_sector (const gr_complex *sample) +{ + float phase = arg(*sample); + float width = M_TWOPI / n_sectors; + int sector = floor(phase/width + 0.5); + unsigned int u_sector; + if (sector < 0) + sector += n_sectors; + u_sector = sector; + return sector; +} + +unsigned int +digital_constellation_psk::calc_sector_value (unsigned int sector) +{ + float phase = sector * M_TWOPI / n_sectors; + gr_complex sector_center = gr_complex(cos(phase), sin(phase)); + unsigned int closest_point = get_closest_point(§or_center); + return closest_point; +} + + +digital_constellation_bpsk_sptr +digital_make_constellation_bpsk() +{ + return digital_constellation_bpsk_sptr(new digital_constellation_bpsk ()); +} + +digital_constellation_bpsk::digital_constellation_bpsk () +{ + d_constellation.resize(2); + d_constellation[0] = gr_complex(-1, 0); + d_constellation[1] = gr_complex(1, 0); + d_rotational_symmetry = 2; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_bpsk::decision_maker(const gr_complex *sample) +{ + return (real(*sample) > 0); +} + + +digital_constellation_qpsk_sptr +digital_make_constellation_qpsk() +{ + return digital_constellation_qpsk_sptr(new digital_constellation_qpsk ()); +} + +digital_constellation_qpsk::digital_constellation_qpsk () +{ + d_constellation.resize(4); + // Gray-coded + d_constellation[0] = gr_complex(-SQRT_TWO, -SQRT_TWO); + d_constellation[1] = gr_complex(SQRT_TWO, -SQRT_TWO); + d_constellation[2] = gr_complex(-SQRT_TWO, SQRT_TWO); + d_constellation[3] = gr_complex(SQRT_TWO, SQRT_TWO); + + /* + d_constellation[0] = gr_complex(SQRT_TWO, SQRT_TWO); + d_constellation[1] = gr_complex(-SQRT_TWO, SQRT_TWO); + d_constellation[2] = gr_complex(SQRT_TWO, -SQRT_TWO); + d_constellation[3] = gr_complex(SQRT_TWO, -SQRT_TWO); + */ + + d_pre_diff_code.resize(4); + d_pre_diff_code[0] = 0x0; + d_pre_diff_code[1] = 0x2; + d_pre_diff_code[2] = 0x3; + d_pre_diff_code[3] = 0x1; + + d_rotational_symmetry = 4; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_qpsk::decision_maker(const gr_complex *sample) +{ + // Real component determines small bit. + // Imag component determines big bit. + return 2*(imag(*sample)>0) + (real(*sample)>0); + + /* + bool a = real(*sample) > 0; + bool b = imag(*sample) > 0; + if(a) { + if(b) + return 0x0; + else + return 0x1; + } + else { + if(b) + return 0x2; + else + return 0x3; + } + */ +} + + +/********************************************************************/ + + +digital_constellation_dqpsk_sptr +digital_make_constellation_dqpsk() +{ + return digital_constellation_dqpsk_sptr(new digital_constellation_dqpsk ()); +} + +digital_constellation_dqpsk::digital_constellation_dqpsk () +{ + // This constellation is not gray coded, which allows + // us to use differential encodings (through gr_diff_encode and + // gr_diff_decode) on the symbols. + d_constellation.resize(4); + d_constellation[0] = gr_complex(+SQRT_TWO, +SQRT_TWO); + d_constellation[1] = gr_complex(-SQRT_TWO, +SQRT_TWO); + d_constellation[2] = gr_complex(-SQRT_TWO, -SQRT_TWO); + d_constellation[3] = gr_complex(+SQRT_TWO, -SQRT_TWO); + + // Use this mapping to convert to gray code before diff enc. + d_pre_diff_code.resize(4); + d_pre_diff_code[0] = 0x0; + d_pre_diff_code[1] = 0x1; + d_pre_diff_code[2] = 0x3; + d_pre_diff_code[3] = 0x2; + d_apply_pre_diff_code = true; + + d_rotational_symmetry = 4; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_dqpsk::decision_maker(const gr_complex *sample) +{ + // Slower deicison maker as we can't slice along one axis. + // Maybe there's a better way to do this, still. + + bool a = real(*sample) > 0; + bool b = imag(*sample) > 0; + if(a) { + if(b) + return 0x0; + else + return 0x3; + } + else { + if(b) + return 0x1; + else + return 0x2; + } +} + +digital_constellation_8psk_sptr +digital_make_constellation_8psk() +{ + return digital_constellation_8psk_sptr(new digital_constellation_8psk ()); +} + +digital_constellation_8psk::digital_constellation_8psk () +{ + float angle = M_PI/8.0; + d_constellation.resize(8); + // Gray-coded + d_constellation[0] = gr_complex(cos( 1*angle), sin( 1*angle)); + d_constellation[1] = gr_complex(cos( 7*angle), sin( 7*angle)); + d_constellation[2] = gr_complex(cos(15*angle), sin(15*angle)); + d_constellation[3] = gr_complex(cos( 9*angle), sin( 9*angle)); + d_constellation[4] = gr_complex(cos( 3*angle), sin( 3*angle)); + d_constellation[5] = gr_complex(cos( 5*angle), sin( 5*angle)); + d_constellation[6] = gr_complex(cos(13*angle), sin(13*angle)); + d_constellation[7] = gr_complex(cos(11*angle), sin(11*angle)); + d_rotational_symmetry = 8; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_8psk::decision_maker(const gr_complex *sample) +{ + unsigned int ret = 0; + + float re = sample->real(); + float im = sample->imag(); + + if(fabsf(re) <= fabsf(im)) + ret = 4; + if(re <= 0) + ret |= 1; + if(im <= 0) + ret |= 2; + + return ret; +} diff --git a/gr-digital/lib/digital_constellation_decoder_cb.cc b/gr-digital/lib/digital_constellation_decoder_cb.cc new file mode 100644 index 000000000..4638790f6 --- /dev/null +++ b/gr-digital/lib/digital_constellation_decoder_cb.cc @@ -0,0 +1,77 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_constellation_decoder_cb.h> +#include <digital_constellation.h> +#include <gr_io_signature.h> +#include <iostream> + +digital_constellation_decoder_cb_sptr +digital_make_constellation_decoder_cb (digital_constellation_sptr constellation) +{ + return gnuradio::get_initial_sptr + (new digital_constellation_decoder_cb(constellation)); +} + +digital_constellation_decoder_cb:: +digital_constellation_decoder_cb (digital_constellation_sptr constellation) + : gr_block ("constellation_decoder_cb", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 1, sizeof (unsigned char))), + d_constellation(constellation), + d_dim(constellation->dimensionality()) +{ + set_relative_rate (1.0 / ((double) d_dim)); +} + +void +digital_constellation_decoder_cb::forecast (int noutput_items, + gr_vector_int &ninput_items_required) +{ + unsigned int input_required = noutput_items * d_dim; + + unsigned ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) + ninput_items_required[i] = input_required; +} + + +int +digital_constellation_decoder_cb::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex const *in = (const gr_complex *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for(int i = 0; i < noutput_items; i++){ + out[i] = d_constellation->decision_maker(&(in[i*d_dim])); + } + + consume_each (noutput_items * d_dim); + return noutput_items; +} diff --git a/gr-digital/lib/digital_constellation_receiver_cb.cc b/gr-digital/lib/digital_constellation_receiver_cb.cc new file mode 100644 index 000000000..b9239962a --- /dev/null +++ b/gr-digital/lib/digital_constellation_receiver_cb.cc @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_constellation_receiver_cb.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +digital_constellation_receiver_cb_sptr +digital_make_constellation_receiver_cb(digital_constellation_sptr constell, + float loop_bw, float fmin, float fmax) +{ + return gnuradio::get_initial_sptr(new digital_constellation_receiver_cb (constell, + loop_bw, + fmin, fmax)); +} + +static int ios[] = {sizeof(char), sizeof(float), sizeof(float), sizeof(float)}; +static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); +digital_constellation_receiver_cb::digital_constellation_receiver_cb (digital_constellation_sptr constellation, + float loop_bw, float fmin, float fmax) + : gr_block ("constellation_receiver_cb", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signaturev (1, 4, iosig)), + gri_control_loop(loop_bw, fmax, fmin), + d_constellation(constellation), + d_current_const_point(0) +{ + if (d_constellation->dimensionality() != 1) + throw std::runtime_error ("This receiver only works with constellations of dimension 1."); +} + +void +digital_constellation_receiver_cb::phase_error_tracking(float phase_error) +{ + advance_loop(phase_error); + phase_wrap(); + frequency_limit(); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation->points()[d_current_const_point].real(), + d_constellation->points()[d_current_const_point].imag()); +#endif +} + +int +digital_constellation_receiver_cb::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + int i=0; + + float phase_error; + unsigned int sym_value; + gr_complex sample, nco; + + float *out_err = 0, *out_phase = 0, *out_freq = 0; + if(output_items.size() == 4) { + out_err = (float *) output_items[1]; + out_phase = (float *) output_items[2]; + out_freq = (float *) output_items[3]; + } + + while((i < noutput_items) && (i < ninput_items[0])) { + sample = in[i]; + nco = gr_expj(d_phase); // get the NCO value for derotating the current sample + sample = nco*sample; // get the downconverted symbol + + sym_value = d_constellation->decision_maker_pe(&sample, &phase_error); + phase_error_tracking(phase_error); // corrects phase and frequency offsets + + out[i] = sym_value; + + if(output_items.size() == 4) { + out_err[i] = phase_error; + out_phase[i] = d_phase; + out_freq[i] = d_freq; + } + i++; + } + + consume_each(i); + return i; +} + diff --git a/gr-digital/lib/digital_correlate_access_code_bb.cc b/gr-digital/lib/digital_correlate_access_code_bb.cc new file mode 100644 index 000000000..f21b57d92 --- /dev/null +++ b/gr-digital/lib/digital_correlate_access_code_bb.cc @@ -0,0 +1,134 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_correlate_access_code_bb.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <gr_count_bits.h> +#include <cstdio> + + +#define VERBOSE 0 + + +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold) +{ + return gnuradio::get_initial_sptr(new digital_correlate_access_code_bb + (access_code, threshold)); +} + + +digital_correlate_access_code_bb::digital_correlate_access_code_bb ( + const std::string &access_code, int threshold) + : gr_sync_block ("correlate_access_code_bb", + gr_make_io_signature (1, 1, sizeof(char)), + gr_make_io_signature (1, 1, sizeof(char))), + d_data_reg(0), d_flag_reg(0), d_flag_bit(0), d_mask(0), + d_threshold(threshold) + +{ + if (!set_access_code(access_code)){ + fprintf(stderr, "digital_correlate_access_code_bb: access_code is > 64 bits\n"); + throw std::out_of_range ("access_code is > 64 bits"); + } +} + +digital_correlate_access_code_bb::~digital_correlate_access_code_bb () +{ +} + +bool +digital_correlate_access_code_bb::set_access_code( + const std::string &access_code) +{ + unsigned len = access_code.length(); // # of bytes in string + if (len > 64) + return false; + + // set len top bits to 1. + d_mask = ((~0ULL) >> (64 - len)) << (64 - len); + + d_flag_bit = 1LL << (64 - len); // Where we or-in new flag values. + // new data always goes in 0x0000000000000001 + d_access_code = 0; + for (unsigned i=0; i < 64; i++){ + d_access_code <<= 1; + if (i < len) + d_access_code |= access_code[i] & 1; // look at LSB only + } + + return true; +} + +int +digital_correlate_access_code_bb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for (int i = 0; i < noutput_items; i++){ + + // compute output value + unsigned int t = 0; + + t |= ((d_data_reg >> 63) & 0x1) << 0; + t |= ((d_flag_reg >> 63) & 0x1) << 1; // flag bit + out[i] = t; + + // compute hamming distance between desired access code and current data + unsigned long long wrong_bits = 0; + unsigned int nwrong = d_threshold+1; + int new_flag = 0; + + wrong_bits = (d_data_reg ^ d_access_code) & d_mask; + nwrong = gr_count_bits64(wrong_bits); + + // test for access code with up to threshold errors + new_flag = (nwrong <= d_threshold); + +#if VERBOSE + if(new_flag) { + fprintf(stderr, "access code found: %llx\n", d_access_code); + } + else { + fprintf(stderr, "%llx ==> %llx\n", d_access_code, d_data_reg); + } +#endif + + // shift in new data and new flag + d_data_reg = (d_data_reg << 1) | (in[i] & 0x1); + d_flag_reg = (d_flag_reg << 1); + if (new_flag) { + d_flag_reg |= d_flag_bit; + } + } + + return noutput_items; +} + diff --git a/gr-digital/lib/digital_costas_loop_cc.cc b/gr-digital/lib/digital_costas_loop_cc.cc new file mode 100644 index 000000000..370dc7e5c --- /dev/null +++ b/gr-digital/lib/digital_costas_loop_cc.cc @@ -0,0 +1,153 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_costas_loop_cc.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_sincos.h> +#include <gr_math.h> + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument) +{ + return gnuradio::get_initial_sptr(new digital_costas_loop_cc + (loop_bw, order)); +} + +digital_costas_loop_cc::digital_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument) + : gr_sync_block ("costas_loop_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature2 (1, 2, sizeof (gr_complex), sizeof(float))), + gri_control_loop(loop_bw, 1.0, -1.0), + d_order(order), d_phase_detector(NULL) +{ + // Set up the phase detector to use based on the constellation order + switch(d_order) { + case 2: + d_phase_detector = &digital_costas_loop_cc::phase_detector_2; + break; + + case 4: + d_phase_detector = &digital_costas_loop_cc::phase_detector_4; + break; + + case 8: + d_phase_detector = &digital_costas_loop_cc::phase_detector_8; + break; + + default: + throw std::invalid_argument("order must be 2, 4, or 8"); + break; + } +} + +float +digital_costas_loop_cc::phase_detector_8(gr_complex sample) const +{ + /* This technique splits the 8PSK constellation into 2 squashed + QPSK constellations, one when I is larger than Q and one where + Q is larger than I. The error is then calculated proportionally + to these squashed constellations by the const K = sqrt(2)-1. + + The signal magnitude must be > 1 or K will incorrectly bias + the error value. + + Ref: Z. Huang, Z. Yi, M. Zhang, K. Wang, "8PSK demodulation for + new generation DVB-S2", IEEE Proc. Int. Conf. Communications, + Circuits and Systems, Vol. 2, pp. 1447 - 1450, 2004. + */ + + float K = (sqrt(2.0) - 1); + if(fabsf(sample.real()) >= fabsf(sample.imag())) { + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real() * K); + } + else { + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() * K - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); + } +} + +float +digital_costas_loop_cc::phase_detector_4(gr_complex sample) const +{ + + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); +} + +float +digital_costas_loop_cc::phase_detector_2(gr_complex sample) const +{ + return (sample.real()*sample.imag()); +} + +int +digital_costas_loop_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *iptr = (gr_complex *) input_items[0]; + gr_complex *optr = (gr_complex *) output_items[0]; + float *foptr = (float *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + float error; + gr_complex nco_out; + + if (write_foptr) { + + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + optr[i] = iptr[i] * nco_out; + + error = (*this.*d_phase_detector)(optr[i]); + error = gr_branchless_clip(error, 1.0); + + advance_loop(error); + phase_wrap(); + frequency_limit(); + + foptr[i] = d_freq; + } + } else { + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + optr[i] = iptr[i] * nco_out; + + error = (*this.*d_phase_detector)(optr[i]); + error = gr_branchless_clip(error, 1.0); + + advance_loop(error); + phase_wrap(); + frequency_limit(); + } + } + return noutput_items; +} diff --git a/gr-digital/lib/digital_cpmmod_bc.cc b/gr-digital/lib/digital_cpmmod_bc.cc new file mode 100644 index 000000000..a95b604d1 --- /dev/null +++ b/gr-digital/lib/digital_cpmmod_bc.cc @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cpmmod_bc.h> +#include <gr_io_signature.h> + + +// Shared pointer constructor +digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta) +{ + return gnuradio::get_initial_sptr(new digital_cpmmod_bc((gr_cpm::cpm_type)type, + h, samples_per_sym, + L, beta)); +} + + +digital_cpmmod_bc::digital_cpmmod_bc(gr_cpm::cpm_type type, float h, + unsigned samples_per_sym, + unsigned L, double beta) + : gr_hier_block2("digital_cpmmod_bc", + gr_make_io_signature(1, 1, sizeof(char)), + gr_make_io_signature2(1, 1, sizeof(gr_complex), sizeof(float))), + d_taps(gr_cpm::phase_response(type, samples_per_sym, L, beta)), + d_char_to_float(gr_make_char_to_float()), + d_pulse_shaper(gr_make_interp_fir_filter_fff(samples_per_sym, d_taps)), + d_fm(gr_make_frequency_modulator_fc(M_PI * h)) +{ + switch (type) { + case gr_cpm::LRC: + case gr_cpm::LSRC: + case gr_cpm::LREC: + case gr_cpm::TFM: + case gr_cpm::GAUSSIAN: + break; + + default: + throw std::invalid_argument("invalid CPM type"); + } + + connect(self(), 0, d_char_to_float, 0); + connect(d_char_to_float, 0, d_pulse_shaper, 0); + connect(d_pulse_shaper, 0, d_fm, 0); + connect(d_fm, 0, self(), 0); +} + diff --git a/gr-digital/lib/digital_crc32.cc b/gr-digital/lib/digital_crc32.cc new file mode 100644 index 000000000..8806d6e9c --- /dev/null +++ b/gr-digital/lib/digital_crc32.cc @@ -0,0 +1,130 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * See also ISO 3309 [ISO-3309] or ITU-T V.42 [ITU-V42] for a formal specification. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <digital_crc32.h> + + +// Automatically generated CRC function +// polynomial: 0x104C11DB7 +unsigned int +digital_update_crc32(unsigned int crc, const unsigned char *data, size_t len) +{ + static const unsigned int table[256] = { + 0x00000000U,0x04C11DB7U,0x09823B6EU,0x0D4326D9U, + 0x130476DCU,0x17C56B6BU,0x1A864DB2U,0x1E475005U, + 0x2608EDB8U,0x22C9F00FU,0x2F8AD6D6U,0x2B4BCB61U, + 0x350C9B64U,0x31CD86D3U,0x3C8EA00AU,0x384FBDBDU, + 0x4C11DB70U,0x48D0C6C7U,0x4593E01EU,0x4152FDA9U, + 0x5F15ADACU,0x5BD4B01BU,0x569796C2U,0x52568B75U, + 0x6A1936C8U,0x6ED82B7FU,0x639B0DA6U,0x675A1011U, + 0x791D4014U,0x7DDC5DA3U,0x709F7B7AU,0x745E66CDU, + 0x9823B6E0U,0x9CE2AB57U,0x91A18D8EU,0x95609039U, + 0x8B27C03CU,0x8FE6DD8BU,0x82A5FB52U,0x8664E6E5U, + 0xBE2B5B58U,0xBAEA46EFU,0xB7A96036U,0xB3687D81U, + 0xAD2F2D84U,0xA9EE3033U,0xA4AD16EAU,0xA06C0B5DU, + 0xD4326D90U,0xD0F37027U,0xDDB056FEU,0xD9714B49U, + 0xC7361B4CU,0xC3F706FBU,0xCEB42022U,0xCA753D95U, + 0xF23A8028U,0xF6FB9D9FU,0xFBB8BB46U,0xFF79A6F1U, + 0xE13EF6F4U,0xE5FFEB43U,0xE8BCCD9AU,0xEC7DD02DU, + 0x34867077U,0x30476DC0U,0x3D044B19U,0x39C556AEU, + 0x278206ABU,0x23431B1CU,0x2E003DC5U,0x2AC12072U, + 0x128E9DCFU,0x164F8078U,0x1B0CA6A1U,0x1FCDBB16U, + 0x018AEB13U,0x054BF6A4U,0x0808D07DU,0x0CC9CDCAU, + 0x7897AB07U,0x7C56B6B0U,0x71159069U,0x75D48DDEU, + 0x6B93DDDBU,0x6F52C06CU,0x6211E6B5U,0x66D0FB02U, + 0x5E9F46BFU,0x5A5E5B08U,0x571D7DD1U,0x53DC6066U, + 0x4D9B3063U,0x495A2DD4U,0x44190B0DU,0x40D816BAU, + 0xACA5C697U,0xA864DB20U,0xA527FDF9U,0xA1E6E04EU, + 0xBFA1B04BU,0xBB60ADFCU,0xB6238B25U,0xB2E29692U, + 0x8AAD2B2FU,0x8E6C3698U,0x832F1041U,0x87EE0DF6U, + 0x99A95DF3U,0x9D684044U,0x902B669DU,0x94EA7B2AU, + 0xE0B41DE7U,0xE4750050U,0xE9362689U,0xEDF73B3EU, + 0xF3B06B3BU,0xF771768CU,0xFA325055U,0xFEF34DE2U, + 0xC6BCF05FU,0xC27DEDE8U,0xCF3ECB31U,0xCBFFD686U, + 0xD5B88683U,0xD1799B34U,0xDC3ABDEDU,0xD8FBA05AU, + 0x690CE0EEU,0x6DCDFD59U,0x608EDB80U,0x644FC637U, + 0x7A089632U,0x7EC98B85U,0x738AAD5CU,0x774BB0EBU, + 0x4F040D56U,0x4BC510E1U,0x46863638U,0x42472B8FU, + 0x5C007B8AU,0x58C1663DU,0x558240E4U,0x51435D53U, + 0x251D3B9EU,0x21DC2629U,0x2C9F00F0U,0x285E1D47U, + 0x36194D42U,0x32D850F5U,0x3F9B762CU,0x3B5A6B9BU, + 0x0315D626U,0x07D4CB91U,0x0A97ED48U,0x0E56F0FFU, + 0x1011A0FAU,0x14D0BD4DU,0x19939B94U,0x1D528623U, + 0xF12F560EU,0xF5EE4BB9U,0xF8AD6D60U,0xFC6C70D7U, + 0xE22B20D2U,0xE6EA3D65U,0xEBA91BBCU,0xEF68060BU, + 0xD727BBB6U,0xD3E6A601U,0xDEA580D8U,0xDA649D6FU, + 0xC423CD6AU,0xC0E2D0DDU,0xCDA1F604U,0xC960EBB3U, + 0xBD3E8D7EU,0xB9FF90C9U,0xB4BCB610U,0xB07DABA7U, + 0xAE3AFBA2U,0xAAFBE615U,0xA7B8C0CCU,0xA379DD7BU, + 0x9B3660C6U,0x9FF77D71U,0x92B45BA8U,0x9675461FU, + 0x8832161AU,0x8CF30BADU,0x81B02D74U,0x857130C3U, + 0x5D8A9099U,0x594B8D2EU,0x5408ABF7U,0x50C9B640U, + 0x4E8EE645U,0x4A4FFBF2U,0x470CDD2BU,0x43CDC09CU, + 0x7B827D21U,0x7F436096U,0x7200464FU,0x76C15BF8U, + 0x68860BFDU,0x6C47164AU,0x61043093U,0x65C52D24U, + 0x119B4BE9U,0x155A565EU,0x18197087U,0x1CD86D30U, + 0x029F3D35U,0x065E2082U,0x0B1D065BU,0x0FDC1BECU, + 0x3793A651U,0x3352BBE6U,0x3E119D3FU,0x3AD08088U, + 0x2497D08DU,0x2056CD3AU,0x2D15EBE3U,0x29D4F654U, + 0xC5A92679U,0xC1683BCEU,0xCC2B1D17U,0xC8EA00A0U, + 0xD6AD50A5U,0xD26C4D12U,0xDF2F6BCBU,0xDBEE767CU, + 0xE3A1CBC1U,0xE760D676U,0xEA23F0AFU,0xEEE2ED18U, + 0xF0A5BD1DU,0xF464A0AAU,0xF9278673U,0xFDE69BC4U, + 0x89B8FD09U,0x8D79E0BEU,0x803AC667U,0x84FBDBD0U, + 0x9ABC8BD5U,0x9E7D9662U,0x933EB0BBU,0x97FFAD0CU, + 0xAFB010B1U,0xAB710D06U,0xA6322BDFU,0xA2F33668U, + 0xBCB4666DU,0xB8757BDAU,0xB5365D03U,0xB1F740B4U, + }; + + while (len > 0) + { + crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8); + data++; + len--; + } + return crc; +} + +unsigned int +digital_update_crc32(unsigned int crc, const std::string s) +{ + return digital_update_crc32(crc, (const unsigned char *) s.data(), s.size()); +} + +unsigned int +digital_crc32(const unsigned char *buf, size_t len) +{ + return digital_update_crc32(0xffffffff, buf, len) ^ 0xffffffff; +} + +unsigned int +digital_crc32(const std::string s) +{ + return digital_crc32((const unsigned char *) s.data(), s.size()); +} diff --git a/gr-digital/lib/digital_fll_band_edge_cc.cc b/gr-digital/lib/digital_fll_band_edge_cc.cc new file mode 100644 index 000000000..05c092622 --- /dev/null +++ b/gr-digital/lib/digital_fll_band_edge_cc.cc @@ -0,0 +1,259 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_fll_band_edge_cc.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <cstdio> + +#define M_TWOPI (2*M_PI) + +float sinc(float x) +{ + if(x == 0) + return 1; + else + return sin(M_PI*x)/(M_PI*x); +} + +digital_fll_band_edge_cc_sptr +digital_make_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth) +{ + return gnuradio::get_initial_sptr(new digital_fll_band_edge_cc (samps_per_sym, rolloff, + filter_size, bandwidth)); +} + + +static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)}; +static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); +digital_fll_band_edge_cc::digital_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth) + : gr_sync_block ("fll_band_edge_cc", + gr_make_io_signature (1, 1, sizeof(gr_complex)), + gr_make_io_signaturev (1, 4, iosig)), + gri_control_loop(bandwidth, M_TWOPI*(2.0/samps_per_sym), -M_TWOPI*(2.0/samps_per_sym)), + d_updated (false) +{ + // Initialize samples per symbol + if(samps_per_sym <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = samps_per_sym; + + // Initialize rolloff factor + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + + // Initialize filter length + if(filter_size <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + + // Build the band edge filters + design_filter(d_sps, d_rolloff, d_filter_size); +} + +digital_fll_band_edge_cc::~digital_fll_band_edge_cc () +{ +} + + +/******************************************************************* + SET FUNCTIONS +*******************************************************************/ + +void +digital_fll_band_edge_cc::set_samples_per_symbol(float sps) +{ + if(sps <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = sps; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +digital_fll_band_edge_cc::set_rolloff(float rolloff) +{ + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +digital_fll_band_edge_cc::set_filter_size(int filter_size) +{ + if(filter_size <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +/******************************************************************* + GET FUNCTIONS +*******************************************************************/ + +float +digital_fll_band_edge_cc::get_samples_per_symbol() const +{ + return d_sps; +} + +float +digital_fll_band_edge_cc::get_rolloff() const +{ + return d_rolloff; +} + +int +digital_fll_band_edge_cc:: get_filter_size() const +{ + return d_filter_size; +} + + +/******************************************************************* +*******************************************************************/ + +void +digital_fll_band_edge_cc::design_filter(float samps_per_sym, + float rolloff, int filter_size) +{ + int M = rint(filter_size / samps_per_sym); + float power = 0; + + // Create the baseband filter by adding two sincs together + std::vector<float> bb_taps; + for(int i = 0; i < filter_size; i++) { + float k = -M + i*2.0/samps_per_sym; + float tap = sinc(rolloff*k - 0.5) + sinc(rolloff*k + 0.5); + power += tap; + + bb_taps.push_back(tap); + } + + d_taps_lower.resize(filter_size); + d_taps_upper.resize(filter_size); + + // Create the band edge filters by spinning the baseband + // filter up and down to the right places in frequency. + // Also, normalize the power in the filters + int N = (bb_taps.size() - 1.0)/2.0; + for(int i = 0; i < filter_size; i++) { + float tap = bb_taps[i] / power; + + float k = (-N + (int)i)/(2.0*samps_per_sym); + + gr_complex t1 = tap * gr_expj(-M_TWOPI*(1+rolloff)*k); + gr_complex t2 = tap * gr_expj(M_TWOPI*(1+rolloff)*k); + + d_taps_lower[filter_size-i-1] = t1; + d_taps_upper[filter_size-i-1] = t2; + } + + d_updated = true; + + // Set the history to ensure enough input items for each filter + set_history(filter_size+1); +} + +void +digital_fll_band_edge_cc::print_taps() +{ + unsigned int i; + + printf("Upper Band-edge: ["); + for(i = 0; i < d_taps_upper.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_upper[i].real(), d_taps_upper[i].imag()); + } + printf("]\n\n"); + + printf("Lower Band-edge: ["); + for(i = 0; i < d_taps_lower.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_lower[i].real(), d_taps_lower[i].imag()); + } + printf("]\n\n"); +} + +int +digital_fll_band_edge_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + float *frq = NULL; + float *phs = NULL; + float *err = NULL; + if(output_items.size() == 4) { + frq = (float *) output_items[1]; + phs = (float *) output_items[2]; + err = (float *) output_items[3]; + } + + if (d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i; + float error; + gr_complex nco_out; + gr_complex out_upper, out_lower; + for(i = 0; i < noutput_items; i++) { + nco_out = gr_expj(d_phase); + out[i+d_filter_size-1] = in[i] * nco_out; + + // Perform the dot product of the output with the filters + out_upper = 0; + out_lower = 0; + for(int k = 0; k < d_filter_size; k++) { + out_upper += d_taps_upper[k] * out[i+k]; + out_lower += d_taps_lower[k] * out[i+k]; + } + error = norm(out_lower) - norm(out_upper); + + advance_loop(error); + phase_wrap(); + frequency_limit(); + + if(output_items.size() == 4) { + frq[i] = d_freq; + phs[i] = d_phase; + err[i] = error; + } + } + + return noutput_items; +} diff --git a/gr-digital/lib/digital_gmskmod_bc.cc b/gr-digital/lib/digital_gmskmod_bc.cc new file mode 100644 index 000000000..e53e90037 --- /dev/null +++ b/gr-digital/lib/digital_gmskmod_bc.cc @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_gmskmod_bc.h> +#include <gr_io_signature.h> + +// Shared pointer constructor +digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L) +{ + return gnuradio::get_initial_sptr(new digital_gmskmod_bc(samples_per_sym, bt, L)); +} + + +digital_gmskmod_bc::digital_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L) + : digital_cpmmod_bc(gr_cpm::GAUSSIAN, 0.5, samples_per_sym, L, bt) +{ +} + diff --git a/gr-digital/lib/digital_kurtotic_equalizer_cc.cc b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc new file mode 100644 index 000000000..c95b56021 --- /dev/null +++ b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_kurtotic_equalizer_cc.h> + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, float mu) +{ + return gnuradio::get_initial_sptr(new digital_kurtotic_equalizer_cc(num_taps, mu)); +} + +digital_kurtotic_equalizer_cc::digital_kurtotic_equalizer_cc(int num_taps, float mu) + : gr_adaptive_fir_ccc("kurtotic_equalizer_cc", 1, std::vector<gr_complex>(num_taps)) +{ + set_gain(mu); + if (num_taps > 0) + d_taps[0] = 1.0; + + d_alpha_p = 0.01; + d_alpha_q = 0.01; + d_alpha_m = 0.01; + + d_p = 0.0f; + d_m = 0.0f; + d_q = gr_complex(0,0); + d_u = gr_complex(0,0); +} + diff --git a/gr-digital/lib/digital_lms_dd_equalizer_cc.cc b/gr-digital/lib/digital_lms_dd_equalizer_cc.cc new file mode 100644 index 000000000..e2c2f16f2 --- /dev/null +++ b/gr-digital/lib/digital_lms_dd_equalizer_cc.cc @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_lms_dd_equalizer_cc.h> +#include <gr_io_signature.h> +#include <gr_misc.h> +#include <iostream> + +digital_lms_dd_equalizer_cc_sptr +digital_make_lms_dd_equalizer_cc(int num_taps, float mu, int sps, + digital_constellation_sptr cnst) +{ + return gnuradio::get_initial_sptr(new digital_lms_dd_equalizer_cc(num_taps, mu, + sps, cnst)); +} + +digital_lms_dd_equalizer_cc::digital_lms_dd_equalizer_cc(int num_taps, float mu, + int sps, + digital_constellation_sptr cnst) + : gr_adaptive_fir_ccc("lms_dd_equalizer_cc", sps, + std::vector<gr_complex>(num_taps, gr_complex(0,0))), + d_taps(num_taps), d_cnst(cnst) +{ + set_gain(mu); + if (num_taps > 0) + d_taps[num_taps/2] = 1.0; +} + + + + +/* +int +digital_lms_dd_equalizer_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + gr_complex acc, decision, error; + + for(int i = 0; i < noutput_items; i++) { + acc = 0; + + // Compute output + for (size_t j=0; j < d_taps.size(); j++) + acc += in[i+j] * conj(d_taps[j]); + + d_cnst->map_to_points(d_cnst->decision_maker(&acc), &decision); + error = decision - acc; + + // Update taps + for (size_t j=0; j < d_taps.size(); j++) + d_taps[j] += d_mu * conj(error) * in[i+j]; + + out[i] = acc; + } + + return noutput_items; +} +*/ diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc new file mode 100644 index 000000000..363b86c9f --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.cc @@ -0,0 +1,316 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_mpsk_receiver_cc.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> +#include <gri_mmse_fir_interpolator_cc.h> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc(unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel) +{ + return gnuradio::get_initial_sptr(new digital_mpsk_receiver_cc (M, theta, + loop_bw, + fmin, fmax, + mu, gain_mu, + omega, gain_omega, + omega_rel)); +} + +digital_mpsk_receiver_cc::digital_mpsk_receiver_cc (unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, + float omega_rel) + : gr_block ("mpsk_receiver_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 1, sizeof (gr_complex))), + gri_control_loop(loop_bw, fmax, fmin), + d_M(M), d_theta(theta), + d_current_const_point(0), + d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), + d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + d_interp = new gri_mmse_fir_interpolator_cc(); + d_dl_idx = 0; + + set_omega(omega); + + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + assert(d_interp->ntaps() <= DLLEN); + + // zero double length delay line. + for (unsigned int i = 0; i < 2 * DLLEN; i++) + d_dl[i] = gr_complex(0.0,0.0); + + // build the constellation vector from M + make_constellation(); + + // Select a phase detector and a decision maker for the modulation order + switch(d_M) { + case 2: // optimized algorithms for BPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_bpsk; //bpsk; + d_decision = &digital_mpsk_receiver_cc::decision_bpsk; + break; + + case 4: // optimized algorithms for QPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_qpsk; //qpsk; + d_decision = &digital_mpsk_receiver_cc::decision_qpsk; + break; + + default: // generic algorithms for any M (power of 2?) but not pretty + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_generic; + d_decision = &digital_mpsk_receiver_cc::decision_generic; + break; + } +} + +digital_mpsk_receiver_cc::~digital_mpsk_receiver_cc () +{ + delete d_interp; +} + +void +digital_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +// FIXME add these back in an test difference in performance +float +digital_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const +{ + float phase_error = 0; + if(fabsf(sample.real()) > fabsf(sample.imag())) { + if(sample.real() > 0) + phase_error = -sample.imag(); + else + phase_error = sample.imag(); + } + else { + if(sample.imag() > 0) + phase_error = sample.real(); + else + phase_error = -sample.real(); + } + + return phase_error; +} + +float +digital_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const +{ + return -(sample.real()*sample.imag()); +} + +float digital_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const +{ + //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point])); + return -arg(sample*conj(d_constellation[d_current_const_point])); +} + +unsigned int +digital_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const +{ + return (gr_branchless_binary_slicer(sample.real()) ^ 1); + //return gr_binary_slicer(sample.real()) ^ 1; +} + +unsigned int +digital_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const +{ + unsigned int index; + + //index = gr_branchless_quad_0deg_slicer(sample); + index = gr_quad_0deg_slicer(sample); + return index; +} + +unsigned int +digital_mpsk_receiver_cc::decision_generic(gr_complex sample) const +{ + unsigned int min_m = 0; + float min_s = 65535; + + // Develop all possible constellation points and find the one that minimizes + // the Euclidean distance (error) with the sample + for(unsigned int m=0; m < d_M; m++) { + gr_complex diff = norm(d_constellation[m] - sample); + + if(fabs(diff.real()) < min_s) { + min_s = fabs(diff.real()); + min_m = m; + } + } + // Return the index of the constellation point that minimizes the error + return min_m; +} + + +void +digital_mpsk_receiver_cc::make_constellation() +{ + for(unsigned int m=0; m < d_M; m++) { + d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m)); + } +} + +void +digital_mpsk_receiver_cc::mm_sampler(const gr_complex symbol) +{ + gr_complex sample, nco; + + d_mu--; // skip a number of symbols between sampling + d_phase += d_freq; // increment the phase based on the frequency of the rotation + + // Keep phase clamped and not walk to infinity + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the current sample + sample = nco*symbol; // get the downconverted symbol + + // Fill up the delay line for the interpolator + d_dl[d_dl_idx] = sample; + d_dl[(d_dl_idx + DLLEN)] = sample; // put this in the second half of the buffer for overflows + d_dl_idx = (d_dl_idx+1) % DLLEN; // Keep the delay line index in bounds +} + +void +digital_mpsk_receiver_cc::mm_error_tracking(gr_complex sample) +{ + gr_complex u, x, y; + float mm_error = 0; + + // Make sample timing corrections + + // set the delayed samples + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = sample; + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + + d_current_const_point = (*this.*d_decision)(d_p_0T); // make a decision on the sample value + d_c_0T = d_constellation[d_current_const_point]; + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_error = u.real(); // the error signal is in the real part + mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val + + d_omega = d_omega + d_gain_omega * mm_error; // update omega based on loop error + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away + + d_mu += d_omega + d_gain_mu * mm_error; // update mu based on loop error + +#if VERBOSE_MM + printf("mm: mu: %f omega: %f mm_error: %f sample: %f+j%f constellation: %f+j%f\n", + d_mu, d_omega, mm_error, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + + +void +digital_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) +{ + float phase_error = 0; + + // Make phase and frequency corrections based on sampled value + phase_error = (*this.*d_phase_error_detector)(sample); + + advance_loop(phase_error); + phase_wrap(); + frequency_limit(); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + +int +digital_mpsk_receiver_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + int i=0, o=0; + + while((o < noutput_items) && (i < ninput_items[0])) { + while((d_mu > 1) && (i < ninput_items[0])) { + mm_sampler(in[i]); // puts symbols into a buffer and adjusts d_mu + i++; + } + + if(i < ninput_items[0]) { + gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu); + + mm_error_tracking(interp_sample); // corrects M&M sample time + phase_error_tracking(interp_sample); // corrects phase and frequency offsets + + out[o++] = interp_sample; + } + } + + #if 0 + printf("ninput_items: %d noutput_items: %d consuming: %d returning: %d\n", + ninput_items[0], noutput_items, i, o); + #endif + + consume_each(i); + return o; +} diff --git a/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc new file mode 100644 index 000000000..192af2591 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_cyclic_prefixer.h> +#include <gr_io_signature.h> + +digital_ofdm_cyclic_prefixer_sptr +digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_cyclic_prefixer (input_size, + output_size)); +} + +digital_ofdm_cyclic_prefixer::digital_ofdm_cyclic_prefixer (size_t input_size, + size_t output_size) + : gr_sync_interpolator ("ofdm_cyclic_prefixer", + gr_make_io_signature (1, 1, input_size*sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex)), + output_size), + d_input_size(input_size), + d_output_size(output_size) + +{ +} + +int +digital_ofdm_cyclic_prefixer::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex *in = (gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + size_t cp_size = d_output_size - d_input_size; + unsigned int i=0, j=0; + + j = cp_size; + for(i=0; i < d_input_size; i++,j++) { + out[j] = in[i]; + } + + j = d_input_size - cp_size; + for(i=0; i < cp_size; i++, j++) { + out[i] = in[j]; + } + + return d_output_size; +} diff --git a/gr-digital/lib/digital_ofdm_frame_acquisition.cc b/gr-digital/lib/digital_ofdm_frame_acquisition.cc new file mode 100644 index 000000000..93b58aeca --- /dev/null +++ b/gr-digital/lib/digital_ofdm_frame_acquisition.cc @@ -0,0 +1,210 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2008,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_frame_acquisition.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> +#include <cstdio> + +#define VERBOSE 0 +#define M_TWOPI (2*M_PI) +#define MAX_NUM_SYMBOLS 1000 + +digital_ofdm_frame_acquisition_sptr +digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_frame_acquisition (occupied_carriers, fft_length, cplen, + known_symbol, max_fft_shift_len)); +} + +digital_ofdm_frame_acquisition::digital_ofdm_frame_acquisition (unsigned occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) + : gr_block ("ofdm_frame_acquisition", + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*fft_length, sizeof(char)*fft_length), + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))), + d_occupied_carriers(occupied_carriers), + d_fft_length(fft_length), + d_cplen(cplen), + d_freq_shift_len(max_fft_shift_len), + d_known_symbol(known_symbol), + d_coarse_freq(0), + d_phase_count(0) +{ + d_symbol_phase_diff.resize(d_fft_length); + d_known_phase_diff.resize(d_occupied_carriers); + d_hestimate.resize(d_occupied_carriers); + + unsigned int i = 0, j = 0; + + std::fill(d_known_phase_diff.begin(), d_known_phase_diff.end(), 0); + for(i = 0; i < d_known_symbol.size()-2; i+=2) { + d_known_phase_diff[i] = norm(d_known_symbol[i] - d_known_symbol[i+2]); + } + + d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS]; + for(i = 0; i <= 2*d_freq_shift_len; i++) { + for(j = 0; j < MAX_NUM_SYMBOLS; j++) { + d_phase_lut[j + i*MAX_NUM_SYMBOLS] = gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j); + } + } +} + +digital_ofdm_frame_acquisition::~digital_ofdm_frame_acquisition(void) +{ + delete [] d_phase_lut; +} + +void +digital_ofdm_frame_acquisition::forecast (int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = 1; +} + +gr_complex +digital_ofdm_frame_acquisition::coarse_freq_comp(int freq_delta, int symbol_count) +{ + // return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count), + // sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count)); + + return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count); + + //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count]; +} + +void +digital_ofdm_frame_acquisition::correlate(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i,j; + + std::fill(d_symbol_phase_diff.begin(), d_symbol_phase_diff.end(), 0); + for(i = 0; i < d_fft_length-2; i++) { + d_symbol_phase_diff[i] = norm(symbol[i] - symbol[i+2]); + } + + // sweep through all possible/allowed frequency offsets and select the best + int index = 0; + float max = 0, sum=0; + for(i = zeros_on_left - d_freq_shift_len; i < zeros_on_left + d_freq_shift_len; i++) { + sum = 0; + for(j = 0; j < d_occupied_carriers; j++) { + sum += (d_known_phase_diff[j] * d_symbol_phase_diff[i+j]); + } + if(sum > max) { + max = sum; + index = i; + } + } + + // set the coarse frequency offset relative to the edge of the occupied tones + d_coarse_freq = index - zeros_on_left; +} + +void +digital_ofdm_frame_acquisition::calculate_equalizer(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i=0; + + // Set first tap of equalizer + d_hestimate[0] = d_known_symbol[0] / + (coarse_freq_comp(d_coarse_freq,1)*symbol[zeros_on_left+d_coarse_freq]); + + // set every even tap based on known symbol + // linearly interpolate between set carriers to set zero-filled carriers + // FIXME: is this the best way to set this? + for(i = 2; i < d_occupied_carriers; i+=2) { + d_hestimate[i] = d_known_symbol[i] / + (coarse_freq_comp(d_coarse_freq,1)*(symbol[i+zeros_on_left+d_coarse_freq])); + d_hestimate[i-1] = (d_hestimate[i] + d_hestimate[i-2]) / gr_complex(2.0, 0.0); + } + + // with even number of carriers; last equalizer tap is wrong + if(!(d_occupied_carriers & 1)) { + d_hestimate[d_occupied_carriers-1] = d_hestimate[d_occupied_carriers-2]; + } + + if(VERBOSE) { + fprintf(stderr, "Equalizer setting:\n"); + for(i = 0; i < d_occupied_carriers; i++) { + gr_complex sym = coarse_freq_comp(d_coarse_freq,1)*symbol[i+zeros_on_left+d_coarse_freq]; + gr_complex output = sym * d_hestimate[i]; + fprintf(stderr, "sym: %+.4f + j%+.4f ks: %+.4f + j%+.4f eq: %+.4f + j%+.4f ==> %+.4f + j%+.4f\n", + sym .real(), sym.imag(), + d_known_symbol[i].real(), d_known_symbol[i].imag(), + d_hestimate[i].real(), d_hestimate[i].imag(), + output.real(), output.imag()); + } + fprintf(stderr, "\n"); + } +} + +int +digital_ofdm_frame_acquisition::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *symbol = (const gr_complex *)input_items[0]; + const char *signal_in = (const char *)input_items[1]; + + gr_complex *out = (gr_complex *) output_items[0]; + char *signal_out = (char *) output_items[1]; + + int unoccupied_carriers = d_fft_length - d_occupied_carriers; + int zeros_on_left = (int)ceil(unoccupied_carriers/2.0); + + if(signal_in[0]) { + d_phase_count = 1; + correlate(symbol, zeros_on_left); + calculate_equalizer(symbol, zeros_on_left); + signal_out[0] = 1; + } + else { + signal_out[0] = 0; + } + + for(unsigned int i = 0; i < d_occupied_carriers; i++) { + out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count) + *symbol[i+zeros_on_left+d_coarse_freq]; + } + + d_phase_count++; + if(d_phase_count == MAX_NUM_SYMBOLS) { + d_phase_count = 1; + } + + consume_each(1); + return 1; +} diff --git a/gr-digital/lib/digital_ofdm_frame_sink.cc b/gr-digital/lib/digital_ofdm_frame_sink.cc new file mode 100644 index 000000000..f8fb1bbb1 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_frame_sink.cc @@ -0,0 +1,405 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_frame_sink.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> +#include <math.h> +#include <cstdio> +#include <stdexcept> +#include <iostream> +#include <string.h> + +#define VERBOSE 0 + +inline void +digital_ofdm_frame_sink::enter_search() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_search\n"); + + d_state = STATE_SYNC_SEARCH; + +} + +inline void +digital_ofdm_frame_sink::enter_have_sync() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_have_sync\n"); + + d_state = STATE_HAVE_SYNC; + + // clear state of demapper + d_byte_offset = 0; + d_partial_byte = 0; + + d_header = 0; + d_headerbytelen_cnt = 0; + + // Resetting PLL + d_freq = 0.0; + d_phase = 0.0; + fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0)); +} + +inline void +digital_ofdm_frame_sink::enter_have_header() +{ + d_state = STATE_HAVE_HEADER; + + // header consists of two 16-bit shorts in network byte order + // payload length is lower 12 bits + // whitener offset is upper 4 bits + d_packetlen = (d_header >> 16) & 0x0fff; + d_packet_whitener_offset = (d_header >> 28) & 0x000f; + d_packetlen_cnt = 0; + + if (VERBOSE) + fprintf(stderr, "@ enter_have_header (payload_len = %d) (offset = %d)\n", + d_packetlen, d_packet_whitener_offset); +} + + +unsigned char digital_ofdm_frame_sink::slicer(const gr_complex x) +{ + unsigned int table_size = d_sym_value_out.size(); + unsigned int min_index = 0; + float min_euclid_dist = norm(x - d_sym_position[0]); + float euclid_dist = 0; + + for (unsigned int j = 1; j < table_size; j++){ + euclid_dist = norm(x - d_sym_position[j]); + if (euclid_dist < min_euclid_dist){ + min_euclid_dist = euclid_dist; + min_index = j; + } + } + return d_sym_value_out[min_index]; +} + +unsigned int digital_ofdm_frame_sink::demapper(const gr_complex *in, + unsigned char *out) +{ + unsigned int i=0, bytes_produced=0; + gr_complex carrier; + + carrier=gr_expj(d_phase); + + gr_complex accum_error = 0.0; + //while(i < d_occupied_carriers) { + while(i < d_subcarrier_map.size()) { + if(d_nresid > 0) { + d_partial_byte |= d_resid; + d_byte_offset += d_nresid; + d_nresid = 0; + d_resid = 0; + } + + //while((d_byte_offset < 8) && (i < d_occupied_carriers)) { + while((d_byte_offset < 8) && (i < d_subcarrier_map.size())) { + //gr_complex sigrot = in[i]*carrier*d_dfe[i]; + gr_complex sigrot = in[d_subcarrier_map[i]]*carrier*d_dfe[i]; + + if(d_derotated_output != NULL){ + d_derotated_output[i] = sigrot; + } + + unsigned char bits = slicer(sigrot); + + gr_complex closest_sym = d_sym_position[bits]; + + accum_error += sigrot * conj(closest_sym); + + // FIX THE FOLLOWING STATEMENT + if (norm(sigrot)> 0.001) d_dfe[i] += d_eq_gain*(closest_sym/sigrot-d_dfe[i]); + + i++; + + if((8 - d_byte_offset) >= d_nbits) { + d_partial_byte |= bits << (d_byte_offset); + d_byte_offset += d_nbits; + } + else { + d_nresid = d_nbits-(8-d_byte_offset); + int mask = ((1<<(8-d_byte_offset))-1); + d_partial_byte |= (bits & mask) << d_byte_offset; + d_resid = bits >> (8-d_byte_offset); + d_byte_offset += (d_nbits - d_nresid); + } + //printf("demod symbol: %.4f + j%.4f bits: %x partial_byte: %x byte_offset: %d resid: %x nresid: %d\n", + // in[i-1].real(), in[i-1].imag(), bits, d_partial_byte, d_byte_offset, d_resid, d_nresid); + } + + if(d_byte_offset == 8) { + //printf("demod byte: %x \n\n", d_partial_byte); + out[bytes_produced++] = d_partial_byte; + d_byte_offset = 0; + d_partial_byte = 0; + } + } + //std::cerr << "accum_error " << accum_error << std::endl; + + float angle = arg(accum_error); + + d_freq = d_freq - d_freq_gain*angle; + d_phase = d_phase + d_freq - d_phase_gain*angle; + if (d_phase >= 2*M_PI) d_phase -= 2*M_PI; + if (d_phase <0) d_phase += 2*M_PI; + + //if(VERBOSE) + // std::cerr << angle << "\t" << d_freq << "\t" << d_phase << "\t" << std::endl; + + return bytes_produced; +} + + +digital_ofdm_frame_sink_sptr +digital_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_frame_sink(sym_position, sym_value_out, + target_queue, occupied_carriers, + phase_gain, freq_gain)); +} + + +digital_ofdm_frame_sink::digital_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) + : gr_sync_block ("ofdm_frame_sink", + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char)), + gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)), + d_target_queue(target_queue), d_occupied_carriers(occupied_carriers), + d_byte_offset(0), d_partial_byte(0), + d_resid(0), d_nresid(0),d_phase(0),d_freq(0),d_phase_gain(phase_gain),d_freq_gain(freq_gain), + d_eq_gain(0.05) +{ + std::string carriers = "FE7F"; + + // A bit hacky to fill out carriers to occupied_carriers length + int diff = (d_occupied_carriers - 4*carriers.length()); + while(diff > 7) { + carriers.insert(0, "f"); + carriers.insert(carriers.length(), "f"); + diff -= 8; + } + + // if there's extras left to be processed + // divide remaining to put on either side of current map + // all of this is done to stick with the concept of a carrier map string that + // can be later passed by the user, even though it'd be cleaner to just do this + // on the carrier map itself + int diff_left=0; + int diff_right=0; + + // dictionary to convert from integers to ascii hex representation + char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + if(diff > 0) { + char c[2] = {0,0}; + + diff_left = (int)ceil((float)diff/2.0f); // number of carriers to put on the left side + c[0] = abc[(1 << diff_left) - 1]; // convert to bits and move to ASCI integer + carriers.insert(0, c); + + diff_right = diff - diff_left; // number of carriers to put on the right side + c[0] = abc[0xF^((1 << diff_right) - 1)]; // convert to bits and move to ASCI integer + carriers.insert(carriers.length(), c); + } + + // It seemed like such a good idea at the time... + // because we are only dealing with the occupied_carriers + // at this point, the diff_left in the following compensates + // for any offset from the 0th carrier introduced + unsigned int i,j,k; + for(i = 0; i < (d_occupied_carriers/4)+diff_left; i++) { + char c = carriers[i]; + for(j = 0; j < 4; j++) { + k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1; + if(k) { + d_subcarrier_map.push_back(4*i + j - diff_left); + } + } + } + + // make sure we stay in the limit currently imposed by the occupied_carriers + if(d_subcarrier_map.size() > d_occupied_carriers) { + throw std::invalid_argument("digital_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers"); + } + + d_bytes_out = new unsigned char[d_occupied_carriers]; + d_dfe.resize(occupied_carriers); + fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0)); + + set_sym_value_out(sym_position, sym_value_out); + + enter_search(); +} + +digital_ofdm_frame_sink::~digital_ofdm_frame_sink () +{ + delete [] d_bytes_out; +} + +bool +digital_ofdm_frame_sink::set_sym_value_out(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out) +{ + if (sym_position.size() != sym_value_out.size()) + return false; + + if (sym_position.size()<1) + return false; + + d_sym_position = sym_position; + d_sym_value_out = sym_value_out; + d_nbits = (unsigned long)ceil(log10(float(d_sym_value_out.size())) / log10(2.0)); + + return true; +} + + +int +digital_ofdm_frame_sink::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + const char *sig = (const char *) input_items[1]; + unsigned int j = 0; + unsigned int bytes=0; + + // If the output is connected, send it the derotated symbols + if(output_items.size() >= 1) + d_derotated_output = (gr_complex *)output_items[0]; + else + d_derotated_output = NULL; + + if (VERBOSE) + fprintf(stderr,">>> Entering state machine\n"); + + switch(d_state) { + + case STATE_SYNC_SEARCH: // Look for flag indicating beginning of pkt + if (VERBOSE) + fprintf(stderr,"SYNC Search, noutput=%d\n", noutput_items); + + if (sig[0]) { // Found it, set up for header decode + enter_have_sync(); + } + break; + + case STATE_HAVE_SYNC: + // only demod after getting the preamble signal; otherwise, the + // equalizer taps will screw with the PLL performance + bytes = demapper(&in[0], d_bytes_out); + + if (VERBOSE) { + if(sig[0]) + printf("ERROR -- Found SYNC in HAVE_SYNC\n"); + fprintf(stderr,"Header Search bitcnt=%d, header=0x%08x\n", + d_headerbytelen_cnt, d_header); + } + + j = 0; + while(j < bytes) { + d_header = (d_header << 8) | (d_bytes_out[j] & 0xFF); + j++; + + if (++d_headerbytelen_cnt == HEADERBYTELEN) { + + if (VERBOSE) + fprintf(stderr, "got header: 0x%08x\n", d_header); + + // we have a full header, check to see if it has been received properly + if (header_ok()){ + enter_have_header(); + + if (VERBOSE) + printf("\nPacket Length: %d\n", d_packetlen); + + while((j < bytes) && (d_packetlen_cnt < d_packetlen)) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + } + + if(d_packetlen_cnt == d_packetlen) { + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + } + } + else { + enter_search(); // bad header + } + } + } + break; + + case STATE_HAVE_HEADER: + bytes = demapper(&in[0], d_bytes_out); + + if (VERBOSE) { + if(sig[0]) + printf("ERROR -- Found SYNC in HAVE_HEADER at %d, length of %d\n", d_packetlen_cnt, d_packetlen); + fprintf(stderr,"Packet Build\n"); + } + + j = 0; + while(j < bytes) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + + if (d_packetlen_cnt == d_packetlen){ // packet is filled + // build a message + // NOTE: passing header field as arg1 is not scalable + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen_cnt); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + break; + } + } + break; + + default: + assert(0); + + } // switch + + return 1; +} diff --git a/gr-digital/lib/digital_ofdm_insert_preamble.cc b/gr-digital/lib/digital_ofdm_insert_preamble.cc new file mode 100644 index 000000000..a46133643 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_insert_preamble.cc @@ -0,0 +1,187 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <digital_ofdm_insert_preamble.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <iostream> +#include <string.h> + +digital_ofdm_insert_preamble_sptr +digital_make_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_insert_preamble(fft_length, + preamble)); +} + +digital_ofdm_insert_preamble::digital_ofdm_insert_preamble + (int fft_length, + const std::vector<std::vector<gr_complex> > &preamble) + : gr_block("ofdm_insert_preamble", + gr_make_io_signature2(2, 2, + sizeof(gr_complex)*fft_length, + sizeof(char)), + gr_make_io_signature2(1, 2, + sizeof(gr_complex)*fft_length, + sizeof(char))), + d_fft_length(fft_length), + d_preamble(preamble), + d_state(ST_IDLE), + d_nsymbols_output(0), + d_pending_flag(0) +{ + // sanity check preamble symbols + for (size_t i = 0; i < d_preamble.size(); i++){ + if (d_preamble[i].size() != (size_t) d_fft_length) + throw std::invalid_argument("digital_ofdm_insert_preamble: invalid length for preamble symbol"); + } + + enter_idle(); +} + + +digital_ofdm_insert_preamble::~digital_ofdm_insert_preamble() +{ +} + +int +digital_ofdm_insert_preamble::general_work (int noutput_items, + gr_vector_int &ninput_items_v, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int ninput_items = std::min(ninput_items_v[0], ninput_items_v[1]); + const gr_complex *in_sym = (const gr_complex *) input_items[0]; + const unsigned char *in_flag = (const unsigned char *) input_items[1]; + + gr_complex *out_sym = (gr_complex *) output_items[0]; + unsigned char *out_flag = 0; + if (output_items.size() == 2) + out_flag = (unsigned char *) output_items[1]; + + + int no = 0; // number items output + int ni = 0; // number items read from input + + +#define write_out_flag() \ + do { if (out_flag) \ + out_flag[no] = d_pending_flag; \ + d_pending_flag = 0; \ + } while(0) + + + while (no < noutput_items && ni < ninput_items){ + switch(d_state){ + case ST_IDLE: + if (in_flag[ni] & 0x1) // this is first symbol of new payload + enter_preamble(); + else + ni++; // eat one input symbol + break; + + case ST_PREAMBLE: + assert(in_flag[ni] & 0x1); + if (d_nsymbols_output >= (int) d_preamble.size()){ + // we've output all the preamble + enter_first_payload(); + } + else { + memcpy(&out_sym[no * d_fft_length], + &d_preamble[d_nsymbols_output][0], + d_fft_length*sizeof(gr_complex)); + + write_out_flag(); + no++; + d_nsymbols_output++; + } + break; + + case ST_FIRST_PAYLOAD: + // copy first payload symbol from input to output + memcpy(&out_sym[no * d_fft_length], + &in_sym[ni * d_fft_length], + d_fft_length * sizeof(gr_complex)); + + write_out_flag(); + no++; + ni++; + enter_payload(); + break; + + case ST_PAYLOAD: + if (in_flag[ni] & 0x1){ // this is first symbol of a new payload + enter_preamble(); + break; + } + + // copy a symbol from input to output + memcpy(&out_sym[no * d_fft_length], + &in_sym[ni * d_fft_length], + d_fft_length * sizeof(gr_complex)); + + write_out_flag(); + no++; + ni++; + break; + + default: + std::cerr << "digital_ofdm_insert_preamble: (can't happen) invalid state, resetting\n"; + enter_idle(); + } + } + + consume_each(ni); + return no; +} + +void +digital_ofdm_insert_preamble::enter_idle() +{ + d_state = ST_IDLE; + d_nsymbols_output = 0; + d_pending_flag = 0; +} + +void +digital_ofdm_insert_preamble::enter_preamble() +{ + d_state = ST_PREAMBLE; + d_nsymbols_output = 0; + d_pending_flag = 1; +} + +void +digital_ofdm_insert_preamble::enter_first_payload() +{ + d_state = ST_FIRST_PAYLOAD; +} + +void +digital_ofdm_insert_preamble::enter_payload() +{ + d_state = ST_PAYLOAD; +} diff --git a/gr-digital/lib/digital_ofdm_mapper_bcv.cc b/gr-digital/lib/digital_ofdm_mapper_bcv.cc new file mode 100644 index 000000000..cf3d08703 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_mapper_bcv.cc @@ -0,0 +1,241 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2008,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_mapper_bcv.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <string.h> + +digital_ofdm_mapper_bcv_sptr +digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit, + unsigned int occupied_carriers, unsigned int fft_length) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_mapper_bcv (constellation, msgq_limit, + occupied_carriers, fft_length)); +} + +// Consumes 1 packet and produces as many OFDM symbols of fft_length to hold the full packet +digital_ofdm_mapper_bcv::digital_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit, + unsigned int occupied_carriers, unsigned int fft_length) + : gr_sync_block ("ofdm_mapper_bcv", + gr_make_io_signature (0, 0, 0), + gr_make_io_signature2 (1, 2, sizeof(gr_complex)*fft_length, sizeof(char))), + d_constellation(constellation), + d_msgq(gr_make_msg_queue(msgq_limit)), d_msg_offset(0), d_eof(false), + d_occupied_carriers(occupied_carriers), + d_fft_length(fft_length), + d_bit_offset(0), + d_pending_flag(0), + d_resid(0), + d_nresid(0) +{ + if (!(d_occupied_carriers <= d_fft_length)) + throw std::invalid_argument("digital_ofdm_mapper_bcv: occupied carriers must be <= fft_length"); + + // this is not the final form of this solution since we still use the occupied_tones concept, + // which would get us into trouble if the number of carriers we seek is greater than the occupied carriers. + // Eventually, we will get rid of the occupied_carriers concept. + std::string carriers = "FE7F"; + + // A bit hacky to fill out carriers to occupied_carriers length + int diff = (d_occupied_carriers - 4*carriers.length()); + while(diff > 7) { + carriers.insert(0, "f"); + carriers.insert(carriers.length(), "f"); + diff -= 8; + } + + // if there's extras left to be processed + // divide remaining to put on either side of current map + // all of this is done to stick with the concept of a carrier map string that + // can be later passed by the user, even though it'd be cleaner to just do this + // on the carrier map itself + int diff_left=0; + int diff_right=0; + + // dictionary to convert from integers to ascii hex representation + char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + if(diff > 0) { + char c[2] = {0,0}; + + diff_left = (int)ceil((float)diff/2.0f); // number of carriers to put on the left side + c[0] = abc[(1 << diff_left) - 1]; // convert to bits and move to ASCI integer + carriers.insert(0, c); + + diff_right = diff - diff_left; // number of carriers to put on the right side + c[0] = abc[0xF^((1 << diff_right) - 1)]; // convert to bits and move to ASCI integer + carriers.insert(carriers.length(), c); + } + + // find out how many zeros to pad on the sides; the difference between the fft length and the subcarrier + // mapping size in chunks of four. This is the number to pack on the left and this number plus any + // residual nulls (if odd) will be packed on the right. + diff = (d_fft_length/4 - carriers.length())/2; + + unsigned int i,j,k; + for(i = 0; i < carriers.length(); i++) { + char c = carriers[i]; // get the current hex character from the string + for(j = 0; j < 4; j++) { // walk through all four bits + k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1; // convert to int and extract next bit + if(k) { // if bit is a 1, + d_subcarrier_map.push_back(4*(i+diff) + j); // use this subcarrier + } + } + } + + // make sure we stay in the limit currently imposed by the occupied_carriers + if(d_subcarrier_map.size() > d_occupied_carriers) { + throw std::invalid_argument("digital_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers"); + } + + d_nbits = (unsigned long)ceil(log10(float(d_constellation.size())) / log10(2.0)); +} + +digital_ofdm_mapper_bcv::~digital_ofdm_mapper_bcv(void) +{ +} + +int digital_ofdm_mapper_bcv::randsym() +{ + return (rand() % d_constellation.size()); +} + +int +digital_ofdm_mapper_bcv::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex *out = (gr_complex *)output_items[0]; + + unsigned int i=0; + + //printf("OFDM BPSK Mapper: ninput_items: %d noutput_items: %d\n", ninput_items[0], noutput_items); + + if(d_eof) { + return -1; + } + + if(!d_msg) { + d_msg = d_msgq->delete_head(); // block, waiting for a message + d_msg_offset = 0; + d_bit_offset = 0; + d_pending_flag = 1; // new packet, write start of packet flag + + if((d_msg->length() == 0) && (d_msg->type() == 1)) { + d_msg.reset(); + return -1; // We're done; no more messages coming. + } + } + + char *out_flag = 0; + if(output_items.size() == 2) + out_flag = (char *) output_items[1]; + + + // Build a single symbol: + // Initialize all bins to 0 to set unused carriers + memset(out, 0, d_fft_length*sizeof(gr_complex)); + + i = 0; + unsigned char bits = 0; + //while((d_msg_offset < d_msg->length()) && (i < d_occupied_carriers)) { + while((d_msg_offset < d_msg->length()) && (i < d_subcarrier_map.size())) { + + // need new data to process + if(d_bit_offset == 0) { + d_msgbytes = d_msg->msg()[d_msg_offset]; + //printf("mod message byte: %x\n", d_msgbytes); + } + + if(d_nresid > 0) { + // take the residual bits, fill out nbits with info from the new byte, and put them in the symbol + d_resid |= (((1 << d_nresid)-1) & d_msgbytes) << (d_nbits - d_nresid); + bits = d_resid; + + out[d_subcarrier_map[i]] = d_constellation[bits]; + i++; + + d_bit_offset += d_nresid; + d_nresid = 0; + d_resid = 0; + //printf("mod bit(r): %x resid: %x nresid: %d bit_offset: %d\n", + // bits, d_resid, d_nresid, d_bit_offset); + } + else { + if((8 - d_bit_offset) >= d_nbits) { // test to make sure we can fit nbits + // take the nbits number of bits at a time from the byte to add to the symbol + bits = ((1 << d_nbits)-1) & (d_msgbytes >> d_bit_offset); + d_bit_offset += d_nbits; + + out[d_subcarrier_map[i]] = d_constellation[bits]; + i++; + } + else { // if we can't fit nbits, store them for the next + // saves d_nresid bits of this message where d_nresid < d_nbits + unsigned int extra = 8-d_bit_offset; + d_resid = ((1 << extra)-1) & (d_msgbytes >> d_bit_offset); + d_bit_offset += extra; + d_nresid = d_nbits - extra; + } + + } + + if(d_bit_offset == 8) { + d_bit_offset = 0; + d_msg_offset++; + } + } + + // Ran out of data to put in symbol + if (d_msg_offset == d_msg->length()) { + if(d_nresid > 0) { + d_resid |= 0x00; + bits = d_resid; + d_nresid = 0; + d_resid = 0; + } + + //while(i < d_occupied_carriers) { // finish filling out the symbol + while(i < d_subcarrier_map.size()) { // finish filling out the symbol + out[d_subcarrier_map[i]] = d_constellation[randsym()]; + + i++; + } + + if (d_msg->type() == 1) // type == 1 sets EOF + d_eof = true; + d_msg.reset(); // finished packet, free message + assert(d_bit_offset == 0); + } + + if (out_flag) + out_flag[0] = d_pending_flag; + d_pending_flag = 0; + + return 1; // produced symbol +} diff --git a/gr-digital/lib/digital_ofdm_sampler.cc b/gr-digital/lib/digital_ofdm_sampler.cc new file mode 100644 index 000000000..cab8c2ba9 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_sampler.cc @@ -0,0 +1,133 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_sampler.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <cstdio> + +digital_ofdm_sampler_sptr +digital_make_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_sampler (fft_length, symbol_length, timeout)); +} + +digital_ofdm_sampler::digital_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout) + : gr_block ("ofdm_sampler", + gr_make_io_signature2 (2, 2, sizeof (gr_complex), sizeof(char)), + gr_make_io_signature2 (2, 2, sizeof (gr_complex)*fft_length, sizeof(char)*fft_length)), + d_state(STATE_NO_SIG), d_timeout_max(timeout), d_fft_length(fft_length), d_symbol_length(symbol_length) +{ + set_relative_rate(1.0/(double) fft_length); // buffer allocator hint +} + +void +digital_ofdm_sampler::forecast (int noutput_items, gr_vector_int &ninput_items_required) +{ + // FIXME do we need more + //int nreqd = (noutput_items-1) * d_symbol_length + d_fft_length; + int nreqd = d_symbol_length + d_fft_length; + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = nreqd; +} + + +int +digital_ofdm_sampler::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *iptr = (const gr_complex *) input_items[0]; + const char *trigger = (const char *) input_items[1]; + + gr_complex *optr = (gr_complex *) output_items[0]; + char *outsig = (char *) output_items[1]; + + //FIXME: we only process a single OFDM symbol at a time; after the preamble, we can + // process a few at a time as long as we always look out for the next preamble. + + unsigned int index=d_fft_length; // start one fft length into the input so we can always look back this far + + outsig[0] = 0; // set output to no signal by default + + // Search for a preamble trigger signal during the next symbol length + while((d_state != STATE_PREAMBLE) && (index <= (d_symbol_length+d_fft_length))) { + if(trigger[index]) { + outsig[0] = 1; // tell the next block there is a preamble coming + d_state = STATE_PREAMBLE; + } + else + index++; + } + + unsigned int i, pos, ret; + switch(d_state) { + case(STATE_PREAMBLE): + // When we found a preamble trigger, get it and set the symbol boundary here + for(i = (index - d_fft_length + 1); i <= index; i++) { + *optr++ = iptr[i]; + } + + d_timeout = d_timeout_max; // tell the system to expect at least this many symbols for a frame + d_state = STATE_FRAME; + consume_each(index - d_fft_length + 1); // consume up to one fft_length away to keep the history + ret = 1; + break; + + case(STATE_FRAME): + // use this state when we have processed a preamble and are getting the rest of the frames + //FIXME: we could also have a power squelch system here to enter STATE_NO_SIG if no power is received + + // skip over fft length history and cyclic prefix + pos = d_symbol_length; // keeps track of where we are in the input buffer + while(pos < d_symbol_length + d_fft_length) { + *optr++ = iptr[pos++]; + } + + if(d_timeout-- == 0) { + printf("TIMEOUT\n"); + d_state = STATE_NO_SIG; + } + + consume_each(d_symbol_length); // jump up by 1 fft length and the cyclic prefix length + ret = 1; + break; + + case(STATE_NO_SIG): + default: + consume_each(index-d_fft_length); // consume everything we've gone through so far leaving the fft length history + ret = 0; + break; + } + + return ret; +} diff --git a/gr-digital/python/.gitignore b/gr-digital/python/.gitignore new file mode 100644 index 000000000..604b402c5 --- /dev/null +++ b/gr-digital/python/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +/run_tests diff --git a/gr-digital/python/Makefile.am b/gr-digital/python/Makefile.am new file mode 100644 index 000000000..ead6f7dfd --- /dev/null +++ b/gr-digital/python/Makefile.am @@ -0,0 +1,70 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +TESTS = +EXTRA_DIST += run_tests.in + +if PYTHON +SUBDIRS = utils +TESTS += run_tests + +digitaldir = $(grpythondir)/digital + +noinst_PYTHON = \ + qa_digital.py \ + qa_binary_slicer_fb.py \ + qa_clock_recovery_mm.py \ + qa_cma_equalizer.py \ + qa_cpm.py \ + qa_constellation.py \ + qa_constellation_receiver.py \ + qa_constellation_decoder_cb.py \ + qa_correlate_access_code.py \ + qa_costas_loop_cc.py \ + qa_crc32.py \ + qa_fll_band_edge.py \ + qa_lms_equalizer.py \ + qa_mpsk_receiver.py \ + qa_ofdm_insert_preamble.py + +digital_PYTHON = \ + __init__.py \ + bpsk.py \ + cpm.py \ + crc.py \ + generic_mod_demod.py \ + gmsk.py \ + modulation_utils.py \ + ofdm.py \ + ofdm_packet_utils.py \ + ofdm_receiver.py \ + ofdm_sync_fixed.py \ + ofdm_sync_ml.py \ + ofdm_sync_pnac.py \ + ofdm_sync_pn.py \ + packet_utils.py \ + pkt.py \ + psk.py \ + qam.py \ + qpsk.py +endif diff --git a/gr-digital/python/__init__.py b/gr-digital/python/__init__.py new file mode 100644 index 000000000..7c76183c9 --- /dev/null +++ b/gr-digital/python/__init__.py @@ -0,0 +1,46 @@ +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +''' +This is the gr-digital package. It contains all of the blocks, +utilities, and examples for doing digital modulation and demodulation. +''' + +# The presence of this file turns this directory into a Python package + +from digital_swig import * +from psk import * +from qam import * +from bpsk import * +from qpsk import * +from gmsk import * +from cpm import * +from pkt import * +from crc import * +from modulation_utils import * +from ofdm import * +from ofdm_receiver import * +from ofdm_sync_fixed import * +from ofdm_sync_ml import * +from ofdm_sync_pnac import * +from ofdm_sync_pn import * + +import packet_utils +import ofdm_packet_utils diff --git a/gr-digital/python/bpsk.py b/gr-digital/python/bpsk.py new file mode 100644 index 000000000..0d8f05c4c --- /dev/null +++ b/gr-digital/python/bpsk.py @@ -0,0 +1,171 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +BPSK modulation and demodulation. +""" + +from math import pi, log +from cmath import exp + +from gnuradio import gr +from gnuradio.digital.generic_mod_demod import generic_mod, generic_demod +import digital_swig +import modulation_utils + +# Default number of points in constellation. +_def_constellation_points = 2 +# Whether differential coding is used. +_def_differential = False + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def bpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("BPSK can only have 2 constellation points.") + return digital_swig.constellation_bpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class bpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=False, *args, **kwargs): + + """ + 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. + + See generic_mod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for BPSK.') + super(bpsk_mod, self).__init__(constellation=constellation, + differential=differential, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class bpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=False, *args, **kwargs): + + """ + 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. + + See generic_demod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for BPSK.') + super(bpsk_demod, self).__init__(constellation=constellation, + differential=differential, *args, **kwargs) + + + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def dbpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("DBPSK can only have 2 constellation points.") + return digital_swig.constellation_dbpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=True, *args, **kwargs): + + """ + Hierarchical block for RRC-filtered DBPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for DBPSK.') + super(dbpsk_mod, self).__init__(constellation=constellation, + differential=True, + *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=True, *args, **kwargs): + + """ + Hierarchical block for RRC-filtered DBPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for DBPSK.') + super(dbpsk_demod, self).__init__(constellation=constellation, + differential=True, + *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('bpsk', bpsk_mod) +modulation_utils.add_type_1_demod('bpsk', bpsk_demod) +modulation_utils.add_type_1_constellation('bpsk', bpsk_constellation) +modulation_utils.add_type_1_mod('dbpsk', dbpsk_mod) +modulation_utils.add_type_1_demod('dbpsk', dbpsk_demod) +modulation_utils.add_type_1_constellation('dbpsk', dbpsk_constellation) diff --git a/gr-digital/python/cpm.py b/gr-digital/python/cpm.py new file mode 100644 index 000000000..05032336d --- /dev/null +++ b/gr-digital/python/cpm.py @@ -0,0 +1,247 @@ +# +# CPM modulation and demodulation. +# +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# See gnuradio-examples/python/digital for examples + +from gnuradio import gr, blks2 +from math import pi +import numpy + +import digital_swig +import modulation_utils + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_bits_per_symbol = 1 +_def_h_numerator = 1 +_def_h_denominator = 2 +_def_cpm_type = 0 # 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL +_def_bt = 0.35 +_def_symbols_per_pulse = 1 +_def_generic_taps = numpy.empty(1) +_def_verbose = False +_def_log = False + + +# ///////////////////////////////////////////////////////////////////////////// +# CPM modulator +# ///////////////////////////////////////////////////////////////////////////// + +class cpm_mod(gr.hier_block2): + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bits_per_symbol=_def_bits_per_symbol, + h_numerator=_def_h_numerator, + h_denominator=_def_h_denominator, + cpm_type=_def_cpm_type, + bt=_def_bt, + symbols_per_pulse=_def_symbols_per_pulse, + generic_taps=_def_generic_taps, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Continuous Phase + modulation. + + The input is a byte stream (unsigned char) + representing packed bits and the + output is the complex modulated signal at baseband. + + See Proakis for definition of generic CPM signals: + s(t)=exp(j phi(t)) + phi(t)= 2 pi h int_0^t f(t') dt' + f(t)=sum_k a_k g(t-kT) + (normalizing assumption: int_0^infty g(t) dt = 1/2) + + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param bits_per_symbol: bits per symbol + @type bits_per_symbol: integer + @param h_numerator: numerator of modulation index + @type h_numerator: integer + @param h_denominator: denominator of modulation index (numerator and denominator must be relative primes) + @type h_denominator: integer + @param cpm_type: supported types are: 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL + @type cpm_type: integer + @param bt: bandwidth symbol time product for GMSK + @type bt: float + @param symbols_per_pulse: shaping pulse duration in symbols + @type symbols_per_pulse: integer + @param generic_taps: define a generic CPM pulse shape (sum = samples_per_symbol/2) + @type generic_taps: array of floats + + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modulation data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "cpm_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bits_per_symbol = bits_per_symbol + self._h_numerator = h_numerator + self._h_denominator = h_denominator + self._cpm_type = cpm_type + self._bt=bt + if cpm_type == 0 or cpm_type == 2 or cpm_type == 3: # CPFSK, RC, Generic + self._symbols_per_pulse = symbols_per_pulse + elif cpm_type == 1: # GMSK + self._symbols_per_pulse = 4 + else: + raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,)) + + self._generic_taps=numpy.array(generic_taps) + + if samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must be >= 2, is %r" % (samples_per_symbol,)) + + self.nsymbols = 2**bits_per_symbol + self.sym_alphabet = numpy.arange(-(self.nsymbols-1),self.nsymbols,2).tolist() + + + self.ntaps = int(self._symbols_per_pulse * samples_per_symbol) + sensitivity = 2 * pi * h_numerator / h_denominator / samples_per_symbol + + # Unpack Bytes into bits_per_symbol groups + self.B2s = gr.packed_to_unpacked_bb(bits_per_symbol,gr.GR_MSB_FIRST) + + + # Turn it into symmetric PAM data. + self.pam = gr.chunks_to_symbols_bf(self.sym_alphabet,1) + + # Generate pulse (sum of taps = samples_per_symbol/2) + if cpm_type == 0: # CPFSK + self.taps= (1.0/self._symbols_per_pulse/2,) * self.ntaps + elif cpm_type == 1: # GMSK + gaussian_taps = gr.firdes.gaussian( + 1.0/2, # gain + samples_per_symbol, # symbol_rate + bt, # bandwidth * symbol time + self.ntaps # number of taps + ) + sqwave = (1,) * samples_per_symbol # rectangular window + self.taps = numpy.convolve(numpy.array(gaussian_taps),numpy.array(sqwave)) + elif cpm_type == 2: # Raised Cosine + # generalize it for arbitrary roll-off factor + self.taps = (1-numpy.cos(2*pi*numpy.arange(0,self.ntaps)/samples_per_symbol/self._symbols_per_pulse))/(2*self._symbols_per_pulse) + elif cpm_type == 3: # Generic CPM + self.taps = generic_taps + else: + raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,)) + + self.filter = blks2.pfb_arb_resampler_fff(samples_per_symbol, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect + self.connect(self, self.B2s, self.pam, self.filter, self.fmmod, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): + return self._bits_per_symbol + + def h_numerator(self): + return self._h_numerator + + def h_denominator(self): + return self._h_denominator + + def cpm_type(self): + return self._cpm_type + + def bt(self): + return self._bt + + def symbols_per_pulse(self): + return self._symbols_per_pulse + + + def _print_verbage(self): + print "Samples per symbol = %d" % self._samples_per_symbol + print "Bits per symbol = %d" % self._bits_per_symbol + print "h = " , self._h_numerator , " / " , self._h_denominator + print "Symbol alphabet = " , self.sym_alphabet + print "Symbols per pulse = %d" % self._symbols_per_pulse + print "taps = " , self.taps + + print "CPM type = %d" % self._cpm_type + if self._cpm_type == 1: + print "Gaussian filter BT = %.2f" % self._bt + + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.B2s, + gr.file_sink(gr.sizeof_float, "symbols.dat")) + self.connect(self.pam, + gr.file_sink(gr.sizeof_float, "pam.dat")) + self.connect(self.filter, + gr.file_sink(gr.sizeof_float, "filter.dat")) + self.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat")) + + + def add_options(parser): + """ + Adds CPM modulation-specific options to the standard parser + """ + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(cpm_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# CPM demodulator +# ///////////////////////////////////////////////////////////////////////////// +# +# Not yet implemented +# + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('cpm', cpm_mod) +#modulation_utils.add_type_1_demod('cpm', cpm_demod) diff --git a/gr-digital/python/crc.py b/gr-digital/python/crc.py new file mode 100644 index 000000000..198ab059f --- /dev/null +++ b/gr-digital/python/crc.py @@ -0,0 +1,38 @@ +# +# Copyright 2005,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gru +import digital_swig +import struct + +def gen_and_append_crc32(s): + crc = digital_swig.crc32(s) + return s + struct.pack(">I", gru.hexint(crc) & 0xFFFFFFFF) + +def check_crc32(s): + if len(s) < 4: + return (False, '') + msg = s[:-4] + #print "msg = '%s'" % (msg,) + actual = digital_swig.crc32(msg) + (expected,) = struct.unpack(">I", s[-4:]) + # print "actual =", hex(actual), "expected =", hex(expected) + return (actual == expected, msg) diff --git a/gr-digital/python/generic_mod_demod.py b/gr-digital/python/generic_mod_demod.py new file mode 100644 index 000000000..ae876e108 --- /dev/null +++ b/gr-digital/python/generic_mod_demod.py @@ -0,0 +1,387 @@ +# +# Copyright 2005,2006,2007,2009,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# See gnuradio-examples/python/digital for examples + +""" +Generic modulation and demodulation. +""" + +from gnuradio import gr +from modulation_utils import extract_kwargs_from_options_for_class +from utils import mod_codes +import digital_swig +import math + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_verbose = False +_def_log = False + +# Frequency correction +_def_freq_bw = 2*math.pi/100.0 +# Symbol timing recovery +_def_timing_bw = 2*math.pi/100.0 +_def_timing_max_dev = 1.5 +# Fine frequency / Phase correction +_def_phase_bw = 2*math.pi/100.0 +# Number of points in constellation +_def_constellation_points = 16 +# Whether differential coding is used. +_def_differential = False + +def add_common_options(parser): + """ + Sets options common to both modulator and demodulator. + """ + parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points, + help="set the number of constellation points (must be a power of 2 for psk, power of 4 for QAM) [default=%default]") + parser.add_option("", "--non-differential", action="store_false", + dest="differential", + help="do not use differential encoding [default=False]") + parser.add_option("", "--differential", action="store_true", + dest="differential", default=True, + help="use differential encoding [default=%default]") + parser.add_option("", "--mod-code", type="choice", choices=mod_codes.codes, + default=mod_codes.NO_CODE, + help="Select modulation code from: %s [default=%%default]" + % (', '.join(mod_codes.codes),)) + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default]") + + +# ///////////////////////////////////////////////////////////////////////////// +# Generic modulator +# ///////////////////////////////////////////////////////////////////////////// + +class generic_mod(gr.hier_block2): + + def __init__(self, constellation, + samples_per_symbol=_def_samples_per_symbol, + differential=_def_differential, + excess_bw=_def_excess_bw, + gray_coded=True, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param constellation: determines the modulation type + @type constellation: gnuradio.digital.gr_constellation + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param gray_coded: turn gray coding on/off + @type gray_coded: bool + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Log modulation data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "generic_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._constellation = constellation.base() + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._differential = differential + + if self._samples_per_symbol < 2: + raise TypeError, ("sbp must be >= 2, is %f" % self._samples_per_symbol) + + arity = pow(2,self.bits_per_symbol()) + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) + + if gray_coded == True: + self.symbol_mapper = gr.map_bb(self._constellation.pre_diff_code()) + + if differential: + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.points()) + + # pulse shaping filter + nfilts = 32 + ntaps = nfilts * 11 * int(self._samples_per_symbol) # make nfilts filters of ntaps each + self.rrc_taps = gr.firdes.root_raised_cosine( + nfilts, # gain + nfilts, # sampling rate based on 32 filters in resampler + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, + self.rrc_taps) + + # Connect + blocks = [self, self.bytes2chunks] + if gray_coded == True: + blocks.append(self.symbol_mapper) + if differential: + blocks.append(self.diffenc) + blocks += [self.chunks2symbols, self.rrc_filter, self] + self.connect(*blocks) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # static method that's also callable on an instance + return self._constellation.bits_per_symbol() + + def add_options(parser): + """ + Adds generic modulation options to the standard parser + """ + add_common_options(parser) + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) + + + def _print_verbage(self): + print "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.bytes2chunks, + gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.8b")) + if self._constellation.apply_pre_diff_code(): + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_symbol_mapper.8b")) + if self._differential: + self.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "tx_diffenc.8b")) + self.connect(self.chunks2symbols, + gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.32fc")) + self.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.32fc")) + + +# ///////////////////////////////////////////////////////////////////////////// +# Generic demodulator +# +# Differentially coherent detection of differentially encoded generically +# modulated signal. +# ///////////////////////////////////////////////////////////////////////////// + +class generic_demod(gr.hier_block2): + + def __init__(self, constellation, + samples_per_symbol=_def_samples_per_symbol, + differential=_def_differential, + excess_bw=_def_excess_bw, + gray_coded=True, + freq_bw=_def_freq_bw, + timing_bw=_def_timing_bw, + phase_bw=_def_phase_bw, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param constellation: determines the modulation type + @type constellation: gnuradio.digital.gr_constellation + @param samples_per_symbol: samples per symbol >= 2 + @type samples_per_symbol: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param gray_coded: turn gray coding on/off + @type gray_coded: bool + @param freq_bw: loop filter lock-in bandwidth + @type freq_bw: float + @param timing_bw: timing recovery loop lock-in bandwidth + @type timing_bw: float + @param phase_bw: phase recovery loop bandwidth + @type phase_bw: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "generic_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._constellation = constellation.base() + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._phase_bw = phase_bw + self._freq_bw = freq_bw + self._timing_bw = timing_bw + self._timing_max_dev= _def_timing_max_dev + self._differential = differential + + if self._samples_per_symbol < 2: + raise TypeError, ("sbp must be >= 2, is %d" % self._samples_per_symbol) + + arity = pow(2,self.bits_per_symbol()) + + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + + # Automatic gain control + self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) + + # Frequency correction + fll_ntaps = 55 + self.freq_recov = digital_swig.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw, + fll_ntaps, self._freq_bw) + + # symbol timing recovery with RRC data filter + taps = gr.firdes.root_raised_cosine(nfilts, nfilts*self._samples_per_symbol, + 1.0, self._excess_bw, ntaps) + self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol, + self._timing_bw, taps, + nfilts, nfilts//2, self._timing_max_dev) + + fmin = -0.25 + fmax = 0.25 + self.receiver = digital_swig.constellation_receiver_cb( + self._constellation, self._phase_bw, + fmin, fmax) + + # Do differential decoding based on phase change of symbols + if differential: + self.diffdec = gr.diff_decoder_bb(arity) + + if gray_coded: + self.symbol_mapper = gr.map_bb( + mod_codes.invert_code(self._constellation.pre_diff_code())) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol()) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect and Initialize base class + blocks = [self, self.agc, self.freq_recov, + self.time_recov, self.receiver] + if differential: + blocks.append(self.diffdec) + if self._constellation.apply_pre_diff_code(): + blocks.append(self.symbol_mapper) + blocks += [self.unpack, self] + self.connect(*blocks) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # staticmethod that's also callable on an instance + return self._constellation.bits_per_symbol() + + def _print_verbage(self): + print "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + print "FLL bandwidth: %.2e" % self._freq_bw + print "Timing bandwidth: %.2e" % self._timing_bw + print "Phase bandwidth: %.2e" % self._phase_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "rx_agc.32fc")) + self.connect((self.freq_recov, 0), + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.32fc")) + self.connect((self.freq_recov, 1), + gr.file_sink(gr.sizeof_float, "rx_freq_recov_freq.32f")) + self.connect((self.freq_recov, 2), + gr.file_sink(gr.sizeof_float, "rx_freq_recov_phase.32f")) + self.connect((self.freq_recov, 3), + gr.file_sink(gr.sizeof_float, "rx_freq_recov_error.32f")) + self.connect((self.time_recov, 0), + gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.32fc")) + self.connect((self.time_recov, 1), + gr.file_sink(gr.sizeof_float, "rx_time_recov_error.32f")) + self.connect((self.time_recov, 2), + gr.file_sink(gr.sizeof_float, "rx_time_recov_rate.32f")) + self.connect((self.time_recov, 3), + gr.file_sink(gr.sizeof_float, "rx_time_recov_phase.32f")) + self.connect((self.receiver, 0), + gr.file_sink(gr.sizeof_char, "rx_receiver.8b")) + self.connect((self.receiver, 1), + gr.file_sink(gr.sizeof_float, "rx_receiver_error.32f")) + self.connect((self.receiver, 2), + gr.file_sink(gr.sizeof_float, "rx_receiver_phase.32f")) + self.connect((self.receiver, 3), + gr.file_sink(gr.sizeof_float, "rx_receiver_freq.32f")) + if self._differential: + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_char, "rx_diffdec.8b")) + if self._constellation.apply_pre_diff_code(): + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "rx_symbol_mapper.8b")) + self.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "rx_unpack.8b")) + + def add_options(parser): + """ + Adds generic demodulation options to the standard parser + """ + # Add options shared with modulator. + add_common_options(parser) + # Add options specific to demodulator. + parser.add_option("", "--freq-bw", type="float", default=_def_freq_bw, + help="set frequency lock loop lock-in bandwidth [default=%default]") + parser.add_option("", "--phase-bw", type="float", default=_def_phase_bw, + help="set phase tracking loop lock-in bandwidth [default=%default]") + parser.add_option("", "--timing-bw", type="float", default=_def_timing_bw, + help="set timing symbol sync loop gain lock-in bandwidth [default=%default]") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) + diff --git a/gr-digital/python/gmsk.py b/gr-digital/python/gmsk.py new file mode 100644 index 000000000..70fa197e3 --- /dev/null +++ b/gr-digital/python/gmsk.py @@ -0,0 +1,276 @@ +# +# GMSK modulation and demodulation. +# +# +# Copyright 2005,2006,2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# See gnuradio-examples/python/digital for examples + +from gnuradio import gr +import digital_swig +import modulation_utils + +from math import pi +import numpy +from pprint import pprint +import inspect + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_bt = 0.35 +_def_verbose = False +_def_log = False + +# Symbol timing recovery +_def_timing_bw = 2*pi/100.0 +_def_timing_max_dev = 1.5 + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_mod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bt=_def_bt, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param bt: Gaussian filter bandwidth * symbol time + @type bt: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "gmsk_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bt = bt + + self._differential = False # make consistant with other modulators + + if samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must >= 2, is %r" % \ + (samples_per_symbol,)) + + ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once + sensitivity = (pi / 2) / samples_per_symbol # phase change per bit = pi / 2 + + # Turn it into NRZ data. + self.nrz = gr.bytes_to_syms() + + # Form Gaussian filter + # Generate Gaussian response (Needs to be convolved with window below). + self.gaussian_taps = gr.firdes.gaussian( + 1, # gain + samples_per_symbol, # symbol_rate + bt, # bandwidth * symbol time + int(ntaps) # number of taps + ) + + self.sqwave = (1,) * int(samples_per_symbol) # rectangular window + self.taps = numpy.convolve(numpy.array(self.gaussian_taps),numpy.array(self.sqwave)) + self.gaussian_filter = gr.pfb_arb_resampler_fff(samples_per_symbol, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.nrz, self.gaussian_filter, self.fmmod, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gaussian filter bt = %.2f" % self._bt + + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.nrz, + gr.file_sink(gr.sizeof_float, "tx_gmsk_nrz.32f")) + self.connect(self.gaussian_filter, + gr.file_sink(gr.sizeof_float, "tx_gmsk_gaussian_filter.32f")) + self.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "tx_gmsk_fmmod.32fc")) + + + def add_options(parser): + """ + Adds GMSK modulation-specific options to the standard parser + """ + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK demodulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bt=_def_bt, + timing_bw=_def_timing_bw, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (the LSB) + + @param samples_per_symbol: samples per baud + @type samples_per_symbol: integer + @param timing_bw: timing recovery loop lock-in bandwidth + @type timing_bw: float + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Print modualtion data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "gmsk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bt = bt + self._timing_bw = timing_bw + self._timing_max_dev= _def_timing_max_dev + + self._differential = False # make consistant with other modulators + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol + + # Demodulate FM + sensitivity = (pi / 2) / samples_per_symbol + self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) + + # the clock recovery block tracks the symbol clock and resamples as needed. + # the output of the block is a stream of soft symbols (float) + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + taps = gr.firdes.gaussian(nfilts, + nfilts*self._samples_per_symbol, + self._bt, ntaps) + self.clock_recovery = \ + gr.pfb_clock_sync_fff(self._samples_per_symbol, + self._timing_bw, taps, + nfilts, nfilts//2, self._timing_max_dev) + + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample + self.slicer = digital_swig.binary_slicer_fb() + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol: %d" % self.bits_per_symbol() + print "Bandwidth-Time Prod: %f" % self._bt + print "Timing bandwidth: %.2e" % self._timing_bw + + + def _setup_logging(self): + print "Demodulation logging turned on." + self.connect(self.fmdemod, + gr.file_sink(gr.sizeof_float, "rx_gmsk_fmdemod.32f")) + self.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_float, "rx_gmsk_clock_recovery.32f")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "rx_gmsk_slicer.8b")) + + def add_options(parser): + """ + Adds GMSK demodulation-specific options to the standard parser + """ + parser.add_option("", "--timing-bw", type="float", default=_def_timing_bw, + help="set timing symbol sync loop gain lock-in bandwidth [default=%default]") + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('gmsk', gmsk_mod) +modulation_utils.add_type_1_demod('gmsk', gmsk_demod) diff --git a/gr-digital/python/modulation_utils.py b/gr-digital/python/modulation_utils.py new file mode 100644 index 000000000..cb3a9812d --- /dev/null +++ b/gr-digital/python/modulation_utils.py @@ -0,0 +1,102 @@ +# +# Copyright 2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Miscellaneous utilities for managing mods and demods, as well as other items +useful in dealing with generalized handling of different modulations and demods. +""" + +import inspect + + +# Type 1 modulators accept a stream of bytes on their input and produce complex baseband output +_type_1_modulators = {} + +def type_1_mods(): + return _type_1_modulators + +def add_type_1_mod(name, mod_class): + _type_1_modulators[name] = mod_class + + +# Type 1 demodulators accept complex baseband input and produce a stream of bits, packed +# 1 bit / byte as their output. Their output is completely unambiguous. There is no need +# to resolve phase or polarity ambiguities. +_type_1_demodulators = {} + +def type_1_demods(): + return _type_1_demodulators + +def add_type_1_demod(name, demod_class): + _type_1_demodulators[name] = demod_class + +# Also record the constellation making functions of the modulations +_type_1_constellations = {} + +def type_1_constellations(): + return _type_1_constellations + +def add_type_1_constellation(name, constellation): + _type_1_constellations[name] = constellation + + +def extract_kwargs_from_options(function, excluded_args, options): + """ + Given a function, a list of excluded arguments and the result of + parsing command line options, create a dictionary of key word + arguments suitable for passing to the function. The dictionary + will be populated with key/value pairs where the keys are those + that are common to the function's argument list (minus the + excluded_args) and the attributes in options. The values are the + corresponding values from options unless that value is None. + In that case, the corresponding dictionary entry is not populated. + + (This allows different modulations that have the same parameter + names, but different default values to coexist. The downside is + that --help in the option parser will list the default as None, + but in that case the default provided in the __init__ argument + list will be used since there is no kwargs entry.) + + @param function: the function whose parameter list will be examined + @param excluded_args: function arguments that are NOT to be added to the dictionary + @type excluded_args: sequence of strings + @param options: result of command argument parsing + @type options: optparse.Values + """ + + # Try this in C++ ;) + args, varargs, varkw, defaults = inspect.getargspec(function) + d = {} + for kw in [a for a in args if a not in excluded_args]: + if hasattr(options, kw): + if getattr(options, kw) is not None: + d[kw] = getattr(options, kw) + return d + +def extract_kwargs_from_options_for_class(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + d = extract_kwargs_from_options( + cls.__init__, ('self',), options) + for base in cls.__bases__: + if hasattr(base, 'extract_kwargs_from_options'): + d.update(base.extract_kwargs_from_options(options)) + return d diff --git a/gr-digital/python/ofdm.py b/gr-digital/python/ofdm.py new file mode 100644 index 000000000..9f57920ef --- /dev/null +++ b/gr-digital/python/ofdm.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from gnuradio import gr +import digital_swig +import ofdm_packet_utils +from ofdm_receiver import ofdm_receiver +import gnuradio.gr.gr_threading as _threading +import psk, qam + +# ///////////////////////////////////////////////////////////////////////////// +# mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class ofdm_mod(gr.hier_block2): + """ + Modulates an OFDM stream. Based on the options fft_length, occupied_tones, and + cp_length, this block creates OFDM symbols using a specified modulation option. + + Send packets by calling send_pkt + """ + def __init__(self, options, msgq_limit=2, pad_for_usrp=True): + """ + Hierarchical block for sending packets + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param options: pass modulation options from higher layers (fft length, occupied tones, etc.) + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + """ + + gr.hier_block2.__init__(self, "ofdm_mod", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._pad_for_usrp = pad_for_usrp + self._modulation = options.modulation + self._fft_length = options.fft_length + self._occupied_tones = options.occupied_tones + self._cp_length = options.cp_length + + win = [] #[1 for i in range(self._fft_length)] + + # Use freq domain to get doubled-up known symbol for correlation in time domain + zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0)) + ksfreq = known_symbols_4512_3[0:self._occupied_tones] + for i in range(len(ksfreq)): + if((zeros_on_left + i) & 1): + ksfreq[i] = 0 + + # hard-coded known symbols + preambles = (ksfreq,) + + padded_preambles = list() + for pre in preambles: + padded = self._fft_length*[0,] + padded[zeros_on_left : zeros_on_left + self._occupied_tones] = pre + padded_preambles.append(padded) + + symbol_length = options.fft_length + options.cp_length + + mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256} + arity = mods[self._modulation] + + rot = 1 + if self._modulation == "qpsk": + rot = (0.707+0.707j) + + # FIXME: pass the constellation objects instead of just the points + if(self._modulation.find("psk") >= 0): + constel = psk.psk_constellation(arity) + rotated_const = map(lambda pt: pt * rot, constel.points()) + elif(self._modulation.find("qam") >= 0): + constel = qam.qam_constellation(arity) + rotated_const = map(lambda pt: pt * rot, constel.points()) + #print rotated_const + self._pkt_input = digital_swig.ofdm_mapper_bcv(rotated_const, + msgq_limit, + options.occupied_tones, + options.fft_length) + + self.preambles = digital_swig.ofdm_insert_preamble(self._fft_length, + padded_preambles) + self.ifft = gr.fft_vcc(self._fft_length, False, win, True) + self.cp_adder = digital_swig.ofdm_cyclic_prefixer(self._fft_length, + symbol_length) + self.scale = gr.multiply_const_cc(1.0 / math.sqrt(self._fft_length)) + + self.connect((self._pkt_input, 0), (self.preambles, 0)) + self.connect((self._pkt_input, 1), (self.preambles, 1)) + self.connect(self.preambles, self.ifft, self.cp_adder, self.scale, self) + + if options.verbose: + self._print_verbage() + + if options.log: + self.connect(self._pkt_input, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_mapper_c.dat")) + self.connect(self.preambles, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_preambles.dat")) + self.connect(self.ifft, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_ifft_c.dat")) + self.connect(self.cp_adder, gr.file_sink(gr.sizeof_gr_complex, + "ofdm_cp_adder_c.dat")) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self._pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = ofdm_packet_utils.make_packet(payload, 1, 1, + self._pad_for_usrp, + whitening=True) + + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + self._pkt_input.msgq().insert_tail(msg) + + def add_options(normal, expert): + """ + Adds OFDM-specific options to the Options Parser + """ + normal.add_option("-m", "--modulation", type="string", default="bpsk", + help="set modulation type (bpsk, qpsk, 8psk, qam{16,64}) [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 "Modulation Type: %s" % (self._modulation) + 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_block2): + """ + Demodulates a received OFDM stream. Based on the options fft_length, occupied_tones, and + cp_length, this block performs synchronization, FFT, and demodulation of incoming OFDM + symbols and passes packets up the a higher layer. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, options, callback=None): + """ + Hierarchical block for demodulating and deframing packets. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param options: pass modulation options from higher layers (fft length, occupied tones, etc.) + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + """ + gr.hier_block2.__init__(self, "ofdm_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + + self._modulation = options.modulation + self._fft_length = options.fft_length + self._occupied_tones = options.occupied_tones + self._cp_length = options.cp_length + self._snr = options.snr + + # Use freq domain to get doubled-up known symbol for correlation in time domain + zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0)) + ksfreq = known_symbols_4512_3[0:self._occupied_tones] + for i in range(len(ksfreq)): + if((zeros_on_left + i) & 1): + ksfreq[i] = 0 + + # hard-coded known symbols + preambles = (ksfreq,) + + symbol_length = self._fft_length + self._cp_length + self.ofdm_recv = ofdm_receiver(self._fft_length, + self._cp_length, + self._occupied_tones, + self._snr, preambles, + options.log) + + mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256} + arity = mods[self._modulation] + + rot = 1 + if self._modulation == "qpsk": + rot = (0.707+0.707j) + + # FIXME: pass the constellation objects instead of just the points + if(self._modulation.find("psk") >= 0): + constel = psk.psk_constellation(arity) + rotated_const = map(lambda pt: pt * rot, constel.points()) + elif(self._modulation.find("qam") >= 0): + constel = qam.qam_constellation(arity) + rotated_const = map(lambda pt: pt * rot, constel.points()) + #print rotated_const + + phgain = 0.25 + frgain = phgain*phgain / 4.0 + self.ofdm_demod = digital_swig.ofdm_frame_sink(rotated_const, range(arity), + self._rcvd_pktq, + self._occupied_tones, + phgain, frgain) + + self.connect(self, self.ofdm_recv) + self.connect((self.ofdm_recv, 0), (self.ofdm_demod, 0)) + self.connect((self.ofdm_recv, 1), (self.ofdm_demod, 1)) + + # added output signature to work around bug, though it might not be a bad + # thing to export, anyway + self.connect(self.ofdm_recv.chan_filt, self) + + if options.log: + self.connect(self.ofdm_demod, + gr.file_sink(gr.sizeof_gr_complex*self._occupied_tones, + "ofdm_frame_sink_c.dat")) + else: + self.connect(self.ofdm_demod, + gr.null_sink(gr.sizeof_gr_complex*self._occupied_tones)) + + if options.verbose: + self._print_verbage() + + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + def add_options(normal, expert): + """ + Adds OFDM-specific options to the Options Parser + """ + normal.add_option("-m", "--modulation", type="string", default="bpsk", + help="set modulation type (bpsk or qpsk) [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]") + expert.add_option("", "--snr", type="float", default=30.0, + help="SNR estimate [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 "Modulation Type: %s" % (self._modulation) + print "FFT length: %3d" % (self._fft_length) + print "Occupied Tones: %3d" % (self._occupied_tones) + print "CP length: %3d" % (self._cp_length) + + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = ofdm_packet_utils.unmake_packet(msg.to_string()) + if self.callback: + self.callback(ok, payload) + +# Generating known symbols with: +# i = [2*random.randint(0,1)-1 for i in range(4512)] + +known_symbols_4512_3 = [-1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1] diff --git a/gr-digital/python/ofdm_packet_utils.py b/gr-digital/python/ofdm_packet_utils.py new file mode 100644 index 000000000..d0000e6db --- /dev/null +++ b/gr-digital/python/ofdm_packet_utils.py @@ -0,0 +1,454 @@ +# +# 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import struct +import numpy +from gnuradio import gru +import crc + +def conv_packed_binary_string_to_1_0_string(s): + """ + '\xAF' --> '10101111' + """ + r = [] + for ch in s: + x = ord(ch) + for i in range(7,-1,-1): + t = (x >> i) & 0x1 + r.append(t) + + return ''.join(map(lambda x: chr(x + ord('0')), r)) + +def conv_1_0_string_to_packed_binary_string(s): + """ + '10101111' -> ('\xAF', False) + + Basically the inverse of conv_packed_binary_string_to_1_0_string, + but also returns a flag indicating if we had to pad with leading zeros + to get to a multiple of 8. + """ + if not is_1_0_string(s): + raise ValueError, "Input must be a string containing only 0's and 1's" + + # pad to multiple of 8 + padded = False + rem = len(s) % 8 + if rem != 0: + npad = 8 - rem + s = '0' * npad + s + padded = True + + assert len(s) % 8 == 0 + + r = [] + i = 0 + while i < len(s): + t = 0 + for j in range(8): + t = (t << 1) | (ord(s[i + j]) - ord('0')) + r.append(chr(t)) + i += 8 + return (''.join(r), padded) + + +def is_1_0_string(s): + if not isinstance(s, str): + return False + for ch in s: + if not ch in ('0', '1'): + return False + return True + +def string_to_hex_list(s): + return map(lambda x: hex(ord(x)), s) + + +def whiten(s, o): + sa = numpy.fromstring(s, numpy.uint8) + z = sa ^ random_mask_vec8[o:len(sa)+o] + return z.tostring() + +def dewhiten(s, o): + return whiten(s, o) # self inverse + + +def make_header(payload_len, whitener_offset=0): + # Upper nibble is offset, lower 12 bits is len + val = ((whitener_offset & 0xf) << 12) | (payload_len & 0x0fff) + #print "offset =", whitener_offset, " len =", payload_len, " val=", val + return struct.pack('!HH', val, val) + +def make_packet(payload, samples_per_symbol, bits_per_symbol, + pad_for_usrp=True, whitener_offset=0, whitening=True): + """ + Build a packet, given access code, payload, and whitener offset + + @param payload: packet payload, len [0, 4096] + @param samples_per_symbol: samples per symbol (needed for padding calculation) + @type samples_per_symbol: int + @param bits_per_symbol: (needed for padding calculation) + @type bits_per_symbol: int + @param whitener_offset offset into whitener string to use [0-16) + @param whitening: Turn whitener on or off + @type whitening: bool + + Packet will have access code at the beginning, followed by length, payload + and finally CRC-32. + """ + + if not whitener_offset >=0 and whitener_offset < 16: + raise ValueError, "whitener_offset must be between 0 and 15, inclusive (%i)" % (whitener_offset,) + + payload_with_crc = crc.gen_and_append_crc32(payload) + #print "outbound crc =", string_to_hex_list(payload_with_crc[-4:]) + + L = len(payload_with_crc) + MAXLEN = len(random_mask_tuple) + if L > MAXLEN: + raise ValueError, "len(payload) must be in [0, %d]" % (MAXLEN,) + + pkt_hd = make_header(L, whitener_offset) + pkt_dt = ''.join((payload_with_crc, '\x55')) + packet_length = len(pkt_hd) + len(pkt_dt) + + if pad_for_usrp: + usrp_packing = _npadding_bytes(packet_length, samples_per_symbol, bits_per_symbol) * '\x55' + pkt_dt = pkt_dt + usrp_packing + + if(whitening): + pkt = pkt_hd + whiten(pkt_dt, whitener_offset) + else: + pkt = pkt_hd + pkt_dt + + #print "make_packet: len(pkt) =", len(pkt) + + return pkt + +def _npadding_bytes(pkt_byte_len, samples_per_symbol, bits_per_symbol): + """ + Generate sufficient padding such that each packet ultimately ends + up being a multiple of 512 bytes when sent across the USB. We + send 4-byte samples across the USB (16-bit I and 16-bit Q), thus + we want to pad so that after modulation the resulting packet + is a multiple of 128 samples. + + @param ptk_byte_len: len in bytes of packet, not including padding. + @param samples_per_symbol: samples per bit (1 bit / symbolwidth GMSK) + @type samples_per_symbol: int + @param bits_per_symbol: bits per symbol (log2(modulation order)) + @type bits_per_symbol: int + + @returns number of bytes of padding to append. + """ + modulus = 128 + byte_modulus = gru.lcm(modulus/8, samples_per_symbol) * bits_per_symbol / samples_per_symbol + r = pkt_byte_len % byte_modulus + if r == 0: + return 0 + return byte_modulus - r + + +def unmake_packet(whitened_payload_with_crc, whitener_offset=0, dewhitening=1): + """ + Return (ok, payload) + + @param whitened_payload_with_crc: string + @param whitener_offset offset into whitener string to use [0-16) + @param dewhitening: Turn whitener on or off + @type dewhitening: bool + """ + + if dewhitening: + payload_with_crc = dewhiten(whitened_payload_with_crc, whitener_offset) + else: + payload_with_crc = whitened_payload_with_crc + + ok, payload = crc.check_crc32(payload_with_crc) + + if 0: + print "payload_with_crc =", string_to_hex_list(payload_with_crc) + print "ok = %r, len(payload) = %d" % (ok, len(payload)) + print "payload =", string_to_hex_list(payload) + + return ok, payload + + +# FYI, this PN code is the output of a 15-bit LFSR +random_mask_tuple = ( + 255, 63, 0, 16, 0, 12, 0, 5, 192, 3, 16, 1, 204, 0, 85, 192, + 63, 16, 16, 12, 12, 5, 197, 195, 19, 17, 205, 204, 85, 149, 255, 47, + 0, 28, 0, 9, 192, 6, 208, 2, 220, 1, 153, 192, 106, 208, 47, 28, + 28, 9, 201, 198, 214, 210, 222, 221, 152, 89, 170, 186, 255, 51, 0, 21, + 192, 15, 16, 4, 12, 3, 69, 193, 243, 16, 69, 204, 51, 21, 213, 207, + 31, 20, 8, 15, 70, 132, 50, 227, 85, 137, 255, 38, 192, 26, 208, 11, + 28, 7, 73, 194, 182, 209, 182, 220, 118, 217, 230, 218, 202, 219, 23, 27, + 78, 139, 116, 103, 103, 106, 170, 175, 63, 60, 16, 17, 204, 12, 85, 197, + 255, 19, 0, 13, 192, 5, 144, 3, 44, 1, 221, 192, 89, 144, 58, 236, + 19, 13, 205, 197, 149, 147, 47, 45, 220, 29, 153, 201, 170, 214, 255, 30, + 192, 8, 80, 6, 188, 2, 241, 193, 132, 80, 99, 124, 41, 225, 222, 200, + 88, 86, 186, 190, 243, 48, 69, 212, 51, 31, 85, 200, 63, 22, 144, 14, + 236, 4, 77, 195, 117, 145, 231, 44, 74, 157, 247, 41, 134, 158, 226, 232, + 73, 142, 182, 228, 118, 203, 102, 215, 106, 222, 175, 24, 124, 10, 161, 199, + 56, 82, 146, 189, 173, 177, 189, 180, 113, 183, 100, 118, 171, 102, 255, 106, + 192, 47, 16, 28, 12, 9, 197, 198, 211, 18, 221, 205, 153, 149, 170, 239, + 63, 12, 16, 5, 204, 3, 21, 193, 207, 16, 84, 12, 63, 69, 208, 51, + 28, 21, 201, 207, 22, 212, 14, 223, 68, 88, 51, 122, 149, 227, 47, 9, + 220, 6, 217, 194, 218, 209, 155, 28, 107, 73, 239, 118, 204, 38, 213, 218, + 223, 27, 24, 11, 74, 135, 119, 34, 166, 153, 186, 234, 243, 15, 5, 196, + 3, 19, 65, 205, 240, 85, 132, 63, 35, 80, 25, 252, 10, 193, 199, 16, + 82, 140, 61, 165, 209, 187, 28, 115, 73, 229, 246, 203, 6, 215, 66, 222, + 177, 152, 116, 106, 167, 111, 58, 172, 19, 61, 205, 209, 149, 156, 111, 41, + 236, 30, 205, 200, 85, 150, 191, 46, 240, 28, 68, 9, 243, 70, 197, 242, + 211, 5, 157, 195, 41, 145, 222, 236, 88, 77, 250, 181, 131, 55, 33, 214, + 152, 94, 234, 184, 79, 50, 180, 21, 183, 79, 54, 180, 22, 247, 78, 198, + 180, 82, 247, 125, 134, 161, 162, 248, 121, 130, 162, 225, 185, 136, 114, 230, + 165, 138, 251, 39, 3, 90, 129, 251, 32, 67, 88, 49, 250, 148, 67, 47, + 113, 220, 36, 89, 219, 122, 219, 99, 27, 105, 203, 110, 215, 108, 94, 173, + 248, 125, 130, 161, 161, 184, 120, 114, 162, 165, 185, 187, 50, 243, 85, 133, + 255, 35, 0, 25, 192, 10, 208, 7, 28, 2, 137, 193, 166, 208, 122, 220, + 35, 25, 217, 202, 218, 215, 27, 30, 139, 72, 103, 118, 170, 166, 255, 58, + 192, 19, 16, 13, 204, 5, 149, 195, 47, 17, 220, 12, 89, 197, 250, 211, + 3, 29, 193, 201, 144, 86, 236, 62, 205, 208, 85, 156, 63, 41, 208, 30, + 220, 8, 89, 198, 186, 210, 243, 29, 133, 201, 163, 22, 249, 206, 194, 212, + 81, 159, 124, 104, 33, 238, 152, 76, 106, 181, 239, 55, 12, 22, 133, 206, + 227, 20, 73, 207, 118, 212, 38, 223, 90, 216, 59, 26, 147, 75, 45, 247, + 93, 134, 185, 162, 242, 249, 133, 130, 227, 33, 137, 216, 102, 218, 170, 219, + 63, 27, 80, 11, 124, 7, 97, 194, 168, 81, 190, 188, 112, 113, 228, 36, + 75, 91, 119, 123, 102, 163, 106, 249, 239, 2, 204, 1, 149, 192, 111, 16, + 44, 12, 29, 197, 201, 147, 22, 237, 206, 205, 148, 85, 175, 127, 60, 32, + 17, 216, 12, 90, 133, 251, 35, 3, 89, 193, 250, 208, 67, 28, 49, 201, + 212, 86, 223, 126, 216, 32, 90, 152, 59, 42, 147, 95, 45, 248, 29, 130, + 137, 161, 166, 248, 122, 194, 163, 17, 185, 204, 114, 213, 229, 159, 11, 40, + 7, 94, 130, 184, 97, 178, 168, 117, 190, 167, 48, 122, 148, 35, 47, 89, + 220, 58, 217, 211, 26, 221, 203, 25, 151, 74, 238, 183, 12, 118, 133, 230, + 227, 10, 201, 199, 22, 210, 142, 221, 164, 89, 187, 122, 243, 99, 5, 233, + 195, 14, 209, 196, 92, 83, 121, 253, 226, 193, 137, 144, 102, 236, 42, 205, + 223, 21, 152, 15, 42, 132, 31, 35, 72, 25, 246, 138, 198, 231, 18, 202, + 141, 151, 37, 174, 155, 60, 107, 81, 239, 124, 76, 33, 245, 216, 71, 26, + 178, 139, 53, 167, 87, 58, 190, 147, 48, 109, 212, 45, 159, 93, 168, 57, + 190, 146, 240, 109, 132, 45, 163, 93, 185, 249, 178, 194, 245, 145, 135, 44, + 98, 157, 233, 169, 142, 254, 228, 64, 75, 112, 55, 100, 22, 171, 78, 255, + 116, 64, 39, 112, 26, 164, 11, 59, 71, 83, 114, 189, 229, 177, 139, 52, + 103, 87, 106, 190, 175, 48, 124, 20, 33, 207, 88, 84, 58, 191, 83, 48, + 61, 212, 17, 159, 76, 104, 53, 238, 151, 12, 110, 133, 236, 99, 13, 233, + 197, 142, 211, 36, 93, 219, 121, 155, 98, 235, 105, 143, 110, 228, 44, 75, + 93, 247, 121, 134, 162, 226, 249, 137, 130, 230, 225, 138, 200, 103, 22, 170, + 142, 255, 36, 64, 27, 112, 11, 100, 7, 107, 66, 175, 113, 188, 36, 113, + 219, 100, 91, 107, 123, 111, 99, 108, 41, 237, 222, 205, 152, 85, 170, 191, + 63, 48, 16, 20, 12, 15, 69, 196, 51, 19, 85, 205, 255, 21, 128, 15, + 32, 4, 24, 3, 74, 129, 247, 32, 70, 152, 50, 234, 149, 143, 47, 36, + 28, 27, 73, 203, 118, 215, 102, 222, 170, 216, 127, 26, 160, 11, 56, 7, + 82, 130, 189, 161, 177, 184, 116, 114, 167, 101, 186, 171, 51, 63, 85, 208, + 63, 28, 16, 9, 204, 6, 213, 194, 223, 17, 152, 12, 106, 133, 239, 35, + 12, 25, 197, 202, 211, 23, 29, 206, 137, 148, 102, 239, 106, 204, 47, 21, + 220, 15, 25, 196, 10, 211, 71, 29, 242, 137, 133, 166, 227, 58, 201, 211, + 22, 221, 206, 217, 148, 90, 239, 123, 12, 35, 69, 217, 243, 26, 197, 203, + 19, 23, 77, 206, 181, 148, 119, 47, 102, 156, 42, 233, 223, 14, 216, 4, + 90, 131, 123, 33, 227, 88, 73, 250, 182, 195, 54, 209, 214, 220, 94, 217, + 248, 90, 194, 187, 17, 179, 76, 117, 245, 231, 7, 10, 130, 135, 33, 162, + 152, 121, 170, 162, 255, 57, 128, 18, 224, 13, 136, 5, 166, 131, 58, 225, + 211, 8, 93, 198, 185, 146, 242, 237, 133, 141, 163, 37, 185, 219, 50, 219, + 85, 155, 127, 43, 96, 31, 104, 8, 46, 134, 156, 98, 233, 233, 142, 206, + 228, 84, 75, 127, 119, 96, 38, 168, 26, 254, 139, 0, 103, 64, 42, 176, + 31, 52, 8, 23, 70, 142, 178, 228, 117, 139, 103, 39, 106, 154, 175, 43, + 60, 31, 81, 200, 60, 86, 145, 254, 236, 64, 77, 240, 53, 132, 23, 35, + 78, 153, 244, 106, 199, 111, 18, 172, 13, 189, 197, 177, 147, 52, 109, 215, + 109, 158, 173, 168, 125, 190, 161, 176, 120, 116, 34, 167, 89, 186, 186, 243, + 51, 5, 213, 195, 31, 17, 200, 12, 86, 133, 254, 227, 0, 73, 192, 54, + 208, 22, 220, 14, 217, 196, 90, 211, 123, 29, 227, 73, 137, 246, 230, 198, + 202, 210, 215, 29, 158, 137, 168, 102, 254, 170, 192, 127, 16, 32, 12, 24, + 5, 202, 131, 23, 33, 206, 152, 84, 106, 191, 111, 48, 44, 20, 29, 207, + 73, 148, 54, 239, 86, 204, 62, 213, 208, 95, 28, 56, 9, 210, 134, 221, + 162, 217, 185, 154, 242, 235, 5, 143, 67, 36, 49, 219, 84, 91, 127, 123, + 96, 35, 104, 25, 238, 138, 204, 103, 21, 234, 143, 15, 36, 4, 27, 67, + 75, 113, 247, 100, 70, 171, 114, 255, 101, 128, 43, 32, 31, 88, 8, 58, + 134, 147, 34, 237, 217, 141, 154, 229, 171, 11, 63, 71, 80, 50, 188, 21, + 177, 207, 52, 84, 23, 127, 78, 160, 52, 120, 23, 98, 142, 169, 164, 126, + 251, 96, 67, 104, 49, 238, 148, 76, 111, 117, 236, 39, 13, 218, 133, 155, + 35, 43, 89, 223, 122, 216, 35, 26, 153, 203, 42, 215, 95, 30, 184, 8, + 114, 134, 165, 162, 251, 57, 131, 82, 225, 253, 136, 65, 166, 176, 122, 244, + 35, 7, 89, 194, 186, 209, 179, 28, 117, 201, 231, 22, 202, 142, 215, 36, + 94, 155, 120, 107, 98, 175, 105, 188, 46, 241, 220, 68, 89, 243, 122, 197, + 227, 19, 9, 205, 198, 213, 146, 223, 45, 152, 29, 170, 137, 191, 38, 240, + 26, 196, 11, 19, 71, 77, 242, 181, 133, 183, 35, 54, 153, 214, 234, 222, + 207, 24, 84, 10, 191, 71, 48, 50, 148, 21, 175, 79, 60, 52, 17, 215, + 76, 94, 181, 248, 119, 2, 166, 129, 186, 224, 115, 8, 37, 198, 155, 18, + 235, 77, 143, 117, 164, 39, 59, 90, 147, 123, 45, 227, 93, 137, 249, 166, + 194, 250, 209, 131, 28, 97, 201, 232, 86, 206, 190, 212, 112, 95, 100, 56, + 43, 82, 159, 125, 168, 33, 190, 152, 112, 106, 164, 47, 59, 92, 19, 121, + 205, 226, 213, 137, 159, 38, 232, 26, 206, 139, 20, 103, 79, 106, 180, 47, + 55, 92, 22, 185, 206, 242, 212, 69, 159, 115, 40, 37, 222, 155, 24, 107, + 74, 175, 119, 60, 38, 145, 218, 236, 91, 13, 251, 69, 131, 115, 33, 229, + 216, 75, 26, 183, 75, 54, 183, 86, 246, 190, 198, 240, 82, 196, 61, 147, + 81, 173, 252, 125, 129, 225, 160, 72, 120, 54, 162, 150, 249, 174, 194, 252, + 81, 129, 252, 96, 65, 232, 48, 78, 148, 52, 111, 87, 108, 62, 173, 208, + 125, 156, 33, 169, 216, 126, 218, 160, 91, 56, 59, 82, 147, 125, 173, 225, + 189, 136, 113, 166, 164, 122, 251, 99, 3, 105, 193, 238, 208, 76, 92, 53, + 249, 215, 2, 222, 129, 152, 96, 106, 168, 47, 62, 156, 16, 105, 204, 46, + 213, 220, 95, 25, 248, 10, 194, 135, 17, 162, 140, 121, 165, 226, 251, 9, + 131, 70, 225, 242, 200, 69, 150, 179, 46, 245, 220, 71, 25, 242, 138, 197, + 167, 19, 58, 141, 211, 37, 157, 219, 41, 155, 94, 235, 120, 79, 98, 180, + 41, 183, 94, 246, 184, 70, 242, 178, 197, 181, 147, 55, 45, 214, 157, 158, + 233, 168, 78, 254, 180, 64, 119, 112, 38, 164, 26, 251, 75, 3, 119, 65, + 230, 176, 74, 244, 55, 7, 86, 130, 190, 225, 176, 72, 116, 54, 167, 86, + 250, 190, 195, 48, 81, 212, 60, 95, 81, 248, 60, 66, 145, 241, 172, 68, + 125, 243, 97, 133, 232, 99, 14, 169, 196, 126, 211, 96, 93, 232, 57, 142, + 146, 228, 109, 139, 109, 167, 109, 186, 173, 179, 61, 181, 209, 183, 28, 118, + 137, 230, 230, 202, 202, 215, 23, 30, 142, 136, 100, 102, 171, 106, 255, 111, + 0, 44, 0, 29, 192, 9, 144, 6, 236, 2, 205, 193, 149, 144, 111, 44, + 44, 29, 221, 201, 153, 150, 234, 238, 207, 12, 84, 5, 255, 67, 0, 49, + 192, 20, 80, 15, 124, 4, 33, 195, 88, 81, 250, 188, 67, 49, 241, 212, + 68, 95, 115, 120, 37, 226, 155, 9, 171, 70, 255, 114, 192, 37, 144, 27, + 44, 11, 93, 199, 121, 146, 162, 237, 185, 141, 178, 229, 181, 139, 55, 39, + 86, 154, 190, 235, 48, 79, 84, 52, 63, 87, 80, 62, 188, 16, 113, 204, + 36, 85, 219, 127, 27, 96, 11, 104, 7, 110, 130, 172, 97, 189, 232, 113, + 142, 164, 100, 123, 107, 99, 111, 105, 236, 46, 205, 220, 85, 153, 255, 42, + 192, 31, 16, 8, 12, 6, 133, 194, 227, 17, 137, 204, 102, 213, 234, 223, + 15, 24, 4, 10, 131, 71, 33, 242, 152, 69, 170, 179, 63, 53, 208, 23, + 28, 14, 137, 196, 102, 211, 106, 221, 239, 25, 140, 10, 229, 199, 11, 18, + 135, 77, 162, 181, 185, 183, 50, 246, 149, 134, 239, 34, 204, 25, 149, 202, + 239, 23, 12, 14, 133, 196, 99, 19, 105, 205, 238, 213, 140, 95, 37, 248, + 27, 2, 139, 65, 167, 112, 122, 164, 35, 59, 89, 211, 122, 221, 227, 25, + 137, 202, 230, 215, 10, 222, 135, 24, 98, 138, 169, 167, 62, 250, 144, 67, + 44, 49, 221, 212, 89, 159, 122, 232, 35, 14, 153, 196, 106, 211, 111, 29, + 236, 9, 141, 198, 229, 146, 203, 45, 151, 93, 174, 185, 188, 114, 241, 229, + 132, 75, 35, 119, 89, 230, 186, 202, 243, 23, 5, 206, 131, 20, 97, 207, + 104, 84, 46, 191, 92, 112, 57, 228, 18, 203, 77, 151, 117, 174, 167, 60, + 122, 145, 227, 44, 73, 221, 246, 217, 134, 218, 226, 219, 9, 155, 70, 235, + 114, 207, 101, 148, 43, 47, 95, 92, 56, 57, 210, 146, 221, 173, 153, 189, + 170, 241, 191, 4, 112, 3, 100, 1, 235, 64, 79, 112, 52, 36, 23, 91, + 78, 187, 116, 115, 103, 101, 234, 171, 15, 63, 68, 16, 51, 76, 21, 245, + 207, 7, 20, 2, 143, 65, 164, 48, 123, 84, 35, 127, 89, 224, 58, 200, + 19, 22, 141, 206, 229, 148, 75, 47, 119, 92, 38, 185, 218, 242, 219, 5, + 155, 67, 43, 113, 223, 100, 88, 43, 122, 159, 99, 40, 41, 222, 158, 216, + 104, 90, 174, 187, 60, 115, 81, 229, 252, 75, 1, 247, 64, 70, 176, 50, + 244, 21, 135, 79, 34, 180, 25, 183, 74, 246, 183, 6, 246, 130, 198, 225, + 146, 200, 109, 150, 173, 174, 253, 188, 65, 177, 240, 116, 68, 39, 115, 90, + 165, 251, 59, 3, 83, 65, 253, 240, 65, 132, 48, 99, 84, 41, 255, 94, + 192, 56, 80, 18, 188, 13, 177, 197, 180, 83, 55, 125, 214, 161, 158, 248, + 104, 66, 174, 177, 188, 116, 113, 231, 100, 74, 171, 119, 63, 102, 144, 42, + 236, 31, 13, 200, 5, 150, 131, 46, 225, 220, 72, 89, 246, 186, 198, 243, + 18, 197, 205, 147, 21, 173, 207, 61, 148, 17, 175, 76, 124, 53, 225, 215, + 8, 94, 134, 184, 98, 242, 169, 133, 190, 227, 48, 73, 212, 54, 223, 86, + 216, 62, 218, 144, 91, 44, 59, 93, 211, 121, 157, 226, 233, 137, 142, 230, + 228, 74, 203, 119, 23, 102, 142, 170, 228, 127, 11, 96, 7, 104, 2, 174, + 129, 188, 96, 113, 232, 36, 78, 155, 116, 107, 103, 111, 106, 172, 47, 61, + 220, 17, 153, 204, 106, 213, 239, 31, 12, 8, 5, 198, 131, 18, 225, 205, + 136, 85, 166, 191, 58, 240, 19, 4, 13, 195, 69, 145, 243, 44, 69, 221, + 243, 25, 133, 202, 227, 23, 9, 206, 134, 212, 98, 223, 105, 152, 46, 234, + 156, 79, 41, 244, 30, 199, 72, 82, 182, 189, 182, 241, 182, 196, 118, 211, + 102, 221, 234, 217, 143, 26, 228, 11, 11, 71, 71, 114, 178, 165, 181, 187, + 55, 51, 86, 149, 254, 239, 0, 76, 0, 53, 192, 23, 16, 14, 140, 4, + 101, 195, 107, 17, 239, 76, 76, 53, 245, 215, 7, 30, 130, 136, 97, 166, + 168, 122, 254, 163, 0, 121, 192, 34, 208, 25, 156, 10, 233, 199, 14, 210, + 132, 93, 163, 121, 185, 226, 242, 201, 133, 150, 227, 46, 201, 220, 86, 217, + 254, 218, 192, 91, 16, 59, 76, 19, 117, 205, 231, 21, 138, 143, 39, 36, + 26, 155, 75, 43, 119, 95, 102, 184, 42, 242, 159, 5, 168, 3, 62, 129, + 208, 96, 92, 40, 57, 222, 146, 216, 109, 154, 173, 171, 61, 191, 81, 176, + 60, 116, 17, 231, 76, 74, 181, 247, 55, 6, 150, 130, 238, 225, 140, 72, + 101, 246, 171, 6, 255, 66, 192, 49, 144, 20, 108, 15, 109, 196, 45, 147, + 93, 173, 249, 189, 130, 241, 161, 132, 120, 99, 98, 169, 233, 190, 206, 240, + 84, 68, 63, 115, 80, 37, 252, 27, 1, 203, 64, 87, 112, 62, 164, 16, + 123, 76, 35, 117, 217, 231, 26, 202, 139, 23, 39, 78, 154, 180, 107, 55, + 111, 86, 172, 62, 253, 208, 65, 156, 48, 105, 212, 46, 223, 92, 88, 57, + 250, 146, 195, 45, 145, 221, 172, 89, 189, 250, 241, 131, 4, 97, 195, 104, + 81, 238, 188, 76, 113, 245, 228, 71, 11, 114, 135, 101, 162, 171, 57, 191, + 82, 240, 61, 132, 17, 163, 76, 121, 245, 226, 199, 9, 146, 134, 237, 162, + 205, 185, 149, 178, 239, 53, 140, 23, 37, 206, 155, 20, 107, 79, 111, 116, + 44, 39, 93, 218, 185, 155, 50, 235, 85, 143, 127, 36, 32, 27, 88, 11, + 122, 135, 99, 34, 169, 217, 190, 218, 240, 91, 4, 59, 67, 83, 113, 253, + 228, 65, 139, 112, 103, 100, 42, 171, 95, 63, 120, 16, 34, 140, 25, 165, + 202, 251, 23, 3, 78, 129, 244, 96, 71, 104, 50, 174, 149, 188, 111, 49, + 236, 20, 77, 207, 117, 148, 39, 47, 90, 156, 59, 41, 211, 94, 221, 248, + 89, 130, 186, 225, 179, 8, 117, 198, 167, 18, 250, 141, 131, 37, 161, 219, + 56, 91, 82, 187, 125, 179, 97, 181, 232, 119, 14, 166, 132, 122, 227, 99, + 9, 233, 198, 206, 210, 212, 93, 159, 121, 168, 34, 254, 153, 128, 106, 224, + 47, 8, 28, 6, 137, 194, 230, 209, 138, 220, 103, 25, 234, 138, 207, 39, + 20, 26, 143, 75, 36, 55, 91, 86, 187, 126, 243, 96, 69, 232, 51, 14, + 149, 196, 111, 19, 108, 13, 237, 197, 141, 147, 37, 173, 219, 61, 155, 81, + 171, 124, 127, 97, 224, 40, 72, 30, 182, 136, 118, 230, 166, 202, 250, 215, + 3, 30, 129, 200, 96, 86, 168, 62, 254, 144, 64, 108, 48, 45, 212, 29, + 159, 73, 168, 54, 254, 150, 192, 110, 208, 44, 92, 29, 249, 201, 130, 214, + 225, 158, 200, 104, 86, 174, 190, 252, 112, 65, 228, 48, 75, 84, 55, 127, + 86, 160, 62, 248, 16, 66, 140, 49, 165, 212, 123, 31, 99, 72, 41, 246, + 158, 198, 232, 82, 206, 189, 148, 113, 175, 100, 124, 43, 97, 223, 104, 88, + 46, 186, 156, 115, 41, 229, 222, 203, 24, 87, 74, 190, 183, 48, 118, 148, + 38, 239, 90, 204, 59, 21, 211, 79, 29, 244, 9, 135, 70, 226, 178, 201, + 181, 150, 247, 46, 198, 156, 82, 233, 253, 142, 193, 164, 80, 123, 124, 35, + 97, 217, 232, 90, 206, 187, 20, 115, 79, 101, 244, 43, 7, 95, 66, 184, + 49, 178, 148, 117, 175, 103, 60, 42, 145, 223, 44, 88, 29, 250, 137, 131, + 38, 225, 218, 200, 91, 22, 187, 78, 243, 116, 69, 231, 115, 10, 165, 199, + 59, 18, 147, 77, 173, 245, 189, 135, 49, 162, 148, 121, 175, 98, 252, 41, + 129, 222, 224, 88, 72, 58, 182, 147, 54, 237, 214, 205, 158, 213, 168, 95, + 62, 184, 16, 114, 140, 37, 165, 219, 59, 27, 83, 75, 125, 247, 97, 134, + 168, 98, 254, 169, 128, 126, 224, 32, 72, 24, 54, 138, 150, 231, 46, 202, + 156, 87, 41, 254, 158, 192, 104, 80, 46, 188, 28, 113, 201, 228, 86, 203, + 126, 215, 96, 94, 168, 56, 126, 146, 160, 109, 184, 45, 178, 157, 181, 169, + 183, 62, 246, 144, 70, 236, 50, 205, 213, 149, 159, 47, 40, 28, 30, 137, + 200, 102, 214, 170, 222, 255, 24, 64, 10, 176, 7, 52, 2, 151, 65, 174, + 176, 124, 116, 33, 231, 88, 74, 186, 183, 51, 54, 149, 214, 239, 30, 204, + 8, 85, 198, 191, 18, 240, 13, 132, 5, 163, 67, 57, 241, 210, 196, 93, + 147, 121, 173, 226, 253, 137, 129, 166, 224, 122, 200, 35, 22, 153, 206, 234, + 212, 79, 31, 116, 8, 39, 70, 154, 178, 235, 53, 143, 87, 36, 62, 155, + 80, 107, 124, 47, 97, 220, 40, 89, 222, 186, 216, 115, 26, 165, 203, 59, + 23, 83, 78, 189, 244, 113, 135, 100, 98, 171, 105, 191, 110, 240, 44, 68, + 29, 243, 73, 133, 246, 227, 6, 201, 194, 214, 209, 158, 220, 104, 89, 238, + 186, 204, 115, 21, 229, 207, 11, 20, 7, 79, 66, 180, 49, 183, 84, 118, + 191, 102, 240, 42, 196, 31, 19, 72, 13, 246, 133, 134, 227, 34, 201, 217, + 150, 218, 238, 219, 12, 91, 69, 251, 115, 3, 101, 193, 235, 16, 79, 76, + 52, 53, 215, 87, 30, 190, 136, 112, 102, 164, 42, 251, 95, 3, 120, 1, + 226, 128, 73, 160, 54, 248, 22, 194, 142, 209, 164, 92, 123, 121, 227, 98, + 201, 233, 150, 206, 238, 212, 76, 95, 117, 248, 39, 2, 154, 129, 171, 32, + 127, 88, 32, 58, 152, 19, 42, 141, 223, 37, 152, 27, 42, 139, 95, 39, + 120, 26, 162, 139, 57, 167, 82, 250, 189, 131, 49, 161, 212, 120, 95, 98, + 184, 41, 178, 158, 245, 168, 71, 62, 178, 144, 117, 172, 39, 61, 218, 145, + 155, 44, 107, 93, 239, 121, 140, 34, 229, 217, 139, 26, 231, 75, 10, 183, + 71, 54, 178, 150, 245, 174, 199, 60, 82, 145, 253, 172, 65, 189, 240, 113, + 132, 36, 99, 91, 105, 251, 110, 195, 108, 81, 237, 252, 77, 129, 245, 160, + 71, 56, 50, 146, 149, 173, 175, 61, 188, 17, 177, 204, 116, 85, 231, 127, + 10, 160, 7, 56, 2, 146, 129, 173, 160, 125, 184, 33, 178, 152, 117, 170, + 167, 63, 58, 144, 19, 44, 13, 221, 197, 153, 147, 42, 237, 223, 13, 152, + 5, 170, 131, 63, 33, 208, 24, 92, 10, 185, 199, 50, 210, 149, 157, 175, + 41, 188, 30, 241, 200, 68, 86, 179, 126, 245, 224, 71, 8, 50, 134, 149, + 162, 239, 57, 140, 18, 229, 205, 139, 21, 167, 79, 58, 180, 19, 55, 77, + 214, 181, 158, 247, 40, 70, 158, 178, 232, 117, 142, 167, 36, 122, 155, 99, + 43, 105, 223, 110, 216, 44, 90, 157, 251, 41, 131, 94, 225, 248, 72, 66, + 182, 177, 182, 244, 118, 199, 102, 210, 170, 221, 191, 25, 176, 10, 244, 7, + 7, 66, 130, 177, 161, 180, 120, 119, 98, 166, 169, 186, 254, 243, 0, 69, + 192, 51, 16, 21, 204, 15, 21, 196, 15, 19, 68, 13, 243, 69, 133, 243, + 35, 5, 217, 195, 26, 209, 203, 28, 87, 73, 254, 182, 192, 118, 208, 38, + 220, 26, 217, 203, 26, 215, 75, 30, 183, 72, 118, 182, 166, 246, 250, 198, + 195, 18, 209, 205, 156, 85, 169, 255, 62, 192, 16, 80, 12, 60, 5, 209, + 195, 28, 81, 201, 252, 86, 193, 254, 208, 64, 92, 48, 57, 212, 18, 223, + 77, 152, 53, 170, 151, 63, 46, 144, 28, 108, 9, 237, 198, 205, 146, 213, + 173, 159, 61, 168, 17, 190, 140, 112, 101, 228, 43, 11, 95, 71, 120, 50, + 162, 149, 185, 175, 50, 252, 21, 129, 207, 32, 84, 24, 63, 74, 144, 55, + 44, 22, 157, 206, 233, 148, 78, 239, 116, 76, 39, 117, 218, 167, 27, 58, + 139, 83, 39, 125, 218, 161, 155, 56, 107, 82, 175, 125, 188, 33, 177, 216, + 116, 90, 167, 123, 58, 163, 83, 57, 253, 210, 193, 157, 144, 105, 172, 46, + 253, 220, 65, 153, 240, 106, 196, 47, 19, 92, 13, 249, 197, 130, 211, 33, + 157, 216, 105, 154, 174, 235, 60, 79, 81, 244, 60, 71, 81, 242, 188, 69, + 177, 243, 52, 69, 215, 115, 30, 165, 200, 123, 22, 163, 78, 249, 244, 66, + 199, 113, 146, 164, 109, 187, 109, 179, 109, 181, 237, 183, 13, 182, 133, 182, + 227, 54, 201, 214, 214, 222, 222, 216, 88, 90, 186, 187, 51, 51, 255, 63 ) + +random_mask_vec8 = numpy.array(random_mask_tuple, numpy.uint8) + diff --git a/gr-digital/python/ofdm_receiver.py b/gr-digital/python/ofdm_receiver.py new file mode 100644 index 000000000..9d4d6e559 --- /dev/null +++ b/gr-digital/python/ofdm_receiver.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# +# Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from numpy import fft +from gnuradio import gr + +import digital_swig +from ofdm_sync_pn import ofdm_sync_pn +from ofdm_sync_fixed import ofdm_sync_fixed +from ofdm_sync_pnac import ofdm_sync_pnac +from ofdm_sync_ml import ofdm_sync_ml + +class ofdm_receiver(gr.hier_block2): + """ + Performs receiver synchronization on OFDM symbols. + + The receiver performs channel filtering as well as symbol, frequency, and phase synchronization. + The synchronization routines are available in three flavors: preamble correlator (Schmidl and Cox), + modifid preamble correlator with autocorrelation (not yet working), and cyclic prefix correlator + (Van de Beeks). + """ + + def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, logging=False): + """ + Hierarchical block for receiving OFDM symbols. + + The input is the complex modulated signal at baseband. + Synchronized packets are sent back to the demodulator. + + @param fft_length: total number of subcarriers + @type fft_length: int + @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) + @type cp_length: int + @param occupied_tones: number of subcarriers used for data + @type occupied_tones: int + @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer + @type snr: float + @param ks: known symbols used as preambles to each packet + @type ks: list of lists + @param logging: turn file logging on or off + @type logging: bool + """ + + gr.hier_block2.__init__(self, "ofdm_receiver", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature + + bw = (float(occupied_tones) / float(fft_length)) / 2.0 + tb = bw*0.08 + chan_coeffs = gr.firdes.low_pass (1.0, # gain + 1.0, # sampling rate + bw+tb, # midpoint of trans. band + tb, # width of trans. band + gr.firdes.WIN_HAMMING) # filter type + self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) + + win = [1 for i in range(fft_length)] + + zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0)) + ks0 = fft_length*[0,] + ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0] + + ks0 = fft.ifftshift(ks0) + ks0time = fft.ifft(ks0) + # ADD SCALING FACTOR + ks0time = ks0time.tolist() + + SYNC = "pn" + if SYNC == "ml": + nco_sensitivity = -1.0/fft_length # correct for fine frequency + self.ofdm_sync = ofdm_sync_ml(fft_length, + cp_length, + snr, + ks0time, + logging) + elif SYNC == "pn": + nco_sensitivity = -2.0/fft_length # correct for fine frequency + self.ofdm_sync = ofdm_sync_pn(fft_length, + cp_length, + logging) + elif SYNC == "pnac": + nco_sensitivity = -2.0/fft_length # correct for fine frequency + self.ofdm_sync = ofdm_sync_pnac(fft_length, + cp_length, + ks0time, + logging) + # for testing only; do not user over the air + # remove filter and filter delay for this + elif SYNC == "fixed": + self.chan_filt = gr.multiply_const_cc(1.0) + nsymbols = 18 # enter the number of symbols per packet + freq_offset = 0.0 # if you use a frequency offset, enter it here + nco_sensitivity = -2.0/fft_length # correct for fine frequency + self.ofdm_sync = ofdm_sync_fixed(fft_length, + cp_length, + nsymbols, + freq_offset, + logging) + + # Set up blocks + + self.nco = gr.frequency_modulator_fc(nco_sensitivity) # generate a signal proportional to frequency error of sync block + self.sigmix = gr.multiply_cc() + self.sampler = digital_swig.ofdm_sampler(fft_length, fft_length+cp_length) + self.fft_demod = gr.fft_vcc(fft_length, True, win, True) + self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, + fft_length, + cp_length, ks[0]) + + self.connect(self, self.chan_filt) # filter the input channel + self.connect(self.chan_filt, self.ofdm_sync) # into the synchronization alg. + self.connect((self.ofdm_sync,0), self.nco, (self.sigmix,1)) # use sync freq. offset output to derotate input signal + self.connect(self.chan_filt, (self.sigmix,0)) # signal to be derotated + self.connect(self.sigmix, (self.sampler,0)) # sample off timing signal detected in sync alg + self.connect((self.ofdm_sync,1), (self.sampler,1)) # timing signal to sample at + + self.connect((self.sampler,0), self.fft_demod) # send derotated sampled signal to FFT + self.connect(self.fft_demod, (self.ofdm_frame_acq,0)) # find frame start and equalize signal + self.connect((self.sampler,1), (self.ofdm_frame_acq,1)) # send timing signal to signal frame start + self.connect((self.ofdm_frame_acq,0), (self,0)) # finished with fine/coarse freq correction, + self.connect((self.ofdm_frame_acq,1), (self,1)) # frame and symbol timing, and equalization + + if logging: + self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) + self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) + self.connect(self.ofdm_frame_acq, + gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) + self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) + self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) + self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) + self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat")) diff --git a/gr-digital/python/ofdm_sync_fixed.py b/gr-digital/python/ofdm_sync_fixed.py new file mode 100644 index 000000000..9bac789bf --- /dev/null +++ b/gr-digital/python/ofdm_sync_fixed.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from gnuradio import gr + +class ofdm_sync_fixed(gr.hier_block2): + def __init__(self, fft_length, cp_length, nsymbols, freq_offset, logging=False): + + gr.hier_block2.__init__(self, "ofdm_sync_fixed", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature + + # Use a fixed trigger point instead of sync block + symbol_length = fft_length + cp_length + pkt_length = nsymbols*symbol_length + data = (pkt_length)*[0,] + data[(symbol_length)-1] = 1 + self.peak_trigger = gr.vector_source_b(data, True) + + # Use a pre-defined frequency offset + foffset = (pkt_length)*[math.pi*freq_offset,] + self.frequency_offset = gr.vector_source_f(foffset, True) + + self.connect(self, gr.null_sink(gr.sizeof_gr_complex)) + self.connect(self.frequency_offset, (self,0)) + self.connect(self.peak_trigger, (self,1)) + + if logging: + self.connect(self.peak_trigger, gr.file_sink(gr.sizeof_char, "ofdm_sync_fixed-peaks_b.dat")) + diff --git a/gr-digital/python/ofdm_sync_ml.py b/gr-digital/python/ofdm_sync_ml.py new file mode 100644 index 000000000..7c75d7f1d --- /dev/null +++ b/gr-digital/python/ofdm_sync_ml.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# Copyright 2007,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from gnuradio import gr + +class ofdm_sync_ml(gr.hier_block2): + def __init__(self, fft_length, cp_length, snr, kstime, logging): + ''' Maximum Likelihood OFDM synchronizer: + J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation + of Time and Frequency Offset in OFDM Systems," IEEE Trans. + Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997. + ''' + + gr.hier_block2.__init__(self, "ofdm_sync_ml", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature + + self.input = gr.add_const_cc(0) + + SNR = 10.0**(snr/10.0) + rho = SNR / (SNR + 1.0) + symbol_length = fft_length + cp_length + + # ML Sync + + # Energy Detection from ML Sync + + self.connect(self, self.input) + + # Create a delay line + self.delay = gr.delay(gr.sizeof_gr_complex, fft_length) + self.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(cp_length)] + self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) + + self.connect(self.input,self.magsqrd1) + self.connect(self.delay,self.magsqrd2) + self.connect(self.magsqrd1,(self.adder,0)) + self.connect(self.magsqrd2,(self.adder,1)) + self.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(cp_length)] + 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.connect(self.input,(self.mixer,1)) + self.connect(self.delay,self.conjg,(self.mixer,0)) + self.connect(self.mixer,self.movingsum2,self.c2mag) + self.connect(self.movingsum2,self.angle) + + # ML Sync output arg, need to find maximum point of this + self.diff = gr.sub_ff() + self.connect(self.c2mag,(self.diff,0)) + self.connect(self.moving_sum_filter,(self.diff,1)) + + #ML measurements input to sampler block and detect + self.f2c = gr.float_to_complex() + self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) + self.sample_and_hold = gr.sample_and_hold_ff() + + # use the sync loop values to set the sampler and the NCO + # self.diff = theta + # self.angle = epsilon + + self.connect(self.diff, self.pk_detect) + + # The DPLL corrects for timing differences between CP correlations + use_dpll = 0 + if use_dpll: + self.dpll = gr.dpll_bb(float(symbol_length),0.01) + self.connect(self.pk_detect, self.dpll) + self.connect(self.dpll, (self.sample_and_hold,1)) + else: + self.connect(self.pk_detect, (self.sample_and_hold,1)) + + self.connect(self.angle, (self.sample_and_hold,0)) + + ################################ + # correlate against known symbol + # This gives us the same timing signal as the PN sync block only on the preamble + # we don't use the signal generated from the CP correlation because we don't want + # to readjust the timing in the middle of the packet or we ruin the equalizer settings. + kstime = [k.conjugate() for k in kstime] + kstime.reverse() + self.kscorr = gr.fir_filter_ccc(1, kstime) + self.corrmag = gr.complex_to_mag_squared() + self.div = gr.divide_ff() + + # The output signature of the correlation has a few spikes because the rest of the + # system uses the repeated preamble symbol. It needs to work that generically if + # anyone wants to use this against a WiMAX-like signal since it, too, repeats. + # The output theta of the correlator above is multiplied with this correlation to + # identify the proper peak and remove other products in this cross-correlation + self.threshold_factor = 0.1 + self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0) + self.f2b = gr.float_to_char() + self.b2f = gr.char_to_float() + self.mul = gr.multiply_ff() + + # Normalize the power of the corr output by the energy. This is not really needed + # and could be removed for performance, but it makes for a cleaner signal. + # if this is removed, the threshold value needs adjustment. + self.connect(self.input, self.kscorr, self.corrmag, (self.div,0)) + self.connect(self.moving_sum_filter, (self.div,1)) + + self.connect(self.div, (self.mul,0)) + self.connect(self.pk_detect, self.b2f, (self.mul,1)) + self.connect(self.mul, self.slice) + + # Set output signals + # Output 0: fine frequency correction value + # Output 1: timing signal + self.connect(self.sample_and_hold, (self,0)) + self.connect(self.slice, self.f2b, (self,1)) + + + if logging: + self.connect(self.moving_sum_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-energy_f.dat")) + self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) + self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-corrmag_f.dat")) + self.connect(self.kscorr, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-kscorr_c.dat")) + self.connect(self.div, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-div_f.dat")) + self.connect(self.mul, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-mul_f.dat")) + self.connect(self.slice, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-slice_f.dat")) + self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) + if use_dpll: + self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) + + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat")) + diff --git a/gr-digital/python/ofdm_sync_pn.py b/gr-digital/python/ofdm_sync_pn.py new file mode 100644 index 000000000..05b1de2e1 --- /dev/null +++ b/gr-digital/python/ofdm_sync_pn.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +# Copyright 2007,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from numpy import fft +from gnuradio import gr + +class ofdm_sync_pn(gr.hier_block2): + def __init__(self, fft_length, cp_length, logging=False): + """ + OFDM synchronization using PN Correlation: + T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing + Synchonization for OFDM," IEEE Trans. Communications, vol. 45, + no. 12, 1997. + """ + + gr.hier_block2.__init__(self, "ofdm_sync_pn", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature + + self.input = gr.add_const_cc(0) + + # PN Sync + + # Create a delay line + self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) + + # Correlation from ML Sync + self.conjg = gr.conjugate_cc(); + self.corr = gr.multiply_cc(); + + # Create a moving sum filter for the corr output + if 1: + moving_sum_taps = [1.0 for i in range(fft_length//2)] + self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) + else: + moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] + self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) + + # Create a moving sum filter for the input + self.inputmag2 = gr.complex_to_mag_squared() + movingsum2_taps = [1.0 for i in range(fft_length//2)] + + if 1: + self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) + else: + self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) + + self.square = gr.multiply_ff() + self.normalize = gr.divide_ff() + + # Get magnitude (peaks) and angle (phase/freq error) + self.c2mag = gr.complex_to_mag_squared() + self.angle = gr.complex_to_arg() + + self.sample_and_hold = gr.sample_and_hold_ff() + + #ML measurements input to sampler block and detect + self.sub1 = gr.add_const_ff(-1) + self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) + #self.pk_detect = gr.peak_detector2_fb(9) + + self.connect(self, self.input) + + # Calculate the frequency offset from the correlation of the preamble + self.connect(self.input, self.delay) + self.connect(self.input, (self.corr,0)) + self.connect(self.delay, self.conjg) + self.connect(self.conjg, (self.corr,1)) + self.connect(self.corr, self.moving_sum_filter) + self.connect(self.moving_sum_filter, self.c2mag) + self.connect(self.moving_sum_filter, self.angle) + self.connect(self.angle, (self.sample_and_hold,0)) + + # Get the power of the input signal to normalize the output of the correlation + self.connect(self.input, self.inputmag2, self.inputmovingsum) + self.connect(self.inputmovingsum, (self.square,0)) + self.connect(self.inputmovingsum, (self.square,1)) + self.connect(self.square, (self.normalize,1)) + self.connect(self.c2mag, (self.normalize,0)) + + # Create a moving sum filter for the corr output + matched_filter_taps = [1.0/cp_length for i in range(cp_length)] + self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) + self.connect(self.normalize, self.matched_filter) + + self.connect(self.matched_filter, self.sub1, self.pk_detect) + #self.connect(self.matched_filter, self.pk_detect) + self.connect(self.pk_detect, (self.sample_and_hold,1)) + + # Set output signals + # Output 0: fine frequency correction value + # Output 1: timing signal + self.connect(self.sample_and_hold, (self,0)) + self.connect(self.pk_detect, (self,1)) + + if logging: + self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) + self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) + self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat")) + diff --git a/gr-digital/python/ofdm_sync_pnac.py b/gr-digital/python/ofdm_sync_pnac.py new file mode 100644 index 000000000..10a125964 --- /dev/null +++ b/gr-digital/python/ofdm_sync_pnac.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +from numpy import fft +from gnuradio import gr + +class ofdm_sync_pnac(gr.hier_block2): + def __init__(self, fft_length, cp_length, kstime, logging=False): + """ + OFDM synchronization using PN Correlation and initial cross-correlation: + F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using + PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207. + + This implementation is meant to be a more robust version of the Schmidl and Cox receiver design. + By correlating against the preamble and using that as the input to the time-delayed correlation, + this circuit produces a very clean timing signal at the end of the preamble. The timing is + more accurate and does not have the problem associated with determining the timing from the + plateau structure in the Schmidl and Cox. + + This implementation appears to require that the signal is received with a normalized power or signal + scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and + the peak detection. A better peak detection block might fix this. + + Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails + when an integer offset is introduced. Another thing to look at. + """ + + gr.hier_block2.__init__(self, "ofdm_sync_pnac", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature + + + self.input = gr.add_const_cc(0) + + symbol_length = fft_length + cp_length + + # PN Sync with cross-correlation input + + # cross-correlate with the known symbol + kstime = [k.conjugate() for k in kstime[0:fft_length//2]] + kstime.reverse() + self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) + + # Create a delay line + self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) + + # Correlation from ML Sync + self.conjg = gr.conjugate_cc(); + self.corr = gr.multiply_cc(); + + # Create a moving sum filter for the input + self.mag = gr.complex_to_mag_squared() + movingsum_taps = (fft_length//1)*[1.0,] + self.power = gr.fir_filter_fff(1,movingsum_taps) + + # Get magnitude (peaks) and angle (phase/freq error) + self.c2mag = gr.complex_to_mag_squared() + self.angle = gr.complex_to_arg() + self.compare = gr.sub_ff() + + self.sample_and_hold = gr.sample_and_hold_ff() + + #ML measurements input to sampler block and detect + self.threshold = gr.threshold_ff(0,0,0) # threshold detection might need to be tweaked + self.peaks = gr.float_to_char() + + self.connect(self, self.input) + + # Cross-correlate input signal with known preamble + self.connect(self.input, self.crosscorr_filter) + + # use the output of the cross-correlation as input time-shifted correlation + self.connect(self.crosscorr_filter, self.delay) + self.connect(self.crosscorr_filter, (self.corr,0)) + self.connect(self.delay, self.conjg) + self.connect(self.conjg, (self.corr,1)) + self.connect(self.corr, self.c2mag) + self.connect(self.corr, self.angle) + self.connect(self.angle, (self.sample_and_hold,0)) + + # Get the power of the input signal to compare against the correlation + self.connect(self.crosscorr_filter, self.mag, self.power) + + # Compare the power to the correlator output to determine timing peak + # When the peak occurs, it peaks above zero, so the thresholder detects this + self.connect(self.c2mag, (self.compare,0)) + self.connect(self.power, (self.compare,1)) + self.connect(self.compare, self.threshold) + self.connect(self.threshold, self.peaks, (self.sample_and_hold,1)) + + # Set output signals + # Output 0: fine frequency correction value + # Output 1: timing signal + self.connect(self.sample_and_hold, (self,0)) + self.connect(self.peaks, (self,1)) + + if logging: + self.connect(self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat")) + self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat")) + self.connect(self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat")) + self.connect(self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat")) + self.connect(self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat")) + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat")) diff --git a/gr-digital/python/packet_utils.py b/gr-digital/python/packet_utils.py new file mode 100644 index 000000000..2e216ff50 --- /dev/null +++ b/gr-digital/python/packet_utils.py @@ -0,0 +1,457 @@ +# +# Copyright 2005,2006,2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import struct +import numpy +from gnuradio import gru +import crc + +def conv_packed_binary_string_to_1_0_string(s): + """ + '\xAF' --> '10101111' + """ + r = [] + for ch in s: + x = ord(ch) + for i in range(7,-1,-1): + t = (x >> i) & 0x1 + r.append(t) + + return ''.join(map(lambda x: chr(x + ord('0')), r)) + +def conv_1_0_string_to_packed_binary_string(s): + """ + '10101111' -> ('\xAF', False) + + Basically the inverse of conv_packed_binary_string_to_1_0_string, + but also returns a flag indicating if we had to pad with leading zeros + to get to a multiple of 8. + """ + if not is_1_0_string(s): + raise ValueError, "Input must be a string containing only 0's and 1's" + + # pad to multiple of 8 + padded = False + rem = len(s) % 8 + if rem != 0: + npad = 8 - rem + s = '0' * npad + s + padded = True + + assert len(s) % 8 == 0 + + r = [] + i = 0 + while i < len(s): + t = 0 + for j in range(8): + t = (t << 1) | (ord(s[i + j]) - ord('0')) + r.append(chr(t)) + i += 8 + return (''.join(r), padded) + + +default_access_code = \ + conv_packed_binary_string_to_1_0_string('\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC') +preamble = \ + conv_packed_binary_string_to_1_0_string('\xA4\xF2') + +def is_1_0_string(s): + if not isinstance(s, str): + return False + for ch in s: + if not ch in ('0', '1'): + return False + return True + +def string_to_hex_list(s): + return map(lambda x: hex(ord(x)), s) + + +def whiten(s, o): + sa = numpy.fromstring(s, numpy.uint8) + z = sa ^ random_mask_vec8[o:len(sa)+o] + return z.tostring() + +def dewhiten(s, o): + return whiten(s, o) # self inverse + + +def make_header(payload_len, whitener_offset=0): + # Upper nibble is offset, lower 12 bits is len + val = ((whitener_offset & 0xf) << 12) | (payload_len & 0x0fff) + #print "offset =", whitener_offset, " len =", payload_len, " val=", val + return struct.pack('!HH', val, val) + +def make_packet(payload, samples_per_symbol, bits_per_symbol, + access_code=default_access_code, pad_for_usrp=True, + whitener_offset=0, whitening=True): + """ + Build a packet, given access code, payload, and whitener offset + + @param payload: packet payload, len [0, 4096] + @param samples_per_symbol: samples per symbol (needed for padding calculation) + @type samples_per_symbol: int + @param bits_per_symbol: (needed for padding calculation) + @type bits_per_symbol: int + @param access_code: string of ascii 0's and 1's + @param whitener_offset offset into whitener string to use [0-16) + + Packet will have access code at the beginning, followed by length, payload + and finally CRC-32. + """ + if not is_1_0_string(access_code): + raise ValueError, "access_code must be a string containing only 0's and 1's (%r)" % (access_code,) + + if not whitener_offset >=0 and whitener_offset < 16: + raise ValueError, "whitener_offset must be between 0 and 15, inclusive (%i)" % (whitener_offset,) + + (packed_access_code, padded) = conv_1_0_string_to_packed_binary_string(access_code) + (packed_preamble, ignore) = conv_1_0_string_to_packed_binary_string(preamble) + + payload_with_crc = crc.gen_and_append_crc32(payload) + #print "outbound crc =", string_to_hex_list(payload_with_crc[-4:]) + + L = len(payload_with_crc) + MAXLEN = len(random_mask_tuple) + if L > MAXLEN: + raise ValueError, "len(payload) must be in [0, %d]" % (MAXLEN,) + + if whitening: + pkt = ''.join((packed_preamble, packed_access_code, make_header(L, whitener_offset), + whiten(payload_with_crc, whitener_offset), '\x55')) + else: + pkt = ''.join((packed_preamble, packed_access_code, make_header(L, whitener_offset), + (payload_with_crc), '\x55')) + + if pad_for_usrp: + pkt = pkt + (_npadding_bytes(len(pkt), int(samples_per_symbol), bits_per_symbol) * '\x55') + + #print "make_packet: len(pkt) =", len(pkt) + return pkt + +def _npadding_bytes(pkt_byte_len, samples_per_symbol, bits_per_symbol): + """ + Generate sufficient padding such that each packet ultimately ends + up being a multiple of 512 bytes when sent across the USB. We + send 4-byte samples across the USB (16-bit I and 16-bit Q), thus + we want to pad so that after modulation the resulting packet + is a multiple of 128 samples. + + @param ptk_byte_len: len in bytes of packet, not including padding. + @param samples_per_symbol: samples per bit (1 bit / symbolwidth GMSK) + @type samples_per_symbol: int + @param bits_per_symbol: bits per symbol (log2(modulation order)) + @type bits_per_symbol: int + + @returns number of bytes of padding to append. + """ + modulus = 128 + byte_modulus = gru.lcm(modulus/8, samples_per_symbol) * bits_per_symbol / samples_per_symbol + r = pkt_byte_len % byte_modulus + if r == 0: + return 0 + return byte_modulus - r + + +def unmake_packet(whitened_payload_with_crc, whitener_offset=0, dewhitening=True): + """ + Return (ok, payload) + + @param whitened_payload_with_crc: string + """ + + if dewhitening: + payload_with_crc = dewhiten(whitened_payload_with_crc, whitener_offset) + else: + payload_with_crc = (whitened_payload_with_crc) + + ok, payload = crc.check_crc32(payload_with_crc) + + if 0: + print "payload_with_crc =", string_to_hex_list(payload_with_crc) + print "ok = %r, len(payload) = %d" % (ok, len(payload)) + print "payload =", string_to_hex_list(payload) + + return ok, payload + + +# FYI, this PN code is the output of a 15-bit LFSR +random_mask_tuple = ( + 255, 63, 0, 16, 0, 12, 0, 5, 192, 3, 16, 1, 204, 0, 85, 192, + 63, 16, 16, 12, 12, 5, 197, 195, 19, 17, 205, 204, 85, 149, 255, 47, + 0, 28, 0, 9, 192, 6, 208, 2, 220, 1, 153, 192, 106, 208, 47, 28, + 28, 9, 201, 198, 214, 210, 222, 221, 152, 89, 170, 186, 255, 51, 0, 21, + 192, 15, 16, 4, 12, 3, 69, 193, 243, 16, 69, 204, 51, 21, 213, 207, + 31, 20, 8, 15, 70, 132, 50, 227, 85, 137, 255, 38, 192, 26, 208, 11, + 28, 7, 73, 194, 182, 209, 182, 220, 118, 217, 230, 218, 202, 219, 23, 27, + 78, 139, 116, 103, 103, 106, 170, 175, 63, 60, 16, 17, 204, 12, 85, 197, + 255, 19, 0, 13, 192, 5, 144, 3, 44, 1, 221, 192, 89, 144, 58, 236, + 19, 13, 205, 197, 149, 147, 47, 45, 220, 29, 153, 201, 170, 214, 255, 30, + 192, 8, 80, 6, 188, 2, 241, 193, 132, 80, 99, 124, 41, 225, 222, 200, + 88, 86, 186, 190, 243, 48, 69, 212, 51, 31, 85, 200, 63, 22, 144, 14, + 236, 4, 77, 195, 117, 145, 231, 44, 74, 157, 247, 41, 134, 158, 226, 232, + 73, 142, 182, 228, 118, 203, 102, 215, 106, 222, 175, 24, 124, 10, 161, 199, + 56, 82, 146, 189, 173, 177, 189, 180, 113, 183, 100, 118, 171, 102, 255, 106, + 192, 47, 16, 28, 12, 9, 197, 198, 211, 18, 221, 205, 153, 149, 170, 239, + 63, 12, 16, 5, 204, 3, 21, 193, 207, 16, 84, 12, 63, 69, 208, 51, + 28, 21, 201, 207, 22, 212, 14, 223, 68, 88, 51, 122, 149, 227, 47, 9, + 220, 6, 217, 194, 218, 209, 155, 28, 107, 73, 239, 118, 204, 38, 213, 218, + 223, 27, 24, 11, 74, 135, 119, 34, 166, 153, 186, 234, 243, 15, 5, 196, + 3, 19, 65, 205, 240, 85, 132, 63, 35, 80, 25, 252, 10, 193, 199, 16, + 82, 140, 61, 165, 209, 187, 28, 115, 73, 229, 246, 203, 6, 215, 66, 222, + 177, 152, 116, 106, 167, 111, 58, 172, 19, 61, 205, 209, 149, 156, 111, 41, + 236, 30, 205, 200, 85, 150, 191, 46, 240, 28, 68, 9, 243, 70, 197, 242, + 211, 5, 157, 195, 41, 145, 222, 236, 88, 77, 250, 181, 131, 55, 33, 214, + 152, 94, 234, 184, 79, 50, 180, 21, 183, 79, 54, 180, 22, 247, 78, 198, + 180, 82, 247, 125, 134, 161, 162, 248, 121, 130, 162, 225, 185, 136, 114, 230, + 165, 138, 251, 39, 3, 90, 129, 251, 32, 67, 88, 49, 250, 148, 67, 47, + 113, 220, 36, 89, 219, 122, 219, 99, 27, 105, 203, 110, 215, 108, 94, 173, + 248, 125, 130, 161, 161, 184, 120, 114, 162, 165, 185, 187, 50, 243, 85, 133, + 255, 35, 0, 25, 192, 10, 208, 7, 28, 2, 137, 193, 166, 208, 122, 220, + 35, 25, 217, 202, 218, 215, 27, 30, 139, 72, 103, 118, 170, 166, 255, 58, + 192, 19, 16, 13, 204, 5, 149, 195, 47, 17, 220, 12, 89, 197, 250, 211, + 3, 29, 193, 201, 144, 86, 236, 62, 205, 208, 85, 156, 63, 41, 208, 30, + 220, 8, 89, 198, 186, 210, 243, 29, 133, 201, 163, 22, 249, 206, 194, 212, + 81, 159, 124, 104, 33, 238, 152, 76, 106, 181, 239, 55, 12, 22, 133, 206, + 227, 20, 73, 207, 118, 212, 38, 223, 90, 216, 59, 26, 147, 75, 45, 247, + 93, 134, 185, 162, 242, 249, 133, 130, 227, 33, 137, 216, 102, 218, 170, 219, + 63, 27, 80, 11, 124, 7, 97, 194, 168, 81, 190, 188, 112, 113, 228, 36, + 75, 91, 119, 123, 102, 163, 106, 249, 239, 2, 204, 1, 149, 192, 111, 16, + 44, 12, 29, 197, 201, 147, 22, 237, 206, 205, 148, 85, 175, 127, 60, 32, + 17, 216, 12, 90, 133, 251, 35, 3, 89, 193, 250, 208, 67, 28, 49, 201, + 212, 86, 223, 126, 216, 32, 90, 152, 59, 42, 147, 95, 45, 248, 29, 130, + 137, 161, 166, 248, 122, 194, 163, 17, 185, 204, 114, 213, 229, 159, 11, 40, + 7, 94, 130, 184, 97, 178, 168, 117, 190, 167, 48, 122, 148, 35, 47, 89, + 220, 58, 217, 211, 26, 221, 203, 25, 151, 74, 238, 183, 12, 118, 133, 230, + 227, 10, 201, 199, 22, 210, 142, 221, 164, 89, 187, 122, 243, 99, 5, 233, + 195, 14, 209, 196, 92, 83, 121, 253, 226, 193, 137, 144, 102, 236, 42, 205, + 223, 21, 152, 15, 42, 132, 31, 35, 72, 25, 246, 138, 198, 231, 18, 202, + 141, 151, 37, 174, 155, 60, 107, 81, 239, 124, 76, 33, 245, 216, 71, 26, + 178, 139, 53, 167, 87, 58, 190, 147, 48, 109, 212, 45, 159, 93, 168, 57, + 190, 146, 240, 109, 132, 45, 163, 93, 185, 249, 178, 194, 245, 145, 135, 44, + 98, 157, 233, 169, 142, 254, 228, 64, 75, 112, 55, 100, 22, 171, 78, 255, + 116, 64, 39, 112, 26, 164, 11, 59, 71, 83, 114, 189, 229, 177, 139, 52, + 103, 87, 106, 190, 175, 48, 124, 20, 33, 207, 88, 84, 58, 191, 83, 48, + 61, 212, 17, 159, 76, 104, 53, 238, 151, 12, 110, 133, 236, 99, 13, 233, + 197, 142, 211, 36, 93, 219, 121, 155, 98, 235, 105, 143, 110, 228, 44, 75, + 93, 247, 121, 134, 162, 226, 249, 137, 130, 230, 225, 138, 200, 103, 22, 170, + 142, 255, 36, 64, 27, 112, 11, 100, 7, 107, 66, 175, 113, 188, 36, 113, + 219, 100, 91, 107, 123, 111, 99, 108, 41, 237, 222, 205, 152, 85, 170, 191, + 63, 48, 16, 20, 12, 15, 69, 196, 51, 19, 85, 205, 255, 21, 128, 15, + 32, 4, 24, 3, 74, 129, 247, 32, 70, 152, 50, 234, 149, 143, 47, 36, + 28, 27, 73, 203, 118, 215, 102, 222, 170, 216, 127, 26, 160, 11, 56, 7, + 82, 130, 189, 161, 177, 184, 116, 114, 167, 101, 186, 171, 51, 63, 85, 208, + 63, 28, 16, 9, 204, 6, 213, 194, 223, 17, 152, 12, 106, 133, 239, 35, + 12, 25, 197, 202, 211, 23, 29, 206, 137, 148, 102, 239, 106, 204, 47, 21, + 220, 15, 25, 196, 10, 211, 71, 29, 242, 137, 133, 166, 227, 58, 201, 211, + 22, 221, 206, 217, 148, 90, 239, 123, 12, 35, 69, 217, 243, 26, 197, 203, + 19, 23, 77, 206, 181, 148, 119, 47, 102, 156, 42, 233, 223, 14, 216, 4, + 90, 131, 123, 33, 227, 88, 73, 250, 182, 195, 54, 209, 214, 220, 94, 217, + 248, 90, 194, 187, 17, 179, 76, 117, 245, 231, 7, 10, 130, 135, 33, 162, + 152, 121, 170, 162, 255, 57, 128, 18, 224, 13, 136, 5, 166, 131, 58, 225, + 211, 8, 93, 198, 185, 146, 242, 237, 133, 141, 163, 37, 185, 219, 50, 219, + 85, 155, 127, 43, 96, 31, 104, 8, 46, 134, 156, 98, 233, 233, 142, 206, + 228, 84, 75, 127, 119, 96, 38, 168, 26, 254, 139, 0, 103, 64, 42, 176, + 31, 52, 8, 23, 70, 142, 178, 228, 117, 139, 103, 39, 106, 154, 175, 43, + 60, 31, 81, 200, 60, 86, 145, 254, 236, 64, 77, 240, 53, 132, 23, 35, + 78, 153, 244, 106, 199, 111, 18, 172, 13, 189, 197, 177, 147, 52, 109, 215, + 109, 158, 173, 168, 125, 190, 161, 176, 120, 116, 34, 167, 89, 186, 186, 243, + 51, 5, 213, 195, 31, 17, 200, 12, 86, 133, 254, 227, 0, 73, 192, 54, + 208, 22, 220, 14, 217, 196, 90, 211, 123, 29, 227, 73, 137, 246, 230, 198, + 202, 210, 215, 29, 158, 137, 168, 102, 254, 170, 192, 127, 16, 32, 12, 24, + 5, 202, 131, 23, 33, 206, 152, 84, 106, 191, 111, 48, 44, 20, 29, 207, + 73, 148, 54, 239, 86, 204, 62, 213, 208, 95, 28, 56, 9, 210, 134, 221, + 162, 217, 185, 154, 242, 235, 5, 143, 67, 36, 49, 219, 84, 91, 127, 123, + 96, 35, 104, 25, 238, 138, 204, 103, 21, 234, 143, 15, 36, 4, 27, 67, + 75, 113, 247, 100, 70, 171, 114, 255, 101, 128, 43, 32, 31, 88, 8, 58, + 134, 147, 34, 237, 217, 141, 154, 229, 171, 11, 63, 71, 80, 50, 188, 21, + 177, 207, 52, 84, 23, 127, 78, 160, 52, 120, 23, 98, 142, 169, 164, 126, + 251, 96, 67, 104, 49, 238, 148, 76, 111, 117, 236, 39, 13, 218, 133, 155, + 35, 43, 89, 223, 122, 216, 35, 26, 153, 203, 42, 215, 95, 30, 184, 8, + 114, 134, 165, 162, 251, 57, 131, 82, 225, 253, 136, 65, 166, 176, 122, 244, + 35, 7, 89, 194, 186, 209, 179, 28, 117, 201, 231, 22, 202, 142, 215, 36, + 94, 155, 120, 107, 98, 175, 105, 188, 46, 241, 220, 68, 89, 243, 122, 197, + 227, 19, 9, 205, 198, 213, 146, 223, 45, 152, 29, 170, 137, 191, 38, 240, + 26, 196, 11, 19, 71, 77, 242, 181, 133, 183, 35, 54, 153, 214, 234, 222, + 207, 24, 84, 10, 191, 71, 48, 50, 148, 21, 175, 79, 60, 52, 17, 215, + 76, 94, 181, 248, 119, 2, 166, 129, 186, 224, 115, 8, 37, 198, 155, 18, + 235, 77, 143, 117, 164, 39, 59, 90, 147, 123, 45, 227, 93, 137, 249, 166, + 194, 250, 209, 131, 28, 97, 201, 232, 86, 206, 190, 212, 112, 95, 100, 56, + 43, 82, 159, 125, 168, 33, 190, 152, 112, 106, 164, 47, 59, 92, 19, 121, + 205, 226, 213, 137, 159, 38, 232, 26, 206, 139, 20, 103, 79, 106, 180, 47, + 55, 92, 22, 185, 206, 242, 212, 69, 159, 115, 40, 37, 222, 155, 24, 107, + 74, 175, 119, 60, 38, 145, 218, 236, 91, 13, 251, 69, 131, 115, 33, 229, + 216, 75, 26, 183, 75, 54, 183, 86, 246, 190, 198, 240, 82, 196, 61, 147, + 81, 173, 252, 125, 129, 225, 160, 72, 120, 54, 162, 150, 249, 174, 194, 252, + 81, 129, 252, 96, 65, 232, 48, 78, 148, 52, 111, 87, 108, 62, 173, 208, + 125, 156, 33, 169, 216, 126, 218, 160, 91, 56, 59, 82, 147, 125, 173, 225, + 189, 136, 113, 166, 164, 122, 251, 99, 3, 105, 193, 238, 208, 76, 92, 53, + 249, 215, 2, 222, 129, 152, 96, 106, 168, 47, 62, 156, 16, 105, 204, 46, + 213, 220, 95, 25, 248, 10, 194, 135, 17, 162, 140, 121, 165, 226, 251, 9, + 131, 70, 225, 242, 200, 69, 150, 179, 46, 245, 220, 71, 25, 242, 138, 197, + 167, 19, 58, 141, 211, 37, 157, 219, 41, 155, 94, 235, 120, 79, 98, 180, + 41, 183, 94, 246, 184, 70, 242, 178, 197, 181, 147, 55, 45, 214, 157, 158, + 233, 168, 78, 254, 180, 64, 119, 112, 38, 164, 26, 251, 75, 3, 119, 65, + 230, 176, 74, 244, 55, 7, 86, 130, 190, 225, 176, 72, 116, 54, 167, 86, + 250, 190, 195, 48, 81, 212, 60, 95, 81, 248, 60, 66, 145, 241, 172, 68, + 125, 243, 97, 133, 232, 99, 14, 169, 196, 126, 211, 96, 93, 232, 57, 142, + 146, 228, 109, 139, 109, 167, 109, 186, 173, 179, 61, 181, 209, 183, 28, 118, + 137, 230, 230, 202, 202, 215, 23, 30, 142, 136, 100, 102, 171, 106, 255, 111, + 0, 44, 0, 29, 192, 9, 144, 6, 236, 2, 205, 193, 149, 144, 111, 44, + 44, 29, 221, 201, 153, 150, 234, 238, 207, 12, 84, 5, 255, 67, 0, 49, + 192, 20, 80, 15, 124, 4, 33, 195, 88, 81, 250, 188, 67, 49, 241, 212, + 68, 95, 115, 120, 37, 226, 155, 9, 171, 70, 255, 114, 192, 37, 144, 27, + 44, 11, 93, 199, 121, 146, 162, 237, 185, 141, 178, 229, 181, 139, 55, 39, + 86, 154, 190, 235, 48, 79, 84, 52, 63, 87, 80, 62, 188, 16, 113, 204, + 36, 85, 219, 127, 27, 96, 11, 104, 7, 110, 130, 172, 97, 189, 232, 113, + 142, 164, 100, 123, 107, 99, 111, 105, 236, 46, 205, 220, 85, 153, 255, 42, + 192, 31, 16, 8, 12, 6, 133, 194, 227, 17, 137, 204, 102, 213, 234, 223, + 15, 24, 4, 10, 131, 71, 33, 242, 152, 69, 170, 179, 63, 53, 208, 23, + 28, 14, 137, 196, 102, 211, 106, 221, 239, 25, 140, 10, 229, 199, 11, 18, + 135, 77, 162, 181, 185, 183, 50, 246, 149, 134, 239, 34, 204, 25, 149, 202, + 239, 23, 12, 14, 133, 196, 99, 19, 105, 205, 238, 213, 140, 95, 37, 248, + 27, 2, 139, 65, 167, 112, 122, 164, 35, 59, 89, 211, 122, 221, 227, 25, + 137, 202, 230, 215, 10, 222, 135, 24, 98, 138, 169, 167, 62, 250, 144, 67, + 44, 49, 221, 212, 89, 159, 122, 232, 35, 14, 153, 196, 106, 211, 111, 29, + 236, 9, 141, 198, 229, 146, 203, 45, 151, 93, 174, 185, 188, 114, 241, 229, + 132, 75, 35, 119, 89, 230, 186, 202, 243, 23, 5, 206, 131, 20, 97, 207, + 104, 84, 46, 191, 92, 112, 57, 228, 18, 203, 77, 151, 117, 174, 167, 60, + 122, 145, 227, 44, 73, 221, 246, 217, 134, 218, 226, 219, 9, 155, 70, 235, + 114, 207, 101, 148, 43, 47, 95, 92, 56, 57, 210, 146, 221, 173, 153, 189, + 170, 241, 191, 4, 112, 3, 100, 1, 235, 64, 79, 112, 52, 36, 23, 91, + 78, 187, 116, 115, 103, 101, 234, 171, 15, 63, 68, 16, 51, 76, 21, 245, + 207, 7, 20, 2, 143, 65, 164, 48, 123, 84, 35, 127, 89, 224, 58, 200, + 19, 22, 141, 206, 229, 148, 75, 47, 119, 92, 38, 185, 218, 242, 219, 5, + 155, 67, 43, 113, 223, 100, 88, 43, 122, 159, 99, 40, 41, 222, 158, 216, + 104, 90, 174, 187, 60, 115, 81, 229, 252, 75, 1, 247, 64, 70, 176, 50, + 244, 21, 135, 79, 34, 180, 25, 183, 74, 246, 183, 6, 246, 130, 198, 225, + 146, 200, 109, 150, 173, 174, 253, 188, 65, 177, 240, 116, 68, 39, 115, 90, + 165, 251, 59, 3, 83, 65, 253, 240, 65, 132, 48, 99, 84, 41, 255, 94, + 192, 56, 80, 18, 188, 13, 177, 197, 180, 83, 55, 125, 214, 161, 158, 248, + 104, 66, 174, 177, 188, 116, 113, 231, 100, 74, 171, 119, 63, 102, 144, 42, + 236, 31, 13, 200, 5, 150, 131, 46, 225, 220, 72, 89, 246, 186, 198, 243, + 18, 197, 205, 147, 21, 173, 207, 61, 148, 17, 175, 76, 124, 53, 225, 215, + 8, 94, 134, 184, 98, 242, 169, 133, 190, 227, 48, 73, 212, 54, 223, 86, + 216, 62, 218, 144, 91, 44, 59, 93, 211, 121, 157, 226, 233, 137, 142, 230, + 228, 74, 203, 119, 23, 102, 142, 170, 228, 127, 11, 96, 7, 104, 2, 174, + 129, 188, 96, 113, 232, 36, 78, 155, 116, 107, 103, 111, 106, 172, 47, 61, + 220, 17, 153, 204, 106, 213, 239, 31, 12, 8, 5, 198, 131, 18, 225, 205, + 136, 85, 166, 191, 58, 240, 19, 4, 13, 195, 69, 145, 243, 44, 69, 221, + 243, 25, 133, 202, 227, 23, 9, 206, 134, 212, 98, 223, 105, 152, 46, 234, + 156, 79, 41, 244, 30, 199, 72, 82, 182, 189, 182, 241, 182, 196, 118, 211, + 102, 221, 234, 217, 143, 26, 228, 11, 11, 71, 71, 114, 178, 165, 181, 187, + 55, 51, 86, 149, 254, 239, 0, 76, 0, 53, 192, 23, 16, 14, 140, 4, + 101, 195, 107, 17, 239, 76, 76, 53, 245, 215, 7, 30, 130, 136, 97, 166, + 168, 122, 254, 163, 0, 121, 192, 34, 208, 25, 156, 10, 233, 199, 14, 210, + 132, 93, 163, 121, 185, 226, 242, 201, 133, 150, 227, 46, 201, 220, 86, 217, + 254, 218, 192, 91, 16, 59, 76, 19, 117, 205, 231, 21, 138, 143, 39, 36, + 26, 155, 75, 43, 119, 95, 102, 184, 42, 242, 159, 5, 168, 3, 62, 129, + 208, 96, 92, 40, 57, 222, 146, 216, 109, 154, 173, 171, 61, 191, 81, 176, + 60, 116, 17, 231, 76, 74, 181, 247, 55, 6, 150, 130, 238, 225, 140, 72, + 101, 246, 171, 6, 255, 66, 192, 49, 144, 20, 108, 15, 109, 196, 45, 147, + 93, 173, 249, 189, 130, 241, 161, 132, 120, 99, 98, 169, 233, 190, 206, 240, + 84, 68, 63, 115, 80, 37, 252, 27, 1, 203, 64, 87, 112, 62, 164, 16, + 123, 76, 35, 117, 217, 231, 26, 202, 139, 23, 39, 78, 154, 180, 107, 55, + 111, 86, 172, 62, 253, 208, 65, 156, 48, 105, 212, 46, 223, 92, 88, 57, + 250, 146, 195, 45, 145, 221, 172, 89, 189, 250, 241, 131, 4, 97, 195, 104, + 81, 238, 188, 76, 113, 245, 228, 71, 11, 114, 135, 101, 162, 171, 57, 191, + 82, 240, 61, 132, 17, 163, 76, 121, 245, 226, 199, 9, 146, 134, 237, 162, + 205, 185, 149, 178, 239, 53, 140, 23, 37, 206, 155, 20, 107, 79, 111, 116, + 44, 39, 93, 218, 185, 155, 50, 235, 85, 143, 127, 36, 32, 27, 88, 11, + 122, 135, 99, 34, 169, 217, 190, 218, 240, 91, 4, 59, 67, 83, 113, 253, + 228, 65, 139, 112, 103, 100, 42, 171, 95, 63, 120, 16, 34, 140, 25, 165, + 202, 251, 23, 3, 78, 129, 244, 96, 71, 104, 50, 174, 149, 188, 111, 49, + 236, 20, 77, 207, 117, 148, 39, 47, 90, 156, 59, 41, 211, 94, 221, 248, + 89, 130, 186, 225, 179, 8, 117, 198, 167, 18, 250, 141, 131, 37, 161, 219, + 56, 91, 82, 187, 125, 179, 97, 181, 232, 119, 14, 166, 132, 122, 227, 99, + 9, 233, 198, 206, 210, 212, 93, 159, 121, 168, 34, 254, 153, 128, 106, 224, + 47, 8, 28, 6, 137, 194, 230, 209, 138, 220, 103, 25, 234, 138, 207, 39, + 20, 26, 143, 75, 36, 55, 91, 86, 187, 126, 243, 96, 69, 232, 51, 14, + 149, 196, 111, 19, 108, 13, 237, 197, 141, 147, 37, 173, 219, 61, 155, 81, + 171, 124, 127, 97, 224, 40, 72, 30, 182, 136, 118, 230, 166, 202, 250, 215, + 3, 30, 129, 200, 96, 86, 168, 62, 254, 144, 64, 108, 48, 45, 212, 29, + 159, 73, 168, 54, 254, 150, 192, 110, 208, 44, 92, 29, 249, 201, 130, 214, + 225, 158, 200, 104, 86, 174, 190, 252, 112, 65, 228, 48, 75, 84, 55, 127, + 86, 160, 62, 248, 16, 66, 140, 49, 165, 212, 123, 31, 99, 72, 41, 246, + 158, 198, 232, 82, 206, 189, 148, 113, 175, 100, 124, 43, 97, 223, 104, 88, + 46, 186, 156, 115, 41, 229, 222, 203, 24, 87, 74, 190, 183, 48, 118, 148, + 38, 239, 90, 204, 59, 21, 211, 79, 29, 244, 9, 135, 70, 226, 178, 201, + 181, 150, 247, 46, 198, 156, 82, 233, 253, 142, 193, 164, 80, 123, 124, 35, + 97, 217, 232, 90, 206, 187, 20, 115, 79, 101, 244, 43, 7, 95, 66, 184, + 49, 178, 148, 117, 175, 103, 60, 42, 145, 223, 44, 88, 29, 250, 137, 131, + 38, 225, 218, 200, 91, 22, 187, 78, 243, 116, 69, 231, 115, 10, 165, 199, + 59, 18, 147, 77, 173, 245, 189, 135, 49, 162, 148, 121, 175, 98, 252, 41, + 129, 222, 224, 88, 72, 58, 182, 147, 54, 237, 214, 205, 158, 213, 168, 95, + 62, 184, 16, 114, 140, 37, 165, 219, 59, 27, 83, 75, 125, 247, 97, 134, + 168, 98, 254, 169, 128, 126, 224, 32, 72, 24, 54, 138, 150, 231, 46, 202, + 156, 87, 41, 254, 158, 192, 104, 80, 46, 188, 28, 113, 201, 228, 86, 203, + 126, 215, 96, 94, 168, 56, 126, 146, 160, 109, 184, 45, 178, 157, 181, 169, + 183, 62, 246, 144, 70, 236, 50, 205, 213, 149, 159, 47, 40, 28, 30, 137, + 200, 102, 214, 170, 222, 255, 24, 64, 10, 176, 7, 52, 2, 151, 65, 174, + 176, 124, 116, 33, 231, 88, 74, 186, 183, 51, 54, 149, 214, 239, 30, 204, + 8, 85, 198, 191, 18, 240, 13, 132, 5, 163, 67, 57, 241, 210, 196, 93, + 147, 121, 173, 226, 253, 137, 129, 166, 224, 122, 200, 35, 22, 153, 206, 234, + 212, 79, 31, 116, 8, 39, 70, 154, 178, 235, 53, 143, 87, 36, 62, 155, + 80, 107, 124, 47, 97, 220, 40, 89, 222, 186, 216, 115, 26, 165, 203, 59, + 23, 83, 78, 189, 244, 113, 135, 100, 98, 171, 105, 191, 110, 240, 44, 68, + 29, 243, 73, 133, 246, 227, 6, 201, 194, 214, 209, 158, 220, 104, 89, 238, + 186, 204, 115, 21, 229, 207, 11, 20, 7, 79, 66, 180, 49, 183, 84, 118, + 191, 102, 240, 42, 196, 31, 19, 72, 13, 246, 133, 134, 227, 34, 201, 217, + 150, 218, 238, 219, 12, 91, 69, 251, 115, 3, 101, 193, 235, 16, 79, 76, + 52, 53, 215, 87, 30, 190, 136, 112, 102, 164, 42, 251, 95, 3, 120, 1, + 226, 128, 73, 160, 54, 248, 22, 194, 142, 209, 164, 92, 123, 121, 227, 98, + 201, 233, 150, 206, 238, 212, 76, 95, 117, 248, 39, 2, 154, 129, 171, 32, + 127, 88, 32, 58, 152, 19, 42, 141, 223, 37, 152, 27, 42, 139, 95, 39, + 120, 26, 162, 139, 57, 167, 82, 250, 189, 131, 49, 161, 212, 120, 95, 98, + 184, 41, 178, 158, 245, 168, 71, 62, 178, 144, 117, 172, 39, 61, 218, 145, + 155, 44, 107, 93, 239, 121, 140, 34, 229, 217, 139, 26, 231, 75, 10, 183, + 71, 54, 178, 150, 245, 174, 199, 60, 82, 145, 253, 172, 65, 189, 240, 113, + 132, 36, 99, 91, 105, 251, 110, 195, 108, 81, 237, 252, 77, 129, 245, 160, + 71, 56, 50, 146, 149, 173, 175, 61, 188, 17, 177, 204, 116, 85, 231, 127, + 10, 160, 7, 56, 2, 146, 129, 173, 160, 125, 184, 33, 178, 152, 117, 170, + 167, 63, 58, 144, 19, 44, 13, 221, 197, 153, 147, 42, 237, 223, 13, 152, + 5, 170, 131, 63, 33, 208, 24, 92, 10, 185, 199, 50, 210, 149, 157, 175, + 41, 188, 30, 241, 200, 68, 86, 179, 126, 245, 224, 71, 8, 50, 134, 149, + 162, 239, 57, 140, 18, 229, 205, 139, 21, 167, 79, 58, 180, 19, 55, 77, + 214, 181, 158, 247, 40, 70, 158, 178, 232, 117, 142, 167, 36, 122, 155, 99, + 43, 105, 223, 110, 216, 44, 90, 157, 251, 41, 131, 94, 225, 248, 72, 66, + 182, 177, 182, 244, 118, 199, 102, 210, 170, 221, 191, 25, 176, 10, 244, 7, + 7, 66, 130, 177, 161, 180, 120, 119, 98, 166, 169, 186, 254, 243, 0, 69, + 192, 51, 16, 21, 204, 15, 21, 196, 15, 19, 68, 13, 243, 69, 133, 243, + 35, 5, 217, 195, 26, 209, 203, 28, 87, 73, 254, 182, 192, 118, 208, 38, + 220, 26, 217, 203, 26, 215, 75, 30, 183, 72, 118, 182, 166, 246, 250, 198, + 195, 18, 209, 205, 156, 85, 169, 255, 62, 192, 16, 80, 12, 60, 5, 209, + 195, 28, 81, 201, 252, 86, 193, 254, 208, 64, 92, 48, 57, 212, 18, 223, + 77, 152, 53, 170, 151, 63, 46, 144, 28, 108, 9, 237, 198, 205, 146, 213, + 173, 159, 61, 168, 17, 190, 140, 112, 101, 228, 43, 11, 95, 71, 120, 50, + 162, 149, 185, 175, 50, 252, 21, 129, 207, 32, 84, 24, 63, 74, 144, 55, + 44, 22, 157, 206, 233, 148, 78, 239, 116, 76, 39, 117, 218, 167, 27, 58, + 139, 83, 39, 125, 218, 161, 155, 56, 107, 82, 175, 125, 188, 33, 177, 216, + 116, 90, 167, 123, 58, 163, 83, 57, 253, 210, 193, 157, 144, 105, 172, 46, + 253, 220, 65, 153, 240, 106, 196, 47, 19, 92, 13, 249, 197, 130, 211, 33, + 157, 216, 105, 154, 174, 235, 60, 79, 81, 244, 60, 71, 81, 242, 188, 69, + 177, 243, 52, 69, 215, 115, 30, 165, 200, 123, 22, 163, 78, 249, 244, 66, + 199, 113, 146, 164, 109, 187, 109, 179, 109, 181, 237, 183, 13, 182, 133, 182, + 227, 54, 201, 214, 214, 222, 222, 216, 88, 90, 186, 187, 51, 51, 255, 63 ) + +random_mask_vec8 = numpy.array(random_mask_tuple, numpy.uint8) + diff --git a/gr-digital/python/pkt.py b/gr-digital/python/pkt.py new file mode 100644 index 000000000..8650bdbb0 --- /dev/null +++ b/gr-digital/python/pkt.py @@ -0,0 +1,167 @@ +# +# Copyright 2005, 2006, 2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from math import pi +from gnuradio import gr +import gnuradio.gr.gr_threading as _threading +import packet_utils +import digital_swig + + +# ///////////////////////////////////////////////////////////////////////////// +# mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class mod_pkts(gr.hier_block2): + """ + Wrap an arbitrary digital modulator in our packet handling framework. + + Send packets by calling send_pkt + """ + def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, + use_whitener_offset=False, modulate=True): + """ + Hierarchical block for sending packets + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param modulator: instance of modulator class (gr_block or hier_block2) + @type modulator: complex baseband out + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's between 1 and 64 long + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + @param use_whitener_offset: If true, start of whitener XOR string is incremented each packet + + See gmsk_mod for remaining parameters + """ + + gr.hier_block2.__init__(self, "mod_pkts", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._modulator = modulator + self._pad_for_usrp = pad_for_usrp + self._use_whitener_offset = use_whitener_offset + self._whitener_offset = 0 + + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + # accepts messages from the outside world + self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) + self.connect(self._pkt_input, self._modulator, self) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self._pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = packet_utils.make_packet(payload, + self._modulator.samples_per_symbol(), + self._modulator.bits_per_symbol(), + self._access_code, + self._pad_for_usrp, + self._whitener_offset) + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + if self._use_whitener_offset is True: + self._whitener_offset = (self._whitener_offset + 1) % 16 + + self._pkt_input.msgq().insert_tail(msg) + + + +class demod_pkts(gr.hier_block2): + """ + Wrap an arbitrary digital demodulator in our packet handling framework. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, demodulator, access_code=None, callback=None, threshold=-1): + """ + Hierarchical block for demodulating and deframing packets. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param demodulator: instance of demodulator class (gr_block or hier_block2) + @type demodulator: complex baseband in + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) + @type threshold: int + """ + + gr.hier_block2.__init__(self, "demod_pkts", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self._demodulator = demodulator + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + if threshold == -1: + threshold = 12 # FIXME raise exception + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + self.correlator = digital_swig.correlate_access_code_bb(access_code, threshold) + + self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) + self.connect(self, self._demodulator, self.correlator, self.framer_sink) + + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) + if self.callback: + self.callback(ok, payload) diff --git a/gr-digital/python/psk.py b/gr-digital/python/psk.py new file mode 100644 index 000000000..58f6787f0 --- /dev/null +++ b/gr-digital/python/psk.py @@ -0,0 +1,122 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +PSK modulation and demodulation. +""" + +from math import pi, log +from cmath import exp + +import digital_swig +import modulation_utils +from utils import mod_codes, gray_code +from generic_mod_demod import generic_mod, generic_demod + +# Default number of points in constellation. +_def_constellation_points = 4 +# The default encoding (e.g. gray-code, set-partition) +_def_mod_code = mod_codes.GRAY_CODE + +def create_encodings(mod_code, arity): + post_diff_code = None + if mod_code not in mod_codes.codes: + raise ValueError('That modulation code does not exist.') + if mod_code == mod_codes.GRAY_CODE: + pre_diff_code = gray_code.gray_code(arity) + elif mod_code == mod_codes.SET_PARTITION_CODE: + pre_diff_code = set_partition_code.set_partition_code(arity) + elif mod_code == mod_codes.NO_CODE: + pre_diff_code = [] + else: + raise ValueError('That modulation code is not implemented for this constellation.') + return (pre_diff_code, post_diff_code) + +# ///////////////////////////////////////////////////////////////////////////// +# PSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def psk_constellation(m=_def_constellation_points, mod_code=_def_mod_code): + """ + Creates a PSK constellation object. + """ + k = log(m) / log(2.0) + if (k != int(k)): + raise StandardError('Number of constellation points must be a power of two.') + points = [exp(2*pi*(0+1j)*i/m) for i in range(0,m)] + pre_diff_code, post_diff_code = create_encodings(mod_code, m) + if post_diff_code is not None: + inverse_post_diff_code = mod_codes.invert_code(post_diff_code) + points = [points[x] for x in inverse_post_diff_code] + constellation = digital_swig.constellation_psk(points, pre_diff_code, m) + return constellation + +# ///////////////////////////////////////////////////////////////////////////// +# PSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class psk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + 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. + + See generic_mod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points, mod_code) + super(psk_mod, self).__init__(constellation, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# PSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class psk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + 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. + + See generic_demod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points, mod_code) + super(psk_demod, self).__init__(constellation, *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('psk', psk_mod) +modulation_utils.add_type_1_demod('psk', psk_demod) +modulation_utils.add_type_1_constellation('psk', psk_constellation) diff --git a/gr-digital/python/qa_binary_slicer_fb.py b/gr-digital/python/qa_binary_slicer_fb.py new file mode 100755 index 000000000..944dc1b5a --- /dev/null +++ b/gr-digital/python/qa_binary_slicer_fb.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import math, random + +class test_constellation_decoder (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_binary_slicer_fb (self): + expected_result = ( 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1) + src_data = (-1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1) + src_data = [s + (1 - random.random()) for s in src_data] # add some noise + src = gr.vector_source_f (src_data) + op = digital_swig.binary_slicer_fb () + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation_decoder, "test_constellation_decoder.xml") + diff --git a/gr-digital/python/qa_clock_recovery_mm.py b/gr-digital/python/qa_clock_recovery_mm.py new file mode 100755 index 000000000..f4c345b03 --- /dev/null +++ b/gr-digital/python/qa_clock_recovery_mm.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import random, cmath + +class test_clock_recovery_mm(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # Test complex/complex version + omega = 2 + gain_omega = 0.001 + mu = 0.5 + gain_mu = 0.01 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 100*[complex(1, 1),] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 100*[complex(0.99972, 0.99972)] # doesn't quite get to 1.0 + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 30 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + + def test02 (self): + # Test float/float version + omega = 2 + gain_omega = 0.01 + mu = 0.5 + gain_mu = 0.01 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_ff(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 100*[1,] + self.src = gr.vector_source_f(data, False) + self.snk = gr.vector_sink_f() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 100*[0.99972, ] # doesn't quite get to 1.0 + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 30 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5) + + + def test03 (self): + # Test complex/complex version with varying input + omega = 2 + gain_omega = 0.01 + mu = 0.25 + gain_mu = 0.1 + omega_rel_lim = 0.0001 + + self.test = digital_swig.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 1000*[complex(1, 1), complex(1, 1), complex(-1, -1), complex(-1, -1)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(-1.2, -1.2), complex(1.2, 1.2)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + + + def test04 (self): + # Test float/float version + omega = 2 + gain_omega = 0.01 + mu = 0.25 + gain_mu = 0.1 + omega_rel_lim = 0.001 + + self.test = digital_swig.clock_recovery_mm_ff(omega, gain_omega, + mu, gain_mu, + omega_rel_lim) + + data = 1000*[1, 1, -1, -1] + self.src = gr.vector_source_f(data, False) + self.snk = gr.vector_sink_f() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[-1.31, 1.31] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 1) + + +if __name__ == '__main__': + gr_unittest.run(test_clock_recovery_mm, "test_clock_recovery_mm.xml") diff --git a/gr-digital/python/qa_cma_equalizer.py b/gr-digital/python/qa_cma_equalizer.py new file mode 100755 index 000000000..75fb0f05e --- /dev/null +++ b/gr-digital/python/qa_cma_equalizer.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig + +class test_cma_equalizer_fir(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def transform(self, src_data): + SRC = gr.vector_source_c(src_data, False) + EQU = digital_swig.cma_equalizer_cc(4, 1.0, .001, 1) + DST = gr.vector_sink_c() + self.tb.connect(SRC, EQU, DST) + self.tb.run() + return DST.data() + + def test_001_identity(self): + # Constant modulus signal so no adjustments + src_data = (1+0j, 0+1j, -1+0j, 0-1j)*1000 + expected_data = src_data + result = self.transform(src_data) + self.assertComplexTuplesAlmostEqual(expected_data, result) + +if __name__ == "__main__": + gr_unittest.run(test_cma_equalizer_fir, "test_cma_equalizer_fir.xml") diff --git a/gr-digital/python/qa_constellation.py b/gr-digital/python/qa_constellation.py new file mode 100755 index 000000000..b17d2a0fc --- /dev/null +++ b/gr-digital/python/qa_constellation.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import random +from cmath import exp, pi, log + +from gnuradio import gr, gr_unittest, blks2 +from utils import mod_codes +import digital_swig + +# import from local folder +import psk +import qam + +tested_mod_codes = (mod_codes.NO_CODE, mod_codes.GRAY_CODE) + +# A list of the constellations to test. +# Each constellation is given by a 3-tuple. +# First item is a function to generate the constellation +# Second item is a dictionary of arguments for function with lists of +# possible values. +# Third item is whether differential encoding should be tested. +# Fourth item is the name of the argument to constructor that specifices +# whether differential encoding is used. + +def twod_constell(): + """ + + """ + points = ((1+0j), (0+1j), + (-1+0j), (0-1j)) + rot_sym = 2 + dim = 2 + return digital_swig.constellation_calcdist(points, [], rot_sym, dim) + +def threed_constell(): + oned_points = ((1+0j), (0+1j), (-1+0j), (0-1j)) + points = [] + r4 = range(0, 4) + for ia in r4: + for ib in r4: + for ic in r4: + points += [oned_points[ia], oned_points[ib], oned_points[ic]] + rot_sym = 4 + dim = 3 + return digital_swig.constellation_calcdist(points, [], rot_sym, dim) + +tested_constellation_info = ( + (psk.psk_constellation, + {'m': (2, 4, 8, 16, 32, 64), + 'mod_code': tested_mod_codes, }, + True, None), + (digital_swig.constellation_bpsk, {}, True, None), + (digital_swig.constellation_qpsk, {}, False, None), + (digital_swig.constellation_dqpsk, {}, True, None), + (digital_swig.constellation_8psk, {}, False, None), + (twod_constell, {}, True, None), + (threed_constell, {}, True, None), + ) + +def tested_constellations(): + """ + Generator to produce (constellation, differential) tuples for testing purposes. + """ + for constructor, poss_args, differential, diff_argname in tested_constellation_info: + if differential: + diff_poss = (True, False) + else: + diff_poss = (False,) + poss_args = [[argname, argvalues, 0] for argname, argvalues in poss_args.items()] + for current_diff in diff_poss: + # Add an index into args to keep track of current position in argvalues + while True: + current_args = dict([(argname, argvalues[argindex]) + for argname, argvalues, argindex in poss_args]) + if diff_argname is not None: + current_args[diff_argname] = current_diff + constellation = constructor(**current_args) + yield (constellation, current_diff) + for this_poss_arg in poss_args: + argname, argvalues, argindex = this_poss_arg + if argindex < len(argvalues) - 1: + this_poss_arg[2] += 1 + break + else: + this_poss_arg[2] = 0 + if sum([argindex for argname, argvalues, argindex in poss_args]) == 0: + break + + +class test_constellation (gr_unittest.TestCase): + + src_length = 256 + + def setUp(self): + # Generate a list of random bits. + self.src_data = tuple([random.randint(0,1) for i in range(0, self.src_length)]) + + def tearDown(self): + pass + + def test_hard_decision(self): + for constellation, differential in tested_constellations(): + if differential: + rs = constellation.rotational_symmetry() + rotations = [exp(i*2*pi*(0+1j)/rs) for i in range(0, rs)] + else: + rotations = [None] + for rotation in rotations: + src = gr.vector_source_b(self.src_data) + content = mod_demod(constellation, differential, rotation) + dst = gr.vector_sink_b() + self.tb = gr.top_block() + self.tb.connect(src, content, dst) + self.tb.run() + data = dst.data() + # Don't worry about cut off data for now. + first = constellation.bits_per_symbol() + self.assertEqual (self.src_data[first:len(data)], data[first:]) + + +class mod_demod(gr.hier_block2): + def __init__(self, constellation, differential, rotation): + if constellation.arity() > 256: + # If this becomes limiting some of the blocks should be generalised so + # that they can work with shorts and ints as well as chars. + raise ValueError("Constellation cannot contain more than 256 points.") + + gr.hier_block2.__init__(self, "mod_demod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + arity = constellation.arity() + + # TX + self.constellation = constellation + self.differential = differential + self.blocks = [self] + # We expect a stream of unpacked bits. + # First step is to pack them. + self.blocks.append( + gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST)) + # Second step we unpack them such that we have k bits in each byte where + # each constellation symbol hold k bits. + self.blocks.append( + gr.packed_to_unpacked_bb(self.constellation.bits_per_symbol(), + gr.GR_MSB_FIRST)) + # Apply any pre-differential coding + # Gray-coding is done here if we're also using differential coding. + if self.constellation.apply_pre_diff_code(): + self.blocks.append(gr.map_bb(self.constellation.pre_diff_code())) + # Differential encoding. + if self.differential: + self.blocks.append(gr.diff_encoder_bb(arity)) + # Convert to constellation symbols. + self.blocks.append(gr.chunks_to_symbols_bc(self.constellation.points(), + self.constellation.dimensionality())) + # CHANNEL + # Channel just consists of a rotation to check differential coding. + if rotation is not None: + self.blocks.append(gr.multiply_const_cc(rotation)) + + # RX + # Convert the constellation symbols back to binary values. + self.blocks.append(digital_swig.constellation_decoder_cb(self.constellation.base())) + # Differential decoding. + if self.differential: + self.blocks.append(gr.diff_decoder_bb(arity)) + # Decode any pre-differential coding. + if self.constellation.apply_pre_diff_code(): + self.blocks.append(gr.map_bb( + mod_codes.invert_code(self.constellation.pre_diff_code()))) + # unpack the k bit vector into a stream of bits + self.blocks.append(gr.unpack_k_bits_bb( + self.constellation.bits_per_symbol())) + # connect to block output + check_index = len(self.blocks) + self.blocks = self.blocks[:check_index] + self.blocks.append(self) + + self.connect(*self.blocks) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation, "test_constellation.xml") diff --git a/gr-digital/python/qa_constellation_decoder_cb.py b/gr-digital/python/qa_constellation_decoder_cb.py new file mode 100755 index 000000000..5401a07fc --- /dev/null +++ b/gr-digital/python/qa_constellation_decoder_cb.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import math + +class test_constellation_decoder (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_constellation_decoder_cb_bpsk (self): + cnst = digital_swig.constellation_bpsk() + src_data = (0.5 + 0.5j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, + 0.8 + 1.0j, -0.5 + 0.1j, 0.1 - 1.2j) + expected_result = ( 1, 1, 0, 0, + 1, 0, 1) + src = gr.vector_source_c (src_data) + op = digital_swig.constellation_decoder_cb (cnst.base()) + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + def test_constellation_decoder_cb_qpsk (self): + cnst = digital_swig.constellation_qpsk() + src_data = (0.5 + 0.5j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, + 0.8 + 1.0j, -0.5 + 0.1j, 0.1 - 1.2j) + expected_result = ( 3, 1, 0, 2, + 3, 2, 1) + src = gr.vector_source_c (src_data) + op = digital_swig.constellation_decoder_cb (cnst.base()) + dst = gr.vector_sink_b () + + self.tb.connect (src, op) + self.tb.connect (op, dst) + self.tb.run () # run the graph and wait for it to finish + + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation_decoder, "test_constellation_decoder.xml") + diff --git a/gr-digital/python/qa_constellation_receiver.py b/gr-digital/python/qa_constellation_receiver.py new file mode 100755 index 000000000..25107e4a7 --- /dev/null +++ b/gr-digital/python/qa_constellation_receiver.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import random + +from gnuradio import gr, blks2, gr_unittest +from utils import mod_codes, alignment +import digital_swig, packet_utils +from generic_mod_demod import generic_mod, generic_demod + +from qa_constellation import tested_constellations, twod_constell + +import math + +# Set a seed so that if errors turn up they are reproducible. +# 1234 fails +random.seed(1239) + +# TESTING PARAMETERS +# The number of symbols to test with. +# We need this many to let the frequency recovery block converge. +DATA_LENGTH = 10000 +# Test fails if fraction of output that is correct is less than this. +REQ_CORRECT = 0.8 + +# CHANNEL PARAMETERS +NOISE_VOLTAGE = 0.01 +FREQUENCY_OFFSET = 0.01 +TIMING_OFFSET = 1.0 + +# RECEIVER PARAMETERS +FREQ_BW = 2*math.pi/100.0 +PHASE_BW = 2*math.pi/100.0 + + +class test_constellation_receiver (gr_unittest.TestCase): + + # We ignore the first half of the output data since often it takes + # a while for the receiver to lock on. + ignore_fraction = 0.8 + seed = 1234 + max_data_length = DATA_LENGTH * 6 + max_num_samples = 1000 + + def test_basic(self): + """ + Tests a bunch of different constellations by using generic + modulation, a channel, and generic demodulation. The generic + demodulation uses constellation_receiver which is what + we're really trying to test. + """ + + # Assumes not more than 64 points in a constellation + # Generates some random input data to use. + self.src_data = tuple( + [random.randint(0,1) for i in range(0, self.max_data_length)]) + # Generates some random indices to use for comparing input and + # output data (a full comparison is too slow in python). + self.indices = alignment.random_sample( + self.max_data_length, self.max_num_samples, self.seed) + + for constellation, differential in tested_constellations(): + # The constellation_receiver doesn't work for constellations + # of multple dimensions (i.e. multiple complex numbers to a + # single symbol). + # That is not implemented since the receiver has no way of + # knowing where the beginning of a symbol is. + # It also doesn't work for non-differential modulation. + if constellation.dimensionality() != 1 or not differential: + continue + data_length = DATA_LENGTH * constellation.bits_per_symbol() + tb = rec_test_tb(constellation, differential, + src_data=self.src_data[:data_length]) + tb.run() + data = tb.dst.data() + d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)] + d2 = data[:int(len(data)*self.ignore_fraction)] + correct, overlap, offset, indices = alignment.align_sequences( + d1, d2, indices=self.indices) + self.assertTrue(correct > REQ_CORRECT) + + +class rec_test_tb (gr.top_block): + """ + Takes a constellation an runs a generic modulation, channel, + and generic demodulation. + """ + def __init__(self, constellation, differential, + data_length=None, src_data=None): + """ + constellation -- a constellation object + differential -- whether differential encoding is used + data_length -- the number of bits of data to use + src_data -- a list of the bits to use + """ + super(rec_test_tb, self).__init__() + # Transmission Blocks + if src_data is None: + self.src_data = tuple([random.randint(0,1) for i in range(0, data_length)]) + else: + self.src_data = src_data + packer = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST) + src = gr.vector_source_b(self.src_data) + mod = generic_mod(constellation, differential=differential) + # Channel + channel = gr.channel_model(NOISE_VOLTAGE, FREQUENCY_OFFSET, TIMING_OFFSET) + # Receiver Blocks + demod = generic_demod(constellation, differential=differential, + freq_bw=FREQ_BW, + phase_bw=PHASE_BW) + self.dst = gr.vector_sink_b() + self.connect(src, packer, mod, channel, demod, self.dst) + +if __name__ == '__main__': + gr_unittest.run(test_constellation_receiver, "test_constellation_receiver.xml") diff --git a/gr-digital/python/qa_correlate_access_code.py b/gr-digital/python/qa_correlate_access_code.py new file mode 100755 index 000000000..6b6f25051 --- /dev/null +++ b/gr-digital/python/qa_correlate_access_code.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import math + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +class test_correlate_access_code(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + pad = (0,) * 64 + # 0 0 0 1 0 0 0 1 + src_data = (1, 0, 1, 1, 1, 1, 0, 1, 1) + pad + (0,) * 7 + expected_result = pad + (1, 0, 1, 1, 3, 1, 0, 1, 1, 2) + (0,) * 6 + src = gr.vector_source_b (src_data) + op = digital_swig.correlate_access_code_bb("1011", 0) + dst = gr.vector_sink_b () + self.tb.connect (src, op, dst) + self.tb.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + def test_002(self): + code = tuple(string_to_1_0_list(default_access_code)) + access_code = to_1_0_string(code) + pad = (0,) * 64 + #print code + #print access_code + src_data = code + (1, 0, 1, 1) + pad + expected_result = pad + code + (3, 0, 1, 1) + src = gr.vector_source_b (src_data) + op = digital_swig.correlate_access_code_bb(access_code, 0) + dst = gr.vector_sink_b () + self.tb.connect (src, op, dst) + self.tb.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + +if __name__ == '__main__': + gr_unittest.run(test_correlate_access_code, "test_correlate_access_code.xml") + diff --git a/gr-digital/python/qa_costas_loop_cc.py b/gr-digital/python/qa_costas_loop_cc.py new file mode 100755 index 000000000..75fdbc2f8 --- /dev/null +++ b/gr-digital/python/qa_costas_loop_cc.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig, psk +import random, cmath + +class test_costas_loop_cc(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # test basic functionality by setting all gains to 0 + natfreq = 0.0 + order = 2 + self.test = digital_swig.costas_loop_cc(natfreq, order) + + data = 100*[complex(1,0),] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = data + dst_data = self.snk.data() + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + def test02 (self): + # Make sure it doesn't diverge given perfect data + natfreq = 0.25 + order = 2 + self.test = digital_swig.costas_loop_cc(natfreq, order) + + data = [complex(2*random.randint(0,1)-1, 0) for i in xrange(100)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = data + dst_data = self.snk.data() + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + def test03 (self): + # BPSK Convergence test with static rotation + natfreq = 0.25 + order = 2 + self.test = digital_swig.costas_loop_cc(natfreq, order) + + rot = cmath.exp(0.2j) # some small rotation + data = [complex(2*random.randint(0,1)-1, 0) for i in xrange(100)] + + N = 40 # settling time + expected_result = data[N:] + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + + def test04 (self): + # QPSK Convergence test with static rotation + natfreq = 0.25 + order = 4 + self.test = digital_swig.costas_loop_cc(natfreq, order) + + rot = cmath.exp(0.2j) # some small rotation + data = [complex(2*random.randint(0,1)-1, 2*random.randint(0,1)-1) + for i in xrange(100)] + + N = 40 # settling time + expected_result = data[N:] + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + + def test05 (self): + # 8PSK Convergence test with static rotation + natfreq = 0.25 + order = 8 + self.test = digital_swig.costas_loop_cc(natfreq, order) + + rot = cmath.exp(-cmath.pi/8.0j) # rotate to match Costas rotation + const = psk.psk_constellation(order) + data = [random.randint(0,7) for i in xrange(100)] + data = [2*rot*const.points()[d] for d in data] + + N = 40 # settling time + expected_result = data[N:] + + rot = cmath.exp(0.1j) # some small rotation + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + +if __name__ == '__main__': + gr_unittest.run(test_costas_loop_cc, "test_costas_loop_cc.xml") diff --git a/gr-digital/python/qa_cpm.py b/gr-digital/python/qa_cpm.py new file mode 100755 index 000000000..12a84c76c --- /dev/null +++ b/gr-digital/python/qa_cpm.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# +# Copyright 2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import numpy + +class test_cpm(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def do_check_phase_shift(self, type, name): + sps = 2 + L = 1 + in_bits = (1,) * 20 + src = gr.vector_source_b(in_bits, False) + cpm = digital_swig.cpmmod_bc(type, 0.5, sps, L) + arg = gr.complex_to_arg() + sink = gr.vector_sink_f() + + self.tb.connect(src, cpm, arg, sink) + self.tb.run() + + symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) + phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), + (2*numpy.pi,) * (len(symbol_phases)-1)) + self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, + msg="Phase shift was not correct for CPM method " + name) + + def test_001_lrec(self): + self.do_check_phase_shift(gr.cpm.LRC, 'LREC') + + def test_001_lrc(self): + self.do_check_phase_shift(gr.cpm.LRC, 'LRC') + + def test_001_lsrc(self): + self.do_check_phase_shift(gr.cpm.LSRC, 'LSRC') + + def test_001_ltfm(self): + self.do_check_phase_shift(gr.cpm.TFM, 'TFM') + + def test_001_lgmsk(self): + sps = 2 + L = 5 + bt = 0.3 + in_bits = (1,) * 20 + src = gr.vector_source_b(in_bits, False) + gmsk = digital_swig.gmskmod_bc(sps, bt, L) + arg = gr.complex_to_arg() + sink = gr.vector_sink_f() + + self.tb.connect(src, gmsk, arg, sink) + self.tb.run() + + symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) + phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), + (2*numpy.pi,) * (len(symbol_phases)-1)) + self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, + msg="Phase shift was not correct for GMSK") + + def test_phase_response(self): + phase_response = gr.cpm.phase_response(gr.cpm.LREC, 2, 4) + self.assertAlmostEqual(numpy.sum(phase_response), 1) + + +if __name__ == '__main__': + gr_unittest.run(test_cpm, "test_cpm.xml") + diff --git a/gr-digital/python/qa_crc32.py b/gr-digital/python/qa_crc32.py new file mode 100755 index 000000000..f86813f3f --- /dev/null +++ b/gr-digital/python/qa_crc32.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import random, cmath + +class test_crc32(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + data = 100*"0" + expected_result = 2943744955 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + + def test02 (self): + data = 100*"1" + expected_result = 2326594156 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + + def test03 (self): + data = 10*"0123456789" + expected_result = 3774345973 + result = digital_swig.crc32(data) + #print hex(result) + + self.assertEqual (expected_result, result) + +if __name__ == '__main__': + gr_unittest.run(test_crc32, "test_crc32.xml") diff --git a/gr-digital/python/qa_digital.py b/gr-digital/python/qa_digital.py new file mode 100755 index 000000000..97e35da56 --- /dev/null +++ b/gr-digital/python/qa_digital.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig + +class test_digital(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + +if __name__ == '__main__': + gr_unittest.run(test_digital, "test_digital.xml") diff --git a/gr-digital/python/qa_fll_band_edge.py b/gr-digital/python/qa_fll_band_edge.py new file mode 100755 index 000000000..088eb2b68 --- /dev/null +++ b/gr-digital/python/qa_fll_band_edge.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import random, math + +class test_fll_band_edge_cc(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + sps = 4 + rolloff = 0.35 + bw = 2*math.pi/100.0 + ntaps = 45 + + # Create pulse shape filter + rrc_taps = gr.firdes.root_raised_cosine( + sps, sps, 1.0, rolloff, ntaps) + + # The frequency offset to correct + foffset = 0.2 / (2.0*math.pi) + + # Create a set of 1's and -1's, pulse shape and interpolate to sps + data = [2.0*random.randint(0, 2) - 1.0 for i in xrange(200)] + self.src = gr.vector_source_c(data, False) + self.rrc = gr.interp_fir_filter_ccf(sps, rrc_taps) + + # Mix symbols with a complex sinusoid to spin them + self.nco = gr.sig_source_c(1, gr.GR_SIN_WAVE, foffset, 1) + self.mix = gr.multiply_cc() + + # FLL will despin the symbols to an arbitrary phase + self.fll = digital_swig.fll_band_edge_cc(sps, rolloff, ntaps, bw) + + # Create sinks for all outputs of the FLL + # we will only care about the freq and error outputs + self.vsnk_frq = gr.vector_sink_f() + self.nsnk_fll = gr.null_sink(gr.sizeof_gr_complex) + self.nsnk_phs = gr.null_sink(gr.sizeof_float) + self.nsnk_err = gr.null_sink(gr.sizeof_float) + + # Connect the blocks + self.tb.connect(self.nco, (self.mix,1)) + self.tb.connect(self.src, self.rrc, (self.mix,0)) + self.tb.connect(self.mix, self.fll, self.nsnk_fll) + self.tb.connect((self.fll,1), self.vsnk_frq) + self.tb.connect((self.fll,2), self.nsnk_phs) + self.tb.connect((self.fll,3), self.nsnk_err) + self.tb.run() + + N = 700 + dst_data = self.vsnk_frq.data()[N:] + + expected_result = len(dst_data)* [-0.20,] + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4) + +if __name__ == '__main__': + gr_unittest.run(test_fll_band_edge_cc, "test_fll_band_edge_cc.xml") diff --git a/gr-digital/python/qa_lms_equalizer.py b/gr-digital/python/qa_lms_equalizer.py new file mode 100755 index 000000000..025c785aa --- /dev/null +++ b/gr-digital/python/qa_lms_equalizer.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig + +class test_lms_dd_equalizer(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def transform(self, src_data, gain, const): + SRC = gr.vector_source_c(src_data, False) + EQU = digital_swig.lms_dd_equalizer_cc(4, gain, 1, const.base()) + DST = gr.vector_sink_c() + self.tb.connect(SRC, EQU, DST) + self.tb.run() + return DST.data() + + def test_001_identity(self): + # Constant modulus signal so no adjustments + const = digital_swig.constellation_qpsk() + src_data = const.points()*1000 + + N = 100 # settling time + expected_data = src_data[N:] + result = self.transform(src_data, 0.1, const)[N:] + self.assertComplexTuplesAlmostEqual(expected_data, result, 5) + +if __name__ == "__main__": + gr_unittest.run(test_lms_dd_equalizer, "test_lms_dd_equalizer.xml") diff --git a/gr-digital/python/qa_mpsk_receiver.py b/gr-digital/python/qa_mpsk_receiver.py new file mode 100755 index 000000000..e1f16ee67 --- /dev/null +++ b/gr-digital/python/qa_mpsk_receiver.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import random, cmath + +class test_mpsk_receiver(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # Test BPSK sync + M = 2 + theta = 0 + loop_bw = cmath.pi/100.0 + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, loop_bw, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex(1,0), complex(1,0), complex(-1,0), complex(-1,0)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(-0.5,0), complex(0.5,0)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #for e,d in zip(expected_result, dst_data): + # print e, d + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + + + def test02 (self): + # Test QPSK sync + M = 4 + theta = 0 + loop_bw = 2*cmath.pi/100.0 + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, loop_bw, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex( 0.707, 0.707), complex( 0.707, 0.707), + complex(-0.707, 0.707), complex(-0.707, 0.707), + complex(-0.707, -0.707), complex(-0.707, -0.707), + complex( 0.707, -0.707), complex( 0.707, -0.707)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(0, -1.0), complex(1.0, 0), + complex(0, 1.0), complex(-1.0, 0)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #for e,d in zip(expected_result, dst_data): + # print e, d + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + +if __name__ == '__main__': + gr_unittest.run(test_mpsk_receiver, "test_mpsk_receiver.xml") diff --git a/gr-digital/python/qa_ofdm_insert_preamble.py b/gr-digital/python/qa_ofdm_insert_preamble.py new file mode 100755 index 000000000..c45893fa3 --- /dev/null +++ b/gr-digital/python/qa_ofdm_insert_preamble.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# +# Copyright 2007,2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +from pprint import pprint +import digital_swig + +class test_ofdm_insert_preamble (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def helper(self, v0, v1, fft_length, preamble): + tb = self.tb + src0 = gr.vector_source_c(v0) + src1 = gr.vector_source_b(v1) + + s2v = gr.stream_to_vector(gr.sizeof_gr_complex, fft_length) + + # print "len(v) = %d" % (len(v)) + + op = digital_swig.ofdm_insert_preamble(fft_length, preamble) + + v2s = gr.vector_to_stream(gr.sizeof_gr_complex, fft_length) + dst0 = gr.vector_sink_c() + dst1 = gr.vector_sink_b() + + tb.connect(src0, s2v, (op, 0)) + tb.connect(src1, (op, 1)) + tb.connect((op, 0), v2s, dst0) + tb.connect((op, 1), dst1) + + tb.run() + r0 = dst0.data() + r0v = [] + for i in range(len(r0)//fft_length): + r0v.append(r0[i*fft_length:(i+1)*fft_length]) + + r1 = dst1.data() + self.assertEqual(len(r0v), len(r1)) + return (r1, r0v) + + def check_match(self, actual, expected_list): + lst = [] + map(lambda x: lst.append(x), expected_list) + self.assertEqual(actual, lst) + + + # ---------------------------------------------------------------- + + def test_000(self): + # no preamble, 1 symbol payloads + + preamble = () + fft_length = 8 + npayloads = 8 + v = [] + p = [] + for i in range(npayloads): + t = fft_length*[(i + i*1j)] + p.append(tuple(t)) + v += t + + p = tuple(p) + + r = self.helper(v, npayloads*[1], fft_length, preamble) + # pprint(r) + + self.assertEqual(r[0], tuple(npayloads*[1])) + self.check_match(r[1], (p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7])) + + + def test_001(self): + # 1 symbol preamble, 1 symbol payloads + preamble = ((100, 101, 102, 103, 104, 105, 106, 107),) + p0 = preamble[0] + fft_length = 8 + npayloads = 8 + v = [] + p = [] + for i in range(npayloads): + t = fft_length*[(i + i*1j)] + p.append(tuple(t)) + v += t + + + r = self.helper(v, npayloads*[1], fft_length, preamble) + + self.assertEqual(r[0], tuple(npayloads*[1, 0])) + self.check_match(r[1], (p0, p[0], + p0, p[1], + p0, p[2], + p0, p[3], + p0, p[4], + p0, p[5], + p0, p[6], + p0, p[7])) + + def test_002(self): + # 2 symbol preamble, 1 symbol payloads + preamble = ((100, 101, 102, 103, 104, 105, 106, 107), + (200, 201, 202, 203, 204, 205, 206, 207)) + p0 = preamble[0] + p1 = preamble[1] + + fft_length = 8 + npayloads = 8 + v = [] + p = [] + for i in range(npayloads): + t = fft_length*[(i + i*1j)] + p.append(tuple(t)) + v += t + + r = self.helper(v, npayloads*[1], fft_length, preamble) + + self.assertEqual(r[0], tuple(npayloads*[1, 0, 0])) + self.check_match(r[1], (p0, p1, p[0], + p0, p1, p[1], + p0, p1, p[2], + p0, p1, p[3], + p0, p1, p[4], + p0, p1, p[5], + p0, p1, p[6], + p0, p1, p[7])) + + + def xtest_003_preamble(self): + # 2 symbol preamble, 2 symbol payloads + preamble = ((100, 101, 102, 103, 104, 105, 106, 107), + (200, 201, 202, 203, 204, 205, 206, 207)) + p0 = preamble[0] + p1 = preamble[1] + + fft_length = 8 + npayloads = 8 + v = [] + p = [] + for i in range(npayloads * 2): + t = fft_length*[(i + i*1j)] + p.append(tuple(t)) + v += t + + r = self.helper(v, npayloads*[1, 0], fft_length, preamble) + + self.assertEqual(r[0], tuple(npayloads*[1, 0, 0, 0])) + self.check_match(r[1], (p0, p1, p[0], p[1], + p0, p1, p[2], p[3], + p0, p1, p[4], p[5], + p0, p1, p[6], p[7], + p0, p1, p[8], p[9], + p0, p1, p[10], p[11], + p0, p1, p[12], p[13], + p0, p1, p[14], p[15])) + + +if __name__ == '__main__': + gr_unittest.run(test_ofdm_insert_preamble, "test_ofdm_insert_preamble.xml") diff --git a/gr-digital/python/qam.py b/gr-digital/python/qam.py new file mode 100644 index 000000000..5b1f7683b --- /dev/null +++ b/gr-digital/python/qam.py @@ -0,0 +1,229 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +QAM modulation and demodulation. +""" + +from math import pi, sqrt, log + +from gnuradio import gr +from generic_mod_demod import generic_mod, generic_demod +from utils.gray_code import gray_code +from utils import mod_codes +import modulation_utils +import digital_swig + +# Default number of points in constellation. +_def_constellation_points = 16 +# Whether the quadrant bits are coded differentially. +_def_differential = True +# Whether gray coding is used. If differential is True then gray +# coding is used within but not between each quadrant. +_def_mod_code = mod_codes.NO_CODE + +def is_power_of_four(x): + v = log(x)/log(4) + return int(v) == v + +def get_bit(x, n): + """ Get the n'th bit of integer x (from little end).""" + return (x&(0x01 << n)) >> n + +def get_bits(x, n, k): + """ Get the k bits of integer x starting at bit n(from little end).""" + # Remove the n smallest bits + v = x >> n + # Remove all bits bigger than n+k-1 + return v % pow(2, k) + +def make_differential_constellation(m, gray_coded): + """ + Create a constellation with m possible symbols where m must be + a power of 4. + + Points are laid out in a square grid. + + Bits referring to the quadrant are differentilly encoded, + remaining bits are gray coded. + + """ + sqrtm = pow(m, 0.5) + if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): + raise ValueError("m must be a power of 4 integer.") + # Each symbol holds k bits. + k = int(log(m) / log(2.0)) + # First create a constellation for one quadrant containing m/4 points. + # The quadrant has 'side' points along each side of a quadrant. + side = int(sqrtm/2) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + else: + i_gcs = dict([(i, i) for i in range(0, side)]) + # The distance between points is found. + step = 1/(side-0.5) + + gc_to_x = [(i_gcs[gc]+0.5)*step for gc in range(0, side)] + + # Takes the (x, y) location of the point with the quadrant along + # with the quadrant number. (x, y) are integers referring to which + # point within the quadrant it is. + # A complex number representing this location of this point is returned. + def get_c(gc_x, gc_y, quad): + if quad == 0: + return complex(gc_to_x[gc_x], gc_to_x[gc_y]) + if quad == 1: + return complex(-gc_to_x[gc_y], gc_to_x[gc_x]) + if quad == 2: + return complex(-gc_to_x[gc_x], -gc_to_x[gc_y]) + if quad == 3: + return complex(gc_to_x[gc_y], -gc_to_x[gc_x]) + raise StandardError("Impossible!") + + # First two bits determine quadrant. + # Next (k-2)/2 bits determine x position. + # Following (k-2)/2 bits determine y position. + # How x and y relate to real and imag depends on quadrant (see get_c function). + const_map = [] + for i in range(m): + y = get_bits(i, 0, (k-2)/2) + x = get_bits(i, (k-2)/2, (k-2)/2) + quad = get_bits(i, k-2, 2) + const_map.append(get_c(x, y, quad)) + + return const_map + +def make_non_differential_constellation(m, gray_coded): + side = int(pow(m, 0.5)) + if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): + raise ValueError("m must be a power of 4 integer.") + # Each symbol holds k bits. + k = int(log(m) / log(2.0)) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = mod_codes.invert_code(gcs) + else: + i_gcs = range(0, side) + # The distance between points is found. + step = 2.0/(side-1) + + gc_to_x = [-1 + i_gcs[gc]*step for gc in range(0, side)] + # First k/2 bits determine x position. + # Following k/2 bits determine y position. + const_map = [] + for i in range(m): + y = gc_to_x[get_bits(i, 0, k/2)] + x = gc_to_x[get_bits(i, k/2, k/2)] + const_map.append(complex(x,y)) + return const_map + +# ///////////////////////////////////////////////////////////////////////////// +# QAM constellation +# ///////////////////////////////////////////////////////////////////////////// + +def qam_constellation(constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code): + """ + Creates a QAM constellation object. + """ + if mod_code == mod_codes.GRAY_CODE: + gray_coded = True + elif mod_code == mod_codes.NO_CODE: + gray_coded = False + else: + raise ValueError("Mod code is not implemented for QAM") + if differential: + points = make_differential_constellation(constellation_points, gray_coded) + else: + points = make_non_differential_constellation(constellation_points, gray_coded) + side = int(sqrt(constellation_points)) + width = 2.0/(side-1) + # No pre-diff code + # Should add one so that we can gray-code the quadrant bits too. + pre_diff_code = [] + constellation = digital_swig.constellation_rect(points, pre_diff_code, 4, + side, side, width, width) + return constellation + +# ///////////////////////////////////////////////////////////////////////////// +# QAM modulator +# ///////////////////////////////////////////////////////////////////////////// + +class qam_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = qam_constellation(constellation_points, differential, mod_code) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_mod, self).__init__(constellation, differential=differential, + *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# QAM demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class qam_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + constellation = qam_constellation(constellation_points, differential, mod_code) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_demod, self).__init__(constellation, differential=differential, + *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('qam', qam_mod) +modulation_utils.add_type_1_demod('qam', qam_demod) +modulation_utils.add_type_1_constellation('qam', qam_constellation) diff --git a/gr-digital/python/qpsk.py b/gr-digital/python/qpsk.py new file mode 100644 index 000000000..be21fd76f --- /dev/null +++ b/gr-digital/python/qpsk.py @@ -0,0 +1,176 @@ +# +# Copyright 2005,2006,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +QPSK modulation. + +Demodulation is not included since the generic_mod_demod +""" + +from gnuradio import gr +from gnuradio.digital.generic_mod_demod import generic_mod, generic_demod +import digital_swig +import modulation_utils + +# Default number of points in constellation. +_def_constellation_points = 4 +# Whether gray coding is used. +_def_gray_coded = True + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def qpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("QPSK can only have 4 constellation points.") + return digital_swig.constellation_qpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class qpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + gray_coded=_def_gray_coded, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_qpsk() + if constellation_points != 4: + raise ValueError("QPSK can only have 4 constellation points.") + if not gray_coded: + raise ValueError("This QPSK mod/demod works only for gray-coded constellations.") + super(qpsk_mod, self).__init__(constellation=constellation, + gray_coded=gray_coded, + *args, **kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class qpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_qpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for QPSK.') + super(qpsk_demod, self).__init__(constellation=constellation, + *args, **kwargs) + + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def dqpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("DQPSK can only have 4 constellation points.") + return digital_swig.constellation_dqpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + gray_coded=_def_gray_coded, + differential=True, *args, **kwargs): + """ + Hierarchical block for RRC-filtered DQPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_dqpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for DQPSK.') + super(dqpsk_mod, self).__init__(constellation=constellation, + gray_coded=gray_coded, + differential=True, + *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=True, *args, **kwargs): + + """ + Hierarchical block for RRC-filtered DQPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + constellation_points = _def_constellation_points + constellation = digital_swig.constellation_dqpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for DQPSK.') + super(dqpsk_demod, self).__init__(constellation=constellation, + differential=True, + *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('qpsk', qpsk_mod) +modulation_utils.add_type_1_demod('qpsk', qpsk_demod) +modulation_utils.add_type_1_constellation('qpsk', qpsk_constellation) +modulation_utils.add_type_1_mod('dqpsk', dqpsk_mod) +modulation_utils.add_type_1_demod('dqpsk', dqpsk_demod) +modulation_utils.add_type_1_constellation('dqpsk', dqpsk_constellation) + diff --git a/gr-digital/python/run_tests.in b/gr-digital/python/run_tests.in new file mode 100644 index 000000000..b39e7e847 --- /dev/null +++ b/gr-digital/python/run_tests.in @@ -0,0 +1,10 @@ +#!/bin/sh + +# 1st parameter is absolute path to component source directory +# 2nd parameter is absolute path to component build directory +# 3rd parameter is path to Python QA directory + +@top_builddir@/run_tests.sh \ + @abs_top_srcdir@/gr-digital \ + @abs_top_builddir@/gr-digital \ + @srcdir@ diff --git a/gr-digital/python/utils/.gitignore b/gr-digital/python/utils/.gitignore new file mode 100644 index 000000000..60c81fdce --- /dev/null +++ b/gr-digital/python/utils/.gitignore @@ -0,0 +1,3 @@ +run_tests +Makefile +Makefile.in diff --git a/gr-digital/python/utils/Makefile.am b/gr-digital/python/utils/Makefile.am new file mode 100644 index 000000000..6da4d61dd --- /dev/null +++ b/gr-digital/python/utils/Makefile.am @@ -0,0 +1,34 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +if PYTHON +utilspythondir = $(grpythondir)/digital/utils + +TESTS = + +utilspython_PYTHON = \ + __init__.py \ + gray_code.py \ + mod_codes.py \ + alignment.py +endif
\ No newline at end of file diff --git a/gr-digital/python/utils/__init__.py b/gr-digital/python/utils/__init__.py new file mode 100644 index 000000000..b3e997f9f --- /dev/null +++ b/gr-digital/python/utils/__init__.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# diff --git a/gr-digital/python/utils/alignment.py b/gr-digital/python/utils/alignment.py new file mode 100644 index 000000000..d32365866 --- /dev/null +++ b/gr-digital/python/utils/alignment.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# +""" +This module contains functions for aligning sequences. + +>>> import random +>>> random.seed(1234) +>>> ran_seq = [random.randint(0,1) for i in range(0, 100)] +>>> offset_seq = [0] * 20 + ran_seq +>>> correct, overlap, offset = align_sequences(ran_seq, offset_seq) +>>> print(correct, overlap, offset) +(1.0, 100, -20) +>>> offset_err_seq = [] +>>> for bit in offset_seq: +... if random.randint(0,4) == 4: +... offset_err_seq.append(random.randint(0,1)) +... else: +... offset_err_seq.append(bit) +>>> correct, overlap, offset = align_sequences(ran_seq, offset_err_seq) +>>> print(overlap, offset) +(100, -20) + +""" + +import random + +# DEFAULT PARAMETERS +# If the fraction of matching bits between two sequences is greater than +# this the sequences are assumed to be aligned. +def_correct_cutoff = 0.9 +# The maximum offset to test during sequence alignment. +def_max_offset = 500 +# The maximum number of samples to take from two sequences to check alignment. +def_num_samples = 1000 + +def compare_sequences(d1, d2, offset, sample_indices=None): + """ + Takes two binary sequences and an offset and returns the number of + matching entries and the number of compared entries. + d1 & d2 -- sequences + offset -- offset of d2 relative to d1 + sample_indices -- a list of indices to use for the comparison + """ + max_index = min(len(d1), len(d2)+offset) + if sample_indices is None: + sample_indices = range(0, max_index) + correct = 0 + total = 0 + for i in sample_indices: + if i >= max_index: + break + if d1[i] == d2[i-offset]: + correct += 1 + total += 1 + return (correct, total) + +def random_sample(size, num_samples=def_num_samples, seed=None): + """ + Returns a set of random integers between 0 and (size-1). + The set contains no more than num_samples integers. + """ + random.seed(seed) + if num_samples > size: + indices = set(range(0, size)) + else: + if num_samples > size/2: + num_samples = num_samples/2 + indices = set([]) + while len(indices) < num_samples: + index = random.randint(0, size-1) + indices.add(index) + indices = list(indices) + indices.sort() + return indices + +def align_sequences(d1, d2, + num_samples=def_num_samples, + max_offset=def_max_offset, + correct_cutoff=def_correct_cutoff, + seed=None, + indices=None): + """ + Takes two sequences and finds the offset and which the two sequences best + match. It returns the fraction correct, the number of entries compared, + the offset. + d1 & d2 -- sequences to compare + num_samples -- the maximum number of entries to compare + max_offset -- the maximum offset between the sequences that is checked + correct_cutoff -- If the fraction of bits correct is greater than this then + the offset is assumed to optimum. + seed -- a random number seed + indices -- an explicit list of the indices used to compare the two sequences + """ + max_overlap = max(len(d1), len(d2)) + if indices is None: + indices = random_sample(max_overlap, num_samples, seed) + max_frac_correct = 0 + best_offset = None + best_compared = None + best_correct = None + pos_range = range(0, min(len(d1), max_offset)) + neg_range = range(-1, -min(len(d2), max_offset), -1) + # Interleave the positive and negative offsets. + int_range = [item for items in zip(pos_range, neg_range) for item in items] + for offset in int_range: + correct, compared = compare_sequences(d1, d2, offset, indices) + frac_correct = 1.0*correct/compared + if frac_correct > max_frac_correct: + max_frac_correct = frac_correct + best_offset = offset + best_compared = compared + best_correct = correct + if frac_correct > correct_cutoff: + break + return max_frac_correct, best_compared, best_offset, indices + +if __name__ == "__main__": + import doctest + doctest.testmod() + diff --git a/gr-digital/python/utils/gray_code.py b/gr-digital/python/utils/gray_code.py new file mode 100644 index 000000000..926a1ded1 --- /dev/null +++ b/gr-digital/python/utils/gray_code.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +class GrayCodeGenerator(object): + """ + Generates and caches gray codes. + """ + + def __init__(self): + self.gcs = [0, 1] + # The last power of two passed through. + self.lp2 = 2 + # The next power of two that will be passed through. + self.np2 = 4 + # Curent index + self.i = 2 + + def get_gray_code(self, length): + """ + Returns a list of gray code of given length. + """ + if len(self.gcs) < length: + self.generate_new_gray_code(length) + return self.gcs[:length] + + def generate_new_gray_code(self, length): + """ + Generates new gray code and places into cache. + """ + while len(self.gcs) < length: + if self.i == self.lp2: + # if i is a power of two then gray number is of form 1100000... + result = self.i + self.i/2 + else: + # if not we take advantage of the symmetry of all but the last bit + # around a power of two. + result = self.gcs[2*self.lp2-1-self.i] + self.lp2 + self.gcs.append(result) + self.i += 1 + if self.i == self.np2: + self.lp2 = self.i + self.np2 = self.i*2 + +_gray_code_generator = GrayCodeGenerator() + +gray_code = _gray_code_generator.get_gray_code + diff --git a/gr-digital/python/utils/mod_codes.py b/gr-digital/python/utils/mod_codes.py new file mode 100644 index 000000000..caacda5cc --- /dev/null +++ b/gr-digital/python/utils/mod_codes.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +GRAY_CODE = 'gray' +SET_PARTITION_CODE = 'set-partition' +NO_CODE = 'none' + +codes = (GRAY_CODE, SET_PARTITION_CODE, NO_CODE) + +def invert_code(code): + c = enumerate(code) + ic = [(b, a) for (a, b) in c] + ic.sort() + return [a for (b, a) in ic] diff --git a/gr-digital/swig/.gitignore b/gr-digital/swig/.gitignore new file mode 100644 index 000000000..7e864f43f --- /dev/null +++ b/gr-digital/swig/.gitignore @@ -0,0 +1,9 @@ +/Makefile +/Makefile.in +/pager_swig.py +/pager_swig.cc +/*.pyc +/run_tests +/run_guile_tests +/guile +/python diff --git a/gr-digital/swig/Makefile.am b/gr-digital/swig/Makefile.am new file mode 100644 index 000000000..1afa44743 --- /dev/null +++ b/gr-digital/swig/Makefile.am @@ -0,0 +1,88 @@ +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common +include $(top_srcdir)/Makefile.swig + +TESTS = +EXTRA_DIST += $(nobase_guile_DATA) + +AM_CPPFLAGS = \ + $(GR_DIGITAL_INCLUDES) \ + $(STD_DEFINES_AND_INCLUDES) \ + $(PYTHON_CPPFLAGS) \ + $(WITH_INCLUDES) + +if GUILE +nobase_guile_DATA = \ + gnuradio/digital.scm +endif + +noinst_GUILE = digital.test + + +############################## +# SWIG interface and library +TOP_SWIG_IFILES = \ + digital_swig.i + +# Install so that they end up available as: +# import gnuradio.digital +# This ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio/digital +digital_swig_pythondir_category = \ + gnuradio/digital + +# additional libraries for linking with the SWIG-generated library +digital_swig_la_swig_libadd = \ + $(abs_top_builddir)/gr-digital/lib/libgnuradio-digital.la + +# additional SWIG files to be installed +digital_swig_swiginclude_headers = \ + digital_binary_slicer_fb.i \ + digital_clock_recovery_mm_cc.i \ + digital_clock_recovery_mm_ff.i \ + digital_constellation.i \ + digital_constellation_receiver_cb.i \ + digital_constellation_decoder_cb.i \ + digital_correlate_access_code_bb.i \ + digital_costas_loop_cc.i \ + digital_cma_equalizer_cc.i \ + digital_crc32.i \ + digital_fll_band_edge_cc.i \ + digital_lms_dd_equalizer_cc.i \ + digital_kurtotic_equalizer_cc.i \ + digital_mpsk_receiver_cc.i \ + digital_ofdm_cyclic_prefixer.i \ + digital_ofdm_frame_acquisition.i \ + digital_ofdm_frame_sink.i \ + digital_ofdm_insert_preamble.i \ + digital_ofdm_mapper_bcv.i \ + digital_ofdm_sampler.i \ + digital_gmskmod_bc.i \ + digital_cpmmod_bc.i + +digital_swig_swig_args = \ + $(GR_DIGITAL_INCLUDES) + +if GUILE +TESTS += run_guile_tests +endif
\ No newline at end of file diff --git a/gr-digital/swig/Makefile.swig.gen b/gr-digital/swig/Makefile.swig.gen new file mode 100644 index 000000000..ff3eff4e9 --- /dev/null +++ b/gr-digital/swig/Makefile.swig.gen @@ -0,0 +1,142 @@ +# -*- Makefile -*- +# +# Copyright 2009 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# Makefile.swig.gen for digital_swig.i + +## Default install locations for these files: +## +## Default location for the Python directory is: +## ${prefix}/lib/python${python_version}/site-packages/[category]/digital_swig +## Default location for the Python exec directory is: +## ${exec_prefix}/lib/python${python_version}/site-packages/[category]/digital_swig +## +## The following can be overloaded to change the install location, but +## this has to be done in the including Makefile.am -before- +## Makefile.swig is included. + +digital_swig_pythondir_category ?= gnuradio/digital_swig +digital_swig_pylibdir_category ?= $(digital_swig_pythondir_category) +digital_swig_pythondir = $(pythondir)/$(digital_swig_pythondir_category) +digital_swig_pylibdir = $(pyexecdir)/$(digital_swig_pylibdir_category) + +# The .so libraries for the guile modules get installed whereever guile +# is installed, usually /usr/lib/guile/gnuradio/ +# FIXME: determince whether these should be installed with gnuradio. +digital_swig_scmlibdir = $(libdir) + +# The scm files for the guile modules get installed where ever guile +# is installed, usually /usr/share/guile/site/digital_swig +# FIXME: determince whether these should be installed with gnuradio. +digital_swig_scmdir = $(guiledir) + +## SWIG headers are always installed into the same directory. + +digital_swig_swigincludedir = $(swigincludedir) + +## This is a template file for a "generated" Makefile addition (in +## this case, "Makefile.swig.gen"). By including the top-level +## Makefile.swig, this file will be used to generate the SWIG +## dependencies. Assign the variable TOP_SWIG_FILES to be the list of +## SWIG .i files to generated wrappings for; there can be more than 1 +## so long as the names are unique (no sorting is done on the +## TOP_SWIG_FILES list). This file explicitly assumes that a SWIG .i +## file will generate .cc, .py, and possibly .h files -- meaning that +## all of these files will have the same base name (that provided for +## the SWIG .i file). +## +## This code is setup to ensure parallel MAKE ("-j" or "-jN") does the +## right thing. For more info, see < +## http://sources.redhat.com/automake/automake.html#Multiple-Outputs > + +## Other cleaned files: dependency files generated by SWIG or this Makefile + +MOSTLYCLEANFILES += $(DEPDIR)/*.S* + +## Various SWIG variables. These can be overloaded in the including +## Makefile.am by setting the variable value there, then including +## Makefile.swig . + +digital_swig_swiginclude_HEADERS = \ + digital_swig.i \ + $(digital_swig_swiginclude_headers) + +if PYTHON +digital_swig_pylib_LTLIBRARIES = \ + _digital_swig.la + +_digital_swig_la_SOURCES = \ + python/digital_swig.cc \ + $(digital_swig_la_swig_sources) + +digital_swig_python_PYTHON = \ + digital_swig.py \ + $(digital_swig_python) + +_digital_swig_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_swig_la_swig_libadd) + +_digital_swig_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_swig_la_swig_ldflags) + +_digital_swig_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) + +python/digital_swig.cc: digital_swig.py +digital_swig.py: digital_swig.i + +# Include the python dependencies for this file +-include python/digital_swig.d + +endif # end of if python + +if GUILE + +digital_swig_scmlib_LTLIBRARIES = \ + libguile-gnuradio-digital_swig.la +libguile_gnuradio_digital_swig_la_SOURCES = \ + guile/digital_swig.cc \ + $(digital_swig_la_swig_sources) +nobase_digital_swig_scm_DATA = \ + gnuradio/digital_swig.scm \ + gnuradio/digital_swig-primitive.scm +libguile_gnuradio_digital_swig_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_swig_la_swig_libadd) +libguile_gnuradio_digital_swig_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_swig_la_swig_ldflags) +libguile_gnuradio_digital_swig_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) \ + $(digital_swig_la_swig_cxxflags) + +guile/digital_swig.cc: gnuradio/digital_swig.scm +gnuradio/digital_swig.scm: digital_swig.i +gnuradio/digital_swig-primitive.scm: gnuradio/digital_swig.scm + +# Include the guile dependencies for this file +-include guile/digital_swig.d + +endif # end of GUILE diff --git a/gr-digital/swig/_digital_hier.i b/gr-digital/swig/_digital_hier.i new file mode 100644 index 000000000..022e38644 --- /dev/null +++ b/gr-digital/swig/_digital_hier.i @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cpmmod_bc.h> +#include <digital_gmskmod_bc.h> +%} + +%include "digital_cpmmod_bc.i" +%include "digital_gmskmod_bc.i" diff --git a/gr-digital/swig/digital_binary_slicer_fb.i b/gr-digital/swig/digital_binary_slicer_fb.i new file mode 100644 index 000000000..30603748b --- /dev/null +++ b/gr-digital/swig/digital_binary_slicer_fb.i @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,binary_slicer_fb); + +digital_binary_slicer_fb_sptr digital_make_binary_slicer_fb (); + +class digital_binary_slicer_fb : public gr_sync_block +{ + private: + digital_binary_slicer_fb (); + + public: +}; diff --git a/gr-digital/swig/digital_clock_recovery_mm_cc.i b/gr-digital/swig/digital_clock_recovery_mm_cc.i new file mode 100644 index 000000000..4ce9a9725 --- /dev/null +++ b/gr-digital/swig/digital_clock_recovery_mm_cc.i @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,clock_recovery_mm_cc); + +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) throw(std::exception); + +class digital_clock_recovery_mm_cc : public gr_sync_block +{ + private: + digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); + +public: + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + void set_mu (float omega) { d_mu = mu; } + void set_omega (float omega) { d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_relative_limit); + d_max_omega = omega*(1.0 + d_omega_relative_limit); + } + void set_verbose (bool verbose) { d_verbose = verbose; } +}; diff --git a/gr-digital/swig/digital_clock_recovery_mm_ff.i b/gr-digital/swig/digital_clock_recovery_mm_ff.i new file mode 100644 index 000000000..054ef9ebf --- /dev/null +++ b/gr-digital/swig/digital_clock_recovery_mm_ff.i @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,clock_recovery_mm_ff); + +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit=0.001) throw(std::exception); + +class digital_clock_recovery_mm_ff : public gr_sync_block +{ + private: + digital_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit); + +public: + float mu() const; + float omega() const; + float gain_mu() const; + float gain_omega() const; + + void set_gain_mu (float gain_mu); + void set_gain_omega (float gain_omega); + void set_mu (float omega); + void set_omega (float omega); +}; diff --git a/gr-digital/swig/digital_cma_equalizer_cc.i b/gr-digital/swig/digital_cma_equalizer_cc.i new file mode 100644 index 000000000..183e43ef9 --- /dev/null +++ b/gr-digital/swig/digital_cma_equalizer_cc.i @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,cma_equalizer_cc) + +// retrieve info on the base class, without generating wrappers since +// the base class has a pure virual method. +%import "gr_adaptive_fir_ccc.i" + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, float modulus, + float mu, int sps); + +class digital_cma_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + digital_cma_equalizer_cc(int num_taps, float modulus, + float mu, int sps); + +public: + float get_gain(); + void set_gain(float mu); + float get_modulus(); + void set_modulus(float mod); +}; diff --git a/gr-digital/swig/digital_constellation.i b/gr-digital/swig/digital_constellation.i new file mode 100644 index 000000000..7e0ad6afe --- /dev/null +++ b/gr-digital/swig/digital_constellation.i @@ -0,0 +1,189 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%template(gr_complex_vector) std::vector<gr_complex>; +%template(unsigned_int_vector) std::vector<unsigned int>; + +// Make sure metric types get SWIGed. +%include "digital_metric_type.h" + +class digital_constellation; +typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; +%template(digital_constellation_sptr) boost::shared_ptr<digital_constellation>; + +class digital_constellation +{ +public: + digital_constellation (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + std::vector<gr_complex> points(); + std::vector<gr_complex> s_points(); + std::vector<std::vector<gr_complex> > v_points(); + virtual unsigned int decision_maker (gr_complex *sample) = 0; + unsigned int decision_maker_v (std::vector<gr_complex> sample); + // void calc_metric(gr_complex *sample, float *metric, trellis_metric_type_t type); + // void calc_euclidean_metric(gr_complex *sample, float *metric); + // void calc_hard_symbol_metric(gr_complex *sample, float *metric); + std::vector<gr_complex> map_to_points_v(unsigned int value); + unsigned int bits_per_symbol (); + unsigned int arity (); + digital_constellation_sptr base (); + bool apply_pre_diff_code(); + void set_pre_diff_code(bool a); + std::vector<unsigned int> pre_diff_code(); + unsigned int rotational_symmetry(); + unsigned int dimensionality(); +}; + +class digital_constellation_calcdist; +typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_calcdist_sptr; +%template(digital_constellation_calcdist_sptr) boost::shared_ptr<digital_constellation_calcdist>; +%rename(constellation_calcdist) digital_make_constellation_calcdist; +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); +%ignore digital_constellation_calcdist; + +class digital_constellation_calcdist: public digital_constellation +{ + public: + digital_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + unsigned int decision_maker (const gr_complex *sample); +}; + +class digital_constellation_sector: public digital_constellation +{ +}; + +class digital_constellation_rect; +typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect_sptr; +%template(digital_constellation_rect_sptr) boost::shared_ptr<digital_constellation_rect>; +%rename(constellation_rect) digital_make_constellation_rect; +digital_constellation_rect_sptr digital_make_constellation_rect(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); +%ignore digital_constellation_rect; + +class digital_constellation_rect : public digital_constellation_sector +{ +public: + digital_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); +}; + +class digital_constellation_psk; +typedef boost::shared_ptr<digital_constellation_psk> digital_constellation_psk_sptr; +%template(digital_constellation_psk_sptr) boost::shared_ptr<digital_constellation_psk>; +%rename(constellation_psk) digital_make_constellation_psk; +digital_constellation_psk_sptr digital_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +%ignore digital_constellation_psk; + +class digital_constellation_psk : public digital_constellation_sector +{ +public: + digital_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +}; + +/* + BPSK Constellation +*/ + +class digital_constellation_bpsk; +typedef boost::shared_ptr<digital_constellation_bpsk> digital_constellation_bpsk_sptr; +%template(digital_constellation_bpsk_sptr) boost::shared_ptr<digital_constellation_bpsk>; +%rename(constellation_bpsk) digital_make_constellation_bpsk; +digital_constellation_bpsk_sptr digital_make_constellation_bpsk(); +%ignore digital_constellation_bpsk; + +class digital_constellation_bpsk : public digital_constellation +{ +public: + digital_constellation_bpsk (); +}; + +/* + QPSK Constellation +*/ + +class digital_constellation_qpsk; +typedef boost::shared_ptr<digital_constellation_qpsk> digital_constellation_qpsk_sptr; +%template(digital_constellation_qpsk_sptr) boost::shared_ptr<digital_constellation_qpsk>; +%rename(constellation_qpsk) digital_make_constellation_qpsk; +digital_constellation_qpsk_sptr digital_make_constellation_qpsk(); +%ignore digital_constellation_qpsk; + +class digital_constellation_qpsk : public digital_constellation +{ +public: + digital_constellation_qpsk (); +}; + +/* + DQPSK Constellation +*/ + +class digital_constellation_dqpsk; +typedef boost::shared_ptr<digital_constellation_dqpsk> digital_constellation_dqpsk_sptr; +%template(digital_constellation_dqpsk_sptr) boost::shared_ptr<digital_constellation_dqpsk>; +%rename(constellation_dqpsk) digital_make_constellation_dqpsk; +digital_constellation_dqpsk_sptr digital_make_constellation_dqpsk(); +%ignore digital_constellation_dqpsk; + +class digital_constellation_dqpsk : public digital_constellation +{ +public: + digital_constellation_dqpsk (); +}; + + +/* + 8PSK Constellation +*/ + +class digital_constellation_8psk; +typedef boost::shared_ptr<digital_constellation_8psk> digital_constellation_8psk_sptr; +%template(digital_constellation_8psk_sptr) boost::shared_ptr<digital_constellation_8psk>; +%rename(constellation_8psk) digital_make_constellation_8psk; +digital_constellation_8psk_sptr digital_make_constellation_8psk(); +%ignore digital_constellation_8psk; + +class digital_constellation_8psk : public digital_constellation +{ +public: + digital_constellation_8psk (); +}; diff --git a/gr-digital/swig/digital_constellation_decoder_cb.i b/gr-digital/swig/digital_constellation_decoder_cb.i new file mode 100644 index 000000000..53d3fe8e0 --- /dev/null +++ b/gr-digital/swig/digital_constellation_decoder_cb.i @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,constellation_decoder_cb) + +digital_constellation_decoder_cb_sptr +digital_make_constellation_decoder_cb (digital_constellation_sptr constellation); + +class digital_constellation_decoder_cb : public gr_sync_block +{ + private: + digital_constellation_decoder_cb (digital_constellation_sptr constellation); + + friend digital_constellation_decoder_cb_sptr + gr_make_constellation_decoder_cb (digital_constellation_sptr constellation); + + public: + ~digital_constellation_decoder_cb(); +}; diff --git a/gr-digital/swig/digital_constellation_receiver_cb.i b/gr-digital/swig/digital_constellation_receiver_cb.i new file mode 100644 index 000000000..9c4ba645e --- /dev/null +++ b/gr-digital/swig/digital_constellation_receiver_cb.i @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,constellation_receiver_cb); + +%include "digital_constellation.i" + +digital_constellation_receiver_cb_sptr +digital_make_constellation_receiver_cb (digital_constellation_sptr constellation, + float loop_bw, float fmin, float fmax); + +class digital_constellation_receiver_cb : public gr_block, public gri_control_loop +{ + private: + digital_constellation_receiver_cb (digital_contellation_sptr constellation, + float loop_bw, float fmin, float fmax); +}; diff --git a/gr-digital/swig/digital_correlate_access_code_bb.i b/gr-digital/swig/digital_correlate_access_code_bb.i new file mode 100644 index 000000000..01087b8e9 --- /dev/null +++ b/gr-digital/swig/digital_correlate_access_code_bb.i @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,correlate_access_code_bb); + +/*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + * \param threshold maximum number of bits that may be wrong + */ +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold) + throw(std::out_of_range); + +/*! + * \brief Examine input for specified access code, one bit at a time. + * \ingroup block + * + * input: stream of bits, 1 bit per input byte (data in LSB) + * output: stream of bits, 2 bits per output byte (data in LSB, flag in next higher bit) + * + * Each output byte contains two valid bits, the data bit, and the + * flag bit. The LSB (bit 0) is the data bit, and is the original + * input data, delayed 64 bits. Bit 1 is the + * flag bit and is 1 if the corresponding data bit is the first data + * bit following the access code. Otherwise the flag bit is 0. + */ +class digital_correlate_access_code_bb : public gr_sync_block +{ + friend digital_correlate_access_code_bb_sptr + digital_make_correlate_access_code_bb (const std::string &access_code, int threshold); + protected: + digital_correlate_access_code_bb(const std::string &access_code, int threshold); + + public: + ~digital_correlate_access_code_bb(); + + /*! + * \param access_code is represented with 1 byte per bit, e.g., "010101010111000100" + */ + bool set_access_code (const std::string &access_code); +}; diff --git a/gr-digital/swig/digital_costas_loop_cc.i b/gr-digital/swig/digital_costas_loop_cc.i new file mode 100644 index 000000000..ab09200a0 --- /dev/null +++ b/gr-digital/swig/digital_costas_loop_cc.i @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,costas_loop_cc); + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float loop_bw, int order + ) throw (std::invalid_argument); + +class digital_costas_loop_cc : public gr_sync_block, public gri_control_loop +{ + private: + digital_costas_loop_cc (float loop_bw, int order); +}; diff --git a/gr-digital/swig/digital_cpmmod_bc.i b/gr-digital/swig/digital_cpmmod_bc.i new file mode 100644 index 000000000..fa7c50da7 --- /dev/null +++ b/gr-digital/swig/digital_cpmmod_bc.i @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital, cpmmod_bc) + +digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta=0.3); + +class digital_cpmmod_bc : public gr_hier_block2 +{ + private: + digital_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + + public: + std::vector<float> get_taps(); +}; + diff --git a/gr-digital/swig/digital_crc32.i b/gr-digital/swig/digital_crc32.i new file mode 100644 index 000000000..806bfad6a --- /dev/null +++ b/gr-digital/swig/digital_crc32.i @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%rename(update_crc32) digital_update_crc32; +%rename(crc32) digital_crc32; + +unsigned int digital_update_crc32(unsigned int crc, const std::string buf); +unsigned int digital_crc32(const std::string buf); diff --git a/gr-digital/swig/digital_fll_band_edge_cc.i b/gr-digital/swig/digital_fll_band_edge_cc.i new file mode 100644 index 000000000..3efcb89ed --- /dev/null +++ b/gr-digital/swig/digital_fll_band_edge_cc.i @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,fll_band_edge_cc); + +digital_fll_band_edge_cc_sptr digital_make_fll_band_edge_cc (float samps_per_sym, + float rolloff, + int filter_size, + float bandwidth); + +class digital_fll_band_edge_cc : public gr_sync_block, public gri_control_loop +{ + private: + digital_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth); + + public: + ~digital_fll_band_edge_cc (); + + void set_loop_bandwidth(float bw); + void set_damping_factor(float df); + void set_alpha(float alpha); + void set_beta(float beta); + void set_samples_per_symbol(float sps); + void set_rolloff(float rolloff); + void set_filter_size(int filter_size); + void set_frequency(float freq); + void set_phase(float phase); + + float get_loop_bandwidth() const; + float get_damping_factor() const; + float get_alpha() const; + float get_beta() const; + float get_samples_per_symbol() const; + float get_rolloff() const; + int get_filter_size() const; + float get_frequency() const; + float get_phase() const; + + void print_taps(); +}; diff --git a/gr-digital/swig/digital_gmskmod_bc.i b/gr-digital/swig/digital_gmskmod_bc.i new file mode 100644 index 000000000..ad7b82237 --- /dev/null +++ b/gr-digital/swig/digital_gmskmod_bc.i @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital, gmskmod_bc) + +digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym=2, + double bt=0.3, unsigned L=4); + +class digital_gmskmod_bc : public gr_hier_block2 +{ + private: + digital_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + double beta, unsigned L); + + public: + std::vector<float> get_taps(); +}; + diff --git a/gr-digital/swig/digital_kurtotic_equalizer_cc.i b/gr-digital/swig/digital_kurtotic_equalizer_cc.i new file mode 100644 index 000000000..67a9dc6fd --- /dev/null +++ b/gr-digital/swig/digital_kurtotic_equalizer_cc.i @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,kurtotic_equalizer_cc) + +// retrieve info on the base class, without generating wrappers since +// the base class has a pure virual method. +%import "gr_adaptive_fir_ccc.i" + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, + float mu); + +class digital_kurtotic_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + digital_kurtotic_equalizer_cc(int num_taps, float mu); + +public: + void set_gain(float mu); +}; diff --git a/gr-digital/swig/digital_lms_dd_equalizer_cc.i b/gr-digital/swig/digital_lms_dd_equalizer_cc.i new file mode 100644 index 000000000..bd5c6ae29 --- /dev/null +++ b/gr-digital/swig/digital_lms_dd_equalizer_cc.i @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +GR_SWIG_BLOCK_MAGIC(digital,lms_dd_equalizer_cc) + +// retrieve info on the base class, without generating wrappers since +// the base class has a pure virual method. +%import "gr_adaptive_fir_ccc.i" + + +digital_lms_dd_equalizer_cc_sptr +digital_make_lms_dd_equalizer_cc (int num_taps, + float mu, int sps, + digital_constellation_sptr cnst); + +class digital_lms_dd_equalizer_cc : public gr_sync_block +{ +private: + digital_lms_dd_equalizer_cc (int num_taps, + float mu, int sps, + digital_constellation_sptr cnst); + +public: + float get_gain(); + void set_gain(float mu); +}; diff --git a/gr-digital/swig/digital_mpsk_receiver_cc.i b/gr-digital/swig/digital_mpsk_receiver_cc.i new file mode 100644 index 000000000..b51411f6f --- /dev/null +++ b/gr-digital/swig/digital_mpsk_receiver_cc.i @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,mpsk_receiver_cc); + +digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, + float omega_rel); +class digital_mpsk_receiver_cc : public gr_block, public gri_control_loop +{ + private: + digital_mpsk_receiver_cc (unsigned int M,float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +public: + float mu() const { return d_mu;} + float omega() const { return d_omega;} + float gain_mu() const { return d_gain_mu;} + float gain_omega() const { return d_gain_omega;} + void set_mu (float mu) { d_mu = mu; } + void set_omega (float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_rel); + d_max_omega = omega*(1.0 + d_omega_rel); + } + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } +}; diff --git a/gr-digital/swig/digital_ofdm_cyclic_prefixer.i b/gr-digital/swig/digital_ofdm_cyclic_prefixer.i new file mode 100644 index 000000000..56d1629a8 --- /dev/null +++ b/gr-digital/swig/digital_ofdm_cyclic_prefixer.i @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_cyclic_prefixer) + +digital_ofdm_cyclic_prefixer_sptr +digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size); + +class digital_ofdm_cyclic_prefixer : public gr_sync_interpolator +{ + protected: + digital_ofdm_cyclic_prefixer (size_t input_size, size_t output_size); + + public: +}; diff --git a/gr-digital/swig/digital_ofdm_frame_acquisition.i b/gr-digital/swig/digital_ofdm_frame_acquisition.i new file mode 100644 index 000000000..b61297bde --- /dev/null +++ b/gr-digital/swig/digital_ofdm_frame_acquisition.i @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <vector> + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_frame_acquisition); + +digital_ofdm_frame_acquisition_sptr +digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len=4); + +class digital_ofdm_frame_acquisition : public gr_sync_decimator +{ + protected: + digital_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); + + public: + float snr() { return d_snr_est; } + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; diff --git a/gr-digital/swig/digital_ofdm_frame_sink.i b/gr-digital/swig/digital_ofdm_frame_sink.i new file mode 100644 index 000000000..cd3fa1422 --- /dev/null +++ b/gr-digital/swig/digital_ofdm_frame_sink.i @@ -0,0 +1,41 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_frame_sink); + +digital_ofdm_frame_sink_sptr +digital_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain=0.25, float freq_gain=0.25*0.25/4); + +class digital_ofdm_frame_sink : public gr_sync_block +{ + protected: + digital_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, + const std::vector<unsigned char> &sym_value_out, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); + + public: + ~digital_ofdm_frame_sink(); +}; diff --git a/gr-digital/swig/digital_ofdm_insert_preamble.i b/gr-digital/swig/digital_ofdm_insert_preamble.i new file mode 100644 index 000000000..5f7b16369 --- /dev/null +++ b/gr-digital/swig/digital_ofdm_insert_preamble.i @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_insert_preamble); + +digital_ofdm_insert_preamble_sptr +digital_make_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble); + + +class digital_ofdm_insert_preamble : public gr_block +{ + protected: + digital_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble); +}; diff --git a/gr-digital/swig/digital_ofdm_mapper_bcv.i b/gr-digital/swig/digital_ofdm_mapper_bcv.i new file mode 100644 index 000000000..4e9aaba7d --- /dev/null +++ b/gr-digital/swig/digital_ofdm_mapper_bcv.i @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_mapper_bcv); + +digital_ofdm_mapper_bcv_sptr +digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, + unsigned int msgq_limit, + unsigned int bits_per_symbol, + unsigned int fft_length) throw(std::exception); + + +class digital_ofdm_mapper_bcv : public gr_sync_block +{ + protected: + digital_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, + unsigned int msgq_limit, + unsigned int bits_per_symbol, + unsigned int fft_length); + + public: + gr_msg_queue_sptr msgq(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; diff --git a/gr-digital/swig/digital_ofdm_sampler.i b/gr-digital/swig/digital_ofdm_sampler.i new file mode 100644 index 000000000..91056c320 --- /dev/null +++ b/gr-digital/swig/digital_ofdm_sampler.i @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital,ofdm_sampler) + + digital_ofdm_sampler_sptr digital_make_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout=1000); + +class digital_ofdm_sampler : public gr_sync_block +{ + private: + digital_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout); +}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i new file mode 100644 index 000000000..3e3a63b61 --- /dev/null +++ b/gr-digital/swig/digital_swig.i @@ -0,0 +1,82 @@ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%include "gnuradio.i" + +%include <gri_control_loop.i> + +%{ +#include "digital_binary_slicer_fb.h" +#include "digital_clock_recovery_mm_cc.h" +#include "digital_clock_recovery_mm_ff.h" +#include "digital_cma_equalizer_cc.h" +#include "digital_constellation.h" +#include "digital_constellation_decoder_cb.h" +#include "digital_constellation_receiver_cb.h" +#include "digital_correlate_access_code_bb.h" +#include "digital_costas_loop_cc.h" +#include "digital_crc32.h" +#include "digital_fll_band_edge_cc.h" +#include "digital_kurtotic_equalizer_cc.h" +#include "digital_lms_dd_equalizer_cc.h" +#include "digital_mpsk_receiver_cc.h" +#include "digital_ofdm_cyclic_prefixer.h" +#include "digital_ofdm_frame_acquisition.h" +#include "digital_ofdm_frame_sink.h" +#include "digital_ofdm_insert_preamble.h" +#include "digital_ofdm_mapper_bcv.h" +#include "digital_ofdm_sampler.h" +#include "digital_cpmmod_bc.h" +#include "digital_gmskmod_bc.h" +%} + +%include "digital_binary_slicer_fb.i" +%include "digital_clock_recovery_mm_cc.i" +%include "digital_clock_recovery_mm_ff.i" +%include "digital_cma_equalizer_cc.i" +%include "digital_constellation.i" +%include "digital_constellation_decoder_cb.i" +%include "digital_constellation_receiver_cb.i" +%include "digital_correlate_access_code_bb.i" +%include "digital_costas_loop_cc.i" +%include "digital_crc32.i" +%include "digital_fll_band_edge_cc.i" +%include "digital_kurtotic_equalizer_cc.i" +%include "digital_lms_dd_equalizer_cc.i" +%include "digital_mpsk_receiver_cc.i" +%include "digital_ofdm_cyclic_prefixer.i" +%include "digital_ofdm_frame_acquisition.i" +%include "digital_ofdm_frame_sink.i" +%include "digital_ofdm_insert_preamble.i" +%include "digital_ofdm_mapper_bcv.i" +%include "digital_ofdm_sampler.i" +%include "digital_cpmmod_bc.i" +%include "digital_gmskmod_bc.i" + +#if SWIGGUILE +%scheme %{ +(load-extension-global "libguile-gnuradio-digital_swig" "scm_init_gnuradio_digital_swig_module") +%} + +%goops %{ +(use-modules (gnuradio gnuradio_core_runtime)) +%} +#endif diff --git a/gr-digital/swig/gnuradio/.gitignore b/gr-digital/swig/gnuradio/.gitignore new file mode 100644 index 000000000..c264c571a --- /dev/null +++ b/gr-digital/swig/gnuradio/.gitignore @@ -0,0 +1,2 @@ +digital_swig-primitive.scm +digital_swig.scm diff --git a/gr-digital/swig/gnuradio/digital.scm b/gr-digital/swig/gnuradio/digital.scm new file mode 100644 index 000000000..834bc8d6d --- /dev/null +++ b/gr-digital/swig/gnuradio/digital.scm @@ -0,0 +1,28 @@ +;;; +;;; Copyright 2011 Free Software Foundation, Inc. +;;; +;;; This file is part of GNU Radio +;;; +;;; GNU Radio is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3, or (at your option) +;;; any later version. +;;; +;;; GNU Radio is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program. If not, see <http://www.gnu.org/licenses/>. +;;; + +;;; Module that just re-exports the digital_swig module + +(define-module (gnuradio digital) + #:use-module (gnuradio export-safely) + #:use-module (gnuradio digital_swig) + #:duplicates (merge-generics replace check)) + +(re-export-all '(gnuradio digital_swig)) + diff --git a/gr-digital/swig/run_guile_tests.in b/gr-digital/swig/run_guile_tests.in new file mode 100644 index 000000000..5d08b0dd5 --- /dev/null +++ b/gr-digital/swig/run_guile_tests.in @@ -0,0 +1,14 @@ +#!/bin/sh + +. @top_builddir@/setup_guile_test_env + +# 1st argument is absolute path to hand coded guile source directory +# 2nd argument is absolute path to component C++ shared library build directory +# 3nd argument is absolute path to component SWIG build directory + +add_local_paths \ + @srcdir@ \ + @abs_builddir@ \ + @abs_builddir@ + +@GUILE@ -e main -c '(use-modules (gnuradio test-suite guile-test))' -t @srcdir@ |