#!   /usr/bin/env   python
#    coding: utf8
##_________________________________________________________________________________________________
##                                                                                                |
##                                           |TDC PTS|                                            |
##                                                                                                |
##                                         CERN,BE/CO-HT                                          |
##________________________________________________________________________________________________|

##-------------------------------------------------------------------------------------------------
##                                                                                                |
##                                         TDC ACAM test                                          |
##                                                                                                |
##-------------------------------------------------------------------------------------------------
##                                                                                                |
## Description  Testing of the communication with the ACAM chip IC8 of the TDC board.             |
##              The test configures the ACAM chip and then checks the status of all its registers.|
##              In detal these lines are tested: TDC_D[27..0], TDC_ADR[3..0], RESET_N, TDC_WRN,   |
##                                               TDC_RDN, TDC_CSN, TDC_OEN                        |
##                                                                                                |
##                                                                                                |
## FW to load   .bin                                                                              |
## Authors      Evangelia Gousiou (Evangelia.Gousiou@cern.ch)                                     |
## Website      http://www.ohwr.org/projects/pts                                                  |
## Date         11/01/2013                                                                        |
##-------------------------------------------------------------------------------------------------

##-------------------------------------------------------------------------------------------------
##                               GNU LESSER GENERAL PUBLIC LICENSE                                |
##                              ------------------------------------                              |
## This source file is free software; you can redistribute it and/or modify it under the terms of |
## the GNU Lesser General Public License as published by the Free Software Foundation; either     |
## version 2.1 of the License, or (at your option) any later version.                             |
## This source is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;       |
## without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      |
## See the GNU Lesser General Public License for more details.                                    |
## You should have received a copy of the GNU Lesser General Public License along with this       |
## source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html                     |
##-------------------------------------------------------------------------------------------------

##-------------------------------------------------------------------------------------------------
##                                            Import                                             --
##-------------------------------------------------------------------------------------------------


# Import system modules
import sys
import time
import os
import math
import pylab
from pylab import *
from datetime import datetime

# Add common modules location tp path
sys.path.append('../../../../')
sys.path.append('../../../../gnurabbit/python/')
sys.path.append('../../../../common/')

# Import common modules
from ptsexcept import *
import rr
import csr
import fmc_tdc
import time
from PendulumCNT91 import *

NUM_OF_TSTAMPS = 1000    # number of fill-ups of the TDC memory
                         # ex NUM_OF_TSTAMPS = 100, means aquisition
                         # of 25600 timestamps; in this setup this means
                         # 6400 rising edge timestamps per channel

PENDULUM       = "/dev/usbtmc1"
USB_DEVICE     = "/dev/ttyUSB0"
RS232_BAUD     = 57600

def pendulum_output_on(meas):
    meas.write("OUTP:TYPE PULS")
    meas.write("SOUR:PULS:PER 0.001")
    meas.write("SOUR:PULS:WIDT 0.0005")
    return (1)

def pendulum_output_off(meas):
    meas.write("OUTP:TYPE OFF")
    return (1)

##-------------------------------------------------------------------------------------------------
##                                             Main                                              --
##-------------------------------------------------------------------------------------------------

def main (default_directory='.'):

    # Constants declaration
    FMC_TDC_ADDR             = '1a39:0004/1a39:0004@0001:0000'#'1a39:0004/1a39:0004@000B:0000'
    FMC_TDC_BITSTREAM_PATH   = '../firmwares/tdc.bin'
    FPGA_LOADER_PATH         = '../../../../gnurabbit/user/fpga_loader'

    # SPEC object declaration
    spec = rr.Gennum()

    # Bind SPEC object to FMC TDC card
    print "\n-------------------------------------------------------------------"
    print "----------------------------- FMC TDC -----------------------------"
    print "-------------------- ACAM test with Cesium PPS --------------------\n"


    print "\n_______________________________Info______________________________\n"
    print "FMC TDC address to parse: %s"%(FMC_TDC_ADDR)
    for name, value in spec.parse_addr(FMC_TDC_ADDR).iteritems():
        print "%s:0x%04X"%(name, value)
    spec.bind(FMC_TDC_ADDR)

    # Load FMC TDC firmware
    print "\n_________________________Initialisations_________________________\n"
    print "Loading FMC TDC firmware...",
    firmware_loader = os.path.join(default_directory, FPGA_LOADER_PATH)
    bitstream = os.path.join(default_directory, FMC_TDC_BITSTREAM_PATH)
    os.system(firmware_loader + ' ' + bitstream)
    time.sleep(1)
    firmware_loader = os.path.join(default_directory, FPGA_LOADER_PATH)
    bitstream = os.path.join(default_directory, FMC_TDC_BITSTREAM_PATH)
    os.system(firmware_loader + ' ' + bitstream)
    time.sleep(1)
    print "Firmware loaded!"

    # TDC object declaration
    tdc = fmc_tdc.CFMCTDC(spec)

    # Check bitsteam type
    bitstream_type = tdc.read_bitstream_type()
    if(bitstream_type == 0xFFFFFFFF):
        msg = ("FATAL ERROR: No access to TDC core")
        print (msg)
    if(bitstream_type != 0x1):
        msg = ("FATAL ERROR: Wrong bitstream type loaded 0x%08X") %bitstream_type
        print (msg)
    else:
        print('Access to TDC core OK')


    # Communication with Pendulum
    print "\n__________________________USB Pendulum_____________________________\n"
    try:
        meas = PendulumCNT91(PENDULUM)
    except OSError:
        raise PtsError('CNT-91 is not switched on or badly connected, aborting.')
    a = pendulum_output_off(meas)


    # Configure ACAM and TDC core registers
    print "\n__________________________Configuration__________________________\n"
    tdc.config_acam()
    time.sleep(1)
    tdc.reset_acam()
    acam_status_test = tdc.read_acam_status()-0xC4000800
    if acam_status_test == 0:
        print "ACAM IC8: Status register OK"
    else:
        msg = ("ERROR: ACAM IC8: No communication")
        print (msg)
    acam_readback_regs = []
    acam_readback_regs = tdc.readback_acam_config()
    for i in range(len(tdc.ACAM_READBACK_REGS)): 
        if (acam_readback_regs[i] == tdc.ACAM_READBACK_REGS[i]):
            print "ACAM IC8: reg 0x%02X: 0x%08X OK"%(tdc.ACAM_READBACK_ADDR[i], acam_readback_regs[i])
        else:
            msg = ("ERROR: ACAM IC8: Configuration registers failure; reg 0x%2X: received 0x%8X, expected 0x%8X"%(tdc.ACAM_READBACK_ADDR[i], acam_readback_regs[i], tdc.ACAM_READBACK_REGS[i]))
            print (msg)

    # Enable terminations of all channels
    tdc.enable_channels()
    tdc.channel_term(1, 0)
    tdc.channel_term(2, 0)
    tdc.channel_term(3, 0)
    tdc.channel_term(4, 0)
    tdc.channel_term(5, 0)

    # DAC
    tdc.configure_mezz_dac(0xA89D)

    # Interrupts
    print "\n____________________________IRQs_________________________________\n"
    print('Set IRQ enable mask: %.4X')%tdc.set_irq_en_mask(0x1F) # was 0xC!!
    tdc.set_irq_tstamp_thresh(0x100)#100
    tdc.set_irq_time_thresh(0xFFFFF)

    r_edge_timestamps_ch1 = []
    r_edge_timestamps_ch2 = []
    r_edge_timestamps_ch3 = []
    r_edge_timestamps_ch4 = []
    r_edge_timestamps_ch5 = []

    # Enable timestamps aquisition (TDC_START_FPGA pulse sent at this step) 
    tdc.start_acq()
    time.sleep(1)


    # pendulum  ON sending pulses
    a = pendulum_output_on(meas)

    for i in range(NUM_OF_TSTAMPS):

        tdc.check_irq(0)

        timestamps, data = tdc.get_timestamps(0)

        # keep only rising edge timestamps
        for m in range(len(timestamps)):
            if (timestamps[m][2] == 1)   and (timestamps[m][1] == 0): # rising tstanmps ch1
                r_edge_timestamps_ch1.append(timestamps[m][0])
            elif (timestamps[m][2] == 1) and (timestamps[m][1] == 1): # rising tstanmps ch2
                r_edge_timestamps_ch2.append(timestamps[m][0])
            elif (timestamps[m][2] == 1) and (timestamps[m][1] == 2): # rising tstanmps ch3
                r_edge_timestamps_ch3.append(timestamps[m][0])
            elif (timestamps[m][2] == 1) and (timestamps[m][1] == 3): # rising tstanmps ch4
                r_edge_timestamps_ch4.append(timestamps[m][0])
            elif (timestamps[m][2] == 1) and (timestamps[m][1] == 4): # rising tstanmps ch5
                r_edge_timestamps_ch5.append(timestamps[m][0])

        tdc.clear_irq_src(int(math.log(tdc.get_irq_src(),2)))


    # pendulum output off
    a = pendulum_output_off(meas)

    print "TDC Write pointer        : %d"%(tdc.get_pointer())
    print "TDC Overflows            : %d"%(tdc.get_overflow_counter())
    if len(r_edge_timestamps_ch1) > 0: r_edge_timestamps_ch1.sort()
    if len(r_edge_timestamps_ch2) > 0: r_edge_timestamps_ch2.sort()
    if len(r_edge_timestamps_ch3) > 0: r_edge_timestamps_ch3.sort()
    if len(r_edge_timestamps_ch4) > 0: r_edge_timestamps_ch4.sort()
    if len(r_edge_timestamps_ch5) > 0: r_edge_timestamps_ch5.sort() 

    print "ch1                   : %20.2f"%(len(r_edge_timestamps_ch1))
    print "ch2                   : %20.2f"%(len(r_edge_timestamps_ch2))
    print "ch3                   : %20.2f"%(len(r_edge_timestamps_ch3))
    print "ch4                   : %20.2f"%(len(r_edge_timestamps_ch4))
    print "ch5                   : %20.2f\n"%(len(r_edge_timestamps_ch5))

    all_measurs = []
    for k in range(0,len(r_edge_timestamps_ch1)-1,1):
        measur = r_edge_timestamps_ch1[k] - r_edge_timestamps_ch5[k]
        if measur > -20000 and measur < 20000:
            all_measurs.append(measur)

    print "avg so far               : %20.2f ps"%(sum(all_measurs, 0.0) / len(all_measurs))
    print "span so far              : %20.2f ps"%((max(all_measurs))-(min(all_measurs)))
    print "samples                   : %20.2f"%(len(all_measurs))
    print "max so far: iter %d       : %20.2f ps"%(all_measurs.index(max(all_measurs)), max(all_measurs))
    print "min so far: iter %d       : %20.2f ps"%(all_measurs.index(min(all_measurs)),min(all_measurs))

    print "\n-----------------------------------------------------------------\n\n\n"


if __name__ == '__main__' :
    main()
