#!   /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
# Last modifications: 1/6/2012

# Import system modules
import sys
import time
import os

# Import specific modules
from fmc_adc_spec import *
from fmc_adc import *
from numpy import *
from pylab import *
from calibr_box import *
import find_usb_tty
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *

# Import common modules
from ptsexcept import *
import rr


"""
test22: Test FMC temperature stability

"""


TEMP_THRES = 45.0
TEMP_RIPPLE = 0.5
FIFO_SIZE = 20
MEAS_SLEEP = 1

PRE_TRIG_SAMPLES = 100
POST_TRIG_SAMPLES = 100000
NB_SHOTS = 1
ACQ_LENGTH = 50000 # in samples

NB_CHANNELS = 4


def disconnect_channels(fmc):
    for i in range(1,NB_CHANNELS+1):
        fmc.set_ssr(i, 0x00)

def fmc_adc_init(spec, fmc, box):
    print "\nInitialise FMC board\n"
    # Reset offset DACs
    fmc.dc_offset_reset()
    # Make sure all switches are OFF
    disconnect_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)
    # Connect channel 2 to AWG
    fmc.set_input_range(2, '10V')
    fmc.set_input_term(2, 'ON')
    box.select_output_ch(2)

def acq_channels(fmc, carrier):
    # Make sure no acquisition is running
    fmc.stop_acq()
    # Start acquisition
    fmc.start_acq()
    # Trigger
    fmc.sw_trig()
    # Wait end of acquisition
    timeout = 0
    time.sleep(0.001)
    while('IDLE' != fmc.get_acq_fsm_state()):
        time.sleep(.01)
        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" interrupt
    carrier.enable_dma_done_irq()
    # Read ACQ_LENGTH samples after the trigger for all channels
    channels_data = carrier.get_data(trig_pos, ACQ_LENGTH*8)
    # Disable "DMA done" interrupt
    carrier.disable_dma_done_irq()
    return 0

def print_temp(temp):
    # Standard in/out temporary redirection
    tmp_stdout = sys.stdout;
    sys.stdout = sys.__stdout__;
    tmp_stdin = sys.stdin;
    sys.stdin = sys.__stdin__;

    # Print temperature at the same position on the screen
    sys.stdout.write("FMC temperature: %3.3f°C \r" % temp)
    sys.stdout.flush()

    # Restore standard in/out
    sys.stdout = tmp_stdout;
    sys.stdin = tmp_stdin;

def newline():
    # Standard in/out temporary redirection
    tmp_stdout = sys.stdout;
    sys.stdout = sys.__stdout__;
    tmp_stdin = sys.stdin;
    sys.stdin = sys.__stdin__;

    # Print temperature at the same position on the screen
    sys.stdout.write("\n")
    sys.stdout.flush()

    # Restore standard in/out
    sys.stdout = tmp_stdout;
    sys.stdin = tmp_stdin;


def main (default_directory='.'):

    # Constants declaration
    TEST_NB = 22
    FMC_ADC_BITSTREAM = '../gatewares/spec_fmcadc100m14b4cha.bin'
    FMC_ADC_BITSTREAM = os.path.join(default_directory, FMC_ADC_BITSTREAM)
    EXPECTED_BITSTREAM_TYPE = 0x1

    # 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


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

    # SPEC object declaration
    print "Loading hardware access library and opening device.\n"
    spec = rr.Gennum()

    # Load FMC ADC gateware
    print "Loading FMC ADC gateware: %s\n" % FMC_ADC_BITSTREAM
    if(os.path.isfile(FMC_ADC_BITSTREAM)):
        spec.load_gateware(FMC_ADC_BITSTREAM)
        time.sleep(2)
    else:
        raise PtsCritical("Gateware file \"%s\" is missing, test stopped." % FMC_ADC_BITSTREAM)

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

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


    try:
        # Others objects declaration
        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 = CCalibr_box(box_tty[0])

        # Initialise fmc adc
        fmc_adc_init(spec, fmc, box)

        # Set AWG
        sine.frequency = 1E6
        sine.amplitude = 8.0
        sine.dc = 0
        gen.connect()
        gen.play(sine)
        gen.output = True

        # Read SPEC unique ID and print to log
        spec_unique_id = carrier.get_unique_id()
        if(spec_unique_id == -1):
            raise PtsError ("Can't read DS18D20 1-wire thermometer on SPEC board.")
        else:
            # print "SPEC Unique ID: %.12X" % spec_unique_id
            pass

        # Read FMC unique ID and print to log
        fmc_unique_id = fmc.get_unique_id()
        if(fmc_unique_id == -1):
            raise PtsError ("Can't read DS18D20 1-wire thermometer on SPEC board.")
        else:
            # print "FMC Unique ID:  %.12X" % fmc_unique_id
            pass

        # Read SPEC temperature and print to log
        spec_temp = []
        spec_temp.append(carrier.get_temp())
        print "SPEC temperature: %3.3f°C" % spec_temp[-1]

        # Read FMC temperature and print to log
        fmc_temp = []
        fmc_temp.append(fmc.get_temp())
        print "FMC temperature: %3.3f°C" % fmc_temp[-1]

        # Wait for the FMC temperature to reach threshold
        print "\nWait for FMC temperature to reach %2.1f°C"%TEMP_THRES
        t0 = time.time()
        while fmc_temp[-1] < TEMP_THRES:
            fmc_temp.append(fmc.get_temp())
            # print "FMC temperature: %3.3f°C" % fmc_temp[-1]
            print_temp(fmc_temp[-1])
            fmc_temp.pop(0)
            acq_channels(fmc, carrier)
            time.sleep(MEAS_SLEEP)
        t1 = time.time()
        print "FMC temperature reached %2.1f°C after %10.3fs\n"%(TEMP_THRES, t1-t0)

        # Wait for the FMC temperature to be stable
        fmc_temp_cnt = 0
        while True:
            fmc_temp.append(fmc.get_temp())
            print_temp(fmc_temp[-1])
            if fmc_temp_cnt >= FIFO_SIZE:
                temp_diff = (max(fmc_temp) - min(fmc_temp))
                fmc_temp.pop(0)
                print "[%4d] fmc temp: %3.3f°C, temp diff: %3.3f°C"%(fmc_temp_cnt, fmc_temp[-1], temp_diff)
                if temp_diff < TEMP_RIPPLE:
                    print "\nTemperature difference in the last %d measurements is less than %2.1f°C"%(FIFO_SIZE, TEMP_RIPPLE)
                    newline()
                    break
                else:
                    print "%4d fmc temp:%3.3f°C"%(fmc_temp_cnt, fmc_temp[-1])
            fmc_temp_cnt += 1
            acq_channels(fmc, carrier)
            time.sleep(MEAS_SLEEP)
        t2 = time.time()
        print "FMC temperature is stable after %10.3fs"%(t2-t0)

    except(FmcAdc100mSpecOperationError, FmcAdc100mOperationError, CalibrBoxOperationError) 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()
