#!   /usr/bin/env   python
#    coding: utf8

# Copyright CERN, 2011
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org

import sys
import rr
import time
import os
from numpy import *
from pylab import *

from ptsexcept import *

import gn4124
import fmc_adc
import spec_fmc_adc
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *


"""
test14: pre-trigger ansd post-trigger counter test

Note: Requires test00.py to run first to load the firmware!
"""

GN4124_CSR = 0x0

USB_DEVICE = "/dev/ttyUSB0"
RS232_BAUD = 57600
NB_CHANNELS = 4

AWG_SET_SLEEP = 0.3
SSR_SET_SLEEP = 0.05

ACQ_TIMEOUT = 10

MAX_FIRMWARE_RELOAD = 10

PRE_TRIG_SAMPLES = 50
POST_TRIG_SAMPLES = 50
NB_SHOTS = 3

DMA_LENGTH = 4096 # DMA length in bytes


def load_firmware(default_directory):
    print('Load firmware to FPGA')
    path_fpga_loader = '../../../gnurabbit/user/fpga_loader';
    path_firmware = '../firmwares/spec_fmcadc100m14b4cha.bin';

    firmware_loader = os.path.join(default_directory, path_fpga_loader)
    bitstream = os.path.join(default_directory, path_firmware)
    print firmware_loader + ' ' + bitstream
    os.system( firmware_loader + ' ' + bitstream )

    time.sleep(2);


def open_all_channels(fmc):
    for i in range(1,NB_CHANNELS+1):
        fmc.set_input_range(i, 'OPEN')
        time.sleep(SSR_SET_SLEEP)


def fmc_adc_init(spec, fmc):
    print('Initialise FMC board.')
    fmc.__init__(spec)
    # Reset offset DACs
    fmc.dc_offset_reset()
    # Make sure all switches are OFF
    open_all_channels(fmc)
    # Set trigger
    fmc.set_soft_trig()
    # Set acquisition
    fmc.set_pre_trig_samples(PRE_TRIG_SAMPLES)
    fmc.set_post_trig_samples(POST_TRIG_SAMPLES)
    fmc.set_shots(NB_SHOTS)

def acquisition(gnum, pages, fmc, channels_data, check_same, spec_fmc):
    print('Make an acquisition')
    # Make sure acq FSM is IDLE
    fmc.stop_acq()
    #print('Acquisition FSM state : %s') % fmc.get_acq_fsm_state()
    # Start acquisition
    fmc.start_acq()
    # Wait end of acquisition
    for i in range(NB_SHOTS):
        time.sleep(0.1)
        fmc.sw_trig()
        print('Software trigger nb:%d')%i
    timeout = 0
    while('IDLE' != fmc.get_acq_fsm_state()):
        #print fmc.get_acq_fsm_state()
        time.sleep(1)
        timeout += 1
        print('Wait for trigger...')
        #if(ACQ_TIMEOUT < timeout):
        #    print('Acquisition timeout. Check that the AWG is switched ON and properly connected.')
        #    return 1
    # Retrieve data trough DMA
    page1_data_before_dma = gnum.get_memory_page(1)
    gnum.add_dma_item(0x0, pages[1], DMA_LENGTH, 0, 0)
    gnum.start_dma()
    gnum.wait_irq()
    irq_src = spec_fmc.get_irq_source()
    print('IRQ source : %.4X')%irq_src
    mult_irq = spec_fmc.get_irq_mult()
    print('Multiple IRQ status: %.4X')%mult_irq
    if(0 != mult_irq):
        spec_fmc.clear_irq_mult(mult_irq)
        print('Multiple IRQ status: %.4X')%spec_fmc.get_irq_mult()
    if(0 != irq_src):
        spec_fmc.clear_irq_source(irq_src)
        print('IRQ source : %.4X')%spec_fmc.get_irq_source()
    page1_data = gnum.get_memory_page(1)
    page_zeros = [0] * len(page1_data)
    if((check_same == True) and ((page1_data_before_dma == page1_data) or (page_zeros == page1_data))):
        print('Previous page:')
        print page1_data_before_dma[0:20]
        print('Current page:')
        print page1_data[0:20]
        print('### Acquisition or DMA error. ###')
        return 1
    for i in range(len(page1_data)):
        channels_data.append(page1_data[i] & 0xFFFF)
        channels_data.append(page1_data[i]>>16)
    return 0


def main (default_directory='.'):

    load_firmware = raw_input('Do you want to load the firmware? [y,n]')
    if(load_firmware == 'y'):
        # Load firmware to FPGA
        path_fpga_loader = '../../../gnurabbit/user/fpga_loader';
        if('y' == raw_input('Test firmware? [y,n]')):
            path_firmware = '../firmwares/spec_fmcadc100m14b4cha_test.bin';
        else:
            path_firmware = '../firmwares/spec_fmcadc100m14b4cha.bin';

        firmware_loader = os.path.join(default_directory, path_fpga_loader)
        bitstream = os.path.join(default_directory, path_firmware)
        print firmware_loader + ' ' + bitstream
        os.system( firmware_loader + ' ' + bitstream )

        time.sleep(2);

    #raw_input('Press a key to continue...')

    # Objects declaration
    spec = rr.Gennum() # bind to the SPEC board
    gnum = gn4124.CGN4124(spec, GN4124_CSR)
    fmc = fmc_adc.CFmcAdc100Ms(spec)
    gen = Agilent33250A(device=USB_DEVICE, bauds=RS232_BAUD)
    sine = SineWaveform()
    spec_fmc = spec_fmc_adc.CSpecFmcAdc100Ms(spec)

    # Initialise fmc adc
    fmc_adc_init(spec, fmc)

    # Enables DMA interrupts
    print('Set IRQ enable mask: %.4X')%spec_fmc.set_irq_en_mask(0x3)

    # Set UTC
    current_time = time.time()
    utc_seconds = int(current_time)
    fmc.set_utc_second_cnt(utc_seconds)
    print('UTC core seconds counter: %d')%fmc.get_utc_second_cnt()

    utc_coarse = int((current_time - utc_seconds)/8E-9)
    fmc.set_utc_coarse_cnt(utc_coarse)
    print('UTC core coarse counter: %d')%fmc.get_utc_coarse_cnt()

    # Set sine params
    sine.frequency = 1E6
    sine.amplitude = 0.25
    sine.dc = 0
    print('Sine frequency:%3.3fMHz amplitude:%2.3fVp offset:%2.3fV')%(sine.frequency/1E6, sine.amplitude, sine.dc)

    # Set AWG
    gen.connect()
    gen.play(sine)
    gen.output = True
    time.sleep(AWG_SET_SLEEP)

    # Connects channel 4 to AWG
    fmc.set_input_range(4, '1V')
    time.sleep(SSR_SET_SLEEP)

    # Use test data instead of data from ADC
    #fmc.test_data_en()

    # Use data pattern instead of ADC data
    #fmc.testpat_en(0x2000)

    # Print configuration
    fmc.print_adc_core_config()

    # Print ADC config
    fmc.print_adc_config()

    # Get physical addresses of the pages for DMA transfer
    pages = gnum.get_physical_addr()

    # Perform an acquisition
    channels_data = []
    error = acquisition(gnum, pages, fmc, channels_data, False, spec_fmc)

    # Print time-tags
    print('UTC time-tags:')
    trig_tag = fmc.get_utc_trig_tag()
    start_tag = fmc.get_utc_start_tag()
    stop_tag = fmc.get_utc_stop_tag()
    end_tag = fmc.get_utc_end_tag()
    print trig_tag
    print start_tag
    print stop_tag
    print end_tag
    print('Trigger time-tag   : %10.10f [s]')%(trig_tag[2]+(trig_tag[3]*8E-9))
    print('Acq start time-tag : %10.10f [s]')%(start_tag[2]+(start_tag[3]*8E-9))
    print('Acq stop time-tag  : %10.10f [s]')%(stop_tag[2]+(stop_tag[3]*8E-9))
    print('Acq end time-tag   : %10.10f [s]')%(end_tag[2]+(end_tag[3]*8E-9))

    # print aqcuisition to file
    # open test09 log file in read mode
    file_name = raw_input('Enter a file name (default=log_test12.txt):')
    if file_name == "":
        file_name = "log_test12.txt"
    file = open(file_name, 'w')
    file.write("CH1 value, CH2 value, CH3 value, CH4 value\n")
    for i in range(0,len(channels_data),4):
        file.write("%d, %d, %d, %d\n"%(channels_data[i], channels_data[i+1], channels_data[i+2], channels_data[i+3]))

    # Plot the acquisition
    sample = arange(len(channels_data)/4)
    plot(sample, channels_data[0::4], 'b', label='Channel 1')
    plot(sample, channels_data[1::4], 'g', label='Channel 2')
    plot(sample, channels_data[2::4], 'r', label='Channel 3')
    plot(sample, channels_data[3::4], 'c', label='Channel 4')
    #ylim(-1, 20)
    legend()
    show()

    # Make sure all switches are OFF
    open_all_channels(fmc)

    # Switch AWG OFF
    gen.output = False
    gen.close()

    # Check if an error occured during frequency response test
    if(error != 0):
        raise PtsError('An error occured, check log for details.')


if __name__ == '__main__' :
    main()
