#!   /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: 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('../../../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 *


"""
test27: Test DMA

"""

NB_CHANNELS = 4

PRE_TRIG_SAMPLES = 1000
POST_TRIG_SAMPLES = 100000
NB_SHOTS = 1

ACQ_LENGTH = 50000 # in samples
ACQ_TIMEOUT = 10

# Values for offset DAC
# Note:
#      values < 0x8000 represents positive offset
#      values > 0x8000 represents negative offset
OFFSET_POS = 0x0000
OFFSET_NEG = 0xFFFF

DAC_SET_SLEEP = 0.01 # in [s]

# Expected ADC values
ADC_NEG = -32768.0
ADC_MID = 0.0
ADC_POS = 32764.0 # not 32767.0 because the ADC is 14-bit left-shifted by 2, the last 2 bits are always 0
ADC_TOL = 200.0


def hex2signed(value):
    if(value & 0x8000):
        return -((~value & 0xFFFF) + 1)
    else:
        return value


def acq_config(fmc):
    print "Initialise FMC board\n"
    # 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)


def acq_channels(fmc, carrier):
    # Make sure no acquisition is running
    fmc.stop_acq()
    # Start acquisition
    fmc.start_acq()
    time.sleep(0.01)
    # 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()
    print("Trigger position: 0x%8X" % trig_pos)
    #carrier.print_irq_controller_regs()
    # Enable "DMA done" interrupt
    carrier.set_irq_en_mask(0x1)
    #carrier.print_irq_controller_regs()
    raw_input("hit any key to start dma.")
    # Read ACQ_LENGTH samples after the trigger for all channels
    #channels_data = carrier.get_data((trig_pos<<3), ACQ_LENGTH*8)
    channels_data = carrier.get_data(0, ACQ_LENGTH*8)
    #for carrier_addr in range(0,10*4096, 4096):
    #    channels_data = carrier.get_data(carrier_addr, 0x1000)
    # Disable "DMA done" iinterrupt
    carrier.set_irq_en_mask(0x0)
    channels_data = [hex2signed(item) for item in channels_data]
    return channels_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 main (default_directory='.'):

    # Constants declaration
    TEST_NB = 27
    FMC_ADC_BITSTREAM = '../firmwares/spec_fmcadc100m14b4cha_gnum_hang_patch.bin'
    FMC_ADC_BITSTREAM = os.path.join(default_directory, FMC_ADC_BITSTREAM)
    EXPECTED_BITSTREAM_TYPE = 0x1


    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
    #print "Loading FMC ADC firmware: %s\n" % FMC_ADC_BITSTREAM
    #spec.load_firmware(FMC_ADC_BITSTREAM)
    #time.sleep(2)

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

        carrier.gnum.set_local_bus_freq(100)

        print("dma pages:")
        pages = spec.getplist()
        # Shift by 12 to get the 32-bit physical addresses
        pages = [addr << 12 for addr in pages]
        for i in range(25):
            print("%3d:0x%8X   %3d:0x%8X   %3d:0x%8X   %3d:0x%8X"%(i, pages[i], i+25, pages[i+25], i+50, pages[i+50], i+75, pages[i+75]))

        # Configuration
        acq_config(fmc)

        error = 0

        # All inputs in calibration mode
        for i in range(1, NB_CHANNELS+1):
            fmc.set_input_range(i, "CAL")

        # Disable test pattern (just in case)
        fmc.testpat_dis()

        """
        # Write pattern to DDR memory
        print("Try to DMA data TO ddr...")
        # Enable "DMA done" interrupt
        carrier.set_irq_en_mask(0x1)
        pattern = 0xDEADFACE
        raw_input("hit any key to start dma.")
        carrier.put_data(0, pattern, ACQ_LENGTH*8)
        """

        # Acquire and read channels
        print("Acquire and read all channels.")
        acq_data = acq_channels(fmc, carrier)
        print("Read done.")
        acq_data_mean = acq_mean(acq_data)
        for i in range(len(acq_data_mean)):
            print "ADC channel %d mean value:%d" % (i+1, acq_data_mean[i])

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