#!   /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 numpy import *
from pylab import *

from ptsexcept import *

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


"""
test16: Test analogue front-end frequency response

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

# 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

NB_CHANNELS = 4

AWG_SET_SLEEP = 0.3
SSR_SET_SLEEP = 0.05
BOX_SET_SLEEP = 0.01

ACQ_TIMEOUT = 10

PRE_TRIG_SAMPLES = 10
POST_TRIG_SAMPLES = 10000
NB_SHOTS = 1

ACQ_LENGTH = 10000 # in samples

START_FREQ = 1E6
STOP_FREQ  = 80E6
STEP_FREQ  = 500E3


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 open_all_channels(fmc):
    for i in range(1,NB_CHANNELS+1):
        fmc.set_input_range(i, 'OPEN')
        time.sleep(SSR_SET_SLEEP)


def fmc_adc_init(spec, fmc):
    print('Initialise FMC board.')
    # Reset offset DACs
    fmc.dc_offset_reset()
    # Make sure all switches are OFF
    open_all_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)
    # Print configuration
    #fmc.print_adc_core_config()


def set_awg_freq(gen, sine, freq):
    sine.frequency = freq
    gen.play(sine)
    #print('Sine frequency:%3.3fMHz')%(sine.frequency/1E6)
    time.sleep(AWG_SET_SLEEP)

def acq_channels(fmc, spec_fmc):
    # 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()
    # 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)
    channels_data = [hex2signed(item) for item in channels_data]
    return channels_data


def show_result_graph(freq, ch_att, ch):
    cutoff = [-3] * len(freq)
    ch_label = 'Channel' + str(ch)
    semilogx(freq, ch_att, 'b', label=ch_label)
    #semilogx(freq, ch_diff[1::4], 'g', label='Channel 2')
    #semilogx(freq, ch_diff[2::4], 'm', label='Channel 3')
    #semilogx(freq, ch_diff[3::4], 'c', label='Channel 4')
    semilogx(freq, cutoff, 'r', label='-3dB')
    grid(which='both')
    legend(loc='upper left')
    show()
    return 0


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


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


def main (default_directory='.'):

    # Load firmware to FPGA
    path_fpga_loader = '../../../gnurabbit/user/fpga_loader';
    path_firmware = '../firmwares/spec_fmcadc100m14b4cha_test.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
    spec_fmc = spec_fmc_adc.CSpecFmcAdc100Ms(spec)
    fmc = fmc_adc.CFmcAdc100Ms(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)

    # Set sine params
    sine.frequency = 1E6
    sine.amplitude = 0.8
    sine.dc = 0
    #print('Sine frequency:%3.3fMHz amplitude:%2.3fVp offset:%2.3fV')%(sine.frequency/1E6, sine.amplitude, sine.dc)

    # Set AWG
    gen.connect()
    gen.play(sine)
    gen.output = True


    ch_sel = int(raw_input('Select a channel [1:4]:'))
    print("Channel %d selected.")%ch_sel

    # Test frequency response of all channels
    ch_ampl = []
    i = ch_sel
    freq = arange(START_FREQ, STOP_FREQ, STEP_FREQ)
    for j in range(len(freq)):
        set_awg_freq(gen, sine, freq[j])
        #for i in range(1,NB_CHANNELS+1):
        fmc.set_input_range(i, '1V')
        fmc.set_input_term(i, 'ON')
        time.sleep(SSR_SET_SLEEP)
        box.select_output_ch(i) # connect AWG to current channel
        time.sleep(BOX_SET_SLEEP)
        channel_data = []
        channels_data = acq_channels(fmc, spec_fmc)
        channel_data = channels_data[ch_sel-1::4]
        channel_data_v = [digital2volt(item,1.0,16) for item in channel_data]
        ampl = max(channel_data_v)-min(channel_data_v)
        print('CH%d frequency:%6.0f Hz amplitude:%f V')%(i, freq[j], ampl)
        ch_ampl.append(ampl)
        fmc.set_input_range(i, 'OPEN')
        fmc.set_input_term(i, 'OFF')

    # Convert volts to dB
    ch_att = []
    vref = ch_ampl[0]
    print "Channel %d vref=%f"%(ch_sel, vref)
    ch_att = [(20*log(item/vref)) for item in ch_ampl]

    # print freqency response to log
    print('Channels frequency response')
    print('Frequency, CH%d value')%ch_sel
    for i in range(len(ch_att)):
        print('%2.3f, %f')%(freq[i], ch_ampl[i])

    # print aqcuisition to file
    file_name = "log_test16.txt"
    file = open(file_name, 'w')
    file.write("Frequency, CH%d amplitude\n"%ch_sel)
    for i in range(len(ch_att)):
        file.write('%2.3f, %f\n'%(freq[i], ch_ampl[i]))

    # Plot results
    show_result_graph(freq, ch_att, ch_sel)

    # Make sure all switches are OFF
    open_all_channels(fmc)

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

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


if __name__ == '__main__' :
    main()
