#!   /usr/bin/env   python
#    coding: utf8
# Copyright CERN, 2013
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org

# Import system modules
import sys
import time
import os
import math
import random as rdm

# Add common modules and libraries location to path
sys.path.append('../../../')
sys.path.append('../../../gnurabbit/python/')
sys.path.append('../../../common/')

# Import common modules
from ptsexcept import *
import rr

# Import specific modules

from fmc_adc_spec import *
from fmc_adc import *
from numpy import *
from pylab import *
from ctypes import *
from calibr_box import *
from cp210x_eeprom import *
from find_usb_tty import *
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *


"""
test28: Long term test

1) Clear DDR memory
2) Configure acquisition with random params
3) Configure waveform generator to generate a sine wave within the acquisition params
4) Perform acquisition
5) Fit acquired waveform to a sine wave and check that it's within boundaries
"""


################################################################################
# Constants declaration
################################################################################
TEST_NB = 28

# Configuration
LOAD_BITSTREAM = True
PLOT_ACQ = True

# Gateware
FMC_ADC_BITSTREAM = '../firmwares/spec_fmcadc100m14b4cha.bin'
EXPECTED_BITSTREAM_TYPE = 0x0

# Calibration box
BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
BOX_USB_PRODUCT_ID = 0xea60 # CP210x Composite Device
BOX_SET_SLEEP = 0.01

# Agilent AWG
AWG_USB_VENDOR_ID = 0x0403 # Future Technology Devices International, Ltd
AWG_USB_PRODUCT_ID = 0x6001 # FT232 USB-Serial (UART) IC
AWG_BAUD = 57600
AWG_SET_SLEEP = 0.3

# fmc-adc mezzanine
NB_CHANNELS = 4
SSR_SET_SLEEP = 0.05
DAC_SET_SLEEP = 0.01 # s
CHANNEL_BYTES = 2 # bytes
MEMORY_SIZE = 2**28/(NB_CHANNELS * CHANNEL_BYTES) # samples
MULTISHOT_MAX_SIZE = 2**11 # samples
SAMP_FREQ = 100E6 # Hz
RANGES = {'10V':10.0, '1V':1.0, '100mV':0.1}
ADC_NBITS = 16 # ADC chip is 14 bits, but shifted to 16 bits in the firmware
DAC_NBITS = 16
DAC_FS = 10 # DAC full scale range is 10V
CALIBR_BIN_FILENAME = "calibration_data.bin"
EEPROM_BIN_FILENAME = "eeprom_content.out"
EEPROM_SIZE = 8192 # in Bytes

# Acquisition
ACQ_MIN_PERIODS = 2
ACQ_TIMEOUT = 10000 # ms

# Input waveform
SINE_MIN_FREQ = 10E3 # Hz
SINE_MAX_FREQ = 1E6  # Hz

# DMA
DMA_MAX_ITEMS = 128 # items
DMA_ITEM_SIZE = 4096 # bytes


################################################################################
# Functions declaration
################################################################################

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


# Converts signed to two's complement hex
def signed2hex(value):
    if value < 0:
        return (((abs(value) ^ 0xffff) + 1) & 0xffff)
    else:
        return value


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


# Converts volts to digital value with half full range offset
def volt2digital(value, full_scale, nb_bit):
    digital = (value + full_scale/2) * 2**nb_bit/full_scale
    if(digital > 2**nb_bit - 1):
        digital = 2**nb_bit - 1
    if(digital < 0):
        digital = 0
    #print('volt2digital: %2.9f > %2.9f')%(value,digital)
    return int(digital)


# Converts volts to digital value
def volt2digital_without_offset(value, full_scale, nb_bit):
    if(value > (2**nb_bit)/2 - 1):
        value = (2**nb_bit)/2 - 1
    if(value < -((2**nb_bit)/2)):
        value = -((2**nb_bit)/2)
    digital = (value) * 2**nb_bit/full_scale
    #print('volt2digital: %2.9f > %2.9f')%(value,digital)
    return int(digital)


# Clears DDR memory (fill with zeros)
def clear_ddr(carrier, verbose=False):
    if verbose:
        print("Clearing DDR memory")
    # dma item size = 4096 bytes
    # max linked items per page= 128
    # 2**28/(4096*128) = 512
    pattern = 0x0
    length = 2**27-(128*4096)
    # Enable "DMA done" interrupt
    carrier.enable_dma_done_irq()
    #carrier.print_irq_controller_regs()
    # Test number of dma transfer required (max dma size limitation of gnurabbit driver)
    dma_required = int(math.ceil((MEMORY_SIZE<<3)/float(254*DMA_ITEM_SIZE*DMA_MAX_ITEMS)))
    start_byte_addr = 0
    if verbose:
        print("%d DMA are required"%(dma_required))
    data = []
    for dma in range(dma_required):
        if dma+1 == dma_required:
            dma_length = (MEMORY_SIZE<<3) - (dma * DMA_ITEM_SIZE * DMA_MAX_ITEMS * 254)
        else:
            dma_length = DMA_ITEM_SIZE * DMA_MAX_ITEMS * 254
        if verbose:
            print("DMA #%d: start: %d, end %d, length: %d"%(dma, start_byte_addr, start_byte_addr+dma_length, dma_length))
        # Perform dma transfer
        dma_data = carrier.clear_ddr(start_byte_addr, pattern, dma_length, False)
        # Incerment dma start address
        start_byte_addr += dma_length
    # Disable "DMA done" interrupt
    carrier.disable_dma_done_irq()


# Returns calibration data from fmc eeprom
def get_calibr_data(fmc, eeprom_bin_filename, calibr_bin_filename, verbose=False):
    if verbose:
        print "Read calibration data from FMC EEPROM:"

    adc_corr_data = {'10V':{'offset':[],'gain':[],'temp':0},
                         '1V':{'offset':[],'gain':[],'temp':0},
                         '100mV':{'offset':[],'gain':[],'temp':0}}

    dac_corr_data = {'10V':{'offset':[],'gain':[],'temp':0},
                         '1V':{'offset':[],'gain':[],'temp':0},
                         '100mV':{'offset':[],'gain':[],'temp':0}}

    # Read entire EEPROM
    print("Read all eeprom content.")
    eeprom_data_read = fmc.sys_i2c_eeprom_read(0, EEPROM_SIZE)

    # Write EEPROM data to binary file
    if verbose:
        print("Write eeprom content to file (binary): %s"%(eeprom_bin_filename))
    f_eeprom = open(eeprom_bin_filename, "wb")
    for byte in eeprom_data_read:
        f_eeprom.write(chr(byte))
    f_eeprom.close()

    # Get calibration data
    #print("Extract calibration binary file to: %s"%(CALIBR_BIN_FILENAME))
    cmd = 'sdb-read -e 0x200 ' + eeprom_bin_filename + ' calib > ' + calibr_bin_filename
    if verbose:
        print("Exctract calibration binary file, cmd: %s"%(cmd))
    os.system(cmd)
    if verbose:
        print "Get calibration data from binary file."
    calibr_data = []
    f_calibr_data = open(calibr_bin_filename, "rb")
    try:
        byte = f_calibr_data.read(1)
        while byte != "":
            calibr_data.append(ord(byte))
            byte = f_calibr_data.read(1)
    finally:
        f_eeprom.close()

    # Re-arrange correction data into 16-bit number (from bytes)
    eeprom_corr_data = []
    for i in range(0,len(calibr_data),2):
        eeprom_corr_data.append((calibr_data[i+1] << 8) + (calibr_data[i]))
        if verbose:
            print "0x%04X" % eeprom_corr_data[-1]
    if verbose:
        print "Calibration data length (16-bit): %d" % len(eeprom_corr_data)

    for adc_range in RANGES:
        for ch in range(NB_CHANNELS):
            adc_corr_data[adc_range]['offset'].append(hex2signed(eeprom_corr_data.pop(0)))
        for ch in range(NB_CHANNELS):
            adc_corr_data[adc_range]['gain'].append(eeprom_corr_data.pop(0))
        adc_corr_data[adc_range]['temp'] = eeprom_corr_data.pop(0)/100.0

    for dac_range in RANGES:
        for ch in range(NB_CHANNELS):
            dac_corr_data[dac_range]['offset'].append(hex2signed(eeprom_corr_data.pop(0)))
        for ch in range(NB_CHANNELS):
            dac_corr_data[dac_range]['gain'].append(eeprom_corr_data.pop(0))
        dac_corr_data[dac_range]['temp'] = eeprom_corr_data.pop(0)/100.0

    return adc_corr_data, dac_corr_data


# Set AWG (sine wave)
def set_awg(awg, freq, ampl, offset, verbose=False):
    # Set sine params
    sine = SineWaveform()
    sine.frequency = freq # Hz
    sine.amplitude = ampl # Vpeak
    sine.dc = offset      # V
    if verbose:
        print('AWG Sine frequency:%3.3fMHz amplitude:%2.3fVp offset:%2.3fV')%(sine.frequency/1E6, sine.amplitude, sine.dc)
    # Set AWG
    awg.connect()
    awg.play(sine)
    awg.output = True
    awg.sync = True
    time.sleep(AWG_SET_SLEEP)


# Get best adc input range for a given amplitude
def get_best_input_range(in_ampl, term, verbose=False):
    # If the 50 ohms termination is enabled, input amplitude is reduced by a factor 2
    if term == 'ON':
        in_ampl = in_ampl/2.0
    in_range = '100mV'
    if in_ampl > 0.05:
        in_range = '1V'
    if in_ampl > 0.5:
        in_range = '10V'
    if verbose:
        print("Selected input range: %s"%in_range)
    return in_range


# Set acquisition params
def set_acq(fmc, nb_shots, pre_trig_samples, post_trig_samples, decim_factor, verbose=False):
    fmc.set_pre_trig_samples(pre_trig_samples)
    fmc.set_post_trig_samples(post_trig_samples)
    fmc.set_shots(nb_shots)
    fmc.set_decimation(decim_factor)
    if verbose:
        print("Acquisition params -> number of shots: %d, pre-trigger samples: %d, post-trigger samples: %d, decimation factor: %d"%(nb_shots, pre_trig_samples, post_trig_samples, decim_factor))


# Set analog input range and termination (same on all channels)
def set_adc_input(fmc, term, in_range, verbose=False):
    for channel in range(1,NB_CHANNELS+1):
        fmc.set_input_term(channel, term)
        fmc.set_input_range(channel, in_range)
    time.sleep(SSR_SET_SLEEP)
    if verbose:
        print("ADC input params -> Termination: %s, input range: %s"%(term, in_range))


# Set channel offset with the DAC (same on all channels)
def set_adc_offset(fmc, offset_volt, verbose=False):
    dac_fs = DAC_FS
    dac_nbits = DAC_NBITS
    dac_d = volt2digital(offset_volt, dac_fs, dac_nbits)
    if verbose:
        print('Offset DAC value: 0x%X (%fV)')%(dac_d, offset_volt)
    for channel in range(1,NB_CHANNELS+1):
        fmc.set_dc_offset(channel,dac_d)
    time.sleep(DAC_SET_SLEEP)


# Set hardware trigger
def set_adc_trig(fmc, hw_sel, hw_pol, channel, thres_volt, delay, in_range, verbose=False):
    fs = RANGES[in_range]
    nbits = ADC_NBITS
    thres = volt2digital_without_offset(thres_volt, fs, nbits)
##################################################
    hw_en = 1
    sw_en = 0
#################################################
    fmc.set_trig_config(hw_sel, hw_pol, hw_en, sw_en, channel, thres, delay)
    fmc.print_trig_config()
    HW_SEL = ['internal', 'external']
    HW_POL = ['rising','falling']
    if verbose:
        print("Trigger params -> Hardware %s on %s edge, on channel %d with a thresold of %fV"%(HW_SEL[hw_sel], HW_POL[hw_pol], channel, thres_volt))

# Make acquisition
def make_acq(carrier, fmc, verbose=False):
    # Make sure no acquisition is running
    fmc.stop_acq()
    # Enables acquisition end interrupt
    fmc.enable_acq_end_irq()
    #fmc.enable_trig_irq()
    if verbose:
        print("IRQ mask   : %.8X"%fmc.get_eic_en_mask())
        print("IRQ source : %.8X"%fmc.get_eic_src())
    # Start acquisition
    if verbose:
        print("Starting acquisition...")
    fmc.start_acq()
###############################
    # Random time before trigger
    #time_to_trig = rdm.randrange(1, 1000)/1000.0
    #if verbose:
    #    print("Time between acquisition start and trigger: %fs"%(time_to_trig))
    #time.sleep(time_to_trig)
    #fmc.sw_trig()
###########################

    # Wait for trigger interrupt
    #carrier.gnum.wait_irq)

    # Wait for acquisition end interrupt
    carrier.gnum.wait_irq()

    irq_src = fmc.get_eic_src()
    irq_vect = carrier.vic_get_current_vector_addr()
    if verbose:
        print("FMC EIC irq source : %.8X"%irq_src)
        print("VIC current vector : 0x%08X"%irq_vect)
    if(irq_vect == carrier.fmc_eic_addr):
        if(fmc.get_acq_end_status()):
            fmc.clear_acq_end_irq()
            carrier.vic_ack_current_vector()
            if verbose:
                print("IRQ source : 0x%08X"%fmc.get_eic_src())
        else:
            raise PtsError("Bad IRQ source.")
    else:
        raise PtsError("Bad IRQ vector. expected:0x%08X got:0x%08X" %(carrier.fmc_eic_addr, irq_vect))

    if 'IDLE' != fmc.get_acq_fsm_state():
        raise PtsError("Acq FSM not IDLE. Current state:%s"%fmc.get_acq_fsm_state())

    if verbose:
        print("Acquisition finished.")

    # Disables acquisition end interrupt
    fmc.disable_acq_end_irq()


# Make a linked list dma
def make_dma(carrier, start_byte_addr, length_bytes, in_range, verbose=False):
    # Enable "DMA done" interrupt
    carrier.enable_dma_done_irq()
    #carrier.print_irq_controller_regs()
    # Test number of dma transfer required (max dma size limitation of gnurabbit driver)
    dma_required = int(math.ceil(length_bytes/float(DMA_ITEM_SIZE*DMA_MAX_ITEMS)))
    if verbose:
        print("%d DMA are required"%(dma_required))
    data = []
    for dma in range(dma_required):
        if dma+1 == dma_required:
            dma_length = length_bytes-(dma * DMA_ITEM_SIZE * DMA_MAX_ITEMS)
        else:
            dma_length = DMA_ITEM_SIZE * DMA_MAX_ITEMS
        #if verbose:
        #    print("DMA #%d: start: %d, end %d, length: %d"%(dma, start_byte_addr, start_byte_addr+dma_length, dma_length))
        # Perform dma transfer
        dma_data = carrier.get_data(start_byte_addr, dma_length)
        # Converts raw data to volts
        fs = RANGES[in_range]
        data += [digital2volt(hex2signed(sample), fs, ADC_NBITS) for sample in dma_data]
        # Incerment dma start address
        start_byte_addr += dma_length
    # Disable "DMA done" interrupt
    carrier.disable_dma_done_irq()
    #carrier.print_irq_controller_regs()
    return data


# Get data from DDR memory
def get_acq_data(carrier, fmc, samples, pre_trig_samples, nb_shots, in_range, verbose=False):

#######################################
    #pre_trig_samples += 100
    #samples += 1000
######################################

    if nb_shots == 1:
        #-----------------------------------------------------------------------
        # Single shot case
        #-----------------------------------------------------------------------
        # Get trigger position (in samples)
        trig_pos = fmc.get_trig_pos()/8

        # Compute acquisition start address (in samples)
        if pre_trig_samples > trig_pos:
            if verbose:
                print("pre-trigger samples are overlapping memory")
            start_pos = MEMORY_SIZE - (pre_trig_samples - trig_pos)
        else:
            if verbose:
                print("pre-trigger samples are not overlapping memory")
            start_pos = trig_pos - pre_trig_samples

        if verbose:
            print("Trigger position (samples)          : %d" % (trig_pos))
            print("Acquisition start position (samples): %d" % (start_pos))
            print("Total number of samples             : %d" % (samples))

        # Check if acquisition is overlapping ddr memory
        if start_pos + samples > MEMORY_SIZE:
            if verbose:
                print("Acquisition is overlapping")
            # First dma from start address to end of memory
            start = start_pos<<3
            length = (MEMORY_SIZE - start_pos)<<3
            if verbose:
                print("DMA start: %d(bytes), length: %d(bytes)"%(start, length))
            data = make_dma(carrier, start, length, in_range, True)
            # Second dma from 0 to end acq address
            start = 0
            length = (samples - (MEMORY_SIZE - start_pos))<<3
            if verbose:
                print("DMA start: %d(bytes), length: %d(bytes)"%(start, length))
            data += make_dma(carrier, start, length, in_range, True)
        else:
            start = start_pos<<3
            length = samples<<3
            if verbose:
                print("Acquisition is not overlapping")
                print("DMA start: %d(bytes) %d, length: %d(bytes)"%(start, start%4, length))
            data = make_dma(carrier, start, length, in_range, True)
    else:
        #-----------------------------------------------------------------------
        # Multi-shot case
        #-----------------------------------------------------------------------
        start = 0
        length = (samples*nb_shots)<<3
        if verbose:
            print("DMA start: %d(bytes), length: %d(bytes)"%(start, length))
        data = make_dma(carrier, start, length, in_range, True)

    # returns interleaved channel data (in volts)
    return data





def acq_mean(acq_data):
    mean_d = []
    for channel in range(1,NB_CHANNELS+1):
        # calculate mean value for each channel
        mean_d.append(mean(acq_data[channel-1::4]))
    return mean_d

def plot_all(data, ylimit, trig_level, trig_pos):
    sample = arange(len(data)/4)
    clf()
    plot(sample, data[0::4], 'b', label='Channel 1')
    plot(sample, data[1::4], 'g', label='Channel 2')
    plot(sample, data[2::4], 'c', label='Channel 3')
    plot(sample, data[3::4], 'm', label='Channel 4')
    plot(sample, len(sample)*[trig_level], 'r', label='Trigger level')
    ylim_min = -ylimit-(ylimit/10.0)
    ylim_max = ylimit+(ylimit/10.0)
    vlines(trig_pos, ylim_min, ylim_max, color='#AA0000', linestyles='solid', label='Trigger position')
    ylim(ylim_min, ylim_max)
    xlim(0, len(sample))
    grid(which='both')
    legend()
    draw()
    show()
    return 0




def main (default_directory='.'):


    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 firmware
    if LOAD_BITSTREAM:
        bitstream_path = os.path.join(default_directory, FMC_ADC_BITSTREAM)
        print "Loading FMC ADC firmware: %s\n" % bitstream_path
        if(os.path.isfile(FMC_ADC_BITSTREAM)):
            spec.load_firmware(FMC_ADC_BITSTREAM)
            time.sleep(2)
        else:
            raise PtsCritical("Firmware file \"%s\" is missing, test stopped." % FMC_ADC_BITSTREAM)

    # Carrier object declaration (SPEC board specific part)
    # Used to check that the firmware 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:

        # Calibration box and arbitrary waveform generator objects declaration
        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)
        awg = Agilent33250A(device=awg_tty[0], bauds=AWG_BAUD)
        box = CCalibr_box(box_tty[0])

        fmc.testpat_dis() # Disable adc test pattern (just in case)
        fmc.test_data_dis() # Disable test data to ddr (just in case)


        ########################################################################
        # Get calibration data from EEPROM
        ########################################################################
        calibr_bin_filename = os.path.join(default_directory, CALIBR_BIN_FILENAME)
        eeprom_bin_filename = os.path.join(default_directory, EEPROM_BIN_FILENAME)
        adc_corr_data, dac_corr_data = get_calibr_data(fmc, eeprom_bin_filename, calibr_bin_filename, False)
        # Apply DAC correction (correction data will be used every time a DAC is set)
        fmc.set_dac_corr(dac_corr_data)


        ########################################################################
        # Clear DDR
        ########################################################################
        t1 = time.time()
        clear_ddr(carrier, True)
        t2 = time.time()
        print "elapsed time: %.2f seconds\n" % (t2-t1)


        ########################################################################
        # Set adc params
        ########################################################################

        # Choose input termination randomly
##########################################################
        adc_term = rdm.choice(['ON', 'OFF'])
        #adc_term = 'ON'
##########################################################
        print("Input termination: %s"%adc_term)

        # Choose input range randomly
##########################################################
        adc_range = rdm.choice(['10V', '1V', '100mV'])
        #adc_range = '10V'
##########################################################
        print("Input range: %s"%adc_range)

        # Configure ADC analogue input
        set_adc_input(fmc, adc_term, adc_range, True)
        adc_offset = 0
        set_adc_offset(fmc, adc_offset, True)
        for ch in range(1, NB_CHANNELS+1):
            fmc.set_adc_gain_offset_corr(ch, adc_corr_data[adc_range]['gain'][ch-1], adc_corr_data[adc_range]['offset'][ch-1])


        ########################################################################
        # Set awg params
        ########################################################################

        # Building input sine wave
        #sine_freq = SINE_MIN_FREQ + ((SINE_MAX_FREQ-SINE_MIN_FREQ)*rdm.random())
        sine_freq = rdm.randrange(SINE_MIN_FREQ, SINE_MAX_FREQ, 1E3)
        sine_min_ampl = 0.1 * (RANGES[adc_range]/2) # 10% full scale
        sine_max_ampl = 0.9 * (RANGES[adc_range]/2) # 90% full scale
        if adc_term == 'ON':
            sine_min_ampl *= 2
            sine_max_ampl *= 2
        print("Input sine wave => min amplitude: %2.3fV, max amplitude: %2.3fV"%(sine_min_ampl, sine_max_ampl))
        #sine_ampl = sine_min_ampl + ((sine_max_ampl-sine_min_ampl)*rdm.random())
        sine_ampl = rdm.randrange(int(sine_min_ampl*1E3), int(sine_max_ampl*1E3), 100)/1000.0
        #sine_offset = (sine_max_ampl-sine_ampl)*rdm.random()
        sine_offset = 0.0
        print("Input sine wave => Frequency: %3.3fMHz, Amplitude: %2.3fVp, Offset: %2.3fV"%(sine_freq/1E6, sine_ampl, sine_offset))

        # Configure AWG with sine wave
#######################
        #sine_freq = 1E6
        #sine_ampl = 2.0
        #sien_offset = 0.0
#######################
        set_awg(awg, sine_freq, sine_ampl, sine_offset, True)


        ########################################################################
        # Set box params
        ########################################################################

        # Randomly select an adc channel
        #channel = rdm.randrange(1,NB_CHANNELS+1)
        channel = 1
        # Connect AWG to selected channel (other channels with 100mV range ref ~= 0.04096V)
        box.select_output_ch(channel)
        time.sleep(BOX_SET_SLEEP)


        ########################################################################
        # Set acquisition params
        ########################################################################

        # Configure trigger
#######################
        trig_hw_sel = 0 # internal
        #trig_hw_sel = 1 # external
        trig_hw_pol = 0 # rising
        trig_channel = channel
        trig_thres = sine_offset # in Volts
        trig_delay = 0 # in samples
#######################
        set_adc_trig(fmc, trig_hw_sel, trig_hw_pol, trig_channel, trig_thres, trig_delay, adc_range, True)

        # Find the minimum number of samples to have ACQ_MIN_PERIODS of the sine wave
        acq_min_samples =  int(ACQ_MIN_PERIODS*SAMP_FREQ/sine_freq)
        print("Minimum number of samples: %d"%acq_min_samples)

        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # Can't use all available memory on-board.
        # Bad memory management in my Python program...
        # -> Limiting the max number of samples.
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        mem_size = MEMORY_SIZE/100
        print("Memory size in samples : %d"%mem_size)

        # Choose the number of shots and samples randomly
#######################
        #multishot = rdm.choice([False, True])
        multishot = False
#######################
        if multishot:
            acq_samples = rdm.randrange(acq_min_samples, MULTISHOT_MAX_SIZE+1)
            acq_nb_shots = rdm.randrange(1, (mem_size/acq_samples)+1)
        else:
            acq_samples = rdm.randrange(acq_min_samples, mem_size+1)
            acq_nb_shots = 1
        print("Final number of samples: %d"%acq_samples)

        # Choose the trigger position randomly
        acq_pre_trig_samples = rdm.randrange(0, acq_samples+1)
        acq_post_trig_samples = acq_samples - acq_pre_trig_samples

        # Configure acquisition
####################
        acq_decim_factor = 1
        acq_nb_shots = 1
        #acq_pre_trig_samples = 100
        #acq_post_trig_samples = 1000
        #acq_samples = acq_pre_trig_samples + acq_post_trig_samples
####################
        set_acq(fmc, acq_nb_shots, acq_pre_trig_samples, acq_post_trig_samples, acq_decim_factor, True)


        ########################################################################
        # Make acqusisition
        ########################################################################

        # Make acquisition
        make_acq(carrier, fmc, True)

        # Get data from DDR
        acq_data = get_acq_data(carrier, fmc, acq_samples, acq_pre_trig_samples, acq_nb_shots, adc_range, True)

        print("Number of retrieved samples: %d"%(len(acq_data)/4))


        ########################################################################
        # Check retreived data
        ########################################################################
        


        # Plot data
        acq_data_ch = acq_data[channel-1::4]
        print("trig thres:%2.6fV, data@trig: %2.6fV"%(trig_thres, acq_data_ch[acq_pre_trig_samples]))
        #fs = RANGES[adc_range]
        #trig_thres = signed2hex(volt2digital_without_offset(trig_thres, fs, ADC_NBITS))
        #print("trig thres:0x%04X, data@trig: 0x%04X"%(trig_thres, acq_data_ch[acq_pre_trig_samples]))

        if PLOT_ACQ:
            plot_all(acq_data, max(acq_data), trig_thres, acq_pre_trig_samples)


        sys.exit()

        # Reset offset DACs
        fmc.dc_offset_reset()
        time.sleep(DAC_SET_SLEEP)

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

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


if __name__ == '__main__' :
    main()
