#!   ./python
#    coding: utf8

# Copyright CERN, 2013
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 30/5/2012

# Import system modules
import sys
import time
import os

# Add common modules and libraries location to path
sys.path.append('../../../')
sys.path.append('../../../common/')
sys.path.append('../../../../svec_pts/ubuntu/pts/pyts/')

# Import common modules
from ptsexcept import *
from rr2vv import *

# Import specific modules
from fmc_adc_svec import *
from fmc_adc import *
from numpy import *


"""
svec_test30: Test software reset.

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


NB_CHANNELS = 4

AWG_SET_SLEEP = 0.3
SSR_SET_SLEEP = 0.05
BOX_SET_SLEEP = 0.01

ACQ_TIMEOUT = 10

PRE_TRIG_SAMPLES = 10
POST_TRIG_SAMPLES = 10000
NB_SHOTS = 1

ACQ_LENGTH = 10000 # in samples


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.\n"
    # 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, carrier, 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()
    # Read ACQ_LENGTH samples after the trigger for all channels
    channels_data = fmc.get_data(trig_pos, ACQ_LENGTH*8)
    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 main (default_directory='.'):

    # Constants declaration
    LUN = 0
    TEST_NB = 30
    FMC_ADC_BITSTREAM = '../../../../../firmwares/svec_fmcadc100m14b4cha.bin'
    FMC_ADC_BITSTREAM = os.path.join(default_directory, FMC_ADC_BITSTREAM)
    EXPECTED_BITSTREAM_TYPE = 0x0

    # Calibration box vendor and product IDs
    BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
    BOX_USB_PRODUCT_ID = 0xea60 # CP210x Composite Device


    start_test_time = time.time()
    print "================================================================================"
    print "Test%02d start\n" % TEST_NB

    # SVEC object declaration
    print "Loading hardware access library and opening device.\n"
    bus = VME_rr_compatible(LUN)
    print "Initialising device.\n"
    bus.vv_init()

    # Load FMC ADC firmware
    print "Loading FMC ADC firmware: %s\n" % FMC_ADC_BITSTREAM
    ret = bus.vv_load(FMC_ADC_BITSTREAM, 1)
    print('')
    time.sleep(2)

    # Carrier object declaration (SPEC board specific part)
    # Used to check that the firmware is loaded.
    try:
        carrier = CFmcAdc100mSvec(bus, EXPECTED_BITSTREAM_TYPE)
    except FmcAdc100mSvecOperationError as e:
        raise PtsCritical("Carrier init failed, test stopped: %s" % e)

    # Mezzanine object declaration (FmcAdc100m14b4cha board specific part)
    try:
        fmc = CFmcAdc100m(bus)
    except FmcAdc100mOperationError as e:
        raise PtsCritical("Mezzanine init failed, test stopped: %s" % e)


    try:

        # Initialise fmc adc
        fmc_adc_init(bus, fmc)

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

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


        # 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}


        # Make sure no acquisition is running
        fmc.stop_acq()
        # Start acquisition
        fmc.start_acq()
        print('\nShould be in acquisition')
        time.sleep(3)

        print('\nFMC 1 software reset state: 0x%.1X'%carrier.get_sw_rst(0))
        print('Resetting mezzanine')
        carrier.sw_rst(0)
        print('FMC 1 software reset state: 0x%.1X'%carrier.get_sw_rst(0))
        print('Re-init the mezzanine')
        fmc.__init__(bus)
        fmc_adc_init(bus, fmc)
        time.sleep(3)

        print('Make a complete acquisition')
        ch_mean = []
        channels_data = []
        for ch in range(NB_CHANNELS):
            # Configure analogue input
            fmc.set_input_range(ch+1, IN_RANGE)
            fmc.set_input_term(ch+1, IN_TERM)
            time.sleep(SSR_SET_SLEEP)
            # Perform an acquisition
            acq_data = acq_channels(fmc, carrier, ADC_FS[IN_RANGE], ACQ_PAUSE)
            channels_data.append(acq_data[ch::4])
            ch_mean.append(mean(channels_data[ch]))

        # Make sure all switches are OFF
        open_all_channels(fmc)

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

    except(FmcAdc100mSvecOperationError, FmcAdc100mOperationError) as e:
        raise PtsError("Test failed: %s" % e)


    print ""
    print "==> End of test%02d" % TEST_NB
    print "================================================================================"
    end_test_time = time.time()
    print "Test%02d elapsed time: %.2f seconds\n" % (TEST_NB, end_test_time-start_test_time)


if __name__ == '__main__' :
    main()
