#!   /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 gn4124
import spec_fmc_adc
import fmc_adc
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!
"""

GN4124_CSR = 0x0

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

AWG_SET_SLEEP = 0.6
SSR_SET_SLEEP = 0.05

ACQ_TIMEOUT = 10

MAX_FIRMWARE_RELOAD = 10

PRE_TRIG_SAMPLES = 10
POST_TRIG_SAMPLES = 10000
NB_SHOTS = 1

ACQ_LENGTH = 10000 # in samples
DMA_LENGTH = 4096 # in bytes

# col 0: freq
# col 1: expected amplitude (ADC raw data)
# col 2: tolerance on the amplitude
points = [[10E3, 33300, 4000],
          [100E3, 33300, 4000],
          [1E6, 33300, 4000],
          [5E6, 33300, 4000],
          [6E6, 33300, 4000],
          [7E6, 33300, 4000],
          [8E6, 33300, 4000],
          [9E6, 33300, 4000],
          [10E6, 21000, 4000],
          [11E6, 21000, 4000],
          [12E6, 21000, 4000],
          [13E6, 21000, 4000],
          [14E6, 21000, 4000],
          [15E6, 16500, 4000],
          [16E6, 15700, 4000],
          [17E6, 15000, 4000],
          [18E6, 14500, 4000],
          [19E6, 14000, 4000],
          #[20E6, 12500, 4000],
          [21E6, 12500, 4000],
          [22E6, 12500, 4000],
          [23E6, 12000, 4000],
          [24E6, 11500, 4000],
          #[25E6, 10000, 4000],
          [30E6, 9000, 4000],
          [32E6, 9000, 4000],
          [34E6, 9000, 4000],
          [36E6, 9000, 4000],
          [38E6, 9000, 4000],
          [40E6, 4500, 4000],
          [42E6, 4500, 4000],
          [44E6, 4500, 4000],
          [46E6, 4500, 4000],
          [48E6, 4500, 4000],
          [60E6, 1000, 700],
          [80E6, 400, 300]]

# The following table is used to test the test
"""
points = [[1E6, 36300, 1000],
          [10E6, 24000, 1000],
          [15E6, 19500, 1000],
          [16E6, 18700, 1000],
          [17E6, 18000, 1000],
          [18E6, 17500, 1000],
          [19E6, 17000, 1000],
          [20E6, 15500, 1000],
          [21E6, 15500, 1000],
          [22E6, 15500, 1000],
          [23E6, 15000, 1000],
          [24E6, 14500, 1000],
          [25E6, 14000, 2000],
          [30E6, 12000, 1000],
          [40E6, 7500, 1000],
          [60E6, 4000, 500],
          [80E6, 3400, 200]]
"""

def load_firmware(default_directory):
    print('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);


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.')
    fmc.__init__(spec)
    # 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 acquisition(gnum, pages, fmc, spec_fmc, channel_nb):
    # Make sure no acquisition is running
    fmc.stop_acq()
    #print('Acquisition FSM state : %s') % fmc.get_acq_fsm_state()
    # 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()):
        #print fmc.get_acq_fsm_state()
        time.sleep(.1)
        timeout += 1
        if(ACQ_TIMEOUT < timeout):
            print('Acquisition timeout. Check that the AWG is switched ON and properly connected.')
            return 1
    # Retrieve data trough DMA
    channels_data = spec_fmc.get_data(0x0, ACQ_LENGTH*8)
    return channels_data[channel_nb-1::4]


def show_result_graph(points, ch_diff, ch):
    pt = array(points)
    freq = pt[:,0]
    a_min = pt[:,1] - pt[:,2]
    a_max = pt[:,1] + pt[:,2]
    cutoff = [-3] * len(points)
    ch_label = 'Channel' + str(ch)
    semilogx(freq, ch_diff, '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, a_min, 'r:', label='Lower limit')
    #semilogx(freq, a_max, 'r:', label='Upper limit')
    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
    gnum = gn4124.CGN4124(spec, GN4124_CSR)
    spec_fmc = spec_fmc_adc.CSpecFmcAdc100Ms(spec)
    fmc = fmc_adc.CFmcAdc100Ms(spec)
    gen = Agilent33250A(device=USB_DEVICE, bauds=RS232_BAUD)
    sine = SineWaveform()

    # Enable "DMA finished" IRQ
    spec_fmc.set_irq_en_mask(0x1)

    # 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

    # Get physical addresses of the pages for DMA transfer
    pages = gnum.get_physical_addr()

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

    # Test frequency response of all channels
    ch_diff = []
    i = ch_sel
    for j in range(len(points)):
        set_awg_freq(gen, sine, points[j][0])
        #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)
        channel_data = []
        channel_data = acquisition(gnum, pages, fmc, spec_fmc, i)
        channel_data = [hex2signed(item) for item in channel_data]
        channel_data_v = [digital2volt(item,1.0,16) for item in channel_data]
        diff = max(channel_data_v)-min(channel_data_v)
        print('CH%d frequency:%6.0f Hz amplitude:%f V')%(i, points[j][0], diff)
        ch_diff.append(diff)
        fmc.set_input_range(i, 'OPEN')
        fmc.set_input_term(i, 'OFF')

    # print freqency response to log
    print('Channels frequency response')
    print('Frequency, CH%d value')%ch_sel
    for i in range(len(points)):
        print('%2.3f, %f')%(points[i][0]/1E6, ch_diff[i])

    # print aqcuisition to file
    # open test09 log file in read mode
    file_name = raw_input('Enter a file name (default=log_test16.txt):')
    if file_name == "":
        file_name = "log_test16.txt"
    file = open(file_name, 'w')
    file.write("Frequency, CH%d amplitude\n"%ch_sel)
    for i in range(len(points)):
        file.write('%2.3f, %f\n'%(points[i][0]/1E6, ch_diff[i]))

    # Convert volts to dB
    vref = ch_diff[0]
    print('vref=%f')% vref
    ch_diff = [(20*log(item/vref)) for item in ch_diff]

    # print freqency response to log
    print('Channels frequency response in dB')
    print('Frequency, CH%d value')%ch_sel
    for i in range(len(points)):
        print('%2.3f, %f')%(points[i][0]/1E6, ch_diff[i])

    # Plot results
    show_result_graph(points, ch_diff, 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()
