#!/usr/bin/env python

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
from transmit_path import transmit_path
from uhd_interface import uhd_transmitter

import struct,time
import sys



class my_top_block1(gr.top_block):
    def __init__(self, demodulator, rx_callback, options):
        gr.top_block.__init__(self)

        if(options.rx_freq is not None):
            # Work-around to get the modulation's bits_per_symbol
            args = demodulator.extract_kwargs_from_options(options)
            symbol_rate = options.bitrate / demodulator(**args).bits_per_symbol()

            self.source = uhd_receiver(options.args, symbol_rate,
                                       options.samples_per_symbol,
                                       options.rx_freq, options.rx_gain,
                                       options.spec, 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.rxdata = gr.file_sink(gr.sizeof_gr_complex, "replayed_rx_data.dat")
         

        self.connect(self.source, self.rxpath)
        self.connect(self.source, self.rxdata)
	'''def rx_cfile(self):
		self.rxdata = gr.file_sink(gr.sizeof_short*2, "node2_rx_data.dat")
		self._head = gr.head(gr.sizeof_gr_complex, int(1008))
		self.connect(self.source, self._head, self._sink)
		'''
		
class my_top_block(gr.top_block):
    def __init__(self, modulator, options):
        gr.top_block.__init__(self)

        if(options.tx_freq is not None):
            # Work-around to get the modulation's bits_per_symbol
            args = modulator.extract_kwargs_from_options(options)
            symbol_rate = options.bitrate / modulator(**args).bits_per_symbol()

            self.sink = uhd_transmitter(options.args, symbol_rate,
                                        options.samples_per_symbol,
                                        options.tx_freq, options.tx_gain,
                                        options.spec, 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.tx_data = gr.file_sink(gr.sizeof_gr_complex, "node1_tx_data.dat")

        self.connect(self.txpath, self.sink)
        self.connect(self.txpath,self.tx_data)
        



class stats(object):
    def __init__(self):
        self.npkts = 0
        self.nright = 0
        
def main():
    st = stats()
    
    def send_pkt(timeref,payload='', eof=False):
        return tb_tx.txpath.send_pkt(timeref,payload, eof)

    def rx_callback(ok, payload):
        st.npkts += 1
        
        print "Replay packet received..."
        #tb.rx_cfile()
        #rxdataflow.stop()
        if ok:
            st.nright += 1
        if len(payload) <= 16:
            print "ok = %5r  payload = '%s'  %d/%d" % (ok, payload, st.nright, st.npkts)
        else:
            (pktno,) = struct.unpack('!H', payload[0:2])
            #print "time rcv:%f" % (secs)
            print "ok = %5r  pktno = %4d  len(payload) = %4d  %d/%d" % (ok, pktno, len(payload),
                                                                        st.nright, st.npkts)
        
        #time.sleep(2)
        tb.stop()                                                                


    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='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)
    receive_path.add_options(parser, expert_grp)
    uhd_receiver.add_options(parser)    

    for mod in mods.values():
        mod.add_options(expert_grp)
    for mod in demods.values():
        mod.add_options(expert_grp)

    (options, args) = parser.parse_args ()
    if len(args) != 0:
        parser.print_help()
        sys.exit(1)

    pkt_size = int(options.size)

	#tb = my_top_block(mods[options.modulation], options)
    tb = my_top_block1(demods[options.modulation], rx_callback, options)
    tb_tx = 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_tx.start()
   
    
    nbytes = int(1e6 * options.megabytes)
    n = 0
    l=0
    pktno = 0
    
    while l<1:
		data = (pkt_size-2) * chr(pktno & 0xff)
		
		payload = struct.pack('!H', pktno & 0xffff) + data
		send_pkt(tb_tx.sink,payload)
		sys.stderr.write('.')
		l=l+1
		#time.sleep(2)
		pktno = pktno +1
    #send_pkt(payload)
    #send_pkt(payload)
    send_pkt(tb_tx.sink,eof=True)
    print "Packet sent waiting for replay packet..."
    

    tb_tx.wait()
    #rxdataflow.start()
    
    
    tb_tx.stop()
    #time.sleep(1)
    tb.start()
    tb.wait()
    
    #rxdataflow.wait()


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass
