#!/usr/bin/env python
#
# Copyright 2004,2005,2006 Free Software Foundation, Inc.
# 
# This file is part of GNU Radio
# 
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# 
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
# 

from gnuradio import gr
from optparse import OptionParser
from ofdm_receiver import ofdm_receiver

class ofdm_mod(gr.hier_block):
    def __init__(self, fg, options):
        self.fg = fg
        self._occupied_tones = options.occupied_tones
        self._fft_length = options.fft_length
        self._cp_length = options.cp_length
        self._mtu = options.mtu

        symbol_length = self._fft_length + self._cp_length

        if self._fft_length < self._occupied_tones:
            sys.stderr.write("occupied tones must be less than FFT length\n")
            raise SystemExit
        if self._fft_length < self._cp_length:
            sys.stderr.write("cyclic prefix length  must be less than FFT length\n")
            raise SystemExit

        win = [] #[1 for i in range(self._fft_length)]

        # hard-coded known symbol
        #ks = self._occupied_tones*[1,]
        ks1 = known_symbols_200_1
        ks2 = known_symbols_200_2
        
        self.ofdm = gr.ofdm_bpsk_mapper(self._mtu, self._occupied_tones, self._fft_length, ks1, ks2)
        self.ifft = gr.fft_vcc(self._fft_length, False, win, True)
        self.cp_adder = gr.ofdm_cyclic_prefixer(self._fft_length, symbol_length)
        
        if options.verbose:
            self._print_verbage()

        self.fg.connect(self.ofdm, self.ifft, self.cp_adder)
        gr.hier_block.__init__(self, self.fg, self.ofdm, self.cp_adder)

    def samples_per_symbol(self):
        return 2

    def mtu(self):
        return self._mtu

    def bits_per_symbol(self=None):   # staticmethod that's also callable on an instance
        return 1
    bits_per_symbol = staticmethod(bits_per_symbol)      # make it a static method.  RTFM

    def add_options(normal, expert):
        """
        Adds OFDM-specific options to the Options Parser
        """
        expert.add_option("", "--mtu", type="int", default=1500,
                          help="set maximum transmit unit [default=%default]")
        expert.add_option("", "--fft-length", type="intx", default=512,
                          help="set the number of FFT bins [default=%default]")
        expert.add_option("", "--occupied-tones", type="intx", default=200,
                          help="set the number of occupied FFT bins [default=%default]")
        expert.add_option("", "--cp-length", type="intx", default=128,
                          help="set the number of bits in the cyclic prefix [default=%default]")
    # Make a static method to call before instantiation
    add_options = staticmethod(add_options)

    def _print_verbage(self):
        """
        Prints information about the OFDM modulator
        """
        print "\nOFDM Modulator:"
        print "FFT length:      %3d"   % (self._fft_length)
        print "Occupied Tones:  %3d"   % (self._occupied_tones)
        print "CP length:       %3d"   % (self._cp_length)


class ofdm_demod(gr.hier_block):
    def __init__(self, fg, options):
        self.fg = fg
        self._occupied_tones = options.occupied_tones
        self._fft_length = options.fft_length
        self._cp_length = options.cp_length
        self._snr = options.snr

        symbol_length = self._fft_length + self._cp_length

        win = [1 for i in range(self._fft_length)]

        # hard-coded known symbol
        ks1 = known_symbols_200_1
        ks2 = known_symbols_200_2

        # ML Sync
        self.ofdm_sync = ofdm_receiver(self.fg, self._fft_length, symbol_length, self._snr)

        # OFDM Demod
        self.fft_demod = gr.fft_vcc(self._fft_length, True, win, True)
        self.ofdm_corr  = gr.ofdm_correlator(self._occupied_tones, self._fft_length,
                                             self._cp_length, ks1, ks2)
        self.ofdm_demod = gr.ofdm_bpsk_demapper(self._occupied_tones)


        if options.verbose:
            self._print_verbage()

        self.fg.connect(self.ofdm_sync, self.fft_demod, self.ofdm_corr, self.ofdm_demod)
        gr.hier_block.__init__(self, self.fg, self.ofdm_sync, self.ofdm_demod)
        
    def bits_per_symbol(self=None):   # staticmethod that's also callable on an instance
        return 1
    bits_per_symbol = staticmethod(bits_per_symbol)      # make it a static method.  RTFM

    def add_options(normal, expert):
        """
        Adds OFDM-specific options to the Options Parser
        """
        expert.add_option("", "--fft-length", type="intx", default=512,
                          help="set the number of FFT bins [default=%default]")
        expert.add_option("", "--occupied-tones", type="intx", default=200,
                          help="set the number of occupied FFT bins [default=%default]")
        expert.add_option("", "--cp-length", type="intx", default=128,
                          help="set the number of bits in the cyclic prefix [default=%default]")
    # Make a static method to call before instantiation
    add_options = staticmethod(add_options)

    def _print_verbage(self):
        """
        Prints information about the OFDM demodulator
        """
        print "\nOFDM Demodulator:"
        print "FFT length:      %3d"   % (self._fft_length)
        print "Occupied Tones:  %3d"   % (self._occupied_tones)
        print "CP length:       %3d"   % (self._cp_length)


# generated in python using:
# import random
# pn = [2.0*random.randint(0,1)-1.0 for i in range(self._occupied_tones)]

known_symbols_200_1 = [1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0]
        
known_symbols_200_2 = [-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0]