#!   /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 fmc_adc
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *


"""
test08: Test analogue front-end switches

Note: Requires test00.py to run first to load the firmware!
"""


USB_DEVICE = "/dev/ttyUSB0"
RS232_BAUD = 57600
NB_CHANNELS = 4

AWG_SET_SLEEP = 1
SSR_SET_SLEEP = 0.05

ADC_MID_TOL = 500
ADC_MID_THRESHOLD = 10

SW1_TOL = 30000
SW2_TOL = 5000
SW3_TOL = 5000
SW4_TOL = 10000
SW5_TOL = 100
SW6_TOL = 20000
SW7_TOL = 20000

RETRY_NB = 10

SW1_THRESHOLD = 10
SW2_THRESHOLD = 10
SW3_THRESHOLD = 10
SW4_THRESHOLD = 10
SW5_THRESHOLD = 5
SW6_THRESHOLD = 10
SW7_THRESHOLD = 10


def set_awg_offset(gen, sine, offset):
    sine.dc = offset
    gen.play(sine)
    time.sleep(AWG_SET_SLEEP)

def print_current_adc_value(fmc, channel, file):
    adc_value = fmc.get_current_adc_value(channel)
    print('CH%d ADC value:0x%.4X  %d') % (channel, adc_value, adc_value)
    file.write('CH%d: 0x%.4X %d\n' % (channel, adc_value, adc_value))

# Routine to test SSR (Solid State Relay)
# Basic operation: Set AWG DC offset and SSRs -> read ADC value, change 1 SSR -> read ADC -> check ADC values difference
# Options: retry -> run the same test several times
#          threshold -> minimum number of try to pass the test
def sw_test(gen, sine, awg_offset, fmc, sw, ssr_1, ssr_2, diff_tol, retry_nb=0, threshold=0):
    print('\nTesting switch %d\n-------------------------')%sw
    set_awg_offset(gen, sine, awg_offset)
    print('AWG offset: %1.3fV') % awg_offset

    for i in range(1,NB_CHANNELS+1):
        pass_nb = 0
        for j in range(retry_nb):
            fmc.set_ssr(i,ssr_1)
            time.sleep(SSR_SET_SLEEP)
            adc_value_before = fmc.get_current_adc_value(i)
            fmc.set_ssr(i,ssr_2)
            time.sleep(SSR_SET_SLEEP)
            adc_value = fmc.get_current_adc_value(i)
            diff = adc_value_before-adc_value
            print('CH%d ssr=0x%.2X: %d  ssr=0x%.2X: %d  diff:%d') % (i, ssr_1, adc_value_before, ssr_2, adc_value, diff)
            if(diff_tol <= abs(diff)):
                pass_nb += 1
            fmc.set_ssr(i,0x0)
            time.sleep(SSR_SET_SLEEP)
        print('   Number of good tests:%d  threshold:%d') % (pass_nb, threshold)
        if(pass_nb < threshold):
            print('#####################################')
            print('SW%d of channel %d is malfunctioning') % (sw, i)
            print('#####################################')
            raise PtsError('SW%d of channel %d is malfunctioning' % (sw, i))

def adc_mid_test(gen, sine, awg_offset, fmc, tol, retry_nb=0, threshold=0):
    print('\nTesting ADC middle scale\n-------------------------')
    set_awg_offset(gen, sine, awg_offset)
    print('AWG offset: %1.3fV') % awg_offset

    for i in range(1,NB_CHANNELS+1):
        pass_nb = 0
        ssr_1 = 0x0
        for j in range(retry_nb):
            fmc.set_ssr(i,ssr_1)
            time.sleep(SSR_SET_SLEEP)
            adc_value = fmc.get_current_adc_value(i)
            diff = adc_value - 0x8000
            print('CH%d ssr=0x%.2X: %d  diff: %d') % (i, ssr_1, adc_value, diff)
            if((-tol < diff) & (tol > diff)):
                pass_nb += 1
        print('   Number of good tests:%d  threshold:%d') % (pass_nb, threshold)
        if(pass_nb < threshold):
            print('############################################')
            print('One of channel %d switches is malfunctioning') % i
            print('############################################')
            raise PtsError('One of channel %d switches is malfunctioning' % i)


def main (default_directory='.'):

    """
    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);
    """

    # Objects declaration
    spec = rr.Gennum() # bind to the SPEC board
    fmc = fmc_adc.CFmcAdc100Ms(spec)
    gen = Agilent33250A(device=USB_DEVICE, bauds=RS232_BAUD)
    sine = SineWaveform()

    # Set sine params -> ~ DC level
    sine.frequency = 0.000001
    sine.amplitude = 0.001
    sine.dc = 1

    # Set AWG and turn it ON
    gen.connect()
    gen.output = True

    fmc.dc_offset_reset()


    # Following commented code scan all SSR configurations to find good ones for testing switches
    """
    file = open("adc_analogue_test.txt", 'w')

    offset = 1
    set_awg_offset(gen, sine, offset)
    print('\nAWG offset: %f') % offset
    file.write('\n\nAWG offset: %f' % offset)

    for ssr in range(0x80):
        print('\nSSR: %.2X') % ssr
        file.write('\nSSR: %.2X\n' % ssr)
        for i in range(1,NB_CHANNELS+1):
            fmc.set_ssr(i,ssr)
            time.sleep(0.05)
            print_current_adc_value(fmc, i, file)
            fmc.set_ssr(i,0x0)
            time.sleep(0.05)


    offset = 0.5
    set_awg_offset(gen, sine, offset)
    print('\nAWG offset: %f') % offset
    file.write('\n\nAWG offset: %f' % offset)

    for ssr in range(0x80):
        print('\nSSR: %.2X') % ssr
        file.write('\nSSR: %.2X\n' % ssr)
        for i in range(1,NB_CHANNELS+1):
            fmc.set_ssr(i,ssr)
            time.sleep(0.05)
            print_current_adc_value(fmc, i, file)
            fmc.set_ssr(i,0x0)
            time.sleep(0.05)


    offset = 0.01
    set_awg_offset(gen, sine, offset)
    print('\nAWG offset: %f') % offset
    file.write('\n\nAWG offset: %f' % offset)

    for ssr in range(0x80):
        print('\nSSR: %.2X') % ssr
        file.write('\nSSR: %.2X\n' % ssr)
        for i in range(1,NB_CHANNELS+1):
            fmc.set_ssr(i,ssr)
            time.sleep(0.05)
            print_current_adc_value(fmc, i, file)
            fmc.set_ssr(i,0x0)
            time.sleep(0.05)


    offset = 0.25
    set_awg_offset(gen, sine, offset)
    print('\nAWG offset: %f') % offset
    file.write('\n\nAWG offset: %f' % offset)

    for ssr in range(0x80):
        print('\nSSR: %.2X') % ssr
        file.write('\nSSR: %.2X\n' % ssr)
        for i in range(1,NB_CHANNELS+1):
            fmc.set_ssr(i,ssr)
            time.sleep(0.05)
            print_current_adc_value(fmc, i, file)
            fmc.set_ssr(i,0x0)
            time.sleep(0.05)


    gen.output = False
    gen.close()
    sys.exit()
    """

    adc_mid_test(gen, sine, 0.25, fmc, ADC_MID_TOL, RETRY_NB, ADC_MID_THRESHOLD)

    sw_test(gen, sine, 0.25, fmc, 1, 0x00, 0x01, SW1_TOL, RETRY_NB, SW1_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 4, 0x01, 0x09, SW4_TOL, RETRY_NB, SW4_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 5, 0x41, 0x51, SW5_TOL, RETRY_NB, SW5_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 6, 0x00, 0x60, SW5_TOL, RETRY_NB, SW6_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 7, 0x01, 0x41, SW6_TOL, RETRY_NB, SW7_THRESHOLD)
    sw_test(gen, sine, 0.01, fmc, 2, 0x20, 0x22, SW2_TOL, RETRY_NB, SW2_THRESHOLD)
    sw_test(gen, sine, 0.01, fmc, 3, 0x22, 0x26, SW3_TOL, RETRY_NB, SW3_THRESHOLD)

    # Following commented code is for testing the tests
    """
    sw_test(gen, sine, 0.25, fmc, 1, 0x00, 0x00, SW1_TOL, RETRY_NB, SW1_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 4, 0x01, 0x01, SW4_TOL, RETRY_NB, SW4_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 5, 0x41, 0x41, SW5_TOL, RETRY_NB, SW5_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 6, 0x00, 0x00, SW5_TOL, RETRY_NB, SW6_THRESHOLD)
    sw_test(gen, sine, 0.25, fmc, 7, 0x01, 0x01, SW6_TOL, RETRY_NB, SW7_THRESHOLD)
    sw_test(gen, sine, 0.01, fmc, 2, 0x20, 0x20, SW2_TOL, RETRY_NB, SW2_THRESHOLD)
    sw_test(gen, sine, 0.01, fmc, 3, 0x22, 0x22, SW3_TOL, RETRY_NB, SW3_THRESHOLD)
    """

    # Make sure all switches are OFF
    for i in range(1,NB_CHANNELS+1):
        fmc.set_ssr(i,0x00)

    # Switch AWG OFF
    gen.output = False
    gen.close()


if __name__ == '__main__' :
    main()
