#!   /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 spec_fmc_adc
import fmc_adc
import calibr_box
import find_usb_tty
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *


"""
test12: Takes an aqcuisition of all channels and print it to a file and on the screen
        Set UTC and read UTC time-tags

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

# Calibration box vendor and product IDs
BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
BOX_USB_PRODUCT_ID = 0xea60 # CP210x Composite Device
# Agilent AWG serial access vendor and product IDs
AWG_USB_VENDOR_ID = 0x0403 # Future Technology Devices International, Ltd
AWG_USB_PRODUCT_ID = 0x6001 # FT232 USB-Serial (UART) IC

AWG_BAUD = 57600

NB_CHANNELS = 4

AWG_SET_SLEEP = 1
SSR_SET_SLEEP = 0.05
BOX_SET_SLEEP = 1

ACQ_TIMEOUT = 10

PRE_TRIG_SAMPLES = 10
POST_TRIG_SAMPLES = 10000
NB_SHOTS = 1

ACQ_LENGTH = 10000 # in samples


def load_firmware(default_directory):
    print('Load firmware to FPGA')
    path_fpga_loader = '../../../gnurabbit/user/fpga_loader';
    path_firmware = '../firmwares/spec_fmcadc100m14b4cha_test.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.')
    # Reset offset DACs
    fmc.dc_offset_reset()
    # Make sure all switches are OFF
    open_all_channels(fmc)
    # Set software 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)

# Converts two's complement hex to signed
def hex2signed(value):
    if(value & 0x8000):
        return -((~value & 0xFFFF) + 1)
    else:
        return value

# Converts digital value to volts
def digital2volt(value, full_scale, nb_bit):
    return float(value) * float(full_scale)/2**nb_bit

def acq_channels(fmc, spec_fmc, adc_fs, pause):
    # Make sure no acquisition is running
    fmc.stop_acq()
    time.sleep(pause)
    # Start acquisition
    fmc.start_acq()
    time.sleep(pause)
    # Trigger
    fmc.sw_trig()
    # Wait end of acquisition
    timeout = 0
    while('IDLE' != fmc.get_acq_fsm_state()):
        time.sleep(.1)
        timeout += 1
        if(ACQ_TIMEOUT < timeout):
            print "Acquisition timeout. Missing trigger?."
            print "Acq FSm state: %s"%fmc.get_acq_fsm_state()
            return 1
    # Retrieve data trough DMA
    trig_pos = fmc.get_trig_pos()
    # Enable "DMA done" iinterrupt
    spec_fmc.set_irq_en_mask(0x1)
    # Read ACQ_LENGTH samples after the trigger for all channels
    channels_data = spec_fmc.get_data((trig_pos<<3), ACQ_LENGTH*8)
    # Disable "DMA done" iinterrupt
    spec_fmc.set_irq_en_mask(0x0)
    channels_data = [hex2signed(item) for item in channels_data]
    channels_data = [digital2volt(item,adc_fs,16) for item in channels_data]
    return channels_data

def plot_channels(ch_data, ch_mean, ylimit):
    sample = arange(len(ch_data[0]))
    plot(sample, ch_data[0], 'b', label='Channel 1')
    plot(sample, ch_data[1], 'g', label='Channel 2')
    plot(sample, ch_data[2], 'm', label='Channel 3')
    plot(sample, ch_data[3], 'c', label='Channel 4')
    plot(sample, [ch_mean[0]]*len(sample), 'r')
    plot(sample, [ch_mean[1]]*len(sample), 'r')
    plot(sample, [ch_mean[2]]*len(sample), 'r')
    plot(sample, [ch_mean[3]]*len(sample), 'r')
    ylim(-ylimit-(ylimit/10.0), ylimit+(ylimit/10.0))
    grid(color='k', linestyle='--', linewidth=1)
    legend(loc='upper left')
    show()
    return 0


def main (default_directory='.'):

    # Load firmware
    load_firmware(default_directory)

    # Objects declaration
    spec = rr.Gennum() # bind to the SPEC board
    spec_fmc = spec_fmc_adc.CSpecFmcAdc100Ms(spec)
    fmc = fmc_adc.CFmcAdc100Ms(spec)
    usb_tty = find_usb_tty.CttyUSB()
    awg_tty = usb_tty.find_usb_tty(AWG_USB_VENDOR_ID, AWG_USB_PRODUCT_ID)
    box_tty = usb_tty.find_usb_tty(BOX_USB_VENDOR_ID, BOX_USB_PRODUCT_ID)
    gen = Agilent33250A(device=awg_tty[0], bauds=AWG_BAUD)
    sine = SineWaveform()
    box = calibr_box.CCalibr_box(box_tty[0])

    # Initialise fmc adc
    fmc_adc_init(spec, fmc)

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

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

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

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

    # Print configuration
    fmc.print_adc_core_config()

    # Print ADC config
    fmc.print_adc_config()


    # Acquisition parameters
    ACQ_PAUSE = 1 # pause between acq. stop and start, start and trigger
    IN_RANGE  = '100mV'
    IN_TERM   = 'ON'
    ADC_FS = {'10V':10.0, '1V':1.0, '100mV':0.1}

    # Set sine params
    sine.frequency = 1E6
    sine.amplitude = 0.8 * ADC_FS[IN_RANGE]
    sine.dc = 0
    print('\nSine 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)


    channels_data = [[],[],[],[]]
    for ch in range(NB_CHANNELS):
        print "\nAcquiring channel %d"%(ch+1)
        # Configure analogue input
        fmc.set_input_range(ch+1, IN_RANGE)
        fmc.set_input_term(ch+1, IN_TERM)
        time.sleep(SSR_SET_SLEEP)
        # connect AWG to current channel
        box.select_output_ch(ch+1)
        time.sleep(BOX_SET_SLEEP)
        # Perform an acquisition
        acq_data = acq_channels(fmc, spec_fmc, ADC_FS[IN_RANGE], ACQ_PAUSE)
        channels_data[ch] = acq_data[ch::4]
        # Get time-tags
        trig_tag = spec_fmc.get_utc_trig_tag()
        start_tag = spec_fmc.get_utc_start_tag()
        stop_tag = spec_fmc.get_utc_stop_tag()
        end_tag = spec_fmc.get_utc_end_tag()
        print('Acq stop time-tag  : %10.10f [s]')%(stop_tag[2]+(stop_tag[3]*8E-9))
        print('Acq start time-tag : %10.10f [s]')%(start_tag[2]+(start_tag[3]*8E-9))
        print('Trigger time-tag   : %10.10f [s]')%(trig_tag[2]+(trig_tag[3]*8E-9))
        print('Acq end time-tag   : %10.10f [s]')%(end_tag[2]+(end_tag[3]*8E-9))

    # Calculate mean for each channel data
    ch_mean = []
    for ch in range(NB_CHANNELS):
        ch_mean.append(mean(channels_data[ch]))

    # print aqcuisition to file
    file_name = "test12_data.txt"
    f = open(file_name, 'w')
    f.write("CH1 value [V], CH2 value [V], CH3 value [V], CH4 value [V]\n")
    for i in range(len(channels_data[0])):
        f.write("%2.4f, %2.4f, %2.4f, %2.4f\n"%(channels_data[0][i], channels_data[1][i], channels_data[2][i], channels_data[3][i]))
    f.close()

    # Plot all channels
    plot_channels(channels_data, ch_mean, (ADC_FS[IN_RANGE]/2))


    # 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()
