#!   /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 ptsexcept import *

import csr
import fmc_adc
import spec_fmc_adc
import calibr_box
import find_usb_tty
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *

"""
test13: Test FMC temperature stability

"""


TEMP_THRES = 50.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

# 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


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 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, spec_fmc):
    # 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" 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)
    return 0


def main (default_directory='.'):

    # Load firmware
    load_firmware(default_directory)

    # Objects declaration
    spec = rr.Gennum() # bind to the SPEC board
    fmc = fmc_adc.CFmcAdc100Ms(spec)
    spec_fmc = spec_fmc_adc.CSpecFmcAdc100Ms(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, box)

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

    print "\n"

    # Read SPEC unique ID and print to log
    spec_unique_id = spec_fmc.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(spec_fmc.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]
        fmc_temp.pop(0)
        acq_channels(fmc, spec_fmc)
        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())
        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)
                break
        else:
            print "%4d fmc temp:%3.3f°C"%(fmc_temp_cnt, fmc_temp[-1])
        fmc_temp_cnt += 1
        acq_channels(fmc, spec_fmc)
        time.sleep(MEAS_SLEEP)
    t2 = time.time()
    print "FMC temperature is stable after %10.3fs"%(t2-t0)



if __name__ == '__main__' :
    main()
